diff --git a/.vpython3 b/.vpython3
index 1ef981d..83763df2 100644
--- a/.vpython3
+++ b/.vpython3
@@ -30,3 +30,38 @@
   name: "infra/python/wheels/six-py2_py3"
   version: "version:1.15.0"
 >
+
+# Common utilities.
+wheel: <
+  name: "infra/python/wheels/psutil/${platform}_${py_python}_${py_abi}"
+  version: "version:5.7.2"
+>
+wheel: <
+  name: "infra/python/wheels/requests-py2_py3"
+  version: "version:2.13.0"
+>
+
+# Used by various python unit tests.
+wheel: <
+  name: "infra/python/wheels/mock-py2_py3"
+  version: "version:2.0.0"
+>
+wheel: <
+  name: "infra/python/wheels/parameterized-py2_py3"
+  version: "version:0.7.1"
+>
+wheel: <
+  name: "infra/python/wheels/pbr-py2_py3"
+  version: "version:3.0.0"
+>
+
+# Used by:
+#   build/chromeos/test_runner.py
+wheel: <
+  name: "infra/python/wheels/jsonlines-py2_py3"
+  version: "version:1.2.0"
+>
+wheel: <
+  name: "infra/python/wheels/python-dateutil-py2_py3"
+  version: "version:2.7.3"
+>
diff --git a/BUILD.gn b/BUILD.gn
index c2b4aa79..b6af235 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -855,6 +855,7 @@
   cr_fuchsia_package("d8_fuchsia_pkg") {
     testonly = true
     binary = "//v8:d8"
+    manifest = "//build/config/fuchsia/tests-with-exec.cmx"
     package_name_override = "d8"
   }
 
diff --git a/DEPS b/DEPS
index 10b5b7f..2a8d7bf 100644
--- a/DEPS
+++ b/DEPS
@@ -199,11 +199,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': '0bad6cf1458da91d48233930d28a869f0d3104bf',
+  'skia_revision': '00f4769e34d8b2ee5a31c607f339eac37a051fd9',
   # 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': '609f77888b667dbb70edc38715437b69f15a4dc4',
+  'v8_revision': '89b433d7c3defd04809fba9deb6f8885160044a4',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling swarming_client
   # and whatever else without interference from each other.
@@ -211,7 +211,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': 'a408ce8349289c8fe457a5ebccb8c4cbac7b9c3f',
+  'angle_revision': 'a685db2e9745b615987b1b514dc23cc2fafff7bd',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -278,7 +278,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling devtools-frontend
   # and whatever else without interference from each other.
-  'devtools_frontend_revision': '8ab2d7e59263642069235798b5a1e357f21fa479',
+  'devtools_frontend_revision': '8c4d6591e05e059903cc45d789db075c6f921b1d',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libprotobuf-mutator
   # and whatever else without interference from each other.
@@ -559,7 +559,7 @@
   },
 
   'src/ios/third_party/material_components_ios/src': {
-      'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + 'afe4105debedd8c7b3376b5a37e9beeafb9b368e',
+      'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + '274ba189abfb3d8065585b64a67f4d4179996e38',
       'condition': 'checkout_ios',
   },
 
@@ -905,12 +905,12 @@
 
   # For Linux and Chromium OS.
   'src/third_party/cros_system_api': {
-      'url': Var('chromium_git') + '/chromiumos/platform2/system_api.git' + '@' + 'eb4873381d11280addc4d988514692ddaf4c91ad',
+      'url': Var('chromium_git') + '/chromiumos/platform2/system_api.git' + '@' + 'd9b81c687469075834a0bb7cd67d243ebbd719ae',
       'condition': 'checkout_linux',
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '9bdbb8f944a7aa679381d87b64b3042daae9fc7b',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '6ff74e1dce4315916851d396e32bc4121e497b0e',
 
   'src/third_party/devtools-frontend/src':
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
@@ -1275,7 +1275,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + 'd8241a7a3e4d6e6a8dc6ec6e00d90ad7a93166eb',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + '0f2d499389c5de52c1d42310715bf83835e44c48',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1487,7 +1487,7 @@
   'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@676d3c095a281f5626a00da882274ca253e33f3a',
 
   'src/third_party/vulkan_memory_allocator':
-    Var('chromium_git') + '/external/github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator.git' + '@' + '6c656df63da5995a932aafd45b32af1974e497d9',
+    Var('chromium_git') + '/external/github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator.git' + '@' + 'b1d65a2b3373fe12c622eaab65e5cf21b906d178',
 
   # Display server protocol for Linux.
   'src/third_party/wayland/src': {
@@ -1583,7 +1583,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@de2f82a60468d9e05188fc465eb945cf144c8aed',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@2358e072d21bd7fa3d9f695d30e21b88d52b65d9',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/WATCHLISTS b/WATCHLISTS
index d9478d54..4146126 100644
--- a/WATCHLISTS
+++ b/WATCHLISTS
@@ -1464,10 +1464,6 @@
                   '^components/omnibox/|'\
                   '^components/search_engines/'
     },
-    'optimization_guide': {
-      'filepath': 'optimization_guide|'\
-                  'optimization_hints',
-    },
     'origin_trials': {
       'filepath': 'origin_trial'\
                   '|OriginTrial'\
@@ -2666,7 +2662,6 @@
                       'romax+watch@chromium.org',
                       'harringtond+watch@google.com'],
     'omnibox': ['jdonnelly+watch@chromium.org'],
-    'optimization_guide': ['dougarnett+watch-optguide@chromium.org'],
     'origin_trials': ['chasej+watch@chromium.org',
                       'iclelland+watch@chromium.org'],
     'ozone': ['kalyan.kondapally@intel.com',
diff --git a/android_webview/lib/aw_main_delegate.cc b/android_webview/lib/aw_main_delegate.cc
index 5e54d8c..64a441070 100644
--- a/android_webview/lib/aw_main_delegate.cc
+++ b/android_webview/lib/aw_main_delegate.cc
@@ -222,6 +222,9 @@
       // which is needed for UseSurfaceLayerForVideo feature.
       // https://crbug.com/853832
       features.EnableIfNotSet(media::kDisableSurfaceLayerForVideo);
+
+      // UseSkiaRenderer requires VizForWebView.
+      features.DisableIfNotSet(::features::kUseSkiaRenderer);
     }
 
     // WebView does not support overlay fullscreen yet for video overlays.
diff --git a/android_webview/lib/webview_tests.cc b/android_webview/lib/webview_tests.cc
index ce8922d..ab2b4cea 100644
--- a/android_webview/lib/webview_tests.cc
+++ b/android_webview/lib/webview_tests.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "android_webview/browser/gfx/gpu_service_webview.h"
+#include "base/base_switches.h"
 #include "base/command_line.h"
 #include "base/test/test_suite.h"
 #include "content/public/common/content_switches.h"
@@ -11,8 +12,11 @@
 #include "ui/gl/test/gl_surface_test_support.h"
 
 int main(int argc, char** argv) {
-  base::CommandLine::ForCurrentProcess()->AppendSwitch(
-      switches::kSingleProcess);
+  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+  command_line->AppendSwitch(switches::kSingleProcess);
+  command_line->AppendSwitchASCII(switches::kDisableFeatures,
+                                  ",Vulkan,UseSkiaRenderer");
+
   gl::GLSurfaceTestSupport::InitializeNoExtensionsOneOff();
   android_webview::GpuServiceWebView::GetInstance();
   base::TestSuite test_suite(argc, argv);
diff --git a/ash/accessibility/accessibility_controller_impl.cc b/ash/accessibility/accessibility_controller_impl.cc
index fc66926..192f9b21 100644
--- a/ash/accessibility/accessibility_controller_impl.cc
+++ b/ash/accessibility/accessibility_controller_impl.cc
@@ -167,6 +167,7 @@
     prefs::kAccessibilityMonoAudioEnabled,
     prefs::kAccessibilityScreenMagnifierEnabled,
     prefs::kAccessibilityScreenMagnifierFocusFollowingEnabled,
+    prefs::kAccessibilityScreenMagnifierMousePanningMode,
     prefs::kAccessibilityScreenMagnifierScale,
     prefs::kAccessibilitySelectToSpeakEnabled,
     prefs::kAccessibilitySpokenFeedbackEnabled,
@@ -624,6 +625,10 @@
       prefs::kAccessibilityAutoclickMenuPosition,
       static_cast<int>(kDefaultAutoclickMenuPosition),
       user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF);
+  registry->RegisterIntegerPref(
+      prefs::kAccessibilityScreenMagnifierMousePanningMode,
+      static_cast<int>(MagnifierMousePanningMode::kNone),
+      user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF);
   registry->RegisterBooleanPref(
       prefs::kAccessibilityCaretHighlightEnabled, false,
       user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF);
diff --git a/ash/public/cpp/accessibility_controller_enums.h b/ash/public/cpp/accessibility_controller_enums.h
index 4375535..09c34ee 100644
--- a/ash/public/cpp/accessibility_controller_enums.h
+++ b/ash/public/cpp/accessibility_controller_enums.h
@@ -178,6 +178,24 @@
   kSystemDefault,
 };
 
+// Mouse panning mode for magnifier. This indicates the way the magnified
+// viewport is panned as the mouse is moved across the screen. These values are
+// written to prefs so they should not be changed. New values should be added at
+// the end.
+enum class MagnifierMousePanningMode {
+  // Default value, indicates no mouse panning mode chosen.
+  kNone,
+
+  // Continuous panning mode.
+  kContinuous,
+
+  // Centered panning mode.
+  kCentered,
+
+  // Edge panning mode.
+  kEdge,
+};
+
 }  // namespace ash
 
 #endif  // ASH_PUBLIC_CPP_ACCESSIBILITY_CONTROLLER_ENUMS_H_
diff --git a/ash/public/cpp/ash_pref_names.cc b/ash/public/cpp/ash_pref_names.cc
index 7f0900f1..5485284c 100644
--- a/ash/public/cpp/ash_pref_names.cc
+++ b/ash/public/cpp/ash_pref_names.cc
@@ -32,6 +32,10 @@
 // is enabled.
 const char kAccessibilityScreenMagnifierFocusFollowingEnabled[] =
     "settings.a11y.screen_magnifier_focus_following";
+// An integer pref which indicates the mouse panning mode for screen magnifier.
+// This maps to AccessibilityController::MagnifierMousePanningMode.
+const char kAccessibilityScreenMagnifierMousePanningMode[] =
+    "settings.a11y.screen_magnifier_mouse_panning_mode";
 // A boolean pref which determines whether screen magnifier should center
 // the text input focus.
 const char kAccessibilityScreenMagnifierCenterFocus[] =
@@ -61,7 +65,7 @@
 const char kAccessibilityAutoclickDelayMs[] =
     "settings.a11y.autoclick_delay_ms";
 // An integer pref which determines the event type for an autoclick event. This
-// maps to mojom::AccessibilityController::AutoclickEventType.
+// maps to AccessibilityController::AutoclickEventType.
 const char kAccessibilityAutoclickEventType[] =
     "settings.a11y.autoclick_event_type";
 // Whether Autoclick should immediately return to left click after performing
diff --git a/ash/public/cpp/ash_pref_names.h b/ash/public/cpp/ash_pref_names.h
index bf06d54..4461bd92 100644
--- a/ash/public/cpp/ash_pref_names.h
+++ b/ash/public/cpp/ash_pref_names.h
@@ -20,6 +20,8 @@
 ASH_PUBLIC_EXPORT extern const char kAccessibilityScreenMagnifierEnabled[];
 ASH_PUBLIC_EXPORT extern const char
     kAccessibilityScreenMagnifierFocusFollowingEnabled[];
+ASH_PUBLIC_EXPORT extern const char
+    kAccessibilityScreenMagnifierMousePanningMode[];
 ASH_PUBLIC_EXPORT extern const char kAccessibilityScreenMagnifierScale[];
 ASH_PUBLIC_EXPORT extern const char kAccessibilityVirtualKeyboardEnabled[];
 ASH_PUBLIC_EXPORT extern const char kAccessibilityVirtualKeyboardFeatures[];
diff --git a/ash/wm/desks/desk_mini_view.cc b/ash/wm/desks/desk_mini_view.cc
index 59aeaf4..2c8f5c12 100644
--- a/ash/wm/desks/desk_mini_view.cc
+++ b/ash/wm/desks/desk_mini_view.cc
@@ -36,25 +36,25 @@
 
 constexpr int kMinDeskNameViewWidth = 56;
 
-// Returns the width of the desk preview based on its |preview_height| and the
-// aspect ratio of the root window taken from |root_window_size|.
-int GetPreviewWidth(const gfx::Size& root_window_size, int preview_height) {
-  return preview_height * root_window_size.width() / root_window_size.height();
-}
-
-// The desk preview bounds are proportional to the bounds of the display on
-// which it resides, and whether the |compact| layout is used.
-gfx::Rect GetDeskPreviewBounds(aura::Window* root_window, bool compact) {
-  const int preview_height = DeskPreviewView::GetHeight(root_window, compact);
-  const auto root_size = root_window->bounds().size();
-  return gfx::Rect(GetPreviewWidth(root_size, preview_height), preview_height);
-}
-
 }  // namespace
 
 // -----------------------------------------------------------------------------
 // DeskMiniView
 
+// static
+int DeskMiniView::GetPreviewWidth(const gfx::Size& root_window_size,
+                                  int preview_height) {
+  return preview_height * root_window_size.width() / root_window_size.height();
+}
+
+// static
+gfx::Rect DeskMiniView::GetDeskPreviewBounds(aura::Window* root_window,
+                                             bool compact) {
+  const int preview_height = DeskPreviewView::GetHeight(root_window, compact);
+  const auto root_size = root_window->bounds().size();
+  return gfx::Rect(GetPreviewWidth(root_size, preview_height), preview_height);
+}
+
 DeskMiniView::DeskMiniView(DesksBarView* owner_bar,
                            aura::Window* root_window,
                            Desk* desk)
diff --git a/ash/wm/desks/desk_mini_view.h b/ash/wm/desks/desk_mini_view.h
index 754142c1..0222d9c60 100644
--- a/ash/wm/desks/desk_mini_view.h
+++ b/ash/wm/desks/desk_mini_view.h
@@ -34,6 +34,16 @@
       public views::TextfieldController,
       public views::ViewObserver {
  public:
+  // Returns the width of the desk preview based on its |preview_height| and the
+  // aspect ratio of the root window taken from |root_window_size|.
+  static int GetPreviewWidth(const gfx::Size& root_window_size,
+                             int preview_height);
+
+  // The desk preview bounds are proportional to the bounds of the display on
+  // which it resides, and whether the |compact| layout is used.
+  static gfx::Rect GetDeskPreviewBounds(aura::Window* root_window,
+                                        bool compact);
+
   DeskMiniView(DesksBarView* owner_bar, aura::Window* root_window, Desk* desk);
   ~DeskMiniView() override;
 
diff --git a/ash/wm/desks/desk_mini_view_animations.cc b/ash/wm/desks/desk_mini_view_animations.cc
index 795e67f3..185ed461 100644
--- a/ash/wm/desks/desk_mini_view_animations.cc
+++ b/ash/wm/desks/desk_mini_view_animations.cc
@@ -147,8 +147,9 @@
     removed_mini_view_->parent()->RemoveChildViewT(removed_mini_view_);
     if (to_zero_state_) {
       DCHECK(bar_view_);
-      bar_view_->zero_state_default_desk_button()->SetVisible(true);
-      bar_view_->zero_state_new_desk_button()->SetVisible(true);
+      // Layout the desks bar to make sure the buttons visibilities and button's
+      // text can be updated correctly while going back to zero state.
+      bar_view_->Layout();
     }
   }
 
diff --git a/ash/wm/desks/desks_bar_view.cc b/ash/wm/desks/desks_bar_view.cc
index c44a3ef..14e2e79 100644
--- a/ash/wm/desks/desks_bar_view.cc
+++ b/ash/wm/desks/desks_bar_view.cc
@@ -272,6 +272,13 @@
           gfx::Rect(gfx::Point((desks_bar_bounds.width() - content_width) / 2,
                                kZeroStateY),
                     zero_state_default_desk_button_size));
+      // Update this button's text since it may changes while removing a desk
+      // and going back to the zero state.
+      zero_state_default_desk_button->UpdateLabelText();
+      // Make sure these two buttons are always visible while in zero state bar
+      // since they are invisible in expanded state bar.
+      zero_state_default_desk_button->SetVisible(true);
+      zero_state_new_desk_button->SetVisible(true);
       zero_state_new_desk_button->SetBoundsRect(gfx::Rect(
           gfx::Point(zero_state_default_desk_button->bounds().right() +
                          kZeroStateButtonSpacing,
@@ -671,7 +678,9 @@
     removed_mini_views.push_back(removed_mini_view);
     removed_mini_views.push_back(mini_views_[0]);
     mini_views_.clear();
-    // Keep current layout until the animation is completed.
+    // Keep current layout until the animation is completed since the animation
+    // for going back to zero state is based on the expanded bar's current
+    // layout.
     PerformExpandedStateToZeroStateMiniViewAnimation(this, removed_mini_views);
     return;
   }
diff --git a/ash/wm/desks/desks_unittests.cc b/ash/wm/desks/desks_unittests.cc
index 2318ad12..98c7fbd 100644
--- a/ash/wm/desks/desks_unittests.cc
+++ b/ash/wm/desks/desks_unittests.cc
@@ -4319,6 +4319,83 @@
   EXPECT_TRUE(new_desk_button->GetEnabled());
 }
 
+TEST_F(DesksBentoTest, ZeroStateDeskButtonText) {
+  UpdateDisplay("1600x1200");
+  auto* overview_controller = Shell::Get()->overview_controller();
+  overview_controller->StartOverview();
+
+  auto* root_window = Shell::GetPrimaryRootWindow();
+  auto* desks_bar_view = GetOverviewGridForRoot(root_window)->desks_bar_view();
+  ASSERT_TRUE(desks_bar_view->IsZeroState());
+  // Show the default name "Desk 1" while initializing the desks bar at the
+  // first time.
+  EXPECT_EQ(base::UTF8ToUTF16("Desk 1"),
+            desks_bar_view->zero_state_default_desk_button()->GetText());
+
+  auto* event_generator = GetEventGenerator();
+  ClickOnView(desks_bar_view->zero_state_default_desk_button(),
+              event_generator);
+  EXPECT_TRUE(desks_bar_view->mini_views()[0]->desk_name_view()->HasFocus());
+
+  auto send_key = [this](ui::KeyboardCode key_code, int flags = 0) {
+    auto* generator = GetEventGenerator();
+    generator->PressKey(key_code, flags);
+    generator->ReleaseKey(key_code, flags);
+  };
+
+  // Change the desk name to "test".
+  send_key(ui::VKEY_T);
+  send_key(ui::VKEY_E);
+  send_key(ui::VKEY_S);
+  send_key(ui::VKEY_T);
+  send_key(ui::VKEY_RETURN);
+  overview_controller->EndOverview();
+  overview_controller->StartOverview();
+
+  desks_bar_view = GetOverviewGridForRoot(root_window)->desks_bar_view();
+  EXPECT_TRUE(desks_bar_view->IsZeroState());
+  // Should show the desk's current name "test" instead of the default name.
+  EXPECT_EQ(base::UTF8ToUTF16("test"),
+            desks_bar_view->zero_state_default_desk_button()->GetText());
+
+  // Create 'Desk 2'.
+  ClickOnView(desks_bar_view->zero_state_new_desk_button(), event_generator);
+  EXPECT_FALSE(desks_bar_view->IsZeroState());
+  send_key(ui::VKEY_RETURN);
+  EXPECT_EQ(base::UTF8ToUTF16("Desk 2"),
+            DesksController::Get()->desks()[1].get()->name());
+
+  // Close desk 'test' should return to zero state and the zero state default
+  // desk button should show current desk's name, which is 'Desk 1'.
+  CloseDeskFromMiniView(desks_bar_view->mini_views()[0], event_generator);
+  EXPECT_TRUE(desks_bar_view->IsZeroState());
+  EXPECT_EQ(base::UTF8ToUTF16("Desk 1"),
+            desks_bar_view->zero_state_default_desk_button()->GetText());
+
+  // Set a super long desk name.
+  ClickOnView(desks_bar_view->zero_state_default_desk_button(),
+              event_generator);
+  for (size_t i = 0; i < DeskNameView::kMaxLength + 5; i++)
+    send_key(ui::VKEY_A);
+  send_key(ui::VKEY_RETURN);
+  overview_controller->EndOverview();
+  overview_controller->StartOverview();
+
+  desks_bar_view = GetOverviewGridForRoot(root_window)->desks_bar_view();
+  auto* zero_state_default_desk_button =
+      desks_bar_view->zero_state_default_desk_button();
+  base::string16 desk_button_text = zero_state_default_desk_button->GetText();
+  base::string16 expected_desk_name(DeskNameView::kMaxLength, L'a');
+  // Zero state desk button should show the elided name as the DeskNameView.
+  EXPECT_EQ(expected_desk_name,
+            DesksController::Get()->desks()[0].get()->name());
+  EXPECT_NE(expected_desk_name, desk_button_text);
+  EXPECT_TRUE(base::StartsWith(base::UTF16ToUTF8(desk_button_text), "aaa",
+                               base::CompareCase::SENSITIVE));
+  EXPECT_FALSE(base::EndsWith(base::UTF16ToUTF8(desk_button_text), "aaa",
+                              base::CompareCase::SENSITIVE));
+}
+
 TEST_F(DesksBentoTest, ReorderDesksByMouse) {
   auto* desks_controller = DesksController::Get();
 
diff --git a/ash/wm/desks/expanded_state_new_desk_button.cc b/ash/wm/desks/expanded_state_new_desk_button.cc
index 1255fb56..97d0479 100644
--- a/ash/wm/desks/expanded_state_new_desk_button.cc
+++ b/ash/wm/desks/expanded_state_new_desk_button.cc
@@ -10,7 +10,6 @@
 #include "ash/style/ash_color_provider.h"
 #include "ash/wm/desks/desk_mini_view.h"
 #include "ash/wm/desks/desk_name_view.h"
-#include "ash/wm/desks/desk_preview_view.h"
 #include "ash/wm/desks/desks_bar_view.h"
 #include "ash/wm/desks/zero_state_button.h"
 #include "ash/wm/overview/overview_controller.h"
@@ -29,17 +28,6 @@
 
 constexpr int kCornerRadius = 4;
 
-// The new desk button in expand desks bar in Bento has the same size as the
-// desk preview, which is proportional to the size of the display on which it
-// resides.
-gfx::Rect GetExpandedStateNewDeskButtonBounds(aura::Window* root_window) {
-  const int preview_height =
-      DeskPreviewView::GetHeight(root_window, /*compact=*/false);
-  const auto root_size = root_window->bounds().size();
-  return gfx::Rect(preview_height * root_size.width() / root_size.height(),
-                   preview_height);
-}
-
 // The button belongs to ExpandedStateNewDeskButton.
 class ASH_EXPORT InnerNewDeskButton : public DeskButtonBase {
  public:
@@ -123,8 +111,9 @@
   if (bar_view_->mini_views().empty())
     return;
 
-  const gfx::Rect new_desk_button_bounds = GetExpandedStateNewDeskButtonBounds(
-      bar_view_->GetWidget()->GetNativeWindow()->GetRootWindow());
+  const gfx::Rect new_desk_button_bounds = DeskMiniView::GetDeskPreviewBounds(
+      bar_view_->GetWidget()->GetNativeWindow()->GetRootWindow(),
+      /*compact=*/false);
   new_desk_button_->SetBoundsRect(new_desk_button_bounds);
 
   const gfx::Size label_size =
diff --git a/ash/wm/desks/zero_state_button.cc b/ash/wm/desks/zero_state_button.cc
index ecb0b812..0c86cea0 100644
--- a/ash/wm/desks/zero_state_button.cc
+++ b/ash/wm/desks/zero_state_button.cc
@@ -7,11 +7,18 @@
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/style/ash_color_provider.h"
+#include "ash/wm/desks/desk.h"
+#include "ash/wm/desks/desk_mini_view.h"
+#include "ash/wm/desks/desk_preview_view.h"
 #include "ash/wm/desks/desks_bar_view.h"
 #include "ash/wm/desks/desks_controller.h"
 #include "ash/wm/wm_highlight_item_border.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/gfx/canvas.h"
+#include "ui/gfx/font_list.h"
+#include "ui/gfx/text_constants.h"
+#include "ui/gfx/text_elider.h"
+#include "ui/views/accessibility/view_accessibility.h"
 #include "ui/views/animation/ink_drop_impl.h"
 #include "ui/views/controls/highlight_path_generator.h"
 
@@ -27,6 +34,8 @@
 
 constexpr int kZeroStateNewDeskButtonWidth = 36;
 
+constexpr int kZeroStateDefaultDeskButtonMinWidth = 56;
+
 }  // namespace
 
 // -----------------------------------------------------------------------------
@@ -48,8 +57,7 @@
   SetFocusPainter(nullptr);
   SetFocusBehavior(views::View::FocusBehavior::ACCESSIBLE_ONLY);
 
-  // TODO(minch): A11y of zero state.
-  SetAccessibleName(base::UTF8ToUTF16(GetClassName()));
+  SetAccessibleName(l10n_util::GetStringUTF16(IDS_ASH_DESKS_NEW_DESK_BUTTON));
 
   auto border = std::make_unique<WmHighlightItemBorder>(border_corder_radius);
   border_ptr_ = border.get();
@@ -137,13 +145,15 @@
 // -----------------------------------------------------------------------------
 // ZeroStateDefaultDeskButton:
 
-// TODO(minch): Show the first desk's current name instead of the default name.
 ZeroStateDefaultDeskButton::ZeroStateDefaultDeskButton(DesksBarView* bar_view)
-    : DeskButtonBase(
-          l10n_util::GetStringUTF16(IDS_ASH_DESKS_DESK_1_MINI_VIEW_TITLE),
-          kCornerRadius,
-          kCornerRadius),
-      bar_view_(bar_view) {}
+    : DeskButtonBase(DesksController::Get()->desks()[0]->name(),
+                     kCornerRadius,
+                     kCornerRadius),
+      bar_view_(bar_view) {
+  GetViewAccessibility().OverrideName(
+      l10n_util::GetStringFUTF16(IDS_ASH_DESKS_DESK_ACCESSIBLE_NAME,
+                                 DesksController::Get()->desks()[0]->name()));
+}
 
 const char* ZeroStateDefaultDeskButton::GetClassName() const {
   return "ZeroStateDefaultDeskButton";
@@ -156,10 +166,19 @@
 }
 
 gfx::Size ZeroStateDefaultDeskButton::CalculatePreferredSize() const {
-  const gfx::Size label_size = label()->GetPreferredSize();
-  return gfx::Size(
-      label_size.width() + 2 * kZeroStateDefaultButtonHorizontalPadding,
-      kZeroStateButtonHeight);
+  auto* root_window =
+      bar_view_->GetWidget()->GetNativeWindow()->GetRootWindow();
+  const int preview_width = DeskMiniView::GetPreviewWidth(
+      root_window->bounds().size(),
+      DeskPreviewView::GetHeight(root_window, /*compact=*/false));
+  int label_width = 0, label_height = 0;
+  gfx::Canvas::SizeStringInt(DesksController::Get()->desks()[0]->name(),
+                             gfx::FontList(), &label_width, &label_height, 0,
+                             gfx::Canvas::NO_ELLIPSIS);
+  const int width = base::ClampToRange(
+      label_width + 2 * kZeroStateDefaultButtonHorizontalPadding,
+      kZeroStateDefaultDeskButtonMinWidth, preview_width);
+  return gfx::Size(width, kZeroStateButtonHeight);
 }
 
 void ZeroStateDefaultDeskButton::OnButtonPressed() {
@@ -167,6 +186,13 @@
                                 /*expanding_bar_view=*/true);
 }
 
+void ZeroStateDefaultDeskButton::UpdateLabelText() {
+  SetText(gfx::ElideText(
+      DesksController::Get()->desks()[0]->name(), gfx::FontList(),
+      bounds().width() - 2 * kZeroStateDefaultButtonHorizontalPadding,
+      gfx::ELIDE_TAIL));
+}
+
 // -----------------------------------------------------------------------------
 // ZeroStateNewDeskButton:
 
diff --git a/ash/wm/desks/zero_state_button.h b/ash/wm/desks/zero_state_button.h
index 15db654..6a67487 100644
--- a/ash/wm/desks/zero_state_button.h
+++ b/ash/wm/desks/zero_state_button.h
@@ -44,6 +44,10 @@
 
   virtual void UpdateButtonState() {}
 
+  // Updates the label's text of the button. E.g, ZeroStateDefaultDeskButton
+  // showing the desk's name, which should be updated on desk name changes.
+  virtual void UpdateLabelText() {}
+
   SkColor GetBackgroundColorForTesting() const { return background_color_; }
 
  protected:
@@ -90,6 +94,7 @@
   void OnThemeChanged() override;
   gfx::Size CalculatePreferredSize() const override;
   void OnButtonPressed() override;
+  void UpdateLabelText() override;
 
  private:
   DesksBarView* bar_view_;
diff --git a/base/memory/shared_memory_security_policy.h b/base/memory/shared_memory_security_policy.h
index e581188..bf356bb 100644
--- a/base/memory/shared_memory_security_policy.h
+++ b/base/memory/shared_memory_security_policy.h
@@ -7,15 +7,8 @@
 
 #include <stddef.h>
 
-#include "base/base_export.h"
 #include "base/compiler_specific.h"
 
-namespace mojo {
-namespace core {
-class ChannelLinux;
-}  // namespace core
-}  // namespace mojo
-
 namespace base {
 
 namespace subtle {
@@ -26,11 +19,10 @@
 // mapped. This can help prevent an attacker from spraying the address space of
 // a process with shared memory mappings to bypass ASLR. For more details, see
 // https://googleprojectzero.blogspot.com/2019/04/virtually-unlimited-memory-escaping.html
-class BASE_EXPORT SharedMemorySecurityPolicy {
+class SharedMemorySecurityPolicy {
  private:
   friend class subtle::PlatformSharedMemoryRegion;
   friend class SharedMemoryMapping;
-  friend class mojo::core::ChannelLinux;
 
   // Checks that a mapping with |size| can be created. Returns false if there is
   // an overflow in internal calculations, or the max limit has been reached.
diff --git a/base/threading/hang_watcher.cc b/base/threading/hang_watcher.cc
index 4d1b7e2..cc7f0f3 100644
--- a/base/threading/hang_watcher.cc
+++ b/base/threading/hang_watcher.cc
@@ -491,9 +491,7 @@
 // static
 void HangWatcher::RecordHang() {
   base::debug::DumpWithoutCrashing();
-  // Inhibit code folding.
-  const int line_number = __LINE__;
-  base::debug::Alias(&line_number);
+  NO_CODE_FOLDING();
 }
 
 ScopedClosureRunner HangWatcher::RegisterThreadInternal(
diff --git a/build/chromeos/test_runner.py b/build/chromeos/test_runner.py
index 1628d3f..8818677 100755
--- a/build/chromeos/test_runner.py
+++ b/build/chromeos/test_runner.py
@@ -22,6 +22,7 @@
 import dateutil.parser  # pylint: disable=import-error
 import jsonlines  # pylint: disable=import-error
 import psutil  # pylint: disable=import-error
+import six
 
 CHROMIUM_SRC_PATH = os.path.abspath(
     os.path.join(os.path.dirname(__file__), '..', '..'))
@@ -33,7 +34,10 @@
 from pylib.base import result_sink  # pylint: disable=import-error
 from pylib.results import json_results  # pylint: disable=import-error
 
-import subprocess32 as subprocess  # pylint: disable=import-error
+if six.PY2:
+  import subprocess32 as subprocess  # pylint: disable=import-error
+else:
+  import subprocess  # pylint: disable=import-error,wrong-import-order
 
 DEFAULT_CROS_CACHE = os.path.abspath(
     os.path.join(CHROMIUM_SRC_PATH, 'build', 'cros_cache'))
@@ -161,7 +165,7 @@
     logging.info('Running the following command on the device:')
     logging.info('\n' + '\n'.join(script_contents))
     fd, tmp_path = tempfile.mkstemp(suffix='.sh', dir=self._path_to_outdir)
-    os.fchmod(fd, 0755)
+    os.fchmod(fd, 0o755)
     with os.fdopen(fd, 'wb') as f:
       f.write('\n'.join(script_contents) + '\n')
     return tmp_path
@@ -186,7 +190,7 @@
 
     signal.signal(signal.SIGTERM, _kill_child_procs)
 
-    for i in xrange(self._retries + 1):
+    for i in range(self._retries + 1):
       logging.info('########################################')
       logging.info('Test attempt #%d', i)
       logging.info('########################################')
@@ -197,13 +201,13 @@
           env=self._test_env)
       try:
         test_proc.wait(timeout=self._timeout)
-      except subprocess.TimeoutExpired:
+      except subprocess.TimeoutExpired:  # pylint: disable=no-member
         logging.error('Test timed out. Sending SIGTERM.')
         # SIGTERM the proc and wait 10s for it to close.
         test_proc.terminate()
         try:
           test_proc.wait(timeout=10)
-        except subprocess.TimeoutExpired:
+        except subprocess.TimeoutExpired:  # pylint: disable=no-member
           # If it hasn't closed in 10s, SIGKILL it.
           logging.error('Test did not exit in time. Sending SIGKILL.')
           test_proc.kill()
diff --git a/build/chromeos/test_runner_test.py b/build/chromeos/test_runner_test.py
index 03cc0dc..9e877824b 100755
--- a/build/chromeos/test_runner_test.py
+++ b/build/chromeos/test_runner_test.py
@@ -14,6 +14,7 @@
 # //.vpython
 import mock  # pylint: disable=import-error
 from parameterized import parameterized  # pylint: disable=import-error
+import six
 
 import test_runner
 
@@ -80,6 +81,16 @@
       ]
     return expectation
 
+  def safeAssertItemsEqual(self, list1, list2):
+    """A Py3 safe version of assertItemsEqual.
+
+    See https://bugs.python.org/issue17866.
+    """
+    if six.PY3:
+      self.assertSetEqual(set(list1), set(list2))
+    else:
+      self.assertItemsEqual(list1, list2)
+
   @parameterized.expand([
       [True],
       [False],
@@ -116,7 +127,7 @@
       expected_cmd.extend(['--start', '--copy-on-write']
                           if use_vm else ['--device', 'localhost:2222'])
       expected_cmd.extend(['--', './device_script.sh'])
-      self.assertItemsEqual(expected_cmd, mock_popen.call_args[0][0])
+      self.safeAssertItemsEqual(expected_cmd, mock_popen.call_args[0][0])
 
       fd_mock().write.assert_called_once_with(
           '#!/bin/sh\nexport HOME=/usr/local/tmp\n'
@@ -146,7 +157,7 @@
           '--tast', 'ui.ChromeLogin'
       ]
 
-      self.assertItemsEqual(expected_cmd, mock_popen.call_args[0][0])
+      self.safeAssertItemsEqual(expected_cmd, mock_popen.call_args[0][0])
 
   @parameterized.expand([
       [True],
@@ -169,7 +180,7 @@
           '--tast=( "group:mainline" && "dep:chrome" && !informational)',
       ]
 
-      self.assertItemsEqual(expected_cmd, mock_popen.call_args[0][0])
+      self.safeAssertItemsEqual(expected_cmd, mock_popen.call_args[0][0])
 
   @parameterized.expand([
       [True],
@@ -197,7 +208,7 @@
               '--deploy-lacros',
           ]
 
-      self.assertItemsEqual(expected_cmd, mock_popen.call_args[0][0])
+      self.safeAssertItemsEqual(expected_cmd, mock_popen.call_args[0][0])
 
   @parameterized.expand([
       [True],
@@ -220,7 +231,7 @@
           '--tast', 'ui.ChromeLogin', '--tast-var', 'key=value'
       ]
 
-      self.assertItemsEqual(expected_cmd, mock_popen.call_args[0][0])
+      self.safeAssertItemsEqual(expected_cmd, mock_popen.call_args[0][0])
 
 
 if __name__ == '__main__':
diff --git a/build/config/fuchsia/add_DebugData_service.test-cmx b/build/config/fuchsia/add_DebugData_service.test-cmx
new file mode 100644
index 0000000..33fb6b0
--- /dev/null
+++ b/build/config/fuchsia/add_DebugData_service.test-cmx
@@ -0,0 +1,7 @@
+{
+  "sandbox": {
+    "services": [
+      "fuchsia.debugdata.DebugData"
+    ]
+  }
+}
\ No newline at end of file
diff --git a/build/config/fuchsia/package.gni b/build/config/fuchsia/package.gni
index 51ebad63..f69be31 100644
--- a/build/config/fuchsia/package.gni
+++ b/build/config/fuchsia/package.gni
@@ -49,25 +49,20 @@
 
   # Process the CMX fragment in |manifest| to get a full manifest.
   action(_component_cmx_target) {
-    forward_variables_from(invoker, [ "testonly" ])
-
-    if (!defined(invoker.manifest)) {
-      assert(testonly == true)
-
-      # TODO(1019938): switch the default to tests.cmx which doesn't request
-      # the deprecated-ambient-replace-as-executable feature.
-      _manifest_fragment = "//build/config/fuchsia/tests-with-exec.cmx"
-    } else {
-      _manifest_fragment = invoker.manifest
-    }
+    forward_variables_from(invoker,
+                           [
+                             "deps",
+                             "testonly",
+                           ])
 
     script = "//build/config/fuchsia/build_cmx_from_fragment.py"
-    inputs = [ _manifest_fragment ]
+
+    inputs = [ invoker.manifest ]
     outputs = [ _component_manifest ]
 
     args = [
       "--cmx-fragment",
-      rebase_path(_manifest_fragment),
+      rebase_path(invoker.manifest),
       "--cmx",
       rebase_path(_component_manifest),
       "--program",
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index 6a039ec..1d827f0 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-0.20210128.1.1
+0.20210128.2.2
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index 6a039ec..69de15c 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-0.20210128.1.1
+0.20210128.2.1
diff --git a/chrome/android/expectations/lint-suppressions.xml b/chrome/android/expectations/lint-suppressions.xml
index c815938..b6c9e06 100644
--- a/chrome/android/expectations/lint-suppressions.xml
+++ b/chrome/android/expectations/lint-suppressions.xml
@@ -274,8 +274,6 @@
     <ignore regexp="The resource `R.string.ntp_article_suggestions_section_empty` appears to be unused"/>
     <!-- 1: TODO(crbug.com/1137985) resource is used in downstream image editor. -->
     <ignore regexp="The resource `R.string.clear` appears to be unused"/>
-    <!-- 1: Temporarily suppressed until impelmentation is ready, see https://crbug.com/1166704. -->
-    <ignore regexp="The resource `R.string.price_drop_spotted_lower_price` appears to be unused"/>
     <!-- 1: Temporarily suppressed until impelmentation is ready, see https://crbug.com/1169845. -->
     <ignore regexp="The resource `R.string.notification_category_price_drop` appears to be unused"/>
     <!-- 4: Temporarily suppressed until impelmentation is ready, see https://crbug.com/1166702 -->
@@ -283,12 +281,6 @@
     <ignore regexp="The resource `R.string.price_drop_alerts_card_content` appears to be unused"/>
     <ignore regexp="The resource `R.string.price_drop_alerts_card_settings` appears to be unused"/>
     <ignore regexp="The resource `R.string.price_drop_alerts_card_get_notified` appears to be unused"/>
-    <!-- 5: Temporarily suppressed until impelmentation is ready, see https://crbug.com/1163136 -->
-    <ignore regexp="The resource `R.string.price_tracking_settings` appears to be unused"/>
-    <ignore regexp="The resource `R.string.track_prices_on_tabs` appears to be unused"/>
-    <ignore regexp="The resource `R.string.track_prices_on_tabs_description` appears to be unused"/>
-    <ignore regexp="The resource `R.string.price_drop_alerts` appears to be unused"/>
-    <ignore regexp="The resource `R.string.price_drop_alerts_description` appears to be unused"/>
     <!-- Endnote: Please specify number of suppressions when adding more -->
   </issue>
   <issue id="VectorPath" severity="ignore"/>
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/PriceWelcomeMessageCardView.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/PriceWelcomeMessageCardView.java
index f16e419..93bb887d 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/PriceWelcomeMessageCardView.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/PriceWelcomeMessageCardView.java
@@ -8,13 +8,17 @@
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
 import android.util.AttributeSet;
+import android.view.View;
 import android.widget.FrameLayout;
 import android.widget.TextView;
 
 import org.chromium.chrome.browser.tab.state.ShoppingPersistedTabData;
+import org.chromium.chrome.browser.util.ChromeAccessibilityUtil;
 import org.chromium.chrome.tab_ui.R;
+import org.chromium.components.browser_ui.widget.textbubble.TextBubble;
 import org.chromium.ui.widget.ButtonCompat;
 import org.chromium.ui.widget.ChromeImageView;
+import org.chromium.ui.widget.ViewRectProvider;
 
 import java.lang.ref.WeakReference;
 
@@ -105,4 +109,19 @@
     void setPriceInfoBoxStrings(ShoppingPersistedTabData.PriceDrop priceDrop) {
         mPriceInfoBox.setPriceStrings(priceDrop.price, priceDrop.previousPrice);
     }
+
+    // TODO(crbug.com/1166704): This method has little to do with this view. Move this function to a
+    // price tracking UI utility class.
+    /**
+     * When user taps on "Show me" on PriceWelcomeMessage, we scroll them to the binding tab, then a
+     * blue tooltip appears and points to the price drop indicator.
+     */
+    public static void showPriceDropTooltip(View view) {
+        ViewRectProvider rectProvider = new ViewRectProvider(view);
+        TextBubble textBubble = new TextBubble(view.getContext(), view,
+                R.string.price_drop_spotted_lower_price, R.string.price_drop_spotted_lower_price,
+                true, rectProvider, ChromeAccessibilityUtil.get().isAccessibilityEnabled());
+        textBubble.setDismissOnTouchInteraction(true);
+        textBubble.show();
+    }
 }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/PriceWelcomeMessageService.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/PriceWelcomeMessageService.java
index c70c6d7..3c18079 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/PriceWelcomeMessageService.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/PriceWelcomeMessageService.java
@@ -60,6 +60,14 @@
          * @return the index within the {@link TabListModel}.
          */
         int getTabIndexFromTabId(int tabId);
+
+        /**
+         * This method updates {@link TabProperties#SHOULD_SHOW_PRICE_DROP_TOOLTIP} of the binding
+         * tab.
+         *
+         * @param index The binding tab index in {@link TabListModel}.
+         */
+        void showPriceDropTooltip(int index);
     }
 
     /**
@@ -170,8 +178,10 @@
     @VisibleForTesting
     public void review() {
         assert mPriceTabData != null;
-        mPriceWelcomeMessageReviewActionProvider.scrollToBindingTab(
-                mPriceWelcomeMessageProvider.getTabIndexFromTabId(mPriceTabData.bindingTabId));
+        int bindingTabIndex =
+                mPriceWelcomeMessageProvider.getTabIndexFromTabId(mPriceTabData.bindingTabId);
+        mPriceWelcomeMessageReviewActionProvider.scrollToBindingTab(bindingTabIndex);
+        mPriceWelcomeMessageProvider.showPriceDropTooltip(bindingTabIndex);
         PriceTrackingUtilities.disablePriceWelcomeMessageCard();
     }
 
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridItemTouchHelperCallback.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridItemTouchHelperCallback.java
index 22e467d..2441cb8 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridItemTouchHelperCallback.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridItemTouchHelperCallback.java
@@ -144,7 +144,8 @@
         TabModel tabModel = mTabModelSelector.getCurrentModel();
         if (filter instanceof EmptyTabModelFilter) {
             tabModel.moveTab(currentTabId,
-                    mModel.indexFromId(currentTabId) + (distance > 0 ? distance + 1 : distance));
+                    mModel.getTabCardCountsBefore(mModel.indexFromId(currentTabId)
+                            + (distance > 0 ? distance + 1 : distance)));
         } else if (!mActionsOnAllRelatedTabs) {
             int destinationIndex = tabModel.indexOf(mTabModelSelector.getTabById(destinationTabId));
             tabModel.moveTab(currentTabId, distance > 0 ? destinationIndex + 1 : destinationIndex);
@@ -202,7 +203,8 @@
                 if (selectedViewHolder != null && !mRecyclerView.isComputingLayout()
                         && shouldUpdate) {
                     View selectedItemView = selectedViewHolder.itemView;
-                    onTabMergeToGroup(mSelectedTabIndex, mHoveredTabIndex);
+                    onTabMergeToGroup(mModel.getTabCardCountsBefore(mSelectedTabIndex),
+                            mModel.getTabCardCountsBefore(mHoveredTabIndex));
                     mRecyclerView.getLayoutManager().removeView(selectedItemView);
                 }
             } else {
@@ -212,7 +214,7 @@
             if (mHoveredTabIndex != TabModel.INVALID_TAB_INDEX && shouldUpdate) {
                 mModel.updateHoveredTabForMergeToGroup(mSelectedTabIndex > mHoveredTabIndex
                                 ? mHoveredTabIndex
-                                : mHoveredTabIndex - 1,
+                                : mModel.getTabIndexBefore(mHoveredTabIndex),
                         false);
             }
             if (mUnGroupTabIndex != TabModel.INVALID_TAB_INDEX) {
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewBinder.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewBinder.java
index 235735c..a49a25e 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewBinder.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewBinder.java
@@ -241,6 +241,14 @@
                 // TODO(crbug.com/1157578): Update the model in mediator.
                 model.set(TabProperties.PRICE_DROP, null);
             }
+        } else if (TabProperties.SHOULD_SHOW_PRICE_DROP_TOOLTIP == propertyKey) {
+            if (model.get(TabProperties.SHOULD_SHOW_PRICE_DROP_TOOLTIP)) {
+                PriceCardView priceCardView =
+                        (PriceCardView) view.fastFindViewById(R.id.price_info_box_outer);
+                assert priceCardView.getVisibility() == View.VISIBLE;
+                PriceWelcomeMessageCardView.showPriceDropTooltip(
+                        priceCardView.findViewById(R.id.current_price));
+            }
         } else if (TabProperties.PAGE_INFO_LISTENER == propertyKey) {
             TabListMediator.TabActionListener listener =
                     model.get(TabProperties.PAGE_INFO_LISTENER);
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListCoordinator.java
index 878c35b..8a19869 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListCoordinator.java
@@ -341,6 +341,13 @@
     }
 
     /**
+     * @see TabListMediator#getPriceWelcomeMessageInsertionIndex().
+     */
+    int getPriceWelcomeMessageInsertionIndex() {
+        return mMediator.getPriceWelcomeMessageInsertionIndex();
+    }
+
+    /**
      * @return The container {@link androidx.recyclerview.widget.RecyclerView} that is showing the
      *         tab list UI.
      */
@@ -457,4 +464,9 @@
     public int getTabIndexFromTabId(int tabId) {
         return mModel.indexFromId(tabId);
     }
+
+    @Override
+    public void showPriceDropTooltip(int index) {
+        mModel.get(index).model.set(TabProperties.SHOULD_SHOW_PRICE_DROP_TOOLTIP, true);
+    }
 }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java
index f92ea2e..f3d15cf 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java
@@ -74,6 +74,8 @@
 import org.chromium.content_public.browser.NavigationHistory;
 import org.chromium.content_public.browser.UiThreadTaskTraits;
 import org.chromium.ui.base.PageTransition;
+import org.chromium.ui.modelutil.ListObservable;
+import org.chromium.ui.modelutil.ListObservable.ListObserver;
 import org.chromium.ui.modelutil.PropertyModel;
 import org.chromium.ui.modelutil.SimpleRecyclerViewAdapter;
 import org.chromium.url.GURL;
@@ -297,6 +299,7 @@
     private int mNextTabId = Tab.INVALID_TAB_ID;
     private @UiType int mUiType;
     private int mSearchChipIconDrawableId;
+    private GridLayoutManager mGridLayoutManager;
 
     private final TabActionListener mTabSelectedListener = new TabActionListener() {
         @Override
@@ -431,6 +434,8 @@
 
     private final TabModelObserver mTabModelObserver;
 
+    private ListObserver<Void> mListObserver;
+
     private @Nullable TemplateUrlService.TemplateUrlServiceObserver mTemplateUrlObserver;
 
     private TabGroupTitleEditor mTabGroupTitleEditor;
@@ -534,18 +539,20 @@
                 if (mActionsOnAllRelatedTabs) {
                     TabModelFilter filter = mTabModelSelector.getTabModelFilterProvider()
                                                     .getCurrentTabModelFilter();
-                    int index = filter.indexOf(tab);
-                    if (index == TabList.INVALID_TAB_INDEX
+                    int filterIndex = filter.indexOf(tab);
+                    if (filterIndex == TabList.INVALID_TAB_INDEX
                             || getRelatedTabsForId(tab.getId()).size() == 1
-                            || index >= mModel.size()) {
+                            || filterIndex >= mModel.size()) {
                         return;
                     }
-                    Tab currentGroupSelectedTab = filter.getTabAt(index);
+                    Tab currentGroupSelectedTab = filter.getTabAt(filterIndex);
 
-                    assert mModel.indexFromId(currentGroupSelectedTab.getId()) == index;
+                    int tabListModelIndex = mModel.indexOfNthTabCard(filterIndex);
+                    assert mModel.indexFromId(currentGroupSelectedTab.getId()) == tabListModelIndex;
 
-                    updateTab(index, PseudoTab.fromTab(currentGroupSelectedTab),
-                            mModel.get(index).model.get(TabProperties.IS_SELECTED), false, false);
+                    updateTab(tabListModelIndex, PseudoTab.fromTab(currentGroupSelectedTab),
+                            mModel.get(tabListModelIndex).model.get(TabProperties.IS_SELECTED),
+                            false, false);
                 }
             }
 
@@ -560,17 +567,19 @@
                     // property models.
                     TabModelFilter filter = mTabModelSelector.getTabModelFilterProvider()
                                                     .getCurrentTabModelFilter();
-                    int index = filter.indexOf(tab);
-                    if (index == TabList.INVALID_TAB_INDEX) return;
-                    Tab currentGroupSelectedTab = filter.getTabAt(index);
+                    int filterIndex = filter.indexOf(tab);
+                    if (filterIndex == TabList.INVALID_TAB_INDEX) return;
+                    Tab currentGroupSelectedTab = filter.getTabAt(filterIndex);
                     // TabModel and TabListModel may be in the process of syncing up through
                     // restoring. Examples of this situation are switching between light/dark mode
                     // in incognito, exiting multi-window mode, etc.
-                    if (mModel.indexFromId(currentGroupSelectedTab.getId()) != index) {
+                    int tabListModelIndex = mModel.indexOfNthTabCard(filterIndex);
+                    if (mModel.indexFromId(currentGroupSelectedTab.getId()) != tabListModelIndex) {
                         return;
                     }
-                    updateTab(index, PseudoTab.fromTab(currentGroupSelectedTab),
-                            mModel.get(index).model.get(TabProperties.IS_SELECTED), false, false);
+                    updateTab(tabListModelIndex, PseudoTab.fromTab(currentGroupSelectedTab),
+                            mModel.get(tabListModelIndex).model.get(TabProperties.IS_SELECTED),
+                            false, false);
                 }
             }
 
@@ -587,7 +596,7 @@
                                 instanceof TabGroupModelFilter) {
                     return;
                 }
-                onTabMoved(newIndex, curIndex);
+                onTabMoved(mModel.indexOfNthTabCard(newIndex), mModel.indexOfNthTabCard(curIndex));
             }
 
             @Override
@@ -637,12 +646,12 @@
 
                 int nextTabId = Tab.INVALID_TAB_ID;
                 if (mModel.size() > 1) {
-                    PropertyModel nextCardModel = closingTabIndex == 0
-                            ? mModel.get(closingTabIndex + 1).model
-                            : mModel.get(closingTabIndex - 1).model;
-                    nextTabId = nextCardModel.get(CARD_TYPE) == TAB
-                            ? nextCardModel.get(TabProperties.TAB_ID)
-                            : Tab.INVALID_TAB_ID;
+                    int nextTabIndex = closingTabIndex == 0
+                            ? mModel.getTabIndexAfter(closingTabIndex)
+                            : mModel.getTabIndexBefore(closingTabIndex);
+                    nextTabId = nextTabIndex == TabModel.INVALID_TAB_INDEX
+                            ? Tab.INVALID_TAB_ID
+                            : mModel.get(nextTabIndex).model.get(TabProperties.TAB_ID);
                 }
 
                 return TabModelUtils.getTabById(mTabModelSelector.getCurrentModel(), nextTabId);
@@ -652,6 +661,35 @@
         mTabGridItemTouchHelperCallback =
                 new TabGridItemTouchHelperCallback(mModel, mTabModelSelector, mTabClosedListener,
                         mTabGridDialogHandler, mComponentName, mActionsOnAllRelatedTabs);
+
+        // Right now we need to update layout only if there is a price welcome message card in tab
+        // switcher.
+        if (mMode == TabListMode.GRID && mUiType != UiType.SELECTABLE
+                && TabUiFeatureUtilities.isPriceTrackingEnabled()) {
+            mListObserver = new ListObserver<Void>() {
+                @Override
+                public void onItemRangeInserted(ListObservable source, int index, int count) {
+                    updateLayout();
+                }
+
+                @Override
+                public void onItemRangeRemoved(ListObservable source, int index, int count) {
+                    updateLayout();
+                }
+
+                @Override
+                public void onItemRangeChanged(
+                        ListObservable<Void> source, int index, int count, @Nullable Void payload) {
+                    updateLayout();
+                }
+
+                @Override
+                public void onItemMoved(ListObservable source, int curIndex, int newIndex) {
+                    updateLayout();
+                }
+            };
+            mModel.addObserver(mListObserver);
+        }
     }
 
     public void initWithNative(Profile profile) {
@@ -693,21 +731,22 @@
                             return;
                         }
                         Tab currentSelectedTab = mTabModelSelector.getCurrentTab();
-                        int index = TabModelUtils.getTabIndexById(
+                        int filterIndex = TabModelUtils.getTabIndexById(
                                 mTabModelSelector.getTabModelFilterProvider()
                                         .getCurrentTabModelFilter(),
                                 movedTab.getId());
-                        addTabInfoToModel(PseudoTab.fromTab(movedTab), index,
+                        addTabInfoToModel(PseudoTab.fromTab(movedTab),
+                                mModel.indexOfNthTabCard(filterIndex),
                                 currentSelectedTab.getId() == movedTab.getId());
                         boolean isSelected = mTabModelSelector.getCurrentTabId()
                                 == filter.getTabAt(prevFilterIndex).getId();
-                        updateTab(prevFilterIndex,
+                        updateTab(mModel.indexOfNthTabCard(prevFilterIndex),
                                 PseudoTab.fromTab(filter.getTabAt(prevFilterIndex)), isSelected,
                                 true, false);
                     } else {
-                        int curIndex = mModel.indexFromId(movedTab.getId());
-                        if (!isValidMovePosition(curIndex)) return;
-                        mModel.removeAt(curIndex);
+                        int curTabListModelIndex = mModel.indexFromId(movedTab.getId());
+                        if (!isValidMovePosition(curTabListModelIndex)) return;
+                        mModel.removeAt(curTabListModelIndex);
                         if (mTabGridDialogHandler != null) {
                             mTabGridDialogHandler.updateDialogContent(isUngroupingLastTabInGroup
                                             ? Tab.INVALID_TAB_ID
@@ -734,10 +773,10 @@
                         RecordUserAction.record("TabGrid.Drag.DropToMerge");
                     }
 
-                    desIndex = srcIndex > desIndex ? desIndex : desIndex - 1;
+                    desIndex = srcIndex > desIndex ? desIndex : mModel.getTabIndexBefore(desIndex);
                     Tab newSelectedTab = mTabModelSelector.getTabModelFilterProvider()
                                                  .getCurrentTabModelFilter()
-                                                 .getTabAt(desIndex);
+                                                 .getTabAt(mModel.getTabCardCountsBefore(desIndex));
                     boolean isSelected = mTabModelSelector.getCurrentTab() == newSelectedTab;
                     updateTab(desIndex, PseudoTab.fromTab(newSelectedTab), isSelected, true, false);
                 }
@@ -756,7 +795,8 @@
                     int curPosition = mModel.indexFromId(currentGroupSelectedTab.getId());
                     if (curPosition == TabModel.INVALID_TAB_INDEX) {
                         // Sync TabListModel with updated TabGroupModelFilter.
-                        int indexToUpdate = filter.indexOf(tabModel.getTabAt(tabModelOldIndex));
+                        int indexToUpdate = mModel.indexOfNthTabCard(
+                                filter.indexOf(tabModel.getTabAt(tabModelOldIndex)));
                         mModel.updateTabListModelIdForGroup(currentGroupSelectedTab, indexToUpdate);
                         curPosition = mModel.indexFromId(currentGroupSelectedTab.getId());
                     }
@@ -772,8 +812,8 @@
                             mTabModelSelector, destinationTab);
                     int newPosition = mModel.indexFromId(destinationGroupSelectedTab.getId());
                     if (newPosition == TabModel.INVALID_TAB_INDEX) {
-                        int indexToUpdate = filter.indexOf(destinationTab)
-                                + (tabModelNewIndex > tabModelOldIndex ? 1 : -1);
+                        int indexToUpdate = mModel.indexOfNthTabCard(filter.indexOf(destinationTab)
+                                + (tabModelNewIndex > tabModelOldIndex ? 1 : -1));
                         mModel.updateTabListModelIdForGroup(
                                 destinationGroupSelectedTab, indexToUpdate);
                         newPosition = mModel.indexFromId(destinationGroupSelectedTab.getId());
@@ -882,9 +922,9 @@
             index = related.indexOf(tab);
             if (index == -1) return TabList.INVALID_TAB_INDEX;
         } else {
-            index = TabModelUtils.getTabIndexById(
+            index = mModel.indexOfNthTabCard(TabModelUtils.getTabIndexById(
                     mTabModelSelector.getTabModelFilterProvider().getCurrentTabModelFilter(),
-                    tab.getId());
+                    tab.getId()));
             // TODO(wychen): the title (tab count in the group) is wrong when it's not the last
             //  tab added in the group.
         }
@@ -947,9 +987,11 @@
             return tabsCount == 0;
         }
         if (tabs.size() != tabsCount) return false;
-        for (int i = 0; i < tabs.size(); i++) {
+        int tabsIndex = 0;
+        for (int i = 0; i < mModel.size(); i++) {
             if (mModel.get(i).model.get(CARD_TYPE) == TAB
-                    && mModel.get(i).model.get(TabProperties.TAB_ID) != tabs.get(i).getId()) {
+                    && mModel.get(i).model.get(TabProperties.TAB_ID)
+                            != tabs.get(tabsIndex++).getId()) {
                 return false;
             }
         }
@@ -977,7 +1019,7 @@
             for (int i = 0; i < tabsList.size(); i++) {
                 PseudoTab tab = tabsList.get(i);
                 boolean isSelected = mTabModelSelector.getCurrentTabId() == tab.getId();
-                updateTab(i, tab, isSelected, false, quickMode);
+                updateTab(mModel.indexOfNthTabCard(i), tab, isSelected, false, quickMode);
             }
             return true;
         }
@@ -1048,6 +1090,7 @@
         }
         mModel.get(index).model.set(TabProperties.TAB_SELECTED_LISTENER, tabSelectedListener);
         mModel.get(index).model.set(TabProperties.IS_SELECTED, isSelected);
+        mModel.get(index).model.set(TabProperties.SHOULD_SHOW_PRICE_DROP_TOOLTIP, false);
         mModel.get(index).model.set(TabProperties.TITLE, getLatestTitleForTab(pseudoTab));
         mModel.get(index).model.set(
                 TabProperties.TAB_CLOSED_LISTENER, isRealTab ? mTabClosedListener : null);
@@ -1107,12 +1150,14 @@
             @Override
             public void onConfigurationChanged(Configuration newConfig) {
                 updateSpanCountForOrientation(manager, newConfig.orientation);
+                if (mMode == TabListMode.GRID && mUiType != UiType.SELECTABLE) updateLayout();
             }
 
             @Override
             public void onLowMemory() {}
         };
         mContext.registerComponentCallbacks(mComponentCallbacks);
+        mGridLayoutManager = manager;
     }
 
     /**
@@ -1190,6 +1235,9 @@
      * Destroy any members that needs clean up.
      */
     public void destroy() {
+        if (mListObserver != null) {
+            mModel.removeObserver(mListObserver);
+        }
         TabModel tabModel = mTabModelSelector.getCurrentModel();
         if (tabModel != null) {
             for (int i = 0; i < tabModel.getCount(); i++) {
@@ -1284,6 +1332,7 @@
                                 tabstripFaviconBackgroundDrawableId)
                         .with(TabProperties.ACCESSIBILITY_DELEGATE, mAccessibilityDelegate)
                         .with(TabProperties.PRICE_DROP, null)
+                        .with(TabProperties.SHOULD_SHOW_PRICE_DROP_TOOLTIP, false)
                         .with(CARD_TYPE, TAB)
                         .build();
 
@@ -1590,6 +1639,60 @@
         return false;
     }
 
+    /**
+     * The PriceWelcomeMessage should be in view when user enters the tab switcher, so we put it
+     * exactly below the currently selected tab.
+     *
+     * @return Where the PriceWelcomeMessage should be inserted in the {@link TabListModel} when
+     *         user enters the tab switcher.
+     */
+    int getPriceWelcomeMessageInsertionIndex() {
+        assert mGridLayoutManager != null;
+        int spanCount = mGridLayoutManager.getSpanCount();
+        int selectedTabIndex = mModel.getIndexForSelectedTab();
+        int indexBelowSelectedTab = (selectedTabIndex / spanCount + 1) * spanCount;
+        int indexAfterLastTab = mModel.getTabIndexBefore(mModel.size()) + 1;
+        return Math.min(indexBelowSelectedTab, indexAfterLastTab);
+    }
+
+    /**
+     * Update the layout of tab switcher to make it compact. Because now we have messages within the
+     * tabs like PriceWelcomeMessage and these messages take up the entire row, some operations like
+     * closing a tab above the message card will leave a blank grid, so we need to update the
+     * layout.
+     */
+    @VisibleForTesting
+    void updateLayout() {
+        // Right now we need to update layout only if there is a price welcome message card in tab
+        // switcher.
+        if (PriceTrackingUtilities.isPriceWelcomeMessageCardDisabled()) return;
+        assert mGridLayoutManager != null;
+        int spanCount = mGridLayoutManager.getSpanCount();
+        GridLayoutManager.SpanSizeLookup spanSizeLookup = mGridLayoutManager.getSpanSizeLookup();
+        int spanSizeSumForCurrentRow = 0;
+        int index = 0;
+        for (; index < mModel.size(); index++) {
+            spanSizeSumForCurrentRow += spanSizeLookup.getSpanSize(index);
+            if (spanSizeSumForCurrentRow == spanCount) {
+                // This row is compact, we clear and recount the spanSize for next row.
+                spanSizeSumForCurrentRow = 0;
+            } else if (spanSizeSumForCurrentRow > spanCount) {
+                // Find a blank grid and break.
+                if (mModel.get(index).type == TabProperties.UiType.PRICE_WELCOME) break;
+                spanSizeSumForCurrentRow = 0;
+            }
+        }
+        if (spanSizeSumForCurrentRow <= spanCount) return;
+        int blankSize = spanCount - (spanSizeSumForCurrentRow - spanSizeLookup.getSpanSize(index));
+        for (int i = index + 1; i < mModel.size(); i++) {
+            if (spanSizeLookup.getSpanSize(i) > blankSize) continue;
+            mModel.move(i, index);
+            // We should return after one move because once item moved, updateLayout() will be
+            // called again.
+            return;
+        }
+    }
+
     @VisibleForTesting
     View.AccessibilityDelegate getAccessibilityDelegateForTesting() {
         return mAccessibilityDelegate;
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListModel.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListModel.java
index bd0ed3fa..e230557 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListModel.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListModel.java
@@ -71,6 +71,88 @@
     }
 
     /**
+     * Find the Nth TAB card in the {@link TabListModel}.
+     * @param n N of the Nth TAB card.
+     * @return The index of Nth TAB card in the {@link TabListModel}.
+     */
+    public int indexOfNthTabCard(int n) {
+        if (n < 0) return TabModel.INVALID_TAB_INDEX;
+        int tabCount = 0;
+        int lastTabIndex = TabModel.INVALID_TAB_INDEX;
+        for (int i = 0; i < size(); i++) {
+            PropertyModel model = get(i).model;
+            if (model.get(CARD_TYPE) == TAB) {
+                if (tabCount++ == n) return i;
+                lastTabIndex = i;
+            }
+        }
+        // If n >= tabCount, we return the index after the last tab. This is used when adding a new
+        // tab.
+        return lastTabIndex + 1;
+    }
+
+    /**
+     * Get the number of TAB cards before the given index in TabListModel.
+     * @param index The given index in TabListModel.
+     * @return The number of TAB cards before the given index.
+     */
+    public int getTabCardCountsBefore(int index) {
+        if (index < 0) return TabModel.INVALID_TAB_INDEX;
+        if (index > size()) index = size();
+        int tabCount = 0;
+        for (int i = 0; i < index; i++) {
+            if (get(i).model.get(CARD_TYPE) == TAB) tabCount++;
+        }
+        return tabCount;
+    }
+
+    /**
+     * Get the index of the last tab before the given index in TabListModel.
+     * @param index The given index in TabListModel.
+     * @return The index of the tab before the given index in TabListModel.
+     */
+    public int getTabIndexBefore(int index) {
+        for (int i = index - 1; i >= 0; i--) {
+            if (get(i).model.get(CARD_TYPE) == TAB) return i;
+        }
+        return TabModel.INVALID_TAB_INDEX;
+    }
+
+    /**
+     * Get the index of the first tab after the given index in TabListModel.
+     * @param index The given index in TabListModel.
+     * @return The index of the tab after the given index in TabListModel.
+     */
+    public int getTabIndexAfter(int index) {
+        for (int i = index + 1; i < size(); i++) {
+            if (get(i).model.get(CARD_TYPE) == TAB) return i;
+        }
+        return TabModel.INVALID_TAB_INDEX;
+    }
+
+    /**
+     * Get the index of currently selected tab in TabListModel.
+     * @return The index within the model.
+     */
+    public int getIndexForSelectedTab() {
+        int selectedTabCount = 0;
+        int tabCount = 0;
+        int firstSelectedTabIndex = TabModel.INVALID_TAB_INDEX;
+        for (int i = size() - 1; i >= 0; i--) {
+            PropertyModel model = get(i).model;
+            if (model.get(CARD_TYPE) != TAB) continue;
+            if (model.get(TabProperties.IS_SELECTED)) {
+                selectedTabCount++;
+                firstSelectedTabIndex = i;
+            }
+            tabCount++;
+        }
+        assert (selectedTabCount == 1 || tabCount == 0)
+            : "There should be exactly one selected tab or no tabs at all when calling this method";
+        return firstSelectedTabIndex;
+    }
+
+    /**
      * Get the index that matches a message item that has the given message type.
      * @param messageType The message type to match.
      * @return The index within the model.
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabProperties.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabProperties.java
index 2a00bd5..c27e915 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabProperties.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabProperties.java
@@ -123,6 +123,9 @@
     public static final WritableObjectPropertyKey<ShoppingPersistedTabData.PriceDrop> PRICE_DROP =
             new WritableObjectPropertyKey<>();
 
+    public static final WritableBooleanPropertyKey SHOULD_SHOW_PRICE_DROP_TOOLTIP =
+            new WritableBooleanPropertyKey();
+
     public static final PropertyKey[] ALL_KEYS_TAB_GRID = new PropertyKey[] {TAB_ID,
             TAB_SELECTED_LISTENER, TAB_CLOSED_LISTENER, FAVICON, THUMBNAIL_FETCHER, IPH_PROVIDER,
             TITLE, IS_SELECTED, CHECKED_DRAWABLE_STATE_LIST, CREATE_GROUP_LISTENER, CARD_ALPHA,
@@ -132,7 +135,7 @@
             SELECTABLE_TAB_ACTION_BUTTON_SELECTED_BACKGROUND, URL_DOMAIN, ACCESSIBILITY_DELEGATE,
             SEARCH_QUERY, PAGE_INFO_LISTENER, PAGE_INFO_ICON_DRAWABLE_ID, CARD_TYPE,
             CONTENT_DESCRIPTION_STRING, CLOSE_BUTTON_DESCRIPTION_STRING,
-            SHOPPING_PERSISTED_TAB_DATA_FETCHER, PRICE_DROP};
+            SHOPPING_PERSISTED_TAB_DATA_FETCHER, PRICE_DROP, SHOULD_SHOW_PRICE_DROP_TOOLTIP};
 
     public static final PropertyKey[] ALL_KEYS_TAB_STRIP =
             new PropertyKey[] {TAB_ID, TAB_SELECTED_LISTENER, TAB_CLOSED_LISTENER, FAVICON,
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherCoordinator.java
index 973d00a..9ff1c51 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherCoordinator.java
@@ -461,12 +461,10 @@
     public boolean resetWithTabs(
             @Nullable List<PseudoTab> tabs, boolean quickMode, boolean mruMode) {
         mMediator.registerFirstMeaningfulPaintRecorder();
-        // Make sure that before resetWithListOfTabs, there are no messages in the middle of tabs in
-        // our TabListModel.
-        removeAllAppendedMessage();
         boolean showQuickly = mTabListCoordinator.resetWithListOfTabs(tabs, quickMode, mruMode);
         if (showQuickly) {
             mTabListCoordinator.removeSpecialListItem(TabProperties.UiType.NEW_TAB_TILE, 0);
+            removeAllAppendedMessage();
         }
 
         int cardsCount = tabs == null ? 0 : tabs.size();
@@ -503,7 +501,8 @@
                 mMessageCardProviderCoordinator.getMessageItems();
         for (int i = 0; i < messages.size(); i++) {
             if (messages.get(i).type == MessageService.MessageType.PRICE_WELCOME) {
-                mTabListCoordinator.addSpecialListItemToEnd(
+                mTabListCoordinator.addSpecialListItem(
+                        mTabListCoordinator.getPriceWelcomeMessageInsertionIndex(),
                         TabProperties.UiType.PRICE_WELCOME, messages.get(i).model);
             } else {
                 mTabListCoordinator.addSpecialListItemToEnd(
@@ -533,7 +532,8 @@
         for (int i = 0; i < messages.size(); i++) {
             if (messages.get(i).type == MessageService.MessageType.PRICE_WELCOME) {
                 mTabListCoordinator.addSpecialListItem(
-                        index + i, TabProperties.UiType.PRICE_WELCOME, messages.get(i).model);
+                        mTabListCoordinator.getPriceWelcomeMessageInsertionIndex(),
+                        TabProperties.UiType.PRICE_WELCOME, messages.get(i).model);
             } else {
                 mTabListCoordinator.addSpecialListItem(
                         index + i, TabProperties.UiType.MESSAGE, messages.get(i).model);
@@ -549,7 +549,8 @@
                 mMessageCardProviderCoordinator.getNextMessageItemForType(messageType);
         if (nextMessage == null) return;
         if (messageType == MessageService.MessageType.PRICE_WELCOME) {
-            mTabListCoordinator.addSpecialListItemToEnd(
+            mTabListCoordinator.addSpecialListItem(
+                    mTabListCoordinator.getPriceWelcomeMessageInsertionIndex(),
                     TabProperties.UiType.PRICE_WELCOME, nextMessage.model);
         } else {
             mTabListCoordinator.addSpecialListItemToEnd(
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/PriceWelcomeMessageServiceUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/PriceWelcomeMessageServiceUnitTest.java
index b7d0cbde..0c1c81f 100644
--- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/PriceWelcomeMessageServiceUnitTest.java
+++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/PriceWelcomeMessageServiceUnitTest.java
@@ -154,6 +154,7 @@
         mMessageService.preparePriceMessage();
         mMessageService.review();
         verify(mReviewActionProvider).scrollToBindingTab(index);
+        verify(mMessageProvider).showPriceDropTooltip(index);
         assertTrue(PriceTrackingUtilities.isPriceWelcomeMessageCardDisabled());
     }
 
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediatorUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediatorUnitTest.java
index 2a54a971..3666ec0 100644
--- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediatorUnitTest.java
+++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediatorUnitTest.java
@@ -236,6 +236,8 @@
     @Mock
     GridLayoutManager mGridLayoutManager;
     @Mock
+    GridLayoutManager.SpanSizeLookup mSpanSizeLookup;
+    @Mock
     Profile mProfile;
     @Mock
     Tracker mTracker;
@@ -342,6 +344,10 @@
                 .openTabGridDialog(any(Tab.class));
         doNothing().when(mContext).registerComponentCallbacks(mComponentCallbacksCaptor.capture());
         doReturn(mGridLayoutManager).when(mRecyclerView).getLayoutManager();
+        doReturn(TabListCoordinator.GRID_LAYOUT_SPAN_COUNT_PORTRAIT)
+                .when(mGridLayoutManager)
+                .getSpanCount();
+        doReturn(mSpanSizeLookup).when(mGridLayoutManager).getSpanSizeLookup();
         doReturn(TAB1_DOMAIN)
                 .when(mUrlUtilitiesJniMock)
                 .getDomainAndRegistry(eq(TAB1_URL), anyBoolean());
@@ -367,6 +373,18 @@
         assertThat(mTabModelObserverCaptor.getAllValues().isEmpty(), equalTo(true));
         mMediator.initWithNative(mProfile);
         assertThat(mTabModelObserverCaptor.getAllValues().isEmpty(), equalTo(false));
+
+        doAnswer(invocation -> {
+            int position = invocation.getArgument(0);
+            int itemType = mModel.get(position).type;
+            if (itemType == TabProperties.UiType.MESSAGE
+                    || itemType == TabProperties.UiType.PRICE_WELCOME) {
+                return mGridLayoutManager.getSpanCount();
+            }
+            return 1;
+        })
+                .when(mSpanSizeLookup)
+                .getSpanSize(anyInt());
     }
 
     @After
@@ -2153,6 +2171,7 @@
                 mTabContentManager::getTabThumbnailWithCallback, mTitleProvider,
                 mTabListFaviconProvider, true, null, null, null, getClass().getSimpleName(),
                 TabProperties.UiType.CLOSABLE);
+        mMediator.registerOrientationListener(mGridLayoutManager);
         mMediator.initWithNative(mProfile);
 
         initAndAssertAllProperties();
@@ -2177,6 +2196,7 @@
                 mTabContentManager::getTabThumbnailWithCallback, mTitleProvider,
                 mTabListFaviconProvider, true, null, null, null, getClass().getSimpleName(),
                 TabProperties.UiType.CLOSABLE);
+        mMediator.registerOrientationListener(mGridLayoutManager);
         mMediator.initWithNative(mProfile);
 
         initAndAssertAllProperties();
@@ -2209,6 +2229,170 @@
     }
 
     @Test
+    public void testGetPriceWelcomeMessageInsertionIndex() {
+        initWithThreeTabs();
+
+        doReturn(TabListCoordinator.GRID_LAYOUT_SPAN_COUNT_PORTRAIT)
+                .when(mGridLayoutManager)
+                .getSpanCount();
+        assertThat(mMediator.getPriceWelcomeMessageInsertionIndex(), equalTo(2));
+
+        doReturn(TabListCoordinator.GRID_LAYOUT_SPAN_COUNT_LANDSCAPE)
+                .when(mGridLayoutManager)
+                .getSpanCount();
+        assertThat(mMediator.getPriceWelcomeMessageInsertionIndex(), equalTo(3));
+    }
+
+    @Test
+    public void testUpdateLayout_PriceWelcome() {
+        initAndAssertAllProperties();
+        addSpecialItem(1, TabProperties.UiType.PRICE_WELCOME, PRICE_WELCOME);
+        assertThat(mModel.lastIndexForMessageItemFromType(PRICE_WELCOME), equalTo(1));
+
+        doAnswer(invocation -> {
+            int position = invocation.getArgument(0);
+            int itemType = mModel.get(position).type;
+            if (itemType == TabProperties.UiType.PRICE_WELCOME) {
+                return mGridLayoutManager.getSpanCount();
+            }
+            return 1;
+        })
+                .when(mSpanSizeLookup)
+                .getSpanSize(anyInt());
+        mMediator.updateLayout();
+        assertThat(mModel.lastIndexForMessageItemFromType(PRICE_WELCOME), equalTo(1));
+        PriceTrackingUtilities.SHARED_PREFERENCES_MANAGER.writeBoolean(
+                PriceTrackingUtilities.PRICE_WELCOME_MESSAGE_CARD, true);
+        mMediator.updateLayout();
+        assertThat(mModel.lastIndexForMessageItemFromType(PRICE_WELCOME), equalTo(2));
+    }
+
+    @Test
+    public void testUpdateLayout_Divider() {
+        initAndAssertAllProperties();
+        addSpecialItem(1, TabProperties.UiType.DIVIDER, 0);
+        assertThat(mModel.get(1).type, equalTo(TabProperties.UiType.DIVIDER));
+
+        doAnswer(invocation -> {
+            int position = invocation.getArgument(0);
+            int itemType = mModel.get(position).type;
+            if (itemType == TabProperties.UiType.DIVIDER) {
+                return mGridLayoutManager.getSpanCount();
+            }
+            return 1;
+        })
+                .when(mSpanSizeLookup)
+                .getSpanSize(anyInt());
+        PriceTrackingUtilities.SHARED_PREFERENCES_MANAGER.writeBoolean(
+                PriceTrackingUtilities.PRICE_WELCOME_MESSAGE_CARD, true);
+        mMediator.updateLayout();
+        assertThat(mModel.get(1).type, equalTo(TabProperties.UiType.DIVIDER));
+    }
+
+    @Test
+    public void testIndexOfNthTabCard() {
+        initAndAssertAllProperties();
+        addSpecialItem(1, TabProperties.UiType.PRICE_WELCOME, PRICE_WELCOME);
+
+        assertThat(mModel.lastIndexForMessageItemFromType(PRICE_WELCOME), equalTo(1));
+        assertThat(mModel.indexOfNthTabCard(-1), equalTo(TabModel.INVALID_TAB_INDEX));
+        assertThat(mModel.indexOfNthTabCard(0), equalTo(0));
+        assertThat(mModel.indexOfNthTabCard(1), equalTo(2));
+        assertThat(mModel.indexOfNthTabCard(2), equalTo(3));
+    }
+
+    @Test
+    public void testGetTabCardCountsBefore() {
+        initAndAssertAllProperties();
+        addSpecialItem(1, TabProperties.UiType.PRICE_WELCOME, PRICE_WELCOME);
+
+        assertThat(mModel.lastIndexForMessageItemFromType(PRICE_WELCOME), equalTo(1));
+        assertThat(mModel.getTabCardCountsBefore(-1), equalTo(TabModel.INVALID_TAB_INDEX));
+        assertThat(mModel.getTabCardCountsBefore(0), equalTo(0));
+        assertThat(mModel.getTabCardCountsBefore(1), equalTo(1));
+        assertThat(mModel.getTabCardCountsBefore(2), equalTo(1));
+        assertThat(mModel.getTabCardCountsBefore(3), equalTo(2));
+    }
+
+    @Test
+    public void testGetTabIndexBefore() {
+        initAndAssertAllProperties();
+        addSpecialItem(1, TabProperties.UiType.PRICE_WELCOME, PRICE_WELCOME);
+        assertThat(mModel.lastIndexForMessageItemFromType(PRICE_WELCOME), equalTo(1));
+        assertThat(mModel.getTabIndexBefore(2), equalTo(0));
+        assertThat(mModel.getTabIndexBefore(0), equalTo(TabModel.INVALID_TAB_INDEX));
+    }
+
+    @Test
+    public void testGetTabIndexAfter() {
+        initAndAssertAllProperties();
+        addSpecialItem(1, TabProperties.UiType.PRICE_WELCOME, PRICE_WELCOME);
+        assertThat(mModel.lastIndexForMessageItemFromType(PRICE_WELCOME), equalTo(1));
+        assertThat(mModel.getTabIndexAfter(0), equalTo(2));
+        assertThat(mModel.getTabIndexAfter(2), equalTo(TabModel.INVALID_TAB_INDEX));
+    }
+
+    @Test
+    public void testGetIndexForSelectedTab() {
+        initAndAssertAllProperties();
+        assertThat(mModel.getIndexForSelectedTab(), equalTo(0));
+    }
+
+    @Test(expected = AssertionError.class)
+    public void testGetIndexForSelectedTab_multipleSelectedTabs() {
+        initAndAssertAllProperties();
+        mModel.get(0).model.set(TabProperties.IS_SELECTED, true);
+        mModel.get(1).model.set(TabProperties.IS_SELECTED, true);
+        mModel.getIndexForSelectedTab();
+    }
+
+    @Test(expected = AssertionError.class)
+    public void testGetIndexForSelectedTab_noSelectedTabs() {
+        initAndAssertAllProperties();
+        mModel.get(0).model.set(TabProperties.IS_SELECTED, false);
+        mModel.get(1).model.set(TabProperties.IS_SELECTED, false);
+        mModel.getIndexForSelectedTab();
+    }
+
+    @Test
+    public void testListObserver_OnItemRangeInserted() {
+        TabUiFeatureUtilities.ENABLE_PRICE_TRACKING.setForTesting(true);
+        mMediator = new TabListMediator(mContext, mModel, TabListMode.GRID, mTabModelSelector,
+                mTabContentManager::getTabThumbnailWithCallback, mTitleProvider,
+                mTabListFaviconProvider, true, null, null, null, getClass().getSimpleName(),
+                TabProperties.UiType.CLOSABLE);
+        mMediator.registerOrientationListener(mGridLayoutManager);
+        mMediator.initWithNative(mProfile);
+        initAndAssertAllProperties();
+
+        PropertyModel model = mock(PropertyModel.class);
+        when(model.get(CARD_TYPE)).thenReturn(MESSAGE);
+        when(model.get(MESSAGE_TYPE)).thenReturn(PRICE_WELCOME);
+        mMediator.addSpecialItemToModel(1, TabProperties.UiType.PRICE_WELCOME, model);
+        assertThat(mModel.lastIndexForMessageItemFromType(PRICE_WELCOME), equalTo(2));
+    }
+
+    @Test
+    public void testListObserver_OnItemRangeRemoved() {
+        TabUiFeatureUtilities.ENABLE_PRICE_TRACKING.setForTesting(true);
+        mMediator = new TabListMediator(mContext, mModel, TabListMode.GRID, mTabModelSelector,
+                mTabContentManager::getTabThumbnailWithCallback, mTitleProvider,
+                mTabListFaviconProvider, true, null, null, null, getClass().getSimpleName(),
+                TabProperties.UiType.CLOSABLE);
+        mMediator.registerOrientationListener(mGridLayoutManager);
+        mMediator.initWithNative(mProfile);
+        initWithThreeTabs();
+
+        PropertyModel model = mock(PropertyModel.class);
+        when(model.get(CARD_TYPE)).thenReturn(MESSAGE);
+        when(model.get(MESSAGE_TYPE)).thenReturn(PRICE_WELCOME);
+        mMediator.addSpecialItemToModel(2, TabProperties.UiType.PRICE_WELCOME, model);
+        assertThat(mModel.lastIndexForMessageItemFromType(PRICE_WELCOME), equalTo(2));
+        mModel.removeAt(0);
+        assertThat(mModel.lastIndexForMessageItemFromType(PRICE_WELCOME), equalTo(2));
+    }
+
+    @Test
     @Features.EnableFeatures({TAB_GROUPS_CONTINUATION_ANDROID})
     public void testUpdateFaviconForGroup() {
         setUpForTabGroupOperation(TabListMediatorType.TAB_SWITCHER);
@@ -2549,6 +2733,7 @@
                 mTabContentManager::getTabThumbnailWithCallback, mTitleProvider,
                 mTabListFaviconProvider, actionOnRelatedTabs, null, null, handler,
                 getClass().getSimpleName(), uiType);
+        mMediator.registerOrientationListener(mGridLayoutManager);
 
         // TabGroupModelFilterObserver is registered when native is ready.
         assertThat(mTabGroupModelFilterObserverCaptor.getAllValues().isEmpty(), equalTo(true));
@@ -2604,4 +2789,26 @@
                 .canApplyOptimization(
                         anyLong(), any(GURL.class), anyInt(), any(OptimizationGuideCallback.class));
     }
+
+    private void initWithThreeTabs() {
+        Tab tab3 = prepareTab(TAB3_ID, TAB3_TITLE, TAB3_URL);
+        List<Tab> tabs = new ArrayList<>(Arrays.asList(mTab1, mTab2, tab3));
+        mMediator.resetWithListOfTabs(PseudoTab.getListOfPseudoTab(tabs), false, false);
+        assertThat(mModel.size(), equalTo(3));
+        assertThat(mModel.get(0).model.get(TabProperties.IS_SELECTED), equalTo(true));
+        assertThat(mModel.get(1).model.get(TabProperties.IS_SELECTED), equalTo(false));
+        assertThat(mModel.get(2).model.get(TabProperties.IS_SELECTED), equalTo(false));
+    }
+
+    private void addSpecialItem(int index, @UiType int uiType, int itemIdentifier) {
+        PropertyModel model = mock(PropertyModel.class);
+        when(model.get(CARD_TYPE)).thenReturn(MESSAGE);
+        if (uiType == TabProperties.UiType.MESSAGE
+                || uiType == TabProperties.UiType.PRICE_WELCOME) {
+            when(model.get(MESSAGE_TYPE)).thenReturn(itemIdentifier);
+        }
+        // Avoid auto-updating the layout when inserting the special card.
+        doReturn(1).when(mSpanSizeLookup).getSpanSize(anyInt());
+        mMediator.addSpecialItemToModel(index, uiType, model);
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/AppHooks.java b/chrome/android/java/src/org/chromium/chrome/browser/AppHooks.java
index ec7e5ba..e609c350 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/AppHooks.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/AppHooks.java
@@ -45,7 +45,6 @@
 import org.chromium.chrome.browser.xsurface.ProcessScope;
 import org.chromium.chrome.browser.xsurface.ProcessScopeDependencyProvider;
 import org.chromium.chrome.modules.image_editor.ImageEditorModuleProvider;
-import org.chromium.components.browser_ui.widget.FeatureHighlightProvider;
 import org.chromium.components.external_intents.AuthenticatorNavigationInterceptor;
 import org.chromium.components.policy.AppRestrictionsProvider;
 import org.chromium.components.policy.CombinedPolicyProvider;
@@ -268,13 +267,6 @@
     }
 
     /**
-     * @return A new {@link FeatureHighlightProvider}.
-     */
-    public FeatureHighlightProvider createFeatureHighlightProvider() {
-        return new FeatureHighlightProvider();
-    }
-
-    /**
      * @return A new {@link DigitalWellbeingClient} instance.
      */
     public DigitalWellbeingClient createDigitalWellbeingClient() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/AppIndexingUtil.java b/chrome/android/java/src/org/chromium/chrome/browser/AppIndexingUtil.java
index a720378..1d6ca47 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/AppIndexingUtil.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/AppIndexingUtil.java
@@ -24,7 +24,6 @@
 import org.chromium.components.embedder_support.util.UrlUtilities;
 import org.chromium.content_public.browser.RenderFrameHost;
 import org.chromium.content_public.browser.WebContents;
-import org.chromium.services.service_manager.InterfaceProvider;
 import org.chromium.url.GURL;
 
 import java.lang.annotation.Retention;
@@ -173,10 +172,9 @@
         RenderFrameHost mainFrame = webContents.getMainFrame();
         if (mainFrame == null) return null;
 
-        InterfaceProvider interfaces = mainFrame.getRemoteInterfaces();
-        if (interfaces == null) return null;
+        if (!mainFrame.isRenderFrameCreated()) return null;
 
-        return interfaces.getInterface(DocumentMetadata.MANAGER);
+        return mainFrame.getInterfaceToRendererFrame(DocumentMetadata.MANAGER);
     }
 
     @VisibleForTesting
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java
index 567d6db3..9241dee 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java
@@ -431,6 +431,7 @@
                         this::getCurrentTabCreator, this::isCustomTab,
                         getStatusBarColorController(), ScreenOrientationProvider.getInstance(),
                         this::getNotificationManagerProxy, getTabContentManagerSupplier(),
+                        this::getActivityTabStartupMetricsTracker,
                         /* CompositorViewHolder.Initializer */ this)
                 : overridenCommonsFactory.create(this, mRootUiCoordinator::getBottomSheetController,
                         mTabModelSelectorSupplier, getBrowserControlsManager(),
@@ -441,6 +442,7 @@
                         this, this::getCurrentTabCreator, this::isCustomTab,
                         getStatusBarColorController(), ScreenOrientationProvider.getInstance(),
                         this::getNotificationManagerProxy, getTabContentManagerSupplier(),
+                        this::getActivityTabStartupMetricsTracker,
                         /* CompositorViewHolder.Initializer */ this);
 
         return createComponent(commonsModule);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/dependency_injection/ChromeActivityCommonsModule.java b/chrome/android/java/src/org/chromium/chrome/browser/dependency_injection/ChromeActivityCommonsModule.java
index 3a001e2..cc30b34 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/dependency_injection/ChromeActivityCommonsModule.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/dependency_injection/ChromeActivityCommonsModule.java
@@ -25,6 +25,7 @@
 import org.chromium.chrome.browser.fullscreen.BrowserControlsManager;
 import org.chromium.chrome.browser.fullscreen.FullscreenManager;
 import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
+import org.chromium.chrome.browser.metrics.ActivityTabStartupMetricsTracker;
 import org.chromium.chrome.browser.tabmodel.TabCreator;
 import org.chromium.chrome.browser.tabmodel.TabCreatorManager;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
@@ -67,6 +68,8 @@
     private final ScreenOrientationProvider mScreenOrientationProvider;
     private final Supplier<NotificationManagerProxy> mNotificationManagerProxySupplier;
     private final ObservableSupplier<TabContentManager> mTabContentManagerSupplier;
+    private final Supplier<ActivityTabStartupMetricsTracker>
+            mActivityTabStartupMetricsTrackerSupplier;
     private final CompositorViewHolder.Initializer mCompositorViewHolderInitializer;
 
     /** See {@link ModuleFactoryOverrides} */
@@ -89,6 +92,7 @@
                 ScreenOrientationProvider screenOrientationProvider,
                 Supplier<NotificationManagerProxy> notificationManagerProxySupplier,
                 ObservableSupplier<TabContentManager> tabContentManagerSupplier,
+                Supplier<ActivityTabStartupMetricsTracker> activityTabStartupMetricsTrackerSupplier,
                 CompositorViewHolder.Initializer compositorViewHolderInitializer);
     }
 
@@ -110,6 +114,7 @@
             ScreenOrientationProvider screenOrientationProvider,
             Supplier<NotificationManagerProxy> notificationManagerProxySupplier,
             ObservableSupplier<TabContentManager> tabContentManagerSupplier,
+            Supplier<ActivityTabStartupMetricsTracker> activityTabStartupMetricsTrackerSupplier,
             CompositorViewHolder.Initializer compositorViewHolderInitializer) {
         mActivity = activity;
         mBottomSheetControllerSupplier = bottomSheetControllerSupplier;
@@ -132,6 +137,7 @@
         mScreenOrientationProvider = screenOrientationProvider;
         mNotificationManagerProxySupplier = notificationManagerProxySupplier;
         mTabContentManagerSupplier = tabContentManagerSupplier;
+        mActivityTabStartupMetricsTrackerSupplier = activityTabStartupMetricsTrackerSupplier;
         mCompositorViewHolderInitializer = compositorViewHolderInitializer;
     }
 
@@ -271,6 +277,11 @@
     }
 
     @Provides
+    public ActivityTabStartupMetricsTracker provideActivityTabStartupMetricsTracker() {
+        return mActivityTabStartupMetricsTrackerSupplier.get();
+    }
+
+    @Provides
     public CompositorViewHolder.Initializer provideCompositorViewHolderInitializer() {
         return mCompositorViewHolderInitializer;
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/indicator/OfflineDetector.java b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/indicator/OfflineDetector.java
index 8d6f1d42..3678592 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/indicator/OfflineDetector.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/indicator/OfflineDetector.java
@@ -64,6 +64,7 @@
     // Effectively offline means that all checks have been passed and the
     // |mCallback| has been invoked to notify the observers.
     private boolean mIsEffectivelyOffline;
+    private boolean mIsEffectivelyOfflineInitialized;
 
     // True if the network is offline as detected by the connectivity detector.
     private boolean mIsOfflineLastReportedByConnectivityDetector;
@@ -122,10 +123,12 @@
 
             // Connection state has not changed since |mUpdateOfflineStatusIndicatorDelayedRunnable|
             // was posted.
-            if (mIsOfflineLastReportedByConnectivityDetector == mIsEffectivelyOffline) {
+            if (mIsEffectivelyOfflineInitialized
+                    && mIsOfflineLastReportedByConnectivityDetector == mIsEffectivelyOffline) {
                 return;
             }
             mIsEffectivelyOffline = mIsOfflineLastReportedByConnectivityDetector;
+            mIsEffectivelyOfflineInitialized = true;
             mCallback.onResult(mIsEffectivelyOffline);
             if (sLoggingEnabled) {
                 logToAdbConsoleNow("Running mUpdateOfflineStatusIndicatorDelayedRunnable end.");
@@ -151,9 +154,9 @@
                 mIsOfflineLastReportedByConnectivityDetector;
         mIsOfflineLastReportedByConnectivityDetector =
                 (connectionState != ConnectionState.VALIDATED);
-        if (previousLastReportedStateByOfflineDetector
-                == mIsOfflineLastReportedByConnectivityDetector) {
-            mConnectivityDetectorInitialized = true;
+        if (mConnectivityDetectorInitialized
+                && previousLastReportedStateByOfflineDetector
+                        == mIsOfflineLastReportedByConnectivityDetector) {
             return;
         }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/indicator/OfflineIndicatorControllerV2.java b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/indicator/OfflineIndicatorControllerV2.java
index d915a60d..ac1c933e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/indicator/OfflineIndicatorControllerV2.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/indicator/OfflineIndicatorControllerV2.java
@@ -15,18 +15,18 @@
 import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.base.Callback;
 import org.chromium.base.CommandLine;
-import org.chromium.base.TimeUtilsJni;
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.metrics.RecordUserAction;
 import org.chromium.base.supplier.ObservableSupplier;
 import org.chromium.base.supplier.Supplier;
 import org.chromium.chrome.R;
+import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
+import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
 import org.chromium.chrome.browser.status_indicator.StatusIndicatorCoordinator;
 import org.chromium.content_public.common.ContentSwitches;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-import java.util.concurrent.TimeUnit;
 
 /**
  * Class that controls visibility and content of {@link StatusIndicatorCoordinator} to relay
@@ -47,6 +47,12 @@
         int NUM_ENTRIES = 4;
     }
 
+    /** Clock to use so we can mock the time in tests */
+    public interface Clock {
+        long currentTimeMillis();
+    }
+    private static Clock sClock = System::currentTimeMillis;
+
     static final long STATUS_INDICATOR_WAIT_BEFORE_HIDE_DURATION_MS = 2000;
 
     // TODO(tbansal): Consider moving the cooldown logic to OfflineDetector.java.
@@ -55,6 +61,9 @@
     // frequently.
     static final long STATUS_INDICATOR_COOLDOWN_BEFORE_NEXT_ACTION_MS = 5000;
 
+    public static final String OFFLINE_INDICATOR_SHOWN_DURATION_V2 =
+            "OfflineIndicator.ShownDurationV2";
+
     private static OfflineDetector sMockOfflineDetector;
     private static Supplier<Long> sMockElapsedTimeSupplier;
 
@@ -72,7 +81,8 @@
     private Runnable mUpdateStatusIndicatorDelayedRunnable;
     private long mLastActionTime;
     private boolean mIsOffline;
-    private long mTimeShownMs;
+    private boolean mIsOfflineStateInitialized;
+    private long mWallTimeShownMs;
 
     /**
      * Constructs the offline indicator.
@@ -113,7 +123,15 @@
 
         mShowRunnable = () -> {
             RecordUserAction.record("OfflineIndicator.Shown");
-            mTimeShownMs = TimeUnit.MICROSECONDS.toMillis(TimeUtilsJni.get().getTimeTicksNowUs());
+
+            // If the value of mWallTimeShownMs is already stored in Prefs, then we keep using that
+            // value.
+            if (isWallTimeShownMsInPrefs()) {
+                mWallTimeShownMs = getWallTimeShownMsFromPrefs();
+            } else {
+                mWallTimeShownMs = sClock.currentTimeMillis();
+                setWallTimeShownMsInPrefs(mWallTimeShownMs);
+            }
 
             setLastActionTime();
 
@@ -135,11 +153,15 @@
 
         mUpdateAndHideRunnable = () -> {
             RecordUserAction.record("OfflineIndicator.Hidden");
-            final long shownDuration =
-                    TimeUnit.MICROSECONDS.toMillis(TimeUtilsJni.get().getTimeTicksNowUs())
-                    - mTimeShownMs;
-            RecordHistogram.recordMediumTimesHistogram(
-                    "OfflineIndicator.ShownDuration", shownDuration);
+
+            final long shownDurationWallTimeMs = sClock.currentTimeMillis() - mWallTimeShownMs;
+            clearWallTimeShownMsFromPrefs();
+
+            // shownDurationWallTimeMs can be negative in cases where the system time is changed, so
+            // we want to avoid recording metrics in cases where we know this happened.
+            if (shownDurationWallTimeMs >= 0) {
+                recordShownDurationV2Histogram(shownDurationWallTimeMs);
+            }
 
             setLastActionTime();
 
@@ -175,7 +197,7 @@
     }
 
     public void onConnectionStateChanged(boolean offline) {
-        if (mIsOffline == offline) {
+        if (mIsOfflineStateInitialized && mIsOffline == offline) {
             return;
         }
 
@@ -213,6 +235,21 @@
 
     private void updateStatusIndicator(boolean offline) {
         mIsOffline = offline;
+        if (!mIsOfflineStateInitialized && !offline) {
+            if (isWallTimeShownMsInPrefs()) {
+                // Record any metrics persisted in Prefs when Chrome starts up and is online.
+                mWallTimeShownMs = getWallTimeShownMsFromPrefs();
+                final long shownDurationWallTimeMs = sClock.currentTimeMillis() - mWallTimeShownMs;
+                clearWallTimeShownMsFromPrefs();
+
+                if (shownDurationWallTimeMs >= 0) {
+                    recordShownDurationV2Histogram(shownDurationWallTimeMs);
+                }
+            }
+            mIsOfflineStateInitialized = true;
+            return;
+        }
+        mIsOfflineStateInitialized = true;
         int surfaceState;
         if (mIsUrlBarFocusedSupplier.get()) {
             // We should clear the runnable if we would be assigning an unnecessary show or hide
@@ -250,6 +287,31 @@
         mLastActionTime = getElapsedTime();
     }
 
+    private void recordShownDurationV2Histogram(long shownDurationMs) {
+        RecordHistogram.recordLongTimesHistogram100(
+                OFFLINE_INDICATOR_SHOWN_DURATION_V2, shownDurationMs);
+    }
+
+    private void setWallTimeShownMsInPrefs(long wallTimeShownMs) {
+        SharedPreferencesManager.getInstance().writeLong(
+                ChromePreferenceKeys.OFFLINE_INDICATOR_V2_WALL_TIME_SHOWN_MS, wallTimeShownMs);
+    }
+
+    private long getWallTimeShownMsFromPrefs() {
+        return SharedPreferencesManager.getInstance().readLong(
+                ChromePreferenceKeys.OFFLINE_INDICATOR_V2_WALL_TIME_SHOWN_MS);
+    }
+
+    private void clearWallTimeShownMsFromPrefs() {
+        SharedPreferencesManager.getInstance().removeKey(
+                ChromePreferenceKeys.OFFLINE_INDICATOR_V2_WALL_TIME_SHOWN_MS);
+    }
+
+    private boolean isWallTimeShownMsInPrefs() {
+        return SharedPreferencesManager.getInstance().contains(
+                ChromePreferenceKeys.OFFLINE_INDICATOR_V2_WALL_TIME_SHOWN_MS);
+    }
+
     @VisibleForTesting
     static void setMockOfflineDetector(OfflineDetector offlineDetector) {
         sMockOfflineDetector = offlineDetector;
@@ -264,4 +326,9 @@
     void setHandlerForTesting(Handler handler) {
         mHandler = handler;
     }
+
+    @VisibleForTesting
+    static void setClockForTesting(Clock clock) {
+        sClock = clock;
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusCoordinator.java
index ae50508..0fc7e5d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusCoordinator.java
@@ -89,9 +89,9 @@
 
         mMediator = new StatusMediator(mModel, mStatusView.getResources(), mStatusView.getContext(),
                 urlBarEditingTextStateProvider, isTablet, forceModelViewReconciliationRunnable,
-                incognitoStateProvider, locationBarDataProvider,
-                PermissionDialogController.getInstance(), searchEngineLogoUtils,
-                templateUrlServiceSupplier, profileSupplier, pageInfoIPHController);
+                locationBarDataProvider, PermissionDialogController.getInstance(),
+                searchEngineLogoUtils, templateUrlServiceSupplier, profileSupplier,
+                pageInfoIPHController);
 
         Resources res = mStatusView.getResources();
         mMediator.setUrlMinWidth(res.getDimensionPixelSize(R.dimen.location_bar_min_url_width)
@@ -176,13 +176,17 @@
     }
 
     // LocationBarData.Observer implementation
-    // Using the default empty onIncognitoStateChanged.
     // Using the default empty onNtpStartedLoading.
     // Using the default empty onPrimaryColorChanged.
     // Using the default empty onTitleChanged.
     // Using the default empty onUrlChanged.
 
     @Override
+    public void onIncognitoStateChanged() {
+        mMediator.onIncognitoStateChanged();
+    }
+
+    @Override
     public void onSecurityStateChanged() {
         updateStatusIcon();
         updateVerboseStatusVisibility();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusMediator.java
index 1490321..6a714ab 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusMediator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusMediator.java
@@ -29,7 +29,6 @@
 import org.chromium.chrome.browser.omnibox.status.StatusProperties.StatusIconResource;
 import org.chromium.chrome.browser.page_info.PageInfoIPHController;
 import org.chromium.chrome.browser.profiles.Profile;
-import org.chromium.chrome.browser.tabmodel.IncognitoStateProvider;
 import org.chromium.chrome.browser.theme.ThemeUtils;
 import org.chromium.components.browser_ui.site_settings.ContentSettingsResources;
 import org.chromium.components.browser_ui.site_settings.SingleWebsiteSettings;
@@ -44,8 +43,7 @@
 /**
  * Contains the controller logic of the Status component.
  */
-public class StatusMediator implements IncognitoStateProvider.IncognitoStateObserver,
-                                       PermissionDialogController.Observer {
+public class StatusMediator implements PermissionDialogController.Observer {
     private static final int PERMISSION_ICON_DISPLAY_TIMEOUT_MS = 8500;
 
     private final PropertyModel mModel;
@@ -94,7 +92,6 @@
     private float mUrlFocusPercent;
     private String mSearchEngineLogoUrl;
 
-    private boolean mIsIncognito;
     private Runnable mForceModelViewReconciliationRunnable;
 
     // Factors used to offset the animation of the status icon's alpha adjustment. The full formula
@@ -108,7 +105,6 @@
     public StatusMediator(PropertyModel model, Resources resources, Context context,
             UrlBarEditingTextStateProvider urlBarEditingTextStateProvider, boolean isTablet,
             Runnable forceModelViewReconciliationRunnable,
-            IncognitoStateProvider incognitoStateProvider,
             LocationBarDataProvider locationBarDataProvider,
             PermissionDialogController permissionDialogController,
             SearchEngineLogoUtils searchEngineLogoUtils,
@@ -136,10 +132,6 @@
 
         mIsTablet = isTablet;
         mForceModelViewReconciliationRunnable = forceModelViewReconciliationRunnable;
-        if (incognitoStateProvider != null) {
-            incognitoStateProvider.addIncognitoStateObserverAndTrigger(this);
-        }
-
         mPermissionDialogController = permissionDialogController;
         mPermissionDialogController.addObserver(this);
     }
@@ -302,7 +294,9 @@
     // Extra logic to support extra NTP use cases which show the status icon when animating and when
     // focused, but hide it when unfocused.
     void setUrlAnimationFinished(boolean urlHasFocus) {
-        if (mIsTablet || !mSearchEngineLogoUtils.shouldShowSearchEngineLogo(mIsIncognito)) {
+        if (mIsTablet
+                || !mSearchEngineLogoUtils.shouldShowSearchEngineLogo(
+                        mLocationBarDataProvider.isIncognito())) {
             return;
         }
 
@@ -328,7 +322,8 @@
         // On tablets, the status icon should always be shown so the following logic doesn't apply.
         assert !mIsTablet : "This logic shouldn't be called on tablets";
 
-        if (!mSearchEngineLogoUtils.shouldShowSearchEngineLogo(mIsIncognito)) {
+        if (!mSearchEngineLogoUtils.shouldShowSearchEngineLogo(
+                    mLocationBarDataProvider.isIncognito())) {
             return;
         }
 
@@ -525,12 +520,14 @@
                 && mShowStatusIconWhenUrlFocused;
 
         // Show the logo unfocused if we're on the NTP.
-        if (mSearchEngineLogoUtils.shouldShowSearchEngineLogo(mIsIncognito)
+        if (mSearchEngineLogoUtils.shouldShowSearchEngineLogo(
+                    mLocationBarDataProvider.isIncognito())
                 && mIsSearchEngineStateSetup
                 && (showIconWhenFocused || showIconWhenScrollingOnNTP)) {
-            getStatusIconResourceForSearchEngineIcon(mIsIncognito, (statusIconRes) -> {
-                mModel.set(StatusProperties.STATUS_ICON_RESOURCE, statusIconRes);
-            });
+            getStatusIconResourceForSearchEngineIcon(
+                    mLocationBarDataProvider.isIncognito(), (statusIconRes) -> {
+                        mModel.set(StatusProperties.STATUS_ICON_RESOURCE, statusIconRes);
+                    });
             return true;
         } else {
             mShouldCancelCustomFavicon = true;
@@ -608,7 +605,8 @@
     /** Return the resource id for the accessibility description or 0 if none apply. */
     private int getAccessibilityDescriptionRes() {
         if (mUrlHasFocus) {
-            if (mSearchEngineLogoUtils.shouldShowSearchEngineLogo(mIsIncognito)) {
+            if (mSearchEngineLogoUtils.shouldShowSearchEngineLogo(
+                        mLocationBarDataProvider.isIncognito())) {
                 return 0;
             } else if (mShowStatusIconWhenUrlFocused) {
                 return R.string.accessibility_toolbar_btn_site_info;
@@ -651,13 +649,10 @@
         return urlTextWithAutocomplete;
     }
 
-    @Override
-    public void onIncognitoStateChanged(boolean isIncognito) {
-        boolean previousIsIncognito = mIsIncognito;
-        mIsIncognito = isIncognito;
-        boolean incognitoBadgeVisible = isIncognito && !mIsTablet;
+    public void onIncognitoStateChanged() {
+        boolean incognitoBadgeVisible = mLocationBarDataProvider.isIncognito() && !mIsTablet;
         mModel.set(StatusProperties.INCOGNITO_BADGE_VISIBLE, incognitoBadgeVisible);
-        if (previousIsIncognito != isIncognito) reconcileVisualState();
+        reconcileVisualState();
     }
 
     /**
@@ -675,8 +670,9 @@
         // No reconciliation is needed on tablet because the status icon is always shown.
         if (mIsTablet) return;
 
-        if (!mShowStatusIconWhenUrlFocused || mIsIncognito
-                || !mSearchEngineLogoUtils.shouldShowSearchEngineLogo(mIsIncognito)) {
+        if (!mShowStatusIconWhenUrlFocused || mLocationBarDataProvider.isIncognito()
+                || !mSearchEngineLogoUtils.shouldShowSearchEngineLogo(
+                        mLocationBarDataProvider.isIncognito())) {
             return;
         }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/ChromePaymentRequestFactory.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/ChromePaymentRequestFactory.java
index 5b24dd8..4d32b7a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/ChromePaymentRequestFactory.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/ChromePaymentRequestFactory.java
@@ -23,8 +23,6 @@
 import org.chromium.content_public.browser.RenderFrameHost;
 import org.chromium.content_public.browser.WebContents;
 import org.chromium.content_public.browser.WebContentsStatics;
-import org.chromium.mojo.system.MojoException;
-import org.chromium.mojo.system.MojoResult;
 import org.chromium.payments.mojom.PaymentRequest;
 import org.chromium.services.service_manager.InterfaceFactory;
 
@@ -145,8 +143,7 @@
     public PaymentRequest createImpl() {
         if (mRenderFrameHost == null) return new InvalidPaymentRequest();
         if (!mRenderFrameHost.isFeatureEnabled(FeaturePolicyFeature.PAYMENT)) {
-            mRenderFrameHost.getRemoteInterfaces().onConnectionError(
-                    new MojoException(MojoResult.PERMISSION_DENIED));
+            mRenderFrameHost.terminateRendererDueToBadMessage(241 /*PAYMENTS_WITHOUT_PERMISSION*/);
             return null;
         }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkActivityLifecycleUmaTracker.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkActivityLifecycleUmaTracker.java
index 3c8487f..554bc689 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkActivityLifecycleUmaTracker.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkActivityLifecycleUmaTracker.java
@@ -21,11 +21,14 @@
 import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
 import org.chromium.chrome.browser.lifecycle.InflationObserver;
 import org.chromium.chrome.browser.lifecycle.PauseResumeWithNativeObserver;
+import org.chromium.chrome.browser.metrics.ActivityTabStartupMetricsTracker;
 import org.chromium.chrome.browser.metrics.WebApkSplashscreenMetrics;
 import org.chromium.chrome.browser.metrics.WebApkUma;
 
 import javax.inject.Inject;
 
+import dagger.Lazy;
+
 /**
  * Handles recording user metrics for WebAPK activities.
  */
@@ -38,6 +41,7 @@
     private final ChromeActivity mActivity;
     private final BrowserServicesIntentDataProvider mIntentDataProvider;
     private final SplashController mSplashController;
+    private final Lazy<ActivityTabStartupMetricsTracker> mStartupMetricsTracker;
 
     /** The start time that the activity becomes focused in milliseconds since boot. */
     private long mStartTime;
@@ -46,10 +50,12 @@
     public WebApkActivityLifecycleUmaTracker(ChromeActivity<?> activity,
             BrowserServicesIntentDataProvider intentDataProvider, SplashController splashController,
             ActivityLifecycleDispatcher lifecycleDispatcher,
-            WebappDeferredStartupWithStorageHandler deferredStartupWithStorageHandler) {
+            WebappDeferredStartupWithStorageHandler deferredStartupWithStorageHandler,
+            Lazy<ActivityTabStartupMetricsTracker> startupMetricsTracker) {
         mActivity = activity;
         mIntentDataProvider = intentDataProvider;
         mSplashController = splashController;
+        mStartupMetricsTracker = startupMetricsTracker;
 
         lifecycleDispatcher.register(this);
         ApplicationStatus.registerStateListenerForActivity(this, mActivity);
@@ -76,8 +82,7 @@
         // Decide whether to record startup UMA histograms. This is a similar check to the one done
         // in ChromeTabbedActivity.performPreInflationStartup refer to the comment there for why.
         if (!LibraryLoader.getInstance().isInitialized()) {
-            mActivity.getActivityTabStartupMetricsTracker().trackStartupMetrics(
-                    STARTUP_UMA_HISTOGRAM_SUFFIX);
+            mStartupMetricsTracker.get().trackStartupMetrics(STARTUP_UMA_HISTOGRAM_SUFFIX);
             // If there is a saved instance state, then the intent (and its stored timestamp) might
             // be stale (Android replays intents if there is a recents entry for the activity).
             if (mActivity.getSavedInstanceState() == null) {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/browserservices/RunningInChromeTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/browserservices/RunningInChromeTest.java
index 44745dd..7b850cd3 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/browserservices/RunningInChromeTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/browserservices/RunningInChromeTest.java
@@ -87,7 +87,7 @@
                     compositorViewHolderSupplier, tabCreatorManager, tabCreatorSupplier,
                     isPromotableToTabSupplier, statusBarColorController, screenOrientationProvider,
                     notificationManagerProxySupplier, tabContentManagerSupplier,
-                    compositorViewHolderInitializer) -> {
+                    activityTabStartupMetricsTrackerSupplier, compositorViewHolderInitializer) -> {
                 return new ChromeActivityCommonsModule(activity, bottomSheetController,
                         tabModelSelectorSupplier, browserControlsManager,
                         browserControlsVisibilityManager, browserControlsSizer, fullscreenManager,
@@ -98,7 +98,8 @@
                         screenOrientationProvider,
                         ()
                                 -> mMockNotificationManager,
-                        tabContentManagerSupplier, compositorViewHolderInitializer);
+                        tabContentManagerSupplier, activityTabStartupMetricsTrackerSupplier,
+                        compositorViewHolderInitializer);
             });
 
     @Rule
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadForegroundServiceManagerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadForegroundServiceManagerTest.java
index 0f2880e6..b5215c8 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadForegroundServiceManagerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadForegroundServiceManagerTest.java
@@ -12,7 +12,6 @@
 
 import android.app.Notification;
 import android.content.Context;
-import android.os.Looper;
 import android.support.test.InstrumentationRegistry;
 
 import androidx.test.filters.SmallTest;
@@ -21,16 +20,20 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import org.chromium.base.test.UiThreadTest;
 import org.chromium.base.test.util.AdvancedMockContext;
+import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.Feature;
 import org.chromium.chrome.browser.notifications.NotificationWrapperBuilderFactory;
 import org.chromium.chrome.browser.notifications.channels.ChromeChannelDefinitions;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.content_public.browser.test.util.TestThreadUtils;
 
 /**
  * Test for DownloadForegroundServiceManager.
  */
 @RunWith(ChromeJUnit4ClassRunner.class)
+@Batch(Batch.UNIT_TESTS)
 public final class DownloadForegroundServiceManagerTest {
     private static final int FAKE_DOWNLOAD_1 = 111;
     private static final int FAKE_DOWNLOAD_2 = 222;
@@ -111,23 +114,25 @@
 
     @Before
     public void setUp() {
-        Looper.prepare();
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            mContext = new AdvancedMockContext(InstrumentationRegistry.getTargetContext());
+            mDownloadServiceManager = new MockDownloadForegroundServiceManager();
 
-        mContext = new AdvancedMockContext(InstrumentationRegistry.getTargetContext());
-        mDownloadServiceManager = new MockDownloadForegroundServiceManager();
-
-        mNotification =
-                NotificationWrapperBuilderFactory
-                        .createNotificationWrapperBuilder(true /* preferCompat */,
-                                ChromeChannelDefinitions.ChannelId.DOWNLOADS)
-                        .setSmallIcon(org.chromium.chrome.R.drawable.ic_file_download_white_24dp)
-                        .setContentTitle(FAKE_NOTIFICATION_CHANNEL)
-                        .setContentText(FAKE_NOTIFICATION_CHANNEL)
-                        .build();
+            mNotification =
+                    NotificationWrapperBuilderFactory
+                            .createNotificationWrapperBuilder(true /* preferCompat */,
+                                    ChromeChannelDefinitions.ChannelId.DOWNLOADS)
+                            .setSmallIcon(
+                                    org.chromium.chrome.R.drawable.ic_file_download_white_24dp)
+                            .setContentTitle(FAKE_NOTIFICATION_CHANNEL)
+                            .setContentText(FAKE_NOTIFICATION_CHANNEL)
+                            .build();
+        });
     }
 
     @Test
     @SmallTest
+    @UiThreadTest
     @Feature({"Download"})
     public void testBasicStartAndStop() {
         // Service starts and stops with addition and removal of one active download.
@@ -170,6 +175,7 @@
 
     @Test
     @SmallTest
+    @UiThreadTest
     @Feature({"Download"})
     public void testDelayedStartStop() {
         // Calls to start and stop service.
@@ -190,6 +196,7 @@
 
     @Test
     @SmallTest
+    @UiThreadTest
     @Feature({"Download"})
     public void testDelayedStartStopStart() {
         // Calls to start and stop and start service.
@@ -219,6 +226,7 @@
 
     @Test
     @SmallTest
+    @UiThreadTest
     @Feature({"Download"})
     public void testIsNotificationKilledOrDetached() {
         // Service starts and is paused, not complete, so notification not killed but is detached.
@@ -265,6 +273,7 @@
 
     @Test
     @SmallTest
+    @UiThreadTest
     @Feature({"Download"})
     public void testStopInitiallyAndCleanQueue() {
         // First call is a download being cancelled.
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadForegroundServiceTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadForegroundServiceTest.java
index 6c31240..27d32443 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadForegroundServiceTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadForegroundServiceTest.java
@@ -21,6 +21,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.Feature;
 import org.chromium.chrome.browser.notifications.NotificationWrapperBuilderFactory;
 import org.chromium.chrome.browser.notifications.channels.ChromeChannelDefinitions;
@@ -36,6 +37,7 @@
  * Test for DownloadForegroundService.
  */
 @RunWith(ChromeJUnit4ClassRunner.class)
+@Batch(Batch.UNIT_TESTS)
 public class DownloadForegroundServiceTest {
     private static final int FAKE_DOWNLOAD_ID1 = 1;
     private static final int FAKE_DOWNLOAD_ID2 = 2;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadNotificationServiceTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadNotificationServiceTest.java
index 927e522..b7155f2 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadNotificationServiceTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadNotificationServiceTest.java
@@ -22,6 +22,7 @@
 import org.chromium.base.test.params.ParameterAnnotations.UseRunnerDelegate;
 import org.chromium.base.test.params.ParameterSet;
 import org.chromium.base.test.params.ParameterizedRunner;
+import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.DisabledTest;
 import org.chromium.base.test.util.Feature;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
@@ -32,9 +33,9 @@
 import org.chromium.components.offline_items_collection.ContentId;
 import org.chromium.components.offline_items_collection.LegacyHelpers;
 import org.chromium.components.offline_items_collection.OfflineItem.Progress;
-import org.chromium.content_public.browser.test.util.TestThreadUtils;
 import org.chromium.components.offline_items_collection.OfflineItemProgressUnit;
 import org.chromium.components.offline_items_collection.PendingState;
+import org.chromium.content_public.browser.test.util.TestThreadUtils;
 
 import java.util.Arrays;
 import java.util.List;
@@ -47,6 +48,7 @@
 @UseRunnerDelegate(ChromeJUnit4RunnerDelegate.class)
 @Features.DisableFeatures({ChromeFeatureList.DOWNLOAD_NOTIFICATION_BADGE,
         ChromeFeatureList.DOWNLOAD_OFFLINE_CONTENT_PROVIDER})
+@Batch(Batch.UNIT_TESTS)
 public class DownloadNotificationServiceTest {
     private static final ContentId ID1 =
             LegacyHelpers.buildLegacyContentId(false, UUID.randomUUID().toString());
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/download/OMADownloadHandlerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/download/OMADownloadHandlerTest.java
index cd611c9..99b9ca2 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/download/OMADownloadHandlerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/download/OMADownloadHandlerTest.java
@@ -15,13 +15,14 @@
 import org.hamcrest.Matchers;
 import org.junit.Assert;
 import org.junit.Before;
-import org.junit.Rule;
+import org.junit.ClassRule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.base.Callback;
 import org.chromium.base.test.util.AdvancedMockContext;
+import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.Criteria;
 import org.chromium.base.test.util.CriteriaHelper;
 import org.chromium.base.test.util.Feature;
@@ -47,9 +48,10 @@
  * Tests for OMADownloadHandler class.
  */
 @RunWith(ChromeJUnit4ClassRunner.class)
+@Batch(Batch.PER_CLASS)
 public class OMADownloadHandlerTest {
-    @Rule
-    public final ChromeBrowserTestRule mBrowserTestRule = new ChromeBrowserTestRule();
+    @ClassRule
+    public static final ChromeBrowserTestRule sBrowserTestRule = new ChromeBrowserTestRule();
 
     private static final String INSTALL_NOTIFY_URI = "http://test/test";
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/download/SystemDownloadNotifierTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/download/SystemDownloadNotifierTest.java
index 8c95cbd2..a6f2146 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/download/SystemDownloadNotifierTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/download/SystemDownloadNotifierTest.java
@@ -12,11 +12,13 @@
 import org.hamcrest.Matchers;
 import org.junit.Assert;
 import org.junit.Before;
-import org.junit.Rule;
+import org.junit.BeforeClass;
+import org.junit.ClassRule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 import org.chromium.base.test.BaseJUnit4ClassRunner;
+import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.Criteria;
 import org.chromium.base.test.util.CriteriaHelper;
 import org.chromium.base.test.util.Feature;
@@ -31,16 +33,21 @@
  * Tests of {@link SystemDownloadNotifier}.
  */
 @RunWith(BaseJUnit4ClassRunner.class)
+@Batch(Batch.PER_CLASS)
 public class SystemDownloadNotifierTest {
-    @Rule
-    public final ChromeBrowserTestRule mBrowserTestRule = new ChromeBrowserTestRule();
+    @ClassRule
+    public static final ChromeBrowserTestRule sBrowserTestRule = new ChromeBrowserTestRule();
 
     private final SystemDownloadNotifier mSystemDownloadNotifier = new SystemDownloadNotifier();
     private MockDownloadNotificationService mMockDownloadNotificationService;
 
+    @BeforeClass
+    public static void beforeClass() {
+        Looper.prepare();
+    }
+
     @Before
     public void setUp() {
-        Looper.prepare();
         mMockDownloadNotificationService = new MockDownloadNotificationService();
         mSystemDownloadNotifier.setDownloadNotificationService(mMockDownloadNotificationService);
         mSystemDownloadNotifier.setHandler(new Handler(Looper.getMainLooper()));
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/status/StatusMediatorUnitTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/status/StatusMediatorUnitTest.java
index c155daa..a4d2ed7 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/status/StatusMediatorUnitTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/status/StatusMediatorUnitTest.java
@@ -107,6 +107,7 @@
             ((Callback<Bitmap>) invocation.getArgument(2)).onResult(mBitmap);
             return null;
         };
+        doReturn(false).when(mLocationBarDataProvider).isIncognito();
         doAnswer(bitmapAnswer)
                 .when(mSearchEngineLogoUtils)
                 .getSearchEngineLogoFavicon(any(), eq(mResources), any(), any());
@@ -114,7 +115,7 @@
         TestThreadUtils.runOnUiThreadBlocking(() -> {
             mMediator = new StatusMediator(mModel, mResources, mContext,
                     mUrlBarEditingTextStateProvider,
-                    /* isTablet */ false, mMockForceModelViewReconciliationRunnable, null,
+                    /* isTablet */ false, mMockForceModelViewReconciliationRunnable,
                     mLocationBarDataProvider, mPermissionDialogController, mSearchEngineLogoUtils,
                     () -> mTemplateUrlService, () -> mProfile, null);
         });
@@ -471,7 +472,8 @@
 
         setupSearchEngineLogoForTesting(
                 /* showLogo= */ true, /* isGoogle= */ true, /* loupeEverywhere= */ false);
-        mMediator.onIncognitoStateChanged(true);
+        doReturn(true).when(mLocationBarDataProvider).isIncognito();
+        mMediator.onIncognitoStateChanged();
         verify(mMockForceModelViewReconciliationRunnable, times(0)).run();
     }
 
@@ -484,8 +486,10 @@
 
         setupSearchEngineLogoForTesting(
                 /* showLogo= */ true, /* isGoogle= */ true, /* loupeEverywhere= */ false);
-        mMediator.onIncognitoStateChanged(true);
-        mMediator.onIncognitoStateChanged(false);
+        doReturn(true).when(mLocationBarDataProvider).isIncognito();
+        mMediator.onIncognitoStateChanged();
+        doReturn(false).when(mLocationBarDataProvider).isIncognito();
+        mMediator.onIncognitoStateChanged();
         verify(mMockForceModelViewReconciliationRunnable).run();
     }
 
@@ -495,9 +499,10 @@
     @UiThreadTest
     public void testIncognitoStateChange_shouldShowStatusIcon() {
         mMediator.setShowIconsWhenUrlFocused(true);
-
-        mMediator.onIncognitoStateChanged(true);
-        mMediator.onIncognitoStateChanged(false);
+        doReturn(true).when(mLocationBarDataProvider).isIncognito();
+        mMediator.onIncognitoStateChanged();
+        doReturn(false).when(mLocationBarDataProvider).isIncognito();
+        mMediator.onIncognitoStateChanged();
         verify(mMockForceModelViewReconciliationRunnable, times(0)).run();
     }
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/page_info/PageInfoDiscoverabilityTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/page_info/PageInfoDiscoverabilityTest.java
index 2f80bc5..eac373d 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/page_info/PageInfoDiscoverabilityTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/page_info/PageInfoDiscoverabilityTest.java
@@ -82,7 +82,7 @@
         TestThreadUtils.runOnUiThreadBlocking(() -> {
             mMediator = new StatusMediator(mModel, mResources, mContext,
                     mUrlBarEditingTextStateProvider,
-                    /* isTablet */ false, mMockForceModelViewReconciliationRunnable, null,
+                    /* isTablet */ false, mMockForceModelViewReconciliationRunnable,
                     mLocationBarDataProvider, mPermissionDialogController, mSearchEngineLogoUtils,
                     () -> mTemplateUrlService, () -> mProfile, null);
         });
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/GoogleServicesSettingsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/GoogleServicesSettingsTest.java
index 8bce2ce94..f8767be 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/GoogleServicesSettingsTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/GoogleServicesSettingsTest.java
@@ -7,14 +7,8 @@
 import static androidx.test.espresso.Espresso.onView;
 import static androidx.test.espresso.action.ViewActions.click;
 import static androidx.test.espresso.matcher.RootMatchers.isDialog;
-import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
 import static androidx.test.espresso.matcher.ViewMatchers.withText;
 
-import static org.hamcrest.CoreMatchers.allOf;
-import static org.hamcrest.CoreMatchers.is;
-
-import static org.chromium.chrome.test.util.ViewUtils.onViewWaiting;
-
 import androidx.test.filters.LargeTest;
 
 import org.junit.After;
@@ -26,6 +20,7 @@
 import org.junit.runner.RunWith;
 
 import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.base.test.util.DisableIf;
 import org.chromium.base.test.util.Feature;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
@@ -47,7 +42,6 @@
 import org.chromium.components.signin.identitymanager.ConsentLevel;
 import org.chromium.components.user_prefs.UserPrefs;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
-import org.chromium.ui.test.util.DisableAnimationsTestRule;
 
 /**
  * Tests for ManageSyncSettings.
@@ -58,9 +52,6 @@
     @Rule
     public final AccountManagerTestRule mAccountManagerTestRule = new AccountManagerTestRule();
 
-    @Rule
-    public final DisableAnimationsTestRule sNoAnimationRule = new DisableAnimationsTestRule();
-
     public final ChromeTabbedActivityTestRule mActivityTestRule =
             new ChromeTabbedActivityTestRule();
 
@@ -120,6 +111,7 @@
     @Test
     @LargeTest
     @Features.EnableFeatures(ChromeFeatureList.MOBILE_IDENTITY_CONSISTENCY)
+    @DisableIf.Build(supported_abis_includes = "x86", message = "https://crbug.com/1142481")
     public void showSignOutDialogBeforeSigningUserOut() {
         mAccountManagerTestRule.addTestAccountThenSigninAndEnableSync();
         final GoogleServicesSettings googleServicesSettings = startGoogleServicesSettings();
@@ -128,7 +120,6 @@
                         GoogleServicesSettings.PREF_ALLOW_SIGNIN);
         Assert.assertTrue("Chrome Signin should be allowed", allowChromeSignin.isChecked());
 
-        onViewWaiting(allOf(is(googleServicesSettings.getView()), isDisplayed()));
         onView(withText(R.string.allow_chrome_signin_title)).perform(click());
         // Accept the sign out Dialog
         onView(withText(R.string.continue_button)).inRoot(isDialog()).perform(click());
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebApkInitializationTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebApkInitializationTest.java
index d600010..33e846d4 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebApkInitializationTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebApkInitializationTest.java
@@ -94,7 +94,8 @@
                     activityWindowAndroid, compositorViewHolderSupplier, tabCreatorManager,
                     tabCreatorSupplier, isPromotableToTabSupplier, statusBarColorController,
                     screenOrientationProvider, notificationManagerProxySupplier,
-                    tabContentManagerSupplier, compositorViewHolderInitializer) -> {
+                    tabContentManagerSupplier, activityTabStartupMetricsTrackerSupplier,
+                    compositorViewHolderInitializer) -> {
                 mTrackingActivityLifecycleDispatcher.init(lifecycleDispatcher);
                 return new ChromeActivityCommonsModule(activity, bottomSheetControllerSupplier,
                         tabModelSelectorSupplier, browserControlsManager,
@@ -104,7 +105,8 @@
                         activityWindowAndroid, compositorViewHolderSupplier, tabCreatorManager,
                         tabCreatorSupplier, isPromotableToTabSupplier, statusBarColorController,
                         screenOrientationProvider, notificationManagerProxySupplier,
-                        tabContentManagerSupplier, compositorViewHolderInitializer);
+                        tabContentManagerSupplier, activityTabStartupMetricsTrackerSupplier,
+                        compositorViewHolderInitializer);
             });
 
     private final WebApkActivityTestRule mActivityRule = new WebApkActivityTestRule();
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/offlinepages/indicator/OfflineDetectorUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/offlinepages/indicator/OfflineDetectorUnitTest.java
index d9a5889..e894f58 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/offlinepages/indicator/OfflineDetectorUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/offlinepages/indicator/OfflineDetectorUnitTest.java
@@ -77,12 +77,12 @@
 
         // Change to online.
         changeConnectionState(false);
-        assertEquals(0, mNotificationReceivedByObserver);
+        assertEquals(1, mNotificationReceivedByObserver);
         assertFalse(mLastNotificationReceivedIsOffline);
 
         // Change to offline.
         changeConnectionState(true);
-        assertEquals("Notification received immediately after connection changed to offline", 0,
+        assertEquals("Notification received immediately after connection changed to offline", 1,
                 mNotificationReceivedByObserver);
         assertFalse("Notification received immediately after connection changed to offline",
                 mLastNotificationReceivedIsOffline);
@@ -92,14 +92,14 @@
         advanceTimeByMs(STATUS_INDICATOR_WAIT_ON_SWITCH_ONLINE_TO_OFFLINE_DEFAULT_DURATION_MS);
         captor.getValue().run();
 
-        assertEquals("Notification count not updated after connection changed to offline", 1,
+        assertEquals("Notification count not updated after connection changed to offline", 2,
                 mNotificationReceivedByObserver);
         assertTrue("Notification not received after connection changed to offline",
                 mLastNotificationReceivedIsOffline);
 
         // Change to online.
         changeConnectionState(false);
-        assertEquals("Notification count not updated after connection changed to online", 2,
+        assertEquals("Notification count not updated after connection changed to online", 3,
                 mNotificationReceivedByObserver);
         assertFalse("Notification not received after connection changed to online",
                 mLastNotificationReceivedIsOffline);
@@ -107,7 +107,7 @@
         // Change to online again. It should not trigger a notification.
         changeConnectionState(false);
         assertEquals(
-                "Extra notification received even though there is no change in connection state", 2,
+                "Extra notification received even though there is no change in connection state", 3,
                 mNotificationReceivedByObserver);
         assertFalse(
                 "Extra notification received even though there is no change in connection state",
@@ -205,7 +205,7 @@
 
         advanceTimeByMs(STATUS_INDICATOR_WAIT_ON_OFFLINE_DURATION_MS);
         captor.getValue().run();
-        assertEquals("Extra notification received even though connection is still online", 0,
+        assertEquals("Extra notification received even though connection is still online", 1,
                 mNotificationReceivedByObserver);
         assertFalse("Connection is reported as online when it's offline",
                 mLastNotificationReceivedIsOffline);
@@ -224,10 +224,10 @@
 
         // Change to online.
         changeConnectionState(false);
-        assertEquals(0, mNotificationReceivedByObserver);
+        assertEquals(1, mNotificationReceivedByObserver);
         assertFalse(mLastNotificationReceivedIsOffline);
 
-        assertEquals("Duplicate notification received after connection changed to online", 0,
+        assertEquals("Duplicate notification received after connection changed to online", 1,
                 mNotificationReceivedByObserver);
         assertFalse("Duplicate notification received after connection changed to online.",
                 mLastNotificationReceivedIsOffline);
@@ -239,7 +239,7 @@
                 eq(STATUS_INDICATOR_WAIT_ON_SWITCH_ONLINE_TO_OFFLINE_DEFAULT_DURATION_MS));
         advanceTimeByMs(STATUS_INDICATOR_WAIT_ON_SWITCH_ONLINE_TO_OFFLINE_DEFAULT_DURATION_MS);
         captor.getValue().run();
-        assertEquals("Notification not received even though connection is now offline", 1,
+        assertEquals("Notification not received even though connection is now offline", 2,
                 mNotificationReceivedByObserver);
         assertTrue("Notification not received even though connection is now offline",
                 mLastNotificationReceivedIsOffline);
@@ -256,7 +256,7 @@
         // immediately.
         changeApplicationStateToBackground(false);
         captor.getValue().run();
-        assertEquals("Notification not received even though connection is now online", 2,
+        assertEquals("Notification not received even though connection is now online", 3,
                 mNotificationReceivedByObserver);
         assertFalse("Notification not received even though connection is now online",
                 mLastNotificationReceivedIsOffline);
@@ -274,12 +274,12 @@
 
         // Change to online.
         changeConnectionState(false);
-        assertEquals(0, mNotificationReceivedByObserver);
+        assertEquals(1, mNotificationReceivedByObserver);
         assertFalse(mLastNotificationReceivedIsOffline);
 
         // Change to offline.
         changeConnectionState(true);
-        assertEquals("Notification received immediately after connection changed to offline", 0,
+        assertEquals("Notification received immediately after connection changed to offline", 1,
                 mNotificationReceivedByObserver);
         assertFalse("Notification received immediately after connection changed to offline.",
                 mLastNotificationReceivedIsOffline);
@@ -289,14 +289,14 @@
         advanceTimeByMs(
                 STATUS_INDICATOR_WAIT_ON_SWITCH_ONLINE_TO_OFFLINE_DEFAULT_DURATION_MS - 1000L);
 
-        assertEquals("Notification received soon after connection changed to offline", 0,
+        assertEquals("Notification received soon after connection changed to offline", 1,
                 mNotificationReceivedByObserver);
         assertFalse("Notification received soon after connection changed to offline",
                 mLastNotificationReceivedIsOffline);
 
         // Change to online.
         changeConnectionState(false);
-        assertEquals("Extra notification received after connection changed to online", 0,
+        assertEquals("Extra notification received after connection changed to online", 1,
                 mNotificationReceivedByObserver);
         assertFalse("Connection is reported as offline when it's online",
                 mLastNotificationReceivedIsOffline);
@@ -306,7 +306,7 @@
         // since the connection is now online.
         advanceTimeByMs(1000L);
         captor.getValue().run();
-        assertEquals("Extra notification received even though connection is still online", 0,
+        assertEquals("Extra notification received even though connection is still online", 1,
                 mNotificationReceivedByObserver);
         assertFalse("Connection is reported as offline when it's online",
                 mLastNotificationReceivedIsOffline);
@@ -321,10 +321,10 @@
         changeApplicationStateToBackground(false);
         // Change to online.
         changeConnectionState(false);
-        assertEquals(0, mNotificationReceivedByObserver);
+        assertEquals(1, mNotificationReceivedByObserver);
         assertFalse(mLastNotificationReceivedIsOffline);
 
-        assertEquals("Notification received immediately after connection changed to online", 0,
+        assertEquals("Notification received immediately after connection changed to online", 1,
                 mNotificationReceivedByObserver);
         assertFalse("Notification received immediately after connection changed to online.",
                 mLastNotificationReceivedIsOffline);
@@ -341,7 +341,7 @@
         verify(mHandler).postDelayed(captor.capture(),
                 eq(STATUS_INDICATOR_WAIT_ON_SWITCH_ONLINE_TO_OFFLINE_DEFAULT_DURATION_MS));
 
-        assertEquals("Extra notification received even though device just changed to offline", 0,
+        assertEquals("Extra notification received even though device just changed to offline", 1,
                 mNotificationReceivedByObserver);
         assertFalse("Extra notification received even though device just changed to offline",
                 mLastNotificationReceivedIsOffline);
@@ -350,7 +350,7 @@
         advanceTimeByMs(STATUS_INDICATOR_WAIT_ON_SWITCH_ONLINE_TO_OFFLINE_DEFAULT_DURATION_MS
                 - STATUS_INDICATOR_WAIT_ON_OFFLINE_DURATION_MS);
         captor.getValue().run();
-        assertEquals("Expected notification when app has been offline for long", 1,
+        assertEquals("Expected notification when app has been offline for long", 2,
                 mNotificationReceivedByObserver);
         assertTrue("Expected notification when app has been offline for long",
                 mLastNotificationReceivedIsOffline);
@@ -367,10 +367,10 @@
         changeApplicationStateToBackground(false);
         // Change to online.
         changeConnectionState(false);
-        assertEquals(0, mNotificationReceivedByObserver);
+        assertEquals(1, mNotificationReceivedByObserver);
         assertFalse(mLastNotificationReceivedIsOffline);
 
-        assertEquals("Notification received immediately after connection changed to online", 0,
+        assertEquals("Notification received immediately after connection changed to online", 1,
                 mNotificationReceivedByObserver);
         assertFalse("Notification received immediately after connection changed to online.",
                 mLastNotificationReceivedIsOffline);
@@ -387,7 +387,7 @@
         verify(mHandler).postDelayed(captor.capture(),
                 eq(STATUS_INDICATOR_WAIT_ON_SWITCH_ONLINE_TO_OFFLINE_DEFAULT_DURATION_MS));
 
-        assertEquals("Extra notification received even though device just changed to offline", 0,
+        assertEquals("Extra notification received even though device just changed to offline", 1,
                 mNotificationReceivedByObserver);
         assertFalse("Extra notification received even though device just changed to offline",
                 mLastNotificationReceivedIsOffline);
@@ -401,7 +401,7 @@
         advanceTimeByMs(STATUS_INDICATOR_WAIT_ON_SWITCH_ONLINE_TO_OFFLINE_DEFAULT_DURATION_MS
                 - STATUS_INDICATOR_WAIT_ON_OFFLINE_DURATION_MS);
         captor.getValue().run();
-        assertEquals("Extra notification received even though device is now online", 0,
+        assertEquals("Extra notification received even though device is now online", 1,
                 mNotificationReceivedByObserver);
         assertFalse("Extra notification received even though device is now online",
                 mLastNotificationReceivedIsOffline);
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/offlinepages/indicator/OfflineIndicatorControllerV2UnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/offlinepages/indicator/OfflineIndicatorControllerV2UnitTest.java
index 3f453ac..b05ccc9 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/offlinepages/indicator/OfflineIndicatorControllerV2UnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/offlinepages/indicator/OfflineIndicatorControllerV2UnitTest.java
@@ -4,6 +4,7 @@
 
 package org.chromium.chrome.browser.offlinepages.indicator;
 
+import static org.junit.Assert.assertEquals;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
@@ -27,9 +28,11 @@
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
 
 import org.chromium.base.TimeUtils;
 import org.chromium.base.TimeUtilsJni;
+import org.chromium.base.metrics.test.ShadowRecordHistogram;
 import org.chromium.base.supplier.ObservableSupplierImpl;
 import org.chromium.base.supplier.Supplier;
 import org.chromium.base.test.BaseRobolectricTestRunner;
@@ -42,6 +45,7 @@
  * Unit tests for {@link OfflineIndicatorControllerV2}.
  */
 @RunWith(BaseRobolectricTestRunner.class)
+@Config(manifest = Config.NONE, shadows = {ShadowRecordHistogram.class})
 public class OfflineIndicatorControllerV2UnitTest {
     @Mock
     private Context mContext;
@@ -65,6 +69,32 @@
     private OfflineIndicatorControllerV2 mController;
     private long mElapsedTimeMs;
 
+    /**
+     * Fake of OfflineIndicatorControllerV2.Clock used to test metrics that rely on the wall time.
+     */
+    public static class FakeClock implements OfflineIndicatorControllerV2.Clock {
+        private long mCurrentTimeMillis;
+
+        public FakeClock() {
+            mCurrentTimeMillis = 0;
+        }
+
+        @Override
+        public long currentTimeMillis() {
+            return mCurrentTimeMillis;
+        }
+
+        public void setCurrentTimeMillis(long currentTimeMillis) {
+            mCurrentTimeMillis = currentTimeMillis;
+        }
+
+        public void advanceCurrentTimeMillis(long millis) {
+            mCurrentTimeMillis += millis;
+        }
+    }
+
+    private FakeClock mFakeClock;
+
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
@@ -80,6 +110,11 @@
         TimeUtilsJni.TEST_HOOKS.setInstanceForTesting(mTimeUtils);
         when(mTimeUtils.getTimeTicksNowUs()).thenReturn(0L);
 
+        mFakeClock = new FakeClock();
+        OfflineIndicatorControllerV2.setClockForTesting(mFakeClock);
+
+        ShadowRecordHistogram.reset();
+
         mIsUrlBarFocusedSupplier.set(false);
         OfflineDetector.setMockConnectivityDetector(mConnectivityDetector);
         OfflineIndicatorControllerV2.setMockOfflineDetector(mOfflineDetector);
@@ -292,6 +327,94 @@
         verify(mStatusIndicator, times(2)).show(eq("Offline"), any(), anyInt(), anyInt(), anyInt());
     }
 
+    /**
+     * Tests that samples are recorded as expected to the OfflineIndicator.ShownDurationV2
+     * histogram when switching between offline and online states.
+     */
+    @Test
+    public void testPersistedMetrics_ChangeConnectionState() {
+        // Simulate the connection state going from online to offline then back to online.
+        changeConnectionState(true);
+        advanceTimeByMs(10000);
+        changeConnectionState(false);
+
+        // Check that the correct sample is recorded to OfflineIndicator.ShownDurationV2.
+        assertEquals(1,
+                ShadowRecordHistogram.getHistogramTotalCountForTesting(
+                        OfflineIndicatorControllerV2.OFFLINE_INDICATOR_SHOWN_DURATION_V2));
+        assertEquals(1,
+                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                        OfflineIndicatorControllerV2.OFFLINE_INDICATOR_SHOWN_DURATION_V2, 10000));
+    }
+
+    /**
+     * Tests that samples are recorded as expected to the OfflineIndicator.ShownDurationV2 histogram
+     * in the case where Chrome is killed while offline, and is still offline the next time Chrome
+     * starts up.
+     */
+    @Test
+    public void testPersistedMetrics_PersistMetricsAcrossSessionsAndStartUpOffline() {
+        // Simulate the system going offline.
+        changeConnectionState(true);
+        advanceTimeByMs(20000);
+
+        // Simulate Chrome being killed.
+        mController = null;
+        advanceTimeByMs(30000);
+
+        // Simulate Chrome starting up again by creating a new instance of the controller.
+        mController = new OfflineIndicatorControllerV2(mContext, mStatusIndicator,
+                mIsUrlBarFocusedSupplier, mCanAnimateNativeBrowserControls);
+        mController.setHandlerForTesting(mHandler);
+
+        // Simulate that the system is still offline upon start up.
+        changeConnectionState(true);
+
+        // Simulate Chrome going back online.
+        advanceTimeByMs(40000);
+        changeConnectionState(false);
+
+        // Check that the correct sample is recorded to OfflineIndicator.ShownDurationV2.
+        assertEquals(1,
+                ShadowRecordHistogram.getHistogramTotalCountForTesting(
+                        OfflineIndicatorControllerV2.OFFLINE_INDICATOR_SHOWN_DURATION_V2));
+        assertEquals(1,
+                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                        OfflineIndicatorControllerV2.OFFLINE_INDICATOR_SHOWN_DURATION_V2, 90000));
+    }
+
+    /**
+     * Tests that samples are recorded as expected to the OfflineIndicator.ShownDurationV2 histogram
+     * in the case where Chrome is killed while offline, and is online the next time Chrome starts
+     * up.
+     */
+    @Test
+    public void testPersistedMetrics_PersistMetricsAcrossSessionsAndStartUpOnline() {
+        // Simulate the system going offline.
+        changeConnectionState(true);
+        advanceTimeByMs(20000);
+
+        // Simulate Chrome being killed.
+        mController = null;
+        advanceTimeByMs(30000);
+
+        // Simulate Chrome starting up  by creating a new instance of the controller.
+        mController = new OfflineIndicatorControllerV2(mContext, mStatusIndicator,
+                mIsUrlBarFocusedSupplier, mCanAnimateNativeBrowserControls);
+        mController.setHandlerForTesting(mHandler);
+
+        // Simulate that the system is online when it starts up.
+        changeConnectionState(false);
+
+        // Check that the correct sample is recorded to OfflineIndicator.ShownDurationV2.
+        assertEquals(1,
+                ShadowRecordHistogram.getHistogramTotalCountForTesting(
+                        OfflineIndicatorControllerV2.OFFLINE_INDICATOR_SHOWN_DURATION_V2));
+        assertEquals(1,
+                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                        OfflineIndicatorControllerV2.OFFLINE_INDICATOR_SHOWN_DURATION_V2, 50000));
+    }
+
     private void changeConnectionState(boolean offline) {
         final int state = offline ? ConnectionState.NO_INTERNET : ConnectionState.VALIDATED;
         when(mOfflineDetector.isConnectionStateOffline()).thenReturn(offline);
@@ -301,5 +424,7 @@
     private void advanceTimeByMs(long delta) {
         mElapsedTimeMs += delta;
         setMockElapsedTimeSupplier(() -> mElapsedTimeMs);
+
+        mFakeClock.advanceCurrentTimeMillis(delta);
     }
 }
diff --git a/chrome/app/os_settings_strings.grdp b/chrome/app/os_settings_strings.grdp
index 692dc93..e9078de 100644
--- a/chrome/app/os_settings_strings.grdp
+++ b/chrome/app/os_settings_strings.grdp
@@ -1295,6 +1295,27 @@
   </message>
 
   <!-- Used by both Crostini and Plugin VM -->
+  <message name="IDS_SETTINGS_GUEST_OS_SHARED_PATHS" desc="Label for managing shared folders for Parallels.">
+    Manage shared folders
+  </message>
+  <message name="IDS_SETTINGS_GUEST_OS_SHARED_PATHS_LIST_HEADING" desc="Label for list of shared folders.">
+    Shared folders
+  </message>
+  <message name="IDS_SETTINGS_GUEST_OS_SHARED_PATHS_INSTRUCTIONS_REMOVE" desc="Instructions for removing shared folders in Parallels.">
+    Removing folders will stop sharing but will not delete files.
+  </message>
+  <message name="IDS_SETTINGS_GUEST_OS_SHARED_PATHS_STOP_SHARING" desc="Tooltip to show when hovering on the remove icon for a Parallels shared folder.">
+    Stop sharing
+  </message>
+  <message name="IDS_SETTINGS_GUEST_OS_SHARED_PATHS_REMOVE_FAILURE_DIALOG_TITLE" desc="Title of the error dialog shown to a user when unsharing a Parallels shared folder fails.">
+    Unshare failed
+  </message>
+  <message name="IDS_SETTINGS_GUEST_OS_SHARED_PATHS_REMOVE_FAILURE_TRY_AGAIN" desc="Button text in the dialog shown when unsharing a Parallels shared folder fails.  Pressing this button will attempt to unshare the folder again.">
+    Try again
+  </message>
+  <message name="IDS_SETTINGS_GUEST_OS_SHARED_PATHS_LIST_EMPTY_MESSAGE" desc="Message shown when the user has not yet shared any folders in Parallels.">
+    Shared folders will appear here
+  </message>
   <message name="IDS_SETTINGS_GUEST_OS_SHARED_USB_DEVICES_LABEL" desc="Label for managing shared USB devices.">
     Manage USB devices
   </message>
@@ -1318,36 +1339,15 @@
   <message name="IDS_SETTINGS_CROSTINI_LABEL" desc="The text associated with the primary section setting. This contains settings for a Linux container running in a virtual machine for use by software developers.">
     Linux development environment (Beta)
   </message>
-  <message name="IDS_SETTINGS_CROSTINI_SHARED_PATHS" desc="Label for managing shared folders in Crostini.">
-    Manage shared folders
-  </message>
-  <message name="IDS_SETTINGS_CROSTINI_SHARED_PATHS_LIST_HEADING" desc="Label for list of shared folders.">
-    Shared folders
-  </message>
   <message name="IDS_SETTINGS_CROSTINI_SHARED_PATHS_INSTRUCTIONS_LOCATE" desc="Instructions for how to locate shared folders in Crostini.">
     Shared folders are available in Linux at <ph name="BASE_DIR">$1<ex>/mnt/chromeos</ex></ph>.
   </message>
   <message name="IDS_SETTINGS_CROSTINI_SHARED_PATHS_INSTRUCTIONS_ADD" desc="Instructions for how to add shared folders in Crostini.">
     To share, right-click on a folder in Files app, then select "Share with Linux".
   </message>
-  <message name="IDS_SETTINGS_CROSTINI_SHARED_PATHS_INSTRUCTIONS_REMOVE" desc="Instructions for removing shared folders in Crostini.">
-    Removing folders from here will stop sharing but will not delete files.
-  </message>
-  <message name="IDS_SETTINGS_CROSTINI_SHARED_PATHS_REMOVE_SHARING" desc="Tooltip to show when hovering on the remove icon for a crostini shared folder.">
-    Remove sharing
-  </message>
   <message name="IDS_SETTINGS_CROSTINI_SHARED_PATHS_REMOVE_FAILURE_DIALOG_MESSAGE" desc="Message to show user when unsharing a crostini shared folder fails.">
     Couldn't unshare because an application is using this folder. The folder will be unshared when Linux is next shut down.
   </message>
-  <message name="IDS_SETTINGS_CROSTINI_SHARED_PATHS_REMOVE_FAILURE_DIALOG_TITLE" desc="Title of the error dialog shown to a user when unsharing a crostini shared folder fails.">
-    Unshare failed
-  </message>
-  <message name="IDS_SETTINGS_CROSTINI_SHARED_PATHS_REMOVE_FAILURE_TRY_AGAIN" desc="Button text in the dialog shown when unsharing a crostini shared folder fails.  Pressing this button will attempt to unshare the folder again.">
-    Try again
-  </message>
-  <message name="IDS_SETTINGS_CROSTINI_SHARED_PATHS_LIST_EMPTY_MESSAGE" desc="Message shown when the user has not yet shared any folders in Crostini.">
-    Shared folders will appear here
-  </message>
   <message name="IDS_SETTINGS_CROSTINI_EXPORT_IMPORT_TITLE" desc="Title for crostini container export and imoprt (backup and restore) section">
     Backup &amp; restore
   </message>
@@ -3373,36 +3373,15 @@
   </message>
 
   <!-- Apps > Manage your apps > Parallels > Shared folders -->
-  <message name="IDS_SETTINGS_APPS_PLUGIN_VM_SHARED_PATHS" desc="Label for managing shared folders for Parallels.">
-    Manage shared folders
-  </message>
-  <message name="IDS_SETTINGS_APPS_PLUGIN_VM_SHARED_PATHS_LIST_HEADING" desc="Label for list of shared folders.">
-    Shared folders
-  </message>
   <message name="IDS_SETTINGS_APPS_PLUGIN_VM_SHARED_PATHS_INSTRUCTIONS_LOCATE" desc="Instructions for how to locate shared folders in Parallels.">
       Shared folders are available in Windows at <ph name="BASE_DIR">$1<ex>Network › ChromeOS</ex></ph>.
   </message>
   <message name="IDS_SETTINGS_APPS_PLUGIN_VM_SHARED_PATHS_INSTRUCTIONS_ADD" desc="Instructions for how to add shared folders in Parallels.">
     To share, right-click on a folder in Files app, then select "Share with Parallels Desktop".
   </message>
-  <message name="IDS_SETTINGS_APPS_PLUGIN_VM_SHARED_PATHS_INSTRUCTIONS_REMOVE" desc="Instructions for removing shared folders in Parallels.">
-    Removing folders will stop sharing but will not delete files.
-  </message>
-  <message name="IDS_SETTINGS_APPS_PLUGIN_VM_SHARED_PATHS_REMOVE_SHARING" desc="Tooltip to show when hovering on the remove icon for a Parallels shared folder.">
-    Stop sharing
-  </message>
   <message name="IDS_SETTINGS_APPS_PLUGIN_VM_SHARED_PATHS_REMOVE_FAILURE_DIALOG_MESSAGE" desc="Message to show user when unsharing a Parallels shared folder fails. Do not translate 'Parallels Desktop'">
     Couldn't unshare because an application is using this folder. The folder will be unshared when Parallels Desktop is next shut down.
   </message>
-  <message name="IDS_SETTINGS_APPS_PLUGIN_VM_SHARED_PATHS_REMOVE_FAILURE_DIALOG_TITLE" desc="Title of the error dialog shown to a user when unsharing a Parallels shared folder fails.">
-    Unshare failed
-  </message>
-  <message name="IDS_SETTINGS_APPS_PLUGIN_VM_SHARED_PATHS_REMOVE_FAILURE_TRY_AGAIN" desc="Button text in the dialog shown when unsharing a Parallels shared folder fails.  Pressing this button will attempt to unshare the folder again.">
-    Try again
-  </message>
-  <message name="IDS_SETTINGS_APPS_PLUGIN_VM_SHARED_PATHS_LIST_EMPTY_MESSAGE" desc="Message shown when the user has not yet shared any folders in Parallels.">
-    Shared folders will appear here
-  </message>
   <message name="IDS_SETTINGS_APPS_PLUGIN_VM_SHARED_USB_DEVICES_DESCRIPTION" desc="Description for managing shared USB devices. Do not translate 'Parallels Desktop'.">
     Give Parallels Desktop permission to access USB devices. Parallels Desktop won't remember a USB device after it's removed.
   </message>
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_APPS_PLUGIN_VM_SHARED_PATHS_LIST_EMPTY_MESSAGE.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_APPS_PLUGIN_VM_SHARED_PATHS_LIST_EMPTY_MESSAGE.png.sha1
deleted file mode 100644
index bc1229e..0000000
--- a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_APPS_PLUGIN_VM_SHARED_PATHS_LIST_EMPTY_MESSAGE.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-7338b60167490936082e4a72214fda0a68a16807
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_APPS_PLUGIN_VM_SHARED_PATHS_REMOVE_FAILURE_DIALOG_TITLE.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_APPS_PLUGIN_VM_SHARED_PATHS_REMOVE_FAILURE_DIALOG_TITLE.png.sha1
deleted file mode 100644
index 04f962d..0000000
--- a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_APPS_PLUGIN_VM_SHARED_PATHS_REMOVE_FAILURE_DIALOG_TITLE.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-ec0fdd89ab5d4afc79c7e65d463ecece4925ac68
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_APPS_PLUGIN_VM_SHARED_PATHS_REMOVE_SHARING.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_GUEST_OS_SHARED_PATHS.png.sha1
similarity index 100%
copy from chrome/app/os_settings_strings_grdp/IDS_SETTINGS_APPS_PLUGIN_VM_SHARED_PATHS_REMOVE_SHARING.png.sha1
copy to chrome/app/os_settings_strings_grdp/IDS_SETTINGS_GUEST_OS_SHARED_PATHS.png.sha1
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_APPS_PLUGIN_VM_SHARED_PATHS_INSTRUCTIONS_REMOVE.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_GUEST_OS_SHARED_PATHS_INSTRUCTIONS_REMOVE.png.sha1
similarity index 100%
rename from chrome/app/os_settings_strings_grdp/IDS_SETTINGS_APPS_PLUGIN_VM_SHARED_PATHS_INSTRUCTIONS_REMOVE.png.sha1
rename to chrome/app/os_settings_strings_grdp/IDS_SETTINGS_GUEST_OS_SHARED_PATHS_INSTRUCTIONS_REMOVE.png.sha1
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_CROSTINI_SHARED_PATHS_LIST_EMPTY_MESSAGE.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_GUEST_OS_SHARED_PATHS_LIST_EMPTY_MESSAGE.png.sha1
similarity index 100%
rename from chrome/app/settings_strings_grdp/IDS_SETTINGS_CROSTINI_SHARED_PATHS_LIST_EMPTY_MESSAGE.png.sha1
rename to chrome/app/os_settings_strings_grdp/IDS_SETTINGS_GUEST_OS_SHARED_PATHS_LIST_EMPTY_MESSAGE.png.sha1
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_CROSTINI_SHARED_PATHS_LIST_HEADING.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_GUEST_OS_SHARED_PATHS_LIST_HEADING.png.sha1
similarity index 100%
rename from chrome/app/settings_strings_grdp/IDS_SETTINGS_CROSTINI_SHARED_PATHS_LIST_HEADING.png.sha1
rename to chrome/app/os_settings_strings_grdp/IDS_SETTINGS_GUEST_OS_SHARED_PATHS_LIST_HEADING.png.sha1
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_CROSTINI_SHARED_PATHS_REMOVE_FAILURE_DIALOG_TITLE.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_GUEST_OS_SHARED_PATHS_REMOVE_FAILURE_DIALOG_TITLE.png.sha1
similarity index 100%
rename from chrome/app/settings_strings_grdp/IDS_SETTINGS_CROSTINI_SHARED_PATHS_REMOVE_FAILURE_DIALOG_TITLE.png.sha1
rename to chrome/app/os_settings_strings_grdp/IDS_SETTINGS_GUEST_OS_SHARED_PATHS_REMOVE_FAILURE_DIALOG_TITLE.png.sha1
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_APPS_PLUGIN_VM_SHARED_PATHS_REMOVE_FAILURE_TRY_AGAIN.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_GUEST_OS_SHARED_PATHS_REMOVE_FAILURE_TRY_AGAIN.png.sha1
similarity index 100%
rename from chrome/app/os_settings_strings_grdp/IDS_SETTINGS_APPS_PLUGIN_VM_SHARED_PATHS_REMOVE_FAILURE_TRY_AGAIN.png.sha1
rename to chrome/app/os_settings_strings_grdp/IDS_SETTINGS_GUEST_OS_SHARED_PATHS_REMOVE_FAILURE_TRY_AGAIN.png.sha1
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_APPS_PLUGIN_VM_SHARED_PATHS_REMOVE_SHARING.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_GUEST_OS_SHARED_PATHS_STOP_SHARING.png.sha1
similarity index 100%
rename from chrome/app/os_settings_strings_grdp/IDS_SETTINGS_APPS_PLUGIN_VM_SHARED_PATHS_REMOVE_SHARING.png.sha1
rename to chrome/app/os_settings_strings_grdp/IDS_SETTINGS_GUEST_OS_SHARED_PATHS_STOP_SHARING.png.sha1
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_CROSTINI_SHARED_PATHS_INSTRUCTIONS_REMOVE.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_CROSTINI_SHARED_PATHS_INSTRUCTIONS_REMOVE.png.sha1
deleted file mode 100644
index 41b5b3f5..0000000
--- a/chrome/app/settings_strings_grdp/IDS_SETTINGS_CROSTINI_SHARED_PATHS_INSTRUCTIONS_REMOVE.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-5ac77392ae93168b2861bf4fda2359288f40df60
\ No newline at end of file
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_CROSTINI_SHARED_PATHS_REMOVE_FAILURE_TRY_AGAIN.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_CROSTINI_SHARED_PATHS_REMOVE_FAILURE_TRY_AGAIN.png.sha1
deleted file mode 100644
index a0047bd..0000000
--- a/chrome/app/settings_strings_grdp/IDS_SETTINGS_CROSTINI_SHARED_PATHS_REMOVE_FAILURE_TRY_AGAIN.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-f735083d7a1a1439fb455dbd6770cd16b5628b21
\ No newline at end of file
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_CROSTINI_SHARED_PATHS_REMOVE_SHARING.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_CROSTINI_SHARED_PATHS_REMOVE_SHARING.png.sha1
deleted file mode 100644
index 456acee..0000000
--- a/chrome/app/settings_strings_grdp/IDS_SETTINGS_CROSTINI_SHARED_PATHS_REMOVE_SHARING.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-4dcc02b4b67dadb033eadd0f51e34946d4b582b4
\ No newline at end of file
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 931d6170..86edc9e 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -2304,7 +2304,6 @@
     "//media/mojo/services",
     "//media/webrtc",
     "//mojo/core/embedder",
-    "//mojo/core/embedder:features",
     "//mojo/public/cpp/bindings",
     "//net",
     "//net:extras",
diff --git a/chrome/browser/DEPS b/chrome/browser/DEPS
index 3ae0cc75..3ac0950 100644
--- a/chrome/browser/DEPS
+++ b/chrome/browser/DEPS
@@ -533,7 +533,4 @@
   "chrome_content_browser_client\.cc" : [
     "+content/public/browser/tts_controller.h",
   ],
-  "about_flags\.cc" : [
-    "+mojo/core/embedder/features.h",
-  ]
 }
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index fb6f0b0..09b1c41 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -157,7 +157,6 @@
 #include "media/media_buildflags.h"
 #include "media/midi/midi_switches.h"
 #include "media/webrtc/webrtc_switches.h"
-#include "mojo/core/embedder/features.h"
 #include "net/base/features.h"
 #include "net/net_buildflags.h"
 #include "net/nqe/effective_connection_type.h"
@@ -3237,13 +3236,6 @@
      FEATURE_VALUE_TYPE(performance_manager::features::kDynamicTcmallocTuning)},
 #endif  // BUILDFLAG(USE_TCMALLOC)
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
-#if (defined(OS_CHROMEOS) || defined(OS_LINUX) || defined(OS_ANDROID)) && \
-    !defined(OS_NACL)
-    {"mojo-linux-sharedmem", flag_descriptions::kMojoLinuxChannelSharedMemName,
-     flag_descriptions::kMojoLinuxChannelSharedMemDescription,
-     kOsCrOS | kOsLinux | kOsAndroid,
-     FEATURE_VALUE_TYPE(mojo::core::kMojoLinuxChannelSharedMem)},
-#endif
 #if defined(OS_ANDROID)
     {"enable-site-isolation-for-password-sites",
      flag_descriptions::kSiteIsolationForPasswordSitesName,
@@ -6100,6 +6092,9 @@
     {"exo-gamepad-vibration", flag_descriptions::kExoGamepadVibrationName,
      flag_descriptions::kExoGamepadVibrationDescription, kOsCrOS,
      FEATURE_VALUE_TYPE(chromeos::features::kGamepadVibration)},
+    {"exo-lock-notification", flag_descriptions::kExoLockNotificationName,
+     flag_descriptions::kExoLockNotificationDescription, kOsCrOS,
+     FEATURE_VALUE_TYPE(chromeos::features::kExoLockNotification)},
     {"exo-ordinal-motion", flag_descriptions::kExoOrdinalMotionName,
      flag_descriptions::kExoOrdinalMotionDescription, kOsCrOS,
      FEATURE_VALUE_TYPE(chromeos::features::kExoOrdinalMotion)},
@@ -7042,6 +7037,14 @@
      FEATURE_VALUE_TYPE(ash::features::kWallpaperWebUI)},
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+    // TODO(b/177462291): make flag available on LaCrOS.
+    {"enable-vaapi-av1-decode-acceleration",
+     flag_descriptions::kVaapiAV1DecoderName,
+     flag_descriptions::kVaapiAV1DecoderDescription, kOsCrOS,
+     FEATURE_VALUE_TYPE(media::kVaapiAV1Decoder)},
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+
     // NOTE: Adding a new flag requires adding a corresponding entry to enum
     // "LoginCustomFlags" in tools/metrics/histograms/enums.xml. See "Flag
     // Histograms" in tools/metrics/histograms/README.md (run the
diff --git a/chrome/browser/accessibility/caption_controller.cc b/chrome/browser/accessibility/caption_controller.cc
index bc284a0f..61c7bfc 100644
--- a/chrome/browser/accessibility/caption_controller.cc
+++ b/chrome/browser/accessibility/caption_controller.cc
@@ -108,7 +108,8 @@
     // which case the UI will construct prematurely.
     // TODO(crbug.com/1160272): Check whether SODA has downloaded without
     // blocking the process.
-    if (speech::SodaInstaller::GetInstance()->IsSodaRegistered()) {
+    if (!base::FeatureList::IsEnabled(media::kUseSodaForLiveCaption) ||
+        speech::SodaInstaller::GetInstance()->IsSodaRegistered()) {
       UpdateUIEnabled();
     } else {
       // Register SODA component and download speech model.
diff --git a/chrome/browser/accessibility/soda_installer_impl.cc b/chrome/browser/accessibility/soda_installer_impl.cc
index 9a62105..44676d3e 100644
--- a/chrome/browser/accessibility/soda_installer_impl.cc
+++ b/chrome/browser/accessibility/soda_installer_impl.cc
@@ -97,8 +97,7 @@
 }
 
 bool SodaInstallerImpl::IsSodaRegistered() {
-  if (!base::FeatureList::IsEnabled(media::kUseSodaForLiveCaption))
-    return true;
+  DCHECK(base::FeatureList::IsEnabled(media::kUseSodaForLiveCaption));
   std::vector<std::string> component_ids =
       g_browser_process->component_updater()->GetComponentIDs();
   const bool has_soda = base::Contains(
diff --git a/chrome/browser/android/vr/arcore_device/fake_arcore.cc b/chrome/browser/android/vr/arcore_device/fake_arcore.cc
index 58c186d3..c90bcec 100644
--- a/chrome/browser/android/vr/arcore_device/fake_arcore.cc
+++ b/chrome/browser/android/vr/arcore_device/fake_arcore.cc
@@ -27,14 +27,26 @@
         required_features,
     const std::unordered_set<device::mojom::XRSessionFeature>&
         optional_features,
-    const std::vector<device::mojom::XRTrackedImagePtr>& tracked_images) {
+    const std::vector<device::mojom::XRTrackedImagePtr>& tracked_images,
+    base::Optional<ArCore::DepthSensingConfiguration> depth_sensing_config) {
   DCHECK(IsOnGlThread());
 
   std::unordered_set<device::mojom::XRSessionFeature> enabled_features;
   enabled_features.insert(required_features.begin(), required_features.end());
   enabled_features.insert(optional_features.begin(), optional_features.end());
 
-  return ArCore::InitializeResult(enabled_features);
+  // Fake device does not support depth for now:
+  if (base::Contains(required_features,
+                     device::mojom::XRSessionFeature::DEPTH)) {
+    return base::nullopt;
+  }
+
+  if (base::Contains(optional_features,
+                     device::mojom::XRSessionFeature::DEPTH)) {
+    enabled_features.erase(device::mojom::XRSessionFeature::DEPTH);
+  }
+
+  return ArCore::InitializeResult(enabled_features, base::nullopt);
 }
 
 void FakeArCore::SetDisplayGeometry(
diff --git a/chrome/browser/android/vr/arcore_device/fake_arcore.h b/chrome/browser/android/vr/arcore_device/fake_arcore.h
index b771bb0..66e2ab9 100644
--- a/chrome/browser/android/vr/arcore_device/fake_arcore.h
+++ b/chrome/browser/android/vr/arcore_device/fake_arcore.h
@@ -28,7 +28,8 @@
           required_features,
       const std::unordered_set<device::mojom::XRSessionFeature>&
           optional_features,
-      const std::vector<device::mojom::XRTrackedImagePtr>& tracked_images)
+      const std::vector<device::mojom::XRTrackedImagePtr>& tracked_images,
+      base::Optional<ArCore::DepthSensingConfiguration> depth_sensing_config)
       override;
   MinMaxRange GetTargetFramerateRange() override;
   void SetCameraTexture(uint32_t texture) override;
diff --git a/chrome/browser/ash/README.md b/chrome/browser/ash/README.md
index 20cdbe4..82ab04e 100644
--- a/chrome/browser/ash/README.md
+++ b/chrome/browser/ash/README.md
@@ -4,6 +4,14 @@
 This directory should contain non-UI Chrome OS specific code that has
 `chrome/browser` dependencies.
 
+The code in this directory should live in namespace ash. While code in
+//chrome is not supposed to be in any namespace, //chrome/browser/ash is
+technically part of the ash binary. The fact that it lives in //chrome/browser
+instead of in //ash is because top level product directories shouldn't be
+depended on by any other directory. In the future, when some of the
+dependencies from //chrome/browser/ash to //chrome/browser are sorted out,
+some of this code will move to //ash.
+
 As of January 2021, code from
 [`chrome/browser/chromeos`](/chrome/browser/chromeos/README.md) is migrating
 into this directory, as part of the [Lacros project](/docs/lacros.md).
diff --git a/chrome/browser/ash/accessibility/spoken_feedback_browsertest.cc b/chrome/browser/ash/accessibility/spoken_feedback_browsertest.cc
index 86043ac..7a01fff7 100644
--- a/chrome/browser/ash/accessibility/spoken_feedback_browsertest.cc
+++ b/chrome/browser/ash/accessibility/spoken_feedback_browsertest.cc
@@ -146,11 +146,12 @@
 
 void LoggedInSpokenFeedbackTest::EnableChromeVox() {
   // Test setup.
-  // Enable ChromeVox, wait for something to be spoken, and disable earcons.
+  // Enable ChromeVox, disable earcons and wait for key mappings to be fetched.
   ASSERT_FALSE(AccessibilityManager::Get()->IsSpokenFeedbackEnabled());
   // TODO(accessibility): fix console error/warnings and insantiate
   // |console_observer_| here.
 
+  // Load ChromeVox and block until it's fully loaded.
   AccessibilityManager::Get()->EnableSpokenFeedback(true);
   base::RunLoop loop;
   ExtensionLoadWaiterOneShot waiter;
@@ -158,8 +159,10 @@
                           loop.QuitClosure());
   loop.Run();
 
+  DisableEarcons();
+
   // Keyboard mappings are async loaded, so we need to wait until they are fully
-  // loaded to start testing.
+  // fetched to start testing.
   std::string script =
       R"JS(
           function waitForKeyMapLoad() {
@@ -177,9 +180,6 @@
           browser()->profile(), extension_misc::kChromeVoxExtensionId, script,
           extensions::browsertest_util::ScriptUserActivation::kDontActivate);
   ASSERT_EQ(result, "ok");
-
-  sm_.ExpectSpeechPattern("*");
-  sm_.Call([this]() { DisableEarcons(); });
 }
 
 // Flaky test, crbug.com/1081563
@@ -1058,7 +1058,7 @@
   sm_.Replay();
 }
 
-IN_PROC_BROWSER_TEST_P(SpokenFeedbackTest, DISABLED_SmartStickyMode) {
+IN_PROC_BROWSER_TEST_P(SpokenFeedbackTest, SmartStickyMode) {
   EnableChromeVox();
   sm_.Call([this]() {
     ui_test_utils::NavigateToURL(browser(),
diff --git a/chrome/browser/ash/account_manager/account_manager_edu_coexistence_controller.cc b/chrome/browser/ash/account_manager/account_manager_edu_coexistence_controller.cc
index 7a0ed993..30741a11 100644
--- a/chrome/browser/ash/account_manager/account_manager_edu_coexistence_controller.cc
+++ b/chrome/browser/ash/account_manager/account_manager_edu_coexistence_controller.cc
@@ -17,7 +17,9 @@
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
 
-namespace chromeos {
+namespace ash {
+
+namespace edu_coexistence = ::chromeos::edu_coexistence;
 
 void EduCoexistenceConsentInvalidationController::RegisterProfilePrefs(
     PrefRegistrySimple* registry) {
@@ -45,7 +47,7 @@
       device_account_id_(device_account_id) {
   DCHECK(profile_);
   DCHECK(profile_->IsChild());
-  DCHECK(chromeos::IsAccountManagerAvailable(profile_));
+  DCHECK(IsAccountManagerAvailable(profile_));
 }
 
 EduCoexistenceConsentInvalidationController::
@@ -158,4 +160,4 @@
   }
 }
 
-}  // namespace chromeos
+}  // namespace ash
diff --git a/chrome/browser/ash/account_manager/account_manager_edu_coexistence_controller.h b/chrome/browser/ash/account_manager/account_manager_edu_coexistence_controller.h
index 026ff8e..150ce53 100644
--- a/chrome/browser/ash/account_manager/account_manager_edu_coexistence_controller.h
+++ b/chrome/browser/ash/account_manager/account_manager_edu_coexistence_controller.h
@@ -10,16 +10,17 @@
 
 #include "base/callback.h"
 #include "base/memory/weak_ptr.h"
+// TODO(https://crbug.com/1164001): move to forward declaration when migrated to
+// ash/components/.
+#include "chromeos/components/account_manager/account_manager.h"
 #include "components/account_id/account_id.h"
 #include "components/account_manager_core/account.h"
 #include "components/prefs/pref_change_registrar.h"
 
-class Profile;
 class PrefRegistrySimple;
+class Profile;
 
-namespace chromeos {
-
-class AccountManager;
+namespace ash {
 
 // Listens to changes to chromeos::prefs::kEduCoexistenceToSVersion policy
 // preference and invalidates secondary edu accounts with outdated terms of
@@ -66,6 +67,6 @@
       weak_factory_{this};
 };
 
-}  // namespace chromeos
+}  // namespace ash
 
 #endif  // CHROME_BROWSER_ASH_ACCOUNT_MANAGER_ACCOUNT_MANAGER_EDU_COEXISTENCE_CONTROLLER_H_
diff --git a/chrome/browser/ash/account_manager/account_manager_edu_coexistence_controller_unittest.cc b/chrome/browser/ash/account_manager/account_manager_edu_coexistence_controller_unittest.cc
index c3661a7..b21f3cf1 100644
--- a/chrome/browser/ash/account_manager/account_manager_edu_coexistence_controller_unittest.cc
+++ b/chrome/browser/ash/account_manager/account_manager_edu_coexistence_controller_unittest.cc
@@ -26,10 +26,12 @@
 #include "services/network/test/test_url_loader_factory.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-namespace chromeos {
+namespace ash {
 
 namespace {
 
+namespace edu_coexistence = ::chromeos::edu_coexistence;
+
 constexpr char kValidToken[] = "valid-token";
 
 constexpr char kPrimaryAccount[] = "primaryaccount@gmail.com";
@@ -242,4 +244,4 @@
             "7");
 }
 
-}  // namespace chromeos
+}  // namespace ash
diff --git a/chrome/browser/ash/account_manager/account_manager_migrator.cc b/chrome/browser/ash/account_manager/account_manager_migrator.cc
index a6c3981b..f9dbbe8 100644
--- a/chrome/browser/ash/account_manager/account_manager_migrator.cc
+++ b/chrome/browser/ash/account_manager/account_manager_migrator.cc
@@ -47,7 +47,7 @@
 #include "components/webdata/common/web_data_service_consumer.h"
 #include "google_apis/gaia/gaia_auth_util.h"
 
-namespace chromeos {
+namespace ash {
 
 namespace {
 
@@ -497,7 +497,7 @@
 void AccountManagerMigrator::Start() {
   DVLOG(1) << "AccountManagerMigrator::Start";
 
-  if (!chromeos::IsAccountManagerAvailable(profile_))
+  if (!IsAccountManagerAvailable(profile_))
     return;
 
   if (migration_runner_ && (migration_runner_->GetStatus() ==
@@ -676,4 +676,4 @@
   return new AccountManagerMigrator(Profile::FromBrowserContext(context));
 }
 
-}  // namespace chromeos
+}  // namespace ash
diff --git a/chrome/browser/ash/account_manager/account_manager_migrator.h b/chrome/browser/ash/account_manager/account_manager_migrator.h
index 3c28dbd..1866e3a 100644
--- a/chrome/browser/ash/account_manager/account_manager_migrator.h
+++ b/chrome/browser/ash/account_manager/account_manager_migrator.h
@@ -21,7 +21,7 @@
 class NoDestructor;
 }  // namespace base
 
-namespace chromeos {
+namespace ash {
 
 class AccountManagerMigrator : public KeyedService {
  public:
@@ -94,6 +94,6 @@
   DISALLOW_COPY_AND_ASSIGN(AccountManagerMigratorFactory);
 };
 
-}  // namespace chromeos
+}  // namespace ash
 
 #endif  // CHROME_BROWSER_ASH_ACCOUNT_MANAGER_ACCOUNT_MANAGER_MIGRATOR_H_
diff --git a/chrome/browser/ash/account_manager/account_manager_policy_controller.cc b/chrome/browser/ash/account_manager/account_manager_policy_controller.cc
index db105b1b..01135e97 100644
--- a/chrome/browser/ash/account_manager/account_manager_policy_controller.cc
+++ b/chrome/browser/ash/account_manager/account_manager_policy_controller.cc
@@ -14,10 +14,11 @@
 #include "chrome/browser/chromeos/child_accounts/secondary_account_consent_logger.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/supervised_user/supervised_user_features.h"
+#include "chromeos/components/account_manager/account_manager.h"
 #include "chromeos/constants/chromeos_pref_names.h"
 #include "components/prefs/pref_service.h"
 
-namespace chromeos {
+namespace ash {
 
 AccountManagerPolicyController::AccountManagerPolicyController(
     Profile* profile,
@@ -34,7 +35,7 @@
 void AccountManagerPolicyController::Start() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-  if (!chromeos::IsAccountManagerAvailable(profile_))
+  if (!IsAccountManagerAvailable(profile_))
     return;
 
   pref_change_registrar_.Init(profile_->GetPrefs());
@@ -46,8 +47,8 @@
   // Take any necessary initial action based on the current state of the pref.
   OnSecondaryAccountsSigninAllowedPrefChanged();
 
-  chromeos::ChildAccountTypeChangedUserData* user_data =
-      chromeos::ChildAccountTypeChangedUserData::GetForProfile(profile_);
+  ChildAccountTypeChangedUserData* user_data =
+      ChildAccountTypeChangedUserData::GetForProfile(profile_);
   child_account_type_changed_subscription_ =
       user_data->RegisterCallback(base::BindRepeating(
           &AccountManagerPolicyController::OnChildAccountTypeChanged,
@@ -174,4 +175,4 @@
   edu_coexistence_consent_invalidation_controller_.reset();
 }
 
-}  // namespace chromeos
+}  // namespace ash
diff --git a/chrome/browser/ash/account_manager/account_manager_policy_controller.h b/chrome/browser/ash/account_manager/account_manager_policy_controller.h
index 276a1145..a12103ed 100644
--- a/chrome/browser/ash/account_manager/account_manager_policy_controller.h
+++ b/chrome/browser/ash/account_manager/account_manager_policy_controller.h
@@ -12,6 +12,8 @@
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "chrome/browser/ash/account_manager/child_account_type_changed_user_data.h"
+// TODO(https://crbug.com/1164001): move to forward declaration when migrated to
+// ash/components/.
 #include "chromeos/components/account_manager/account_manager.h"
 #include "components/account_id/account_id.h"
 #include "components/account_manager_core/account.h"
@@ -21,9 +23,7 @@
 
 class Profile;
 
-namespace chromeos {
-
-class AccountManager;
+namespace ash {
 class EduCoexistenceConsentInvalidationController;
 
 class AccountManagerPolicyController : public KeyedService {
@@ -33,7 +33,7 @@
                                  const AccountId& device_account_id);
   ~AccountManagerPolicyController() override;
 
-  // Starts applying the behaviour required by |chromeos::AccountManager|
+  // Starts applying the behaviour required by |AccountManager|
   // specific prefs and policies.
   void Start();
 
@@ -84,6 +84,6 @@
   DISALLOW_COPY_AND_ASSIGN(AccountManagerPolicyController);
 };
 
-}  // namespace chromeos
+}  // namespace ash
 
 #endif  // CHROME_BROWSER_ASH_ACCOUNT_MANAGER_ACCOUNT_MANAGER_POLICY_CONTROLLER_H_
diff --git a/chrome/browser/ash/account_manager/account_manager_policy_controller_browsertest.cc b/chrome/browser/ash/account_manager/account_manager_policy_controller_browsertest.cc
index 65e050b5..48ec297 100644
--- a/chrome/browser/ash/account_manager/account_manager_policy_controller_browsertest.cc
+++ b/chrome/browser/ash/account_manager/account_manager_policy_controller_browsertest.cc
@@ -22,8 +22,7 @@
 #include "content/public/test/browser_test.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-namespace chromeos {
-
+namespace ash {
 namespace {
 constexpr char kFakePrimaryUsername[] = "test-primary@example.com";
 constexpr char kFakeSecondaryUsername[] = "test-secondary@example.com";
@@ -133,8 +132,7 @@
   // (|true|).
   profile()->GetPrefs()->SetBoolean(
       chromeos::prefs::kSecondaryGoogleAccountSigninAllowed, true);
-  chromeos::ChildAccountTypeChangedUserData::GetForProfile(profile())->SetValue(
-      false);
+  ChildAccountTypeChangedUserData::GetForProfile(profile())->SetValue(false);
 
   // All accounts must be intact.
   accounts = GetAccountManagerAccounts();
@@ -181,8 +179,7 @@
   ASSERT_GT(initial_num_accounts, 1UL);
 
   // Disallow secondary account sign-ins.
-  chromeos::ChildAccountTypeChangedUserData::GetForProfile(profile())->SetValue(
-      true);
+  ChildAccountTypeChangedUserData::GetForProfile(profile())->SetValue(true);
 
   // Secondary Accounts must be removed.
   accounts = GetAccountManagerAccounts();
@@ -202,4 +199,4 @@
             accounts[0].key.id);
 }
 
-}  // namespace chromeos
+}  // namespace ash
diff --git a/chrome/browser/ash/account_manager/account_manager_policy_controller_factory.cc b/chrome/browser/ash/account_manager/account_manager_policy_controller_factory.cc
index 3d49ba17..ac58964 100644
--- a/chrome/browser/ash/account_manager/account_manager_policy_controller_factory.cc
+++ b/chrome/browser/ash/account_manager/account_manager_policy_controller_factory.cc
@@ -13,7 +13,7 @@
 #include "chromeos/components/account_manager/account_manager_factory.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
 
-namespace chromeos {
+namespace ash {
 
 // static
 AccountManagerPolicyController*
@@ -63,4 +63,4 @@
   return service;
 }
 
-}  // namespace chromeos
+}  // namespace ash
diff --git a/chrome/browser/ash/account_manager/account_manager_policy_controller_factory.h b/chrome/browser/ash/account_manager/account_manager_policy_controller_factory.h
index e75ff04..fd072710 100644
--- a/chrome/browser/ash/account_manager/account_manager_policy_controller_factory.h
+++ b/chrome/browser/ash/account_manager/account_manager_policy_controller_factory.h
@@ -15,8 +15,7 @@
 class NoDestructor;
 }  // namespace base
 
-namespace chromeos {
-
+namespace ash {
 class AccountManagerPolicyController;
 
 class AccountManagerPolicyControllerFactory
@@ -42,6 +41,6 @@
   DISALLOW_COPY_AND_ASSIGN(AccountManagerPolicyControllerFactory);
 };
 
-}  // namespace chromeos
+}  // namespace ash
 
 #endif  // CHROME_BROWSER_ASH_ACCOUNT_MANAGER_ACCOUNT_MANAGER_POLICY_CONTROLLER_FACTORY_H_
diff --git a/chrome/browser/ash/account_manager/account_manager_ui_impl.cc b/chrome/browser/ash/account_manager/account_manager_ui_impl.cc
index 3b26bc1..9b48fe5 100644
--- a/chrome/browser/ash/account_manager/account_manager_ui_impl.cc
+++ b/chrome/browser/ash/account_manager/account_manager_ui_impl.cc
@@ -5,7 +5,9 @@
 #include "chrome/browser/ash/account_manager/account_manager_ui_impl.h"
 #include "chrome/browser/ui/webui/signin/inline_login_dialog_chromeos.h"
 
-namespace chromeos {
+namespace ash {
+
+using ::chromeos::InlineLoginDialogChromeOS;
 
 AccountManagerUIImpl::AccountManagerUIImpl() = default;
 AccountManagerUIImpl::~AccountManagerUIImpl() = default;
@@ -25,4 +27,4 @@
   return InlineLoginDialogChromeOS::IsShown();
 }
 
-}  // namespace chromeos
+}  // namespace ash
diff --git a/chrome/browser/ash/account_manager/account_manager_ui_impl.h b/chrome/browser/ash/account_manager/account_manager_ui_impl.h
index 047bc5c..434004d 100644
--- a/chrome/browser/ash/account_manager/account_manager_ui_impl.h
+++ b/chrome/browser/ash/account_manager/account_manager_ui_impl.h
@@ -8,7 +8,7 @@
 #include "base/callback_forward.h"
 #include "chromeos/components/account_manager/account_manager_ui.h"
 
-namespace chromeos {
+namespace ash {
 
 class AccountManagerUIImpl : public AccountManagerUI {
  public:
@@ -25,6 +25,6 @@
   bool IsDialogShown() override;
 };
 
-}  // namespace chromeos
+}  // namespace ash
 
 #endif  // CHROME_BROWSER_ASH_ACCOUNT_MANAGER_ACCOUNT_MANAGER_UI_IMPL_H_
diff --git a/chrome/browser/ash/account_manager/account_manager_util.cc b/chrome/browser/ash/account_manager/account_manager_util.cc
index 4592886d..b635ae3 100644
--- a/chrome/browser/ash/account_manager/account_manager_util.cc
+++ b/chrome/browser/ash/account_manager/account_manager_util.cc
@@ -18,7 +18,7 @@
 #include "components/user_manager/user_manager.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 
-namespace chromeos {
+namespace ash {
 
 bool IsAccountManagerAvailable(const Profile* const profile) {
   // Signin Profile does not have any accounts associated with it,
@@ -63,4 +63,4 @@
       std::move(initialization_callback));
 }
 
-}  // namespace chromeos
+}  // namespace ash
diff --git a/chrome/browser/ash/account_manager/account_manager_util.h b/chrome/browser/ash/account_manager/account_manager_util.h
index 0f6284a3..e0b99680 100644
--- a/chrome/browser/ash/account_manager/account_manager_util.h
+++ b/chrome/browser/ash/account_manager/account_manager_util.h
@@ -10,7 +10,7 @@
 
 class Profile;
 
-namespace chromeos {
+namespace ash {
 
 bool IsAccountManagerAvailable(const Profile* const profile);
 
@@ -21,6 +21,6 @@
 void InitializeAccountManager(const base::FilePath& cryptohome_root_dir,
                               base::OnceClosure initialization_callback);
 
-}  // namespace chromeos
+}  // namespace ash
 
 #endif  // CHROME_BROWSER_ASH_ACCOUNT_MANAGER_ACCOUNT_MANAGER_UTIL_H_
diff --git a/chrome/browser/ash/account_manager/account_migration_runner.cc b/chrome/browser/ash/account_manager/account_migration_runner.cc
index 10a8c03..50532f0 100644
--- a/chrome/browser/ash/account_manager/account_migration_runner.cc
+++ b/chrome/browser/ash/account_manager/account_migration_runner.cc
@@ -10,7 +10,7 @@
 #include "base/bind.h"
 #include "base/metrics/histogram_functions.h"
 
-namespace chromeos {
+namespace ash {
 
 // Used in histograms.
 constexpr char kMigrationStepResultMetricName[] =
@@ -130,4 +130,4 @@
                                            failed_step_id /* failed_step */});
 }
 
-}  // namespace chromeos
+}  // namespace ash
diff --git a/chrome/browser/ash/account_manager/account_migration_runner.h b/chrome/browser/ash/account_manager/account_migration_runner.h
index 39f37c8..73fba57 100644
--- a/chrome/browser/ash/account_manager/account_migration_runner.h
+++ b/chrome/browser/ash/account_manager/account_migration_runner.h
@@ -14,7 +14,7 @@
 #include "base/memory/weak_ptr.h"
 #include "base/sequence_checker.h"
 
-namespace chromeos {
+namespace ash {
 
 // A utility class to run account migrations for |chromeos::AccountManager|. It
 // enables the specification of a series of async migration |Step|s in a
@@ -146,6 +146,6 @@
   DISALLOW_COPY_AND_ASSIGN(AccountMigrationRunner);
 };
 
-}  // namespace chromeos
+}  // namespace ash
 
 #endif  // CHROME_BROWSER_ASH_ACCOUNT_MANAGER_ACCOUNT_MIGRATION_RUNNER_H_
diff --git a/chrome/browser/ash/account_manager/account_migration_runner_unittest.cc b/chrome/browser/ash/account_manager/account_migration_runner_unittest.cc
index e27ecf4..0ca554e 100644
--- a/chrome/browser/ash/account_manager/account_migration_runner_unittest.cc
+++ b/chrome/browser/ash/account_manager/account_migration_runner_unittest.cc
@@ -15,7 +15,7 @@
 #include "base/test/task_environment.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-namespace chromeos {
+namespace ash {
 
 namespace {
 
@@ -196,4 +196,4 @@
   EXPECT_EQ(2, num_steps_executed_);
 }
 
-}  // namespace chromeos
+}  // namespace ash
diff --git a/chrome/browser/ash/account_manager/child_account_type_changed_user_data.cc b/chrome/browser/ash/account_manager/child_account_type_changed_user_data.cc
index 4e91653..d687062 100644
--- a/chrome/browser/ash/account_manager/child_account_type_changed_user_data.cc
+++ b/chrome/browser/ash/account_manager/child_account_type_changed_user_data.cc
@@ -6,7 +6,7 @@
 
 #include "chrome/browser/profiles/profile.h"
 
-namespace chromeos {
+namespace ash {
 
 namespace {
 const void* const kChildAccountTypeChangedUserDataUserKey =
@@ -47,4 +47,5 @@
     const base::RepeatingCallback<void(bool)>& cb) {
   return callback_list_.Add(cb);
 }
-}  // namespace chromeos
+
+}  // namespace ash
diff --git a/chrome/browser/ash/account_manager/child_account_type_changed_user_data.h b/chrome/browser/ash/account_manager/child_account_type_changed_user_data.h
index 39bbe05..6b350dde 100644
--- a/chrome/browser/ash/account_manager/child_account_type_changed_user_data.h
+++ b/chrome/browser/ash/account_manager/child_account_type_changed_user_data.h
@@ -11,7 +11,8 @@
 #include "base/supports_user_data.h"
 
 class Profile;
-namespace chromeos {
+
+namespace ash {
 
 class ChildAccountTypeChangedUserData : public base::SupportsUserData::Data {
  public:
@@ -36,6 +37,7 @@
   bool value_ = false;
   base::CallbackList<void(bool)> callback_list_;
 };
-}  // namespace chromeos
+
+}  // namespace ash
 
 #endif  // CHROME_BROWSER_ASH_ACCOUNT_MANAGER_CHILD_ACCOUNT_TYPE_CHANGED_USER_DATA_H_
diff --git a/chrome/browser/banners/app_banner_manager_desktop.h b/chrome/browser/banners/app_banner_manager_desktop.h
index 6ece0e4a..0814f2ad 100644
--- a/chrome/browser/banners/app_banner_manager_desktop.h
+++ b/chrome/browser/banners/app_banner_manager_desktop.h
@@ -64,6 +64,10 @@
       const blink::Manifest::RelatedApplication& related_app) const override;
   bool IsWebAppConsideredInstalled() const override;
 
+  // content::WebContentsObserver override.
+  void DidFinishLoad(content::RenderFrameHost* render_frame_host,
+                     const GURL& validated_url) override;
+
   // Called when the web app install initiated by a banner has completed.
   virtual void DidFinishCreatingWebApp(const web_app::AppId& app_id,
                                        web_app::InstallResultCode code);
@@ -78,10 +82,6 @@
   bool ShouldAllowWebAppReplacementInstall() override;
   void ShowBannerUi(WebappInstallSource install_source) override;
 
-  // content::WebContentsObserver override.
-  void DidFinishLoad(content::RenderFrameHost* render_frame_host,
-                     const GURL& validated_url) override;
-
   // SiteEngagementObserver override.
   void OnEngagementEvent(content::WebContents* web_contents,
                          const GURL& url,
diff --git a/chrome/browser/banners/test_app_banner_manager_desktop.cc b/chrome/browser/banners/test_app_banner_manager_desktop.cc
index 2d85d49..bb36845 100644
--- a/chrome/browser/banners/test_app_banner_manager_desktop.cc
+++ b/chrome/browser/banners/test_app_banner_manager_desktop.cc
@@ -117,6 +117,17 @@
   OnFinished();
 }
 
+void TestAppBannerManagerDesktop::DidFinishLoad(
+    content::RenderFrameHost* render_frame_host,
+    const GURL& validated_url) {
+  if (ShouldIgnore(render_frame_host, validated_url)) {
+    SetInstallable(false);
+    return;
+  }
+
+  AppBannerManagerDesktop::DidFinishLoad(render_frame_host, validated_url);
+}
+
 void TestAppBannerManagerDesktop::UpdateState(AppBannerManager::State state) {
   AppBannerManager::UpdateState(state);
 
@@ -128,7 +139,7 @@
 }
 
 void TestAppBannerManagerDesktop::SetInstallable(bool installable) {
-  DCHECK(!installable_.has_value());
+  DCHECK(!installable_.has_value() || installable_ == installable);
   installable_ = installable;
   if (installable_quit_closure_)
     std::move(installable_quit_closure_).Run();
diff --git a/chrome/browser/banners/test_app_banner_manager_desktop.h b/chrome/browser/banners/test_app_banner_manager_desktop.h
index 154b43b..e4fe41a 100644
--- a/chrome/browser/banners/test_app_banner_manager_desktop.h
+++ b/chrome/browser/banners/test_app_banner_manager_desktop.h
@@ -61,6 +61,8 @@
   void OnInstall(blink::mojom::DisplayMode display) override;
   void DidFinishCreatingWebApp(const web_app::AppId& app_id,
                                web_app::InstallResultCode code) override;
+  void DidFinishLoad(content::RenderFrameHost* render_frame_host,
+                     const GURL& validated_url) override;
   void UpdateState(AppBannerManager::State state) override;
 
  private:
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index f21470e..a1c9739 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -3773,7 +3773,7 @@
 #endif  // defined(OS_POSIX) && !defined(OS_MAC)
 
 #if defined(OS_WIN)
-base::string16 ChromeContentBrowserClient::GetAppContainerSidForSandboxType(
+std::wstring ChromeContentBrowserClient::GetAppContainerSidForSandboxType(
     sandbox::policy::SandboxType sandbox_type) {
   // TODO(wfh): Add support for more process types here. crbug.com/499523
   switch (sandbox_type) {
@@ -3803,7 +3803,7 @@
     case sandbox::policy::SandboxType::kMediaFoundationCdm:
       // Should never reach here.
       CHECK(0);
-      return base::string16();
+      return std::wstring();
   }
 }
 
diff --git a/chrome/browser/chrome_content_browser_client.h b/chrome/browser/chrome_content_browser_client.h
index 6dbae2cd..2328468 100644
--- a/chrome/browser/chrome_content_browser_client.h
+++ b/chrome/browser/chrome_content_browser_client.h
@@ -406,7 +406,7 @@
   bool PreSpawnChild(sandbox::TargetPolicy* policy,
                      sandbox::policy::SandboxType sandbox_type,
                      ChildSpawnFlags flags) override;
-  base::string16 GetAppContainerSidForSandboxType(
+  std::wstring GetAppContainerSidForSandboxType(
       sandbox::policy::SandboxType sandbox_type) override;
   bool IsRendererCodeIntegrityEnabled() override;
 #endif
diff --git a/chrome/browser/chromeos/arc/auth/arc_auth_service.cc b/chrome/browser/chromeos/arc/auth/arc_auth_service.cc
index 06247c6..ec42e5c 100644
--- a/chrome/browser/chromeos/arc/auth/arc_auth_service.cc
+++ b/chrome/browser/chromeos/arc/auth/arc_auth_service.cc
@@ -155,29 +155,29 @@
 }
 
 void TriggerAccountManagerMigrationsIfRequired(Profile* profile) {
-  if (!chromeos::IsAccountManagerAvailable(profile))
+  if (!ash::IsAccountManagerAvailable(profile))
     return;
 
-  chromeos::AccountManagerMigrator* const migrator =
-      chromeos::AccountManagerMigratorFactory::GetForBrowserContext(profile);
+  ash::AccountManagerMigrator* const migrator =
+      ash::AccountManagerMigratorFactory::GetForBrowserContext(profile);
   if (!migrator) {
     // Migrator can be null for ephemeral and kiosk sessions. Ignore those cases
     // since there are no accounts to be migrated in that case.
     return;
   }
-  const base::Optional<chromeos::AccountMigrationRunner::MigrationResult>
+  const base::Optional<ash::AccountMigrationRunner::MigrationResult>
       last_migration_run_result = migrator->GetLastMigrationRunResult();
 
   if (!last_migration_run_result)
     return;
 
   if (last_migration_run_result->final_status !=
-      chromeos::AccountMigrationRunner::Status::kFailure) {
+      ash::AccountMigrationRunner::Status::kFailure) {
     return;
   }
 
   if (last_migration_run_result->failed_step_id !=
-      chromeos::AccountManagerMigrator::kArcAccountsMigrationId) {
+      ash::AccountManagerMigrator::kArcAccountsMigrationId) {
     // Migrations failed but not because of ARC. ARC should not try to re-run
     // migrations in this case.
     return;
@@ -504,25 +504,25 @@
 
 void ArcAuthService::IsAccountManagerAvailable(
     IsAccountManagerAvailableCallback callback) {
-  std::move(callback).Run(chromeos::IsAccountManagerAvailable(profile_));
+  std::move(callback).Run(ash::IsAccountManagerAvailable(profile_));
 }
 
 void ArcAuthService::HandleAddAccountRequest() {
-  DCHECK(chromeos::IsAccountManagerAvailable(profile_));
+  DCHECK(ash::IsAccountManagerAvailable(profile_));
 
   chromeos::InlineLoginDialogChromeOS::ShowDeprecated(
       ::account_manager::AccountManagerFacade::AccountAdditionSource::kArc);
 }
 
 void ArcAuthService::HandleRemoveAccountRequest(const std::string& email) {
-  DCHECK(chromeos::IsAccountManagerAvailable(profile_));
+  DCHECK(ash::IsAccountManagerAvailable(profile_));
 
   chrome::SettingsWindowManager::GetInstance()->ShowOSSettings(
       profile_, chromeos::settings::mojom::kMyAccountsSubpagePath);
 }
 
 void ArcAuthService::HandleUpdateCredentialsRequest(const std::string& email) {
-  DCHECK(chromeos::IsAccountManagerAvailable(profile_));
+  DCHECK(ash::IsAccountManagerAvailable(profile_));
 
   chromeos::InlineLoginDialogChromeOS::ShowDeprecated(
       email,
@@ -535,7 +535,7 @@
   // proper Profile independent entity once we have that.
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
-  if (!chromeos::IsAccountManagerAvailable(profile_))
+  if (!ash::IsAccountManagerAvailable(profile_))
     return;
 
   // Ignore the update if ARC has not been provisioned yet.
@@ -571,7 +571,7 @@
     const AccountInfo& account_info) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
-  if (!chromeos::IsAccountManagerAvailable(profile_))
+  if (!ash::IsAccountManagerAvailable(profile_))
     return;
 
   DCHECK(!IsPrimaryGaiaAccount(account_info.gaia));
@@ -801,7 +801,7 @@
 }
 
 void ArcAuthService::TriggerAccountsPushToArc(bool filter_primary_account) {
-  if (!chromeos::IsAccountManagerAvailable(profile_))
+  if (!ash::IsAccountManagerAvailable(profile_))
     return;
 
   const std::vector<CoreAccountInfo> accounts =
diff --git a/chrome/browser/chromeos/arc/fileapi/arc_file_system_bridge.cc b/chrome/browser/chromeos/arc/fileapi/arc_file_system_bridge.cc
index cbaca25..3daa33ac 100644
--- a/chrome/browser/chromeos/arc/fileapi/arc_file_system_bridge.cc
+++ b/chrome/browser/chromeos/arc/fileapi/arc_file_system_bridge.cc
@@ -514,7 +514,7 @@
     case storage::FileSystemType::kFileSystemTypeDriveFs:
     case storage::FileSystemType::kFileSystemTypeSmbFs:
       return path;
-    case storage::FileSystemType::kFileSystemTypeNativeLocal: {
+    case storage::FileSystemType::kFileSystemTypeLocal: {
       base::FilePath crostini_mount_path =
           file_manager::util::GetCrostiniMountDirectory(profile);
       if (crostini_mount_path == path || crostini_mount_path.IsParent(path))
diff --git a/chrome/browser/chromeos/arc/fileapi/arc_file_system_bridge_unittest.cc b/chrome/browser/chromeos/arc/fileapi/arc_file_system_bridge_unittest.cc
index c12f72a..2110f3c 100644
--- a/chrome/browser/chromeos/arc/fileapi/arc_file_system_bridge_unittest.cc
+++ b/chrome/browser/chromeos/arc/fileapi/arc_file_system_bridge_unittest.cc
@@ -318,7 +318,7 @@
           "path/to/file");
   base::FilePath crostini_vfs_path =
       arc_file_system_bridge_->GetLinuxVFSPathForPathOnFileSystemType(
-          profile_, crostini_path, storage::kFileSystemTypeNativeLocal);
+          profile_, crostini_path, storage::kFileSystemTypeLocal);
   EXPECT_EQ(crostini_vfs_path, crostini_path);
 
   // Check: fuse-zip and rar2fs paths are returned as passed in.
@@ -327,17 +327,16 @@
           .Append("path/to/file");
   base::FilePath archive_vfs_path =
       arc_file_system_bridge_->GetLinuxVFSPathForPathOnFileSystemType(
-          profile_, archive_path, storage::kFileSystemTypeNativeLocal);
+          profile_, archive_path, storage::kFileSystemTypeLocal);
   EXPECT_EQ(archive_vfs_path, archive_path);
 
-  // Check: Other kFileSystemTypeNativeLocal paths that are not descendants of
+  // Check: Other kFileSystemTypeLocal paths that are not descendants of
   // the Crostini, fuse-zip or rar2fs mount points return an empty path.
   const base::FilePath empty_path,
       unsupported_local_path = base::FilePath("/path/to/file");
   base::FilePath unsupported_local_vfs_path =
       arc_file_system_bridge_->GetLinuxVFSPathForPathOnFileSystemType(
-          profile_, unsupported_local_path,
-          storage::kFileSystemTypeNativeLocal);
+          profile_, unsupported_local_path, storage::kFileSystemTypeLocal);
   EXPECT_EQ(empty_path, unsupported_local_vfs_path);
 
   // Check: Paths from unsupported FileSystemTypes return an empty path.
diff --git a/chrome/browser/chromeos/browser_context_keyed_service_factories.cc b/chrome/browser/chromeos/browser_context_keyed_service_factories.cc
index 0ee5e65..c3921bb 100644
--- a/chrome/browser/chromeos/browser_context_keyed_service_factories.cc
+++ b/chrome/browser/chromeos/browser_context_keyed_service_factories.cc
@@ -53,6 +53,8 @@
 
 namespace chromeos {
 
+using ::ash::AccountManagerMigratorFactory;
+
 void EnsureBrowserContextKeyedServiceFactoriesBuilt() {
   AccountManagerMigratorFactory::GetInstance();
   android_sms::AndroidSmsServiceFactory::GetInstance();
diff --git a/chrome/browser/chromeos/crostini/crostini_export_import_unittest.cc b/chrome/browser/chromeos/crostini/crostini_export_import_unittest.cc
index e7b4eaeb..f47107f 100644
--- a/chrome/browser/chromeos/crostini/crostini_export_import_unittest.cc
+++ b/chrome/browser/chromeos/crostini/crostini_export_import_unittest.cc
@@ -148,7 +148,7 @@
 
     storage::ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
         file_manager::util::GetDownloadsMountPointName(profile()),
-        storage::kFileSystemTypeNativeLocal, storage::FileSystemMountOption(),
+        storage::kFileSystemTypeLocal, storage::FileSystemMountOption(),
         profile()->GetPath());
     tarball_ = file_manager::util::GetMyFilesFolderForProfile(profile()).Append(
         "crostini_export_import_unittest_tarball.tar.gz");
diff --git a/chrome/browser/chromeos/crostini/crostini_manager.cc b/chrome/browser/chromeos/crostini/crostini_manager.cc
index 0c5aa47..ef03e559 100644
--- a/chrome/browser/chromeos/crostini/crostini_manager.cc
+++ b/chrome/browser/chromeos/crostini/crostini_manager.cc
@@ -759,7 +759,7 @@
     base::FilePath mount_path = base::FilePath(mount_info.mount_path);
     storage::ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
         file_manager::util::GetCrostiniMountPointName(profile_),
-        storage::kFileSystemTypeNativeLocal, storage::FileSystemMountOption(),
+        storage::kFileSystemTypeLocal, storage::FileSystemMountOption(),
         mount_path);
 
     // VolumeManager is null in unittest.
diff --git a/chrome/browser/chromeos/crostini/crostini_package_service_unittest.cc b/chrome/browser/chromeos/crostini/crostini_package_service_unittest.cc
index 0b7a4d0..974dfa6 100644
--- a/chrome/browser/chromeos/crostini/crostini_package_service_unittest.cc
+++ b/chrome/browser/chromeos/crostini/crostini_package_service_unittest.cc
@@ -191,7 +191,7 @@
     std::string mount_point_name =
         file_manager::util::GetDownloadsMountPointName(profile_.get());
     mount_points->RegisterFileSystem(
-        mount_point_name, storage::kFileSystemTypeNativeLocal,
+        mount_point_name, storage::kFileSystemTypeLocal,
         storage::FileSystemMountOption(),
         file_manager::util::GetDownloadsFolderForProfile(profile_.get()));
     package_file_url_ = mount_points->CreateExternalFileSystemURL(
diff --git a/chrome/browser/chromeos/exo/chrome_data_exchange_delegate_unittest.cc b/chrome/browser/chromeos/exo/chrome_data_exchange_delegate_unittest.cc
index 1fe8760..73906b0 100644
--- a/chrome/browser/chromeos/exo/chrome_data_exchange_delegate_unittest.cc
+++ b/chrome/browser/chromeos/exo/chrome_data_exchange_delegate_unittest.cc
@@ -83,14 +83,14 @@
     myfiles_dir_ =
         file_manager::util::GetMyFilesFolderForProfile(profile_.get());
     mount_points_->RegisterFileSystem(
-        myfiles_mount_name_, storage::kFileSystemTypeNativeLocal,
+        myfiles_mount_name_, storage::kFileSystemTypeLocal,
         storage::FileSystemMountOption(), myfiles_dir_);
     crostini_mount_name_ =
         file_manager::util::GetCrostiniMountPointName(profile_.get());
     crostini_dir_ =
         file_manager::util::GetCrostiniMountDirectory(profile_.get());
     mount_points_->RegisterFileSystem(
-        crostini_mount_name_, storage::kFileSystemTypeNativeLocal,
+        crostini_mount_name_, storage::kFileSystemTypeLocal,
         storage::FileSystemMountOption(), crostini_dir_);
 
     // DBus seneschal client.
diff --git a/chrome/browser/chromeos/extensions/file_manager/file_browser_handler_api_test.cc b/chrome/browser/chromeos/extensions/file_manager/file_browser_handler_api_test.cc
index cf4b972..7ff9744 100644
--- a/chrome/browser/chromeos/extensions/file_manager/file_browser_handler_api_test.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/file_browser_handler_api_test.cc
@@ -182,8 +182,7 @@
   // Creates new, test mount point.
   void AddTmpMountPoint(const std::string& extension_id) {
     BrowserContext::GetMountPoints(browser()->profile())
-        ->RegisterFileSystem("tmp",
-                             storage::kFileSystemTypeNativeLocal,
+        ->RegisterFileSystem("tmp", storage::kFileSystemTypeLocal,
                              storage::FileSystemMountOption(),
                              tmp_mount_point_);
   }
diff --git a/chrome/browser/chromeos/extensions/file_manager/file_manager_private_apitest.cc b/chrome/browser/chromeos/extensions/file_manager/file_manager_private_apitest.cc
index fb1f833..5e8b14e 100644
--- a/chrome/browser/chromeos/extensions/file_manager/file_manager_private_apitest.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/file_manager_private_apitest.cc
@@ -161,7 +161,7 @@
 
   ASSERT_TRUE(
       content::BrowserContext::GetMountPoints(profile)->RegisterFileSystem(
-          kLocalMountPointName, storage::kFileSystemTypeNativeLocal,
+          kLocalMountPointName, storage::kFileSystemTypeLocal,
           storage::FileSystemMountOption(), root));
   file_manager::VolumeManager::Get(profile)->AddVolumeForTesting(
       root, file_manager::VOLUME_TYPE_TESTING, chromeos::DEVICE_TYPE_UNKNOWN,
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_file_system.cc b/chrome/browser/chromeos/extensions/file_manager/private_api_file_system.cc
index 96d3066..943b064 100644
--- a/chrome/browser/chromeos/extensions/file_manager/private_api_file_system.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_file_system.cc
@@ -1352,7 +1352,7 @@
     return RespondNow(
         Error("FileSystemBackend failed to handle the entry's url."));
   }
-  if (file_system_url.type() != storage::kFileSystemTypeNativeLocal &&
+  if (file_system_url.type() != storage::kFileSystemTypeLocal &&
       file_system_url.type() != storage::kFileSystemTypeDriveFs) {
     return RespondNow(Error("Only local directories are supported."));
   }
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_misc.cc b/chrome/browser/chromeos/extensions/file_manager/private_api_misc.cc
index bb22ac9..4f3b2f1a 100644
--- a/chrome/browser/chromeos/extensions/file_manager/private_api_misc.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_misc.cc
@@ -189,7 +189,7 @@
       return true;
 
     case api::file_manager_private::SOURCE_RESTRICTION_NATIVE_SOURCE:
-      return type == storage::kFileSystemTypeNativeLocal;
+      return type == storage::kFileSystemTypeLocal;
   }
 }
 
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_thumbnail.cc b/chrome/browser/chromeos/extensions/file_manager/private_api_thumbnail.cc
index 5b447a29..3ff7b33 100644
--- a/chrome/browser/chromeos/extensions/file_manager/private_api_thumbnail.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_thumbnail.cc
@@ -206,7 +206,7 @@
   const storage::FileSystemURL file_system_url =
       file_system_context->CrackURL(url);
 
-  if (file_system_url.type() != storage::kFileSystemTypeNativeLocal) {
+  if (file_system_url.type() != storage::kFileSystemTypeLocal) {
     return RespondNow(Error("Expected a native local URL"));
   }
 
diff --git a/chrome/browser/chromeos/file_manager/external_filesystem_apitest.cc b/chrome/browser/chromeos/file_manager/external_filesystem_apitest.cc
index 2dbedec..b5eb735 100644
--- a/chrome/browser/chromeos/file_manager/external_filesystem_apitest.cc
+++ b/chrome/browser/chromeos/file_manager/external_filesystem_apitest.cc
@@ -428,7 +428,7 @@
   void AddTestMountPoint() override {
     EXPECT_TRUE(
         content::BrowserContext::GetMountPoints(profile())->RegisterFileSystem(
-            kLocalMountPointName, storage::kFileSystemTypeNativeLocal,
+            kLocalMountPointName, storage::kFileSystemTypeLocal,
             storage::FileSystemMountOption(), mount_point_dir_));
     VolumeManager::Get(profile())->AddVolumeForTesting(
         mount_point_dir_, VOLUME_TYPE_TESTING, chromeos::DEVICE_TYPE_UNKNOWN,
@@ -459,8 +459,7 @@
   void AddTestMountPoint() override {
     EXPECT_TRUE(
         content::BrowserContext::GetMountPoints(profile())->RegisterFileSystem(
-            kRestrictedMountPointName,
-            storage::kFileSystemTypeRestrictedNativeLocal,
+            kRestrictedMountPointName, storage::kFileSystemTypeRestrictedLocal,
             storage::FileSystemMountOption(), mount_point_dir_));
     VolumeManager::Get(profile())->AddVolumeForTesting(
         mount_point_dir_, VOLUME_TYPE_TESTING, chromeos::DEVICE_TYPE_UNKNOWN,
@@ -667,7 +666,7 @@
   void AddTestMountPoint() override {
     EXPECT_TRUE(
         content::BrowserContext::GetMountPoints(profile())->RegisterFileSystem(
-            kLocalMountPointName, storage::kFileSystemTypeNativeLocal,
+            kLocalMountPointName, storage::kFileSystemTypeLocal,
             storage::FileSystemMountOption(), local_mount_point_dir_));
     VolumeManager::Get(profile())->AddVolumeForTesting(
         local_mount_point_dir_, VOLUME_TYPE_TESTING,
diff --git a/chrome/browser/chromeos/file_manager/file_browser_handlers.cc b/chrome/browser/chromeos/file_manager/file_browser_handlers.cc
index 59136fc..3f72164 100644
--- a/chrome/browser/chromeos/file_manager/file_browser_handlers.cc
+++ b/chrome/browser/chromeos/file_manager/file_browser_handlers.cc
@@ -228,8 +228,8 @@
     base::FilePath virtual_path = url.virtual_path();
 
     const bool is_native_file =
-        url.type() == storage::kFileSystemTypeNativeLocal ||
-        url.type() == storage::kFileSystemTypeRestrictedNativeLocal;
+        url.type() == storage::kFileSystemTypeLocal ||
+        url.type() == storage::kFileSystemTypeRestrictedLocal;
 
     // If the file is from a physical volume, actual file must be found.
     if (is_native_file) {
diff --git a/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc b/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc
index ef99276b..3add445 100644
--- a/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc
+++ b/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc
@@ -1015,8 +1015,8 @@
     // Revoke name() mount point first, then re-add its mount point.
     GetMountPoints()->RevokeFileSystem(name());
     const bool added = GetMountPoints()->RegisterFileSystem(
-        name(), storage::kFileSystemTypeNativeLocal,
-        storage::FileSystemMountOption(), root_path());
+        name(), storage::kFileSystemTypeLocal, storage::FileSystemMountOption(),
+        root_path());
     if (!added)
       return false;
 
diff --git a/chrome/browser/chromeos/file_manager/file_tasks_notifier.cc b/chrome/browser/chromeos/file_manager/file_tasks_notifier.cc
index 79b7007..bf740f14 100644
--- a/chrome/browser/chromeos/file_manager/file_tasks_notifier.cc
+++ b/chrome/browser/chromeos/file_manager/file_tasks_notifier.cc
@@ -34,8 +34,8 @@
 
 bool IsSupportedFileSystemType(storage::FileSystemType type) {
   switch (type) {
-    case storage::kFileSystemTypeNativeLocal:
-    case storage::kFileSystemTypeRestrictedNativeLocal:
+    case storage::kFileSystemTypeLocal:
+    case storage::kFileSystemTypeRestrictedLocal:
     case storage::kFileSystemTypeDriveFs:
       return true;
     default:
diff --git a/chrome/browser/chromeos/file_manager/file_tasks_notifier_unittest.cc b/chrome/browser/chromeos/file_manager/file_tasks_notifier_unittest.cc
index 2666b43..1241975c 100644
--- a/chrome/browser/chromeos/file_manager/file_tasks_notifier_unittest.cc
+++ b/chrome/browser/chromeos/file_manager/file_tasks_notifier_unittest.cc
@@ -37,7 +37,7 @@
 
 storage::FileSystemURL CreateFileSystemUrl(
     const base::FilePath& path,
-    storage::FileSystemType type = storage::kFileSystemTypeNativeLocal) {
+    storage::FileSystemType type = storage::kFileSystemTypeLocal) {
   return storage::FileSystemURL::CreateForTest({}, {}, {}, "", type, path, "",
                                                {});
 }
@@ -140,7 +140,7 @@
     ASSERT_TRUE(base::CreateDirectory(my_files_));
     base::WriteFile(my_files_.Append("file"), "data", 4);
     ASSERT_TRUE(mount_points->RegisterFileSystem(
-        "downloads", storage::kFileSystemTypeNativeLocal, {}, my_files_));
+        "downloads", storage::kFileSystemTypeLocal, {}, my_files_));
     ASSERT_TRUE(mount_points->RegisterFileSystem(
         "drivefs", storage::kFileSystemTypeDriveFs, {},
         base::FilePath("/media/fuse/drivefs")));
diff --git a/chrome/browser/chromeos/file_manager/file_tasks_unittest.cc b/chrome/browser/chromeos/file_manager/file_tasks_unittest.cc
index 23282a9..694b022c 100644
--- a/chrome/browser/chromeos/file_manager/file_tasks_unittest.cc
+++ b/chrome/browser/chromeos/file_manager/file_tasks_unittest.cc
@@ -1400,7 +1400,7 @@
   void SetUp() override {
     storage::ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
         util::GetDownloadsMountPointName(&test_profile_),
-        storage::kFileSystemTypeNativeLocal, storage::FileSystemMountOption(),
+        storage::kFileSystemTypeLocal, storage::FileSystemMountOption(),
         util::GetMyFilesFolderForProfile(&test_profile_));
   }
 
diff --git a/chrome/browser/chromeos/file_manager/filesystem_api_util.cc b/chrome/browser/chromeos/file_manager/filesystem_api_util.cc
index 0c8d23f5..fb28fa2 100644
--- a/chrome/browser/chromeos/file_manager/filesystem_api_util.cc
+++ b/chrome/browser/chromeos/file_manager/filesystem_api_util.cc
@@ -127,8 +127,8 @@
 
 bool IsNonNativeFileSystemType(storage::FileSystemType type) {
   switch (type) {
-    case storage::kFileSystemTypeNativeLocal:
-    case storage::kFileSystemTypeRestrictedNativeLocal:
+    case storage::kFileSystemTypeLocal:
+    case storage::kFileSystemTypeRestrictedLocal:
     case storage::kFileSystemTypeDriveFs:
     case storage::kFileSystemTypeSmbFs:
       return false;
diff --git a/chrome/browser/chromeos/file_manager/guest_os_file_tasks_unittest.cc b/chrome/browser/chromeos/file_manager/guest_os_file_tasks_unittest.cc
index c2e56703..1d484b0 100644
--- a/chrome/browser/chromeos/file_manager/guest_os_file_tasks_unittest.cc
+++ b/chrome/browser/chromeos/file_manager/guest_os_file_tasks_unittest.cc
@@ -40,7 +40,7 @@
   void SetUp() override {
     storage::ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
         util::GetDownloadsMountPointName(&profile_),
-        storage::kFileSystemTypeNativeLocal, storage::FileSystemMountOption(),
+        storage::kFileSystemTypeLocal, storage::FileSystemMountOption(),
         util::GetMyFilesFolderForProfile(&profile_));
     fake_crostini_features_.set_enabled(true);
     fake_plugin_vm_features_.set_enabled(true);
diff --git a/chrome/browser/chromeos/file_manager/path_util_unittest.cc b/chrome/browser/chromeos/file_manager/path_util_unittest.cc
index 96621dc0..6019b12 100644
--- a/chrome/browser/chromeos/file_manager/path_util_unittest.cc
+++ b/chrome/browser/chromeos/file_manager/path_util_unittest.cc
@@ -98,9 +98,8 @@
 
   // Mount the volume to test the return from mount_points.
   storage::ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
-      GetDownloadsMountPointName(profile_.get()),
-      storage::kFileSystemTypeNativeLocal, storage::FileSystemMountOption(),
-      profile_path.Append("MyFiles"));
+      GetDownloadsMountPointName(profile_.get()), storage::kFileSystemTypeLocal,
+      storage::FileSystemMountOption(), profile_path.Append("MyFiles"));
 
   // When returning from the mount_point Downloads should still point to
   // MyFiles/Downloads.
@@ -333,27 +332,27 @@
 
   // Register crostini, my files, drive, android.
   mount_points->RegisterFileSystem(GetCrostiniMountPointName(profile_.get()),
-                                   storage::kFileSystemTypeNativeLocal,
+                                   storage::kFileSystemTypeLocal,
                                    storage::FileSystemMountOption(),
                                    GetCrostiniMountDirectory(profile_.get()));
   mount_points->RegisterFileSystem(GetDownloadsMountPointName(profile_.get()),
-                                   storage::kFileSystemTypeNativeLocal,
+                                   storage::kFileSystemTypeLocal,
                                    storage::FileSystemMountOption(),
                                    GetMyFilesFolderForProfile(profile_.get()));
   mount_points->RegisterFileSystem(
-      GetAndroidFilesMountPointName(), storage::kFileSystemTypeNativeLocal,
+      GetAndroidFilesMountPointName(), storage::kFileSystemTypeLocal,
       storage::FileSystemMountOption(), base::FilePath(kAndroidFilesPath));
   drive::DriveIntegrationService* integration_service =
       drive::DriveIntegrationServiceFactory::GetForProfile(profile_.get());
   base::FilePath mount_point_drive = integration_service->GetMountPointPath();
   mount_points->RegisterFileSystem(
-      mount_point_drive.BaseName().value(), storage::kFileSystemTypeNativeLocal,
+      mount_point_drive.BaseName().value(), storage::kFileSystemTypeLocal,
       storage::FileSystemMountOption(), mount_point_drive);
   mount_points->RegisterFileSystem(
-      chromeos::kSystemMountNameRemovable, storage::kFileSystemTypeNativeLocal,
+      chromeos::kSystemMountNameRemovable, storage::kFileSystemTypeLocal,
       storage::FileSystemMountOption(), base::FilePath(kRemovableMediaPath));
   mount_points->RegisterFileSystem(
-      chromeos::kSystemMountNameArchive, storage::kFileSystemTypeNativeLocal,
+      chromeos::kSystemMountNameArchive, storage::kFileSystemTypeLocal,
       storage::FileSystemMountOption(), base::FilePath(kArchiveMountPath));
   // SmbFsShare comes up with a unique stable ID for the share, it can
   // just be faked here, the mount ID is expected to appear in the mount
@@ -488,15 +487,15 @@
   std::string downloads_mount_name = GetDownloadsMountPointName(profile_.get());
   base::FilePath downloads = GetDownloadsFolderForProfile(profile_.get());
   mount_points->RegisterFileSystem(downloads_mount_name,
-                                   storage::kFileSystemTypeNativeLocal,
+                                   storage::kFileSystemTypeLocal,
                                    storage::FileSystemMountOption(), downloads);
   base::FilePath removable = base::FilePath(kRemovableMediaPath);
   mount_points->RegisterFileSystem(
-      chromeos::kSystemMountNameRemovable, storage::kFileSystemTypeNativeLocal,
+      chromeos::kSystemMountNameRemovable, storage::kFileSystemTypeLocal,
       storage::FileSystemMountOption(), base::FilePath(kRemovableMediaPath));
   base::FilePath archive = base::FilePath(kArchiveMountPath);
   mount_points->RegisterFileSystem(
-      chromeos::kSystemMountNameArchive, storage::kFileSystemTypeNativeLocal,
+      chromeos::kSystemMountNameArchive, storage::kFileSystemTypeLocal,
       storage::FileSystemMountOption(), base::FilePath(kArchiveMountPath));
   std::string relative_path_1 = "foo";
   std::string relative_path_2 = "foo/bar";
@@ -623,7 +622,7 @@
     // Add a crostini mount point for the primary profile.
     crostini_mount_point_ = GetCrostiniMountDirectory(primary_profile);
     mount_points->RegisterFileSystem(GetCrostiniMountPointName(primary_profile),
-                                     storage::kFileSystemTypeNativeLocal,
+                                     storage::kFileSystemTypeLocal,
                                      storage::FileSystemMountOption(),
                                      crostini_mount_point_);
 
diff --git a/chrome/browser/chromeos/file_manager/volume_manager.cc b/chrome/browser/chromeos/file_manager/volume_manager.cc
index 94e2ff87..eee28d1 100644
--- a/chrome/browser/chromeos/file_manager/volume_manager.cc
+++ b/chrome/browser/chromeos/file_manager/volume_manager.cc
@@ -82,10 +82,9 @@
   // In some tests we want to override existing Downloads mount point, so we
   // first revoke the existing mount point (if any).
   mount_points->RevokeFileSystem(mount_point_name);
-  return mount_points->RegisterFileSystem(mount_point_name,
-                                          storage::kFileSystemTypeNativeLocal,
-                                          storage::FileSystemMountOption(),
-                                          path);
+  return mount_points->RegisterFileSystem(
+      mount_point_name, storage::kFileSystemTypeLocal,
+      storage::FileSystemMountOption(), path);
 }
 
 // Registers a mount point for Android files to ExternalMountPoints.
@@ -94,7 +93,7 @@
       storage::ExternalMountPoints::GetSystemInstance();
   return mount_points->RegisterFileSystem(
       file_manager::util::GetAndroidFilesMountPointName(),
-      storage::kFileSystemTypeNativeLocal, storage::FileSystemMountOption(),
+      storage::kFileSystemTypeLocal, storage::FileSystemMountOption(),
       base::FilePath(util::kAndroidFilesPath));
 }
 
@@ -697,7 +696,7 @@
   bool result =
       storage::ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
           file_manager::util::GetAndroidFilesMountPointName(),
-          storage::kFileSystemTypeNativeLocal, storage::FileSystemMountOption(),
+          storage::kFileSystemTypeLocal, storage::FileSystemMountOption(),
           path);
   DCHECK(result);
   DoMountEvent(chromeos::MOUNT_ERROR_NONE, Volume::CreateForAndroidFiles(path));
@@ -753,7 +752,7 @@
   bool success =
       storage::ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
           file_manager::util::GetCrostiniMountPointName(profile_),
-          storage::kFileSystemTypeNativeLocal, storage::FileSystemMountOption(),
+          storage::kFileSystemTypeLocal, storage::FileSystemMountOption(),
           path);
   DoMountEvent(
       success ? chromeos::MOUNT_ERROR_NONE : chromeos::MOUNT_ERROR_INVALID_PATH,
diff --git a/chrome/browser/chromeos/fileapi/file_change_service_unittest.cc b/chrome/browser/chromeos/fileapi/file_change_service_unittest.cc
index db4dd930..559b863 100644
--- a/chrome/browser/chromeos/fileapi/file_change_service_unittest.cc
+++ b/chrome/browser/chromeos/fileapi/file_change_service_unittest.cc
@@ -113,7 +113,7 @@
 
     ASSERT_TRUE(
         storage::ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
-            name_, storage::kFileSystemTypeNativeLocal,
+            name_, storage::kFileSystemTypeLocal,
             storage::FileSystemMountOption(), temp_dir_.GetPath()));
 
     GetFileSystemContext(profile_)
@@ -131,7 +131,7 @@
   // Returns a file system URL for the specified path relative to `temp_dir_`.
   storage::FileSystemURL CreateFileSystemURL(const std::string& path) {
     return GetFileSystemContext(profile_)->CreateCrackedFileSystemURL(
-        origin_, storage::kFileSystemTypeNativeLocal,
+        origin_, storage::kFileSystemTypeLocal,
         temp_dir_.GetPath().Append(base::FilePath::FromUTF8Unsafe(path)));
   }
 
diff --git a/chrome/browser/chromeos/fileapi/file_system_backend.cc b/chrome/browser/chromeos/fileapi/file_system_backend.cc
index bccee2b..3578def 100644
--- a/chrome/browser/chromeos/fileapi/file_system_backend.cc
+++ b/chrome/browser/chromeos/fileapi/file_system_backend.cc
@@ -63,8 +63,8 @@
 bool FileSystemBackend::CanHandleURL(const storage::FileSystemURL& url) {
   if (!url.is_valid())
     return false;
-  return url.type() == storage::kFileSystemTypeNativeLocal ||
-         url.type() == storage::kFileSystemTypeRestrictedNativeLocal ||
+  return url.type() == storage::kFileSystemTypeLocal ||
+         url.type() == storage::kFileSystemTypeRestrictedLocal ||
          url.type() == storage::kFileSystemTypeProvided ||
          url.type() == storage::kFileSystemTypeDeviceMediaAsFileStorage ||
          url.type() == storage::kFileSystemTypeArcContent ||
@@ -104,15 +104,15 @@
   // already exists, hence it's safe to call without checking if a mount
   // point already exists or not.
   system_mount_points_->RegisterFileSystem(
-      kSystemMountNameArchive, storage::kFileSystemTypeNativeLocal,
+      kSystemMountNameArchive, storage::kFileSystemTypeLocal,
       storage::FileSystemMountOption(),
       chromeos::CrosDisksClient::GetArchiveMountPoint());
   system_mount_points_->RegisterFileSystem(
-      kSystemMountNameRemovable, storage::kFileSystemTypeNativeLocal,
+      kSystemMountNameRemovable, storage::kFileSystemTypeLocal,
       storage::FileSystemMountOption(storage::FlushPolicy::FLUSH_ON_COMPLETION),
       chromeos::CrosDisksClient::GetRemovableDiskMountPoint());
   system_mount_points_->RegisterFileSystem(
-      kSystemMountNameOem, storage::kFileSystemTypeRestrictedNativeLocal,
+      kSystemMountNameOem, storage::kFileSystemTypeRestrictedLocal,
       storage::FileSystemMountOption(),
       base::FilePath(FILE_PATH_LITERAL("/usr/share/oem")));
 }
@@ -120,9 +120,9 @@
 bool FileSystemBackend::CanHandleType(storage::FileSystemType type) const {
   switch (type) {
     case storage::kFileSystemTypeExternal:
-    case storage::kFileSystemTypeRestrictedNativeLocal:
-    case storage::kFileSystemTypeNativeLocal:
-    case storage::kFileSystemTypeNativeForPlatformApp:
+    case storage::kFileSystemTypeRestrictedLocal:
+    case storage::kFileSystemTypeLocal:
+    case storage::kFileSystemTypeLocalForPlatformApp:
     case storage::kFileSystemTypeDeviceMediaAsFileStorage:
     case storage::kFileSystemTypeProvided:
     case storage::kFileSystemTypeArcContent:
@@ -245,7 +245,7 @@
     return true;
 
   const std::string& extension_id = url.origin().host();
-  if (url.type() == storage::kFileSystemTypeRestrictedNativeLocal) {
+  if (url.type() == storage::kFileSystemTypeRestrictedLocal) {
     for (size_t i = 0; i < base::size(kOemAccessibleExtensions); ++i) {
       if (extension_id == kOemAccessibleExtensions[i])
         return true;
@@ -294,8 +294,8 @@
   switch (type) {
     case storage::kFileSystemTypeProvided:
       return file_system_provider_delegate_->GetAsyncFileUtil(type);
-    case storage::kFileSystemTypeNativeLocal:
-    case storage::kFileSystemTypeRestrictedNativeLocal:
+    case storage::kFileSystemTypeLocal:
+    case storage::kFileSystemTypeRestrictedLocal:
       return local_file_util_.get();
     case storage::kFileSystemTypeDeviceMediaAsFileStorage:
       return mtp_delegate_->GetAsyncFileUtil(type);
@@ -356,8 +356,8 @@
         std::make_unique<storage::FileSystemOperationContext>(
             context, MediaFileSystemBackend::MediaTaskRunner().get()));
   }
-  if (url.type() == storage::kFileSystemTypeNativeLocal ||
-      url.type() == storage::kFileSystemTypeRestrictedNativeLocal ||
+  if (url.type() == storage::kFileSystemTypeLocal ||
+      url.type() == storage::kFileSystemTypeRestrictedLocal ||
       url.type() == storage::kFileSystemTypeDriveFs ||
       url.type() == storage::kFileSystemTypeSmbFs) {
     return new ObservableFileSystemOperationImpl(
@@ -394,8 +394,8 @@
     // TODO(fukino): Support in-place copy for DocumentsProvider.
     // crbug.com/953603.
     case storage::kFileSystemTypeArcDocumentsProvider:
-    case storage::kFileSystemTypeNativeLocal:
-    case storage::kFileSystemTypeRestrictedNativeLocal:
+    case storage::kFileSystemTypeLocal:
+    case storage::kFileSystemTypeRestrictedLocal:
     case storage::kFileSystemTypeArcContent:
     // TODO(crbug.com/939235): Implement in-place copy in SmbFs.
     case storage::kFileSystemTypeSmbFs:
@@ -422,8 +422,8 @@
     case storage::kFileSystemTypeProvided:
       return file_system_provider_delegate_->CreateFileStreamReader(
           url, offset, max_bytes_to_read, expected_modification_time, context);
-    case storage::kFileSystemTypeNativeLocal:
-    case storage::kFileSystemTypeRestrictedNativeLocal:
+    case storage::kFileSystemTypeLocal:
+    case storage::kFileSystemTypeRestrictedLocal:
     case storage::kFileSystemTypeDriveFs:
     case storage::kFileSystemTypeSmbFs:
       return std::unique_ptr<storage::FileStreamReader>(
@@ -461,7 +461,7 @@
     case storage::kFileSystemTypeProvided:
       return file_system_provider_delegate_->CreateFileStreamWriter(
           url, offset, context);
-    case storage::kFileSystemTypeNativeLocal:
+    case storage::kFileSystemTypeLocal:
     case storage::kFileSystemTypeDriveFs:
     case storage::kFileSystemTypeSmbFs:
       return storage::FileStreamWriter::CreateForLocalFile(
@@ -475,7 +475,7 @@
       return arc_documents_provider_delegate_->CreateFileStreamWriter(
           url, offset, context);
     // Read only file systems.
-    case storage::kFileSystemTypeRestrictedNativeLocal:
+    case storage::kFileSystemTypeRestrictedLocal:
     case storage::kFileSystemTypeArcContent:
       return std::unique_ptr<storage::FileStreamWriter>();
     default:
@@ -508,8 +508,8 @@
     case storage::kFileSystemTypeDeviceMediaAsFileStorage:
       mtp_delegate_->GetRedirectURLForContents(url, std::move(callback));
       return;
-    case storage::kFileSystemTypeNativeLocal:
-    case storage::kFileSystemTypeRestrictedNativeLocal:
+    case storage::kFileSystemTypeLocal:
+    case storage::kFileSystemTypeRestrictedLocal:
     case storage::kFileSystemTypeArcContent:
     case storage::kFileSystemTypeArcDocumentsProvider:
     case storage::kFileSystemTypeDriveFs:
diff --git a/chrome/browser/chromeos/fileapi/file_system_backend_unittest.cc b/chrome/browser/chromeos/fileapi/file_system_backend_unittest.cc
index bd706020..8909376 100644
--- a/chrome/browser/chromeos/fileapi/file_system_backend_unittest.cc
+++ b/chrome/browser/chromeos/fileapi/file_system_backend_unittest.cc
@@ -82,22 +82,18 @@
   const size_t initial_root_dirs_size = backend.GetRootDirectories().size();
 
   // Register 'local' test mount points.
-  mount_points->RegisterFileSystem("c",
-                                   storage::kFileSystemTypeNativeLocal,
+  mount_points->RegisterFileSystem("c", storage::kFileSystemTypeLocal,
                                    storage::FileSystemMountOption(),
                                    base::FilePath(FPL("/a/b/c")));
-  mount_points->RegisterFileSystem("d",
-                                   storage::kFileSystemTypeNativeLocal,
+  mount_points->RegisterFileSystem("d", storage::kFileSystemTypeLocal,
                                    storage::FileSystemMountOption(),
                                    base::FilePath(FPL("/b/c/d")));
 
   // Register system test mount points.
-  system_mount_points->RegisterFileSystem("d",
-                                          storage::kFileSystemTypeNativeLocal,
+  system_mount_points->RegisterFileSystem("d", storage::kFileSystemTypeLocal,
                                           storage::FileSystemMountOption(),
                                           base::FilePath(FPL("/g/c/d")));
-  system_mount_points->RegisterFileSystem("e",
-                                          storage::kFileSystemTypeNativeLocal,
+  system_mount_points->RegisterFileSystem("e", storage::kFileSystemTypeLocal,
                                           storage::FileSystemMountOption(),
                                           base::FilePath(FPL("/g/d/e")));
 
@@ -129,20 +125,15 @@
 
   // Initialize mount points.
   ASSERT_TRUE(system_mount_points->RegisterFileSystem(
-      "system",
-      storage::kFileSystemTypeNativeLocal,
-      storage::FileSystemMountOption(),
+      "system", storage::kFileSystemTypeLocal, storage::FileSystemMountOption(),
       base::FilePath(FPL("/g/system"))));
   ASSERT_TRUE(mount_points->RegisterFileSystem(
-      "removable",
-      storage::kFileSystemTypeNativeLocal,
+      "removable", storage::kFileSystemTypeLocal,
       storage::FileSystemMountOption(),
       base::FilePath(FPL("/media/removable"))));
   ASSERT_TRUE(mount_points->RegisterFileSystem(
-      "oem",
-      storage::kFileSystemTypeRestrictedNativeLocal,
-      storage::FileSystemMountOption(),
-      base::FilePath(FPL("/usr/share/oem"))));
+      "oem", storage::kFileSystemTypeRestrictedLocal,
+      storage::FileSystemMountOption(), base::FilePath(FPL("/usr/share/oem"))));
 
   // Backend specific mount point access.
   EXPECT_FALSE(backend.IsAccessAllowed(
@@ -169,11 +160,9 @@
 
   // The extension cannot access new mount points.
   // TODO(tbarzic): This should probably be changed.
-  ASSERT_TRUE(
-      mount_points->RegisterFileSystem("test",
-                                       storage::kFileSystemTypeNativeLocal,
-                                       storage::FileSystemMountOption(),
-                                       base::FilePath(FPL("/foo/test"))));
+  ASSERT_TRUE(mount_points->RegisterFileSystem(
+      "test", storage::kFileSystemTypeLocal, storage::FileSystemMountOption(),
+      base::FilePath(FPL("/foo/test"))));
   EXPECT_FALSE(backend.IsAccessAllowed(
       CreateFileSystemURL(extension, "test_/foo", mount_points.get())));
 
@@ -197,7 +186,7 @@
       nullptr,  // smbfs_delegate
       mount_points.get(), system_mount_points.get());
 
-  const storage::FileSystemType type = storage::kFileSystemTypeNativeLocal;
+  const storage::FileSystemType type = storage::kFileSystemTypeLocal;
   const storage::FileSystemMountOption option =
       storage::FileSystemMountOption();
 
diff --git a/chrome/browser/chromeos/fileapi/recent_model_unittest.cc b/chrome/browser/chromeos/fileapi/recent_model_unittest.cc
index b1c3db3..c65dd0e98 100644
--- a/chrome/browser/chromeos/fileapi/recent_model_unittest.cc
+++ b/chrome/browser/chromeos/fileapi/recent_model_unittest.cc
@@ -28,7 +28,7 @@
                           const base::Time& last_modified) {
   storage::FileSystemURL url = storage::FileSystemURL::CreateForTest(
       url::Origin(),  // origin
-      storage::kFileSystemTypeNativeLocal, base::FilePath(name));
+      storage::kFileSystemTypeLocal, base::FilePath(name));
   return RecentFile(url, last_modified);
 }
 
diff --git a/chrome/browser/chromeos/full_restore/OWNERS b/chrome/browser/chromeos/full_restore/OWNERS
new file mode 100644
index 0000000..b1c6d30c
--- /dev/null
+++ b/chrome/browser/chromeos/full_restore/OWNERS
@@ -0,0 +1,2 @@
+dominickn@chromium.org
+nancylingwang@chromium.org
diff --git a/chrome/browser/chromeos/login/chrome_restart_request.cc b/chrome/browser/chromeos/login/chrome_restart_request.cc
index 13c4d1c..1feed77 100644
--- a/chrome/browser/chromeos/login/chrome_restart_request.cc
+++ b/chrome/browser/chromeos/login/chrome_restart_request.cc
@@ -30,6 +30,7 @@
 #include "chrome/common/chrome_paths.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/url_constants.h"
+#include "chromeos/constants/chromeos_features.h"
 #include "chromeos/constants/chromeos_switches.h"
 #include "chromeos/cryptohome/cryptohome_parameters.h"
 #include "chromeos/dbus/constants/dbus_switches.h"
@@ -210,6 +211,9 @@
     chromeos::switches::kEnterpriseEnableForcedReEnrollment,
     chromeos::switches::kFormFactor,
     chromeos::switches::kHasChromeOSKeyboard,
+    chromeos::switches::kLacrosChromeAdditionalArgs,
+    chromeos::switches::kLacrosChromeAdditionalEnv,
+    chromeos::switches::kLacrosChromePath,
     chromeos::switches::kLoginProfile,
     chromeos::switches::kNaturalScrollDefault,
     chromeos::switches::kRlzPingDelay,
@@ -236,6 +240,7 @@
 void DeriveEnabledFeatures(base::CommandLine* out_command_line) {
   static const base::Feature* kForwardEnabledFeatures[] = {
       &ash::features::kAutoNightLight,
+      &chromeos::features::kLacrosSupport,
   };
 
   std::vector<std::string> enabled_features;
diff --git a/chrome/browser/chromeos/login/easy_unlock/easy_unlock_service_regular.cc b/chrome/browser/chromeos/login/easy_unlock/easy_unlock_service_regular.cc
index 32347de3..ea13b9870 100644
--- a/chrome/browser/chromeos/login/easy_unlock/easy_unlock_service_regular.cc
+++ b/chrome/browser/chromeos/login/easy_unlock/easy_unlock_service_regular.cc
@@ -520,6 +520,7 @@
     RecordEasyUnlockScreenUnlockEvent(event);
 
     if (will_authenticate_using_easy_unlock()) {
+      // TODO(crbug.com/1171972): Deprecate the AuthMethodChoice metric.
       SmartLockMetricsRecorder::RecordSmartLockUnlockAuthMethodChoice(
           SmartLockMetricsRecorder::SmartLockAuthMethodChoice::kSmartLock);
       SmartLockMetricsRecorder::RecordAuthResultUnlockSuccess();
@@ -528,6 +529,7 @@
     } else {
       SmartLockMetricsRecorder::RecordAuthMethodChoiceUnlockPasswordState(
           GetSmartUnlockPasswordAuthEvent());
+      // TODO(crbug.com/1171972): Deprecate the AuthMethodChoice metric.
       SmartLockMetricsRecorder::RecordSmartLockUnlockAuthMethodChoice(
           SmartLockMetricsRecorder::SmartLockAuthMethodChoice::kOther);
       OnUserEnteredPassword();
diff --git a/chrome/browser/chromeos/login/easy_unlock/easy_unlock_service_signin_chromeos.cc b/chrome/browser/chromeos/login/easy_unlock/easy_unlock_service_signin_chromeos.cc
index ef0c5ae..81f766b5 100644
--- a/chrome/browser/chromeos/login/easy_unlock/easy_unlock_service_signin_chromeos.cc
+++ b/chrome/browser/chromeos/login/easy_unlock/easy_unlock_service_signin_chromeos.cc
@@ -362,12 +362,11 @@
       proximity_auth::ScreenlockBridge::LockHandler::SIGNIN_SCREEN)
     return;
 
-  // Only record metrics for users who have enabled the feature.
+  // TODO(crbug.com/1171972): Deprecate this metric. Note also that checking
+  // IsEnabled() here often incorrectly returns false, because
+  // OnScreenDidUnlock() is occurring during user session startup. See
+  // https://crbug.com/1154766 for more.
   if (IsEnabled()) {
-    if (will_authenticate_using_easy_unlock()) {
-      SmartLockMetricsRecorder::RecordAuthResultSignInSuccess();
-    }
-
     SmartLockMetricsRecorder::RecordSmartLockSignInAuthMethodChoice(
         will_authenticate_using_easy_unlock()
             ? SmartLockMetricsRecorder::SmartLockAuthMethodChoice::kSmartLock
diff --git a/chrome/browser/chromeos/login/easy_unlock/easy_unlock_user_login_flow.cc b/chrome/browser/chromeos/login/easy_unlock/easy_unlock_user_login_flow.cc
index a3cc7fc..88663f53 100644
--- a/chrome/browser/chromeos/login/easy_unlock/easy_unlock_user_login_flow.cc
+++ b/chrome/browser/chromeos/login/easy_unlock/easy_unlock_user_login_flow.cc
@@ -16,18 +16,20 @@
 EasyUnlockUserLoginFlow::~EasyUnlockUserLoginFlow() {}
 
 bool EasyUnlockUserLoginFlow::HandleLoginFailure(const AuthFailure& failure) {
-  SmartLockMetricsRecorder::RecordAuthResultSignInFailure(
-      SmartLockMetricsRecorder::SmartLockAuthResultFailureReason::
-          kUserControllerSignInFailure);
-  UMA_HISTOGRAM_ENUMERATION(
-      "SmartLock.AuthResult.SignIn.Failure.UserControllerAuth",
-      failure.reason(), AuthFailure::FailureReason::NUM_FAILURE_REASONS);
   Profile* profile = ProfileHelper::GetSigninProfile();
   EasyUnlockService* service = EasyUnlockService::Get(profile);
   if (!service)
     return false;
   service->HandleAuthFailure(account_id());
   service->RecordEasySignInOutcome(account_id(), false);
+
+  SmartLockMetricsRecorder::RecordAuthResultSignInFailure(
+      SmartLockMetricsRecorder::SmartLockAuthResultFailureReason::
+          kUserControllerSignInFailure);
+  UMA_HISTOGRAM_ENUMERATION(
+      "SmartLock.AuthResult.SignIn.Failure.UserControllerAuth",
+      failure.reason(), AuthFailure::FailureReason::NUM_FAILURE_REASONS);
+
   UnregisterFlowSoon();
   return true;
 }
@@ -38,6 +40,7 @@
   if (!service)
     return;
   service->RecordEasySignInOutcome(account_id(), true);
+  SmartLockMetricsRecorder::RecordAuthResultSignInSuccess();
 }
 
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/login/session/user_session_manager.cc b/chrome/browser/chromeos/login/session/user_session_manager.cc
index c3b60e2..cc4f071a 100644
--- a/chrome/browser/chromeos/login/session/user_session_manager.cc
+++ b/chrome/browser/chromeos/login/session/user_session_manager.cc
@@ -159,12 +159,13 @@
 #include "ui/base/ime/chromeos/input_method_util.h"
 #include "url/gurl.h"
 
-using signin::ConsentLevel;
-
 namespace chromeos {
 
 namespace {
 
+using ::ash::AccountManagerMigratorFactory;
+using ::signin::ConsentLevel;
+
 // Time to wait for child policy refresh. If that time is exceeded session
 // should start with cached policy.
 constexpr base::TimeDelta kWaitForChildPolicyTimeout =
@@ -1142,7 +1143,7 @@
       ProfileHelper::GetProfilePathByUserIdHash(user_context_.GetUserIDHash());
 
   if (ProfileHelper::IsRegularProfilePath(profile_path)) {
-    chromeos::InitializeAccountManager(
+    ash::InitializeAccountManager(
         profile_path,
         base::BindOnce(&UserSessionManager::PrepareProfile, AsWeakPtr(),
                        profile_path) /* initialization_callback */);
@@ -1321,7 +1322,19 @@
         identity_manager->GetPrimaryAccountMutator()
             ->SetUnconsentedPrimaryAccount(account_info->account_id);
       }
-      CHECK(identity_manager->HasPrimaryAccount(ConsentLevel::kNotRequired));
+
+      // TODO(https://crbug.com/1166265): Replace logs once issue is resolved.
+      if (!identity_manager->HasPrimaryAccount(ConsentLevel::kNotRequired)) {
+        if (account_info.has_value()) {
+          LOG(FATAL) << "IdentityManager missing primary account. "
+                     << "GAIA ID: " << gaia_id << ", "
+                     << "Account GAIA: " << account_info->gaia << ", "
+                     << "Account email: " << account_info->email;
+        } else {
+          LOG(FATAL) << "IdentityManager missing primary account. "
+                     << "GAIA ID: " << gaia_id << ", Account info missing";
+        }
+      }
       CHECK_EQ(
           identity_manager->GetPrimaryAccountInfo(ConsentLevel::kNotRequired)
               .gaia,
@@ -1332,8 +1345,25 @@
       // profile might only have an unconsented primary account.
       identity_manager->GetPrimaryAccountMutator()->SetPrimaryAccount(
           account_info->account_id);
-      CHECK(identity_manager->HasPrimaryAccount(ConsentLevel::kSync));
-      CHECK_EQ(identity_manager->GetPrimaryAccountInfo().gaia, gaia_id);
+
+      // TODO(https://crbug.com/1166265): Replace logs once issue is resolved.
+      if (!identity_manager->HasPrimaryAccount(ConsentLevel::kSync)) {
+        if (account_info.has_value()) {
+          LOG(FATAL) << "IdentityManager missing primary account. "
+                     << "GAIA ID: " << gaia_id << ", "
+                     << "Account GAIA: " << account_info->gaia << ", "
+                     << "Account email: " << account_info->email;
+        } else {
+          LOG(FATAL) << "IdentityManager missing primary account. "
+                     << "GAIA ID: " << gaia_id << ", Account info missing";
+        }
+      }
+      if (identity_manager->GetPrimaryAccountInfo().gaia != gaia_id) {
+        LOG(FATAL) << "IdentityManager GAIA ID is mismatched. "
+                   << "Primary account GAIA ID: "
+                   << identity_manager->GetPrimaryAccountInfo().gaia
+                   << ", Expected: " << gaia_id;
+      }
     }
 
     CoreAccountId account_id =
@@ -2022,8 +2052,7 @@
 
 void UserSessionManager::StartAccountManagerMigration(Profile* profile) {
   // `migrator` is nullptr for incognito profiles.
-  auto* migrator =
-      chromeos::AccountManagerMigratorFactory::GetForBrowserContext(profile);
+  auto* migrator = AccountManagerMigratorFactory::GetForBrowserContext(profile);
   if (migrator)
     migrator->Start();
 }
diff --git a/chrome/browser/chromeos/login/signin/signin_error_notifier_ash.cc b/chrome/browser/chromeos/login/signin/signin_error_notifier_ash.cc
index 58f2435..0f7ef6a0 100644
--- a/chrome/browser/chromeos/login/signin/signin_error_notifier_ash.cc
+++ b/chrome/browser/chromeos/login/signin/signin_error_notifier_ash.cc
@@ -168,7 +168,7 @@
 
   const AccountId account_id =
       multi_user_util::GetAccountIdFromProfile(profile_);
-  if (!chromeos::IsAccountManagerAvailable(profile_)) {
+  if (!ash::IsAccountManagerAvailable(profile_)) {
     // If this flag is disabled, Chrome OS does not have a concept of Secondary
     // Accounts. Preserve existing behavior.
     RecordReauthReason(account_id, chromeos::ReauthReason::SYNC_FAILED);
diff --git a/chrome/browser/chromeos/plugin_vm/plugin_vm_files_unittest.cc b/chrome/browser/chromeos/plugin_vm/plugin_vm_files_unittest.cc
index 887f353..db4bae6 100644
--- a/chrome/browser/chromeos/plugin_vm/plugin_vm_files_unittest.cc
+++ b/chrome/browser/chromeos/plugin_vm/plugin_vm_files_unittest.cc
@@ -68,7 +68,7 @@
     mount_points_ = storage::ExternalMountPoints::GetSystemInstance();
     mount_name_ = file_manager::util::GetDownloadsMountPointName(&profile_);
     mount_points_->RegisterFileSystem(
-        mount_name_, storage::kFileSystemTypeNativeLocal,
+        mount_name_, storage::kFileSystemTypeLocal,
         storage::FileSystemMountOption(), GetMyFilesFolderPath());
   }
 
@@ -212,7 +212,7 @@
 
   // Path in different volume.
   mount_points_->RegisterFileSystem(
-      "other-volume", storage::kFileSystemTypeNativeLocal,
+      "other-volume", storage::kFileSystemTypeLocal,
       storage::FileSystemMountOption(), GetMyFilesFolderPath());
   storage::FileSystemURL url = mount_points_->CreateExternalFileSystemURL(
       url::Origin(), "other-volume", base::FilePath("other/volume"));
diff --git a/chrome/browser/chromeos/policy/status_collector/device_status_collector_browsertest.cc b/chrome/browser/chromeos/policy/status_collector/device_status_collector_browsertest.cc
index 8d1faf7..23dab9b5 100644
--- a/chrome/browser/chromeos/policy/status_collector/device_status_collector_browsertest.cc
+++ b/chrome/browser/chromeos/policy/status_collector/device_status_collector_browsertest.cc
@@ -812,8 +812,8 @@
     // Setup a fake file system that should show up in mount points.
     storage::ExternalMountPoints::GetSystemInstance()->RevokeAllFileSystems();
     storage::ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
-        "c", storage::kFileSystemTypeNativeLocal,
-        storage::FileSystemMountOption(), base::FilePath(kExternalMountPoint));
+        "c", storage::kFileSystemTypeLocal, storage::FileSystemMountOption(),
+        base::FilePath(kExternalMountPoint));
 
     // Just verify that we are properly setting the mount points.
     std::vector<storage::MountPoints::MountPointInfo> external_mount_points;
diff --git a/chrome/browser/chromeos/ui/screen_capture_notification_ui_chromeos.cc b/chrome/browser/chromeos/ui/screen_capture_notification_ui_chromeos.cc
index 1da4472..fb22c19e 100644
--- a/chrome/browser/chromeos/ui/screen_capture_notification_ui_chromeos.cc
+++ b/chrome/browser/chromeos/ui/screen_capture_notification_ui_chromeos.cc
@@ -37,11 +37,6 @@
   return 0;
 }
 
-void ScreenCaptureNotificationUIChromeOS::SetStopCallback(
-    base::OnceClosure stop_callback) {
-  stop_callback_ = std::move(stop_callback);
-}
-
 void ScreenCaptureNotificationUIChromeOS::ProcessStopRequestFromUI() {
   if (!stop_callback_.is_null()) {
     std::move(stop_callback_).Run();
diff --git a/chrome/browser/chromeos/ui/screen_capture_notification_ui_chromeos.h b/chrome/browser/chromeos/ui/screen_capture_notification_ui_chromeos.h
index c159133..7eb10ab6 100644
--- a/chrome/browser/chromeos/ui/screen_capture_notification_ui_chromeos.h
+++ b/chrome/browser/chromeos/ui/screen_capture_notification_ui_chromeos.h
@@ -23,8 +23,6 @@
       base::OnceClosure stop_callback,
       content::MediaStreamUI::SourceCallback source_callback) override;
 
-  void SetStopCallback(base::OnceClosure stop_callback) override;
-
  private:
   void ProcessStopRequestFromUI();
 
diff --git a/chrome/browser/chromeos/usb/cros_usb_detector.cc b/chrome/browser/chromeos/usb/cros_usb_detector.cc
index c1839336..dba9576 100644
--- a/chrome/browser/chromeos/usb/cros_usb_detector.cc
+++ b/chrome/browser/chromeos/usb/cros_usb_detector.cc
@@ -557,6 +557,15 @@
   new_device.sharable_with_crostini =
       has_supported_interface || new_device.allowed_interfaces_mask != 0;
 
+  // Storage devices already plugged in at log-in time will already be mounted.
+  for (const auto& iter : disks::DiskMountManager::GetInstance()->disks()) {
+    if (iter.second->bus_number() == device_info->bus_number &&
+        iter.second->device_number() == device_info->port_number &&
+        iter.second->is_mounted()) {
+      new_device.mount_points.insert(iter.second->mount_path());
+    }
+  }
+
   usb_devices_.push_back(new_device);
   available_device_info_.emplace(device_info->guid, device_info.Clone());
   SignalUsbDeviceObservers();
diff --git a/chrome/browser/chromeos/usb/cros_usb_detector_unittest.cc b/chrome/browser/chromeos/usb/cros_usb_detector_unittest.cc
index f977506..b71f675a 100644
--- a/chrome/browser/chromeos/usb/cros_usb_detector_unittest.cc
+++ b/chrome/browser/chromeos/usb/cros_usb_detector_unittest.cc
@@ -250,6 +250,8 @@
       const std::string& name,
       chromeos::disks::DiskMountManager::MountEvent event,
       chromeos::MountError mount_error = chromeos::MOUNT_ERROR_NONE) {
+    // In theory we should also clear the mounted flag from the disk, but we
+    // don't rely on that.
     chromeos::disks::DiskMountManager::MountPointInfo info(
         "/dev/" + name, "/mount/" + name, chromeos::MOUNT_TYPE_DEVICE,
         chromeos::disks::MOUNT_CONDITION_NONE);
@@ -1090,14 +1092,23 @@
   ConnectToDeviceManager();
   base::RunLoop().RunUntilIdle();
 
+  // Disks mounted before the usb device is detected by the CrosUsbDetector
+  // require a prompt.
+  AddDisk("disk_early", 1, 5, true);
+
   device_manager_.CreateAndAddDevice(
       0x0200, 0xff, 0xff, 0xff, 0x0100, 1, 2, /*bus_number=*/1,
       /*port_number=*/5, kManufacturerName, kProductName_1, "5");
   base::RunLoop().RunUntilIdle();
 
   auto device_info = GetSingleDeviceInfo();
+  EXPECT_TRUE(cros_usb_detector_->SharingRequiresReassignPrompt(device_info));
+
+  NotifyMountEvent("disk_early", chromeos::disks::DiskMountManager::UNMOUNTING);
+  device_info = GetSingleDeviceInfo();
   EXPECT_FALSE(cros_usb_detector_->SharingRequiresReassignPrompt(device_info));
 
+  // A disk which fails to mount shouldn't cause the prompt to be shown.
   AddDisk("disk_error", 1, 5, /*mounted=*/false);
   NotifyMountEvent("disk_error", chromeos::disks::DiskMountManager::MOUNTING,
                    chromeos::MOUNT_ERROR_INTERNAL);
@@ -1107,9 +1118,4 @@
   AddDisk("disk_success", 1, 5, true);
   device_info = GetSingleDeviceInfo();
   EXPECT_TRUE(cros_usb_detector_->SharingRequiresReassignPrompt(device_info));
-
-  NotifyMountEvent("disk_success",
-                   chromeos::disks::DiskMountManager::UNMOUNTING);
-  device_info = GetSingleDeviceInfo();
-  EXPECT_FALSE(cros_usb_detector_->SharingRequiresReassignPrompt(device_info));
 }
diff --git a/chrome/browser/devtools/devtools_file_helper.cc b/chrome/browser/devtools/devtools_file_helper.cc
index 2a7c15ae..bde2af7 100644
--- a/chrome/browser/devtools/devtools_file_helper.cc
+++ b/chrome/browser/devtools/devtools_file_helper.cc
@@ -155,7 +155,7 @@
   std::string root_name(kRootName);
   storage::IsolatedContext::ScopedFSHandle file_system =
       isolated_context()->RegisterFileSystemForPath(
-          storage::kFileSystemTypeNativeLocal, std::string(), path, &root_name);
+          storage::kFileSystemTypeLocal, std::string(), path, &root_name);
 
   content::ChildProcessSecurityPolicy* policy =
       content::ChildProcessSecurityPolicy::GetInstance();
diff --git a/chrome/browser/extensions/api/developer_private/developer_private_api.cc b/chrome/browser/extensions/api/developer_private/developer_private_api.cc
index ed13891..e0618a9a 100644
--- a/chrome/browser/extensions/api/developer_private/developer_private_api.cc
+++ b/chrome/browser/extensions/api/developer_private/developer_private_api.cc
@@ -1433,8 +1433,8 @@
       storage::kFileSystemTypeIsolated, virtual_path);
 
   if (directory_url.is_valid() &&
-      directory_url.type() != storage::kFileSystemTypeNativeLocal &&
-      directory_url.type() != storage::kFileSystemTypeRestrictedNativeLocal &&
+      directory_url.type() != storage::kFileSystemTypeLocal &&
+      directory_url.type() != storage::kFileSystemTypeRestrictedLocal &&
       directory_url.type() != storage::kFileSystemTypeDragged) {
     return LoadByFileSystemAPI(directory_url);
   }
diff --git a/chrome/browser/extensions/api/file_system/chrome_file_system_delegate.cc b/chrome/browser/extensions/api/file_system/chrome_file_system_delegate.cc
index 303e330..8acf3137 100644
--- a/chrome/browser/extensions/api/file_system/chrome_file_system_delegate.cc
+++ b/chrome/browser/extensions/api/file_system/chrome_file_system_delegate.cc
@@ -171,7 +171,7 @@
   std::string register_name = "fs";
   const storage::IsolatedContext::ScopedFSHandle file_system =
       isolated_context->RegisterFileSystemForPath(
-          storage::kFileSystemTypeNativeForPlatformApp,
+          storage::kFileSystemTypeLocalForPlatformApp,
           std::string() /* file_system_id */, original_url.path(),
           &register_name);
   if (!file_system.is_valid()) {
diff --git a/chrome/browser/extensions/api/file_system/file_system_apitest_chromeos.cc b/chrome/browser/extensions/api/file_system/file_system_apitest_chromeos.cc
index 0273ed21..0f653bfb 100644
--- a/chrome/browser/extensions/api/file_system/file_system_apitest_chromeos.cc
+++ b/chrome/browser/extensions/api/file_system/file_system_apitest_chromeos.cc
@@ -275,7 +275,7 @@
         base::CreateDirectory(mount_point_path.Append(kChildDirectory)));
     ASSERT_TRUE(content::BrowserContext::GetMountPoints(browser()->profile())
                     ->RegisterFileSystem(
-                        mount_point_name, storage::kFileSystemTypeNativeLocal,
+                        mount_point_name, storage::kFileSystemTypeLocal,
                         storage::FileSystemMountOption(), mount_point_path));
     VolumeManager* const volume_manager =
         VolumeManager::Get(browser()->profile());
diff --git a/chrome/browser/extensions/api/settings_private/prefs_util.cc b/chrome/browser/extensions/api/settings_private/prefs_util.cc
index 42952b84..e08c8ae 100644
--- a/chrome/browser/extensions/api/settings_private/prefs_util.cc
+++ b/chrome/browser/extensions/api/settings_private/prefs_util.cc
@@ -475,6 +475,8 @@
   (*s_allowlist)
       [ash::prefs::kAccessibilityScreenMagnifierFocusFollowingEnabled] =
           settings_api::PrefType::PREF_TYPE_BOOLEAN;
+  (*s_allowlist)[ash::prefs::kAccessibilityScreenMagnifierMousePanningMode] =
+      settings_api::PrefType::PREF_TYPE_NUMBER;
   (*s_allowlist)[ash::prefs::kAccessibilityScreenMagnifierScale] =
       settings_api::PrefType::PREF_TYPE_NUMBER;
   (*s_allowlist)[ash::prefs::kAccessibilitySelectToSpeakEnabled] =
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 750fae6..696b0c3 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -2418,6 +2418,11 @@
     "expiry_milestone": 90
   },
   {
+    "name": "enable-vaapi-av1-decode-acceleration",
+    "owners": [ "andrescj", "chromeos-gfx-video@google.com" ],
+    "expiry_milestone": 93
+  },
+  {
     "name": "enable-vaapi-jpeg-image-decode-acceleration",
     "owners": [ "andrescj", "chromeos-gfx@google.com" ],
     "expiry_milestone": 90
@@ -2619,6 +2624,11 @@
     "expiry_milestone": 90
   },
   {
+    "name": "exo-lock-notification",
+    "owners": [ "cpelling@google.com" ],
+    "expiry_milestone": 95
+  },
+  {
     "name": "exo-ordinal-motion",
     "owners": [ "hollingum@google.com" ],
     "expiry_milestone": 90
@@ -3421,11 +3431,6 @@
     "expiry_milestone": 92
   },
   {
-    "name": "mojo-linux-sharedmem",
-    "owners": ["bgeffon", "rockot"],
-    "expiry_milestone": 99
-  },
-  {
     "name": "mouse-subframe-no-implicit-capture",
     "owners": [ "eirage", "nzolghadr", "input-dev" ],
     "expiry_milestone": 84
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 880be9bb..8f64505 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -1442,12 +1442,6 @@
 const char kMobilePwaInstallUseBottomSheetDescription[] =
     "Enables use of a rich bottom sheet when offering mobile PWA installation.";
 
-const char kMojoLinuxChannelSharedMemName[] =
-    "Enable Mojo Shared Memory Channel";
-const char kMojoLinuxChannelSharedMemDescription[] =
-    "If enabled Mojo on Linux based platforms can use shared memory as an "
-    "alternate channel for most messages.";
-
 const char kMouseSubframeNoImplicitCaptureName[] =
     "Disable mouse implicit capture for iframe";
 const char kMouseSubframeNoImplicitCaptureDescription[] =
@@ -4282,6 +4276,11 @@
     "Allow Linux applications to request a pointer lock, i.e. exclusive use of "
     "the mouse pointer.";
 
+const char kExoLockNotificationName[] = "Notification bubble for UI lock";
+const char kExoLockNotificationDescription[] =
+    "Show a notification bubble once an application has switched to "
+    "non-immersive fullscreen mode or obtained pointer lock.";
+
 const char kExperimentalAccessibilityChromeVoxAnnotationsName[] =
     "Enable experimental ChromeVox annotations feature.";
 const char kExperimentalAccessibilityChromeVoxAnnotationsDescription[] =
@@ -4727,6 +4726,10 @@
     "Enable unified media view to browse recently-modified media files from"
     " local disk, Google Drive, and Android.";
 
+const char kVaapiAV1DecoderName[] = "VA-API decode acceleration for AV1";
+const char kVaapiAV1DecoderDescription[] =
+    "Enable or disable decode acceleration of AV1 videos using the VA-API.";
+
 const char kVaapiJpegImageDecodeAccelerationName[] =
     "VA-API JPEG decode acceleration for images";
 const char kVaapiJpegImageDecodeAccelerationDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index c45847fe..5ee5276 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -853,9 +853,6 @@
 extern const char kMobilePwaInstallUseBottomSheetName[];
 extern const char kMobilePwaInstallUseBottomSheetDescription[];
 
-extern const char kMojoLinuxChannelSharedMemName[];
-extern const char kMojoLinuxChannelSharedMemDescription[];
-
 extern const char kMouseSubframeNoImplicitCaptureName[];
 extern const char kMouseSubframeNoImplicitCaptureDescription[];
 
@@ -2499,6 +2496,9 @@
 extern const char kExoPointerLockName[];
 extern const char kExoPointerLockDescription[];
 
+extern const char kExoLockNotificationName[];
+extern const char kExoLockNotificationDescription[];
+
 extern const char kExperimentalAccessibilityChromeVoxAnnotationsName[];
 extern const char kExperimentalAccessibilityChromeVoxAnnotationsDescription[];
 
@@ -2760,6 +2760,10 @@
 extern const char kUseFakeDeviceForMediaStreamName[];
 extern const char kUseFakeDeviceForMediaStreamDescription[];
 
+// TODO(b/177462291): make flag available on LaCrOS.
+extern const char kVaapiAV1DecoderName[];
+extern const char kVaapiAV1DecoderDescription[];
+
 extern const char kVaapiJpegImageDecodeAccelerationName[];
 extern const char kVaapiJpegImageDecodeAccelerationDescription[];
 
diff --git a/chrome/browser/media/router/mojo/media_router_desktop.cc b/chrome/browser/media/router/mojo/media_router_desktop.cc
index 3f831b0c..acce2f6 100644
--- a/chrome/browser/media/router/mojo/media_router_desktop.cc
+++ b/chrome/browser/media/router/mojo/media_router_desktop.cc
@@ -132,8 +132,6 @@
   config->use_mirroring_service = true;
   std::move(callback).Run(instance_id(), std::move(config));
 
-  SyncStateToMediaRouteProvider(provider_id);
-
   if (provider_id == MediaRouteProviderId::EXTENSION) {
     RegisterExtensionMediaRouteProvider(std::move(media_route_provider_remote));
   } else {
@@ -144,6 +142,8 @@
                        weak_factory_.GetWeakPtr(), provider_id));
     media_route_providers_[provider_id] = std::move(bound_remote);
   }
+
+  SyncStateToMediaRouteProvider(provider_id);
 }
 
 void MediaRouterDesktop::OnSinksReceived(
diff --git a/chrome/browser/media/router/mojo/media_router_mojo_impl.cc b/chrome/browser/media/router/mojo/media_router_mojo_impl.cc
index 65e82da..4166679a 100644
--- a/chrome/browser/media/router/mojo/media_router_mojo_impl.cc
+++ b/chrome/browser/media/router/mojo/media_router_mojo_impl.cc
@@ -1089,6 +1089,7 @@
       }
       break;
     case MediaRouteProviderId::ANDROID_CAF:
+    case MediaRouteProviderId::TEST:
     case MediaRouteProviderId::UNKNOWN:
       break;
   }
diff --git a/chrome/browser/media/router/mojo/media_router_mojo_impl.h b/chrome/browser/media/router/mojo/media_router_mojo_impl.h
index 49679d6f..02af2dc 100644
--- a/chrome/browser/media/router/mojo/media_router_mojo_impl.h
+++ b/chrome/browser/media/router/mojo/media_router_mojo_impl.h
@@ -156,6 +156,7 @@
   friend class MediaRouterFactory;
   friend class MediaRouterMojoImplTest;
   friend class MediaRouterMojoTest;
+  friend class MediaRouterNativeIntegrationBrowserTest;
   FRIEND_TEST_ALL_PREFIXES(MediaRouterMojoImplTest, JoinRouteTimedOutFails);
   FRIEND_TEST_ALL_PREFIXES(MediaRouterMojoImplTest,
                            JoinRouteIncognitoMismatchFails);
diff --git a/chrome/browser/media/router/providers/test/test_media_route_provider.cc b/chrome/browser/media/router/providers/test/test_media_route_provider.cc
index 95bc9cf..4581fd37 100644
--- a/chrome/browser/media/router/providers/test/test_media_route_provider.cc
+++ b/chrome/browser/media/router/providers/test/test_media_route_provider.cc
@@ -37,7 +37,7 @@
 }  // namespace
 
 const MediaRouteProviderId TestMediaRouteProvider::kProviderId =
-    MediaRouteProviderId::CAST;
+    MediaRouteProviderId::TEST;
 
 TestMediaRouteProvider::TestMediaRouteProvider(
     mojo::PendingReceiver<mojom::MediaRouteProvider> receiver,
@@ -49,11 +49,17 @@
       kProviderId, mojom::MediaRouter::SinkAvailability::PER_SOURCE);
 }
 
+TestMediaRouteProvider::~TestMediaRouteProvider() = default;
+
 void TestMediaRouteProvider::SetSinks() {
   MediaSinkInternal sink_internal_1;
   MediaSinkInternal sink_internal_2;
-  sink_internal_1.set_sink(MediaSink("id1", "test-sink-1", SinkIconType::CAST));
-  sink_internal_2.set_sink(MediaSink("id2", "test-sink-2", SinkIconType::CAST));
+  MediaSink sink1("id1", "test-sink-1", SinkIconType::CAST);
+  MediaSink sink2("id2", "test-sink-2", SinkIconType::CAST);
+  sink1.set_provider_id(kProviderId);
+  sink2.set_provider_id(kProviderId);
+  sink_internal_1.set_sink(sink1);
+  sink_internal_2.set_sink(sink2);
   sinks_ = {sink_internal_1, sink_internal_2};
 }
 
@@ -207,29 +213,19 @@
 }
 
 void TestMediaRouteProvider::StopObservingMediaSinks(
-    const std::string& media_source) {
-  NOTIMPLEMENTED();
-}
+    const std::string& media_source) {}
 
 void TestMediaRouteProvider::StartObservingMediaRoutes(
-    const std::string& media_source) {
-  NOTIMPLEMENTED();
-}
+    const std::string& media_source) {}
 
 void TestMediaRouteProvider::StopObservingMediaRoutes(
-    const std::string& media_source) {
-  NOTIMPLEMENTED();
-}
+    const std::string& media_source) {}
 
 void TestMediaRouteProvider::StartListeningForRouteMessages(
-    const std::string& route_id) {
-  NOTIMPLEMENTED();
-}
+    const std::string& route_id) {}
 
 void TestMediaRouteProvider::StopListeningForRouteMessages(
-    const std::string& route_id) {
-  NOTIMPLEMENTED();
-}
+    const std::string& route_id) {}
 
 void TestMediaRouteProvider::DetachRoute(const std::string& route_id) {
   media_router_->OnPresentationConnectionClosed(
@@ -237,12 +233,9 @@
       "Close route");
 }
 
-void TestMediaRouteProvider::EnableMdnsDiscovery() {
-  NOTIMPLEMENTED();
-}
+void TestMediaRouteProvider::EnableMdnsDiscovery() {}
 
 void TestMediaRouteProvider::UpdateMediaSinks(const std::string& media_source) {
-  NOTIMPLEMENTED();
 }
 
 void TestMediaRouteProvider::CreateMediaRouteController(
@@ -250,13 +243,15 @@
     mojo::PendingReceiver<mojom::MediaController> media_controller,
     mojo::PendingRemote<mojom::MediaStatusObserver> observer,
     CreateMediaRouteControllerCallback callback) {
-  NOTIMPLEMENTED();
+  std::move(callback).Run(false);
 }
 
 void TestMediaRouteProvider::ProvideSinks(
     const std::string& provider_name,
-    const std::vector<media_router::MediaSinkInternal>& sinks) {
-  NOTIMPLEMENTED();
+    const std::vector<media_router::MediaSinkInternal>& sinks) {}
+
+void TestMediaRouteProvider::GetState(GetStateCallback callback) {
+  std::move(callback).Run(nullptr);
 }
 
 std::vector<MediaRoute> TestMediaRouteProvider::GetMediaRoutes() {
diff --git a/chrome/browser/media/router/providers/test/test_media_route_provider.h b/chrome/browser/media/router/providers/test/test_media_route_provider.h
index 6a3f274..a0aa03d 100644
--- a/chrome/browser/media/router/providers/test/test_media_route_provider.h
+++ b/chrome/browser/media/router/providers/test/test_media_route_provider.h
@@ -75,6 +75,7 @@
   void ProvideSinks(
       const std::string& provider_name,
       const std::vector<media_router::MediaSinkInternal>& sinks) override;
+  void GetState(GetStateCallback callback) override;
 
  private:
   void set_close_route_error_on_send() {
diff --git a/chrome/browser/media/webrtc/media_stream_capture_indicator.cc b/chrome/browser/media/webrtc/media_stream_capture_indicator.cc
index 1a8cf3d..aa23936 100644
--- a/chrome/browser/media/webrtc/media_stream_capture_indicator.cc
+++ b/chrome/browser/media/webrtc/media_stream_capture_indicator.cc
@@ -248,12 +248,6 @@
 #endif
   }
 
-  void SetStopCallback(base::OnceClosure stop_callback) override {
-    if (ui_) {
-      ui_->SetStopCallback(std::move(stop_callback));
-    }
-  }
-
   base::WeakPtr<WebContentsDeviceUsage> device_usage_;
   const blink::MediaStreamDevices devices_;
   const std::unique_ptr<::MediaStreamUI> ui_;
diff --git a/chrome/browser/media/webrtc/media_stream_capture_indicator.h b/chrome/browser/media/webrtc/media_stream_capture_indicator.h
index c200c34c..18dc1a8 100644
--- a/chrome/browser/media/webrtc/media_stream_capture_indicator.h
+++ b/chrome/browser/media/webrtc/media_stream_capture_indicator.h
@@ -41,9 +41,6 @@
   virtual gfx::NativeViewId OnStarted(
       base::OnceClosure stop_callback,
       content::MediaStreamUI::SourceCallback source_callback) = 0;
-
-  // Replaces the stop callback set in OnStarted(), if any.
-  virtual void SetStopCallback(base::OnceClosure stop) = 0;
 };
 
 // Keeps track of which WebContents are capturing media streams. Used to display
diff --git a/chrome/browser/media/webrtc/webrtc_logging_controller.cc b/chrome/browser/media/webrtc/webrtc_logging_controller.cc
index 1a5a4d7..69680324 100644
--- a/chrome/browser/media/webrtc/webrtc_logging_controller.cc
+++ b/chrome/browser/media/webrtc/webrtc_logging_controller.cc
@@ -303,9 +303,9 @@
 
   std::string registered_name;
   storage::IsolatedContext::ScopedFSHandle file_system =
-      isolated_context->RegisterFileSystemForPath(
-          storage::kFileSystemTypeNativeLocal, std::string(), logs_path,
-          &registered_name);
+      isolated_context->RegisterFileSystemForPath(storage::kFileSystemTypeLocal,
+                                                  std::string(), logs_path,
+                                                  &registered_name);
 
   // Only granting read and delete access to reduce contention with
   // webrtcLogging APIs that modify files in that folder.
diff --git a/chrome/browser/media_galleries/fileapi/media_file_system_backend.cc b/chrome/browser/media_galleries/fileapi/media_file_system_backend.cc
index 4677870..2bcd68e 100644
--- a/chrome/browser/media_galleries/fileapi/media_file_system_backend.cc
+++ b/chrome/browser/media_galleries/fileapi/media_file_system_backend.cc
@@ -216,7 +216,7 @@
 
 bool MediaFileSystemBackend::CanHandleType(storage::FileSystemType type) const {
   switch (type) {
-    case storage::kFileSystemTypeNativeMedia:
+    case storage::kFileSystemTypeLocalMedia:
     case storage::kFileSystemTypeDeviceMedia:
       return true;
     default:
@@ -241,7 +241,7 @@
   // We count file system usages here, because we want to count (per session)
   // when the file system is actually used for I/O, rather than merely present.
   switch (type) {
-    case storage::kFileSystemTypeNativeMedia:
+    case storage::kFileSystemTypeLocalMedia:
       return native_media_file_util_.get();
 #if defined(OS_WIN) || defined(OS_MAC) || BUILDFLAG(IS_CHROMEOS_ASH)
     case storage::kFileSystemTypeDeviceMedia:
@@ -265,7 +265,7 @@
   DCHECK(error_code);
   *error_code = base::File::FILE_OK;
   switch (type) {
-    case storage::kFileSystemTypeNativeMedia:
+    case storage::kFileSystemTypeLocalMedia:
     case storage::kFileSystemTypeDeviceMedia:
       if (!media_copy_or_move_file_validator_factory_) {
         *error_code = base::File::FILE_ERROR_SECURITY;
@@ -301,7 +301,7 @@
 
 bool MediaFileSystemBackend::HasInplaceCopyImplementation(
     storage::FileSystemType type) const {
-  DCHECK(type == storage::kFileSystemTypeNativeMedia ||
+  DCHECK(type == storage::kFileSystemTypeLocalMedia ||
          type == storage::kFileSystemTypeDeviceMedia);
   return true;
 }
diff --git a/chrome/browser/media_galleries/fileapi/media_file_validator_browsertest.cc b/chrome/browser/media_galleries/fileapi/media_file_validator_browsertest.cc
index 082f309..e2b9b0e1 100644
--- a/chrome/browser/media_galleries/fileapi/media_file_validator_browsertest.cc
+++ b/chrome/browser/media_galleries/fileapi/media_file_validator_browsertest.cc
@@ -148,7 +148,7 @@
     ASSERT_TRUE(base::CreateDirectory(dest_path));
     dest_fs_ =
         storage::IsolatedContext::GetInstance()->RegisterFileSystemForPath(
-            storage::kFileSystemTypeNativeMedia, std::string(), dest_path,
+            storage::kFileSystemTypeLocalMedia, std::string(), dest_path,
             nullptr);
 
     size_t extension_index = filename.find_last_of(".");
diff --git a/chrome/browser/media_galleries/fileapi/native_media_file_util_unittest.cc b/chrome/browser/media_galleries/fileapi/native_media_file_util_unittest.cc
index ec3b3a91..8d7bc6ec 100644
--- a/chrome/browser/media_galleries/fileapi/native_media_file_util_unittest.cc
+++ b/chrome/browser/media_galleries/fileapi/native_media_file_util_unittest.cc
@@ -142,7 +142,7 @@
         storage::CreateAllowFileAccessOptions());
 
     filesystem_ = isolated_context()->RegisterFileSystemForPath(
-        storage::kFileSystemTypeNativeMedia, std::string(), root_path(),
+        storage::kFileSystemTypeLocalMedia, std::string(), root_path(),
         nullptr);
     filesystem_id_ = filesystem_.id();
   }
@@ -177,7 +177,7 @@
 
   GURL origin() { return GURL("http://example.com"); }
 
-  storage::FileSystemType type() { return storage::kFileSystemTypeNativeMedia; }
+  storage::FileSystemType type() { return storage::kFileSystemTypeLocalMedia; }
 
   storage::FileSystemOperationRunner* operation_runner() {
     return file_system_context_->operation_runner();
diff --git a/chrome/browser/media_galleries/media_file_system_registry.cc b/chrome/browser/media_galleries/media_file_system_registry.cc
index 1080b77..0c8bc43 100644
--- a/chrome/browser/media_galleries/media_file_system_registry.cc
+++ b/chrome/browser/media_galleries/media_file_system_registry.cc
@@ -699,7 +699,7 @@
     CHECK(!path.ReferencesParent());
 
     return ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
-        fs_name, storage::kFileSystemTypeNativeMedia,
+        fs_name, storage::kFileSystemTypeLocalMedia,
         storage::FileSystemMountOption(), path);
   }
 
diff --git a/chrome/browser/payments/android/java/src/org/chromium/chrome/browser/payments/ChromePaymentRequestFactoryTest.java b/chrome/browser/payments/android/java/src/org/chromium/chrome/browser/payments/ChromePaymentRequestFactoryTest.java
index c6e39b76..74844d80 100644
--- a/chrome/browser/payments/android/java/src/org/chromium/chrome/browser/payments/ChromePaymentRequestFactoryTest.java
+++ b/chrome/browser/payments/android/java/src/org/chromium/chrome/browser/payments/ChromePaymentRequestFactoryTest.java
@@ -27,9 +27,8 @@
 import org.chromium.content_public.browser.FeaturePolicyFeature;
 import org.chromium.content_public.browser.RenderFrameHost;
 import org.chromium.content_public.browser.WebContents;
-import org.chromium.services.service_manager.InterfaceProvider;
 
-import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
 
 /** A test for ChromePaymentRequestFactory. */
 @RunWith(BaseRobolectricTestRunner.class)
@@ -82,19 +81,18 @@
 
     @Test
     @Feature({"Payments"})
-    public void testDisabledPolicyCausesNullReturn() {
+    public void testDisabledPolicyCausesBadMessage() {
         setPaymentFeaturePolicy(false);
-        InterfaceProvider provider = Mockito.mock(InterfaceProvider.class);
-        Mockito.doReturn(provider).when(mRenderFrameHost).getRemoteInterfaces();
-        AtomicBoolean isConnectionError = new AtomicBoolean(false);
-        Mockito.doAnswer((args) -> {
-                   isConnectionError.set(true);
+        AtomicInteger isKilledReason = new AtomicInteger(0);
+        Mockito.doAnswer(invocation -> {
+                   isKilledReason.set((int) invocation.getArguments()[0]);
                    return null;
                })
-                .when(provider)
-                .onConnectionError(Mockito.any());
+                .when(mRenderFrameHost)
+                .terminateRendererDueToBadMessage(Mockito.anyInt());
         Assert.assertNull(createFactory(mRenderFrameHost).createImpl());
-        Assert.assertTrue(isConnectionError.get());
+        // 241 == PAYMENTS_WITHOUT_PERMISSION.
+        Assert.assertEquals(isKilledReason.get(), 241);
     }
 
     @Test
diff --git a/chrome/browser/platform_util_unittest.cc b/chrome/browser/platform_util_unittest.cc
index b093629..ecd0bc1 100644
--- a/chrome/browser/platform_util_unittest.cc
+++ b/chrome/browser/platform_util_unittest.cc
@@ -81,7 +81,7 @@
 
     // The test_directory needs to be mounted for it to be accessible.
     content::BrowserContext::GetMountPoints(GetProfile())
-        ->RegisterFileSystem("test", storage::kFileSystemTypeNativeLocal,
+        ->RegisterFileSystem("test", storage::kFileSystemTypeLocal,
                              storage::FileSystemMountOption(), test_directory);
 
     // To test opening a file, we are going to register a mock extension that
diff --git a/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java b/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java
index eba8962b..20ae65d 100644
--- a/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java
+++ b/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java
@@ -515,6 +515,12 @@
     public static final String OFFLINE_INDICATOR_V2_ENABLED = "offline_indicator_v2_enabled";
 
     /**
+     * The wall time of when the offline indicator was shown in milliseconds.
+     */
+    public static final String OFFLINE_INDICATOR_V2_WALL_TIME_SHOWN_MS =
+            "Chrome.OfflineIndicatorV2.WallTimeShownMs";
+
+    /**
      * The measurement interval (in minutes) used to schedule the currently running
      * OfflineMeasureBackgroundTask. This value is zero if the OfflineMeasureBackgroundTask is not
      * currently running.
@@ -873,6 +879,7 @@
                 IMAGE_DESCRIPTIONS_JUST_ONCE_COUNT,
                 IMAGE_DESCRIPTIONS_DONT_ASK_AGAIN,
                 ISOLATED_SPLITS_DEX_COMPILE_VERSION,
+                OFFLINE_INDICATOR_V2_WALL_TIME_SHOWN_MS,
                 OFFLINE_MEASUREMENTS_CURRENT_TASK_MEASUREMENT_INTERVAL_IN_MINUTES,
                 OFFLINE_MEASUREMENTS_LAST_CHECK_MILLIS,
                 OFFLINE_MEASUREMENTS_TIME_BETWEEN_CHECKS_MILLIS_LIST,
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc
index 6788f985..353400f 100644
--- a/chrome/browser/prefs/browser_prefs.cc
+++ b/chrome/browser/prefs/browser_prefs.cc
@@ -1044,7 +1044,7 @@
   chromeos::RegisterSamlProfilePrefs(registry);
   chromeos::ScreenTimeController::RegisterProfilePrefs(registry);
   SecondaryAccountConsentLogger::RegisterPrefs(registry);
-  chromeos::EduCoexistenceConsentInvalidationController::RegisterProfilePrefs(
+  ash::EduCoexistenceConsentInvalidationController::RegisterProfilePrefs(
       registry);
   SigninErrorNotifier::RegisterPrefs(registry);
   chromeos::ServicesCustomizationDocument::RegisterProfilePrefs(registry);
diff --git a/chrome/browser/prefs/pref_service_incognito_allowlist.cc b/chrome/browser/prefs/pref_service_incognito_allowlist.cc
index 449610c2..df35458 100644
--- a/chrome/browser/prefs/pref_service_incognito_allowlist.cc
+++ b/chrome/browser/prefs/pref_service_incognito_allowlist.cc
@@ -41,6 +41,7 @@
     ash::prefs::kAccessibilityScreenMagnifierCenterFocus,
     ash::prefs::kAccessibilityScreenMagnifierEnabled,
     ash::prefs::kAccessibilityScreenMagnifierFocusFollowingEnabled,
+    ash::prefs::kAccessibilityScreenMagnifierMousePanningMode,
     ash::prefs::kAccessibilityScreenMagnifierScale,
     ash::prefs::kAccessibilityVirtualKeyboardEnabled,
     ash::prefs::kAccessibilityMonoAudioEnabled,
diff --git a/chrome/browser/profiles/profile_impl.cc b/chrome/browser/profiles/profile_impl.cc
index 40e89a1..6448724 100644
--- a/chrome/browser/profiles/profile_impl.cc
+++ b/chrome/browser/profiles/profile_impl.cc
@@ -532,14 +532,14 @@
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   if (is_regular_profile) {
-    // |chromeos::InitializeAccountManager| is called during a User's session
+    // |ash::InitializeAccountManager| is called during a User's session
     // initialization but some tests do not properly login to a User Session.
-    // This invocation of |chromeos::InitializeAccountManager| is used only
-    // during tests.
-    // Note: |chromeos::InitializeAccountManager| is idempotent and safe to call
+    // This invocation of |ash::InitializeAccountManager| is used only during
+    // tests.
+    // Note: |ash::InitializeAccountManager| is idempotent and safe to call
     // multiple times.
     // TODO(https://crbug.com/982233): Remove this call.
-    chromeos::InitializeAccountManager(
+    ash::InitializeAccountManager(
         path_, base::DoNothing() /* initialization_callback */);
 
     chromeos::AccountManager* account_manager =
diff --git a/chrome/browser/profiles/profile_manager.cc b/chrome/browser/profiles/profile_manager.cc
index a7930ea..701ecf8a 100644
--- a/chrome/browser/profiles/profile_manager.cc
+++ b/chrome/browser/profiles/profile_manager.cc
@@ -1114,11 +1114,11 @@
       }
       profile->GetPrefs()->SetInteger(arc::prefs::kArcSupervisionTransition,
                                       static_cast<int>(supervisionTransition));
-      chromeos::ChildAccountTypeChangedUserData::GetForProfile(profile)
-          ->SetValue(true);
+      ash::ChildAccountTypeChangedUserData::GetForProfile(profile)->SetValue(
+          true);
     } else {
-      chromeos::ChildAccountTypeChangedUserData::GetForProfile(profile)
-          ->SetValue(false);
+      ash::ChildAccountTypeChangedUserData::GetForProfile(profile)->SetValue(
+          false);
     }
 
     if (user_is_child) {
@@ -1475,8 +1475,7 @@
   AccessibilityLabelsServiceFactory::GetForProfile(profile)->Init();
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
-  chromeos::AccountManagerPolicyControllerFactory::GetForBrowserContext(
-      profile);
+  ash::AccountManagerPolicyControllerFactory::GetForBrowserContext(profile);
 #endif
 
   // Creates the LiteVideo Keyed Service and begins loading the
diff --git a/chrome/browser/renderer_host/pepper/pepper_isolated_file_system_message_filter.cc b/chrome/browser/renderer_host/pepper/pepper_isolated_file_system_message_filter.cc
index 0a04a1e..e90525f 100644
--- a/chrome/browser/renderer_host/pepper/pepper_isolated_file_system_message_filter.cc
+++ b/chrome/browser/renderer_host/pepper/pepper_isolated_file_system_message_filter.cc
@@ -112,9 +112,7 @@
   // First level directory for isolated filesystem to lookup.
   std::string kFirstLevelDirectory("crxfs");
   return storage::IsolatedContext::GetInstance()->RegisterFileSystemForPath(
-      storage::kFileSystemTypeNativeLocal,
-      std::string(),
-      extension->path(),
+      storage::kFileSystemTypeLocal, std::string(), extension->path(),
       &kFirstLevelDirectory);
 #else
   return storage::IsolatedContext::ScopedFSHandle();
diff --git a/chrome/browser/resources/settings/chromeos/BUILD.gn b/chrome/browser/resources/settings/chromeos/BUILD.gn
index 92f7b29b..522c77d0 100644
--- a/chrome/browser/resources/settings/chromeos/BUILD.gn
+++ b/chrome/browser/resources/settings/chromeos/BUILD.gn
@@ -554,8 +554,6 @@
     "chromeos/crostini_page/crostini_port_forwarding.js",
     "chromeos/crostini_page/crostini_port_forwarding_add_port_dialog.html",
     "chromeos/crostini_page/crostini_port_forwarding_add_port_dialog.js",
-    "chromeos/crostini_page/crostini_shared_paths.html",
-    "chromeos/crostini_page/crostini_shared_paths.js",
     "chromeos/crostini_page/crostini_subpage.html",
     "chromeos/crostini_page/crostini_subpage.js",
     "chromeos/date_time_page/date_time_page.html",
@@ -607,6 +605,8 @@
     "chromeos/google_assistant_page/google_assistant_page.js",
     "chromeos/guest_os/guest_os_browser_proxy.html",
     "chromeos/guest_os/guest_os_browser_proxy.js",
+    "chromeos/guest_os/guest_os_shared_paths.html",
+    "chromeos/guest_os/guest_os_shared_paths.js",
     "chromeos/guest_os/guest_os_shared_usb_devices.html",
     "chromeos/guest_os/guest_os_shared_usb_devices.js",
     "chromeos/internet_page/esim_remove_profile_dialog.js",
@@ -769,8 +769,6 @@
     "chromeos/os_apps_page/app_management_page/plugin_vm_page/plugin_vm_browser_proxy.js",
     "chromeos/os_apps_page/app_management_page/plugin_vm_page/plugin_vm_detail_view.html",
     "chromeos/os_apps_page/app_management_page/plugin_vm_page/plugin_vm_detail_view.js",
-    "chromeos/os_apps_page/app_management_page/plugin_vm_page/plugin_vm_shared_paths.html",
-    "chromeos/os_apps_page/app_management_page/plugin_vm_page/plugin_vm_shared_paths.js",
     "chromeos/os_apps_page/app_management_page/pwa_detail_view.html",
     "chromeos/os_apps_page/app_management_page/pwa_detail_view.js",
     "chromeos/os_apps_page/app_management_page/reducers.html",
diff --git a/chrome/browser/resources/settings/chromeos/crostini_page/BUILD.gn b/chrome/browser/resources/settings/chromeos/crostini_page/BUILD.gn
index 52648002..cf0c37a9 100644
--- a/chrome/browser/resources/settings/chromeos/crostini_page/BUILD.gn
+++ b/chrome/browser/resources/settings/chromeos/crostini_page/BUILD.gn
@@ -14,7 +14,6 @@
     ":crostini_page",
     ":crostini_port_forwarding",
     ":crostini_port_forwarding_add_port_dialog",
-    ":crostini_shared_paths",
     ":crostini_subpage",
   ]
 }
@@ -34,9 +33,9 @@
 
 js_library("crostini_browser_proxy") {
   deps = [
+    "//ui/webui/resources/cr_elements/cr_input:cr_input",
     "//ui/webui/resources/js:cr",
     "//ui/webui/resources/js:load_time_data",
-    "//ui/webui/resources/cr_elements/cr_input:cr_input",
   ]
 }
 
@@ -69,15 +68,6 @@
   externs_list = [ "$externs_path/settings_private.js" ]
 }
 
-js_library("crostini_shared_paths") {
-  deps = [
-    "..:metrics_recorder",
-    "..:os_route",
-    "../../prefs:prefs_behavior",
-    "../guest_os:guest_os_browser_proxy",
-  ]
-}
-
 js_library("crostini_port_forwarding") {
   deps = [
     ":crostini_browser_proxy",
@@ -129,8 +119,6 @@
     ":crostini_port_forwarding.m",
     ":crostini_port_forwarding_add_port_dialog.m",
 
-    #    ":crostini_shared_paths.m",
-    #    ":crostini_shared_usb_devices.m",
     #    ":crostini_subpage.m"
   ]
 }
@@ -164,9 +152,9 @@
 js_library("crostini_browser_proxy.m") {
   sources = [ "$root_gen_dir/chrome/browser/resources/settings/chromeos/crostini_page/crostini_browser_proxy.m.js" ]
   deps = [
+    "//ui/webui/resources/cr_elements/cr_input:cr_input.m",
     "//ui/webui/resources/js:cr.m",
     "//ui/webui/resources/js:load_time_data.m",
-    "//ui/webui/resources/cr_elements/cr_input:cr_input.m",
   ]
   extra_deps = [ ":modulize" ]
 }
@@ -277,14 +265,6 @@
   extra_deps = [ ":crostini_port_forwarding_add_port_dialog_module" ]
 }
 
-js_library("crostini_shared_paths.m") {
-  sources = [ "$root_gen_dir/chrome/browser/resources/settings/chromeos/crostini_page/crostini_shared_paths.m.js" ]
-  deps = [
-    # TODO: Fill those in.
-  ]
-  extra_deps = [ ":crostini_shared_paths_module" ]
-}
-
 js_library("crostini_subpage.m") {
   sources = [ "$root_gen_dir/chrome/browser/resources/settings/chromeos/crostini_page/crostini_subpage.m.js" ]
   deps = [
@@ -307,7 +287,6 @@
     ":crostini_page_module",
     ":crostini_port_forwarding_add_port_dialog_module",
     ":crostini_port_forwarding_module",
-    ":crostini_shared_paths_module",
     ":crostini_subpage_module",
     ":modulize",
   ]
@@ -391,12 +370,6 @@
   namespace_rewrites = os_settings_namespace_rewrites
 }
 
-polymer_modulizer("crostini_shared_paths") {
-  js_file = "crostini_shared_paths.js"
-  html_file = "crostini_shared_paths.html"
-  html_type = "dom-module"
-}
-
 polymer_modulizer("crostini_subpage") {
   js_file = "crostini_subpage.js"
   html_file = "crostini_subpage.html"
diff --git a/chrome/browser/resources/settings/chromeos/crostini_page/crostini_browser_proxy.js b/chrome/browser/resources/settings/chromeos/crostini_page/crostini_browser_proxy.js
index a37d1152..2fc9496 100644
--- a/chrome/browser/resources/settings/chromeos/crostini_page/crostini_browser_proxy.js
+++ b/chrome/browser/resources/settings/chromeos/crostini_page/crostini_browser_proxy.js
@@ -23,20 +23,6 @@
 };
 
 /**
- * @typedef {{path: string,
- *            pathDisplayText: string}}
- */
-/* #export */ let CrostiniSharedPath;
-
-/**
- * @typedef {{label: string,
- *            guid: string,
- *            shared: boolean,
- *            shareWillReassign: boolean}}
- */
-/* #export */ let CrostiniSharedUsbDevice;
-
-/**
  * @typedef {{label: string,
  *            port_number: number,
  *            protocol_type: !CrostiniPortProtocol}}
@@ -89,31 +75,6 @@
     requestRemoveCrostini() {}
 
     /**
-     * @param {!Array<string>} paths Paths to sanitze.
-     * @return {!Promise<!Array<string>>} Text to display in UI.
-     */
-    getCrostiniSharedPathsDisplayText(paths) {}
-
-    /**
-     * Called when page is ready.
-     * @return {!Promise<boolean>}
-     */
-    notifyCrostiniSharedUsbDevicesPageReady() {}
-
-    /**
-     * @param {string} guid Unique device identifier.
-     * @param {boolean} shared Whether device is currently shared with Crostini.
-     */
-    setCrostiniUsbDeviceShared(guid, shared) {}
-
-    /**
-     * @param {string} vmName VM to stop sharing path with.
-     * @param {string} path Path to stop sharing.
-     * @return {!Promise<boolean>} Result of unsharing.
-     */
-    removeCrostiniSharedPath(vmName, path) {}
-
-    /**
      * Request chrome send a crostini-installer-status-changed event with the
      * current installer status
      */
@@ -286,26 +247,6 @@
     }
 
     /** @override */
-    getCrostiniSharedPathsDisplayText(paths) {
-      return cr.sendWithPromise('getCrostiniSharedPathsDisplayText', paths);
-    }
-
-    /** @override */
-    notifyCrostiniSharedUsbDevicesPageReady() {
-      return cr.sendWithPromise('notifyCrostiniSharedUsbDevicesPageReady');
-    }
-
-    /** @override */
-    setCrostiniUsbDeviceShared(guid, shared) {
-      return chrome.send('setCrostiniUsbDeviceShared', [guid, shared]);
-    }
-
-    /** @override */
-    removeCrostiniSharedPath(vmName, path) {
-      return cr.sendWithPromise('removeCrostiniSharedPath', vmName, path);
-    }
-
-    /** @override */
     requestCrostiniInstallerStatus() {
       chrome.send('requestCrostiniInstallerStatus');
     }
diff --git a/chrome/browser/resources/settings/chromeos/crostini_page/crostini_page.html b/chrome/browser/resources/settings/chromeos/crostini_page/crostini_page.html
index fc573526..5dc6efd9 100644
--- a/chrome/browser/resources/settings/chromeos/crostini_page/crostini_page.html
+++ b/chrome/browser/resources/settings/chromeos/crostini_page/crostini_page.html
@@ -12,13 +12,13 @@
 <link rel="import" href="../../settings_page/settings_animated_pages.html">
 <link rel="import" href="../../settings_page/settings_subpage.html">
 <link rel="import" href="../../settings_shared_css.html">
+<link rel="import" href="../guest_os/guest_os_shared_paths.html">
 <link rel="import" href="../guest_os/guest_os_shared_usb_devices.html">
 <link rel="import" href="../localized_link/localized_link.html">
 <link rel="import" href="crostini_arc_adb.html">
 <link rel="import" href="crostini_browser_proxy.html">
 <link rel="import" href="crostini_export_import.html">
 <link rel="import" href="crostini_port_forwarding.html">
-<link rel="import" href="crostini_shared_paths.html">
 <link rel="import" href="crostini_subpage.html">
 
 <dom-module id="settings-crostini-page">
@@ -103,9 +103,10 @@
       <template is="dom-if" route-path="/crostini/sharedPaths">
         <settings-subpage
             associated-control="[[$$('#crostini')]]"
-            page-title="$i18n{crostiniSharedPaths}">
-          <settings-crostini-shared-paths prefs="{{prefs}}">
-          </settings-crostini-shared-paths>
+            page-title="$i18n{guestOsSharedPaths}">
+          <settings-guest-os-shared-paths
+              guest-os-type="crostini" prefs="{{prefs}}">
+          </settings-guest-os-shared-paths>
         </settings-subpage>
       </template>
 
diff --git a/chrome/browser/resources/settings/chromeos/crostini_page/crostini_shared_paths.html b/chrome/browser/resources/settings/chromeos/crostini_page/crostini_shared_paths.html
deleted file mode 100644
index 7046927..0000000
--- a/chrome/browser/resources/settings/chromeos/crostini_page/crostini_shared_paths.html
+++ /dev/null
@@ -1,74 +0,0 @@
-<link rel="import" href="chrome://resources/html/polymer.html">
-
-<link rel="import" href="chrome://resources/cr_elements/cr_icon_button/cr_icon_button.html">
-<link rel="import" href="chrome://resources/html/i18n_behavior.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/iron-list/iron-list.html">
-<link rel="import" href="../guest_os/guest_os_browser_proxy.html">
-<link rel="import" href="../../i18n_setup.html">
-<link rel="import" href="../../prefs/prefs_behavior.html">
-<link rel="import" href="../../settings_shared_css.html">
-<link rel="import" href="../metrics_recorder.html">
-
-<dom-module id="settings-crostini-shared-paths">
-  <template>
-    <style include="settings-shared"></style>
-    <div class="settings-box first">
-      <div role="text">
-        $i18n{crostiniSharedPathsInstructionsLocate}
-        $i18n{crostiniSharedPathsInstructionsAdd}
-        <span id="crostiniInstructionsRemove"
-            hidden="[[!sharedPaths_.length]]">
-          $i18n{crostiniSharedPathsInstructionsRemove}
-        </span>
-      </div>
-    </div>
-    <div id="crostiniListEmpty" class="settings-box secondary continuation"
-        hidden="[[sharedPaths_.length]]" >
-       $i18n{crostiniSharedPathsListEmptyMessage}
-    </div>
-    <div id="crostiniList" hidden="[[!sharedPaths_.length]]">
-      <div class="settings-box continuation">
-        <h2 id="crostiniListHeading" class="start">
-          $i18n{crostiniSharedPathsListHeading}
-        </h2>
-      </div>
-      <iron-list class="list-frame vertical-list" role="list"
-          aria-labeledby="crostiniListHeading" items="[[sharedPaths_]]">
-        <template>
-          <div class="list-item" role="listitem">
-            <div class="start" aria-hidden="true"
-                id="[[generatePathDisplayTextId_(index)]]">
-              [[item.pathDisplayText]]
-            </div>
-            <cr-icon-button class="icon-clear" tabindex$="[[tabIndex]]"
-                on-click="onRemoveSharedPathTap_"
-                title="$i18n{crostiniSharedPathsRemoveSharing}"
-                aria-labeledby$="[[generatePathDisplayTextId_(index)]]">
-            </cr-icon-button>
-          </div>
-        </template>
-      </iron-list>
-    </div>
-    <template is="dom-if" if="[[sharedPathWhichFailedRemoval_]]" restamp>
-      <cr-dialog id="removeSharedPathFailedDialog" close-text="$i18n{close}">
-        <div slot="title">
-          $i18n{crostiniSharedPathsRemoveFailureDialogTitle}
-        </div>
-        <div slot="body">
-          $i18n{crostiniSharedPathsRemoveFailureDialogMessage}
-        </div>
-        <div slot="button-container">
-          <cr-button id="cancel" class="cancel-button"
-              on-click="onRemoveFailedDismissTap_">
-            $i18n{ok}
-          </cr-button>
-          <cr-button id="retry" class="action-button"
-              on-click="onRemoveFailedRetryTap_">
-            $i18n{crostiniSharedPathsRemoveFailureTryAgain}
-          </cr-button>
-        </div>
-      </cr-dialog>
-    </template>
-  </template>
-  <script src="crostini_shared_paths.js"></script>
-</dom-module>
diff --git a/chrome/browser/resources/settings/chromeos/crostini_page/crostini_shared_paths.js b/chrome/browser/resources/settings/chromeos/crostini_page/crostini_shared_paths.js
deleted file mode 100644
index 1119b95..0000000
--- a/chrome/browser/resources/settings/chromeos/crostini_page/crostini_shared_paths.js
+++ /dev/null
@@ -1,124 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-/**
- * @fileoverview
- * 'crostini-shared-paths' is the settings shared paths subpage for Crostini.
- */
-
-(function() {
-
-/**
- * The default crostini VM is named 'termina'.
- * https://cs.chromium.org/chromium/src/chrome/browser/chromeos/crostini/crostini_util.h?q=kCrostiniDefaultVmName&dr=CSs
- * @type {string}
- */
-const DEFAULT_CROSTINI_VM = 'termina';
-
-Polymer({
-  is: 'settings-crostini-shared-paths',
-
-  behaviors: [PrefsBehavior],
-
-  properties: {
-    /** Preferences state. */
-    prefs: {
-      type: Object,
-      notify: true,
-    },
-
-    /**
-     * The shared path string suitable for display in the UI.
-     * @private {Array<!{path: string, pathDisplayText: string}>}
-     */
-    sharedPaths_: Array,
-
-    /**
-     * The shared path which failed to be removed in the most recent attempt
-     * to remove a path. Null indicates that removal succeeded.
-     * @private {?string}
-     */
-    sharedPathWhichFailedRemoval_: {
-      type: String,
-      value: null,
-    },
-  },
-
-  observers: [
-    'onGuestOsSharedPathsChanged_(prefs.guest_os.paths_shared_to_vms.value)'
-  ],
-
-  /**
-   * @param {!Object<!Array<string>>} paths
-   * @private
-   */
-  onGuestOsSharedPathsChanged_(paths) {
-    const vmPaths = [];
-    for (const path in paths) {
-      const vms = paths[path];
-      if (vms.includes(DEFAULT_CROSTINI_VM)) {
-        vmPaths.push(path);
-      }
-    }
-    settings.GuestOsBrowserProxyImpl.getInstance()
-        .getGuestOsSharedPathsDisplayText(vmPaths)
-        .then(text => {
-          this.sharedPaths_ = vmPaths.map(
-              (path, i) => ({path: path, pathDisplayText: text[i]}));
-        });
-  },
-
-  /**
-   * @param {string} path
-   * @private
-   */
-  removeSharedPath_(path) {
-    this.sharedPathWhichFailedRemoval_ = null;
-    settings.GuestOsBrowserProxyImpl.getInstance()
-        .removeGuestOsSharedPath(DEFAULT_CROSTINI_VM, path)
-        .then(result => {
-          if (!result) {
-            this.sharedPathWhichFailedRemoval_ = path;
-            // Flush to make sure that the retry dialog is attached.
-            Polymer.dom.flush();
-            this.$$('#removeSharedPathFailedDialog').showModal();
-          }
-        });
-    settings.recordSettingChange();
-  },
-
-  /**
-   * @param {!Event} event
-   * @private
-   */
-  onRemoveSharedPathTap_(event) {
-    this.removeSharedPath_(event.model.item.path);
-  },
-
-  /**
-   * @param {!Event} event
-   * @private
-   */
-  onRemoveFailedRetryTap_(event) {
-    this.removeSharedPath_(assert(this.sharedPathWhichFailedRemoval_));
-  },
-
-  /**
-   * @param {!Event} event
-   * @private
-   */
-  onRemoveFailedDismissTap_(event) {
-    this.sharedPathWhichFailedRemoval_ = null;
-  },
-
-  /**
-   * @param {number} index
-   * @return {string}
-   * @private
-   */
-  generatePathDisplayTextId_(index) {
-    return 'path-display-text-' + index;
-  },
-});
-})();
diff --git a/chrome/browser/resources/settings/chromeos/crostini_page/crostini_subpage.html b/chrome/browser/resources/settings/chromeos/crostini_page/crostini_subpage.html
index 035296f..392e0d5 100644
--- a/chrome/browser/resources/settings/chromeos/crostini_page/crostini_subpage.html
+++ b/chrome/browser/resources/settings/chromeos/crostini_page/crostini_subpage.html
@@ -46,7 +46,7 @@
     <cr-link-row
         class="hr"
         id="crostini-shared-paths"
-        label="$i18n{crostiniSharedPaths}"
+        label="$i18n{guestOsSharedPaths}"
         on-click="onSharedPathsClick_"
         role-description="$i18n{subpageArrowRoleDescription}">
     </cr-link-row>
diff --git a/chrome/browser/resources/settings/chromeos/guest_os/BUILD.gn b/chrome/browser/resources/settings/chromeos/guest_os/BUILD.gn
index d76940d..464f4e1 100644
--- a/chrome/browser/resources/settings/chromeos/guest_os/BUILD.gn
+++ b/chrome/browser/resources/settings/chromeos/guest_os/BUILD.gn
@@ -10,6 +10,7 @@
 js_type_check("closure_compile") {
   deps = [
     ":guest_os_browser_proxy",
+    ":guest_os_shared_paths",
     ":guest_os_shared_usb_devices",
   ]
 }
@@ -18,6 +19,13 @@
   deps = [ "//ui/webui/resources/js:cr" ]
 }
 
+js_library("guest_os_shared_paths") {
+  deps = [
+    ":guest_os_browser_proxy",
+    "..:metrics_recorder",
+  ]
+}
+
 js_library("guest_os_shared_usb_devices") {
   deps = [
     ":guest_os_browser_proxy",
@@ -47,13 +55,28 @@
   extra_deps = [ ":guest_os_shared_usb_devices_module" ]
 }
 
+js_library("guest_os_shared_paths.m") {
+  sources = [ "$root_gen_dir/chrome/browser/resources/settings/chromeos/guest_os/guest_os_shared_paths.m.js" ]
+  deps = [
+    # TODO: Fill those in.
+  ]
+  extra_deps = [ ":guest_os_shared_paths_module" ]
+}
+
 group("polymer3_elements") {
   public_deps = [
+    ":guest_os_shared_paths_module",
     ":guest_os_shared_usb_devices_module",
     ":modulize",
   ]
 }
 
+polymer_modulizer("guest_os_shared_paths") {
+  js_file = "guest_os_shared_paths.js"
+  html_file = "guest_os_shared_paths.html"
+  html_type = "dom-module"
+}
+
 polymer_modulizer("guest_os_shared_usb_devices") {
   js_file = "guest_os_shared_usb_devices.js"
   html_file = "guest_os_shared_usb_devices.html"
diff --git a/chrome/browser/resources/settings/chromeos/guest_os/guest_os_shared_paths.html b/chrome/browser/resources/settings/chromeos/guest_os/guest_os_shared_paths.html
new file mode 100644
index 0000000..6b97f61
--- /dev/null
+++ b/chrome/browser/resources/settings/chromeos/guest_os/guest_os_shared_paths.html
@@ -0,0 +1,70 @@
+<link rel="import" href="chrome://resources/html/polymer.html">
+
+<link rel="import" href="chrome://resources/cr_elements/cr_icon_button/cr_icon_button.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/iron-list/iron-list.html">
+<link rel="import" href="guest_os_browser_proxy.html">
+<link rel="import" href="../../settings_shared_css.html">
+<link rel="import" href="../metrics_recorder.html">
+
+<dom-module id="settings-guest-os-shared-paths">
+  <template>
+    <style include="settings-shared"></style>
+    <div class="settings-box first">
+      <div role="text">
+        [[getDescriptionText_()]]
+        <span id="guestOsInstructionsRemove" hidden="[[!sharedPaths_.length]]">
+          $i18n{guestOsSharedPathsInstructionsRemove}
+        </span>
+      </div>
+    </div>
+    <div id="guestOsListEmpty" class="settings-box secondary continuation"
+        hidden="[[sharedPaths_.length]]" >
+       $i18n{guestOsSharedPathsListEmptyMessage}
+    </div>
+    <div id="guestOsList" hidden="[[!sharedPaths_.length]]">
+      <div class="settings-box continuation">
+        <h2 id="guestOsListHeading" class="start">
+          $i18n{guestOsSharedPathsListHeading}
+        </h2>
+      </div>
+      <iron-list class="list-frame vertical-list" role="list"
+          aria-labeledby="guestOsListHeading" items="[[sharedPaths_]]">
+        <template>
+          <div class="list-item" role="listitem">
+            <div class="start" aria-hidden="true"
+                id="[[generatePathDisplayTextId_(index)]]">
+              [[item.pathDisplayText]]
+            </div>
+            <cr-icon-button class="icon-clear" tabindex$="[[tabIndex]]"
+                on-click="onRemoveSharedPathClick_"
+                title="$i18n{guestOsSharedPathsStopSharing}"
+                aria-labeledby$="[[generatePathDisplayTextId_(index)]]">
+            </cr-icon-button>
+          </div>
+        </template>
+      </iron-list>
+    </div>
+    <template is="dom-if" if="[[sharedPathWhichFailedRemoval_]]" restamp>
+      <cr-dialog id="removeSharedPathFailedDialog" close-text="$i18n{close}"
+          show-on-attach>
+        <div slot="title">
+          $i18n{guestOsSharedPathsRemoveFailureDialogTitle}
+        </div>
+        <div slot="body">
+          [[getRemoveFailureMessage_()]]
+        </div>
+        <div slot="button-container">
+          <cr-button id="cancel" class="cancel-button"
+              on-click="onRemoveFailedDismissClick_">
+            $i18n{ok}
+          </cr-button>
+          <cr-button id="retry" class="action-button"
+              on-click="onRemoveFailedRetryClick_">
+            $i18n{guestOsSharedPathsRemoveFailureTryAgain}
+          </cr-button>
+        </div>
+      </cr-dialog>
+    </template>
+  </template>
+  <script src="guest_os_shared_paths.js"></script>
+</dom-module>
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/plugin_vm_page/plugin_vm_shared_paths.js b/chrome/browser/resources/settings/chromeos/guest_os/guest_os_shared_paths.js
similarity index 62%
rename from chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/plugin_vm_page/plugin_vm_shared_paths.js
rename to chrome/browser/resources/settings/chromeos/guest_os/guest_os_shared_paths.js
index c26f6a05..0ff456a 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/plugin_vm_page/plugin_vm_shared_paths.js
+++ b/chrome/browser/resources/settings/chromeos/guest_os/guest_os_shared_paths.js
@@ -4,22 +4,25 @@
 
 /**
  * @fileoverview
- * 'plugin-vm-shared-paths' is the settings shared paths subpage for Plugin VM.
+ * 'guest-os-shared-paths' is the settings shared paths subpage for guest OSes.
  */
 
 (function() {
 
-/**
- * The Plugin VM is named 'PvmDefault'.
- * https://cs.chromium.org/chromium/src/chrome/browser/chromeos/plugin_vm/plugin_vm_util.h?q=kPluginVmName
- * @type {string}
- */
-const PLUGIN_VM = 'PvmDefault';
+const CROSTINI_TYPE = 'crostini';
+const PLUGIN_VM_TYPE = 'pluginVm';
 
 Polymer({
-  is: 'settings-plugin-vm-shared-paths',
+  is: 'settings-guest-os-shared-paths',
+
+  behaviors: [I18nBehavior],
 
   properties: {
+    /**
+     * The type of Guest OS to share with. Should be 'crostini' or 'pluginVm'.
+     */
+    guestOsType: String,
+
     /** Preferences state. */
     prefs: {
       type: Object,
@@ -56,7 +59,7 @@
     const vmPaths = [];
     for (const path in paths) {
       const vms = paths[path];
-      if (vms.includes(PLUGIN_VM)) {
+      if (vms.includes(this.vmName_())) {
         vmPaths.push(path);
       }
     }
@@ -75,7 +78,7 @@
   removeSharedPath_(path) {
     this.sharedPathWhichFailedRemoval_ = null;
     settings.GuestOsBrowserProxyImpl.getInstance()
-        .removeGuestOsSharedPath(PLUGIN_VM, path)
+        .removeGuestOsSharedPath(this.vmName_(), path)
         .then(success => {
           if (!success) {
             this.sharedPathWhichFailedRemoval_ = path;
@@ -101,5 +104,40 @@
   onRemoveFailedDismissClick_() {
     this.sharedPathWhichFailedRemoval_ = null;
   },
+
+  /**
+   * @return {string} The name of the VM to share devices with.
+   * @private
+   */
+  vmName_() {
+    return {crostini: 'termina', pluginVm: 'PvmDefault'}[this.guestOsType];
+  },
+
+  /**
+   * @return {string} Description for the page.
+   * @private
+   */
+  getDescriptionText_() {
+    return this.i18n(this.guestOsType + 'SharedPathsInstructionsLocate') +
+        '\n' + this.i18n(this.guestOsType + 'SharedPathsInstructionsAdd');
+  },
+
+  /**
+   * @return {string} Message to display when removing a shared path fails.
+   * @private
+   */
+  getRemoveFailureMessage_() {
+    return this.i18n(
+        this.guestOsType + 'SharedPathsRemoveFailureDialogMessage');
+  },
+
+  /**
+   * @param {number} index
+   * @return {string}
+   * @private
+   */
+  generatePathDisplayTextId_(index) {
+    return 'path-display-text-' + index;
+  },
 });
 })();
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/plugin_vm_page/BUILD.gn b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/plugin_vm_page/BUILD.gn
index 33562021..d27c07ed 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/plugin_vm_page/BUILD.gn
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/plugin_vm_page/BUILD.gn
@@ -8,7 +8,6 @@
   deps = [
     ":plugin_vm_browser_proxy",
     ":plugin_vm_detail_view",
-    ":plugin_vm_shared_paths",
   ]
 }
 
@@ -27,22 +26,12 @@
   ]
 }
 
-js_library("plugin_vm_shared_paths") {
-  deps = [
-    "../../..:metrics_recorder",
-    "../../../guest_os:guest_os_browser_proxy",
-    "//ui/webui/resources/js:i18n_behavior",
-    "//ui/webui/resources/js:web_ui_listener_behavior",
-  ]
-}
-
 # TODO: Uncomment as the Polymer3 migration makes progress.
 #js_type_check("closure_compile_module") {
 #  is_polymer3 = true
 #  deps = [
 #    ":plugin_vm_browser_proxy.m",
 #    ":plugin_vm_detail_view.m",
-#    ":plugin_vm_shared_paths.m",
 #  ]
 #}
 
@@ -62,21 +51,12 @@
   extra_deps = [ ":plugin_vm_detail_view_module" ]
 }
 
-js_library("plugin_vm_shared_paths.m") {
-  sources = [ "$root_gen_dir/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/plugin_vm_page/plugin_vm_shared_paths.m.js" ]
-  deps = [
-    # TODO: Fill those in.
-  ]
-  extra_deps = [ ":plugin_vm_shared_paths_module" ]
-}
-
 import("//tools/polymer/polymer.gni")
 
 group("polymer3_elements") {
   public_deps = [
     ":modulize",
     ":plugin_vm_detail_view_module",
-    ":plugin_vm_shared_paths_module",
   ]
 }
 
@@ -86,12 +66,6 @@
   html_type = "dom-module"
 }
 
-polymer_modulizer("plugin_vm_shared_paths") {
-  js_file = "plugin_vm_shared_paths.js"
-  html_file = "plugin_vm_shared_paths.html"
-  html_type = "dom-module"
-}
-
 import("//ui/webui/resources/tools/js_modulizer.gni")
 
 js_modulizer("modulize") {
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/plugin_vm_page/plugin_vm_detail_view.html b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/plugin_vm_page/plugin_vm_detail_view.html
index 48998ea..2c8c098 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/plugin_vm_page/plugin_vm_detail_view.html
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/plugin_vm_page/plugin_vm_detail_view.html
@@ -57,7 +57,7 @@
       <div class="permission-card-row separated-row header-text clickable"
           on-click="onSharedPathsClick_">
         <div id="sharedPathsLabel" class="header-text" aria-hidden="true">
-          $i18n{pluginVmSharedPaths}
+          $i18n{guestOsSharedPaths}
         </div>
         <div class="permission-row-controls">
           <cr-icon-button class="subpage-arrow app-management-item-arrow"
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/plugin_vm_page/plugin_vm_shared_paths.html b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/plugin_vm_page/plugin_vm_shared_paths.html
deleted file mode 100644
index 02f28af88..0000000
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/plugin_vm_page/plugin_vm_shared_paths.html
+++ /dev/null
@@ -1,68 +0,0 @@
-<link rel="import" href="chrome://resources/html/polymer.html">
-
-<link rel="import" href="chrome://resources/cr_elements/cr_icon_button/cr_icon_button.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/iron-list/iron-list.html">
-<link rel="import" href="../../../guest_os/guest_os_browser_proxy.html">
-<link rel="import" href="../../../../settings_shared_css.html">
-<link rel="import" href="../../../metrics_recorder.html">
-
-<dom-module id="settings-plugin-vm-shared-paths">
-  <template>
-    <style include="settings-shared"></style>
-    <div class="settings-box first">
-      <div role="text">
-        $i18n{pluginVmSharedPathsInstructionsLocate}
-        $i18n{pluginVmSharedPathsInstructionsAdd}
-        <span id="pluginVmInstructionsRemove"
-            hidden="[[!sharedPaths_.length]]">
-          $i18n{pluginVmSharedPathsInstructionsRemove}
-        </span>
-      </div>
-    </div>
-    <div id="pluginVmListEmpty" class="settings-box secondary continuation"
-        hidden="[[sharedPaths_.length]]" >
-       $i18n{pluginVmSharedPathsListEmptyMessage}
-    </div>
-    <div id="pluginVmList" hidden="[[!sharedPaths_.length]]">
-      <div class="settings-box continuation">
-        <h2 id="pluginVmListHeading" class="start">
-          $i18n{pluginVmSharedPathsListHeading}
-        </h2>
-      </div>
-      <iron-list class="list-frame vertical-list" role="list"
-          aria-labeledby="pluginVmListHeading" items="[[sharedPaths_]]">
-        <template>
-          <div class="list-item" role="listitem">
-            <div class="start">[[item.pathDisplayText]]</div>
-            <cr-icon-button class="icon-clear" tabindex$="[[tabIndex]]"
-                on-click="onRemoveSharedPathClick_"
-                title="$i18n{pluginVmSharedPathsRemoveSharing}">
-            </cr-icon-button>
-          </div>
-        </template>
-      </iron-list>
-    </div>
-    <template is="dom-if" if="[[sharedPathWhichFailedRemoval_]]" restamp>
-      <cr-dialog id="removeSharedPathFailedDialog" close-text="$i18n{close}"
-          show-on-attach>
-        <div slot="title">
-          $i18n{pluginVmSharedPathsRemoveFailureDialogTitle}
-        </div>
-        <div slot="body">
-          $i18n{pluginVmSharedPathsRemoveFailureDialogMessage}
-        </div>
-        <div slot="button-container">
-          <cr-button id="cancel" class="cancel-button"
-              on-click="onRemoveFailedDismissClick_">
-            $i18n{ok}
-          </cr-button>
-          <cr-button id="retry" class="action-button"
-              on-click="onRemoveFailedRetryClick_">
-            $i18n{pluginVmSharedPathsRemoveFailureTryAgain}
-          </cr-button>
-        </div>
-      </cr-dialog>
-    </template>
-  </template>
-  <script src="plugin_vm_shared_paths.js"></script>
-</dom-module>
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/os_apps_page.html b/chrome/browser/resources/settings/chromeos/os_apps_page/os_apps_page.html
index 346efc0..196400c 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/os_apps_page.html
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/os_apps_page.html
@@ -15,12 +15,12 @@
 <link rel="import" href="../../settings_shared_css.html">
 <link rel="import" href="../../settings_shared_css.html">
 <link rel="import" href="../guest_os/guest_os_shared_usb_devices.html">
+<link rel="import" href="../guest_os/guest_os_shared_paths.html">
 <link rel="import" href="../localized_link/localized_link.html">
 <link rel="import" href="android_apps_browser_proxy.html">
 <link rel="import" href="android_apps_subpage.html">
 <link rel="import" href="app_management_page/app_management_page.html">
 <link rel="import" href="app_management_page/app_detail_view.html">
-<link rel="import" href="app_management_page/plugin_vm_page/plugin_vm_shared_paths.html">
 <link rel="import" href="app_management_page/uninstall_button.html">
 
 <dom-module id="os-settings-apps-page">
@@ -122,9 +122,10 @@
     <!-- Plugin VM -->
     <template is="dom-if" if="[[showPluginVm]]" restamp>
       <template is="dom-if" route-path="/app-management/pluginVm/sharedPaths">
-        <settings-subpage page-title="$i18n{pluginVmSharedPaths}">
-          <settings-plugin-vm-shared-paths prefs="{{prefs}}">
-          </settings-plugin-vm-shared-paths>
+        <settings-subpage page-title="$i18n{guestOsSharedPaths}">
+          <settings-guest-os-shared-paths
+              guest-os-type="pluginVm" prefs="{{prefs}}">
+          </settings-guest-os-shared-paths>
         </settings-subpage>
       </template>
       <template is="dom-if"
diff --git a/chrome/browser/resources/settings/chromeos/os_settings.gni b/chrome/browser/resources/settings/chromeos/os_settings.gni
index 898f5f4..fadcbc0 100644
--- a/chrome/browser/resources/settings/chromeos/os_settings.gni
+++ b/chrome/browser/resources/settings/chromeos/os_settings.gni
@@ -96,7 +96,7 @@
                            cr_elements_chromeos_auto_imports + [
                              "chrome/browser/resources/settings/chromeos/ambient_mode_page/ambient_mode_browser_proxy.html|AmbientModeBrowserProxy,AmbientModeBrowserProxyImpl",
                              "chrome/browser/resources/settings/chromeos/ambient_mode_page/constants.html|AmbientModeTopicSource,AmbientModeTemperatureUnit,AmbientModeAlbum,AmbientModeSettings,TopicSourceItem",
-                             "chrome/browser/resources/settings/chromeos/crostini_page/crostini_browser_proxy.html|DEFAULT_CROSTINI_VM, DEFAULT_CROSTINI_CONTAINER, CrostiniPortProtocol, CrostiniSharedPath, CrostiniSharedUsbDevice, CrostiniPortSetting, CrostiniDiskInfo, CrostiniPortActiveSetting, CrostiniBrowserProxy, CrostiniBrowserProxyImpl,PortState,MIN_VALID_PORT_NUMBER, MAX_VALID_PORT_NUMBER",
+                             "chrome/browser/resources/settings/chromeos/crostini_page/crostini_browser_proxy.html|DEFAULT_CROSTINI_VM, DEFAULT_CROSTINI_CONTAINER, CrostiniPortProtocol, CrostiniPortSetting, CrostiniDiskInfo, CrostiniPortActiveSetting, CrostiniBrowserProxy, CrostiniBrowserProxyImpl,PortState,MIN_VALID_PORT_NUMBER, MAX_VALID_PORT_NUMBER",
                              "chrome/browser/resources/settings/chromeos/internet_page/cellular_setup_settings_delegate.html|CellularSetupSettingsDelegate",
                              "chrome/browser/resources/settings/chromeos/internet_page/internet_page_browser_proxy.html|InternetPageBrowserProxy,InternetPageBrowserProxyImpl",
                              "chrome/browser/resources/settings/chromeos/deep_linking_behavior.html|DeepLinkingBehavior",
diff --git a/chrome/browser/safe_browsing/chrome_password_protection_service.h b/chrome/browser/safe_browsing/chrome_password_protection_service.h
index f92fd1a..4dc2560 100644
--- a/chrome/browser/safe_browsing/chrome_password_protection_service.h
+++ b/chrome/browser/safe_browsing/chrome_password_protection_service.h
@@ -114,7 +114,7 @@
                         const std::string& verdict_token,
                         ReusedPasswordAccountType password_type) override;
 
-  void ShowInterstitial(content::WebContents* web_contens,
+  void ShowInterstitial(content::WebContents* web_contents,
                         ReusedPasswordAccountType password_type) override;
 
   // Called when user interacts with password protection UIs.
diff --git a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/link_to_text/LinkToTextCoordinator.java b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/link_to_text/LinkToTextCoordinator.java
index 1e9c60d..78e841e 100644
--- a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/link_to_text/LinkToTextCoordinator.java
+++ b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/link_to_text/LinkToTextCoordinator.java
@@ -15,7 +15,6 @@
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TabHidingType;
 import org.chromium.components.browser_ui.share.ShareParams;
-import org.chromium.services.service_manager.InterfaceProvider;
 import org.chromium.ui.widget.Toast;
 import org.chromium.url.GURL;
 
@@ -87,8 +86,8 @@
             return;
         }
 
-        InterfaceProvider interfaces = mTab.getWebContents().getMainFrame().getRemoteInterfaces();
-        mProducer = interfaces.getInterface(TextFragmentSelectorProducer.MANAGER);
+        mProducer = mTab.getWebContents().getMainFrame().getInterfaceToRendererFrame(
+                TextFragmentSelectorProducer.MANAGER);
         mProducer.generateSelector(new TextFragmentSelectorProducer.GenerateSelectorResponse() {
             @Override
             public void call(String selector) {
@@ -134,4 +133,4 @@
         mCancelRequest = true;
         mTab.removeObserver(this);
     }
-}
\ No newline at end of file
+}
diff --git a/chrome/browser/signin/account_consistency_mode_manager.cc b/chrome/browser/signin/account_consistency_mode_manager.cc
index 8f43f97..cffefa4b 100644
--- a/chrome/browser/signin/account_consistency_mode_manager.cc
+++ b/chrome/browser/signin/account_consistency_mode_manager.cc
@@ -198,7 +198,7 @@
 #endif
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
-  return chromeos::IsAccountManagerAvailable(profile)
+  return ash::IsAccountManagerAvailable(profile)
              ? AccountConsistencyMethod::kMirror
              : AccountConsistencyMethod::kDisabled;
 #endif
diff --git a/chrome/browser/signin/account_reconcilor_factory.cc b/chrome/browser/signin/account_reconcilor_factory.cc
index aa8a6cf5..36bc499 100644
--- a/chrome/browser/signin/account_reconcilor_factory.cc
+++ b/chrome/browser/signin/account_reconcilor_factory.cc
@@ -120,7 +120,7 @@
  public:
   ChromeOSAccountReconcilorDelegate(
       signin::IdentityManager* identity_manager,
-      chromeos::AccountManagerMigrator* account_migrator)
+      ash::AccountManagerMigrator* account_migrator)
       : signin::MirrorAccountReconcilorDelegate(identity_manager),
         account_migrator_(account_migrator) {}
   ~ChromeOSAccountReconcilorDelegate() override = default;
@@ -132,14 +132,14 @@
       return false;
     }
 
-    const chromeos::AccountMigrationRunner::Status status =
+    const ash::AccountMigrationRunner::Status status =
         account_migrator_->GetStatus();
-    return status != chromeos::AccountMigrationRunner::Status::kNotStarted &&
-           status != chromeos::AccountMigrationRunner::Status::kRunning;
+    return status != ash::AccountMigrationRunner::Status::kNotStarted &&
+           status != ash::AccountMigrationRunner::Status::kRunning;
   }
 
   // A non-owning pointer.
-  const chromeos::AccountManagerMigrator* const account_migrator_;
+  const ash::AccountManagerMigrator* const account_migrator_;
 
   DISALLOW_COPY_AND_ASSIGN(ChromeOSAccountReconcilorDelegate);
 };
@@ -211,7 +211,7 @@
       // TODO(https://crbug.com/993317): Remove the check for
       // |IsAccountManagerAvailable| after fixing https://crbug.com/1008349 and
       // https://crbug.com/993317.
-      if (chromeos::IsAccountManagerAvailable(profile) &&
+      if (ash::IsAccountManagerAvailable(profile) &&
           chromeos::InstallAttributes::Get()->IsActiveDirectoryManaged()) {
         return std::make_unique<
             signin::ActiveDirectoryAccountReconcilorDelegate>();
@@ -229,8 +229,7 @@
       // users have been migrated to Account Manager.
       return std::make_unique<ChromeOSAccountReconcilorDelegate>(
           IdentityManagerFactory::GetForProfile(profile),
-          chromeos::AccountManagerMigratorFactory::GetForBrowserContext(
-              profile));
+          ash::AccountManagerMigratorFactory::GetForBrowserContext(profile));
 #endif
       return std::make_unique<signin::MirrorAccountReconcilorDelegate>(
           IdentityManagerFactory::GetForProfile(profile));
diff --git a/chrome/browser/speech/speech_recognition_client_browser_interface.cc b/chrome/browser/speech/speech_recognition_client_browser_interface.cc
index 47376428..726bb037 100644
--- a/chrome/browser/speech/speech_recognition_client_browser_interface.cc
+++ b/chrome/browser/speech/speech_recognition_client_browser_interface.cc
@@ -11,6 +11,7 @@
 #include "chrome/common/pref_names.h"
 #include "components/prefs/pref_change_registrar.h"
 #include "components/prefs/pref_service.h"
+#include "media/base/media_switches.h"
 
 class PrefChangeRegistrar;
 
@@ -62,7 +63,8 @@
   bool enabled = profile_prefs_->GetBoolean(prefs::kLiveCaptionEnabled);
 
   if (enabled) {
-    if (speech::SodaInstaller::GetInstance()->IsSodaRegistered()) {
+    if (!base::FeatureList::IsEnabled(media::kUseSodaForLiveCaption) ||
+        speech::SodaInstaller::GetInstance()->IsSodaRegistered()) {
       NotifyObservers(enabled);
     } else {
       speech::SodaInstaller::GetInstance()->AddObserver(this);
diff --git a/chrome/browser/ui/ash/holding_space/holding_space_keyed_service_unittest.cc b/chrome/browser/ui/ash/holding_space/holding_space_keyed_service_unittest.cc
index 0c6221a..d45a56d 100644
--- a/chrome/browser/ui/ash/holding_space/holding_space_keyed_service_unittest.cc
+++ b/chrome/browser/ui/ash/holding_space/holding_space_keyed_service_unittest.cc
@@ -1440,13 +1440,13 @@
 // Tests that items from an unmounted volume get removed from the holding space.
 TEST_F(HoldingSpaceKeyedServiceTest, RemoveItemsFromUnmountedVolumes) {
   auto test_mount_1 = std::make_unique<ScopedTestMountPoint>(
-      "test_mount_1", storage::kFileSystemTypeNativeLocal,
+      "test_mount_1", storage::kFileSystemTypeLocal,
       file_manager::VOLUME_TYPE_TESTING);
   test_mount_1->Mount(GetProfile());
   HoldingSpaceModelAttachedWaiter(GetProfile()).Wait();
 
   auto test_mount_2 = std::make_unique<ScopedTestMountPoint>(
-      "test_mount_2", storage::kFileSystemTypeNativeLocal,
+      "test_mount_2", storage::kFileSystemTypeLocal,
       file_manager::VOLUME_TYPE_TESTING);
   test_mount_2->Mount(GetProfile());
   HoldingSpaceModelAttachedWaiter(GetProfile()).Wait();
diff --git a/chrome/browser/ui/ash/holding_space/holding_space_thumbnail_loader_browsertest.cc b/chrome/browser/ui/ash/holding_space/holding_space_thumbnail_loader_browsertest.cc
index d1e7d4c1..9b695cb 100644
--- a/chrome/browser/ui/ash/holding_space/holding_space_thumbnail_loader_browsertest.cc
+++ b/chrome/browser/ui/ash/holding_space/holding_space_thumbnail_loader_browsertest.cc
@@ -57,8 +57,8 @@
       return;
 
     storage::ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
-        name_, storage::kFileSystemTypeNativeLocal,
-        storage::FileSystemMountOption(), temp_dir_.GetPath());
+        name_, storage::kFileSystemTypeLocal, storage::FileSystemMountOption(),
+        temp_dir_.GetPath());
     file_manager::util::GetFileSystemContextForExtensionId(
         profile, file_manager::kImageLoaderExtensionId)
         ->external_backend()
diff --git a/chrome/browser/ui/ash/holding_space/scoped_test_mount_point.cc b/chrome/browser/ui/ash/holding_space/scoped_test_mount_point.cc
index fec4173d..66ea49f 100644
--- a/chrome/browser/ui/ash/holding_space/scoped_test_mount_point.cc
+++ b/chrome/browser/ui/ash/holding_space/scoped_test_mount_point.cc
@@ -20,7 +20,7 @@
 ScopedTestMountPoint::CreateAndMountDownloads(Profile* profile) {
   auto mount_point = std::make_unique<ScopedTestMountPoint>(
       file_manager::util::GetDownloadsMountPointName(profile),
-      storage::kFileSystemTypeNativeLocal,
+      storage::kFileSystemTypeLocal,
       file_manager::VOLUME_TYPE_DOWNLOADS_DIRECTORY);
   mount_point->Mount(profile);
   return mount_point;
diff --git a/chrome/browser/ui/screen_capture_notification_ui_stub.cc b/chrome/browser/ui/screen_capture_notification_ui_stub.cc
index 3a61eb6..4f7a5e2 100644
--- a/chrome/browser/ui/screen_capture_notification_ui_stub.cc
+++ b/chrome/browser/ui/screen_capture_notification_ui_stub.cc
@@ -17,10 +17,6 @@
     NOTIMPLEMENTED();
     return 0;
   }
-
-  void SetStopCallback(base::OnceClosure stop_callback) override {
-    NOTIMPLEMENTED();
-  }
 };
 
 // static
diff --git a/chrome/browser/ui/tab_sharing/tab_sharing_infobar_delegate_unittest.cc b/chrome/browser/ui/tab_sharing/tab_sharing_infobar_delegate_unittest.cc
index 0656057..0596b924 100644
--- a/chrome/browser/ui/tab_sharing/tab_sharing_infobar_delegate_unittest.cc
+++ b/chrome/browser/ui/tab_sharing/tab_sharing_infobar_delegate_unittest.cc
@@ -32,8 +32,6 @@
       content::MediaStreamUI::SourceCallback source_callback) override {
     return 0;
   }
-
-  void SetStopCallback(base::OnceClosure stop_callback) override {}
 };
 
 }  // namespace
diff --git a/chrome/browser/ui/views/screen_capture_notification_ui_views.cc b/chrome/browser/ui/views/screen_capture_notification_ui_views.cc
index 71c465c..1fb4059 100644
--- a/chrome/browser/ui/views/screen_capture_notification_ui_views.cc
+++ b/chrome/browser/ui/views/screen_capture_notification_ui_views.cc
@@ -82,8 +82,6 @@
       base::OnceClosure stop_callback,
       content::MediaStreamUI::SourceCallback source_callback) override;
 
-  void SetStopCallback(base::OnceClosure stop_callback) override;
-
   // views::WidgetDelegateView:
   void DeleteDelegate() override;
   views::ClientView* CreateClientView(views::Widget* widget) override;
@@ -225,11 +223,6 @@
   return 0;
 }
 
-void ScreenCaptureNotificationUIViews::SetStopCallback(
-    base::OnceClosure stop_callback) {
-  stop_callback_ = std::move(stop_callback);
-}
-
 void ScreenCaptureNotificationUIViews::DeleteDelegate() {
   NotifyStopped();
 }
diff --git a/chrome/browser/ui/views/tab_sharing/tab_sharing_ui_views.cc b/chrome/browser/ui/views/tab_sharing/tab_sharing_ui_views.cc
index 13cbfef..5c8c20e 100644
--- a/chrome/browser/ui/views/tab_sharing/tab_sharing_ui_views.cc
+++ b/chrome/browser/ui/views/tab_sharing/tab_sharing_ui_views.cc
@@ -152,10 +152,6 @@
   return 0;
 }
 
-void TabSharingUIViews::SetStopCallback(base::OnceClosure stop_callback) {
-  stop_callback_ = std::move(stop_callback);
-}
-
 void TabSharingUIViews::StartSharing(infobars::InfoBar* infobar) {
   if (source_callback_.is_null())
     return;
diff --git a/chrome/browser/ui/views/tab_sharing/tab_sharing_ui_views.h b/chrome/browser/ui/views/tab_sharing/tab_sharing_ui_views.h
index de19b84..62e5a4e 100644
--- a/chrome/browser/ui/views/tab_sharing/tab_sharing_ui_views.h
+++ b/chrome/browser/ui/views/tab_sharing/tab_sharing_ui_views.h
@@ -45,8 +45,6 @@
       base::OnceClosure stop_callback,
       content::MediaStreamUI::SourceCallback source_callback) override;
 
-  void SetStopCallback(base::OnceClosure stop_callback) override;
-
   // TabSharingUI:
   // Runs |source_callback_| to start sharing the tab containing |infobar|.
   // Removes infobars on all tabs; OnStarted() will recreate the infobars with
diff --git a/chrome/browser/ui/views/web_apps/web_app_integration_browsertest.cc b/chrome/browser/ui/views/web_apps/web_app_integration_browsertest.cc
index ace13f8..5c4b325 100644
--- a/chrome/browser/ui/views/web_apps/web_app_integration_browsertest.cc
+++ b/chrome/browser/ui/views/web_apps/web_app_integration_browsertest.cc
@@ -2,850 +2,63 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "base/base_paths.h"
-#include "base/containers/flat_map.h"
-#include "base/files/file_util.h"
 #include "base/path_service.h"
-#include "base/strings/string_split.h"
-#include "base/strings/string_util.h"
-#include "base/test/bind.h"
-#include "build/build_config.h"
-#include "build/chromeos_buildflags.h"
-#include "chrome/app/chrome_command_ids.h"
-#include "chrome/browser/banners/test_app_banner_manager_desktop.h"
-#include "chrome/browser/profiles/profile_manager.h"
-#include "chrome/browser/ui/browser.h"
-#include "chrome/browser/ui/browser_commands.h"
-#include "chrome/browser/ui/browser_dialogs.h"
-#include "chrome/browser/ui/browser_list.h"
-#include "chrome/browser/ui/views/frame/browser_view.h"
-#include "chrome/browser/ui/views/frame/toolbar_button_provider.h"
-#include "chrome/browser/ui/views/page_action/page_action_icon_view.h"
-#include "chrome/browser/ui/web_applications/test/web_app_browsertest_util.h"
-#include "chrome/browser/ui/web_applications/web_app_dialog_utils.h"
-#include "chrome/browser/ui/web_applications/web_app_menu_model.h"
-#include "chrome/browser/web_applications/components/app_registry_controller.h"
-#include "chrome/browser/web_applications/components/install_finalizer.h"
+#include "chrome/browser/ui/views/web_apps/web_app_integration_browsertest_base.h"
 #include "chrome/browser/web_applications/components/os_integration_manager.h"
-#include "chrome/browser/web_applications/components/policy/web_app_policy_constants.h"
-#include "chrome/browser/web_applications/components/policy/web_app_policy_manager.h"
-#include "chrome/browser/web_applications/components/web_app_constants.h"
-#include "chrome/browser/web_applications/components/web_app_id.h"
-#include "chrome/browser/web_applications/components/web_app_provider_base.h"
-#include "chrome/browser/web_applications/test/web_app_install_observer.h"
-#include "chrome/browser/web_applications/web_app_provider.h"
-#include "chrome/browser/web_applications/web_app_registrar.h"
-#include "chrome/common/pref_names.h"
 #include "chrome/test/base/in_process_browser_test.h"
-#include "chrome/test/base/ui_test_utils.h"
-#include "components/prefs/scoped_user_pref_update.h"
 #include "content/public/test/browser_test.h"
-#include "content/public/test/test_navigation_observer.h"
-#include "extensions/browser/extension_dialog_auto_confirm.h"
-#include "net/dns/mock_host_resolver.h"
-#include "net/test/embedded_test_server/embedded_test_server.h"
 #include "services/network/public/cpp/network_switches.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "third_party/blink/public/mojom/manifest/display_mode.mojom-shared.h"
-#include "third_party/re2/src/re2/re2.h"
+
+namespace web_app {
 
 namespace {
 
-const std::string kTestCaseFilename =
-    "web_app_integration_browsertest_cases.csv";
-const std::string kExpectationsFilename = "TestExpectations";
-const std::string kPlatformName =
-#if BUILDFLAG(IS_CHROMEOS_ASH)
-    "ChromeOS";
-#elif defined(OS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)
-    "Linux";
-#elif defined(OS_MAC)
-    "Mac";
-#elif defined(OS_WIN)
-    "Win";
-#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
-
-// Command-line switch that overrides test case input. Takes a comma
-// separated list of testing actions. This aids in development of tests
-// by allowing one to run a single test at a time, and avoid running every
-// test case in the suite.
-const char kWebAppIntegrationTestCase[] = "web-app-integration-test-case";
-
-struct TabState {
-  TabState(GURL tab_url, bool is_tab_installable)
-      : url(tab_url), is_installable(is_tab_installable) {}
-  TabState& operator=(const TabState&) = default;
-  bool operator==(const TabState& other) const {
-    return url == other.url && is_installable == other.is_installable;
-  }
-
-  GURL url;
-  bool is_installable;
-};
-
-struct BrowserState {
-  BrowserState(Browser* browser_ptr,
-               base::flat_map<content::WebContents*, TabState> tab_state,
-               content::WebContents* active_web_contents,
-               bool is_an_app_browser,
-               bool install_icon_visible,
-               bool launch_icon_visible)
-      : browser(browser_ptr),
-        tabs(std::move(tab_state)),
-        active_tab(active_web_contents),
-        is_app_browser(is_an_app_browser),
-        install_icon_shown(install_icon_visible),
-        launch_icon_shown(launch_icon_visible) {}
-  BrowserState& operator=(const BrowserState&) = default;
-  bool operator==(const BrowserState& other) const {
-    return browser == other.browser && tabs == other.tabs &&
-           active_tab == other.active_tab &&
-           is_app_browser == other.is_app_browser &&
-           install_icon_shown == other.install_icon_shown &&
-           launch_icon_shown == other.launch_icon_shown;
-  }
-
-  Browser* browser;
-  base::flat_map<content::WebContents*, TabState> tabs;
-  content::WebContents* active_tab;
-  bool is_app_browser;
-  bool install_icon_shown;
-  bool launch_icon_shown;
-};
-
-struct AppState {
-  AppState(web_app::AppId app_id,
-           const std::string app_name,
-           const GURL app_scope,
-           const blink::mojom::DisplayMode& app_display_mode)
-      : id(app_id),
-        name(app_name),
-        scope(app_scope),
-        display_mode(app_display_mode) {}
-  AppState& operator=(const AppState&) = default;
-  bool operator==(const AppState& other) const {
-    return id == other.id && name == other.name && scope == other.scope &&
-           display_mode == other.display_mode;
-  }
-
-  web_app::AppId id;
-  std::string name;
-  GURL scope;
-  blink::mojom::DisplayMode display_mode;
-};
-
-struct StateSnapshot {
-  StateSnapshot(base::flat_map<Browser*, BrowserState> browser_state,
-                base::flat_map<web_app::AppId, AppState> app_state)
-      : browsers(std::move(browser_state)), apps(std::move(app_state)) {}
-  StateSnapshot& operator=(const StateSnapshot&) = default;
-  bool operator==(const StateSnapshot& other) const {
-    return browsers == other.browsers && apps == other.apps;
-  }
-
-  base::flat_map<Browser*, BrowserState> browsers;
-  base::flat_map<web_app::AppId, AppState> apps;
-};
-
-base::Optional<BrowserState> GetStateForBrowser(
-    base::flat_map<Browser*, BrowserState> browser_state_map,
-    Browser* browser) {
-  auto it = browser_state_map.find(browser);
-  return it == browser_state_map.end()
-             ? base::nullopt
-             : base::make_optional<BrowserState>(it->second);
-}
-
-base::Optional<TabState> GetStateForActiveTab(BrowserState browser_state) {
-  if (!browser_state.active_tab) {
-    return base::nullopt;
-  }
-
-  auto it = browser_state.tabs.find(browser_state.active_tab);
-  DCHECK(it != browser_state.tabs.end());
-  return base::make_optional<TabState>(it->second);
-}
-
-base::Optional<AppState> GetStateForAppId(
-    base::flat_map<web_app::AppId, AppState> apps,
-    web_app::AppId id) {
-  auto it = apps.find(id);
-  return it == apps.end() ? base::nullopt
-                          : base::make_optional<AppState>(it->second);
-}
-
-std::string StripAllWhitespace(std::string line) {
-  std::string output;
-  output.reserve(line.size());
-  for (const char& c : line) {
-    if (!isspace(c)) {
-      output += c;
-    }
-  }
-  return output;
-}
-
-bool IsInspectionAction(const std::string& action) {
-  return base::StartsWith(action, "assert_");
-}
-
 // Returns the path of the requested file in the test data directory.
-base::FilePath GetTestFilePath(const std::string& file_name) {
+base::FilePath GetTestFileDir() {
   base::FilePath file_path;
   base::PathService::Get(base::DIR_SOURCE_ROOT, &file_path);
   file_path = file_path.Append(FILE_PATH_LITERAL("chrome"));
   file_path = file_path.Append(FILE_PATH_LITERAL("test"));
   file_path = file_path.Append(FILE_PATH_LITERAL("data"));
-  file_path = file_path.Append(FILE_PATH_LITERAL("web_apps"));
-  return file_path.AppendASCII(file_name);
-}
-
-std::string GetCommandLineTestOverride() {
-  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
-  if (command_line->HasSwitch(kWebAppIntegrationTestCase)) {
-    return command_line->GetSwitchValueASCII(kWebAppIntegrationTestCase);
-  }
-  return "";
-}
-
-std::vector<std::string> ReadTestInputFile(const std::string& file_name) {
-  std::vector<std::string> test_cases;
-  std::string command_line_test_case = GetCommandLineTestOverride();
-  if (!command_line_test_case.empty()) {
-    test_cases.push_back(StripAllWhitespace(command_line_test_case));
-    return test_cases;
-  }
-
-  base::FilePath file = GetTestFilePath(file_name);
-  std::string contents;
-  if (!base::ReadFileToString(file, &contents)) {
-    return test_cases;
-  }
-
-  std::vector<std::string> file_lines = base::SplitString(
-      contents, "\n", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
-  for (const auto& line : file_lines) {
-    if (line[0] == '#') {
-      continue;
-    }
-
-    if (line.find('|') == std::string::npos) {
-      test_cases.push_back(StripAllWhitespace(line));
-      continue;
-    }
-
-    std::vector<std::string> platforms_and_test = base::SplitString(
-        line, "|", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
-    if (platforms_and_test[0].find(kPlatformName) != std::string::npos) {
-      test_cases.push_back(StripAllWhitespace(platforms_and_test[1]));
-    }
-  }
-
-  return test_cases;
-}
-
-std::vector<std::string> GetPlatformIgnoredTests(const std::string& file_name) {
-  base::FilePath file = GetTestFilePath(file_name);
-  std::string contents;
-  std::vector<std::string> platform_expectations;
-  if (!base::ReadFileToString(file, &contents)) {
-    return platform_expectations;
-  }
-
-  std::vector<std::string> file_lines = base::SplitString(
-      contents, "\n", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
-  for (const auto& line : file_lines) {
-    if (line[0] == '#') {
-      continue;
-    }
-
-    std::string platform;
-    std::string expectation;
-    std::string test_case;
-    RE2::FullMatch(
-        line, "crbug.com/\\d* \\[ (\\w*) \\] \\[ (\\w*) \\] ([\\w*,\\s*]*)",
-        &platform, &expectation, &test_case);
-    if (platform == kPlatformName) {
-      if (expectation == "Skip") {
-        platform_expectations.push_back(StripAllWhitespace(test_case));
-      } else {
-        NOTREACHED() << "Unsupported expectation " << expectation;
-      }
-    }
-  }
-  return platform_expectations;
+  return file_path.Append(FILE_PATH_LITERAL("web_apps"));
 }
 
 std::vector<std::string> BuildAllPlatformTestCaseSet() {
-  std::vector<std::string> test_cases_all =
-      ReadTestInputFile(kTestCaseFilename);
-  std::sort(test_cases_all.begin(), test_cases_all.end());
-
-  std::vector<std::string> ignored_cases =
-      GetPlatformIgnoredTests(kExpectationsFilename);
-  std::sort(ignored_cases.begin(), ignored_cases.end());
-
-  std::vector<std::string> final_tests(test_cases_all.size());
-  auto iter = std::set_difference(test_cases_all.begin(), test_cases_all.end(),
-                                  ignored_cases.begin(), ignored_cases.end(),
-                                  final_tests.begin());
-  final_tests.resize(iter - final_tests.begin());
-  return final_tests;
+  return WebAppIntegrationBrowserTestBase::BuildAllPlatformTestCaseSet(
+      GetTestFileDir());
 }
 
 }  // anonymous namespace
 
-namespace web_app {
-
-struct NavigateToSiteResult {
-  content::WebContents* web_contents;
-  webapps::TestAppBannerManagerDesktop* app_banner_manager;
-  bool installable;
-};
-
 class WebAppIntegrationBrowserTest
     : public InProcessBrowserTest,
       public testing::WithParamInterface<std::string> {
  public:
-  WebAppIntegrationBrowserTest()
-      : https_server_(net::EmbeddedTestServer::TYPE_HTTPS) {}
-  ~WebAppIntegrationBrowserTest() override = default;
-
-  WebAppIntegrationBrowserTest(const WebAppIntegrationBrowserTest&) = delete;
-  WebAppIntegrationBrowserTest& operator=(const WebAppIntegrationBrowserTest&) =
-      delete;
+  WebAppIntegrationBrowserTest() : helper_(this) {}
 
   // InProcessBrowserTest
   void SetUp() override {
-    https_server_.AddDefaultHandlers(GetChromeTestDataDir());
-    ASSERT_TRUE(https_server_.Start());
-
-    webapps::TestAppBannerManagerDesktop::SetUp();
-
+    helper_.SetUp(GetChromeTestDataDir());
     InProcessBrowserTest::SetUp();
   }
 
   // BrowserTestBase
-  void SetUpOnMainThread() override {
-    os_hooks_suppress_ =
-        OsIntegrationManager::ScopedSuppressOsHooksForTesting();
-    pwa_install_view_ =
-        BrowserView::GetBrowserViewForBrowser(browser())
-            ->toolbar_button_provider()
-            ->GetPageActionIconView(PageActionIconType::kPwaInstall);
-    ASSERT_TRUE(pwa_install_view_);
-    EXPECT_FALSE(pwa_install_view_->GetVisible());
-  }
+  void SetUpOnMainThread() override { helper_.SetUpOnMainThread(); }
 
   void SetUpCommandLine(base::CommandLine* command_line) override {
     command_line->AppendSwitchASCII(
         network::switches::kUnsafelyTreatInsecureOriginAsSecure,
-        GetInstallableAppURL().GetOrigin().spec());
+        helper_.GetInstallableAppURL().GetOrigin().spec());
   }
 
-  // Test Framework
-  void ParseParams() {
-    std::string action_strings = GetParam();
-    LOG(ERROR) << "Test case: " << action_strings;
-    testing_actions_ = base::SplitString(
-        action_strings, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
-  }
-
-  // Non-assert actions implemented before assert actions. Implemented in
-  // alphabetical order.
-  void ExecuteAction(const std::string& action_string) {
-    if (base::EndsWith(action_string, "site_b")) {
-      FAIL() << "site_b actions not yet supported: " << action_string;
-    }
-
-    if (!IsInspectionAction(action_string)) {
-      before_action_state_ = std::move(after_action_state_);
-    }
-
-    if (base::StartsWith(action_string, "add_policy_app_internal_tabbed")) {
-      AddPolicyAppInternal(base::Value(kDefaultLaunchContainerTabValue));
-    } else if (base::StartsWith(action_string,
-                                "add_policy_app_internal_windowed")) {
-      AddPolicyAppInternal(base::Value(kDefaultLaunchContainerWindowValue));
-    } else if (action_string == "close_pwa") {
-      ClosePWA();
-    } else if (action_string == "install_create_shortcut_tabbed") {
-      InstallCreateShortcutTabbed();
-    } else if (action_string == "install_omnibox_or_menu") {
-      InstallOmniboxOrMenu();
-    } else if (base::StartsWith(action_string, "install_internal_windowed")) {
-      InstallOmniboxOrMenu();
-    } else if (base::StartsWith(action_string, "launch_internal")) {
-      LaunchInternal();
-    } else if (action_string == "list_apps_internal") {
-      ListAppsInternal();
-    } else if (base::StartsWith(action_string, "navigate_browser_in_scope")) {
-      NavigateToSite(browser(), GetInScopeURL());
-    } else if (base::StartsWith(action_string, "navigate_installable")) {
-      NavigateToSite(browser(), GetInstallableAppURL());
-    } else if (action_string == "navigate_not_installable") {
-      NavigateToSite(browser(), GetNonInstallableAppURL());
-    } else if (action_string == "remove_policy_app") {
-      RemovePolicyApp();
-    } else if (base::StartsWith(action_string, "set_open_in_tab_internal")) {
-      SetOpenInTabInternal();
-    } else if (base::StartsWith(action_string, "set_open_in_window_internal")) {
-      SetOpenInWindowInternal();
-    } else if (action_string == "uninstall_from_menu") {
-      UninstallFromMenu();
-    } else if (base::StartsWith(action_string, "uninstall_internal")) {
-      UninstallInternal();
-    } else if (action_string == "assert_app_in_list_not_windowed") {
-      AssertAppInListNotWindowed();
-    } else if (base::StartsWith(action_string, "assert_app_not_in_list")) {
-      AssertAppNotInList();
-    } else if (action_string == "assert_display_mode_standalone_internal") {
-      AssertDisplayModeInternal(DisplayMode::kStandalone);
-    } else if (action_string == "assert_display_mode_browser_internal") {
-      AssertDisplayModeInternal(DisplayMode::kBrowser);
-    } else if (action_string == "assert_installable") {
-      AssertInstallable();
-    } else if (action_string == "assert_install_icon_shown") {
-      AssertInstallIconShown();
-    } else if (action_string == "assert_install_icon_not_shown") {
-      AssertInstallIconNotShown();
-    } else if (action_string == "assert_launch_icon_shown") {
-      AssertLaunchIconShown();
-    } else if (action_string == "assert_launch_icon_not_shown") {
-      AssertLaunchIconNotShown();
-    } else if (action_string == "assert_no_crash") {
-    } else if (action_string == "assert_tab_created") {
-      AssertTabCreated();
-    } else if (action_string == "assert_window_created") {
-      AssertWindowCreated();
-    } else {
-      FAIL() << "Unimplemented action: " << action_string;
-    }
-
-    if (IsInspectionAction(action_string)) {
-      DCHECK(!after_action_state_ ||
-             *after_action_state_ == ConstructStateSnapshot());
-    } else {
-      after_action_state_ =
-          std::make_unique<StateSnapshot>(ConstructStateSnapshot());
-    }
-  }
-
-  // Automated Testing Actions
-  void AddPolicyAppInternal(base::Value default_launch_container) {
-    GURL url = GetInstallableAppURL();
-    auto* web_app_registrar =
-        WebAppProvider::Get(profile())->registrar().AsWebAppRegistrar();
-    base::RunLoop run_loop;
-    WebAppInstallObserver observer(profile());
-    observer.SetWebAppInstalledDelegate(
-        base::BindLambdaForTesting([&](const AppId& app_id) {
-          bool is_installed = web_app_registrar->IsInstalled(app_id);
-          GURL installed_url = web_app_registrar->GetAppStartUrl(app_id);
-          if (is_installed && installed_url.is_valid() &&
-              installed_url.spec() == url.spec()) {
-            active_app_id_ = app_id;
-            run_loop.Quit();
-          }
-        }));
-    {
-      base::Value item(base::Value::Type::DICTIONARY);
-      item.SetKey(kUrlKey, base::Value(url.spec()));
-      item.SetKey(kDefaultLaunchContainerKey,
-                  std::move(default_launch_container));
-      ListPrefUpdate update(profile()->GetPrefs(),
-                            prefs::kWebAppInstallForceList);
-      update->Append(item.Clone());
-    }
-    run_loop.Run();
-  }
-
-  void ClosePWA() {
-    DCHECK(app_browser_);
-    app_browser_->window()->Close();
-    ui_test_utils::WaitForBrowserToClose(app_browser_);
-  }
-
-  void InstallCreateShortcutTabbed() {
-    chrome::SetAutoAcceptWebAppDialogForTesting(/*auto_accept=*/true,
-                                                /*auto_open_in_window=*/false);
-    WebAppInstallObserver observer(profile());
-    CHECK(chrome::ExecuteCommand(browser(), IDC_CREATE_SHORTCUT));
-    active_app_id_ = observer.AwaitNextInstall();
-    chrome::SetAutoAcceptWebAppDialogForTesting(false, false);
-  }
-
-  web_app::AppId InstallOmniboxOrMenu() {
-    chrome::SetAutoAcceptPWAInstallConfirmationForTesting(true);
-
-    web_app::AppId app_id;
-    base::RunLoop run_loop;
-    web_app::SetInstalledCallbackForTesting(base::BindLambdaForTesting(
-        [&app_id, &run_loop](const web_app::AppId& installed_app_id,
-                             web_app::InstallResultCode code) {
-          app_id = installed_app_id;
-          run_loop.Quit();
-        }));
-
-    pwa_install_view()->ExecuteForTesting();
-
-    run_loop.Run();
-
-    chrome::SetAutoAcceptPWAInstallConfirmationForTesting(false);
-    active_app_id_ = app_id;
-    auto* browser_list = BrowserList::GetInstance();
-    app_browser_ = browser_list->GetLastActive();
-    DCHECK(AppBrowserController::IsWebApp(app_browser_));
-
-    return app_id;
-  }
-
-  void LaunchInternal() {
-    auto* web_app_provider = GetProvider();
-    AppRegistrar& app_registrar = web_app_provider->registrar();
-    DisplayMode display_mode =
-        app_registrar.GetAppEffectiveDisplayMode(active_app_id_);
-    if (display_mode == blink::mojom::DisplayMode::kStandalone) {
-      app_browser_ = LaunchWebAppBrowserAndWait(
-          ProfileManager::GetActiveUserProfile(), active_app_id_);
-    } else {
-      ui_test_utils::UrlLoadObserver url_observer(
-          WebAppProviderBase::GetProviderBase(profile())
-              ->registrar()
-              .GetAppLaunchUrl(active_app_id_),
-          content::NotificationService::AllSources());
-      LaunchBrowserForWebAppInTab(profile(), active_app_id_);
-      url_observer.Wait();
-    }
-  }
-
-  void ListAppsInternal() {
-    auto* web_app_registrar =
-        WebAppProvider::Get(profile())->registrar().AsWebAppRegistrar();
-    app_ids_ = web_app_registrar->GetAppIds();
-  }
-
-  NavigateToSiteResult NavigateToSite(Browser* browser, const GURL& url) {
-    content::WebContents* web_contents = GetCurrentTab(browser);
-    auto* app_banner_manager =
-        webapps::TestAppBannerManagerDesktop::FromWebContents(web_contents);
-
-    ui_test_utils::NavigateToURL(browser, url);
-    bool installable = app_banner_manager->WaitForInstallableCheck();
-
-    last_navigation_result_ =
-        NavigateToSiteResult{web_contents, app_banner_manager, installable};
-    site_installability_map_[url.spec()] = installable;
-    return last_navigation_result_;
-  }
-
-  void RemovePolicyApp() {
-    GURL url = GetInstallableAppURL();
-    base::RunLoop run_loop;
-    WebAppInstallObserver observer(profile());
-    observer.SetWebAppUninstalledDelegate(
-        base::BindLambdaForTesting([&](const AppId& app_id) {
-          if (active_app_id_ == app_id) {
-            run_loop.Quit();
-          }
-        }));
-    {
-      ListPrefUpdate update(profile()->GetPrefs(),
-                            prefs::kWebAppInstallForceList);
-      update->EraseListValueIf([&](const base::Value& item) {
-        const base::Value* url_value = item.FindKey(kUrlKey);
-        return url_value && url_value->GetString() == url.spec();
-      });
-    }
-    run_loop.Run();
-  }
-
-  void SetOpenInTabInternal() {
-    auto& app_registry_controller =
-        WebAppProvider::Get(profile())->registry_controller();
-    app_registry_controller.SetAppUserDisplayMode(
-        active_app_id_, blink::mojom::DisplayMode::kBrowser, true);
-  }
-
-  void SetOpenInWindowInternal() {
-    auto& app_registry_controller =
-        WebAppProvider::Get(profile())->registry_controller();
-    app_registry_controller.SetAppUserDisplayMode(
-        active_app_id_, blink::mojom::DisplayMode::kStandalone, true);
-  }
-
-  // TODO(https://crbug.com/1159651): Support this action on CrOS.
-  void UninstallFromMenu() {
-    DCHECK(app_browser_);
-    base::RunLoop run_loop;
-    WebAppInstallObserver observer(profile());
-    observer.SetWebAppUninstalledDelegate(
-        base::BindLambdaForTesting([&](const AppId& app_id) {
-          if (app_id == active_app_id_) {
-            run_loop.Quit();
-          }
-        }));
-
-    extensions::ScopedTestDialogAutoConfirm auto_confirm(
-        extensions::ScopedTestDialogAutoConfirm::ACCEPT);
-    auto app_menu_model =
-        std::make_unique<WebAppMenuModel>(/*provider=*/nullptr, app_browser_);
-    app_menu_model->Init();
-    ui::MenuModel* model = app_menu_model.get();
-    int index = -1;
-    const bool found = app_menu_model->GetModelAndIndexForCommandId(
-        WebAppMenuModel::kUninstallAppCommandId, &model, &index);
-    EXPECT_TRUE(found);
-    EXPECT_TRUE(model->IsEnabledAt(index));
-
-    app_menu_model->ExecuteCommand(WebAppMenuModel::kUninstallAppCommandId,
-                                   /*event_flags=*/0);
-    // The |app_menu_model| must be destroyed here, as the |run_loop| waits
-    // until the app is fully uninstalled, which includes closing and deleting
-    // the app_browser_.
-    app_menu_model.reset();
-    app_browser_ = nullptr;
-    run_loop.Run();
-  }
-
-  void UninstallInternal() {
-    WebAppProviderBase* const provider =
-        WebAppProviderBase::GetProviderBase(profile());
-    base::RunLoop run_loop;
-
-    DCHECK(provider->install_finalizer().CanUserUninstallExternalApp(
-        active_app_id_));
-    provider->install_finalizer().UninstallExternalAppByUser(
-        active_app_id_, base::BindLambdaForTesting([&](bool uninstalled) {
-          EXPECT_TRUE(uninstalled);
-          run_loop.Quit();
-        }));
-
-    run_loop.Run();
-  }
-
-  // Assert Actions
-  void AssertAppInListNotWindowed() {
-    EXPECT_TRUE(base::Contains(app_ids_, active_app_id_));
-    DCHECK(after_action_state_);
-    base::Optional<AppState> app_state =
-        GetStateForAppId(after_action_state_->apps, active_app_id_);
-    ASSERT_TRUE(app_state.has_value());
-    EXPECT_EQ(DisplayMode::kBrowser, app_state->display_mode);
-  }
-
-  void AssertAppNotInList() {
-    EXPECT_FALSE(base::Contains(app_ids_, active_app_id_));
-  }
-
-  void AssertDisplayModeInternal(DisplayMode display_mode) {
-    DCHECK(after_action_state_);
-    base::Optional<AppState> app_state =
-        GetStateForAppId(after_action_state_->apps, active_app_id_);
-    ASSERT_TRUE(app_state.has_value());
-    EXPECT_EQ(display_mode, app_state->display_mode);
-  }
-
-  void AssertInstallable() {
-    DCHECK(after_action_state_);
-    base::Optional<BrowserState> browser_state =
-        GetStateForBrowser(after_action_state_->browsers, browser());
-    ASSERT_TRUE(browser_state.has_value());
-    base::Optional<TabState> active_tab =
-        GetStateForActiveTab(browser_state.value());
-    ASSERT_TRUE(active_tab.has_value());
-    EXPECT_TRUE(active_tab->is_installable);
-  }
-
-  void AssertInstallIconShown() {
-    DCHECK(after_action_state_);
-    base::Optional<BrowserState> browser_state =
-        GetStateForBrowser(after_action_state_->browsers, browser());
-    ASSERT_TRUE(browser_state.has_value());
-    EXPECT_TRUE(browser_state->install_icon_shown);
-    EXPECT_TRUE(pwa_install_view()->GetVisible());
-  }
-
-  void AssertInstallIconNotShown() {
-    base::Optional<BrowserState> browser_state =
-        GetStateForBrowser(after_action_state_->browsers, browser());
-    ASSERT_TRUE(browser_state.has_value());
-    EXPECT_FALSE(browser_state->install_icon_shown);
-    EXPECT_FALSE(pwa_install_view()->GetVisible());
-  }
-
-  void AssertLaunchIconShown() {
-    DCHECK(after_action_state_);
-    base::Optional<BrowserState> browser_state =
-        GetStateForBrowser(after_action_state_->browsers, browser());
-    ASSERT_TRUE(browser_state.has_value());
-    EXPECT_TRUE(browser_state->launch_icon_shown);
-  }
-
-  void AssertLaunchIconNotShown() {
-    DCHECK(after_action_state_);
-    base::Optional<BrowserState> browser_state =
-        GetStateForBrowser(after_action_state_->browsers, browser());
-    ASSERT_TRUE(browser_state.has_value());
-    EXPECT_FALSE(browser_state->launch_icon_shown);
-  }
-
-  void AssertTabCreated() {
-    DCHECK(before_action_state_);
-    DCHECK(after_action_state_);
-    base::Optional<BrowserState> most_recent_browser_state =
-        GetStateForBrowser(after_action_state_->browsers, browser());
-    base::Optional<BrowserState> previous_browser_state =
-        GetStateForBrowser(before_action_state_->browsers, browser());
-    ASSERT_TRUE(most_recent_browser_state.has_value());
-    ASSERT_TRUE(previous_browser_state.has_value());
-    EXPECT_GT(most_recent_browser_state->tabs.size(),
-              previous_browser_state->tabs.size());
-
-    base::Optional<TabState> active_tab =
-        GetStateForActiveTab(most_recent_browser_state.value());
-    ASSERT_TRUE(active_tab.has_value());
-    EXPECT_EQ(GetInstallableAppURL(), active_tab->url);
-  }
-
-  void AssertWindowCreated() {
-    DCHECK(before_action_state_);
-    DCHECK(after_action_state_);
-    EXPECT_GT(after_action_state_->browsers.size(),
-              before_action_state_->browsers.size());
-  }
-
-  GURL GetInstallableAppURL() {
-    return https_server_.GetURL("/banners/manifest_test_page.html");
-  }
-
-  GURL GetNonInstallableAppURL() {
-    return https_server_.GetURL("/banners/no_manifest_test_page.html");
-  }
-
-  GURL GetInScopeURL() {
-    return https_server_.GetURL("/banners/manifest_test_page.html");
-  }
-
-  GURL GetOutOfScopeURL() {
-    return https_server_.GetURL("/out_of_scope/index.html");
-  }
-
-  content::WebContents* GetCurrentTab(Browser* browser) {
-    return browser->tab_strip_model()->GetActiveWebContents();
-  }
-
-  Profile* profile() { return browser()->profile(); }
-  Browser* app_browser() { return app_browser_; }
-  WebAppProvider* GetProvider() { return WebAppProvider::Get(profile()); }
-  std::vector<std::string>& testing_actions() { return testing_actions_; }
-  PageActionIconView* pwa_install_view() { return pwa_install_view_; }
-
- private:
-  StateSnapshot ConstructStateSnapshot() {
-    base::flat_map<Browser*, BrowserState> browser_state;
-    auto* browser_list = BrowserList::GetInstance();
-    for (Browser* browser : *browser_list) {
-      TabStripModel* tabs = browser->tab_strip_model();
-      base::flat_map<content::WebContents*, TabState> tab_state_map;
-      for (int i = 0; i < tabs->count(); ++i) {
-        content::WebContents* tab = tabs->GetWebContentsAt(i);
-        DCHECK(tab);
-        GURL url = tab->GetURL();
-        auto* app_banner_manager =
-            webapps::TestAppBannerManagerDesktop::FromWebContents(tab);
-        bool installable = app_banner_manager->WaitForInstallableCheck();
-
-        tab_state_map.emplace(tab, TabState(url, installable));
-      }
-      bool is_app_browser = AppBrowserController::IsWebApp(browser);
-      bool install_icon_visible = false;
-      bool launch_icon_visible = false;
-      content::WebContents* active_tab = tabs->GetActiveWebContents();
-      if (!is_app_browser) {
-        install_icon_visible =
-            GetAppMenuCommandState(IDC_INSTALL_PWA, browser) == kEnabled;
-        launch_icon_visible =
-            GetAppMenuCommandState(IDC_OPEN_IN_PWA_WINDOW, browser) == kEnabled;
-      }
-      browser_state.emplace(
-          browser, BrowserState(browser, tab_state_map, active_tab,
-                                AppBrowserController::IsWebApp(browser),
-                                install_icon_visible, launch_icon_visible));
-    }
-
-    auto* registrar = WebAppProvider::Get(browser()->profile())
-                          ->registrar()
-                          .AsWebAppRegistrar();
-    auto app_ids = registrar->GetAppIds();
-    base::flat_map<AppId, AppState> app_state;
-    for (const auto& app_id : app_ids) {
-      app_state.emplace(
-          app_id, AppState(app_id, registrar->GetAppShortName(app_id),
-                           registrar->GetAppScope(app_id),
-                           registrar->GetAppEffectiveDisplayMode(app_id)));
-    }
-    return StateSnapshot(std::move(browser_state), std::move(app_state));
-  }
-
-  std::unique_ptr<StateSnapshot> before_action_state_;
-  std::unique_ptr<StateSnapshot> after_action_state_;
-  base::flat_map<std::string, bool> site_installability_map_;
-  Browser* app_browser_ = nullptr;
-  std::vector<AppId> app_ids_;
-  std::vector<std::string> testing_actions_;
-  NavigateToSiteResult last_navigation_result_;
-  AppId active_app_id_;
-  net::EmbeddedTestServer https_server_;
-  PageActionIconView* pwa_install_view_ = nullptr;
-  ScopedOsHooksSuppress os_hooks_suppress_;
+  WebAppIntegrationBrowserTestBase helper_;
 };
 
-// Tests that installing a PWA will cause the install icon to be hidden, and
-// the launch icon to be shown.
-IN_PROC_BROWSER_TEST_F(WebAppIntegrationBrowserTest,
-                       InstallAndVerifyUIUpdates) {
-  bool installable =
-      NavigateToSite(browser(), GetInstallableAppURL()).installable;
-  ASSERT_TRUE(installable);
-
-  EXPECT_EQ(GetAppMenuCommandState(IDC_CREATE_SHORTCUT, browser()), kEnabled);
-  EXPECT_EQ(GetAppMenuCommandState(IDC_INSTALL_PWA, browser()), kEnabled);
-  EXPECT_TRUE(pwa_install_view()->GetVisible());
-  EXPECT_EQ(GetAppMenuCommandState(IDC_OPEN_IN_PWA_WINDOW, browser()),
-            kNotPresent);
-
-  InstallOmniboxOrMenu();
-
-  chrome::NewTab(browser());
-  NavigateToSite(browser(), GetInstallableAppURL());
-  EXPECT_EQ(GetAppMenuCommandState(IDC_INSTALL_PWA, browser()), kNotPresent);
-  EXPECT_FALSE(pwa_install_view()->GetVisible());
-  EXPECT_EQ(GetAppMenuCommandState(IDC_OPEN_IN_PWA_WINDOW, browser()),
-            kEnabled);
-}
-
-IN_PROC_BROWSER_TEST_F(WebAppIntegrationBrowserTest, LaunchInternal) {
-  auto* browser_list = BrowserList::GetInstance();
-  EXPECT_EQ(1U, browser_list->size());
-  EXPECT_FALSE(AppBrowserController::IsWebApp(browser_list->GetLastActive()));
-  NavigateToSite(browser(), GetInstallableAppURL());
-  InstallOmniboxOrMenu();
-  EXPECT_EQ(2U, browser_list->size());
-  EXPECT_TRUE(AppBrowserController::IsWebApp(browser_list->GetLastActive()));
-  ClosePWA();
-  EXPECT_EQ(1U, browser_list->size());
-  EXPECT_FALSE(AppBrowserController::IsWebApp(browser_list->GetLastActive()));
-  LaunchInternal();
-  EXPECT_EQ(2U, browser_list->size());
-  EXPECT_TRUE(AppBrowserController::IsWebApp(browser_list->GetLastActive()));
-}
-
 IN_PROC_BROWSER_TEST_P(WebAppIntegrationBrowserTest, Default) {
-  ParseParams();
+  helper_.ParseParams(GetParam());
 
-  for (auto& action : testing_actions()) {
-    ExecuteAction(action);
+  for (auto& action : helper_.testing_actions()) {
+    helper_.ExecuteAction(action);
   }
 }
 
diff --git a/chrome/browser/ui/views/web_apps/web_app_integration_browsertest_base.cc b/chrome/browser/ui/views/web_apps/web_app_integration_browsertest_base.cc
new file mode 100644
index 0000000..ce647e6
--- /dev/null
+++ b/chrome/browser/ui/views/web_apps/web_app_integration_browsertest_base.cc
@@ -0,0 +1,747 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/views/web_apps/web_app_integration_browsertest_base.h"
+
+#include "base/base_paths.h"
+#include "base/containers/flat_map.h"
+#include "base/files/file_util.h"
+#include "base/strings/string_split.h"
+#include "base/test/bind.h"
+#include "build/build_config.h"
+#include "build/chromeos_buildflags.h"
+#include "chrome/app/chrome_command_ids.h"
+#include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_commands.h"
+#include "chrome/browser/ui/browser_dialogs.h"
+#include "chrome/browser/ui/browser_list.h"
+#include "chrome/browser/ui/views/frame/browser_view.h"
+#include "chrome/browser/ui/views/frame/toolbar_button_provider.h"
+#include "chrome/browser/ui/views/page_action/page_action_icon_view.h"
+#include "chrome/browser/ui/web_applications/test/web_app_browsertest_util.h"
+#include "chrome/browser/ui/web_applications/web_app_dialog_utils.h"
+#include "chrome/browser/ui/web_applications/web_app_menu_model.h"
+#include "chrome/browser/web_applications/components/app_registry_controller.h"
+#include "chrome/browser/web_applications/components/install_finalizer.h"
+#include "chrome/browser/web_applications/components/policy/web_app_policy_constants.h"
+#include "chrome/browser/web_applications/components/policy/web_app_policy_manager.h"
+#include "chrome/browser/web_applications/components/web_app_constants.h"
+#include "chrome/browser/web_applications/components/web_app_id.h"
+#include "chrome/browser/web_applications/components/web_app_provider_base.h"
+#include "chrome/browser/web_applications/test/web_app_install_observer.h"
+#include "chrome/browser/web_applications/web_app_registrar.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "components/prefs/scoped_user_pref_update.h"
+#include "content/public/test/test_navigation_observer.h"
+#include "extensions/browser/extension_dialog_auto_confirm.h"
+#include "net/dns/mock_host_resolver.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "third_party/blink/public/mojom/manifest/display_mode.mojom-shared.h"
+#include "third_party/re2/src/re2/re2.h"
+
+namespace web_app {
+
+namespace {
+
+const std::string kTestCaseFilename =
+    "web_app_integration_browsertest_cases.csv";
+const std::string kExpectationsFilename = "TestExpectations";
+const std::string kPlatformName =
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+    "ChromeOS";
+#elif defined(OS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)
+    "Linux";
+#elif defined(OS_MAC)
+    "Mac";
+#elif defined(OS_WIN)
+    "Win";
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+
+// Command-line switch that overrides test case input. Takes a comma
+// separated list of testing actions. This aids in development of tests
+// by allowing one to run a single test at a time, and avoid running every
+// test case in the suite.
+const char kWebAppIntegrationTestCase[] = "web-app-integration-test-case";
+
+}  // anonymous namespace
+
+BrowserState::BrowserState(
+    Browser* browser_ptr,
+    base::flat_map<content::WebContents*, TabState> tab_state,
+    content::WebContents* active_web_contents,
+    bool is_an_app_browser,
+    bool install_icon_visible,
+    bool launch_icon_visible)
+    : browser(browser_ptr),
+      tabs(std::move(tab_state)),
+      active_tab(active_web_contents),
+      is_app_browser(is_an_app_browser),
+      install_icon_shown(install_icon_visible),
+      launch_icon_shown(launch_icon_visible) {}
+BrowserState::~BrowserState() = default;
+BrowserState::BrowserState(const BrowserState&) = default;
+bool BrowserState::operator==(const BrowserState& other) const {
+  return browser == other.browser && tabs == other.tabs &&
+         active_tab == other.active_tab &&
+         is_app_browser == other.is_app_browser &&
+         install_icon_shown == other.install_icon_shown &&
+         launch_icon_shown == other.launch_icon_shown;
+}
+
+AppState::AppState(web_app::AppId app_id,
+                   const std::string app_name,
+                   const GURL app_scope,
+                   const blink::mojom::DisplayMode& app_display_mode)
+    : id(app_id),
+      name(app_name),
+      scope(app_scope),
+      display_mode(app_display_mode) {}
+AppState::~AppState() = default;
+AppState::AppState(const AppState&) = default;
+bool AppState::operator==(const AppState& other) const {
+  return id == other.id && name == other.name && scope == other.scope &&
+         display_mode == other.display_mode;
+}
+
+StateSnapshot::StateSnapshot(
+    base::flat_map<Browser*, BrowserState> browser_state,
+    base::flat_map<web_app::AppId, AppState> app_state)
+    : browsers(std::move(browser_state)), apps(std::move(app_state)) {}
+StateSnapshot::~StateSnapshot() = default;
+StateSnapshot::StateSnapshot(const StateSnapshot&) = default;
+bool StateSnapshot::operator==(const StateSnapshot& other) const {
+  return browsers == other.browsers && apps == other.apps;
+}
+
+WebAppIntegrationBrowserTestBase::WebAppIntegrationBrowserTestBase(
+    InProcessBrowserTest* in_process_browser_test)
+    : in_process_browser_test_(in_process_browser_test),
+      https_server_(net::EmbeddedTestServer::TYPE_HTTPS) {}
+
+WebAppIntegrationBrowserTestBase::~WebAppIntegrationBrowserTestBase() = default;
+
+// static
+base::Optional<BrowserState>
+WebAppIntegrationBrowserTestBase::GetStateForBrowser(
+    base::flat_map<Browser*, BrowserState> browser_state_map,
+    Browser* browser) {
+  auto it = browser_state_map.find(browser);
+  return it == browser_state_map.end()
+             ? base::nullopt
+             : base::make_optional<BrowserState>(it->second);
+}
+
+// static
+base::Optional<TabState> WebAppIntegrationBrowserTestBase::GetStateForActiveTab(
+    BrowserState browser_state) {
+  if (!browser_state.active_tab) {
+    return base::nullopt;
+  }
+
+  auto it = browser_state.tabs.find(browser_state.active_tab);
+  DCHECK(it != browser_state.tabs.end());
+  return base::make_optional<TabState>(it->second);
+}
+
+// static
+base::Optional<AppState> WebAppIntegrationBrowserTestBase::GetStateForAppId(
+    base::flat_map<web_app::AppId, AppState> apps,
+    web_app::AppId id) {
+  auto it = apps.find(id);
+  return it == apps.end() ? base::nullopt
+                          : base::make_optional<AppState>(it->second);
+}
+
+// static
+bool WebAppIntegrationBrowserTestBase::IsInspectionAction(
+    const std::string& action) {
+  return base::StartsWith(action, "assert_");
+}
+
+// static
+std::string WebAppIntegrationBrowserTestBase::StripAllWhitespace(
+    std::string line) {
+  std::string output;
+  output.reserve(line.size());
+  for (const char& c : line) {
+    if (!isspace(c)) {
+      output += c;
+    }
+  }
+  return output;
+}
+
+// static
+std::string WebAppIntegrationBrowserTestBase::GetCommandLineTestOverride() {
+  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+  if (command_line->HasSwitch(kWebAppIntegrationTestCase)) {
+    return command_line->GetSwitchValueASCII(kWebAppIntegrationTestCase);
+  }
+  return "";
+}
+
+void WebAppIntegrationBrowserTestBase::SetUp(base::FilePath test_data_dir) {
+  https_server_.AddDefaultHandlers(test_data_dir);
+  ASSERT_TRUE(https_server_.Start());
+
+  webapps::TestAppBannerManagerDesktop::SetUp();
+}
+
+void WebAppIntegrationBrowserTestBase::SetUpOnMainThread() {
+  os_hooks_suppress_ = OsIntegrationManager::ScopedSuppressOsHooksForTesting();
+
+  pwa_install_view_ =
+      BrowserView::GetBrowserViewForBrowser(in_process_browser_test_->browser())
+          ->toolbar_button_provider()
+          ->GetPageActionIconView(PageActionIconType::kPwaInstall);
+  ASSERT_TRUE(pwa_install_view_);
+  EXPECT_FALSE(pwa_install_view_->GetVisible());
+}
+
+void WebAppIntegrationBrowserTestBase::ParseParams(std::string action_strings) {
+  // Useful for debugging since all tests are run in a single parameterized
+  // test.
+  LOG(ERROR) << "Test case: " << action_strings;
+  testing_actions_ = base::SplitString(
+      action_strings, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
+}
+
+base::FilePath WebAppIntegrationBrowserTestBase::GetTestFilePath(
+    base::FilePath test_data_dir,
+    const std::string& file_name) {
+  return test_data_dir.AppendASCII(file_name);
+}
+
+std::vector<std::string> WebAppIntegrationBrowserTestBase::ReadTestInputFile(
+    base::FilePath test_data_dir,
+    const std::string& file_name) {
+  std::vector<std::string> test_cases;
+  std::string command_line_test_case = GetCommandLineTestOverride();
+  if (!command_line_test_case.empty()) {
+    test_cases.push_back(StripAllWhitespace(command_line_test_case));
+    return test_cases;
+  }
+
+  base::FilePath file = GetTestFilePath(test_data_dir, file_name);
+  std::string contents;
+  if (!base::ReadFileToString(file, &contents)) {
+    return test_cases;
+  }
+
+  std::vector<std::string> file_lines = base::SplitString(
+      contents, "\n", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
+  for (const auto& line : file_lines) {
+    if (line[0] == '#') {
+      continue;
+    }
+
+    if (line.find('|') == std::string::npos) {
+      test_cases.push_back(StripAllWhitespace(line));
+      continue;
+    }
+
+    std::vector<std::string> platforms_and_test = base::SplitString(
+        line, "|", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
+    if (platforms_and_test[0].find(kPlatformName) != std::string::npos) {
+      test_cases.push_back(StripAllWhitespace(platforms_and_test[1]));
+    }
+  }
+
+  return test_cases;
+}
+
+std::vector<std::string>
+WebAppIntegrationBrowserTestBase::GetPlatformIgnoredTests(
+    base::FilePath test_data_dir,
+    const std::string& file_name) {
+  base::FilePath file = GetTestFilePath(test_data_dir, file_name);
+  std::string contents;
+  std::vector<std::string> platform_expectations;
+  if (!base::ReadFileToString(file, &contents)) {
+    return platform_expectations;
+  }
+
+  std::vector<std::string> file_lines = base::SplitString(
+      contents, "\n", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
+  for (const auto& line : file_lines) {
+    if (line[0] == '#') {
+      continue;
+    }
+
+    std::string platform;
+    std::string expectation;
+    std::string test_case;
+    RE2::FullMatch(
+        line, "crbug.com/\\d* \\[ (\\w*) \\] \\[ (\\w*) \\] ([\\w*,\\s*]*)",
+        &platform, &expectation, &test_case);
+    if (platform == kPlatformName) {
+      if (expectation == "Skip") {
+        platform_expectations.push_back(StripAllWhitespace(test_case));
+      } else {
+        NOTREACHED() << "Unsupported expectation " << expectation;
+      }
+    }
+  }
+  return platform_expectations;
+}
+
+std::vector<std::string>
+WebAppIntegrationBrowserTestBase::BuildAllPlatformTestCaseSet(
+    base::FilePath test_data_dir) {
+  std::vector<std::string> test_cases_all =
+      ReadTestInputFile(test_data_dir, kTestCaseFilename);
+  std::sort(test_cases_all.begin(), test_cases_all.end());
+
+  std::vector<std::string> ignored_cases =
+      GetPlatformIgnoredTests(test_data_dir, kExpectationsFilename);
+  std::sort(ignored_cases.begin(), ignored_cases.end());
+
+  std::vector<std::string> final_tests(test_cases_all.size());
+  auto iter = std::set_difference(test_cases_all.begin(), test_cases_all.end(),
+                                  ignored_cases.begin(), ignored_cases.end(),
+                                  final_tests.begin());
+  final_tests.resize(iter - final_tests.begin());
+  return final_tests;
+}
+
+// Non-assert actions implemented before assert actions. Implemented in
+// alphabetical order.
+void WebAppIntegrationBrowserTestBase::ExecuteAction(
+    const std::string& action_string) {
+  if (base::EndsWith(action_string, "site_b")) {
+    FAIL() << "site_b actions not yet supported: " << action_string;
+  }
+
+  if (!IsInspectionAction(action_string)) {
+    before_action_state_ = std::move(after_action_state_);
+  }
+
+  if (base::StartsWith(action_string, "add_policy_app_internal_tabbed")) {
+    AddPolicyAppInternal(base::Value(kDefaultLaunchContainerTabValue));
+  } else if (base::StartsWith(action_string,
+                              "add_policy_app_internal_windowed")) {
+    AddPolicyAppInternal(base::Value(kDefaultLaunchContainerWindowValue));
+  } else if (action_string == "close_pwa") {
+    ClosePWA();
+  } else if (action_string == "install_create_shortcut_tabbed") {
+    InstallCreateShortcutTabbed();
+  } else if (base::StartsWith(action_string, "install_internal_windowed")) {
+    InstallOmniboxOrMenu();
+  } else if (action_string == "install_omnibox_or_menu") {
+    InstallOmniboxOrMenu();
+  } else if (base::StartsWith(action_string, "launch_internal")) {
+    LaunchInternal();
+  } else if (action_string == "list_apps_internal") {
+    ListAppsInternal();
+  } else if (base::StartsWith(action_string, "navigate_browser_in_scope")) {
+    NavigateToSite(browser(), GetInScopeURL());
+  } else if (base::StartsWith(action_string, "navigate_installable")) {
+    NavigateToSite(browser(), GetInstallableAppURL());
+  } else if (action_string == "navigate_not_installable") {
+    NavigateToSite(browser(), GetNonInstallableAppURL());
+  } else if (action_string == "remove_policy_app") {
+    RemovePolicyApp();
+  } else if (base::StartsWith(action_string, "set_open_in_tab_internal")) {
+    SetOpenInTabInternal();
+  } else if (base::StartsWith(action_string, "set_open_in_window_internal")) {
+    SetOpenInWindowInternal();
+  } else if (action_string == "uninstall_from_menu") {
+    UninstallFromMenu();
+  } else if (base::StartsWith(action_string, "uninstall_internal")) {
+    UninstallInternal();
+  } else if (action_string == "assert_app_in_list_not_windowed") {
+    AssertAppInListNotWindowed();
+  } else if (base::StartsWith(action_string, "assert_app_not_in_list")) {
+    AssertAppNotInList();
+  } else if (action_string == "assert_display_mode_standalone_internal") {
+    AssertDisplayModeInternal(DisplayMode::kStandalone);
+  } else if (action_string == "assert_display_mode_browser_internal") {
+    AssertDisplayModeInternal(DisplayMode::kBrowser);
+  } else if (action_string == "assert_installable") {
+    AssertInstallable();
+  } else if (action_string == "assert_install_icon_shown") {
+    AssertInstallIconShown();
+  } else if (action_string == "assert_install_icon_not_shown") {
+    AssertInstallIconNotShown();
+  } else if (action_string == "assert_launch_icon_shown") {
+    AssertLaunchIconShown();
+  } else if (action_string == "assert_launch_icon_not_shown") {
+    AssertLaunchIconNotShown();
+  } else if (action_string == "assert_no_crash") {
+  } else if (action_string == "assert_tab_created") {
+    AssertTabCreated();
+  } else if (action_string == "assert_window_created") {
+    AssertWindowCreated();
+  } else {
+    FAIL() << "Unimplemented action: " << action_string;
+  }
+
+  if (IsInspectionAction(action_string)) {
+    DCHECK(!after_action_state_ ||
+           *after_action_state_ == ConstructStateSnapshot());
+  } else {
+    after_action_state_ =
+        std::make_unique<StateSnapshot>(ConstructStateSnapshot());
+  }
+}
+
+// Automated Testing Actions
+void WebAppIntegrationBrowserTestBase::AddPolicyAppInternal(
+    base::Value default_launch_container) {
+  GURL url = GetInstallableAppURL();
+  auto* web_app_registrar =
+      WebAppProvider::Get(profile())->registrar().AsWebAppRegistrar();
+  base::RunLoop run_loop;
+  WebAppInstallObserver observer(profile());
+  observer.SetWebAppInstalledDelegate(
+      base::BindLambdaForTesting([&](const AppId& app_id) {
+        bool is_installed = web_app_registrar->IsInstalled(app_id);
+        GURL installed_url = web_app_registrar->GetAppStartUrl(app_id);
+        if (is_installed && installed_url.is_valid() &&
+            installed_url.spec() == url.spec()) {
+          active_app_id_ = app_id;
+          run_loop.Quit();
+        }
+      }));
+  {
+    base::Value item(base::Value::Type::DICTIONARY);
+    item.SetKey(kUrlKey, base::Value(url.spec()));
+    item.SetKey(kDefaultLaunchContainerKey,
+                std::move(default_launch_container));
+    ListPrefUpdate update(profile()->GetPrefs(),
+                          prefs::kWebAppInstallForceList);
+    update->Append(item.Clone());
+  }
+  run_loop.Run();
+}
+
+void WebAppIntegrationBrowserTestBase::ClosePWA() {
+  DCHECK(app_browser_);
+  app_browser_->window()->Close();
+  ui_test_utils::WaitForBrowserToClose(app_browser_);
+}
+
+void WebAppIntegrationBrowserTestBase::InstallCreateShortcutTabbed() {
+  chrome::SetAutoAcceptWebAppDialogForTesting(/*auto_accept=*/true,
+                                              /*auto_open_in_window=*/false);
+  WebAppInstallObserver observer(profile());
+  CHECK(chrome::ExecuteCommand(browser(), IDC_CREATE_SHORTCUT));
+  active_app_id_ = observer.AwaitNextInstall();
+  chrome::SetAutoAcceptWebAppDialogForTesting(false, false);
+}
+
+web_app::AppId WebAppIntegrationBrowserTestBase::InstallOmniboxOrMenu() {
+  chrome::SetAutoAcceptPWAInstallConfirmationForTesting(true);
+
+  web_app::AppId app_id;
+  base::RunLoop run_loop;
+  web_app::SetInstalledCallbackForTesting(base::BindLambdaForTesting(
+      [&app_id, &run_loop](const web_app::AppId& installed_app_id,
+                           web_app::InstallResultCode code) {
+        app_id = installed_app_id;
+        run_loop.Quit();
+      }));
+
+  pwa_install_view()->ExecuteForTesting();
+
+  run_loop.Run();
+
+  chrome::SetAutoAcceptPWAInstallConfirmationForTesting(false);
+  active_app_id_ = app_id;
+  auto* browser_list = BrowserList::GetInstance();
+  app_browser_ = browser_list->GetLastActive();
+  DCHECK(AppBrowserController::IsWebApp(app_browser_));
+
+  return app_id;
+}
+
+void WebAppIntegrationBrowserTestBase::LaunchInternal() {
+  auto* web_app_provider = GetProvider();
+  AppRegistrar& app_registrar = web_app_provider->registrar();
+  DisplayMode display_mode =
+      app_registrar.GetAppEffectiveDisplayMode(active_app_id_);
+  if (display_mode == blink::mojom::DisplayMode::kStandalone) {
+    app_browser_ = LaunchWebAppBrowserAndWait(
+        ProfileManager::GetActiveUserProfile(), active_app_id_);
+  } else {
+    ui_test_utils::UrlLoadObserver url_observer(
+        WebAppProviderBase::GetProviderBase(profile())
+            ->registrar()
+            .GetAppLaunchUrl(active_app_id_),
+        content::NotificationService::AllSources());
+    LaunchBrowserForWebAppInTab(profile(), active_app_id_);
+    url_observer.Wait();
+  }
+}
+
+void WebAppIntegrationBrowserTestBase::ListAppsInternal() {
+  auto* web_app_registrar =
+      WebAppProvider::Get(profile())->registrar().AsWebAppRegistrar();
+  app_ids_ = web_app_registrar->GetAppIds();
+}
+
+NavigateToSiteResult WebAppIntegrationBrowserTestBase::NavigateToSite(
+    Browser* browser,
+    const GURL& url) {
+  content::WebContents* web_contents = GetCurrentTab(browser);
+  auto* app_banner_manager =
+      webapps::TestAppBannerManagerDesktop::FromWebContents(web_contents);
+
+  ui_test_utils::NavigateToURL(browser, url);
+  bool installable = app_banner_manager->WaitForInstallableCheck();
+
+  last_navigation_result_ =
+      NavigateToSiteResult{web_contents, app_banner_manager, installable};
+  site_installability_map_[url.spec()] = installable;
+  return last_navigation_result_;
+}
+
+void WebAppIntegrationBrowserTestBase::RemovePolicyApp() {
+  GURL url = GetInstallableAppURL();
+  base::RunLoop run_loop;
+  WebAppInstallObserver observer(profile());
+  observer.SetWebAppUninstalledDelegate(
+      base::BindLambdaForTesting([&](const AppId& app_id) {
+        if (active_app_id_ == app_id) {
+          run_loop.Quit();
+        }
+      }));
+  {
+    ListPrefUpdate update(profile()->GetPrefs(),
+                          prefs::kWebAppInstallForceList);
+    update->EraseListValueIf([&](const base::Value& item) {
+      const base::Value* url_value = item.FindKey(kUrlKey);
+      return url_value && url_value->GetString() == url.spec();
+    });
+  }
+  run_loop.Run();
+}
+
+void WebAppIntegrationBrowserTestBase::SetOpenInTabInternal() {
+  auto& app_registry_controller =
+      WebAppProvider::Get(profile())->registry_controller();
+  app_registry_controller.SetAppUserDisplayMode(
+      active_app_id_, blink::mojom::DisplayMode::kBrowser, true);
+}
+
+void WebAppIntegrationBrowserTestBase::SetOpenInWindowInternal() {
+  auto& app_registry_controller =
+      WebAppProvider::Get(profile())->registry_controller();
+  app_registry_controller.SetAppUserDisplayMode(
+      active_app_id_, blink::mojom::DisplayMode::kStandalone, true);
+}
+
+// TODO(https://crbug.com/1159651): Support this action on CrOS.
+void WebAppIntegrationBrowserTestBase::UninstallFromMenu() {
+  DCHECK(app_browser_);
+  base::RunLoop run_loop;
+  WebAppInstallObserver observer(profile());
+  observer.SetWebAppUninstalledDelegate(
+      base::BindLambdaForTesting([&](const AppId& app_id) {
+        if (app_id == active_app_id_) {
+          run_loop.Quit();
+        }
+      }));
+
+  extensions::ScopedTestDialogAutoConfirm auto_confirm(
+      extensions::ScopedTestDialogAutoConfirm::ACCEPT);
+  auto app_menu_model =
+      std::make_unique<WebAppMenuModel>(/*provider=*/nullptr, app_browser_);
+  app_menu_model->Init();
+  ui::MenuModel* model = app_menu_model.get();
+  int index = -1;
+  const bool found = app_menu_model->GetModelAndIndexForCommandId(
+      WebAppMenuModel::kUninstallAppCommandId, &model, &index);
+  EXPECT_TRUE(found);
+  EXPECT_TRUE(model->IsEnabledAt(index));
+
+  app_menu_model->ExecuteCommand(WebAppMenuModel::kUninstallAppCommandId,
+                                 /*event_flags=*/0);
+  // The |app_menu_model| must be destroyed here, as the |run_loop| waits
+  // until the app is fully uninstalled, which includes closing and deleting
+  // the app_browser_.
+  app_menu_model.reset();
+  app_browser_ = nullptr;
+  run_loop.Run();
+}
+
+void WebAppIntegrationBrowserTestBase::UninstallInternal() {
+  WebAppProviderBase* const provider =
+      WebAppProviderBase::GetProviderBase(profile());
+  base::RunLoop run_loop;
+
+  DCHECK(provider->install_finalizer().CanUserUninstallExternalApp(
+      active_app_id_));
+  provider->install_finalizer().UninstallExternalAppByUser(
+      active_app_id_, base::BindLambdaForTesting([&](bool uninstalled) {
+        EXPECT_TRUE(uninstalled);
+        run_loop.Quit();
+      }));
+
+  run_loop.Run();
+}
+
+// Assert Actions
+void WebAppIntegrationBrowserTestBase::AssertAppInListNotWindowed() {
+  EXPECT_TRUE(base::Contains(app_ids_, active_app_id_));
+  DCHECK(after_action_state_);
+  base::Optional<AppState> app_state =
+      GetStateForAppId(after_action_state_->apps, active_app_id_);
+  ASSERT_TRUE(app_state.has_value());
+  EXPECT_EQ(DisplayMode::kBrowser, app_state->display_mode);
+}
+
+void WebAppIntegrationBrowserTestBase::AssertAppNotInList() {
+  EXPECT_FALSE(base::Contains(app_ids_, active_app_id_));
+}
+
+void WebAppIntegrationBrowserTestBase::AssertDisplayModeInternal(
+    DisplayMode display_mode) {
+  DCHECK(after_action_state_);
+  base::Optional<AppState> app_state =
+      GetStateForAppId(after_action_state_->apps, active_app_id_);
+  ASSERT_TRUE(app_state.has_value());
+  EXPECT_EQ(display_mode, app_state->display_mode);
+}
+
+void WebAppIntegrationBrowserTestBase::AssertInstallable() {
+  DCHECK(after_action_state_);
+  base::Optional<BrowserState> browser_state =
+      GetStateForBrowser(after_action_state_->browsers, browser());
+  ASSERT_TRUE(browser_state.has_value());
+  base::Optional<TabState> active_tab =
+      GetStateForActiveTab(browser_state.value());
+  ASSERT_TRUE(active_tab.has_value());
+  EXPECT_TRUE(active_tab->is_installable);
+}
+
+void WebAppIntegrationBrowserTestBase::AssertInstallIconShown() {
+  DCHECK(after_action_state_);
+  base::Optional<BrowserState> browser_state =
+      GetStateForBrowser(after_action_state_->browsers, browser());
+  ASSERT_TRUE(browser_state.has_value());
+  EXPECT_TRUE(browser_state->install_icon_shown);
+  EXPECT_TRUE(pwa_install_view()->GetVisible());
+}
+
+void WebAppIntegrationBrowserTestBase::AssertInstallIconNotShown() {
+  base::Optional<BrowserState> browser_state =
+      GetStateForBrowser(after_action_state_->browsers, browser());
+  ASSERT_TRUE(browser_state.has_value());
+  EXPECT_FALSE(browser_state->install_icon_shown);
+  EXPECT_FALSE(pwa_install_view()->GetVisible());
+}
+
+void WebAppIntegrationBrowserTestBase::AssertLaunchIconShown() {
+  DCHECK(after_action_state_);
+  base::Optional<BrowserState> browser_state =
+      GetStateForBrowser(after_action_state_->browsers, browser());
+  ASSERT_TRUE(browser_state.has_value());
+  EXPECT_TRUE(browser_state->launch_icon_shown);
+}
+
+void WebAppIntegrationBrowserTestBase::AssertLaunchIconNotShown() {
+  DCHECK(after_action_state_);
+  base::Optional<BrowserState> browser_state =
+      GetStateForBrowser(after_action_state_->browsers, browser());
+  ASSERT_TRUE(browser_state.has_value());
+  EXPECT_FALSE(browser_state->launch_icon_shown);
+}
+
+void WebAppIntegrationBrowserTestBase::AssertTabCreated() {
+  DCHECK(before_action_state_);
+  DCHECK(after_action_state_);
+  base::Optional<BrowserState> most_recent_browser_state =
+      GetStateForBrowser(after_action_state_->browsers, browser());
+  base::Optional<BrowserState> previous_browser_state =
+      GetStateForBrowser(before_action_state_->browsers, browser());
+  ASSERT_TRUE(most_recent_browser_state.has_value());
+  ASSERT_TRUE(previous_browser_state.has_value());
+  EXPECT_GT(most_recent_browser_state->tabs.size(),
+            previous_browser_state->tabs.size());
+
+  base::Optional<TabState> active_tab =
+      GetStateForActiveTab(most_recent_browser_state.value());
+  ASSERT_TRUE(active_tab.has_value());
+  EXPECT_EQ(GetInstallableAppURL(), active_tab->url);
+}
+
+void WebAppIntegrationBrowserTestBase::AssertWindowCreated() {
+  DCHECK(before_action_state_);
+  DCHECK(after_action_state_);
+  EXPECT_GT(after_action_state_->browsers.size(),
+            before_action_state_->browsers.size());
+}
+
+GURL WebAppIntegrationBrowserTestBase::GetInstallableAppURL() {
+  return https_server_.GetURL("/banners/manifest_test_page.html");
+}
+
+GURL WebAppIntegrationBrowserTestBase::GetNonInstallableAppURL() {
+  return https_server_.GetURL("/banners/no_manifest_test_page.html");
+}
+
+GURL WebAppIntegrationBrowserTestBase::GetInScopeURL() {
+  return https_server_.GetURL("/banners/manifest_test_page.html");
+}
+
+GURL WebAppIntegrationBrowserTestBase::GetOutOfScopeURL() {
+  return https_server_.GetURL("/out_of_scope/index.html");
+}
+
+content::WebContents* WebAppIntegrationBrowserTestBase::GetCurrentTab(
+    Browser* browser) {
+  return browser->tab_strip_model()->GetActiveWebContents();
+}
+
+StateSnapshot WebAppIntegrationBrowserTestBase::ConstructStateSnapshot() {
+  base::flat_map<Browser*, BrowserState> browser_state;
+  auto* browser_list = BrowserList::GetInstance();
+  for (Browser* browser : *browser_list) {
+    TabStripModel* tabs = browser->tab_strip_model();
+    base::flat_map<content::WebContents*, TabState> tab_state_map;
+    for (int i = 0; i < tabs->count(); ++i) {
+      content::WebContents* tab = tabs->GetWebContentsAt(i);
+      DCHECK(tab);
+      GURL url = tab->GetURL();
+      auto* app_banner_manager =
+          webapps::TestAppBannerManagerDesktop::FromWebContents(tab);
+      bool installable = app_banner_manager->WaitForInstallableCheck();
+
+      tab_state_map.emplace(tab, TabState(url, installable));
+    }
+    bool is_app_browser = AppBrowserController::IsWebApp(browser);
+    bool install_icon_visible = false;
+    bool launch_icon_visible = false;
+    content::WebContents* active_tab = tabs->GetActiveWebContents();
+    if (!is_app_browser) {
+      install_icon_visible =
+          GetAppMenuCommandState(IDC_INSTALL_PWA, browser) == kEnabled;
+      launch_icon_visible =
+          GetAppMenuCommandState(IDC_OPEN_IN_PWA_WINDOW, browser) == kEnabled;
+    }
+    browser_state.emplace(
+        browser, BrowserState(browser, tab_state_map, active_tab,
+                              AppBrowserController::IsWebApp(browser),
+                              install_icon_visible, launch_icon_visible));
+  }
+
+  auto* registrar = WebAppProvider::Get(browser()->profile())
+                        ->registrar()
+                        .AsWebAppRegistrar();
+  auto app_ids = registrar->GetAppIds();
+  base::flat_map<AppId, AppState> app_state;
+  for (const auto& app_id : app_ids) {
+    app_state.emplace(app_id,
+                      AppState(app_id, registrar->GetAppShortName(app_id),
+                               registrar->GetAppScope(app_id),
+                               registrar->GetAppEffectiveDisplayMode(app_id)));
+  }
+  return StateSnapshot(std::move(browser_state), std::move(app_state));
+}
+
+}  // namespace web_app
diff --git a/chrome/browser/ui/views/web_apps/web_app_integration_browsertest_base.h b/chrome/browser/ui/views/web_apps/web_app_integration_browsertest_base.h
new file mode 100644
index 0000000..cd2201e
--- /dev/null
+++ b/chrome/browser/ui/views/web_apps/web_app_integration_browsertest_base.h
@@ -0,0 +1,177 @@
+// 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_UI_VIEWS_WEB_APPS_WEB_APP_INTEGRATION_BROWSERTEST_BASE_H_
+#define CHROME_BROWSER_UI_VIEWS_WEB_APPS_WEB_APP_INTEGRATION_BROWSERTEST_BASE_H_
+
+#include "chrome/browser/banners/test_app_banner_manager_desktop.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/views/page_action/page_action_icon_view.h"
+#include "chrome/browser/web_applications/components/os_integration_manager.h"
+#include "chrome/browser/web_applications/components/web_app_id.h"
+#include "chrome/browser/web_applications/web_app_provider.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
+
+namespace web_app {
+
+struct TabState {
+  TabState(GURL tab_url, bool is_tab_installable)
+      : url(tab_url), is_installable(is_tab_installable) {}
+  TabState& operator=(const TabState&) = default;
+  bool operator==(const TabState& other) const {
+    return url == other.url && is_installable == other.is_installable;
+  }
+
+  GURL url;
+  bool is_installable;
+};
+
+struct BrowserState {
+  BrowserState(Browser* browser_ptr,
+               base::flat_map<content::WebContents*, TabState> tab_state,
+               content::WebContents* active_web_contents,
+               bool is_an_app_browser,
+               bool install_icon_visible,
+               bool launch_icon_visible);
+  ~BrowserState();
+  BrowserState(const BrowserState&);
+  bool operator==(const BrowserState& other) const;
+
+  Browser* browser;
+  base::flat_map<content::WebContents*, TabState> tabs;
+  content::WebContents* active_tab;
+  bool is_app_browser;
+  bool install_icon_shown;
+  bool launch_icon_shown;
+};
+
+struct AppState {
+  AppState(web_app::AppId app_id,
+           const std::string app_name,
+           const GURL app_scope,
+           const blink::mojom::DisplayMode& app_display_mode);
+  ~AppState();
+  AppState(const AppState&);
+  bool operator==(const AppState& other) const;
+
+  web_app::AppId id;
+  std::string name;
+  GURL scope;
+  blink::mojom::DisplayMode display_mode;
+};
+
+struct StateSnapshot {
+  StateSnapshot(base::flat_map<Browser*, BrowserState> browser_state,
+                base::flat_map<web_app::AppId, AppState> app_state);
+  ~StateSnapshot();
+  StateSnapshot(const StateSnapshot&);
+  bool operator==(const StateSnapshot& other) const;
+
+  base::flat_map<Browser*, BrowserState> browsers;
+  base::flat_map<web_app::AppId, AppState> apps;
+};
+
+struct NavigateToSiteResult {
+  content::WebContents* web_contents;
+  webapps::TestAppBannerManagerDesktop* app_banner_manager;
+  bool installable;
+};
+
+class WebAppIntegrationBrowserTestBase {
+ public:
+  explicit WebAppIntegrationBrowserTestBase(
+      InProcessBrowserTest* in_process_browser_test);
+  ~WebAppIntegrationBrowserTestBase();
+
+  static base::Optional<BrowserState> GetStateForBrowser(
+      base::flat_map<Browser*, BrowserState> browser_state_map,
+      Browser* browser);
+  static base::Optional<TabState> GetStateForActiveTab(
+      BrowserState browser_state);
+  static base::Optional<AppState> GetStateForAppId(
+      base::flat_map<web_app::AppId, AppState> apps,
+      web_app::AppId id);
+
+  static bool IsInspectionAction(const std::string& action);
+  static std::string StripAllWhitespace(std::string line);
+  static std::string GetCommandLineTestOverride();
+
+  void SetUp(base::FilePath test_data_dir);
+  void SetUpOnMainThread();
+
+  // Test Framework
+  static base::FilePath GetTestFilePath(base::FilePath test_data_dir,
+                                        const std::string& file_name);
+  static std::vector<std::string> ReadTestInputFile(
+      base::FilePath test_data_dir,
+      const std::string& file_name);
+  static std::vector<std::string> GetPlatformIgnoredTests(
+      base::FilePath test_data_dir,
+      const std::string& file_name);
+  static std::vector<std::string> BuildAllPlatformTestCaseSet(
+      base::FilePath test_data_dir);
+  void ParseParams(std::string action_strings);
+  void ExecuteAction(const std::string& action_string);
+
+  // Automated Testing Actions
+  void AddPolicyAppInternal(base::Value default_launch_container);
+  void ClosePWA();
+  void InstallCreateShortcutTabbed();
+  web_app::AppId InstallOmniboxOrMenu();
+  void LaunchInternal();
+  void ListAppsInternal();
+  NavigateToSiteResult NavigateToSite(Browser* browser, const GURL& url);
+  void RemovePolicyApp();
+  void SetOpenInTabInternal();
+  void SetOpenInWindowInternal();
+  void UninstallFromMenu();
+  void UninstallInternal();
+
+  // Assert Actions
+  void AssertAppInListNotWindowed();
+  void AssertAppNotInList();
+  void AssertDisplayModeInternal(DisplayMode display_mode);
+  void AssertInstallable();
+  void AssertInstallIconShown();
+  void AssertInstallIconNotShown();
+  void AssertLaunchIconShown();
+  void AssertLaunchIconNotShown();
+  void AssertTabCreated();
+  void AssertWindowCreated();
+
+  std::vector<std::string>& testing_actions() { return testing_actions_; }
+  GURL GetInstallableAppURL();
+
+ private:
+  StateSnapshot ConstructStateSnapshot();
+  GURL GetNonInstallableAppURL();
+  GURL GetInScopeURL();
+  GURL GetOutOfScopeURL();
+
+  content::WebContents* GetCurrentTab(Browser* browser);
+  Browser* browser() { return in_process_browser_test_->browser(); }
+  Profile* profile() { return browser()->profile(); }
+  Browser* app_browser() { return app_browser_; }
+  WebAppProvider* GetProvider() { return WebAppProvider::Get(profile()); }
+  PageActionIconView* pwa_install_view() { return pwa_install_view_; }
+
+  InProcessBrowserTest* in_process_browser_test_;
+  std::unique_ptr<StateSnapshot> before_action_state_;
+  std::unique_ptr<StateSnapshot> after_action_state_;
+  base::flat_map<std::string, bool> site_installability_map_;
+  Browser* app_browser_ = nullptr;
+  std::vector<AppId> app_ids_;
+  std::vector<std::string> testing_actions_;
+  NavigateToSiteResult last_navigation_result_;
+  AppId active_app_id_;
+  net::EmbeddedTestServer https_server_;
+  base::FilePath test_data_dir_;
+  PageActionIconView* pwa_install_view_ = nullptr;
+  ScopedOsHooksSuppress os_hooks_suppress_;
+};
+
+}  // namespace web_app
+
+#endif  // CHROME_BROWSER_UI_VIEWS_WEB_APPS_WEB_APP_INTEGRATION_BROWSERTEST_BASE_H_
diff --git a/chrome/browser/ui/webui/print_preview/print_preview_handler.cc b/chrome/browser/ui/webui/print_preview/print_preview_handler.cc
index c0ed6e19..0f39a5e 100644
--- a/chrome/browser/ui/webui/print_preview/print_preview_handler.cc
+++ b/chrome/browser/ui/webui/print_preview/print_preview_handler.cc
@@ -712,7 +712,7 @@
   DCHECK(profile);
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
-  if (chromeos::IsAccountManagerAvailable(profile)) {
+  if (ash::IsAccountManagerAvailable(profile)) {
     // Chrome OS Account Manager is enabled on this Profile and hence, all
     // account management flows will go through native UIs and not through a
     // tabbed browser window.
diff --git a/chrome/browser/ui/webui/settings/chromeos/apps_section.cc b/chrome/browser/ui/webui/settings/chromeos/apps_section.cc
index eeb6db1..c9e20c5 100644
--- a/chrome/browser/ui/webui/settings/chromeos/apps_section.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/apps_section.cc
@@ -138,6 +138,19 @@
 void AddGuestOsStrings(content::WebUIDataSource* html_source) {
   // These strings are used for both Crostini and Plugin VM.
   static constexpr webui::LocalizedString kLocalizedStrings[] = {
+      {"guestOsSharedPaths", IDS_SETTINGS_GUEST_OS_SHARED_PATHS},
+      {"guestOsSharedPathsListHeading",
+       IDS_SETTINGS_GUEST_OS_SHARED_PATHS_LIST_HEADING},
+      {"guestOsSharedPathsInstructionsRemove",
+       IDS_SETTINGS_GUEST_OS_SHARED_PATHS_INSTRUCTIONS_REMOVE},
+      {"guestOsSharedPathsStopSharing",
+       IDS_SETTINGS_GUEST_OS_SHARED_PATHS_STOP_SHARING},
+      {"guestOsSharedPathsRemoveFailureDialogTitle",
+       IDS_SETTINGS_GUEST_OS_SHARED_PATHS_REMOVE_FAILURE_DIALOG_TITLE},
+      {"guestOsSharedPathsRemoveFailureTryAgain",
+       IDS_SETTINGS_GUEST_OS_SHARED_PATHS_REMOVE_FAILURE_TRY_AGAIN},
+      {"guestOsSharedPathsListEmptyMessage",
+       IDS_SETTINGS_GUEST_OS_SHARED_PATHS_LIST_EMPTY_MESSAGE},
       {"guestOsSharedUsbDevicesLabel",
        IDS_SETTINGS_GUEST_OS_SHARED_USB_DEVICES_LABEL},
       {"guestOsSharedUsbDevicesExtraDescription",
@@ -265,12 +278,11 @@
       IDS_SETTINGS_APP_DETAILS_TITLE, mojom::Subpage::kAppDetails,
       mojom::Subpage::kAppManagement, mojom::SearchResultIcon::kAppsGrid,
       mojom::SearchResultDefaultRank::kMedium, mojom::kAppDetailsSubpagePath);
-  generator->RegisterNestedSubpage(IDS_SETTINGS_APPS_PLUGIN_VM_SHARED_PATHS,
-                                   mojom::Subpage::kPluginVmSharedPaths,
-                                   mojom::Subpage::kAppManagement,
-                                   mojom::SearchResultIcon::kAppsGrid,
-                                   mojom::SearchResultDefaultRank::kMedium,
-                                   mojom::kPluginVmSharedPathsSubpagePath);
+  generator->RegisterNestedSubpage(
+      IDS_SETTINGS_GUEST_OS_SHARED_PATHS, mojom::Subpage::kPluginVmSharedPaths,
+      mojom::Subpage::kAppManagement, mojom::SearchResultIcon::kAppsGrid,
+      mojom::SearchResultDefaultRank::kMedium,
+      mojom::kPluginVmSharedPathsSubpagePath);
   generator->RegisterNestedSubpage(
       IDS_SETTINGS_GUEST_OS_SHARED_USB_DEVICES_LABEL,
       mojom::Subpage::kPluginVmUsbPreferences, mojom::Subpage::kAppManagement,
@@ -329,23 +341,10 @@
 void AppsSection::AddPluginVmLoadTimeData(
     content::WebUIDataSource* html_source) {
   static constexpr webui::LocalizedString kLocalizedStrings[] = {
-      {"pluginVmSharedPaths", IDS_SETTINGS_APPS_PLUGIN_VM_SHARED_PATHS},
-      {"pluginVmSharedPathsListHeading",
-       IDS_SETTINGS_APPS_PLUGIN_VM_SHARED_PATHS_LIST_HEADING},
       {"pluginVmSharedPathsInstructionsAdd",
        IDS_SETTINGS_APPS_PLUGIN_VM_SHARED_PATHS_INSTRUCTIONS_ADD},
-      {"pluginVmSharedPathsInstructionsRemove",
-       IDS_SETTINGS_APPS_PLUGIN_VM_SHARED_PATHS_INSTRUCTIONS_REMOVE},
-      {"pluginVmSharedPathsRemoveSharing",
-       IDS_SETTINGS_APPS_PLUGIN_VM_SHARED_PATHS_REMOVE_SHARING},
       {"pluginVmSharedPathsRemoveFailureDialogMessage",
        IDS_SETTINGS_APPS_PLUGIN_VM_SHARED_PATHS_REMOVE_FAILURE_DIALOG_MESSAGE},
-      {"pluginVmSharedPathsRemoveFailureDialogTitle",
-       IDS_SETTINGS_APPS_PLUGIN_VM_SHARED_PATHS_REMOVE_FAILURE_DIALOG_TITLE},
-      {"pluginVmSharedPathsRemoveFailureTryAgain",
-       IDS_SETTINGS_APPS_PLUGIN_VM_SHARED_PATHS_REMOVE_FAILURE_TRY_AGAIN},
-      {"pluginVmSharedPathsListEmptyMessage",
-       IDS_SETTINGS_APPS_PLUGIN_VM_SHARED_PATHS_LIST_EMPTY_MESSAGE},
       {"pluginVmSharedUsbDevicesDescription",
        IDS_SETTINGS_APPS_PLUGIN_VM_SHARED_USB_DEVICES_DESCRIPTION},
       {"pluginVmPermissionDialogCameraLabel",
diff --git a/chrome/browser/ui/webui/settings/chromeos/crostini_section.cc b/chrome/browser/ui/webui/settings/chromeos/crostini_section.cc
index 7587911..c94e19d 100644
--- a/chrome/browser/ui/webui/settings/chromeos/crostini_section.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/crostini_section.cc
@@ -236,23 +236,10 @@
       {"crostiniPageTitle", IDS_SETTINGS_CROSTINI_TITLE},
       {"crostiniPageLabel", IDS_SETTINGS_CROSTINI_LABEL},
       {"crostiniEnable", IDS_SETTINGS_TURN_ON},
-      {"crostiniSharedPaths", IDS_SETTINGS_CROSTINI_SHARED_PATHS},
-      {"crostiniSharedPathsListHeading",
-       IDS_SETTINGS_CROSTINI_SHARED_PATHS_LIST_HEADING},
       {"crostiniSharedPathsInstructionsAdd",
        IDS_SETTINGS_CROSTINI_SHARED_PATHS_INSTRUCTIONS_ADD},
-      {"crostiniSharedPathsInstructionsRemove",
-       IDS_SETTINGS_CROSTINI_SHARED_PATHS_INSTRUCTIONS_REMOVE},
-      {"crostiniSharedPathsRemoveSharing",
-       IDS_SETTINGS_CROSTINI_SHARED_PATHS_REMOVE_SHARING},
       {"crostiniSharedPathsRemoveFailureDialogMessage",
        IDS_SETTINGS_CROSTINI_SHARED_PATHS_REMOVE_FAILURE_DIALOG_MESSAGE},
-      {"crostiniSharedPathsRemoveFailureDialogTitle",
-       IDS_SETTINGS_CROSTINI_SHARED_PATHS_REMOVE_FAILURE_DIALOG_TITLE},
-      {"crostiniSharedPathsRemoveFailureTryAgain",
-       IDS_SETTINGS_CROSTINI_SHARED_PATHS_REMOVE_FAILURE_TRY_AGAIN},
-      {"crostiniSharedPathsListEmptyMessage",
-       IDS_SETTINGS_CROSTINI_SHARED_PATHS_LIST_EMPTY_MESSAGE},
       {"crostiniExportImportTitle", IDS_SETTINGS_CROSTINI_EXPORT_IMPORT_TITLE},
       {"crostiniExport", IDS_SETTINGS_CROSTINI_EXPORT},
       {"crostiniExportLabel", IDS_SETTINGS_CROSTINI_EXPORT_LABEL},
@@ -473,7 +460,7 @@
 
   // Manage shared folders.
   generator->RegisterNestedSubpage(
-      IDS_SETTINGS_CROSTINI_SHARED_PATHS,
+      IDS_SETTINGS_GUEST_OS_SHARED_PATHS,
       mojom::Subpage::kCrostiniManageSharedFolders,
       mojom::Subpage::kCrostiniDetails, mojom::SearchResultIcon::kPenguin,
       mojom::SearchResultDefaultRank::kMedium,
diff --git a/chrome/browser/ui/webui/settings/chromeos/device_storage_handler_unittest.cc b/chrome/browser/ui/webui/settings/chromeos/device_storage_handler_unittest.cc
index 474a14a..e82018c 100644
--- a/chrome/browser/ui/webui/settings/chromeos/device_storage_handler_unittest.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/device_storage_handler_unittest.cc
@@ -106,7 +106,7 @@
     CHECK(base::CreateDirectory(my_files_path));
     CHECK(storage::ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
         file_manager::util::GetDownloadsMountPointName(profile_),
-        storage::kFileSystemTypeNativeLocal, storage::FileSystemMountOption(),
+        storage::kFileSystemTypeLocal, storage::FileSystemMountOption(),
         my_files_path));
   }
 
@@ -320,7 +320,7 @@
   // Register android files mount point.
   CHECK(storage::ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
       file_manager::util::GetAndroidFilesMountPointName(),
-      storage::kFileSystemTypeNativeLocal, storage::FileSystemMountOption(),
+      storage::kFileSystemTypeLocal, storage::FileSystemMountOption(),
       android_files_path));
 
   // Add files in My files and android files.
diff --git a/chrome/browser/ui/webui/settings/chromeos/people_section.cc b/chrome/browser/ui/webui/settings/chromeos/people_section.cc
index 2bf8f8fb..bcf9c1cd 100644
--- a/chrome/browser/ui/webui/settings/chromeos/people_section.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/people_section.cc
@@ -60,6 +60,8 @@
 namespace settings {
 namespace {
 
+using ::ash::IsAccountManagerAvailable;
+
 const std::vector<SearchConcept>& GetPeopleSearchConcepts() {
   static const base::NoDestructor<std::vector<SearchConcept>> tags([] {
     std::vector<SearchConcept> all_tags({
diff --git a/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
index 988bb11..03f096b 100644
--- a/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
@@ -1250,7 +1250,7 @@
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   // Toggles the Chrome OS Account Manager submenu in the People section.
   html_source->AddBoolean("isAccountManagerEnabled",
-                          chromeos::IsAccountManagerAvailable(profile));
+                          ash::IsAccountManagerAvailable(profile));
 #elif BUILDFLAG(IS_CHROMEOS_LACROS)
   html_source->AddBoolean("isAccountManagerEnabled",
                           IsAccountManagerAvailable(profile));
diff --git a/chrome/browser/ui/webui/settings/settings_ui.cc b/chrome/browser/ui/webui/settings/settings_ui.cc
index 8f51463..2f69077 100644
--- a/chrome/browser/ui/webui/settings/settings_ui.cc
+++ b/chrome/browser/ui/webui/settings/settings_ui.cc
@@ -379,7 +379,7 @@
 
   // TODO(jamescook): Sort out how account management is split between Chrome OS
   // and browser settings.
-  if (chromeos::IsAccountManagerAvailable(profile)) {
+  if (ash::IsAccountManagerAvailable(profile)) {
     chromeos::AccountManagerFactory* factory =
         g_browser_process->platform_part()->GetAccountManagerFactory();
     chromeos::AccountManager* account_manager =
diff --git a/chrome/browser/ui/webui/signin/inline_login_dialog_chromeos.h b/chrome/browser/ui/webui/signin/inline_login_dialog_chromeos.h
index cbaf956..ca1e1be 100644
--- a/chrome/browser/ui/webui/signin/inline_login_dialog_chromeos.h
+++ b/chrome/browser/ui/webui/signin/inline_login_dialog_chromeos.h
@@ -17,6 +17,10 @@
 
 class GURL;
 
+namespace ash {
+class AccountManagerUIImpl;
+}
+
 namespace chromeos {
 
 // Extends from |SystemWebDialogDelegate| to create an always-on-top but movable
@@ -99,7 +103,7 @@
  private:
   // `Show` method can be called directly only by `AccountManagerUIImpl` class.
   // To show the dialog, use `AccountManagerFacade`.
-  friend class AccountManagerUIImpl;
+  friend class ash::AccountManagerUIImpl;
 
   // Displays the dialog. |close_dialog_closure| will be called when the dialog
   // is closed.
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index 7bf9bae..1f125d3 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-master-1611856733-a21458dcae1f312e1b9bb9647f9f3b0865709090.profdata
+chrome-linux-master-1611878402-7f3d0a17d68f247d75e69369a67930b8470b50fb.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index be1da09..3fee1885 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-master-1611856733-3e44d52638b87d5eb2c9ef1ab766a80a1b8db797.profdata
+chrome-mac-master-1611878402-2d30cdfb2e2d99278586dac534fff3b65fc772ea.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index fccfcf8..2201b8bd 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-master-1611834165-4b335240b4b05daf7ebafae407d3b94205e08a4f.profdata
+chrome-win32-master-1611867260-2a7afb31171a7c6ba4d158bc4269427e2f851b66.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index 575f8950..6250839 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-master-1611834165-f4f84b17839308b62c7941fe22fb5fc879fe91da.profdata
+chrome-win64-master-1611867260-5f4da81c3097621296b6304a437aecf17fe13b8e.profdata
diff --git a/chrome/common/extensions/api/_manifest_features.json b/chrome/common/extensions/api/_manifest_features.json
index a2dbd61..d5e059f 100644
--- a/chrome/common/extensions/api/_manifest_features.json
+++ b/chrome/common/extensions/api/_manifest_features.json
@@ -66,8 +66,7 @@
   },
   "commands": {
     "channel": "stable",
-    "extension_types": ["extension", "platform_app"],
-    "min_manifest_version": 2
+    "extension_types": ["extension", "platform_app"]
   },
   "content_scripts": {
     "channel": "stable",
@@ -217,8 +216,7 @@
       "legacy_packaged_app",
       "platform_app",
       "login_screen_extension"
-    ],
-    "min_manifest_version": 2
+    ]
   },
   "system_indicator": [
     {
diff --git a/chrome/common/extensions/api/_permission_features.json b/chrome/common/extensions/api/_permission_features.json
index 21cd3b6..c03de9e 100644
--- a/chrome/common/extensions/api/_permission_features.json
+++ b/chrome/common/extensions/api/_permission_features.json
@@ -37,8 +37,7 @@
   },
   "activeTab": {
     "channel": "stable",
-    "extension_types": ["extension", "legacy_packaged_app"],
-    "min_manifest_version": 2
+    "extension_types": ["extension", "legacy_packaged_app"]
   },
   "activityLogPrivate": {
     "channel": "stable",
diff --git a/chrome/common/extensions/api/scripting.idl b/chrome/common/extensions/api/scripting.idl
index 2060e8f4..ce1512ff 100644
--- a/chrome/common/extensions/api/scripting.idl
+++ b/chrome/common/extensions/api/scripting.idl
@@ -70,7 +70,7 @@
 
   dictionary InjectionResult {
     // The result of the script execution.
-    any result;
+    any? result;
 
     // The frame associated with the injection.
     long frameId;
diff --git a/chrome/common/extensions/manifest_unittest.cc b/chrome/common/extensions/manifest_unittest.cc
index 4b23fea9..f43acfa 100644
--- a/chrome/common/extensions/manifest_unittest.cc
+++ b/chrome/common/extensions/manifest_unittest.cc
@@ -19,6 +19,7 @@
 #include "extensions/common/features/simple_feature.h"
 #include "extensions/common/install_warning.h"
 #include "extensions/common/manifest_constants.h"
+#include "extensions/common/value_builder.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace extensions {
@@ -182,50 +183,73 @@
   MutateManifest(&manifest, keys::kLaunchWebURL, nullptr);
 }
 
-// Verifies that the getters filter restricted keys.
-TEST_F(ManifestUnitTest, RestrictedKeys) {
-  std::unique_ptr<base::DictionaryValue> value(new base::DictionaryValue());
-  value->SetString(keys::kName, "extension");
-  value->SetString(keys::kVersion, "1");
+// Verifies that the getters filter restricted keys taking into account the
+// manifest version.
+TEST_F(ManifestUnitTest, RestrictedKeys_ManifestVersion) {
+  std::unique_ptr<base::DictionaryValue> value =
+      DictionaryBuilder()
+          .Set(keys::kName, "extension")
+          .Set(keys::kVersion, "1")
+          .Set(keys::kManifestVersion, 2)
+          .Build();
 
-  std::unique_ptr<Manifest> manifest(
-      new Manifest(Manifest::INTERNAL, std::move(value),
-                   crx_file::id_util::GenerateId("extid")));
+  auto manifest =
+      std::make_unique<Manifest>(Manifest::INTERNAL, std::move(value),
+                                 crx_file::id_util::GenerateId("extid"));
   std::string error;
   std::vector<InstallWarning> warnings;
   EXPECT_TRUE(manifest->ValidateManifest(&error, &warnings));
   EXPECT_TRUE(error.empty());
   EXPECT_TRUE(warnings.empty());
 
-  // "Commands" requires manifest version 2.
+  // "host_permissions" requires manifest version 3.
+  MutateManifest(&manifest, keys::kHostPermissions,
+                 std::make_unique<base::Value>(base::Value::Type::LIST));
   const base::Value* output = nullptr;
-  MutateManifest(&manifest, keys::kCommands,
-                 std::make_unique<base::DictionaryValue>());
-  EXPECT_FALSE(manifest->HasKey(keys::kCommands));
-  EXPECT_FALSE(manifest->Get(keys::kCommands, &output));
+  EXPECT_FALSE(manifest->HasKey(keys::kHostPermissions));
+  EXPECT_FALSE(manifest->Get(keys::kHostPermissions, &output));
 
+  // Update the extension to be manifest_version: 3; the host_permissions
+  // should then be available.
   MutateManifest(&manifest, keys::kManifestVersion,
-                 std::make_unique<base::Value>(2));
-  EXPECT_TRUE(manifest->HasKey(keys::kCommands));
-  EXPECT_TRUE(manifest->Get(keys::kCommands, &output));
+                 std::make_unique<base::Value>(3));
+  EXPECT_TRUE(manifest->HasKey(keys::kHostPermissions));
+  EXPECT_TRUE(manifest->Get(keys::kHostPermissions, &output));
+}
 
-  MutateManifest(&manifest, keys::kPageAction,
-                 std::make_unique<base::DictionaryValue>());
+// Verifies that the getters filter restricted keys taking into account the
+// item type.
+TEST_F(ManifestUnitTest, RestrictedKeys_ItemType) {
+  std::unique_ptr<base::DictionaryValue> value =
+      DictionaryBuilder()
+          .Set(keys::kName, "item")
+          .Set(keys::kVersion, "1")
+          .Set(keys::kManifestVersion, 2)
+          .Set(keys::kPageAction,
+               std::make_unique<base::Value>(base::Value::Type::DICTIONARY))
+          .Build();
+
+  auto manifest =
+      std::make_unique<Manifest>(Manifest::INTERNAL, std::move(value),
+                                 crx_file::id_util::GenerateId("extid"));
+  std::string error;
+  std::vector<InstallWarning> warnings;
+  EXPECT_TRUE(manifest->ValidateManifest(&error, &warnings));
+  EXPECT_TRUE(error.empty());
+  EXPECT_TRUE(warnings.empty());
   AssertType(manifest.get(), Manifest::TYPE_EXTENSION);
+
+  // Extensions can specify "page_action"...
+  const base::Value* output = nullptr;
   EXPECT_TRUE(manifest->HasKey(keys::kPageAction));
   EXPECT_TRUE(manifest->Get(keys::kPageAction, &output));
 
-  // Platform apps cannot have a "page_action" key.
   MutateManifest(&manifest, keys::kPlatformAppBackground,
                  std::make_unique<base::DictionaryValue>());
   AssertType(manifest.get(), Manifest::TYPE_PLATFORM_APP);
+  // ...But platform apps may not.
   EXPECT_FALSE(manifest->HasKey(keys::kPageAction));
   EXPECT_FALSE(manifest->Get(keys::kPageAction, &output));
-  MutateManifest(&manifest, keys::kPlatformAppBackground, nullptr);
-
-  // Platform apps also can't have a "Commands" key.
-  EXPECT_FALSE(manifest->HasKey(keys::kCommands));
-  EXPECT_FALSE(manifest->Get(keys::kCommands, &output));
 }
 
 }  // namespace extensions
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index a882060..1494bdd 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -1511,6 +1511,8 @@
       "../browser/ui/views/web_apps/frame_toolbar/web_app_frame_toolbar_browsertest.cc",
       "../browser/ui/views/web_apps/frame_toolbar/web_app_minimal_ui_test.cc",
       "../browser/ui/views/web_apps/web_app_integration_browsertest.cc",
+      "../browser/ui/views/web_apps/web_app_integration_browsertest_base.cc",
+      "../browser/ui/views/web_apps/web_app_integration_browsertest_base.h",
       "../browser/ui/views/web_apps/web_app_tab_strip_browsertest.cc",
       "../browser/ui/views/webauthn/authenticator_dialog_view_browsertest.cc",
       "../browser/ui/views/webview_accessibility_browsertest.cc",
diff --git a/chrome/test/data/extensions/api_test/scripting/main_frame/worker.js b/chrome/test/data/extensions/api_test/scripting/main_frame/worker.js
index 43e59a0..e428c53 100644
--- a/chrome/test/data/extensions/api_test/scripting/main_frame/worker.js
+++ b/chrome/test/data/extensions/api_test/scripting/main_frame/worker.js
@@ -65,6 +65,79 @@
     chrome.test.succeed();
   },
 
+  async function injectedFunctionReturnsNothing() {
+    const query = {url: 'http://example.com/*'};
+    let tab = await getSingleTab(query);
+    const results = await new Promise(resolve => {
+      chrome.scripting.executeScript(
+          {
+            target: {
+              tabId: tab.id,
+            },
+            // Note: This function has no return statement; in JS, this means
+            // the return value will be undefined.
+            function: () => { },
+          },
+          resolve);
+    });
+    chrome.test.assertNoLastError();
+    chrome.test.assertEq(1, results.length);
+    // NOTE: Undefined results are mapped to null in our bindings layer,
+    // because they converted from empty base::Values in the same way.
+    // NOTE AS WELL: We use `val === null` (rather than
+    // `assertEq(null, val)` because assertEq will classify null and undefined
+    // as equal.
+    chrome.test.assertTrue(results[0].result === null);
+    chrome.test.succeed();
+  },
+
+  async function injectedFunctionReturnsNull() {
+    const query = {url: 'http://example.com/*'};
+    let tab = await getSingleTab(query);
+    const results = await new Promise(resolve => {
+      chrome.scripting.executeScript(
+          {
+            target: {
+              tabId: tab.id,
+            },
+            function: () => { return null; },
+          },
+          resolve);
+    });
+    chrome.test.assertNoLastError();
+    chrome.test.assertEq(1, results.length);
+    // NOTE: We use `val === null` (rather than `assertEq(null, val)` because
+    // assertEq will classify null and undefined as equal.
+    chrome.test.assertTrue(results[0].result === null);
+    chrome.test.succeed();
+  },
+
+  async function injectedFunctionHasError() {
+    const query = {url: 'http://example.com/*'};
+    let tab = await getSingleTab(query);
+    chrome.scripting.executeScript(
+        {
+          target: {
+            tabId: tab.id,
+          },
+          // This will throw a runtime error, since foo, bar, and baz aren't
+          // defined.
+          function: () => {
+            foo.bar = baz;
+            return 3;
+          },
+        },
+        results => {
+          // TODO(devlin): Currently, we don't pass the error from the injected
+          // script back to the extension in any way. It'd be helpful to pass
+          // this along to the extension.
+          chrome.test.assertNoLastError();
+          chrome.test.assertEq(1, results.length);
+          chrome.test.assertEq(null, results[0].result);
+          chrome.test.succeed();
+        });
+  },
+
   async function noSuchTab() {
     const nonExistentTabId = 99999;
     // NOTE(devlin): We can't use a fancy `await` here, because the lastError
diff --git a/chrome/test/data/webui/cr_components/customize_themes_test.js b/chrome/test/data/webui/cr_components/customize_themes_test.js
index ff83ed3..6d79b5d 100644
--- a/chrome/test/data/webui/cr_components/customize_themes_test.js
+++ b/chrome/test/data/webui/cr_components/customize_themes_test.js
@@ -234,6 +234,35 @@
         });
   });
 
+  function assertSingleThemeIconIsFocusableWithTabKey(customizeThemesElement) {
+    const numberOfthemeIcons =
+        customizeThemesElement.shadowRoot.querySelectorAll('div cr-theme-icon')
+            .length;
+    const numberOfNoneFocusableThemeIcons =
+        customizeThemesElement.shadowRoot
+            .querySelectorAll('div[tabindex="-1"] cr-theme-icon')
+            .length;
+    assertEquals(numberOfNoneFocusableThemeIcons, numberOfthemeIcons - 1);
+    assertEquals(
+        customizeThemesElement.shadowRoot
+            .querySelectorAll('div[tabindex="0"] cr-theme-icon')
+            .length,
+        1);
+  }
+
+  test('No theme selected', () => {
+    const customizeThemesElement = createCustomizeThemesElement();
+    // First item of the grid has tabindex 0.
+    const focusableIcons = customizeThemesElement.shadowRoot.querySelectorAll(
+        'div[tabindex="0"] cr-theme-icon');
+    assertEquals(focusableIcons.length, 1);
+    assertEquals(
+        focusableIcons[0],
+        customizeThemesElement.shadowRoot.querySelector(
+            'div[id="autogeneratedThemeContainer"] cr-theme-icon'));
+    assertSingleThemeIconIsFocusableWithTabKey(customizeThemesElement);
+  });
+
   test('setting autogenerated theme selects and updates icon', async () => {
     // Arrange.
     const customizeThemesElement = createCustomizeThemesElement();
@@ -267,6 +296,11 @@
         selectedIcons,
         customizeThemesElement.shadowRoot.querySelectorAll(
             'div[aria-checked="true"] cr-theme-icon'));
+    assertDeepEquals(
+        selectedIcons,
+        customizeThemesElement.shadowRoot.querySelectorAll(
+            'div[tabindex="0"] cr-theme-icon'));
+    assertSingleThemeIconIsFocusableWithTabKey(customizeThemesElement);
   });
 
   test('setting default theme selects and updates icon', async () => {
@@ -291,6 +325,11 @@
         selectedIcons,
         customizeThemesElement.shadowRoot.querySelectorAll(
             'div[aria-checked="true"] cr-theme-icon'));
+    assertDeepEquals(
+        selectedIcons,
+        customizeThemesElement.shadowRoot.querySelectorAll(
+            'div[tabindex="0"] cr-theme-icon'));
+    assertSingleThemeIconIsFocusableWithTabKey(customizeThemesElement);
   });
 
   test('setting Chrome theme selects and updates icon', async () => {
@@ -328,6 +367,11 @@
         selectedIcons,
         customizeThemesElement.shadowRoot.querySelectorAll(
             'div[aria-checked="true"] cr-theme-icon'));
+    assertDeepEquals(
+        selectedIcons,
+        customizeThemesElement.shadowRoot.querySelectorAll(
+            'div[tabindex="0"] cr-theme-icon'));
+    assertSingleThemeIconIsFocusableWithTabKey(customizeThemesElement);
   });
 
   test('setting third-party theme shows uninstall UI', async () => {
diff --git a/chrome/test/data/webui/settings/chromeos/crostini_page_test.js b/chrome/test/data/webui/settings/chromeos/crostini_page_test.js
index b08c51dc..57fe36e 100644
--- a/chrome/test/data/webui/settings/chromeos/crostini_page_test.js
+++ b/chrome/test/data/webui/settings/chromeos/crostini_page_test.js
@@ -153,7 +153,7 @@
         subpage.$$('#crostini-shared-paths').click();
 
         await test_util.flushTasks();
-        subpage = crostiniPage.$$('settings-crostini-shared-paths');
+        subpage = crostiniPage.$$('settings-guest-os-shared-paths');
         assertTrue(!!subpage);
       });
 
@@ -973,12 +973,14 @@
     });
   });
 
+  // Functionality is already tested in OSSettingsGuestOsSharedPathsTest,
+  // so just check that we correctly set up the page for our 'termina' VM.
   suite('SubPageSharedPaths', function() {
     let subpage;
 
     setup(async function() {
       setCrostiniPrefs(
-          true, {sharedPaths: {path1: ['termina'], path2: ['termina']}});
+          true, {sharedPaths: {path1: ['termina'], path2: ['some-other-vm']}});
 
       await test_util.flushTasks();
       settings.Router.getInstance().navigateTo(
@@ -986,75 +988,12 @@
 
       await test_util.flushTasks();
       Polymer.dom.flush();
-      subpage = crostiniPage.$$('settings-crostini-shared-paths');
+      subpage = crostiniPage.$$('settings-guest-os-shared-paths');
       assertTrue(!!subpage);
     });
 
     test('Basic', function() {
-      assertEquals(
-          3, subpage.shadowRoot.querySelectorAll('.settings-box').length);
-      assertEquals(2, subpage.shadowRoot.querySelectorAll('.list-item').length);
-    });
-
-    test('Remove', async function() {
-      assertFalse(subpage.$.crostiniInstructionsRemove.hidden);
-      assertFalse(subpage.$.crostiniList.hidden);
-      assertTrue(subpage.$.crostiniListEmpty.hidden);
-      assertTrue(!!subpage.$$('.list-item cr-icon-button'));
-      const rows = '.list-item:not([hidden])';
-      assertEquals(2, subpage.shadowRoot.querySelectorAll(rows).length);
-
-      {
-        // Remove first shared path, still one left.
-        subpage.$$('.list-item cr-icon-button').click();
-        const [vmName, path] =
-            await guestOsBrowserProxy.whenCalled('removeGuestOsSharedPath');
-        assertEquals('termina', vmName);
-        assertEquals('path1', path);
-        setCrostiniPrefs(true, {sharedPaths: {path2: ['termina']}});
-      }
-
-      await test_util.flushTasks();
-      Polymer.dom.flush();
-      assertEquals(1, subpage.shadowRoot.querySelectorAll(rows).length);
-      assertFalse(subpage.$.crostiniInstructionsRemove.hidden);
-
-      {
-        // Remove remaining shared path, none left.
-        guestOsBrowserProxy.resetResolver('removeGuestOsSharedPath');
-        subpage.$$(`${rows} cr-icon-button`).click();
-        const [vmName, path] =
-            await guestOsBrowserProxy.whenCalled('removeGuestOsSharedPath');
-        assertEquals('termina', vmName);
-        assertEquals('path2', path);
-        setCrostiniPrefs(true, {sharedPaths: {}});
-      }
-
-      await test_util.flushTasks();
-      Polymer.dom.flush();
-      // Verify remove instructions are hidden, and empty list message is shown.
-      assertTrue(subpage.$.crostiniInstructionsRemove.hidden);
-      assertTrue(subpage.$.crostiniList.hidden);
-      assertFalse(subpage.$.crostiniListEmpty.hidden);
-    });
-
-    test('RemoveFailedRetry', async function() {
-      // Remove shared path fails.
-      guestOsBrowserProxy.removeSharedPathResult = false;
-      subpage.$$('.list-item cr-icon-button').click();
-
-      await guestOsBrowserProxy.whenCalled('removeGuestOsSharedPath');
-      Polymer.dom.flush();
-      assertTrue(subpage.$$('#removeSharedPathFailedDialog').open);
-
-      // Click retry and make sure 'removeCrostiniSharedPath' is called
-      // and dialog is closed/removed.
-      guestOsBrowserProxy.removeSharedPathResult = true;
-      subpage.$$('#removeSharedPathFailedDialog')
-          .querySelector('.action-button')
-          .click();
-      await guestOsBrowserProxy.whenCalled('removeGuestOsSharedPath');
-      assertFalse(!!subpage.$$('#removeSharedPathFailedDialog'));
+      assertEquals(1, subpage.shadowRoot.querySelectorAll('.list-item').length);
     });
   });
 
diff --git a/chrome/test/data/webui/settings/chromeos/app_management/plugin_vm_shared_paths_test.js b/chrome/test/data/webui/settings/chromeos/guest_os_shared_paths_test.js
similarity index 86%
rename from chrome/test/data/webui/settings/chromeos/app_management/plugin_vm_shared_paths_test.js
rename to chrome/test/data/webui/settings/chromeos/guest_os_shared_paths_test.js
index ddc8eb3..71f1da73 100644
--- a/chrome/test/data/webui/settings/chromeos/app_management/plugin_vm_shared_paths_test.js
+++ b/chrome/test/data/webui/settings/chromeos/guest_os_shared_paths_test.js
@@ -26,7 +26,7 @@
 }
 
 suite('SharedPaths', function() {
-  /** @type {?SettingsPluginVmSharedPathsElement} */
+  /** @type {?SettingsGuestOsSharedPathsElement} */
   let page = null;
 
   /** @type {?TestGuestOsBrowserProxy} */
@@ -49,7 +49,8 @@
     guestOsBrowserProxy = new TestGuestOsBrowserProxy();
     settings.GuestOsBrowserProxyImpl.instance_ = guestOsBrowserProxy;
     PolymerTest.clearBody();
-    page = document.createElement('settings-plugin-vm-shared-paths');
+    page = document.createElement('settings-guest-os-shared-paths');
+    page.guestOsType = 'pluginVm';
     document.body.appendChild(page);
   });
 
@@ -63,9 +64,9 @@
     const rows = '.list-item:not([hidden])';
     assertEquals(2, page.shadowRoot.querySelectorAll(rows).length);
 
-    assertFalse(page.$.pluginVmInstructionsRemove.hidden);
-    assertFalse(page.$.pluginVmList.hidden);
-    assertTrue(page.$.pluginVmListEmpty.hidden);
+    assertFalse(page.$.guestOsInstructionsRemove.hidden);
+    assertFalse(page.$.guestOsList.hidden);
+    assertTrue(page.$.guestOsListEmpty.hidden);
     assertTrue(!!page.$$('.list-item cr-icon-button'));
 
     // Remove first shared path, still one left.
@@ -78,7 +79,7 @@
     }
     await setPrefs({'path2': ['PvmDefault']});
     assertEquals(1, page.shadowRoot.querySelectorAll(rows).length);
-    assertFalse(page.$.pluginVmInstructionsRemove.hidden);
+    assertFalse(page.$.guestOsInstructionsRemove.hidden);
 
     // Remove remaining shared path, none left.
     guestOsBrowserProxy.resetResolver('removeGuestOsSharedPath');
@@ -90,11 +91,11 @@
       assertEquals('path2', path);
     }
     await setPrefs({'ignored': ['ignore']});
-    assertTrue(page.$.pluginVmList.hidden);
+    assertTrue(page.$.guestOsList.hidden);
     // Verify remove instructions are hidden, and empty list message is shown.
-    assertTrue(page.$.pluginVmInstructionsRemove.hidden);
-    assertTrue(page.$.pluginVmList.hidden);
-    assertFalse(page.$.pluginVmListEmpty.hidden);
+    assertTrue(page.$.guestOsInstructionsRemove.hidden);
+    assertTrue(page.$.guestOsList.hidden);
+    assertFalse(page.$.guestOsListEmpty.hidden);
   });
 
   test('RemoveFailedRetry', async function() {
diff --git a/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js b/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js
index 01e7774f..4b001cd 100644
--- a/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js
+++ b/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js
@@ -542,31 +542,6 @@
       mocha.run();
     });
 
-// Test fixture for the Plugin VM shared paths page.
-// eslint-disable-next-line no-var
-var OSSettingsAppManagementPluginVmSharedPathsTest =
-    class extends OSSettingsAppManagementBrowserTest {
-  /** @override */
-  get browsePreload() {
-    return super.browsePreload +
-        'app_management/plugin_vm_page/plugin_vm_shared_paths.html';
-  }
-
-  /** @override */
-  get extraLibraries() {
-    return super.extraLibraries.concat([
-      BROWSER_SETTINGS_PATH + '../test_browser_proxy.js',
-      'app_management/plugin_vm_shared_paths_test.js',
-    ]);
-  }
-};
-
-TEST_F(
-    'OSSettingsAppManagementPluginVmSharedPathsTest', 'MAYBE_AllJsTests',
-    () => {
-      mocha.run();
-    });
-
 // Test fixture for the app management managed app view.
 // eslint-disable-next-line no-var
 var OSSettingsAppManagementManagedAppTest =
@@ -717,6 +692,28 @@
       mocha.grep('\\bSubPageSharedUsbDevices\\b').run();
     });
 
+// Test fixture for the Guest OS shared paths page.
+// eslint-disable-next-line no-var
+var OSSettingsGuestOsSharedPathsTest =
+    class extends OSSettingsAppManagementBrowserTest {
+  /** @override */
+  get browsePreload() {
+    return super.browsePreload + 'guest_os/guest_os_shared_paths.html';
+  }
+
+  /** @override */
+  get extraLibraries() {
+    return super.extraLibraries.concat([
+      BROWSER_SETTINGS_PATH + '../test_browser_proxy.js',
+      'guest_os_shared_paths_test.js',
+    ]);
+  }
+};
+
+TEST_F('OSSettingsGuestOsSharedPathsTest', 'MAYBE_AllJsTests', () => {
+  mocha.run();
+});
+
 // Test fixture for the Guest OS shared USB devices page.
 // eslint-disable-next-line no-var
 var OSSettingsGuestOsSharedUsbDevicesTest =
diff --git a/chrome/test/data/webui/settings/chromeos/test_crostini_browser_proxy.js b/chrome/test/data/webui/settings/chromeos/test_crostini_browser_proxy.js
index 9124562..ed13eb3 100644
--- a/chrome/test/data/webui/settings/chromeos/test_crostini_browser_proxy.js
+++ b/chrome/test/data/webui/settings/chromeos/test_crostini_browser_proxy.js
@@ -8,10 +8,6 @@
     super([
       'requestCrostiniInstallerView',
       'requestRemoveCrostini',
-      'getCrostiniSharedPathsDisplayText',
-      'notifyCrostiniSharedUsbDevicesPageReady',
-      'setCrostiniUsbDeviceShared',
-      'removeCrostiniSharedPath',
       'exportCrostiniContainer',
       'importCrostiniContainer',
       'requestCrostiniContainerUpgradeView',
@@ -33,8 +29,6 @@
       'getCrostiniMicSharingEnabled',
       'requestCrostiniInstallerStatus',
     ]);
-    this.sharedUsbDevices = [];
-    this.removeSharedPathResult = true;
     this.crostiniMicSharingEnabled = false;
     this.crostiniIsRunning = true;
     this.methodCalls_ = {};
@@ -77,30 +71,6 @@
     this.methodCalled('requestRemoveCrostini');
   }
 
-  /** override */
-  getCrostiniSharedPathsDisplayText(paths) {
-    this.methodCalled('getCrostiniSharedPathsDisplayText');
-    return Promise.resolve(paths.map(path => path + '-displayText'));
-  }
-
-  /** @override */
-  notifyCrostiniSharedUsbDevicesPageReady() {
-    this.methodCalled('notifyCrostiniSharedUsbDevicesPageReady');
-    cr.webUIListenerCallback(
-        'crostini-shared-usb-devices-changed', this.sharedUsbDevices);
-  }
-
-  /** @override */
-  setCrostiniUsbDeviceShared(guid, shared) {
-    this.methodCalled('setCrostiniUsbDeviceShared', [guid, shared]);
-  }
-
-  /** override */
-  removeCrostiniSharedPath(vmName, path) {
-    this.methodCalled('removeCrostiniSharedPath', [vmName, path]);
-    return Promise.resolve(this.removeSharedPathResult);
-  }
-
   /** @override */
   requestCrostiniInstallerStatus() {
     this.methodCalled('requestCrostiniInstallerStatus');
diff --git a/chrome/test/data/webui/signin/local_profile_customization_test.js b/chrome/test/data/webui/signin/local_profile_customization_test.js
index 80e09ac..822fbc9 100644
--- a/chrome/test/data/webui/signin/local_profile_customization_test.js
+++ b/chrome/test/data/webui/signin/local_profile_customization_test.js
@@ -27,9 +27,11 @@
     document.body.innerHTML = '';
     customizeProfileElement = /** @type {!LocalProfileCustomizationElement} */ (
         document.createElement('local-profile-customization'));
+    customizeProfileElement.profileThemeInfo = browserProxy.profileThemeInfo;
     document.body.appendChild(customizeProfileElement);
+    await browserProxy.whenCalled('getProfileThemeInfo');
+    browserProxy.resetResolver('getProfileThemeInfo');
     await waitBeforeNextRender(customizeProfileElement);
-    await setProfileTheme(browserProxy.profileThemeInfo);
   }
 
   setup(function() {
diff --git a/chrome/test/media_router/BUILD.gn b/chrome/test/media_router/BUILD.gn
index 1ebfd0a..0920c6e 100644
--- a/chrome/test/media_router/BUILD.gn
+++ b/chrome/test/media_router/BUILD.gn
@@ -20,6 +20,7 @@
     "media_router_integration_browsertest.cc",
     "media_router_integration_browsertest.h",
     "media_router_integration_ui_browsertest.cc",
+    "media_router_native_integration_browsertest.cc",
     "media_router_one_ua_integration_browsertest.cc",
     "media_router_ui_for_test.cc",
     "media_router_ui_for_test.h",
@@ -30,6 +31,7 @@
     "//chrome/app:generated_resources",
     "//chrome/browser",
     "//chrome/browser/media/router",
+    "//chrome/browser/media/router:test_support",
     "//chrome/browser/ui",
     "//chrome/common",
     "//chrome/test:test_support",
diff --git a/chrome/test/media_router/media_router_integration_browsertest.cc b/chrome/test/media_router/media_router_integration_browsertest.cc
index ad7ef980..017e805b 100644
--- a/chrome/test/media_router/media_router_integration_browsertest.cc
+++ b/chrome/test/media_router/media_router_integration_browsertest.cc
@@ -412,10 +412,6 @@
   ASSERT_EQ(session_id, reconnected_session_id);
 }
 
-IN_PROC_BROWSER_TEST_F(MediaRouterIntegrationBrowserTest, Basic) {
-  RunBasicTest();
-}
-
 // Tests that creating a route with a local file opens the file in a new tab.
 //
 // This test is disabled because the test needs to wait until navigation is
diff --git a/chrome/test/media_router/media_router_native_integration_browsertest.cc b/chrome/test/media_router/media_router_native_integration_browsertest.cc
new file mode 100644
index 0000000..2f04f51
--- /dev/null
+++ b/chrome/test/media_router/media_router_native_integration_browsertest.cc
@@ -0,0 +1,52 @@
+// 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/media/router/mojo/media_router_desktop.h"
+#include "chrome/browser/media/router/providers/test/test_media_route_provider.h"
+#include "chrome/test/media_router/media_router_integration_browsertest.h"
+#include "components/media_router/browser/media_router_factory.h"
+#include "components/media_router/common/media_route_provider_helper.h"
+#include "content/public/test/browser_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace media_router {
+
+// TODO(crbug.com/1152593): Merge this class back into
+// MediaRouterIntegrationBrowserTest once all the test cases have been converted
+// to use the native test MRP instead of the extension MRP. Then the extension
+// setup code in MediaRouterBaseBrowserTest can also be deleted.
+class MediaRouterNativeIntegrationBrowserTest
+    : public MediaRouterIntegrationBrowserTest {
+ public:
+  MediaRouterNativeIntegrationBrowserTest() = default;
+  ~MediaRouterNativeIntegrationBrowserTest() override = default;
+
+  void SetUpOnMainThread() override {
+    // We don't call super::SetUpOnMainThread() here so that we're not setting
+    // up the extension test MRP.
+    MediaRouterMojoImpl* router = static_cast<MediaRouterMojoImpl*>(
+        MediaRouterFactory::GetApiForBrowserContext(browser()->profile()));
+    mojo::PendingRemote<mojom::MediaRouter> media_router_remote;
+    mojo::PendingRemote<mojom::MediaRouteProvider> provider_remote;
+    router->BindToMojoReceiver(
+        media_router_remote.InitWithNewPipeAndPassReceiver());
+    test_provider_ = std::make_unique<TestMediaRouteProvider>(
+        provider_remote.InitWithNewPipeAndPassReceiver(),
+        std::move(media_router_remote));
+    router->RegisterMediaRouteProvider(MediaRouteProviderId::TEST,
+                                       std::move(provider_remote),
+                                       base::DoNothing());
+
+    test_ui_ =
+        MediaRouterUiForTest::GetOrCreateForWebContents(GetActiveWebContents());
+  }
+
+  std::unique_ptr<TestMediaRouteProvider> test_provider_;
+};
+
+IN_PROC_BROWSER_TEST_F(MediaRouterNativeIntegrationBrowserTest, Basic) {
+  RunBasicTest();
+}
+
+}  // namespace media_router
diff --git a/chromecast/common/extensions_api/_manifest_features.json b/chromecast/common/extensions_api/_manifest_features.json
index 079a4f5..c183c0a 100644
--- a/chromecast/common/extensions_api/_manifest_features.json
+++ b/chromecast/common/extensions_api/_manifest_features.json
@@ -26,8 +26,7 @@
   },
   "commands": {
     "channel": "stable",
-    "extension_types": ["extension", "platform_app"],
-    "min_manifest_version": 2
+    "extension_types": ["extension", "platform_app"]
   },
   "options_page": {
     "channel": "stable",
diff --git a/chromeos/components/account_manager/account_manager.h b/chromeos/components/account_manager/account_manager.h
index ecd3464..36e06dba3 100644
--- a/chromeos/components/account_manager/account_manager.h
+++ b/chromeos/components/account_manager/account_manager.h
@@ -421,4 +421,9 @@
 
 }  // namespace chromeos
 
+// TODO(https://crbug.com/1164001): remove after moved to ash/.
+namespace ash {
+using ::chromeos::AccountManager;
+}
+
 #endif  // CHROMEOS_COMPONENTS_ACCOUNT_MANAGER_ACCOUNT_MANAGER_H_
diff --git a/chromeos/components/account_manager/account_manager_factory.h b/chromeos/components/account_manager/account_manager_factory.h
index 2931137..4e7c17a 100644
--- a/chromeos/components/account_manager/account_manager_factory.h
+++ b/chromeos/components/account_manager/account_manager_factory.h
@@ -64,4 +64,9 @@
 
 }  // namespace chromeos
 
+// TODO(https://crbug.com/1164001): remove after moved to ash/.
+namespace ash {
+using ::chromeos::AccountManagerFactory;
+}
+
 #endif  // CHROMEOS_COMPONENTS_ACCOUNT_MANAGER_ACCOUNT_MANAGER_FACTORY_H_
diff --git a/chromeos/components/account_manager/account_manager_ui.h b/chromeos/components/account_manager/account_manager_ui.h
index 2adb8f3..9b2952a 100644
--- a/chromeos/components/account_manager/account_manager_ui.h
+++ b/chromeos/components/account_manager/account_manager_ui.h
@@ -36,4 +36,9 @@
 
 }  // namespace chromeos
 
+// TODO(https://crbug.com/1164001): remove after moved to ash/
+namespace ash {
+using ::chromeos::AccountManagerUI;
+}
+
 #endif  // CHROMEOS_COMPONENTS_ACCOUNT_MANAGER_ACCOUNT_MANAGER_UI_H_
diff --git a/chromeos/components/proximity_auth/proximity_auth_profile_pref_manager.cc b/chromeos/components/proximity_auth/proximity_auth_profile_pref_manager.cc
index bed798d..d66c56af 100644
--- a/chromeos/components/proximity_auth/proximity_auth_profile_pref_manager.cc
+++ b/chromeos/components/proximity_auth/proximity_auth_profile_pref_manager.cc
@@ -123,6 +123,9 @@
 }
 
 bool ProximityAuthProfilePrefManager::IsEasyUnlockEnabled() const {
+  // Note: if GetFeatureState() is called in the first few hundred milliseconds
+  // of user session startup, it can incorrectly return a feature-default state
+  // of kProhibitedByPolicy. See https://crbug.com/1154766 for more.
   return multidevice_setup_client_->GetFeatureState(
              chromeos::multidevice_setup::mojom::Feature::kSmartLock) ==
          chromeos::multidevice_setup::mojom::FeatureState::kEnabledByUser;
diff --git a/chromeos/components/proximity_auth/smart_lock_metrics_recorder.h b/chromeos/components/proximity_auth/smart_lock_metrics_recorder.h
index 6d0e605..61e8baf 100644
--- a/chromeos/components/proximity_auth/smart_lock_metrics_recorder.h
+++ b/chromeos/components/proximity_auth/smart_lock_metrics_recorder.h
@@ -77,6 +77,7 @@
     kMaxValue = kPrimaryUserAbsent
   };
 
+  // TODO(crbug.com/1171972): Deprecate the AuthMethodChoice metric.
   static void RecordSmartLockUnlockAuthMethodChoice(
       SmartLockAuthMethodChoice auth_method_choice);
   static void RecordSmartLockSignInAuthMethodChoice(
diff --git a/chromeos/constants/chromeos_features.cc b/chromeos/constants/chromeos_features.cc
index 2d86e2a..0d59dc5 100644
--- a/chromeos/constants/chromeos_features.cc
+++ b/chromeos/constants/chromeos_features.cc
@@ -305,6 +305,10 @@
 const base::Feature kExoPointerLock{"ExoPointerLock",
                                     base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Enable or disable bubble showing when an application gains any UI lock.
+const base::Feature kExoLockNotification{"ExoLockNotification",
+                                         base::FEATURE_DISABLED_BY_DEFAULT};
+
 const base::Feature kDisablePeripheralDataAccessProtection{
     "DisablePeripheralDataAccessProtection", base::FEATURE_DISABLED_BY_DEFAULT};
 
diff --git a/chromeos/constants/chromeos_features.h b/chromeos/constants/chromeos_features.h
index 5eb5e43..0d5ab4a 100644
--- a/chromeos/constants/chromeos_features.h
+++ b/chromeos/constants/chromeos_features.h
@@ -148,6 +148,8 @@
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
 extern const base::Feature kExoPointerLock;
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const base::Feature kExoLockNotification;
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
 extern const base::Feature kFamilyLinkOnSchoolDevice;
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
 extern const base::Feature kFilesCameraFolder;
diff --git a/chromeos/constants/chromeos_switches.cc b/chromeos/constants/chromeos_switches.cc
index bb2ff95..3034fce5 100644
--- a/chromeos/constants/chromeos_switches.cc
+++ b/chromeos/constants/chromeos_switches.cc
@@ -66,10 +66,6 @@
 // Signals the availability of the ARC instance on this device.
 const char kArcAvailable[] = "arc-available";
 
-// A JSON dictionary whose content is the same as cros config's
-// /arc/build-properties.
-const char kArcBuildProperties[] = "arc-build-properties";
-
 // Flag that forces ARC data be cleaned on each start.
 const char kArcDataCleanupOnStart[] = "arc-data-cleanup-on-start";
 
diff --git a/chromeos/constants/chromeos_switches.h b/chromeos/constants/chromeos_switches.h
index 4a4f787c..7138a6d 100644
--- a/chromeos/constants/chromeos_switches.h
+++ b/chromeos/constants/chromeos_switches.h
@@ -31,7 +31,6 @@
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kAppOemManifestFile[];
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kArcAvailability[];
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kArcAvailable[];
-COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kArcBuildProperties[];
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kArcDataCleanupOnStart[];
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kArcDisableAppSync[];
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
diff --git a/components/browser_ui/widget/android/BUILD.gn b/components/browser_ui/widget/android/BUILD.gn
index df652e9..d4e68c2 100644
--- a/components/browser_ui/widget/android/BUILD.gn
+++ b/components/browser_ui/widget/android/BUILD.gn
@@ -17,7 +17,6 @@
     "java/src/org/chromium/components/browser_ui/widget/FadingEdgeScrollView.java",
     "java/src/org/chromium/components/browser_ui/widget/FadingShadow.java",
     "java/src/org/chromium/components/browser_ui/widget/FadingShadowView.java",
-    "java/src/org/chromium/components/browser_ui/widget/FeatureHighlightProvider.java",
     "java/src/org/chromium/components/browser_ui/widget/InsetObserverView.java",
     "java/src/org/chromium/components/browser_ui/widget/LoadingView.java",
     "java/src/org/chromium/components/browser_ui/widget/MaterialProgressBar.java",
diff --git a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/FeatureHighlightProvider.java b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/FeatureHighlightProvider.java
deleted file mode 100644
index f4d7672f..0000000
--- a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/FeatureHighlightProvider.java
+++ /dev/null
@@ -1,87 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.components.browser_ui.widget;
-
-import android.view.View;
-
-import androidx.annotation.ColorInt;
-import androidx.annotation.IntDef;
-import androidx.annotation.StringRes;
-import androidx.annotation.StyleRes;
-import androidx.appcompat.app.AppCompatActivity;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/** A means of showing highlight for a particular feature as a form of in product help. */
-public class FeatureHighlightProvider {
-    /**
-     * These values determine text alignment and need to match the values in the closed-source
-     * library.
-     */
-    @IntDef({TextAlignment.START, TextAlignment.CENTER, TextAlignment.END})
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface TextAlignment {
-        int START = 0;
-        int CENTER = 1;
-        int END = 2;
-    }
-
-    /** The value for IPH timeout if the IPH will not timeout. */
-    public static final long NO_TIMEOUT = -1;
-
-    public FeatureHighlightProvider() {}
-
-    /**
-     * Build and show a feature highlight bubble for a particular view.
-     * @param activity An activity to attach the highlight to.
-     * @param view The view to focus.
-     * @param headTextId The text shown in the header section of the bubble.
-     * @param headAlignment Alignment of the head text.
-     * @param headStyle Style of the head text size and color.
-     * @param bodyTextId The text shown in the body section of the bubble.
-     * @param bodyAlignment Alignment of the body text.
-     * @param bodyStyle Style of the body text size and color.
-     * @param pulseColor The inner color of the bubble.
-     * @param outerColor The outer color of the bubble.
-     * @param scrimColor The color of the out side of feature highlight.
-     * @param timeoutMs The amount of time in ms before the bubble disappears.
-     * @param tapToDismiss The feature highlight bubble can be dismissiable.
-     */
-    public void buildForView(AppCompatActivity activity, View view, @StringRes int headTextId,
-            @TextAlignment int headAlignment, @StyleRes int headStyle, @StringRes int bodyTextId,
-            @TextAlignment int bodyAlignment, @StyleRes int bodyStyle, @ColorInt int pulseColor,
-            @ColorInt int outerColor, @ColorInt int scrimColor, long timeoutMs,
-            Boolean tapToDismiss) {}
-
-    /**
-     * Build and show a feature highlight bubble for a particular view.
-     * @param activity An activity to attach the highlight to.
-     * @param view The view to focus.
-     * @param headTextId The text shown in the header section of the bubble.
-     * @param headAlignment Alignment of the head text.
-     * @param headStyle Style of the head text size and color.
-     * @param bodyTextId The text shown in the body section of the bubble.
-     * @param bodyAlignment Alignment of the body text.
-     * @param bodyStyle Style of the body text size and color.
-     * @param pulseColor The inner color of the bubble.
-     * @param outerColor The outer color of the bubble.
-     * @param scrimColor The color of the out side of feature highlight.
-     * @param timeoutMs The amount of time in ms before the bubble disappears.
-     * @param tapToDismiss The feature highlight bubble can be dismissiable.
-     * @param completeRunnable The Runnable to be called if the user tab on the view.
-     */
-    public void buildForView(AppCompatActivity activity, View view, @StringRes int headTextId,
-            @TextAlignment int headAlignment, @StyleRes int headStyle, @StringRes int bodyTextId,
-            @TextAlignment int bodyAlignment, @StyleRes int bodyStyle, @ColorInt int pulseColor,
-            @ColorInt int outerColor, @ColorInt int scrimColor, long timeoutMs,
-            Boolean tapToDismiss, Runnable completeRunnable) {}
-
-    /**
-     * Dismiss the feature highlight bubble for a particular view.
-     * @param activity An activity to attach the IPH to.
-     */
-    public void dismiss(AppCompatActivity activity) {}
-}
diff --git a/components/certificate_transparency/tools/make_ct_known_logs_list.py b/components/certificate_transparency/tools/make_ct_known_logs_list.py
index f1563dbe..861f110 100755
--- a/components/certificate_transparency/tools/make_ct_known_logs_list.py
+++ b/components/certificate_transparency/tools/make_ct_known_logs_list.py
@@ -83,13 +83,12 @@
 
 def _is_log_disqualified(log):
   # Disqualified logs are denoted with state="retired"
-  assert (len(log.get("state").keys()) == 1)
+  assert (len(log.get("state")) == 1)
   log_state = list(log.get("state"))[0]
   return log_state == "retired"
 
 
 def _escape_c_string(s):
-
   def _escape_char(c):
     if 32 <= ord(c) <= 126 and c not in '\\"':
       return c
@@ -144,9 +143,8 @@
 
 
 def _sorted_disqualified_logs(all_logs):
-  return sorted(
-      filter(_is_log_disqualified, all_logs),
-      key=lambda l: base64.b64decode(l["log_id"]))
+  return sorted([l for l in all_logs if _is_log_disqualified(l)],
+                key = lambda l: base64.b64decode(l["log_id"]))
 
 
 def _write_qualifying_logs_loginfo(f, qualifying_logs):
@@ -157,7 +155,7 @@
 
 
 def _is_log_once_or_currently_qualified(log):
-  assert (len(log.get("state").keys()) == 1)
+  assert (len(log.get("state")) == 1)
   return list(log.get("state"))[0] not in ("pending", "rejected")
 
 
@@ -193,7 +191,7 @@
 
 def main():
   if len(sys.argv) != 3:
-    print("usage: %s in_loglist_json out_header" % sys.argv[0])
+    print(("usage: %s in_loglist_json out_header" % sys.argv[0]))
     return 1
   with open(sys.argv[1], "r") as infile, open(sys.argv[2], "w") as outfile:
     generate_cpp_file(infile, outfile)
diff --git a/components/media_router/browser/media_router_metrics.cc b/components/media_router/browser/media_router_metrics.cc
index 7e749aaf..3ff662c 100644
--- a/components/media_router/browser/media_router_metrics.cc
+++ b/components/media_router/browser/media_router_metrics.cc
@@ -38,8 +38,9 @@
       return base_name + ".WiredDisplay";
     case MediaRouteProviderId::ANDROID_CAF:
       return base_name + ".AndroidCaf";
-    // |EXTENSION| and |UNKNOWN| use the base histogram name.
+    // The rest use the base histogram name.
     case MediaRouteProviderId::EXTENSION:
+    case MediaRouteProviderId::TEST:
     case MediaRouteProviderId::UNKNOWN:
       return base_name;
   }
diff --git a/components/media_router/common/media_route_provider_helper.cc b/components/media_router/common/media_route_provider_helper.cc
index a48e9b9..b9d55c9 100644
--- a/components/media_router/common/media_route_provider_helper.cc
+++ b/components/media_router/common/media_route_provider_helper.cc
@@ -12,6 +12,7 @@
 constexpr const char kDial[] = "DIAL";
 constexpr const char kCast[] = "CAST";
 constexpr const char kAndroidCaf[] = "ANDROID_CAF";
+constexpr const char kTest[] = "TEST";
 constexpr const char kUnknown[] = "UNKNOWN";
 
 namespace media_router {
@@ -28,6 +29,8 @@
       return kDial;
     case ANDROID_CAF:
       return kAndroidCaf;
+    case TEST:
+      return kTest;
     case UNKNOWN:
       return kUnknown;
   }
@@ -47,6 +50,8 @@
     return MediaRouteProviderId::DIAL;
   } else if (provider_id == kAndroidCaf) {
     return MediaRouteProviderId::ANDROID_CAF;
+  } else if (provider_id == kTest) {
+    return MediaRouteProviderId::TEST;
   } else {
     return MediaRouteProviderId::UNKNOWN;
   }
diff --git a/components/media_router/common/media_route_provider_helper.h b/components/media_router/common/media_route_provider_helper.h
index c61858b..7e9b667 100644
--- a/components/media_router/common/media_route_provider_helper.h
+++ b/components/media_router/common/media_route_provider_helper.h
@@ -21,6 +21,7 @@
   CAST,
   DIAL,
   ANDROID_CAF,
+  TEST,
   UNKNOWN  // New values must be added above this value.
 };
 
diff --git a/components/media_router/common/mojom/media_router.mojom b/components/media_router/common/mojom/media_router.mojom
index fa553f7b..ff162f39d 100644
--- a/components/media_router/common/mojom/media_router.mojom
+++ b/components/media_router/common/mojom/media_router.mojom
@@ -297,6 +297,7 @@
     CAST,
     DIAL,
     ANDROID_CAF,
+    TEST,
   };
 
   // Creates a media route from |media_source| to the sink given by |sink_id|.
diff --git a/components/media_router/common/mojom/media_router_mojom_traits.h b/components/media_router/common/mojom/media_router_mojom_traits.h
index 38eeb8d..80a77f4d 100644
--- a/components/media_router/common/mojom/media_router_mojom_traits.h
+++ b/components/media_router/common/mojom/media_router_mojom_traits.h
@@ -513,6 +513,8 @@
         return media_router::mojom::MediaRouteProvider_Id::DIAL;
       case media_router::MediaRouteProviderId::ANDROID_CAF:
         return media_router::mojom::MediaRouteProvider_Id::ANDROID_CAF;
+      case media_router::MediaRouteProviderId::TEST:
+        return media_router::mojom::MediaRouteProvider_Id::TEST;
       case media_router::MediaRouteProviderId::UNKNOWN:
         break;
     }
@@ -539,6 +541,9 @@
       case media_router::mojom::MediaRouteProvider_Id::ANDROID_CAF:
         *provider_id = media_router::MediaRouteProviderId::ANDROID_CAF;
         return true;
+      case media_router::mojom::MediaRouteProvider_Id::TEST:
+        *provider_id = media_router::MediaRouteProviderId::TEST;
+        return true;
     }
     return false;
   }
diff --git a/components/metrics/metrics_provider.h b/components/metrics/metrics_provider.h
index 108cd7b8..28bc4fa 100644
--- a/components/metrics/metrics_provider.h
+++ b/components/metrics/metrics_provider.h
@@ -34,6 +34,9 @@
   virtual void AsyncInit(base::OnceClosure done_callback);
 
   // Called when a new MetricsLog is created.
+  // This can be used to log a histogram that will appear in the log. Not safe
+  // for some other uses, like user actions.
+  // TODO(crbug.com/1171830): Improve this.
   virtual void OnDidCreateMetricsLog();
 
   // Called when metrics recording has been enabled.
diff --git a/components/metrics/metrics_service.cc b/components/metrics/metrics_service.cc
index 2855d94..5ba33668 100644
--- a/components/metrics/metrics_service.cc
+++ b/components/metrics/metrics_service.cc
@@ -560,7 +560,11 @@
   // Create the initial log.
   if (!initial_metrics_log_) {
     initial_metrics_log_ = CreateLog(MetricsLog::ONGOING_LOG);
-    delegating_provider_.OnDidCreateMetricsLog();
+    // Note: We explicitly do not call OnDidCreateMetricsLog() here, as this
+    // function would have already been called in Start() and this log will
+    // already contain any histograms logged there. OnDidCreateMetricsLog()
+    // will be called again after the initial log is closed, for the next log.
+    // TODO(crbug.com/1171830): Consider getting rid of |initial_metrics_log_|.
   }
 
   rotation_scheduler_->InitTaskComplete();
@@ -782,6 +786,13 @@
   log_manager_.FinishCurrentLog(log_store());
   log_manager_.ResumePausedLog();
 
+  // We call OnDidCreateMetricsLog() here for the next log. Normally, this is
+  // called when the log is created, but in this special case, the log we paused
+  // was created much earlier - by Start(). The histograms that were recorded
+  // via OnDidCreateMetricsLog() are now in the initial metrics log we just
+  // processed, so we need to record new ones for the next log.
+  delegating_provider_.OnDidCreateMetricsLog();
+
   // Store unsent logs, including the initial log that was just saved, so
   // that they're not lost in case of a crash before upload time.
   log_store()->TrimAndPersistUnsentLogs();
diff --git a/components/metrics/metrics_service.h b/components/metrics/metrics_service.h
index 596c5f1..0dff15d 100644
--- a/components/metrics/metrics_service.h
+++ b/components/metrics/metrics_service.h
@@ -203,7 +203,6 @@
       PrefService* local_state,
       DelegatingProvider* delegating_provider);
 
- private:
   // The MetricsService has a lifecycle that is stored as a state.
   // See metrics_service.cc for description of this lifecycle.
   enum State {
@@ -213,6 +212,9 @@
     SENDING_LOGS,         // Sending logs an creating new ones when we run out.
   };
 
+  State state() const { return state_; }
+
+ private:
   enum ShutdownCleanliness {
     CLEANLY_SHUTDOWN = 0xdeadbeef,
     NEED_TO_SHUTDOWN = ~CLEANLY_SHUTDOWN
diff --git a/components/metrics/metrics_service_unittest.cc b/components/metrics/metrics_service_unittest.cc
index 0adbd9d..b159eb1f 100644
--- a/components/metrics/metrics_service_unittest.cc
+++ b/components/metrics/metrics_service_unittest.cc
@@ -13,6 +13,7 @@
 #include "base/bind.h"
 #include "base/macros.h"
 #include "base/metrics/field_trial.h"
+#include "base/metrics/histogram_functions.h"
 #include "base/metrics/metrics_hashes.h"
 #include "base/metrics/statistics_recorder.h"
 #include "base/metrics/user_metrics.h"
@@ -77,7 +78,10 @@
       : MetricsService(state_manager, client, local_state) {}
   ~TestMetricsService() override = default;
 
+  using MetricsService::INIT_TASK_SCHEDULED;
   using MetricsService::RecordCurrentEnvironmentHelper;
+  using MetricsService::SENDING_LOGS;
+  using MetricsService::state;
 
   // MetricsService:
   void SetPersistentSystemProfile(const std::string& serialized_proto,
@@ -113,6 +117,18 @@
   DISALLOW_COPY_AND_ASSIGN(TestMetricsLog);
 };
 
+const char kOnDidCreateMetricsLogHistogramName[] = "Test.OnDidCreateMetricsLog";
+
+class TestMetricsProviderForOnDidCreateMetricsLog : public TestMetricsProvider {
+ public:
+  TestMetricsProviderForOnDidCreateMetricsLog() = default;
+  ~TestMetricsProviderForOnDidCreateMetricsLog() override = default;
+
+  void OnDidCreateMetricsLog() override {
+    base::UmaHistogramBoolean(kOnDidCreateMetricsLogHistogramName, true);
+  }
+};
+
 class MetricsServiceTest : public testing::Test {
  public:
   MetricsServiceTest()
@@ -174,6 +190,33 @@
     }
   }
 
+  // Returns the number of samples logged to the specified histogram or 0 if
+  // the histogram was not found.
+  int GetHistogramSampleCount(const ChromeUserMetricsExtension& uma_log,
+                              base::StringPiece histogram_name) {
+    const auto histogram_name_hash = HashMetricName(histogram_name);
+    int samples = 0;
+    for (int i = 0; i < uma_log.histogram_event_size(); ++i) {
+      const auto& histogram = uma_log.histogram_event(i);
+      if (histogram.name_hash() == histogram_name_hash) {
+        for (int j = 0; j < histogram.bucket_size(); ++j) {
+          const auto& bucket = histogram.bucket(j);
+          // Per proto comments, count field not being set means 1 sample.
+          samples += (!bucket.has_count() ? 1 : bucket.count());
+        }
+      }
+    }
+    return samples;
+  }
+
+  // Returns the sampled count of the |kOnDidCreateMetricsLogHistogramName|
+  // histogram in the currently staged log in |test_log_store|.
+  int GetSampleCountOfOnDidCreateLogHistogram(MetricsLogStore* test_log_store) {
+    ChromeUserMetricsExtension log;
+    EXPECT_TRUE(DecodeLogDataToProto(test_log_store->staged_log(), &log));
+    return GetHistogramSampleCount(log, kOnDidCreateMetricsLogHistogramName);
+  }
+
  protected:
   scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
   base::ThreadTaskRunnerHandle task_runner_handle_;
@@ -369,6 +412,48 @@
   EXPECT_EQ(1, uma_log.system_profile().stability().crash_count());
 }
 
+TEST_F(MetricsServiceTest, InitialLogsHaveOnDidCreateMetricsLogHistograms) {
+  EnableMetricsReporting();
+  TestMetricsServiceClient client;
+  TestMetricsService service(GetMetricsStateManager(), &client,
+                             GetLocalState());
+
+  // Create a provider that will log to |kOnDidCreateMetricsLogHistogramName|
+  // in OnDidCreateMetricsLog()
+  auto* test_provider = new TestMetricsProviderForOnDidCreateMetricsLog();
+  service.RegisterMetricsProvider(
+      std::unique_ptr<MetricsProvider>(test_provider));
+
+  service.InitializeMetricsRecordingState();
+  // Start() will create an initial log.
+  service.Start();
+  ASSERT_EQ(TestMetricsService::INIT_TASK_SCHEDULED, service.state());
+
+  // Run pending tasks to finish the init task, which will create the
+  // |initial_metrics_log_|.
+  task_runner_->RunPendingTasks();
+  ASSERT_EQ(TestMetricsService::SENDING_LOGS, service.state());
+
+  MetricsLogStore* test_log_store = service.LogStoreForTest();
+
+  // Stage the next log, which should be the |initial_metrics_log_|.
+  // Check that it has one sample in |kOnDidCreateMetricsLogHistogramName|.
+  test_log_store->StageNextLog();
+  EXPECT_EQ(1, GetSampleCountOfOnDidCreateLogHistogram(test_log_store));
+
+  // Discard the staged log and close and stage the next one. This is the
+  // first "ongoing log".
+  // Check that it has one sample in |kOnDidCreateMetricsLogHistogramName|.
+  test_log_store->DiscardStagedLog();
+  service.StageCurrentLogForTest();
+  EXPECT_EQ(1, GetSampleCountOfOnDidCreateLogHistogram(test_log_store));
+
+  // Check one more log for good measure.
+  test_log_store->DiscardStagedLog();
+  service.StageCurrentLogForTest();
+  EXPECT_EQ(1, GetSampleCountOfOnDidCreateLogHistogram(test_log_store));
+}
+
 TEST_F(MetricsServiceTest,
        MetricsProviderOnRecordingDisabledCalledOnInitialStop) {
   TestMetricsServiceClient client;
diff --git a/components/safe_browsing/content/password_protection/BUILD.gn b/components/safe_browsing/content/password_protection/BUILD.gn
index d0baf03..af10f080 100644
--- a/components/safe_browsing/content/password_protection/BUILD.gn
+++ b/components/safe_browsing/content/password_protection/BUILD.gn
@@ -16,6 +16,8 @@
       "password_protection_request_content.h",
       "password_protection_service.cc",
       "password_protection_service.h",
+      "password_protection_service_base.cc",
+      "password_protection_service_base.h",
       "request_canceler_content.cc",
       "request_canceler_content.h",
     ]
diff --git a/components/safe_browsing/content/password_protection/password_protection_request_content.cc b/components/safe_browsing/content/password_protection/password_protection_request_content.cc
index 863f80f2..c789183 100644
--- a/components/safe_browsing/content/password_protection/password_protection_request_content.cc
+++ b/components/safe_browsing/content/password_protection/password_protection_request_content.cc
@@ -105,8 +105,10 @@
 void PasswordProtectionRequestContent::MaybeLogPasswordReuseLookupEvent(
     RequestOutcome outcome,
     const LoginReputationClientResponse* response) {
-  password_protection_service()->MaybeLogPasswordReuseLookupEvent(
-      web_contents_, outcome, password_type(), response);
+  PasswordProtectionService* service =
+      static_cast<PasswordProtectionService*>(password_protection_service());
+  service->MaybeLogPasswordReuseLookupEvent(web_contents_, outcome,
+                                            password_type(), response);
 }
 
 void PasswordProtectionRequestContent::MaybeAddPingToWebUI() {
@@ -124,8 +126,9 @@
 #if BUILDFLAG(SAFE_BROWSING_AVAILABLE)
 void PasswordProtectionRequestContent::GetDomFeatures() {
   content::RenderFrameHost* rfh = web_contents_->GetMainFrame();
-  password_protection_service()->GetPhishingDetector(rfh->GetRemoteInterfaces(),
-                                                     &phishing_detector_);
+  PasswordProtectionService* service =
+      static_cast<PasswordProtectionService*>(password_protection_service());
+  service->GetPhishingDetector(rfh->GetRemoteInterfaces(), &phishing_detector_);
   dom_features_collection_complete_ = false;
   phishing_detector_->StartPhishingDetection(
       main_frame_url(),
@@ -260,8 +263,10 @@
 
 #if defined(OS_ANDROID)
 void PasswordProtectionRequestContent::SetReferringAppInfo() {
+  PasswordProtectionService* service =
+      static_cast<PasswordProtectionService*>(password_protection_service());
   LoginReputationClientRequest::ReferringAppInfo referring_app_info =
-      password_protection_service()->GetReferringAppInfo(web_contents_);
+      service->GetReferringAppInfo(web_contents_);
   UMA_HISTOGRAM_ENUMERATION(
       "PasswordProtection.RequestReferringAppSource",
       referring_app_info.referring_app_source(),
diff --git a/components/safe_browsing/content/password_protection/password_protection_service.cc b/components/safe_browsing/content/password_protection/password_protection_service.cc
index 140053e..d0ca1cb9 100644
--- a/components/safe_browsing/content/password_protection/password_protection_service.cc
+++ b/components/safe_browsing/content/password_protection/password_protection_service.cc
@@ -9,23 +9,13 @@
 #include <memory>
 #include <string>
 
-#include "base/base64.h"
-#include "base/bind.h"
-#include "base/callback.h"
 #include "base/metrics/histogram_macros.h"
-#include "base/stl_util.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_split.h"
-#include "base/strings/string_util.h"
-#include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "components/password_manager/core/browser/password_manager_metrics_util.h"
 #include "components/password_manager/core/browser/password_reuse_detector.h"
 #include "components/safe_browsing/content/password_protection/password_protection_navigation_throttle.h"
-#include "components/safe_browsing/content/password_protection/password_protection_request.h"
 #include "components/safe_browsing/content/password_protection/password_protection_request_content.h"
 #include "components/safe_browsing/core/common/thread_utils.h"
 #include "components/safe_browsing/core/common/utils.h"
-#include "components/safe_browsing/core/db/database_manager.h"
 #include "components/safe_browsing/core/features.h"
 #include "components/zoom/zoom_controller.h"
 #include "content/public/browser/navigation_handle.h"
@@ -36,56 +26,12 @@
 #include "third_party/blink/public/common/page/page_zoom.h"
 
 using content::WebContents;
-using history::HistoryService;
 using password_manager::metrics_util::PasswordType;
 
 namespace safe_browsing {
 
-using PasswordReuseEvent = LoginReputationClientRequest::PasswordReuseEvent;
-
-namespace {
-
-// Keys for storing password protection verdict into a DictionaryValue.
-const int kRequestTimeoutMs = 10000;
-const char kPasswordProtectionRequestUrl[] =
-    "https://sb-ssl.google.com/safebrowsing/clientreport/login";
-
-}  // namespace
-
-PasswordProtectionServiceBase::PasswordProtectionServiceBase(
-    const scoped_refptr<SafeBrowsingDatabaseManager>& database_manager,
-    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-    HistoryService* history_service)
-    : database_manager_(database_manager),
-      url_loader_factory_(url_loader_factory) {
-  DCHECK(CurrentlyOnThread(ThreadID::UI));
-  if (history_service)
-    history_service_observation_.Observe(history_service);
-
-  common_spoofed_domains_ = {"login.live.com", "facebook.com", "box.com",
-                             "google.com",     "paypal.com",   "apple.com",
-                             "yahoo.com",      "adobe.com",    "amazon.com",
-                             "linkedin.com"};
-}
-
-PasswordProtectionServiceBase::~PasswordProtectionServiceBase() {
-  tracker_.TryCancelAll();
-  CancelPendingRequests();
-  history_service_observation_.Reset();
-  weak_factory_.InvalidateWeakPtrs();
-}
-
-// static
-bool PasswordProtectionServiceBase::CanGetReputationOfURL(const GURL& url) {
-  if (!safe_browsing::CanGetReputationOfUrl(url)) {
-    return false;
-  }
-  const std::string hostname = url.HostNoBrackets();
-  return !net::IsHostnameNonUnique(hostname);
-}
-
 #if defined(ON_FOCUS_PING_ENABLED)
-void PasswordProtectionServiceBase::MaybeStartPasswordFieldOnFocusRequest(
+void PasswordProtectionService::MaybeStartPasswordFieldOnFocusRequest(
     WebContents* web_contents,
     const GURL& main_frame_url,
     const GURL& password_form_action,
@@ -112,7 +58,7 @@
 }
 #endif
 
-void PasswordProtectionServiceBase::MaybeStartProtectedPasswordEntryRequest(
+void PasswordProtectionService::MaybeStartProtectedPasswordEntryRequest(
     WebContents* web_contents,
     const GURL& main_frame_url,
     const std::string& username,
@@ -164,37 +110,7 @@
   }
 }
 
-bool PasswordProtectionServiceBase::ShouldShowModalWarning(
-    LoginReputationClientRequest::TriggerType trigger_type,
-    ReusedPasswordAccountType password_type,
-    LoginReputationClientResponse::VerdictType verdict_type) {
-  if (trigger_type != LoginReputationClientRequest::PASSWORD_REUSE_EVENT ||
-      !IsSupportedPasswordTypeForModalWarning(password_type)) {
-    return false;
-  }
-
-  return (verdict_type == LoginReputationClientResponse::PHISHING ||
-          verdict_type == LoginReputationClientResponse::LOW_REPUTATION) &&
-         IsWarningEnabled(password_type);
-}
-
-LoginReputationClientResponse::VerdictType
-PasswordProtectionServiceBase::GetCachedVerdict(
-    const GURL& url,
-    LoginReputationClientRequest::TriggerType trigger_type,
-    ReusedPasswordAccountType password_type,
-    LoginReputationClientResponse* out_response) {
-  return LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED;
-}
-
-void PasswordProtectionServiceBase::CacheVerdict(
-    const GURL& url,
-    LoginReputationClientRequest::TriggerType trigger_type,
-    ReusedPasswordAccountType password_type,
-    const LoginReputationClientResponse& verdict,
-    const base::Time& receive_time) {}
-
-void PasswordProtectionServiceBase::StartRequest(
+void PasswordProtectionService::StartRequest(
     WebContents* web_contents,
     const GURL& main_frame_url,
     const GURL& password_form_action,
@@ -216,324 +132,6 @@
   pending_requests_.insert(std::move(request));
 }
 
-bool PasswordProtectionServiceBase::CanSendPing(
-    LoginReputationClientRequest::TriggerType trigger_type,
-    const GURL& main_frame_url,
-    ReusedPasswordAccountType password_type) {
-  return IsPingingEnabled(trigger_type, password_type) &&
-         !IsURLAllowlistedForPasswordEntry(main_frame_url) &&
-         !IsInExcludedCountry();
-}
-
-void PasswordProtectionServiceBase::RequestFinished(
-    PasswordProtectionRequest* request,
-    RequestOutcome outcome,
-    std::unique_ptr<LoginReputationClientResponse> response) {
-  DCHECK(CurrentlyOnThread(ThreadID::UI));
-  DCHECK(request);
-
-  if (response) {
-    ReusedPasswordAccountType password_type =
-        GetPasswordProtectionReusedPasswordAccountType(request->password_type(),
-                                                       request->username());
-    if (outcome != RequestOutcome::RESPONSE_ALREADY_CACHED) {
-      CacheVerdict(request->main_frame_url(), request->trigger_type(),
-                   password_type, *response, base::Time::Now());
-    }
-    bool enable_warning_for_non_sync_users = base::FeatureList::IsEnabled(
-        safe_browsing::kPasswordProtectionForSignedInUsers);
-    if (!enable_warning_for_non_sync_users &&
-        request->password_type() == PasswordType::OTHER_GAIA_PASSWORD) {
-      return;
-    }
-
-    // If it's password alert mode and a Gsuite/enterprise account, we do not
-    // show a modal warning.
-    if (outcome == RequestOutcome::PASSWORD_ALERT_MODE &&
-        (password_type.account_type() == ReusedPasswordAccountType::GSUITE ||
-         password_type.account_type() ==
-             ReusedPasswordAccountType::NON_GAIA_ENTERPRISE)) {
-      return;
-    }
-
-    if (ShouldShowModalWarning(request->trigger_type(), password_type,
-                               response->verdict_type())) {
-      username_for_last_shown_warning_ = request->username();
-      reused_password_account_type_for_last_shown_warning_ = password_type;
-      saved_passwords_matching_domains_ = request->matching_domains();
-      ShowModalWarning(request, response->verdict_type(),
-                       response->verdict_token(), password_type);
-      request->set_is_modal_warning_showing(true);
-    }
-  }
-
-  MaybeHandleDeferredNavigations(request);
-
-  // If the request is canceled, the PasswordProtectionServiceBase is already
-  // partially destroyed, and we won't be able to log accurate metrics.
-  if (outcome != RequestOutcome::CANCELED) {
-    auto verdict =
-        response ? response->verdict_type()
-                 : LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED;
-
-// Disabled on Android, because enterprise reporting extension is not supported.
-#if !defined(OS_ANDROID)
-    MaybeReportPasswordReuseDetected(
-        request, request->username(), request->password_type(),
-        verdict == LoginReputationClientResponse::PHISHING);
-#endif
-
-    // Persist a bit in CompromisedCredentials table when saved password is
-    // reused on a phishing or low reputation site.
-    auto is_unsafe_url =
-        verdict == LoginReputationClientResponse::PHISHING ||
-        verdict == LoginReputationClientResponse::LOW_REPUTATION;
-    if (is_unsafe_url) {
-      PersistPhishedSavedPasswordCredential(
-          request->matching_reused_credentials());
-    }
-  }
-
-  // Remove request from |pending_requests_| list. If it triggers warning, add
-  // it into the !warning_reqeusts_| list.
-  for (auto it = pending_requests_.begin(); it != pending_requests_.end();
-       it++) {
-    if (it->get() == request) {
-      if (request->is_modal_warning_showing())
-        warning_requests_.insert(std::move(request));
-      pending_requests_.erase(it);
-      break;
-    }
-  }
-}
-
-void PasswordProtectionServiceBase::CancelPendingRequests() {
-  DCHECK(CurrentlyOnThread(ThreadID::UI));
-  for (auto it = pending_requests_.begin(); it != pending_requests_.end();) {
-    PasswordProtectionRequest* request = it->get();
-    // These are the requests for whom we're still waiting for verdicts.
-    // We need to advance the iterator before we cancel because canceling
-    // the request will invalidate it when RequestFinished is called.
-    it++;
-    request->Cancel(false);
-  }
-  DCHECK(pending_requests_.empty());
-}
-
-int PasswordProtectionServiceBase::GetStoredVerdictCount(
-    LoginReputationClientRequest::TriggerType trigger_type) {
-  return -1;
-}
-
-scoped_refptr<SafeBrowsingDatabaseManager>
-PasswordProtectionServiceBase::database_manager() {
-  return database_manager_;
-}
-
-// static
-GURL PasswordProtectionServiceBase::GetPasswordProtectionRequestUrl() {
-  GURL url(kPasswordProtectionRequestUrl);
-  std::string api_key = google_apis::GetAPIKey();
-  DCHECK(!api_key.empty());
-  return url.Resolve("?key=" + net::EscapeQueryParamValue(api_key, true));
-}
-
-// static
-int PasswordProtectionServiceBase::GetRequestTimeoutInMS() {
-  return kRequestTimeoutMs;
-}
-
-void PasswordProtectionServiceBase::FillUserPopulation(
-    LoginReputationClientRequest::TriggerType trigger_type,
-    LoginReputationClientRequest* request_proto) {
-  ChromeUserPopulation* user_population = request_proto->mutable_population();
-  user_population->set_user_population(
-      IsEnhancedProtection()
-          ? ChromeUserPopulation::ENHANCED_PROTECTION
-          : IsExtendedReporting() ? ChromeUserPopulation::EXTENDED_REPORTING
-                                  : ChromeUserPopulation::SAFE_BROWSING);
-  user_population->set_profile_management_status(
-      GetProfileManagementStatus(GetBrowserPolicyConnector()));
-  user_population->set_is_history_sync_enabled(IsHistorySyncEnabled());
-#if BUILDFLAG(FULL_SAFE_BROWSING)
-  user_population->set_is_under_advanced_protection(
-      IsUnderAdvancedProtection());
-#endif
-  user_population->set_is_incognito(IsIncognito());
-  user_population->set_is_mbb_enabled(IsUserMBBOptedIn());
-}
-
-void PasswordProtectionServiceBase::OnURLsDeleted(
-    history::HistoryService* history_service,
-    const history::DeletionInfo& deletion_info) {
-  GetTaskRunner(ThreadID::UI)
-      ->PostTask(
-          FROM_HERE,
-          base::BindRepeating(&PasswordProtectionServiceBase::
-                                  RemoveUnhandledSyncPasswordReuseOnURLsDeleted,
-                              GetWeakPtr(), deletion_info.IsAllHistory(),
-                              deletion_info.deleted_rows()));
-}
-
-void PasswordProtectionServiceBase::HistoryServiceBeingDeleted(
-    history::HistoryService* history_service) {
-  DCHECK(history_service_observation_.IsObservingSource(history_service));
-  history_service_observation_.Reset();
-}
-
-bool PasswordProtectionServiceBase::IsWarningEnabled(
-    ReusedPasswordAccountType password_type) {
-  return GetPasswordProtectionWarningTriggerPref(password_type) ==
-         PHISHING_REUSE;
-}
-
-// static
-ReusedPasswordType
-PasswordProtectionServiceBase::GetPasswordProtectionReusedPasswordType(
-    password_manager::metrics_util::PasswordType password_type) {
-  switch (password_type) {
-    case PasswordType::SAVED_PASSWORD:
-      return PasswordReuseEvent::SAVED_PASSWORD;
-    case PasswordType::PRIMARY_ACCOUNT_PASSWORD:
-      return PasswordReuseEvent::SIGN_IN_PASSWORD;
-    case PasswordType::OTHER_GAIA_PASSWORD:
-      return PasswordReuseEvent::OTHER_GAIA_PASSWORD;
-    case PasswordType::ENTERPRISE_PASSWORD:
-      return PasswordReuseEvent::ENTERPRISE_PASSWORD;
-    case PasswordType::PASSWORD_TYPE_UNKNOWN:
-      return PasswordReuseEvent::REUSED_PASSWORD_TYPE_UNKNOWN;
-    case PasswordType::PASSWORD_TYPE_COUNT:
-      break;
-  }
-  NOTREACHED();
-  return PasswordReuseEvent::REUSED_PASSWORD_TYPE_UNKNOWN;
-}
-
-ReusedPasswordAccountType
-PasswordProtectionServiceBase::GetPasswordProtectionReusedPasswordAccountType(
-    password_manager::metrics_util::PasswordType password_type,
-    const std::string& username) const {
-  ReusedPasswordAccountType reused_password_account_type;
-  switch (password_type) {
-    case PasswordType::SAVED_PASSWORD:
-      reused_password_account_type.set_account_type(
-          ReusedPasswordAccountType::SAVED_PASSWORD);
-      return reused_password_account_type;
-    case PasswordType::ENTERPRISE_PASSWORD:
-      reused_password_account_type.set_account_type(
-          ReusedPasswordAccountType::NON_GAIA_ENTERPRISE);
-      return reused_password_account_type;
-    case PasswordType::PRIMARY_ACCOUNT_PASSWORD: {
-      reused_password_account_type.set_is_account_syncing(
-          IsPrimaryAccountSyncing());
-      if (!IsPrimaryAccountSignedIn()) {
-        reused_password_account_type.set_account_type(
-            ReusedPasswordAccountType::UNKNOWN);
-        return reused_password_account_type;
-      }
-      reused_password_account_type.set_account_type(
-          IsPrimaryAccountGmail() ? ReusedPasswordAccountType::GMAIL
-                                  : ReusedPasswordAccountType::GSUITE);
-      return reused_password_account_type;
-    }
-    case PasswordType::OTHER_GAIA_PASSWORD: {
-      AccountInfo account_info = GetSignedInNonSyncAccount(username);
-      if (account_info.account_id.empty()) {
-        reused_password_account_type.set_account_type(
-            ReusedPasswordAccountType::UNKNOWN);
-        return reused_password_account_type;
-      }
-      reused_password_account_type.set_account_type(
-          IsOtherGaiaAccountGmail(username)
-              ? ReusedPasswordAccountType::GMAIL
-              : ReusedPasswordAccountType::GSUITE);
-      return reused_password_account_type;
-    }
-    case PasswordType::PASSWORD_TYPE_UNKNOWN:
-    case PasswordType::PASSWORD_TYPE_COUNT:
-      reused_password_account_type.set_account_type(
-          ReusedPasswordAccountType::UNKNOWN);
-      return reused_password_account_type;
-  }
-  NOTREACHED();
-  return reused_password_account_type;
-}
-
-// static
-PasswordType
-PasswordProtectionServiceBase::ConvertReusedPasswordAccountTypeToPasswordType(
-    ReusedPasswordAccountType password_type) {
-  if (password_type.is_account_syncing()) {
-    return PasswordType::PRIMARY_ACCOUNT_PASSWORD;
-  } else if (password_type.account_type() ==
-             ReusedPasswordAccountType::NON_GAIA_ENTERPRISE) {
-    return PasswordType::ENTERPRISE_PASSWORD;
-  } else if (password_type.account_type() ==
-             ReusedPasswordAccountType::SAVED_PASSWORD) {
-    return PasswordType::SAVED_PASSWORD;
-  } else if (password_type.account_type() ==
-             ReusedPasswordAccountType::UNKNOWN) {
-    return PasswordType::PASSWORD_TYPE_UNKNOWN;
-  } else {
-    return PasswordType::OTHER_GAIA_PASSWORD;
-  }
-}
-
-bool PasswordProtectionServiceBase::IsSupportedPasswordTypeForPinging(
-    PasswordType password_type) const {
-  switch (password_type) {
-    case PasswordType::SAVED_PASSWORD:
-      return true;
-    case PasswordType::PRIMARY_ACCOUNT_PASSWORD:
-      return true;
-    case PasswordType::ENTERPRISE_PASSWORD:
-      return true;
-    case PasswordType::OTHER_GAIA_PASSWORD:
-      return base::FeatureList::IsEnabled(
-          safe_browsing::kPasswordProtectionForSignedInUsers);
-    case PasswordType::PASSWORD_TYPE_UNKNOWN:
-    case PasswordType::PASSWORD_TYPE_COUNT:
-      return false;
-  }
-  NOTREACHED();
-  return false;
-}
-
-bool PasswordProtectionServiceBase::IsSupportedPasswordTypeForModalWarning(
-    ReusedPasswordAccountType password_type) const {
-  if (password_type.account_type() ==
-          ReusedPasswordAccountType::SAVED_PASSWORD &&
-      base::FeatureList::IsEnabled(
-          safe_browsing::kPasswordProtectionForSavedPasswords))
-    return true;
-
-// Currently password reuse warnings are only supported for saved passwords on
-// Android.
-#if defined(OS_ANDROID)
-  return false;
-#else
-  if (password_type.account_type() ==
-      ReusedPasswordAccountType::NON_GAIA_ENTERPRISE)
-    return true;
-
-  if (password_type.account_type() != ReusedPasswordAccountType::GMAIL &&
-      password_type.account_type() != ReusedPasswordAccountType::GSUITE)
-    return false;
-
-  return password_type.is_account_syncing() ||
-         base::FeatureList::IsEnabled(
-             safe_browsing::kPasswordProtectionForSignedInUsers);
-#endif
-}
-
-#if BUILDFLAG(SAFE_BROWSING_AVAILABLE)
-void PasswordProtectionServiceBase::GetPhishingDetector(
-    service_manager::InterfaceProvider* provider,
-    mojo::Remote<mojom::PhishingDetector>* phishing_detector) {
-  provider->GetInterface(phishing_detector->BindNewPipeAndPassReceiver());
-}
-#endif
-
 std::unique_ptr<PasswordProtectionNavigationThrottle>
 PasswordProtectionService::MaybeCreateNavigationThrottle(
     content::NavigationHandle* navigation_handle) {
@@ -566,6 +164,14 @@
   return nullptr;
 }
 
+#if BUILDFLAG(SAFE_BROWSING_AVAILABLE)
+void PasswordProtectionService::GetPhishingDetector(
+    service_manager::InterfaceProvider* provider,
+    mojo::Remote<mojom::PhishingDetector>* phishing_detector) {
+  provider->GetInterface(phishing_detector->BindNewPipeAndPassReceiver());
+}
+#endif
+
 void PasswordProtectionService::RemoveWarningRequestsByWebContents(
     content::WebContents* web_contents) {
   for (auto it = warning_requests_.begin(); it != warning_requests_.end();) {
diff --git a/components/safe_browsing/content/password_protection/password_protection_service.h b/components/safe_browsing/content/password_protection/password_protection_service.h
index d8db376..d81ed88 100644
--- a/components/safe_browsing/content/password_protection/password_protection_service.h
+++ b/components/safe_browsing/content/password_protection/password_protection_service.h
@@ -5,96 +5,36 @@
 #ifndef COMPONENTS_SAFE_BROWSING_CONTENT_PASSWORD_PROTECTION_PASSWORD_PROTECTION_SERVICE_H_
 #define COMPONENTS_SAFE_BROWSING_CONTENT_PASSWORD_PROTECTION_PASSWORD_PROTECTION_SERVICE_H_
 
-#include <set>
-#include <unordered_map>
-
-#include "base/callback.h"
-#include "base/feature_list.h"
-#include "base/gtest_prod_util.h"
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "base/memory/weak_ptr.h"
-#include "base/scoped_observation.h"
-#include "base/task/cancelable_task_tracker.h"
-#include "base/values.h"
 #include "build/build_config.h"
-#include "components/history/core/browser/history_service.h"
-#include "components/history/core/browser/history_service_observer.h"
 #include "components/password_manager/core/browser/password_manager_metrics_util.h"
 #include "components/password_manager/core/browser/password_reuse_detector.h"
 #include "components/safe_browsing/buildflags.h"
 #include "components/safe_browsing/content/common/safe_browsing.mojom.h"
-#include "components/safe_browsing/core/browser/referrer_chain_provider.h"
-#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
-#include "components/safe_browsing/core/db/v4_protocol_manager_util.h"
+#include "components/safe_browsing/content/password_protection/password_protection_service_base.h"
 #include "components/safe_browsing/core/password_protection/metrics_util.h"
 #include "components/safe_browsing/core/proto/csd.pb.h"
-#include "components/sessions/core/session_id.h"
-#include "components/signin/public/identity_manager/account_info.h"
 #include "mojo/public/cpp/bindings/remote.h"
-#include "services/network/public/cpp/shared_url_loader_factory.h"
-#include "services/service_manager/public/cpp/interface_provider.h"
-#include "third_party/protobuf/src/google/protobuf/repeated_field.h"
 
 namespace content {
-class WebContents;
 class NavigationHandle;
-}
-
-namespace policy {
-class BrowserPolicyConnector;
-}
+class WebContents;
+}  // namespace content
 
 class GURL;
-class HostContentSettingsMap;
 
 namespace safe_browsing {
 
 class PasswordProtectionNavigationThrottle;
 class PasswordProtectionRequest;
-class SafeBrowsingDatabaseManager;
 
 using ReusedPasswordAccountType =
     LoginReputationClientRequest::PasswordReuseEvent::ReusedPasswordAccountType;
-using ReusedPasswordType =
-    LoginReputationClientRequest::PasswordReuseEvent::ReusedPasswordType;
 using password_manager::metrics_util::PasswordType;
 
-// Manage password protection pings and verdicts. There is one instance of this
-// class per profile. Therefore, every PasswordProtectionServiceBase instance is
-// associated with a unique HistoryService instance and a unique
-// HostContentSettingsMap instance.
-class PasswordProtectionServiceBase : public history::HistoryServiceObserver {
+class PasswordProtectionService : public PasswordProtectionServiceBase {
+  using PasswordProtectionServiceBase::PasswordProtectionServiceBase;
+
  public:
-  PasswordProtectionServiceBase(
-      const scoped_refptr<SafeBrowsingDatabaseManager>& database_manager,
-      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-      history::HistoryService* history_service);
-
-  ~PasswordProtectionServiceBase() override;
-
-  base::WeakPtr<PasswordProtectionServiceBase> GetWeakPtr() {
-    return weak_factory_.GetWeakPtr();
-  }
-
-  // Looks up |settings| to find the cached verdict response. If verdict is not
-  // available or is expired, return VERDICT_TYPE_UNSPECIFIED. Can be called on
-  // any thread.
-  virtual LoginReputationClientResponse::VerdictType GetCachedVerdict(
-      const GURL& url,
-      LoginReputationClientRequest::TriggerType trigger_type,
-      ReusedPasswordAccountType password_type,
-      LoginReputationClientResponse* out_response);
-
-  // Stores |verdict| in |settings| based on its |trigger_type|, |url|,
-  // reused |password_type|, |verdict| and |receive_time|.
-  virtual void CacheVerdict(
-      const GURL& url,
-      LoginReputationClientRequest::TriggerType trigger_type,
-      ReusedPasswordAccountType password_type,
-      const LoginReputationClientResponse& verdict,
-      const base::Time& receive_time);
-
   // Creates an instance of PasswordProtectionRequest and call Start() on that
   // instance. This function also insert this request object in |requests_| for
   // record keeping.
@@ -132,356 +72,28 @@
   virtual void MaybeLogPasswordReuseDetectedEvent(
       content::WebContents* web_contents) = 0;
 
-  // If we want to show password reuse modal warning.
-  bool ShouldShowModalWarning(
-      LoginReputationClientRequest::TriggerType trigger_type,
-      ReusedPasswordAccountType password_type,
-      LoginReputationClientResponse::VerdictType verdict_type);
-
-  // Shows modal warning dialog on the current |web_contents| and pass the
-  // |verdict_token| to callback of this dialog.
-  virtual void ShowModalWarning(
-      PasswordProtectionRequest* request,
-      LoginReputationClientResponse::VerdictType verdict_type,
-      const std::string& verdict_token,
-      ReusedPasswordAccountType password_type) = 0;
+  // Records a Chrome Sync event for the result of the URL reputation lookup
+  // if the user enters their sync password on a website.
+  virtual void MaybeLogPasswordReuseLookupEvent(
+      content::WebContents* web_contents,
+      RequestOutcome outcome,
+      PasswordType password_type,
+      const LoginReputationClientResponse* response) = 0;
 
   // Shows chrome://reset-password interstitial.
-  virtual void ShowInterstitial(content::WebContents* web_contens,
+  virtual void ShowInterstitial(content::WebContents* web_contents,
                                 ReusedPasswordAccountType password_type) = 0;
 
-// The following functions are disabled on Android, because enterprise reporting
-// extension is not supported.
-#if !defined(OS_ANDROID)
-  // Triggers the safeBrowsingPrivate.OnPolicySpecifiedPasswordReuseDetected.
-  virtual void MaybeReportPasswordReuseDetected(
-      PasswordProtectionRequest* request,
-      const std::string& username,
-      PasswordType password_type,
-      bool is_phishing_url) = 0;
-
-  // Called when a protected password change is detected. Must be called on
-  // UI thread.
-  virtual void ReportPasswordChanged() = 0;
-#endif
-
   virtual void UpdateSecurityState(safe_browsing::SBThreatType threat_type,
                                    ReusedPasswordAccountType password_type,
                                    content::WebContents* web_contents) = 0;
 
-  scoped_refptr<SafeBrowsingDatabaseManager> database_manager();
-
-  // Safe Browsing backend cannot get a reliable reputation of a URL if
-  // (1) URL is not valid
-  // (2) URL doesn't have http or https scheme
-  // (3) It maps to a local host.
-  // (4) Its hostname is an IP Address in an IANA-reserved range.
-  // (5) Its hostname is a not-yet-assigned by ICANN gTLD.
-  // (6) Its hostname is a dotless domain.
-  static bool CanGetReputationOfURL(const GURL& url);
-
-  // If user has clicked through any Safe Browsing interstitial for |request|'s
-  // web contents.
-  virtual bool UserClickedThroughSBInterstitial(
-      PasswordProtectionRequest* request) = 0;
-
-  // Returns if the warning UI is enabled.
-  bool IsWarningEnabled(ReusedPasswordAccountType password_type);
-
-  // Returns the pref value of password protection warning trigger.
-  virtual PasswordProtectionTrigger GetPasswordProtectionWarningTriggerPref(
-      ReusedPasswordAccountType password_type) const = 0;
-
-  // If |url| matches Safe Browsing allowlist domains, password protection
-  // change password URL, or password protection login URLs in the enterprise
-  // policy.
-  virtual bool IsURLAllowlistedForPasswordEntry(const GURL& url) const = 0;
-
-  // Persist the phished saved password credential in the "compromised
-  // credentials" table. Calls the password store to add a row for each
-  // MatchingReusedCredential where the phished saved password is used on.
-  virtual void PersistPhishedSavedPasswordCredential(
-      const std::vector<password_manager::MatchingReusedCredential>&
-          matching_reused_credentials) = 0;
-
-  // Remove all rows of the phished saved password credential in the
-  // "compromised credentials" table. Calls the password store to remove a row
-  // for each
-  virtual void RemovePhishedSavedPasswordCredential(
-      const std::vector<password_manager::MatchingReusedCredential>&
-          matching_reused_credentials) = 0;
-
 #if defined(OS_ANDROID)
   // Returns the referring app info that starts the activity.
   virtual LoginReputationClientRequest::ReferringAppInfo GetReferringAppInfo(
       content::WebContents* web_contents) = 0;
 #endif
 
-  // Converts from password::metrics_util::PasswordType to
-  // LoginReputationClientRequest::PasswordReuseEvent::ReusedPasswordType.
-  static ReusedPasswordType GetPasswordProtectionReusedPasswordType(
-      PasswordType password_type);
-
-  // Converts from password_manager::metrics_util::PasswordType
-  // to PasswordReuseEvent::ReusedPasswordAccountType. |username| is only
-  // used if |password_type| is OTHER_GAIA_PASSWORD because it needs to be
-  // compared to the list of signed in accounts.
-  ReusedPasswordAccountType GetPasswordProtectionReusedPasswordAccountType(
-      PasswordType password_type,
-      const std::string& username) const;
-
-  // Converts from ReusedPasswordAccountType to
-  // password_manager::metrics_util::PasswordType.
-  static PasswordType ConvertReusedPasswordAccountTypeToPasswordType(
-      ReusedPasswordAccountType password_type);
-
-  // If we can send ping for this type of reused password.
-  bool IsSupportedPasswordTypeForPinging(PasswordType password_type) const;
-
-  // If we can show modal warning for this type of reused password.
-  bool IsSupportedPasswordTypeForModalWarning(
-      ReusedPasswordAccountType password_type) const;
-
-  const ReusedPasswordAccountType&
-  reused_password_account_type_for_last_shown_warning() const {
-    return reused_password_account_type_for_last_shown_warning_;
-  }
-#if defined(UNIT_TEST)
-  void set_reused_password_account_type_for_last_shown_warning(
-      ReusedPasswordAccountType
-          reused_password_account_type_for_last_shown_warning) {
-    reused_password_account_type_for_last_shown_warning_ =
-        reused_password_account_type_for_last_shown_warning;
-  }
-#endif
-
-  const std::string& username_for_last_shown_warning() const {
-    return username_for_last_shown_warning_;
-  }
-#if defined(UNIT_TEST)
-  void set_username_for_last_shown_warning(const std::string& username) {
-    username_for_last_shown_warning_ = username;
-  }
-#endif
-
-  const std::vector<password_manager::MatchingReusedCredential>&
-  saved_passwords_matching_reused_credentials() const {
-    return saved_passwords_matching_reused_credentials_;
-  }
-#if defined(UNIT_TEST)
-  void set_saved_passwords_matching_reused_credentials(
-      const std::vector<password_manager::MatchingReusedCredential>&
-          credentials) {
-    saved_passwords_matching_reused_credentials_ = credentials;
-  }
-#endif
-  const std::vector<std::string>& saved_passwords_matching_domains() const {
-    return saved_passwords_matching_domains_;
-  }
-#if defined(UNIT_TEST)
-  void set_saved_passwords_matching_domains(
-      const std::vector<std::string>& matching_domains) {
-    saved_passwords_matching_domains_ = matching_domains;
-  }
-#endif
-
-  virtual AccountInfo GetAccountInfo() const = 0;
-
-  // Returns the URL where PasswordProtectionRequest instances send requests.
-  static GURL GetPasswordProtectionRequestUrl();
-
- protected:
-  friend class PasswordProtectionRequest;
-  friend class PasswordProtectionRequestContent;
-
-  // Chrome can send password protection ping if it is allowed by for the
-  // |trigger_type| and |password_type| and if Safe Browsing can compute
-  // reputation of |main_frame_url| (e.g. Safe Browsing is not able to compute
-  // reputation of a private IP or a local host).
-  bool CanSendPing(LoginReputationClientRequest::TriggerType trigger_type,
-                   const GURL& main_frame_url,
-                   ReusedPasswordAccountType password_type);
-
-  // Called by a PasswordProtectionRequest instance when it finishes to remove
-  // itself from |requests_|.
-  virtual void RequestFinished(
-      PasswordProtectionRequest* request,
-      RequestOutcome outcome,
-      std::unique_ptr<LoginReputationClientResponse> response);
-
-  // Called by a PasswordProtectionRequest instance to check if a sample ping
-  // can be sent to Safe Browsing.
-  virtual bool CanSendSamplePing() = 0;
-
-  // Sanitize referrer chain by only keeping origin information of all URLs.
-  virtual void SanitizeReferrerChain(ReferrerChain* referrer_chain) = 0;
-
-  // Cancels all requests in |requests_|, empties it, and releases references to
-  // the requests.
-  void CancelPendingRequests();
-
-  // Gets the total number of verdicts of the specified |trigger_type| we cached
-  // for this profile. This counts both expired and active verdicts.
-  virtual int GetStoredVerdictCount(
-      LoginReputationClientRequest::TriggerType trigger_type);
-
-  // Gets an unowned |BrowserPolicyConnector| for the current platform.
-  virtual const policy::BrowserPolicyConnector* GetBrowserPolicyConnector()
-      const = 0;
-
-  scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory() {
-    return url_loader_factory_;
-  }
-
-  // Gets the request timeout in milliseconds.
-  static int GetRequestTimeoutInMS();
-
-  // Obtains referrer chain of |event_url| and |event_tab_id| and adds this
-  // info into |frame|.
-  virtual void FillReferrerChain(
-      const GURL& event_url,
-      SessionID
-          event_tab_id,  // SessionID::InvalidValue() if tab not available.
-      LoginReputationClientRequest::Frame* frame) = 0;
-
-  void FillUserPopulation(
-      LoginReputationClientRequest::TriggerType trigger_type,
-      LoginReputationClientRequest* request_proto);
-
-  virtual bool IsExtendedReporting() = 0;
-
-  virtual bool IsEnhancedProtection() = 0;
-
-  virtual bool IsIncognito() = 0;
-
-  virtual bool IsUserMBBOptedIn() = 0;
-
-  virtual bool IsInPasswordAlertMode(
-      ReusedPasswordAccountType password_type) = 0;
-
-  virtual bool IsPingingEnabled(
-      LoginReputationClientRequest::TriggerType trigger_type,
-      ReusedPasswordAccountType password_type) = 0;
-
-  virtual bool IsHistorySyncEnabled() = 0;
-
-  // If primary account is syncing.
-  virtual bool IsPrimaryAccountSyncing() const = 0;
-
-  // If primary account is signed in.
-  virtual bool IsPrimaryAccountSignedIn() const = 0;
-
-  // If a domain is not defined for the primary account. This means the primary
-  // account is a Gmail account.
-  virtual bool IsPrimaryAccountGmail() const = 0;
-
-  // If the domain for the non sync account is equal to |kNoHostedDomainFound|,
-  // this means that the account is a Gmail account.
-  virtual bool IsOtherGaiaAccountGmail(const std::string& username) const = 0;
-
-  // Gets the account based off of the username from a list of signed in
-  // accounts.
-  virtual AccountInfo GetSignedInNonSyncAccount(
-      const std::string& username) const = 0;
-
-#if BUILDFLAG(FULL_SAFE_BROWSING)
-  virtual bool IsUnderAdvancedProtection() = 0;
-#endif
-
-  // If Safe browsing endpoint is not enabled in the country.
-  virtual bool IsInExcludedCountry() = 0;
-
-  // Records a Chrome Sync event for the result of the URL reputation lookup
-  // if the user enters their sync password on a website.
-  virtual void MaybeLogPasswordReuseLookupEvent(
-      content::WebContents* web_contents,
-      RequestOutcome,
-      PasswordType password_type,
-      const LoginReputationClientResponse*) = 0;
-
-  // Determines if we should show chrome://reset-password interstitial based on
-  // the reused |password_type| and the |main_frame_url|.
-  virtual bool CanShowInterstitial(ReusedPasswordAccountType password_type,
-                                   const GURL& main_frame_url) = 0;
-
-  void CheckCsdAllowlistOnIOThread(const GURL& url, bool* check_result);
-
-  // Gets the type of sync account associated with current profile or
-  // |NOT_SIGNED_IN|.
-  virtual LoginReputationClientRequest::PasswordReuseEvent::SyncAccountType
-  GetSyncAccountType() const = 0;
-
-  // Get information about Delayed Warnings and Omnibox URL display experiments.
-  // This information is sent in PhishGuard pings.
-  virtual LoginReputationClientRequest::UrlDisplayExperiment
-  GetUrlDisplayExperiment() const = 0;
-
-  // Returns the reason why a ping is not sent based on the |trigger_type|,
-  // |url| and |password_type|. Crash if |CanSendPing| is true.
-  virtual RequestOutcome GetPingNotSentReason(
-      LoginReputationClientRequest::TriggerType trigger_type,
-      const GURL& url,
-      ReusedPasswordAccountType password_type) = 0;
-
-  const std::list<std::string>& common_spoofed_domains() const {
-    return common_spoofed_domains_;
-  }
-
-  // Subclasses may override this method to either cancel or resume deferred
-  // navigations. By default, deferred navigations are not handled.
-  virtual void MaybeHandleDeferredNavigations(
-      PasswordProtectionRequest* request) {}
-
-  // Set of pending PasswordProtectionRequests that are still waiting for
-  // verdict.
-  std::set<scoped_refptr<PasswordProtectionRequest>> pending_requests_;
-
-  // Set of PasswordProtectionRequests that are triggering modal warnings.
-  std::set<scoped_refptr<PasswordProtectionRequest>> warning_requests_;
-
- private:
-  friend class PasswordProtectionServiceTest;
-  friend class TestPasswordProtectionService;
-  friend class ChromePasswordProtectionServiceTest;
-  friend class ChromePasswordProtectionServiceBrowserTest;
-  FRIEND_TEST_ALL_PREFIXES(PasswordProtectionServiceTest,
-                           TestParseInvalidVerdictEntry);
-  FRIEND_TEST_ALL_PREFIXES(PasswordProtectionServiceTest,
-                           TestParseValidVerdictEntry);
-  FRIEND_TEST_ALL_PREFIXES(PasswordProtectionServiceTest,
-                           TestPathVariantsMatchCacheExpression);
-  FRIEND_TEST_ALL_PREFIXES(PasswordProtectionServiceTest,
-                           TestRemoveCachedVerdictOnURLsDeleted);
-  FRIEND_TEST_ALL_PREFIXES(PasswordProtectionServiceTest,
-                           TestCleanUpExpiredVerdict);
-
-  // Overridden from history::HistoryServiceObserver.
-  void OnURLsDeleted(history::HistoryService* history_service,
-                     const history::DeletionInfo& deletion_info) override;
-
-  void HistoryServiceBeingDeleted(
-      history::HistoryService* history_service) override;
-
-  // Posted to UI thread by OnURLsDeleted(...). This function remove the related
-  // entries in kSafeBrowsingUnhandledSyncPasswordReuses.
-  virtual void RemoveUnhandledSyncPasswordReuseOnURLsDeleted(
-      bool all_history,
-      const history::URLRows& deleted_rows) = 0;
-
-  static bool PathVariantsMatchCacheExpression(
-      const std::vector<std::string>& generated_paths,
-      const std::string& cache_expression_path);
-
-  void RecordNoPingingReason(
-      LoginReputationClientRequest::TriggerType trigger_type,
-      RequestOutcome reason,
-      PasswordType password_type);
-
-#if BUILDFLAG(FULL_SAFE_BROWSING)
-  // Get the content area size of current browsing window.
-  virtual gfx::Size GetCurrentContentAreaSize() const = 0;
-#endif
-
 #if BUILDFLAG(SAFE_BROWSING_AVAILABLE)
   // Binds the |phishing_detector| to the appropriate interface, as provided by
   // |provider|.
@@ -490,47 +102,6 @@
       mojo::Remote<mojom::PhishingDetector>* phishing_detector);
 #endif
 
-  // The username of the account which password has been reused on. It is only
-  // set once a modal warning or interstitial is verified to be shown.
-  std::string username_for_last_shown_warning_ = "";
-
-  // The last ReusedPasswordAccountType that was shown a warning or
-  // interstitial.
-  ReusedPasswordAccountType
-      reused_password_account_type_for_last_shown_warning_;
-
-  std::vector<password_manager::MatchingReusedCredential>
-      saved_passwords_matching_reused_credentials_;
-
-  std::vector<std::string> saved_passwords_matching_domains_;
-
-  scoped_refptr<SafeBrowsingDatabaseManager> database_manager_;
-
-  // The context we use to issue network requests. This request_context_getter
-  // is obtained from SafeBrowsingService so that we can use the Safe Browsing
-  // cookie store.
-  scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
-
-  // List of most commonly spoofed domains to default to on the password warning
-  // dialog.
-  std::list<std::string> common_spoofed_domains_;
-
-  base::ScopedObservation<history::HistoryService,
-                          history::HistoryServiceObserver>
-      history_service_observation_{this};
-
-  // Weakptr can only cancel task if it is posted to the same thread. Therefore,
-  // we need CancelableTaskTracker to cancel tasks posted to IO thread.
-  base::CancelableTaskTracker tracker_;
-
-  base::WeakPtrFactory<PasswordProtectionServiceBase> weak_factory_{this};
-  DISALLOW_COPY_AND_ASSIGN(PasswordProtectionServiceBase);
-};
-
-class PasswordProtectionService : public PasswordProtectionServiceBase {
-  using PasswordProtectionServiceBase::PasswordProtectionServiceBase;
-
- public:
   // Called when a new navigation is starting. Create throttle if there is a
   // pending sync password reuse ping or if there is a modal warning dialog
   // showing in the corresponding web contents.
diff --git a/components/safe_browsing/content/password_protection/password_protection_service_base.cc b/components/safe_browsing/content/password_protection/password_protection_service_base.cc
new file mode 100644
index 0000000..aacb5e8
--- /dev/null
+++ b/components/safe_browsing/content/password_protection/password_protection_service_base.cc
@@ -0,0 +1,417 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/safe_browsing/content/password_protection/password_protection_service_base.h"
+
+#include <stddef.h>
+
+#include <memory>
+#include <string>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/stl_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
+#include "components/password_manager/core/browser/password_manager_metrics_util.h"
+#include "components/password_manager/core/browser/password_reuse_detector.h"
+#include "components/safe_browsing/content/password_protection/password_protection_request.h"
+#include "components/safe_browsing/core/common/thread_utils.h"
+#include "components/safe_browsing/core/common/utils.h"
+#include "components/safe_browsing/core/db/database_manager.h"
+#include "components/safe_browsing/core/features.h"
+#include "google_apis/google_api_keys.h"
+#include "net/base/escape.h"
+#include "net/base/url_util.h"
+
+using password_manager::metrics_util::PasswordType;
+
+namespace safe_browsing {
+
+using PasswordReuseEvent = LoginReputationClientRequest::PasswordReuseEvent;
+
+namespace {
+
+// Keys for storing password protection verdict into a DictionaryValue.
+const int kRequestTimeoutMs = 10000;
+const char kPasswordProtectionRequestUrl[] =
+    "https://sb-ssl.google.com/safebrowsing/clientreport/login";
+
+}  // namespace
+
+PasswordProtectionServiceBase::PasswordProtectionServiceBase(
+    const scoped_refptr<SafeBrowsingDatabaseManager>& database_manager,
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+    history::HistoryService* history_service)
+    : database_manager_(database_manager),
+      url_loader_factory_(url_loader_factory) {
+  DCHECK(CurrentlyOnThread(ThreadID::UI));
+  if (history_service)
+    history_service_observation_.Observe(history_service);
+
+  common_spoofed_domains_ = {"login.live.com", "facebook.com", "box.com",
+                             "google.com",     "paypal.com",   "apple.com",
+                             "yahoo.com",      "adobe.com",    "amazon.com",
+                             "linkedin.com"};
+}
+
+PasswordProtectionServiceBase::~PasswordProtectionServiceBase() {
+  tracker_.TryCancelAll();
+  CancelPendingRequests();
+  history_service_observation_.Reset();
+  weak_factory_.InvalidateWeakPtrs();
+}
+
+// static
+bool PasswordProtectionServiceBase::CanGetReputationOfURL(const GURL& url) {
+  if (!safe_browsing::CanGetReputationOfUrl(url)) {
+    return false;
+  }
+  const std::string hostname = url.HostNoBrackets();
+  return !net::IsHostnameNonUnique(hostname);
+}
+
+bool PasswordProtectionServiceBase::ShouldShowModalWarning(
+    LoginReputationClientRequest::TriggerType trigger_type,
+    ReusedPasswordAccountType password_type,
+    LoginReputationClientResponse::VerdictType verdict_type) {
+  if (trigger_type != LoginReputationClientRequest::PASSWORD_REUSE_EVENT ||
+      !IsSupportedPasswordTypeForModalWarning(password_type)) {
+    return false;
+  }
+
+  return (verdict_type == LoginReputationClientResponse::PHISHING ||
+          verdict_type == LoginReputationClientResponse::LOW_REPUTATION) &&
+         IsWarningEnabled(password_type);
+}
+
+LoginReputationClientResponse::VerdictType
+PasswordProtectionServiceBase::GetCachedVerdict(
+    const GURL& url,
+    LoginReputationClientRequest::TriggerType trigger_type,
+    ReusedPasswordAccountType password_type,
+    LoginReputationClientResponse* out_response) {
+  return LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED;
+}
+
+void PasswordProtectionServiceBase::CacheVerdict(
+    const GURL& url,
+    LoginReputationClientRequest::TriggerType trigger_type,
+    ReusedPasswordAccountType password_type,
+    const LoginReputationClientResponse& verdict,
+    const base::Time& receive_time) {}
+
+bool PasswordProtectionServiceBase::CanSendPing(
+    LoginReputationClientRequest::TriggerType trigger_type,
+    const GURL& main_frame_url,
+    ReusedPasswordAccountType password_type) {
+  return IsPingingEnabled(trigger_type, password_type) &&
+         !IsURLAllowlistedForPasswordEntry(main_frame_url) &&
+         !IsInExcludedCountry();
+}
+
+void PasswordProtectionServiceBase::RequestFinished(
+    PasswordProtectionRequest* request,
+    RequestOutcome outcome,
+    std::unique_ptr<LoginReputationClientResponse> response) {
+  DCHECK(CurrentlyOnThread(ThreadID::UI));
+  DCHECK(request);
+
+  if (response) {
+    ReusedPasswordAccountType password_type =
+        GetPasswordProtectionReusedPasswordAccountType(request->password_type(),
+                                                       request->username());
+    if (outcome != RequestOutcome::RESPONSE_ALREADY_CACHED) {
+      CacheVerdict(request->main_frame_url(), request->trigger_type(),
+                   password_type, *response, base::Time::Now());
+    }
+    bool enable_warning_for_non_sync_users = base::FeatureList::IsEnabled(
+        safe_browsing::kPasswordProtectionForSignedInUsers);
+    if (!enable_warning_for_non_sync_users &&
+        request->password_type() == PasswordType::OTHER_GAIA_PASSWORD) {
+      return;
+    }
+
+    // If it's password alert mode and a Gsuite/enterprise account, we do not
+    // show a modal warning.
+    if (outcome == RequestOutcome::PASSWORD_ALERT_MODE &&
+        (password_type.account_type() == ReusedPasswordAccountType::GSUITE ||
+         password_type.account_type() ==
+             ReusedPasswordAccountType::NON_GAIA_ENTERPRISE)) {
+      return;
+    }
+
+    if (ShouldShowModalWarning(request->trigger_type(), password_type,
+                               response->verdict_type())) {
+      username_for_last_shown_warning_ = request->username();
+      reused_password_account_type_for_last_shown_warning_ = password_type;
+      saved_passwords_matching_domains_ = request->matching_domains();
+      ShowModalWarning(request, response->verdict_type(),
+                       response->verdict_token(), password_type);
+      request->set_is_modal_warning_showing(true);
+    }
+  }
+
+  MaybeHandleDeferredNavigations(request);
+
+  // If the request is canceled, the PasswordProtectionServiceBase is already
+  // partially destroyed, and we won't be able to log accurate metrics.
+  if (outcome != RequestOutcome::CANCELED) {
+    auto verdict =
+        response ? response->verdict_type()
+                 : LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED;
+
+// Disabled on Android, because enterprise reporting extension is not supported.
+#if !defined(OS_ANDROID)
+    MaybeReportPasswordReuseDetected(
+        request, request->username(), request->password_type(),
+        verdict == LoginReputationClientResponse::PHISHING);
+#endif
+
+    // Persist a bit in CompromisedCredentials table when saved password is
+    // reused on a phishing or low reputation site.
+    auto is_unsafe_url =
+        verdict == LoginReputationClientResponse::PHISHING ||
+        verdict == LoginReputationClientResponse::LOW_REPUTATION;
+    if (is_unsafe_url) {
+      PersistPhishedSavedPasswordCredential(
+          request->matching_reused_credentials());
+    }
+  }
+
+  // Remove request from |pending_requests_| list. If it triggers warning, add
+  // it into the !warning_reqeusts_| list.
+  for (auto it = pending_requests_.begin(); it != pending_requests_.end();
+       it++) {
+    if (it->get() == request) {
+      if (request->is_modal_warning_showing())
+        warning_requests_.insert(std::move(request));
+      pending_requests_.erase(it);
+      break;
+    }
+  }
+}
+
+void PasswordProtectionServiceBase::CancelPendingRequests() {
+  DCHECK(CurrentlyOnThread(ThreadID::UI));
+  for (auto it = pending_requests_.begin(); it != pending_requests_.end();) {
+    PasswordProtectionRequest* request = it->get();
+    // These are the requests for whom we're still waiting for verdicts.
+    // We need to advance the iterator before we cancel because canceling
+    // the request will invalidate it when RequestFinished is called.
+    it++;
+    request->Cancel(false);
+  }
+  DCHECK(pending_requests_.empty());
+}
+
+int PasswordProtectionServiceBase::GetStoredVerdictCount(
+    LoginReputationClientRequest::TriggerType trigger_type) {
+  return -1;
+}
+
+scoped_refptr<SafeBrowsingDatabaseManager>
+PasswordProtectionServiceBase::database_manager() {
+  return database_manager_;
+}
+
+// static
+GURL PasswordProtectionServiceBase::GetPasswordProtectionRequestUrl() {
+  GURL url(kPasswordProtectionRequestUrl);
+  std::string api_key = google_apis::GetAPIKey();
+  DCHECK(!api_key.empty());
+  return url.Resolve("?key=" + net::EscapeQueryParamValue(api_key, true));
+}
+
+// static
+int PasswordProtectionServiceBase::GetRequestTimeoutInMS() {
+  return kRequestTimeoutMs;
+}
+
+void PasswordProtectionServiceBase::FillUserPopulation(
+    LoginReputationClientRequest::TriggerType trigger_type,
+    LoginReputationClientRequest* request_proto) {
+  ChromeUserPopulation* user_population = request_proto->mutable_population();
+  user_population->set_user_population(
+      IsEnhancedProtection()
+          ? ChromeUserPopulation::ENHANCED_PROTECTION
+          : IsExtendedReporting() ? ChromeUserPopulation::EXTENDED_REPORTING
+                                  : ChromeUserPopulation::SAFE_BROWSING);
+  user_population->set_profile_management_status(
+      GetProfileManagementStatus(GetBrowserPolicyConnector()));
+  user_population->set_is_history_sync_enabled(IsHistorySyncEnabled());
+#if BUILDFLAG(FULL_SAFE_BROWSING)
+  user_population->set_is_under_advanced_protection(
+      IsUnderAdvancedProtection());
+#endif
+  user_population->set_is_incognito(IsIncognito());
+  user_population->set_is_mbb_enabled(IsUserMBBOptedIn());
+}
+
+void PasswordProtectionServiceBase::OnURLsDeleted(
+    history::HistoryService* history_service,
+    const history::DeletionInfo& deletion_info) {
+  GetTaskRunner(ThreadID::UI)
+      ->PostTask(
+          FROM_HERE,
+          base::BindRepeating(&PasswordProtectionServiceBase::
+                                  RemoveUnhandledSyncPasswordReuseOnURLsDeleted,
+                              GetWeakPtr(), deletion_info.IsAllHistory(),
+                              deletion_info.deleted_rows()));
+}
+
+void PasswordProtectionServiceBase::HistoryServiceBeingDeleted(
+    history::HistoryService* history_service) {
+  DCHECK(history_service_observation_.IsObservingSource(history_service));
+  history_service_observation_.Reset();
+}
+
+bool PasswordProtectionServiceBase::IsWarningEnabled(
+    ReusedPasswordAccountType password_type) {
+  return GetPasswordProtectionWarningTriggerPref(password_type) ==
+         PHISHING_REUSE;
+}
+
+// static
+ReusedPasswordType
+PasswordProtectionServiceBase::GetPasswordProtectionReusedPasswordType(
+    password_manager::metrics_util::PasswordType password_type) {
+  switch (password_type) {
+    case PasswordType::SAVED_PASSWORD:
+      return PasswordReuseEvent::SAVED_PASSWORD;
+    case PasswordType::PRIMARY_ACCOUNT_PASSWORD:
+      return PasswordReuseEvent::SIGN_IN_PASSWORD;
+    case PasswordType::OTHER_GAIA_PASSWORD:
+      return PasswordReuseEvent::OTHER_GAIA_PASSWORD;
+    case PasswordType::ENTERPRISE_PASSWORD:
+      return PasswordReuseEvent::ENTERPRISE_PASSWORD;
+    case PasswordType::PASSWORD_TYPE_UNKNOWN:
+      return PasswordReuseEvent::REUSED_PASSWORD_TYPE_UNKNOWN;
+    case PasswordType::PASSWORD_TYPE_COUNT:
+      break;
+  }
+  NOTREACHED();
+  return PasswordReuseEvent::REUSED_PASSWORD_TYPE_UNKNOWN;
+}
+
+ReusedPasswordAccountType
+PasswordProtectionServiceBase::GetPasswordProtectionReusedPasswordAccountType(
+    password_manager::metrics_util::PasswordType password_type,
+    const std::string& username) const {
+  ReusedPasswordAccountType reused_password_account_type;
+  switch (password_type) {
+    case PasswordType::SAVED_PASSWORD:
+      reused_password_account_type.set_account_type(
+          ReusedPasswordAccountType::SAVED_PASSWORD);
+      return reused_password_account_type;
+    case PasswordType::ENTERPRISE_PASSWORD:
+      reused_password_account_type.set_account_type(
+          ReusedPasswordAccountType::NON_GAIA_ENTERPRISE);
+      return reused_password_account_type;
+    case PasswordType::PRIMARY_ACCOUNT_PASSWORD: {
+      reused_password_account_type.set_is_account_syncing(
+          IsPrimaryAccountSyncing());
+      if (!IsPrimaryAccountSignedIn()) {
+        reused_password_account_type.set_account_type(
+            ReusedPasswordAccountType::UNKNOWN);
+        return reused_password_account_type;
+      }
+      reused_password_account_type.set_account_type(
+          IsPrimaryAccountGmail() ? ReusedPasswordAccountType::GMAIL
+                                  : ReusedPasswordAccountType::GSUITE);
+      return reused_password_account_type;
+    }
+    case PasswordType::OTHER_GAIA_PASSWORD: {
+      AccountInfo account_info = GetSignedInNonSyncAccount(username);
+      if (account_info.account_id.empty()) {
+        reused_password_account_type.set_account_type(
+            ReusedPasswordAccountType::UNKNOWN);
+        return reused_password_account_type;
+      }
+      reused_password_account_type.set_account_type(
+          IsOtherGaiaAccountGmail(username)
+              ? ReusedPasswordAccountType::GMAIL
+              : ReusedPasswordAccountType::GSUITE);
+      return reused_password_account_type;
+    }
+    case PasswordType::PASSWORD_TYPE_UNKNOWN:
+    case PasswordType::PASSWORD_TYPE_COUNT:
+      reused_password_account_type.set_account_type(
+          ReusedPasswordAccountType::UNKNOWN);
+      return reused_password_account_type;
+  }
+  NOTREACHED();
+  return reused_password_account_type;
+}
+
+// static
+PasswordType
+PasswordProtectionServiceBase::ConvertReusedPasswordAccountTypeToPasswordType(
+    ReusedPasswordAccountType password_type) {
+  if (password_type.is_account_syncing()) {
+    return PasswordType::PRIMARY_ACCOUNT_PASSWORD;
+  } else if (password_type.account_type() ==
+             ReusedPasswordAccountType::NON_GAIA_ENTERPRISE) {
+    return PasswordType::ENTERPRISE_PASSWORD;
+  } else if (password_type.account_type() ==
+             ReusedPasswordAccountType::SAVED_PASSWORD) {
+    return PasswordType::SAVED_PASSWORD;
+  } else if (password_type.account_type() ==
+             ReusedPasswordAccountType::UNKNOWN) {
+    return PasswordType::PASSWORD_TYPE_UNKNOWN;
+  } else {
+    return PasswordType::OTHER_GAIA_PASSWORD;
+  }
+}
+
+bool PasswordProtectionServiceBase::IsSupportedPasswordTypeForPinging(
+    PasswordType password_type) const {
+  switch (password_type) {
+    case PasswordType::SAVED_PASSWORD:
+      return true;
+    case PasswordType::PRIMARY_ACCOUNT_PASSWORD:
+      return true;
+    case PasswordType::ENTERPRISE_PASSWORD:
+      return true;
+    case PasswordType::OTHER_GAIA_PASSWORD:
+      return base::FeatureList::IsEnabled(
+          safe_browsing::kPasswordProtectionForSignedInUsers);
+    case PasswordType::PASSWORD_TYPE_UNKNOWN:
+    case PasswordType::PASSWORD_TYPE_COUNT:
+      return false;
+  }
+  NOTREACHED();
+  return false;
+}
+
+bool PasswordProtectionServiceBase::IsSupportedPasswordTypeForModalWarning(
+    ReusedPasswordAccountType password_type) const {
+  if (password_type.account_type() ==
+          ReusedPasswordAccountType::SAVED_PASSWORD &&
+      base::FeatureList::IsEnabled(
+          safe_browsing::kPasswordProtectionForSavedPasswords))
+    return true;
+
+// Currently password reuse warnings are only supported for saved passwords on
+// Android.
+#if defined(OS_ANDROID)
+  return false;
+#else
+  if (password_type.account_type() ==
+      ReusedPasswordAccountType::NON_GAIA_ENTERPRISE)
+    return true;
+
+  if (password_type.account_type() != ReusedPasswordAccountType::GMAIL &&
+      password_type.account_type() != ReusedPasswordAccountType::GSUITE)
+    return false;
+
+  return password_type.is_account_syncing() ||
+         base::FeatureList::IsEnabled(
+             safe_browsing::kPasswordProtectionForSignedInUsers);
+#endif
+}
+
+}  // namespace safe_browsing
diff --git a/components/safe_browsing/content/password_protection/password_protection_service_base.h b/components/safe_browsing/content/password_protection/password_protection_service_base.h
new file mode 100644
index 0000000..5291971
--- /dev/null
+++ b/components/safe_browsing/content/password_protection/password_protection_service_base.h
@@ -0,0 +1,454 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_SAFE_BROWSING_CONTENT_PASSWORD_PROTECTION_PASSWORD_PROTECTION_SERVICE_BASE_H_
+#define COMPONENTS_SAFE_BROWSING_CONTENT_PASSWORD_PROTECTION_PASSWORD_PROTECTION_SERVICE_BASE_H_
+
+#include <set>
+
+#include "base/feature_list.h"
+#include "base/gtest_prod_util.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/scoped_observation.h"
+#include "base/task/cancelable_task_tracker.h"
+#include "base/values.h"
+#include "build/build_config.h"
+#include "components/history/core/browser/history_service.h"
+#include "components/history/core/browser/history_service_observer.h"
+#include "components/password_manager/core/browser/password_manager_metrics_util.h"
+#include "components/password_manager/core/browser/password_reuse_detector.h"
+#include "components/safe_browsing/buildflags.h"
+#include "components/safe_browsing/core/browser/referrer_chain_provider.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/core/db/v4_protocol_manager_util.h"
+#include "components/safe_browsing/core/password_protection/metrics_util.h"
+#include "components/safe_browsing/core/proto/csd.pb.h"
+#include "components/sessions/core/session_id.h"
+#include "components/signin/public/identity_manager/account_info.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "services/service_manager/public/cpp/interface_provider.h"
+#include "third_party/protobuf/src/google/protobuf/repeated_field.h"
+
+namespace policy {
+class BrowserPolicyConnector;
+}
+
+class GURL;
+
+namespace safe_browsing {
+
+class PasswordProtectionRequest;
+class SafeBrowsingDatabaseManager;
+
+using ReusedPasswordAccountType =
+    LoginReputationClientRequest::PasswordReuseEvent::ReusedPasswordAccountType;
+using ReusedPasswordType =
+    LoginReputationClientRequest::PasswordReuseEvent::ReusedPasswordType;
+using password_manager::metrics_util::PasswordType;
+
+// Manage password protection pings and verdicts. There is one instance of this
+// class per profile. Therefore, every PasswordProtectionServiceBase instance is
+// associated with a unique HistoryService instance and a unique
+// HostContentSettingsMap instance.
+class PasswordProtectionServiceBase : public history::HistoryServiceObserver {
+ public:
+  PasswordProtectionServiceBase(
+      const scoped_refptr<SafeBrowsingDatabaseManager>& database_manager,
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+      history::HistoryService* history_service);
+
+  ~PasswordProtectionServiceBase() override;
+
+  base::WeakPtr<PasswordProtectionServiceBase> GetWeakPtr() {
+    return weak_factory_.GetWeakPtr();
+  }
+
+  // Looks up |settings| to find the cached verdict response. If verdict is not
+  // available or is expired, return VERDICT_TYPE_UNSPECIFIED. Can be called on
+  // any thread.
+  virtual LoginReputationClientResponse::VerdictType GetCachedVerdict(
+      const GURL& url,
+      LoginReputationClientRequest::TriggerType trigger_type,
+      ReusedPasswordAccountType password_type,
+      LoginReputationClientResponse* out_response);
+
+  // Stores |verdict| in |settings| based on its |trigger_type|, |url|,
+  // reused |password_type|, |verdict| and |receive_time|.
+  virtual void CacheVerdict(
+      const GURL& url,
+      LoginReputationClientRequest::TriggerType trigger_type,
+      ReusedPasswordAccountType password_type,
+      const LoginReputationClientResponse& verdict,
+      const base::Time& receive_time);
+
+  // If we want to show password reuse modal warning.
+  bool ShouldShowModalWarning(
+      LoginReputationClientRequest::TriggerType trigger_type,
+      ReusedPasswordAccountType password_type,
+      LoginReputationClientResponse::VerdictType verdict_type);
+
+  // Shows modal warning dialog on the current |web_contents| and pass the
+  // |verdict_token| to callback of this dialog.
+  virtual void ShowModalWarning(
+      PasswordProtectionRequest* request,
+      LoginReputationClientResponse::VerdictType verdict_type,
+      const std::string& verdict_token,
+      ReusedPasswordAccountType password_type) = 0;
+
+// The following functions are disabled on Android, because enterprise reporting
+// extension is not supported.
+#if !defined(OS_ANDROID)
+  // Triggers the safeBrowsingPrivate.OnPolicySpecifiedPasswordReuseDetected.
+  virtual void MaybeReportPasswordReuseDetected(
+      PasswordProtectionRequest* request,
+      const std::string& username,
+      PasswordType password_type,
+      bool is_phishing_url) = 0;
+
+  // Called when a protected password change is detected. Must be called on
+  // UI thread.
+  virtual void ReportPasswordChanged() = 0;
+#endif
+
+  scoped_refptr<SafeBrowsingDatabaseManager> database_manager();
+
+  // Safe Browsing backend cannot get a reliable reputation of a URL if
+  // (1) URL is not valid
+  // (2) URL doesn't have http or https scheme
+  // (3) It maps to a local host.
+  // (4) Its hostname is an IP Address in an IANA-reserved range.
+  // (5) Its hostname is a not-yet-assigned by ICANN gTLD.
+  // (6) Its hostname is a dotless domain.
+  static bool CanGetReputationOfURL(const GURL& url);
+
+  // If user has clicked through any Safe Browsing interstitial for |request|'s
+  // web contents.
+  virtual bool UserClickedThroughSBInterstitial(
+      PasswordProtectionRequest* request) = 0;
+
+  // Returns if the warning UI is enabled.
+  bool IsWarningEnabled(ReusedPasswordAccountType password_type);
+
+  // Returns the pref value of password protection warning trigger.
+  virtual PasswordProtectionTrigger GetPasswordProtectionWarningTriggerPref(
+      ReusedPasswordAccountType password_type) const = 0;
+
+  // If |url| matches Safe Browsing allowlist domains, password protection
+  // change password URL, or password protection login URLs in the enterprise
+  // policy.
+  virtual bool IsURLAllowlistedForPasswordEntry(const GURL& url) const = 0;
+
+  // Persist the phished saved password credential in the "compromised
+  // credentials" table. Calls the password store to add a row for each
+  // MatchingReusedCredential where the phished saved password is used on.
+  virtual void PersistPhishedSavedPasswordCredential(
+      const std::vector<password_manager::MatchingReusedCredential>&
+          matching_reused_credentials) = 0;
+
+  // Remove all rows of the phished saved password credential in the
+  // "compromised credentials" table. Calls the password store to remove a row
+  // for each
+  virtual void RemovePhishedSavedPasswordCredential(
+      const std::vector<password_manager::MatchingReusedCredential>&
+          matching_reused_credentials) = 0;
+
+  // Converts from password::metrics_util::PasswordType to
+  // LoginReputationClientRequest::PasswordReuseEvent::ReusedPasswordType.
+  static ReusedPasswordType GetPasswordProtectionReusedPasswordType(
+      PasswordType password_type);
+
+  // Converts from password_manager::metrics_util::PasswordType
+  // to PasswordReuseEvent::ReusedPasswordAccountType. |username| is only
+  // used if |password_type| is OTHER_GAIA_PASSWORD because it needs to be
+  // compared to the list of signed in accounts.
+  ReusedPasswordAccountType GetPasswordProtectionReusedPasswordAccountType(
+      PasswordType password_type,
+      const std::string& username) const;
+
+  // Converts from ReusedPasswordAccountType to
+  // password_manager::metrics_util::PasswordType.
+  static PasswordType ConvertReusedPasswordAccountTypeToPasswordType(
+      ReusedPasswordAccountType password_type);
+
+  // If we can send ping for this type of reused password.
+  bool IsSupportedPasswordTypeForPinging(PasswordType password_type) const;
+
+  // If we can show modal warning for this type of reused password.
+  bool IsSupportedPasswordTypeForModalWarning(
+      ReusedPasswordAccountType password_type) const;
+
+  const ReusedPasswordAccountType&
+  reused_password_account_type_for_last_shown_warning() const {
+    return reused_password_account_type_for_last_shown_warning_;
+  }
+#if defined(UNIT_TEST)
+  void set_reused_password_account_type_for_last_shown_warning(
+      ReusedPasswordAccountType
+          reused_password_account_type_for_last_shown_warning) {
+    reused_password_account_type_for_last_shown_warning_ =
+        reused_password_account_type_for_last_shown_warning;
+  }
+#endif
+
+  const std::string& username_for_last_shown_warning() const {
+    return username_for_last_shown_warning_;
+  }
+#if defined(UNIT_TEST)
+  void set_username_for_last_shown_warning(const std::string& username) {
+    username_for_last_shown_warning_ = username;
+  }
+#endif
+
+  const std::vector<password_manager::MatchingReusedCredential>&
+  saved_passwords_matching_reused_credentials() const {
+    return saved_passwords_matching_reused_credentials_;
+  }
+#if defined(UNIT_TEST)
+  void set_saved_passwords_matching_reused_credentials(
+      const std::vector<password_manager::MatchingReusedCredential>&
+          credentials) {
+    saved_passwords_matching_reused_credentials_ = credentials;
+  }
+#endif
+  const std::vector<std::string>& saved_passwords_matching_domains() const {
+    return saved_passwords_matching_domains_;
+  }
+#if defined(UNIT_TEST)
+  void set_saved_passwords_matching_domains(
+      const std::vector<std::string>& matching_domains) {
+    saved_passwords_matching_domains_ = matching_domains;
+  }
+#endif
+
+  virtual AccountInfo GetAccountInfo() const = 0;
+
+  // Returns the URL where PasswordProtectionRequest instances send requests.
+  static GURL GetPasswordProtectionRequestUrl();
+
+ protected:
+  friend class PasswordProtectionRequest;
+  friend class PasswordProtectionRequestContent;
+
+  // Chrome can send password protection ping if it is allowed by for the
+  // |trigger_type| and |password_type| and if Safe Browsing can compute
+  // reputation of |main_frame_url| (e.g. Safe Browsing is not able to compute
+  // reputation of a private IP or a local host).
+  bool CanSendPing(LoginReputationClientRequest::TriggerType trigger_type,
+                   const GURL& main_frame_url,
+                   ReusedPasswordAccountType password_type);
+
+  // Called by a PasswordProtectionRequest instance when it finishes to remove
+  // itself from |requests_|.
+  virtual void RequestFinished(
+      PasswordProtectionRequest* request,
+      RequestOutcome outcome,
+      std::unique_ptr<LoginReputationClientResponse> response);
+
+  // Called by a PasswordProtectionRequest instance to check if a sample ping
+  // can be sent to Safe Browsing.
+  virtual bool CanSendSamplePing() = 0;
+
+  // Sanitize referrer chain by only keeping origin information of all URLs.
+  virtual void SanitizeReferrerChain(ReferrerChain* referrer_chain) = 0;
+
+  // Cancels all requests in |requests_|, empties it, and releases references to
+  // the requests.
+  void CancelPendingRequests();
+
+  // Gets the total number of verdicts of the specified |trigger_type| we cached
+  // for this profile. This counts both expired and active verdicts.
+  virtual int GetStoredVerdictCount(
+      LoginReputationClientRequest::TriggerType trigger_type);
+
+  // Gets an unowned |BrowserPolicyConnector| for the current platform.
+  virtual const policy::BrowserPolicyConnector* GetBrowserPolicyConnector()
+      const = 0;
+
+  scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory() {
+    return url_loader_factory_;
+  }
+
+  // Gets the request timeout in milliseconds.
+  static int GetRequestTimeoutInMS();
+
+  // Obtains referrer chain of |event_url| and |event_tab_id| and adds this
+  // info into |frame|.
+  virtual void FillReferrerChain(
+      const GURL& event_url,
+      SessionID
+          event_tab_id,  // SessionID::InvalidValue() if tab not available.
+      LoginReputationClientRequest::Frame* frame) = 0;
+
+  void FillUserPopulation(
+      LoginReputationClientRequest::TriggerType trigger_type,
+      LoginReputationClientRequest* request_proto);
+
+  virtual bool IsExtendedReporting() = 0;
+
+  virtual bool IsEnhancedProtection() = 0;
+
+  virtual bool IsIncognito() = 0;
+
+  virtual bool IsUserMBBOptedIn() = 0;
+
+  virtual bool IsInPasswordAlertMode(
+      ReusedPasswordAccountType password_type) = 0;
+
+  virtual bool IsPingingEnabled(
+      LoginReputationClientRequest::TriggerType trigger_type,
+      ReusedPasswordAccountType password_type) = 0;
+
+  virtual bool IsHistorySyncEnabled() = 0;
+
+  // If primary account is syncing.
+  virtual bool IsPrimaryAccountSyncing() const = 0;
+
+  // If primary account is signed in.
+  virtual bool IsPrimaryAccountSignedIn() const = 0;
+
+  // If a domain is not defined for the primary account. This means the primary
+  // account is a Gmail account.
+  virtual bool IsPrimaryAccountGmail() const = 0;
+
+  // If the domain for the non sync account is equal to |kNoHostedDomainFound|,
+  // this means that the account is a Gmail account.
+  virtual bool IsOtherGaiaAccountGmail(const std::string& username) const = 0;
+
+  // Gets the account based off of the username from a list of signed in
+  // accounts.
+  virtual AccountInfo GetSignedInNonSyncAccount(
+      const std::string& username) const = 0;
+
+#if BUILDFLAG(FULL_SAFE_BROWSING)
+  virtual bool IsUnderAdvancedProtection() = 0;
+#endif
+
+  // If Safe browsing endpoint is not enabled in the country.
+  virtual bool IsInExcludedCountry() = 0;
+
+  // Determines if we should show chrome://reset-password interstitial based on
+  // the reused |password_type| and the |main_frame_url|.
+  virtual bool CanShowInterstitial(ReusedPasswordAccountType password_type,
+                                   const GURL& main_frame_url) = 0;
+
+  void CheckCsdAllowlistOnIOThread(const GURL& url, bool* check_result);
+
+  // Gets the type of sync account associated with current profile or
+  // |NOT_SIGNED_IN|.
+  virtual LoginReputationClientRequest::PasswordReuseEvent::SyncAccountType
+  GetSyncAccountType() const = 0;
+
+  // Get information about Delayed Warnings and Omnibox URL display experiments.
+  // This information is sent in PhishGuard pings.
+  virtual LoginReputationClientRequest::UrlDisplayExperiment
+  GetUrlDisplayExperiment() const = 0;
+
+  // Returns the reason why a ping is not sent based on the |trigger_type|,
+  // |url| and |password_type|. Crash if |CanSendPing| is true.
+  virtual RequestOutcome GetPingNotSentReason(
+      LoginReputationClientRequest::TriggerType trigger_type,
+      const GURL& url,
+      ReusedPasswordAccountType password_type) = 0;
+
+  const std::list<std::string>& common_spoofed_domains() const {
+    return common_spoofed_domains_;
+  }
+
+  // Subclasses may override this method to either cancel or resume deferred
+  // navigations. By default, deferred navigations are not handled.
+  virtual void MaybeHandleDeferredNavigations(
+      PasswordProtectionRequest* request) {}
+
+  // Set of pending PasswordProtectionRequests that are still waiting for
+  // verdict.
+  std::set<scoped_refptr<PasswordProtectionRequest>> pending_requests_;
+
+  // Set of PasswordProtectionRequests that are triggering modal warnings.
+  std::set<scoped_refptr<PasswordProtectionRequest>> warning_requests_;
+
+  // The username of the account which password has been reused on. It is only
+  // set once a modal warning or interstitial is verified to be shown.
+  std::string username_for_last_shown_warning_ = "";
+
+  // The last ReusedPasswordAccountType that was shown a warning or
+  // interstitial.
+  ReusedPasswordAccountType
+      reused_password_account_type_for_last_shown_warning_;
+
+  std::vector<password_manager::MatchingReusedCredential>
+      saved_passwords_matching_reused_credentials_;
+
+ private:
+  friend class PasswordProtectionServiceTest;
+  friend class TestPasswordProtectionService;
+  friend class ChromePasswordProtectionServiceTest;
+  friend class ChromePasswordProtectionServiceBrowserTest;
+  FRIEND_TEST_ALL_PREFIXES(PasswordProtectionServiceTest,
+                           TestParseInvalidVerdictEntry);
+  FRIEND_TEST_ALL_PREFIXES(PasswordProtectionServiceTest,
+                           TestParseValidVerdictEntry);
+  FRIEND_TEST_ALL_PREFIXES(PasswordProtectionServiceTest,
+                           TestPathVariantsMatchCacheExpression);
+  FRIEND_TEST_ALL_PREFIXES(PasswordProtectionServiceTest,
+                           TestRemoveCachedVerdictOnURLsDeleted);
+  FRIEND_TEST_ALL_PREFIXES(PasswordProtectionServiceTest,
+                           TestCleanUpExpiredVerdict);
+
+  // Overridden from history::HistoryServiceObserver.
+  void OnURLsDeleted(history::HistoryService* history_service,
+                     const history::DeletionInfo& deletion_info) override;
+
+  void HistoryServiceBeingDeleted(
+      history::HistoryService* history_service) override;
+
+  // Posted to UI thread by OnURLsDeleted(...). This function remove the related
+  // entries in kSafeBrowsingUnhandledSyncPasswordReuses.
+  virtual void RemoveUnhandledSyncPasswordReuseOnURLsDeleted(
+      bool all_history,
+      const history::URLRows& deleted_rows) = 0;
+
+  static bool PathVariantsMatchCacheExpression(
+      const std::vector<std::string>& generated_paths,
+      const std::string& cache_expression_path);
+
+  void RecordNoPingingReason(
+      LoginReputationClientRequest::TriggerType trigger_type,
+      RequestOutcome reason,
+      PasswordType password_type);
+
+#if BUILDFLAG(FULL_SAFE_BROWSING)
+  // Get the content area size of current browsing window.
+  virtual gfx::Size GetCurrentContentAreaSize() const = 0;
+#endif
+
+  std::vector<std::string> saved_passwords_matching_domains_;
+
+  scoped_refptr<SafeBrowsingDatabaseManager> database_manager_;
+
+  // The context we use to issue network requests. This request_context_getter
+  // is obtained from SafeBrowsingService so that we can use the Safe Browsing
+  // cookie store.
+  scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
+
+  // List of most commonly spoofed domains to default to on the password warning
+  // dialog.
+  std::list<std::string> common_spoofed_domains_;
+
+  base::ScopedObservation<history::HistoryService,
+                          history::HistoryServiceObserver>
+      history_service_observation_{this};
+
+  // Weakptr can only cancel task if it is posted to the same thread. Therefore,
+  // we need CancelableTaskTracker to cancel tasks posted to IO thread.
+  base::CancelableTaskTracker tracker_;
+
+  base::WeakPtrFactory<PasswordProtectionServiceBase> weak_factory_{this};
+  DISALLOW_COPY_AND_ASSIGN(PasswordProtectionServiceBase);
+};
+
+}  // namespace safe_browsing
+
+#endif  // COMPONENTS_SAFE_BROWSING_CONTENT_PASSWORD_PROTECTION_PASSWORD_PROTECTION_SERVICE_BASE_H_
diff --git a/components/viz/common/features.cc b/components/viz/common/features.cc
index 5746b04..a0f471e 100644
--- a/components/viz/common/features.cc
+++ b/components/viz/common/features.cc
@@ -32,8 +32,9 @@
 // Use the SkiaRenderer.
 const base::Feature kUseSkiaRenderer {
   "UseSkiaRenderer",
-#if defined(OS_WIN) || (defined(OS_LINUX) && !(BUILDFLAG(IS_CHROMEOS_ASH) || \
-                                               BUILDFLAG(IS_CHROMECAST)))
+#if defined(OS_WIN) || defined(OS_ANDROID) || \
+    (defined(OS_LINUX) &&                     \
+     !(BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMECAST)))
       base::FEATURE_ENABLED_BY_DEFAULT
 #else
       base::FEATURE_DISABLED_BY_DEFAULT
diff --git a/components/viz/service/BUILD.gn b/components/viz/service/BUILD.gn
index 0f6a5a2..0e237c0 100644
--- a/components/viz/service/BUILD.gn
+++ b/components/viz/service/BUILD.gn
@@ -28,6 +28,8 @@
     "display/delegated_ink_point_renderer_base.h",
     "display/delegated_ink_point_renderer_skia.cc",
     "display/delegated_ink_point_renderer_skia.h",
+    "display/delegated_ink_trail_data.cc",
+    "display/delegated_ink_trail_data.h",
     "display/direct_renderer.cc",
     "display/direct_renderer.h",
     "display/display.cc",
diff --git a/components/viz/service/display/delegated_ink_point_pixel_test_helper.cc b/components/viz/service/display/delegated_ink_point_pixel_test_helper.cc
index ad8fe95..30c6f30 100644
--- a/components/viz/service/display/delegated_ink_point_pixel_test_helper.cc
+++ b/components/viz/service/display/delegated_ink_point_pixel_test_helper.cc
@@ -36,52 +36,76 @@
     const gfx::PointF& point,
     float diameter,
     SkColor color,
+    base::TimeTicks timestamp,
     const gfx::RectF& presentation_area) {
   DCHECK(renderer_);
-  metadata_ = DelegatedInkMetadata(point, diameter, color,
-                                   base::TimeTicks::Now(), presentation_area,
-                                   base::TimeTicks::Now(), /*hovering*/ false);
+  metadata_ =
+      DelegatedInkMetadata(point, diameter, color, timestamp, presentation_area,
+                           base::TimeTicks::Now(), /*hovering*/ false);
   GetInkRenderer()->SetDelegatedInkMetadata(
       std::make_unique<DelegatedInkMetadata>(metadata_));
 }
 
 void DelegatedInkPointPixelTestHelper::CreateAndSendMetadataFromLastPoint() {
-  DCHECK(renderer_);
-  metadata_ = DelegatedInkMetadata(
-      ink_points_.back().point(), metadata_.diameter(), metadata_.color(),
-      ink_points_.back().timestamp(), metadata_.presentation_area(),
-      metadata_.frame_time(), metadata_.is_hovering());
-  GetInkRenderer()->SetDelegatedInkMetadata(
-      std::make_unique<DelegatedInkMetadata>(metadata_));
+  DCHECK_EQ(static_cast<int>(ink_points_.size()), 1);
+  CreateAndSendMetadataFromLastPoint(ink_points_.begin()->first);
+}
+
+void DelegatedInkPointPixelTestHelper::CreateAndSendMetadataFromLastPoint(
+    int32_t pointer_id) {
+  DCHECK(ink_points_.find(pointer_id) != ink_points_.end());
+  CreateAndSendMetadata(ink_points_[pointer_id].back().point(),
+                        metadata_.diameter(), metadata_.color(),
+                        ink_points_[pointer_id].back().timestamp(),
+                        metadata_.presentation_area());
 }
 
 void DelegatedInkPointPixelTestHelper::CreateAndSendPoint(
     const gfx::PointF& point,
     base::TimeTicks timestamp) {
-  DCHECK(renderer_);
-  constexpr int32_t kPointerId = 1;
-  ink_points_.emplace_back(point, timestamp, kPointerId);
-  GetInkRenderer()->StoreDelegatedInkPoint(ink_points_.back());
+  CreateAndSendPoint(point, timestamp, /*pointer_id*/ 1);
 }
 
-void DelegatedInkPointPixelTestHelper::CreateAndSendPointFromMetadata() {
-  CreateAndSendPoint(metadata().point(), metadata().timestamp());
+void DelegatedInkPointPixelTestHelper::CreateAndSendPoint(
+    const gfx::PointF& point,
+    base::TimeTicks timestamp,
+    int32_t pointer_id) {
+  DCHECK(renderer_);
+  ink_points_[pointer_id].emplace_back(point, timestamp, pointer_id);
+  GetInkRenderer()->StoreDelegatedInkPoint(ink_points_[pointer_id].back());
 }
 
 void DelegatedInkPointPixelTestHelper::CreateAndSendPointFromLastPoint(
     const gfx::PointF& point) {
-  EXPECT_GT(static_cast<int>(ink_points_.size()), 0);
-  CreateAndSendPoint(point, ink_points_.back().timestamp() +
-                                base::TimeDelta::FromMicroseconds(10));
+  DCHECK_EQ(static_cast<int>(ink_points_.size()), 1);
+  CreateAndSendPointFromLastPoint(ink_points_.begin()->first, point);
+}
+
+void DelegatedInkPointPixelTestHelper::CreateAndSendPointFromLastPoint(
+    int32_t pointer_id,
+    const gfx::PointF& point) {
+  DCHECK(ink_points_.find(pointer_id) != ink_points_.end());
+  EXPECT_GT(static_cast<int>(ink_points_[pointer_id].size()), 0);
+  CreateAndSendPoint(point,
+                     ink_points_[pointer_id].back().timestamp() +
+                         base::TimeDelta::FromMicroseconds(10),
+                     pointer_id);
 }
 
 gfx::Rect DelegatedInkPointPixelTestHelper::GetDelegatedInkDamageRect() {
-  EXPECT_GT(static_cast<int>(ink_points_.size()), 0);
+  DCHECK_EQ(static_cast<int>(ink_points_.size()), 1);
+  return GetDelegatedInkDamageRect(ink_points_.begin()->first);
+}
+
+gfx::Rect DelegatedInkPointPixelTestHelper::GetDelegatedInkDamageRect(
+    int32_t pointer_id) {
+  DCHECK(ink_points_.find(pointer_id) != ink_points_.end());
+  EXPECT_GT(static_cast<int>(ink_points_[pointer_id].size()), 0);
   gfx::RectF ink_damage_rect_f =
-      gfx::RectF(ink_points_[0].point(), gfx::SizeF(1, 1));
-  for (uint64_t i = 1; i < ink_points_.size(); ++i) {
+      gfx::RectF(ink_points_[pointer_id][0].point(), gfx::SizeF(1, 1));
+  for (uint64_t i = 1; i < ink_points_[pointer_id].size(); ++i) {
     ink_damage_rect_f.Union(
-        gfx::RectF(ink_points_[i].point(), gfx::SizeF(1, 1)));
+        gfx::RectF(ink_points_[pointer_id][i].point(), gfx::SizeF(1, 1)));
   }
   ink_damage_rect_f.Inset(-metadata().diameter() / 2.f,
                           -metadata().diameter() / 2.f);
diff --git a/components/viz/service/display/delegated_ink_point_pixel_test_helper.h b/components/viz/service/display/delegated_ink_point_pixel_test_helper.h
index c9003de..c172238b 100644
--- a/components/viz/service/display/delegated_ink_point_pixel_test_helper.h
+++ b/components/viz/service/display/delegated_ink_point_pixel_test_helper.h
@@ -5,6 +5,7 @@
 #ifndef COMPONENTS_VIZ_SERVICE_DISPLAY_DELEGATED_INK_POINT_PIXEL_TEST_HELPER_H_
 #define COMPONENTS_VIZ_SERVICE_DISPLAY_DELEGATED_INK_POINT_PIXEL_TEST_HELPER_H_
 
+#include <unordered_map>
 #include <vector>
 
 #include "components/viz/common/delegated_ink_metadata.h"
@@ -35,23 +36,31 @@
   void CreateAndSendMetadata(const gfx::PointF& point,
                              float diameter,
                              SkColor color,
+                             base::TimeTicks timestamp,
                              const gfx::RectF& presentation_area);
 
   void CreateAndSendMetadataFromLastPoint();
+  void CreateAndSendMetadataFromLastPoint(int32_t pointer_id);
 
   void CreateAndSendPoint(const gfx::PointF& point, base::TimeTicks timestamp);
-  void CreateAndSendPointFromMetadata();
+  void CreateAndSendPoint(const gfx::PointF& point,
+                          base::TimeTicks timestamp,
+                          int32_t pointer_id);
+
   // Used when sending multiple points to be drawn as a single trail, so it uses
   // the most recently provided point's timestamp to determine the new one.
   void CreateAndSendPointFromLastPoint(const gfx::PointF& point);
+  void CreateAndSendPointFromLastPoint(int32_t pointer_id,
+                                       const gfx::PointF& point);
 
   gfx::Rect GetDelegatedInkDamageRect();
+  gfx::Rect GetDelegatedInkDamageRect(int32_t pointer_id);
 
   const DelegatedInkMetadata& metadata() { return metadata_; }
 
  private:
   DirectRenderer* renderer_ = nullptr;
-  std::vector<DelegatedInkPoint> ink_points_;
+  std::unordered_map<int32_t, std::vector<DelegatedInkPoint>> ink_points_;
   DelegatedInkMetadata metadata_;
 };
 
diff --git a/components/viz/service/display/delegated_ink_point_renderer_base.cc b/components/viz/service/display/delegated_ink_point_renderer_base.cc
index 9918e52b..93ef86f 100644
--- a/components/viz/service/display/delegated_ink_point_renderer_base.cc
+++ b/components/viz/service/display/delegated_ink_point_renderer_base.cc
@@ -6,17 +6,11 @@
 
 #include "base/trace_event/trace_event.h"
 #include "components/viz/common/delegated_ink_metadata.h"
-#include "ui/base/prediction/kalman_predictor.h"
+#include "components/viz/service/display/delegated_ink_trail_data.h"
 
 namespace viz {
 
-DelegatedInkPointRendererBase::DelegatedInkPointRendererBase()
-    : metrics_handler_("Renderer.DelegatedInkTrail.Prediction") {
-  unsigned int predictor_options =
-      ui::KalmanPredictor::PredictionOptions::kHeuristicsEnabled |
-      ui::KalmanPredictor::PredictionOptions::kDirectionCutOffEnabled;
-  predictor_ = std::make_unique<ui::KalmanPredictor>(predictor_options);
-}
+DelegatedInkPointRendererBase::DelegatedInkPointRendererBase() = default;
 DelegatedInkPointRendererBase::~DelegatedInkPointRendererBase() = default;
 
 void DelegatedInkPointRendererBase::InitMessagePipeline(
@@ -29,7 +23,7 @@
   if (receiver_.is_bound()) {
     receiver_.reset();
     metadata_.reset();
-    points_.clear();
+    pointer_ids_.clear();
   }
   receiver_.Bind(std::move(receiver));
 }
@@ -40,14 +34,42 @@
   // at time of creation, so confirm that it was actually set.
   DCHECK_NE(metadata->frame_time(), base::TimeTicks());
   metadata_ = std::move(metadata);
+
+  // If we already have a cached pointer ID, check if the same pointer ID
+  // matches the new metadata.
+  if (pointer_id_.has_value() &&
+      pointer_ids_[pointer_id_.value()].ContainsMatchingPoint(
+          metadata_.get())) {
+    return;
+  }
+
+  // If not, find the pointer ID that does match it, if any, and cache it.
+  for (auto& it : pointer_ids_) {
+    if (it.second.ContainsMatchingPoint(metadata_.get())) {
+      pointer_id_ = it.first;
+      return;
+    }
+  }
+
+  // If we aren't able to find any matching point, set the pointer ID to null
+  // so that FilterPoints and PredictPoints can early out.
+  pointer_id_ = base::nullopt;
 }
 
 std::vector<DelegatedInkPoint> DelegatedInkPointRendererBase::FilterPoints() {
-  if (points_.size() == 0)
+  if (pointer_ids_.size() == 0)
     return {};
 
   DCHECK(metadata_);
 
+  // Any stored point with a timestamp earlier than the metadata's has already
+  // been drawn as part of the ink stroke and therefore should not be part of
+  // the delegated ink trail. Do this before checking if |pointer_id_| is valid
+  // because it helps manage the number of DelegatedInkPoints that are being
+  // stored and isn't dependent on |pointer_id_| at all.
+  for (auto& it : pointer_ids_)
+    it.second.ErasePointsOlderThanMetadata(metadata_.get());
+
   // TODO(1052145): Add additional filtering to prevent points in |points_| from
   // having a timestamp that is far ahead of |metadata_|'s timestamp. This could
   // occur if the renderer stalls before sending a metadata while the browser
@@ -56,57 +78,33 @@
   // stored here, resulting in a long possibly incorrect trail if the max
   // number of points to store was reached.
 
-  // First remove all points from |points_| with timestamps earlier than
-  // |metadata_|, as they have already been rendered by the app and are no
-  // longer useful for a trail.
-  // After that, there are three possible state of |points_|:
-  //   1. The earliest DelegatedInkPoint in |points_| matches |metadata_|'s
-  //      timestamp. All the points in |points_| can be used to draw a trail.
-  //   2. |points_| is empty. No DelegatedInkPoints arrived from the browser
-  //      process with a timestamp equal to or later than |metadata_|'s, so we
-  //      don't have any points to make a trail from.
-  //   3. There are DelegatedInkPoints in |points_|, but the earliest one is
-  //      later than |metadata_|. This can happen most often when the API is
-  //      first used, as the browser process did not know to send the point
-  //      to viz before it was used to make the metadata in the renderer. So
-  //      although it didn't send the DelegatedInkPoint matching |metadata_|, it
-  //      still may have sent future points before the metadata propagated all
-  //      the way here. In this case, we choose not to use the points in
-  //      |points_| to draw, as we have no way of confirming that there
-  //      shouldn't be any extra points between |metadata_| and the beginning
-  //      of |points_|. So instead, just leave everything after |metadata_| in
-  //      |points_| so that they may be used in future trails and don't draw
-  //      any trail for the current |metadata_|.
-  // So if |points_| contains a timestamp that matches |metadata_|'s timestamp,
-  // add it and every point after it to |points_to_draw| and return it for
-  // drawing. If it doesn't, just return an empty vector and leave any point
-  // with a timestamp later than |metadata_|'s in |points_|.
-  std::vector<DelegatedInkPoint> points_to_draw;
+  // If no point with any pointer id exactly matches the metadata, then we can't
+  // confirm which set of points to use for the delegated ink trail, so just
+  // return an empty vector so that nothing will be drawn. This happens most
+  // often at the beginning of delegated ink trail use. The metadata is created
+  // using a PointerEvent earlier than any DelegatedInkPoint is created,
+  // resulting in the metadata having an earlier timestamp and a point that
+  // doesn't match anything that is sent here from viz. Even if only a single
+  // pointer ID is in use, we can't know with any certainty what happened
+  // between the metadata point and the earliest DelegatedInkPoint we have, so
+  // we choose to just not draw anything.
+  if (!pointer_id_.has_value())
+    return {};
 
-  auto it = points_.begin();
-  while (points_.size() > 0 && it != points_.end()) {
-    if (it->first < metadata_->timestamp()) {
-      // Sanity check to confirm that we always find the points that are before
-      // |metadata_|'s timestamp at the beginning of |points_| since it should
-      // be sorted.
-      DCHECK(it == points_.begin());
-      it = points_.erase(it);
-    } else {
-      if (it->first == metadata_->timestamp() || points_to_draw.size() > 0) {
-        points_to_draw.push_back(it->second);
-        metrics_handler_.AddRealEvent(it->second.point(),
-                                      it->second.timestamp(),
-                                      metadata_->frame_time());
-        it++;
-      } else {
-        // If we find a point that is later than |metadata_|'s timestamp before
-        // finding one that matches |metadata_|'s timestamp, that means that
-        // it doesn't exist in |points_|, so return an empty vector as there are
-        // no valid points to draw.
-        break;
-      }
-    }
-  }
+  DelegatedInkTrailData& trail_data = pointer_ids_[pointer_id_.value()];
+
+  // Make sure the metrics handler is provided the new real events to accurately
+  // measure the prediction later.
+  trail_data.UpdateMetrics(metadata_.get());
+
+  // Any remaining points must be the points that should be part of the
+  // delegated ink trail
+  std::vector<DelegatedInkPoint> points_to_draw;
+  for (auto it : trail_data.GetPoints())
+    points_to_draw.emplace_back(it.second, it.first, pointer_id_.value());
+
+  DCHECK(points_to_draw.front().point() == metadata_->point() &&
+         points_to_draw.front().timestamp() == metadata_->timestamp());
 
   return points_to_draw;
 }
@@ -114,26 +112,26 @@
 void DelegatedInkPointRendererBase::PredictPoints(
     std::vector<DelegatedInkPoint>* ink_points_to_draw) {
   DCHECK(metadata_);
+
+  if (!pointer_id_.has_value())
+    return;
+
+  DelegatedInkTrailData& trail_data = pointer_ids_[pointer_id_.value()];
   int points_predicted = 0;
 
   // |ink_points_to_draw| needs to have at least one point in it already as a
   // reference to know what timestamp to start predicting points at. This single
   // point may just match |metadata_|.
-  if (predictor_->HasPrediction() && ink_points_to_draw->size() > 0) {
+  if (trail_data.HasPrediction() && ink_points_to_draw->size() > 0) {
     for (int i = 0; i < kNumberOfPointsToPredict; ++i) {
       base::TimeTicks timestamp =
           ink_points_to_draw->back().timestamp() +
           base::TimeDelta::FromMilliseconds(
               kNumberOfMillisecondsIntoFutureToPredictPerPoint);
-      std::unique_ptr<ui::InputPredictor::InputData> predicted_point =
-          predictor_->GeneratePrediction(timestamp);
-      if (predicted_point) {
-        ink_points_to_draw->emplace_back(
-            predicted_point->pos, predicted_point->time_stamp,
-            ink_points_to_draw->front().pointer_id());
-        metrics_handler_.AddPredictedEvent(predicted_point->pos,
-                                           predicted_point->time_stamp,
-                                           metadata_->frame_time());
+      base::Optional<DelegatedInkPoint> predicted_point =
+          trail_data.GetPredictedPoint(timestamp, metadata_->frame_time());
+      if (predicted_point.has_value()) {
+        ink_points_to_draw->push_back(predicted_point.value());
         points_predicted++;
       } else {
         // HasPrediction() can return true while GeneratePrediction() fails to
@@ -149,12 +147,13 @@
                        TRACE_EVENT_SCOPE_THREAD, "predicted points",
                        points_predicted);
 
-  metrics_handler_.EvaluatePrediction();
+  if (points_predicted > 0)
+    trail_data.EvaluatePrediction();
 }
 
 void DelegatedInkPointRendererBase::ResetPrediction() {
-  predictor_->Reset();
-  metrics_handler_.Reset();
+  for (auto& it : pointer_ids_)
+    it.second.Reset();
   TRACE_EVENT_INSTANT0("viz", "Delegated ink prediction reset.",
                        TRACE_EVENT_SCOPE_THREAD);
 }
@@ -165,16 +164,7 @@
                        "DelegatedInkPointRendererImpl::StoreDelegatedInkPoint",
                        TRACE_EVENT_SCOPE_THREAD, "point", point.ToString());
 
-  predictor_->Update(
-      ui::InputPredictor::InputData(point.point(), point.timestamp()));
-
-  // Fail-safe to prevent storing excessive points if they are being sent but
-  // never filtered and used, like if the renderer has stalled during a long
-  // running script.
-  if (points_.size() == kMaximumDelegatedInkPointsStored)
-    points_.erase(points_.begin());
-
-  points_.insert({point.timestamp(), point});
+  pointer_ids_[point.pointer_id()].AddPoint(point);
 }
 
 }  // namespace viz
diff --git a/components/viz/service/display/delegated_ink_point_renderer_base.h b/components/viz/service/display/delegated_ink_point_renderer_base.h
index 0d72e6b..534996a 100644
--- a/components/viz/service/display/delegated_ink_point_renderer_base.h
+++ b/components/viz/service/display/delegated_ink_point_renderer_base.h
@@ -5,24 +5,19 @@
 #ifndef COMPONENTS_VIZ_SERVICE_DISPLAY_DELEGATED_INK_POINT_RENDERER_BASE_H_
 #define COMPONENTS_VIZ_SERVICE_DISPLAY_DELEGATED_INK_POINT_RENDERER_BASE_H_
 
-#include <map>
 #include <memory>
+#include <unordered_map>
 #include <utility>
 #include <vector>
 
+#include "base/optional.h"
 #include "components/viz/service/viz_service_export.h"
 #include "mojo/public/cpp/bindings/receiver.h"
 #include "services/viz/public/mojom/compositing/delegated_ink_point.mojom.h"
-#include "ui/base/prediction/input_predictor.h"
-#include "ui/base/prediction/prediction_metrics_handler.h"
 
 namespace viz {
 class DelegatedInkMetadata;
-
-// The maximum number of delegated ink points that will be stored at a time.
-// When this is hit, the oldest one will be removed each time a new one is
-// added.
-constexpr int kMaximumDelegatedInkPointsStored = 10;
+class DelegatedInkTrailData;
 
 // The number of points to predict into the future, when prediction is
 // available.
@@ -73,9 +68,9 @@
  private:
   friend class SkiaDelegatedInkRendererTest;
 
-  const std::map<base::TimeTicks, DelegatedInkPoint>& GetPointsMapForTest()
-      const {
-    return points_;
+  const std::unordered_map<int32_t, DelegatedInkTrailData>&
+  GetPointsMapForTest() const {
+    return pointer_ids_;
   }
 
   const DelegatedInkMetadata* GetMetadataForTest() const {
@@ -84,16 +79,16 @@
 
   virtual int GetPathPointCountForTest() const = 0;
 
+  // Cached pointer id that matches the most recent metadata. This is set when
+  // a metadata arrives, and if no stored DelegatedInkPoints match the metadata,
+  // then it is null.
+  base::Optional<int32_t> pointer_id_;
+
   // The points that arrived from the browser process and may be drawn as part
-  // of the ink trail.
-  std::map<base::TimeTicks, DelegatedInkPoint> points_;
-
-  // Kalman predictor that is used for generating predicted points.
-  std::unique_ptr<ui::InputPredictor> predictor_;
-
-  // Handler for calculating useful metrics for evaluating predicted points
-  // and populating the histograms with those metrics.
-  ui::PredictionMetricsHandler metrics_handler_;
+  // of the ink trail are stored according to their pointer ids so that if
+  // more than one source of points is arriving, we can choose the correct set
+  // of points to use when drawing the delegated ink trail.
+  std::unordered_map<int32_t, DelegatedInkTrailData> pointer_ids_;
 
   mojo::Receiver<mojom::DelegatedInkPointRenderer> receiver_{this};
 };
diff --git a/components/viz/service/display/delegated_ink_trail_data.cc b/components/viz/service/display/delegated_ink_trail_data.cc
new file mode 100644
index 0000000..21cce34
--- /dev/null
+++ b/components/viz/service/display/delegated_ink_trail_data.cc
@@ -0,0 +1,85 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/viz/service/display/delegated_ink_trail_data.h"
+
+#include "components/viz/common/delegated_ink_metadata.h"
+#include "components/viz/common/delegated_ink_point.h"
+#include "ui/base/prediction/kalman_predictor.h"
+
+namespace viz {
+
+DelegatedInkTrailData::DelegatedInkTrailData()
+    : metrics_handler_("Renderer.DelegatedInkTrail.Prediction") {
+  unsigned int predictor_options =
+      ui::KalmanPredictor::PredictionOptions::kHeuristicsEnabled |
+      ui::KalmanPredictor::PredictionOptions::kDirectionCutOffEnabled;
+  predictor_ = std::make_unique<ui::KalmanPredictor>(predictor_options);
+}
+
+DelegatedInkTrailData::~DelegatedInkTrailData() = default;
+
+void DelegatedInkTrailData::AddPoint(const DelegatedInkPoint& point) {
+  if (static_cast<int>(points_.size()) == 0)
+    pointer_id_ = point.pointer_id();
+  else
+    DCHECK_EQ(pointer_id_, point.pointer_id());
+
+  predictor_->Update(
+      ui::InputPredictor::InputData(point.point(), point.timestamp()));
+
+  // Fail-safe to prevent storing excessive points if they are being sent but
+  // never filtered and used, like if the renderer has stalled during a long
+  // running script.
+  if (points_.size() == kMaximumDelegatedInkPointsStored)
+    points_.erase(points_.begin());
+
+  points_.insert({point.timestamp(), point.point()});
+}
+
+base::Optional<DelegatedInkPoint> DelegatedInkTrailData::GetPredictedPoint(
+    base::TimeTicks timestamp,
+    base::TimeTicks frame_time) {
+  std::unique_ptr<ui::InputPredictor::InputData> predicted_point =
+      predictor_->GeneratePrediction(timestamp);
+  if (!predicted_point)
+    return base::nullopt;
+
+  metrics_handler_.AddPredictedEvent(predicted_point->pos,
+                                     predicted_point->time_stamp, frame_time);
+  return DelegatedInkPoint(predicted_point->pos, predicted_point->time_stamp,
+                           pointer_id_);
+}
+
+void DelegatedInkTrailData::Reset() {
+  predictor_->Reset();
+  metrics_handler_.Reset();
+}
+
+bool DelegatedInkTrailData::ContainsMatchingPoint(
+    DelegatedInkMetadata* metadata) const {
+  auto point = points_.find(metadata->timestamp());
+  if (point == points_.end())
+    return false;
+
+  return point->second == metadata->point();
+}
+
+void DelegatedInkTrailData::ErasePointsOlderThanMetadata(
+    DelegatedInkMetadata* metadata) {
+  // Any points with a timestamp earlier than the metadata have already been
+  // drawn by the app. Since the metadata timestamp will only increase, we can
+  // safely erase every point earlier than it and be left only with the points
+  // that can be drawn.
+  while (points_.size() > 0 && points_.begin()->first < metadata->timestamp() &&
+         points_.begin()->second != metadata->point())
+    points_.erase(points_.begin());
+}
+
+void DelegatedInkTrailData::UpdateMetrics(DelegatedInkMetadata* metadata) {
+  for (auto it : points_)
+    metrics_handler_.AddRealEvent(it.second, it.first, metadata->frame_time());
+}
+
+}  // namespace viz
diff --git a/components/viz/service/display/delegated_ink_trail_data.h b/components/viz/service/display/delegated_ink_trail_data.h
new file mode 100644
index 0000000..1dbce08
--- /dev/null
+++ b/components/viz/service/display/delegated_ink_trail_data.h
@@ -0,0 +1,65 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_VIZ_SERVICE_DISPLAY_DELEGATED_INK_TRAIL_DATA_H_
+#define COMPONENTS_VIZ_SERVICE_DISPLAY_DELEGATED_INK_TRAIL_DATA_H_
+
+#include <map>
+#include <memory>
+
+#include "base/optional.h"
+#include "base/time/time.h"
+#include "components/viz/service/viz_service_export.h"
+#include "ui/base/prediction/input_predictor.h"
+#include "ui/base/prediction/prediction_metrics_handler.h"
+#include "ui/gfx/geometry/point_f.h"
+
+namespace viz {
+class DelegatedInkMetadata;
+class DelegatedInkPoint;
+
+// The maximum number of delegated ink points that will be stored at a time.
+// When this is hit, the oldest one will be removed each time a new one is
+// added.
+constexpr int kMaximumDelegatedInkPointsStored = 10;
+
+class VIZ_SERVICE_EXPORT DelegatedInkTrailData {
+ public:
+  DelegatedInkTrailData();
+  ~DelegatedInkTrailData();
+
+  void AddPoint(const DelegatedInkPoint& point);
+  base::Optional<DelegatedInkPoint> GetPredictedPoint(
+      base::TimeTicks timestamp,
+      base::TimeTicks frame_time);
+  void Reset();
+  bool ContainsMatchingPoint(DelegatedInkMetadata* metadata) const;
+  void ErasePointsOlderThanMetadata(DelegatedInkMetadata* metadata);
+  void UpdateMetrics(DelegatedInkMetadata* metadata);
+
+  const std::map<base::TimeTicks, gfx::PointF>& GetPoints() const {
+    return points_;
+  }
+  bool HasPrediction() const { return predictor_->HasPrediction(); }
+  void EvaluatePrediction() { metrics_handler_.EvaluatePrediction(); }
+
+ private:
+  // The points that arrived from the browser process and will be used to draw
+  // the delegated ink trail.
+  std::map<base::TimeTicks, gfx::PointF> points_;
+
+  // Kalman predictor that is used for generating predicted points.
+  std::unique_ptr<ui::InputPredictor> predictor_;
+
+  // Handler for calculating useful metrics for evaluating predicted points
+  // and populating the histograms with those metrics.
+  ui::PredictionMetricsHandler metrics_handler_;
+
+  // The pointer id associated with these points.
+  int32_t pointer_id_;
+};
+
+}  // namespace viz
+
+#endif  // COMPONENTS_VIZ_SERVICE_DISPLAY_DELEGATED_INK_TRAIL_DATA_H_
diff --git a/components/viz/service/display/display_unittest.cc b/components/viz/service/display/display_unittest.cc
index db1d0e6..950f8cd 100644
--- a/components/viz/service/display/display_unittest.cc
+++ b/components/viz/service/display/display_unittest.cc
@@ -6,6 +6,7 @@
 
 #include <limits>
 #include <map>
+#include <unordered_map>
 #include <utility>
 
 #include "base/bind.h"
@@ -32,6 +33,7 @@
 #include "components/viz/common/surfaces/subtree_capture_id.h"
 #include "components/viz/service/display/aggregated_frame.h"
 #include "components/viz/service/display/delegated_ink_point_renderer_skia.h"
+#include "components/viz/service/display/delegated_ink_trail_data.h"
 #include "components/viz/service/display/direct_renderer.h"
 #include "components/viz/service/display/display_client.h"
 #include "components/viz/service/display/display_scheduler.h"
@@ -4536,19 +4538,51 @@
     return display_->renderer_for_testing()->GetDelegatedInkPointRenderer();
   }
 
-  const std::map<base::TimeTicks, DelegatedInkPoint>& stored_points() {
-    return ink_renderer()->GetPointsMapForTest();
+  int UniqueStoredPointerIds() {
+    return ink_renderer()->GetPointsMapForTest().size();
+  }
+
+  int StoredPointsForPointerId(int32_t pointer_id) {
+    return GetPointsForPointerId(pointer_id).size();
+  }
+
+  const std::map<base::TimeTicks, gfx::PointF>& GetPointsForPointerId(
+      int32_t pointer_id) {
+    DCHECK(ink_renderer()->GetPointsMapForTest().find(pointer_id) !=
+           ink_renderer()->GetPointsMapForTest().end());
+    return ink_renderer()
+        ->GetPointsMapForTest()
+        .find(pointer_id)
+        ->second.GetPoints();
   }
 
   void CreateAndStoreDelegatedInkPoint(const gfx::PointF& point,
                                        base::TimeTicks timestamp,
                                        int32_t pointer_id) {
-    ink_points_.emplace_back(point, timestamp, pointer_id);
-    ink_renderer()->StoreDelegatedInkPoint(ink_points_.back());
+    ink_points_[pointer_id].emplace_back(point, timestamp, pointer_id);
+    ink_renderer()->StoreDelegatedInkPoint(ink_points_[pointer_id].back());
+  }
+
+  void CreateAndStoreDelegatedInkPointFromPreviousPoint(int32_t pointer_id) {
+    DCHECK(ink_points_.find(pointer_id) != ink_points_.end());
+
+    gfx::PointF point(ink_points_[pointer_id].back().point());
+    point.Offset(10, 10);
+
+    base::TimeTicks timestamp = ink_points_[pointer_id].back().timestamp();
+    timestamp += base::TimeDelta::FromMilliseconds(5);
+
+    CreateAndStoreDelegatedInkPoint(point, timestamp, pointer_id);
   }
 
   void StoreAlreadyCreatedDelegatedInkPoints() {
-    for (DelegatedInkPoint ink_point : ink_points_)
+    DCHECK_EQ(static_cast<int>(ink_points_.size()), 1);
+    StoreAlreadyCreatedDelegatedInkPoints(ink_points_.begin()->first);
+  }
+
+  void StoreAlreadyCreatedDelegatedInkPoints(int32_t pointer_id) {
+    DCHECK(ink_points_.find(pointer_id) != ink_points_.end());
+    for (DelegatedInkPoint ink_point : ink_points_[pointer_id])
       ink_renderer()->StoreDelegatedInkPoint(ink_point);
   }
 
@@ -4562,11 +4596,24 @@
       float diameter,
       SkColor color,
       const gfx::RectF& presentation_area) {
-    EXPECT_GE(index, 0);
-    EXPECT_LT(index, ink_points_size());
+    DCHECK_EQ(static_cast<int>(ink_points_.size()), 1);
+    return MakeAndSendMetadataFromStoredInkPoint(
+        ink_points_.begin()->first, index, diameter, color, presentation_area);
+  }
 
-    DelegatedInkMetadata metadata(ink_points_[index].point(), diameter, color,
-                                  ink_points_[index].timestamp(),
+  DelegatedInkMetadata MakeAndSendMetadataFromStoredInkPoint(
+      int32_t pointer_id,
+      int index,
+      float diameter,
+      SkColor color,
+      const gfx::RectF& presentation_area) {
+    DCHECK(ink_points_.find(pointer_id) != ink_points_.end());
+    EXPECT_GE(index, 0);
+    EXPECT_LT(index, ink_points_size(pointer_id));
+
+    DelegatedInkMetadata metadata(ink_points_[pointer_id][index].point(),
+                                  diameter, color,
+                                  ink_points_[pointer_id][index].timestamp(),
                                   presentation_area, base::TimeTicks::Now(),
                                   /*hovering*/ false);
     SendMetadata(metadata);
@@ -4621,15 +4668,34 @@
   }
 
   const DelegatedInkPoint& ink_point(int index) {
-    EXPECT_GE(index, 0);
-    EXPECT_LT(index, ink_points_size());
-    return ink_points_[index];
+    DCHECK_EQ(static_cast<int>(ink_points_.size()), 1);
+    return ink_point(ink_points_.begin()->first, index);
   }
 
-  int ink_points_size() { return ink_points_.size(); }
+  const DelegatedInkPoint& ink_point(int32_t pointer_id, int index) {
+    DCHECK(ink_points_.find(pointer_id) != ink_points_.end());
+    EXPECT_GE(index, 0);
+    EXPECT_LT(index, ink_points_size(pointer_id));
+    return ink_points_[pointer_id][index];
+  }
+
+  const DelegatedInkPoint& last_ink_point(int32_t pointer_id) {
+    DCHECK(ink_points_.find(pointer_id) != ink_points_.end());
+    return ink_points_[pointer_id].back();
+  }
+
+  int ink_points_size() {
+    DCHECK_EQ(static_cast<int>(ink_points_.size()), 1);
+    return ink_points_.begin()->second.size();
+  }
+
+  int ink_points_size(int32_t pointer_id) {
+    DCHECK(ink_points_.find(pointer_id) != ink_points_.end());
+    return ink_points_[pointer_id].size();
+  }
 
  private:
-  std::vector<DelegatedInkPoint> ink_points_;
+  std::unordered_map<int32_t, std::vector<DelegatedInkPoint>> ink_points_;
 };
 
 // Testing filtering points in the the delegated ink renderer when the skia
@@ -4638,7 +4704,7 @@
   SetUpRenderers();
 
   // First, a sanity check.
-  EXPECT_EQ(0, static_cast<int>(stored_points().size()));
+  EXPECT_EQ(0, UniqueStoredPointerIds());
 
   // Insert 3 arbitrary points into the ink renderer to confirm that they go
   // where we expect and are all stored correctly.
@@ -4646,20 +4712,22 @@
   base::TimeTicks timestamp = base::TimeTicks::Now();
   gfx::PointF point(10, 10);
   const int32_t kPointerId = std::numeric_limits<int32_t>::max();
-  for (int i = 0; i < kInitialDelegatedPoints; ++i) {
-    CreateAndStoreDelegatedInkPoint(point, timestamp, kPointerId);
-    point.Offset(10, 10);
-    timestamp += base::TimeDelta::FromMilliseconds(5);
-  }
+  CreateAndStoreDelegatedInkPoint(point, timestamp, kPointerId);
+  for (int i = 1; i < kInitialDelegatedPoints; ++i)
+    CreateAndStoreDelegatedInkPointFromPreviousPoint(kPointerId);
 
-  EXPECT_EQ(kInitialDelegatedPoints, static_cast<int>(stored_points().size()));
+  // They all have the same pointer ID, so there should be exactly one unique
+  // element in the map, and that element should itself have all three points.
+  EXPECT_EQ(1, UniqueStoredPointerIds());
+  EXPECT_EQ(kInitialDelegatedPoints, StoredPointsForPointerId(kPointerId));
 
   // No metadata has been provided yet, so filtering shouldn't occur and all
   // points should still exist after a FinalizePath() call.
   FinalizePathAndCheckHistograms(base::TimeDelta::Min(),
                                  base::TimeDelta::Min());
 
-  EXPECT_EQ(kInitialDelegatedPoints, static_cast<int>(stored_points().size()));
+  EXPECT_EQ(1, UniqueStoredPointerIds());
+  EXPECT_EQ(kInitialDelegatedPoints, StoredPointsForPointerId(kPointerId));
 
   // Now provide metadata with a timestamp matching one of the points to
   // confirm that earlier points are removed and later points remain.
@@ -4670,18 +4738,19 @@
 
   // The histogram should count one in the bucket that is the difference between
   // the latest point stored and the metadata. No prediction should occur with
-  // 3 provided, points, so the *WithPrediction histogram should count 1 in the
+  // 3 provided points, so the *WithPrediction histogram should count 1 in the
   // same bucket as the *WithoutPrediction histogram.
   base::TimeDelta bucket_without_prediction =
-      ink_point(ink_points_size() - 1).timestamp() - metadata.timestamp();
+      last_ink_point(kPointerId).timestamp() - metadata.timestamp();
   FinalizePathAndCheckHistograms(bucket_without_prediction,
                                  bucket_without_prediction);
 
   EXPECT_EQ(kInitialDelegatedPoints - kInkPointForMetadata,
-            static_cast<int>(stored_points().size()));
-  EXPECT_EQ(metadata.point(), stored_points().begin()->second.point());
-  EXPECT_EQ(ink_point(ink_points_size() - 1).point(),
-            stored_points().rbegin()->second.point());
+            StoredPointsForPointerId(kPointerId));
+  EXPECT_EQ(metadata.point(),
+            GetPointsForPointerId(kPointerId).begin()->second);
+  EXPECT_EQ(last_ink_point(kPointerId).point(),
+            GetPointsForPointerId(kPointerId).rbegin()->second);
   EXPECT_EQ(ink_point(0).pointer_id(), kPointerId);
 
   // Confirm that the metadata is cleared when DrawDelegatedInkTrail() is
@@ -4694,28 +4763,129 @@
   const int kPointsBeyondMaxAllowed = 2;
   StoreAlreadyCreatedDelegatedInkPoints();
   while (ink_points_size() <
-         kMaximumDelegatedInkPointsStored + kPointsBeyondMaxAllowed) {
-    CreateAndStoreDelegatedInkPoint(point, timestamp, kPointerId);
-    point.Offset(10, 10);
-    timestamp += base::TimeDelta::FromMilliseconds(10);
-  }
+         kMaximumDelegatedInkPointsStored + kPointsBeyondMaxAllowed)
+    CreateAndStoreDelegatedInkPointFromPreviousPoint(kPointerId);
 
   EXPECT_EQ(kMaximumDelegatedInkPointsStored,
-            static_cast<int>(stored_points().size()));
+            StoredPointsForPointerId(kPointerId));
   EXPECT_EQ(ink_point(kPointsBeyondMaxAllowed).point(),
-            stored_points().begin()->second.point());
-  EXPECT_EQ(ink_point(ink_points_size() - 1).point(),
-            stored_points().rbegin()->second.point());
-  EXPECT_EQ(ink_point(ink_points_size() - 1).pointer_id(), kPointerId);
+            GetPointsForPointerId(kPointerId).begin()->second);
+  EXPECT_EQ(last_ink_point(kPointerId).point(),
+            GetPointsForPointerId(kPointerId).rbegin()->second);
+  EXPECT_EQ(last_ink_point(kPointerId).pointer_id(), kPointerId);
 
   // Now send metadata with a timestamp before all of the points that are
   // currently stored to confirm that no points are filtered out and the number
   // stored remains the same while both histograms records 0 improvement.
-  const uint64_t kExpectedPoints = stored_points().size();
+  const int kExpectedPoints = StoredPointsForPointerId(kPointerId);
   SendMetadata(metadata);
   FinalizePathAndCheckHistograms(base::TimeDelta::FromMilliseconds(0),
                                  base::TimeDelta::FromMilliseconds(0));
-  EXPECT_EQ(kExpectedPoints, stored_points().size());
+  EXPECT_EQ(kExpectedPoints, StoredPointsForPointerId(kPointerId));
+}
+
+// Test filtering when points arrive with several different pointer IDs.
+TEST_F(SkiaDelegatedInkRendererTest,
+       SkiaDelegatedInkRendererFilteringPointsWithMultiplePointerIds) {
+  SetUpRenderers();
+
+  // Unique pointer IDs used - numbers arbitrary.
+  const std::vector<int32_t> kPointerIds = {1, 20, 300};
+
+  // First add just one DelegatedInkPoint for each pointer id to confirm that
+  // they all get stored separately.
+  base::TimeTicks timestamp = base::TimeTicks::Now();
+  for (uint64_t i = 0; i < kPointerIds.size(); ++i) {
+    // Make sure that each pointer id has slightly different points so that when
+    // new points are added later that are based on previous points, it doesn't
+    // result in multiple pointer ids having identical DelegatedInkPoints
+    CreateAndStoreDelegatedInkPoint(gfx::PointF(i * 5, i * 10), timestamp,
+                                    kPointerIds[i]);
+    timestamp += base::TimeDelta::FromMilliseconds(5);
+  }
+
+  EXPECT_EQ(static_cast<int>(kPointerIds.size()), UniqueStoredPointerIds());
+  for (int32_t pointer_id : kPointerIds)
+    EXPECT_EQ(1, StoredPointsForPointerId(pointer_id));
+
+  // Add more points so that the first pointer ID contains 4 DelegatedInkPoints,
+  // and the third pointer id contains 2 DelegatedInkPoints
+  const int kNumPointsForPointerId0 = 4;
+  while (ink_points_size(kPointerIds[0]) < kNumPointsForPointerId0)
+    CreateAndStoreDelegatedInkPointFromPreviousPoint(kPointerIds[0]);
+  CreateAndStoreDelegatedInkPointFromPreviousPoint(kPointerIds[2]);
+
+  // Confirm all the points got stored where they should have been.
+  for (int32_t pointer_id : kPointerIds) {
+    EXPECT_EQ(ink_points_size(pointer_id),
+              StoredPointsForPointerId(pointer_id));
+  }
+
+  // Now provide metadata with a timestamp matching one of the points in the
+  // first pointer id bucket to confirm that earlier points are removed and
+  // later points remain.
+  const int kInkPointForMetadata = 1;
+  const float kDiameter = 1.f;
+  DelegatedInkMetadata metadata = MakeAndSendMetadataFromStoredInkPoint(
+      kPointerIds[0], kInkPointForMetadata, kDiameter, SK_ColorBLACK,
+      gfx::RectF());
+
+  // 3 points should be enough for prediction to work, so the histogram should
+  // have one in the *WithoutPrediction bucket that matches the difference
+  // between the metadata and the final point, and one in the *WithPrediction
+  // bucket that matches the amount of prediction that is being done (plus the
+  // difference between the final point and the metadata).
+  base::TimeDelta bucket_without_prediction =
+      last_ink_point(kPointerIds[0]).timestamp() - metadata.timestamp();
+  FinalizePathAndCheckHistograms(
+      bucket_without_prediction,
+      bucket_without_prediction +
+          base::TimeDelta::FromMilliseconds(
+              kNumberOfMillisecondsIntoFutureToPredictPerPoint *
+              kNumberOfPointsToPredict));
+
+  // Confirm the size, first, and last points of the first pointer ID are what
+  // we expect.
+  EXPECT_EQ(kNumPointsForPointerId0 - kInkPointForMetadata,
+            StoredPointsForPointerId(kPointerIds[0]));
+  EXPECT_EQ(metadata.point(),
+            GetPointsForPointerId(kPointerIds[0]).begin()->second);
+  EXPECT_EQ(last_ink_point(kPointerIds[0]).point(),
+            GetPointsForPointerId(kPointerIds[0]).rbegin()->second);
+
+  // Confirm that neither of the other pointer ids were impacted.
+  for (uint64_t i = 1; i < kPointerIds.size(); ++i) {
+    EXPECT_EQ(ink_points_size(kPointerIds[i]),
+              StoredPointsForPointerId(kPointerIds[i]));
+  }
+
+  // Send a metadata whose point and timestamp doesn't match any stored
+  // DelegatedInkPoint and confirm that it doesn't cause any changes to the
+  // stored values. Histograms should have 1 in both 0 buckets since no points
+  // will be drawn.
+  SendMetadata(DelegatedInkMetadata(
+      gfx::PointF(100, 100), 5.6f, SK_ColorBLACK, base::TimeTicks::Min(),
+      gfx::RectF(), base::TimeTicks::Min(), /*hovering*/ false));
+  FinalizePathAndCheckHistograms(base::TimeDelta::FromMilliseconds(0),
+                                 base::TimeDelta::FromMilliseconds(0));
+  EXPECT_EQ(kNumPointsForPointerId0 - kInkPointForMetadata,
+            StoredPointsForPointerId(kPointerIds[0]));
+  for (uint64_t i = 1; i < kPointerIds.size(); ++i) {
+    EXPECT_EQ(ink_points_size(kPointerIds[i]),
+              StoredPointsForPointerId(kPointerIds[i]));
+  }
+
+  // Finally, send a metadata with a timestamp beyond all of the stored points.
+  // This should result in all of the points being erased, but the pointer ids
+  // will still exist as they contains the predictors as well.
+  SendMetadata(DelegatedInkMetadata(
+      gfx::PointF(100, 100), 5.6f, SK_ColorBLACK,
+      base::TimeTicks::Now() + base::TimeDelta::FromMilliseconds(1000),
+      gfx::RectF(), base::TimeTicks::Now(), /*hovering*/ false));
+  FinalizePathAndCheckHistograms(base::TimeDelta::FromMilliseconds(0),
+                                 base::TimeDelta::FromMilliseconds(0));
+  for (int i : kPointerIds)
+    EXPECT_EQ(0, StoredPointsForPointerId(i));
 }
 
 // Confirm that the delegated ink trail histograms record latency correctly.
diff --git a/components/viz/service/display/renderer_pixeltest.cc b/components/viz/service/display/renderer_pixeltest.cc
index 4903b9c..d8b5617 100644
--- a/components/viz/service/display/renderer_pixeltest.cc
+++ b/components/viz/service/display/renderer_pixeltest.cc
@@ -5226,17 +5226,19 @@
 // Draw a single trail and erase it, making sure that no bits of trail are left
 // behind.
 TEST_P(DelegatedInkTest, DrawOneTrailAndErase) {
-  // First provide the metadata required to draw the trail, numbers arbitrary.
-  CreateAndSendMetadata(gfx::PointF(10, 10), 3.5f, SK_ColorBLACK,
-                        gfx::RectF(0, 0, 175, 172));
-
-  // Then provide some points for the trail to draw. Numbers chosen arbitrarily
-  // after the first point, which must match the metadata. This will predict no
+  // Send some DelegatedInkPoints, numbers arbitrary. This will predict no
   // points, so a trail made of 3 points will be drawn.
-  CreateAndSendPointFromMetadata();
+  const gfx::PointF kFirstPoint(10, 10);
+  const base::TimeTicks kFirstTimestamp = base::TimeTicks::Now();
+  CreateAndSendPoint(kFirstPoint, kFirstTimestamp);
   CreateAndSendPointFromLastPoint(gfx::PointF(75, 62));
   CreateAndSendPointFromLastPoint(gfx::PointF(124, 45));
 
+  // Provide the metadata required to draw the trail, matching the first
+  // DelegatedInkPoint sent.
+  CreateAndSendMetadata(kFirstPoint, 3.5f, SK_ColorBLACK, kFirstTimestamp,
+                        gfx::RectF(0, 0, 175, 172));
+
   // Confirm that the trail was drawn.
   EXPECT_TRUE(
       DrawAndTestTrail(FILE_PATH_LITERAL("delegated_ink_one_trail.png")));
@@ -5252,16 +5254,18 @@
   if (renderer_type() == RendererType::kSkiaDawn)
     return;
 
-  // First provide the metadata required to draw the trail, numbers arbitrary.
-  CreateAndSendMetadata(gfx::PointF(140, 48), 8.2f, SK_ColorMAGENTA,
-                        gfx::RectF(0, 0, 200, 200));
-
-  // Then provide some points for the trail to draw. Numbers chosen arbitrarily
-  // after the first point, which must match the metadata. No points will be
-  // predicted, so a trail made of 2 points will be drawn.
-  CreateAndSendPointFromMetadata();
+  // Numbers chosen arbitrarily. No points will be predicted, so a trail made of
+  // 2 points will be drawn.
+  const gfx::PointF kFirstPoint(140, 48);
+  const base::TimeTicks kFirstTimestamp = base::TimeTicks::Now();
+  CreateAndSendPoint(kFirstPoint, kFirstTimestamp);
   CreateAndSendPointFromLastPoint(gfx::PointF(115, 85));
 
+  // Provide the metadata required to draw the trail, numbers matching the first
+  // DelegatedInkPoint sent.
+  CreateAndSendMetadata(kFirstPoint, 8.2f, SK_ColorMAGENTA, kFirstTimestamp,
+                        gfx::RectF(0, 0, 200, 200));
+
   // Confirm that the trail was drawn correctly.
   EXPECT_TRUE(DrawAndTestTrail(
       FILE_PATH_LITERAL("delegated_ink_two_trails_first.png")));
@@ -5287,14 +5291,13 @@
   if (renderer_type() == RendererType::kSkiaDawn)
     return;
 
-  const gfx::RectF kPresentationArea(30, 30, 100, 100);
-  CreateAndSendMetadata(gfx::PointF(50.2f, 89.999f), 15.22f, SK_ColorCYAN,
-                        kPresentationArea);
+  const gfx::PointF kFirstPoint(50.2f, 89.999f);
+  const base::TimeTicks kFirstTimestamp = base::TimeTicks::Now();
 
   // Send points such that some extend beyond the presentation area to confirm
   // that the trail is clipped correctly. One point will be predicted, so the
   // trail will be made of 9 points.
-  CreateAndSendPointFromMetadata();
+  CreateAndSendPoint(kFirstPoint, kFirstTimestamp);
   CreateAndSendPointFromLastPoint(gfx::PointF(80.7f, 149.6f));
   CreateAndSendPointFromLastPoint(gfx::PointF(128.999f, 110.01f));
   CreateAndSendPointFromLastPoint(gfx::PointF(50, 50));
@@ -5302,6 +5305,11 @@
   CreateAndSendPointFromLastPoint(gfx::PointF(29.98f, 66));
   CreateAndSendPointFromLastPoint(gfx::PointF(52.3456f, 2.31f));
   CreateAndSendPointFromLastPoint(gfx::PointF(97, 36.9f));
+
+  const gfx::RectF kPresentationArea(30, 30, 100, 100);
+  CreateAndSendMetadata(kFirstPoint, 15.22f, SK_ColorCYAN, kFirstTimestamp,
+                        kPresentationArea);
+
   EXPECT_TRUE(DrawAndTestTrail(FILE_PATH_LITERAL(
       "delegated_ink_trail_clipped_by_presentation_area.png")));
 }
@@ -5332,13 +5340,16 @@
   AggregatedRenderPassList pass_list;
   pass_list.push_back(std::move(pass));
 
-  const gfx::RectF kPresentationArea(0, 0, 200, 200);
-  CreateAndSendMetadata(gfx::PointF(34.f, 72.f), 7.77f, SK_ColorDKGRAY,
-                        kPresentationArea);
-  CreateAndSendPointFromMetadata();
+  const gfx::PointF kFirstPoint(34.f, 72.f);
+  const base::TimeTicks kFirstTimestamp = base::TimeTicks::Now();
+  CreateAndSendPoint(kFirstPoint, kFirstTimestamp);
   CreateAndSendPointFromLastPoint(gfx::PointF(79, 101));
   CreateAndSendPointFromLastPoint(gfx::PointF(134, 114));
 
+  const gfx::RectF kPresentationArea(0, 0, 200, 200);
+  CreateAndSendMetadata(kFirstPoint, 7.77f, SK_ColorDKGRAY, kFirstTimestamp,
+                        kPresentationArea);
+
   EXPECT_TRUE(this->RunPixelTest(
       &pass_list,
       base::FilePath(
@@ -5375,13 +5386,16 @@
   pass_list.push_back(std::move(root_pass));
 
   // Values for a simple delegated ink trail, numbers chosen arbitrarily.
-  const gfx::RectF kPresentationArea(0, 0, 200, 200);
-  CreateAndSendMetadata(gfx::PointF(156.f, 111.f), 19.177f, SK_ColorRED,
-                        kPresentationArea);
-  CreateAndSendPointFromMetadata();
+  const gfx::PointF kFirstPoint(156.f, 111.f);
+  const base::TimeTicks kFirstTimestamp = base::TimeTicks::Now();
+  CreateAndSendPoint(kFirstPoint, kFirstTimestamp);
   CreateAndSendPointFromLastPoint(gfx::PointF(119, 87.23f));
   CreateAndSendPointFromLastPoint(gfx::PointF(74.222f, 95.4f));
 
+  const gfx::RectF kPresentationArea(0, 0, 200, 200);
+  CreateAndSendMetadata(kFirstPoint, 19.177f, SK_ColorRED, kFirstTimestamp,
+                        kPresentationArea);
+
   // This will only check what was drawn in the child pass, which should never
   // contain a delegated ink trail, so it should be solid green.
   EXPECT_TRUE(this->RunPixelTestWithReadbackTarget(
@@ -5389,6 +5403,62 @@
       base::FilePath(FILE_PATH_LITERAL("green.png")),
       cc::ExactPixelComparator(true)));
 }
+
+// Draw two different trails that are made up of sets of DelegatedInkPoints with
+// different pointer IDs. All numbers arbitrarily chosen.
+TEST_P(DelegatedInkTest, DrawTrailsWithDifferentPointerIds) {
+  const int32_t kPointerId1 = 2;
+  const int32_t kPointerId2 = 100;
+
+  const base::TimeTicks kTimestamp = base::TimeTicks::Now();
+
+  // Constants used for sending points and making sure we can send matching
+  // DelegatedInkMetadata later.
+  const gfx::PointF kPointerId1StartPoint(40, 27);
+  const base::TimeTicks kPointerId1StartTime = kTimestamp;
+  const gfx::PointF kPointerId2StartPoint(160, 190);
+  const base::TimeTicks kPointerId2StartTime =
+      kTimestamp + base::TimeDelta::FromMilliseconds(15);
+
+  // Send four points for pointer ID 1 and two points for pointer ID 2 in mixed
+  // order to confirm that they get put in the right buckets. Some timestamps
+  // match intentionally to make sure that point is considered when matching
+  // DelegatedInkMetadata to DelegatedInkPoints
+  CreateAndSendPoint(kPointerId1StartPoint, kPointerId1StartTime, kPointerId1);
+  CreateAndSendPoint(gfx::PointF(24, 80),
+                     kTimestamp + base::TimeDelta::FromMilliseconds(15),
+                     kPointerId1);
+  CreateAndSendPoint(kPointerId2StartPoint, kPointerId2StartTime, kPointerId2);
+  CreateAndSendPoint(gfx::PointF(60, 130),
+                     kTimestamp + base::TimeDelta::FromMilliseconds(24),
+                     kPointerId1);
+  CreateAndSendPoint(gfx::PointF(80, 118),
+                     kTimestamp + base::TimeDelta::FromMilliseconds(20),
+                     kPointerId2);
+  CreateAndSendPoint(gfx::PointF(100, 190),
+                     kTimestamp + base::TimeDelta::FromMilliseconds(30),
+                     kPointerId1);
+
+  const gfx::RectF kPresentationArea(200, 200);
+
+  // Now send a metadata to match the first point of the first pointer id to
+  // confirm that only that trail is drawn.
+  CreateAndSendMetadata(kPointerId1StartPoint, 7, SK_ColorYELLOW,
+                        kPointerId1StartTime, kPresentationArea);
+  EXPECT_TRUE(
+      DrawAndTestTrail(FILE_PATH_LITERAL("delegated_ink_pointer_id_1.png")));
+
+  // Then send metadata that matches the first point of the other pointer id.
+  // These points should not have been erased, so all 3 points should be drawn.
+  CreateAndSendMetadata(kPointerId2StartPoint, 2.4f, SK_ColorRED,
+                        kPointerId2StartTime, kPresentationArea);
+  EXPECT_TRUE(
+      DrawAndTestTrail(FILE_PATH_LITERAL("delegated_ink_pointer_id_2.png")));
+
+  // The metadata should have been cleared after drawing, so confirm that there
+  // is no trail after another draw.
+  EXPECT_TRUE(DrawAndTestTrail(FILE_PATH_LITERAL("white.png")));
+}
 #endif  // !defined(OS_ANDROID)
 
 }  // namespace
diff --git a/components/viz/service/display/surface_aggregator_pixeltest.cc b/components/viz/service/display/surface_aggregator_pixeltest.cc
index 96203ed4..7789e7db 100644
--- a/components/viz/service/display/surface_aggregator_pixeltest.cc
+++ b/components/viz/service/display/surface_aggregator_pixeltest.cc
@@ -347,12 +347,16 @@
 
   // Create and send metadata and points to the renderer that will be drawn.
   // Points and timestamps are chosen arbitrarily.
-  delegated_ink_helper.CreateAndSendMetadata(
-      gfx::PointF(10, 10), 7.7f, SK_ColorWHITE, gfx::RectF(0, 0, 200, 200));
-  delegated_ink_helper.CreateAndSendPointFromMetadata();
+  const gfx::PointF kFirstPoint(10, 10);
+  const base::TimeTicks kFirstTimestamp = base::TimeTicks::Now();
+  delegated_ink_helper.CreateAndSendPoint(kFirstPoint, kFirstTimestamp);
   delegated_ink_helper.CreateAndSendPointFromLastPoint(gfx::PointF(26, 37));
   delegated_ink_helper.CreateAndSendPointFromLastPoint(gfx::PointF(45, 87));
 
+  delegated_ink_helper.CreateAndSendMetadata(kFirstPoint, 7.7f, SK_ColorWHITE,
+                                             kFirstTimestamp,
+                                             gfx::RectF(0, 0, 200, 200));
+
   gfx::Rect rect(this->device_viewport_size_);
   CompositorRenderPassId id{1};
   auto pass = CompositorRenderPass::Create();
diff --git a/components/viz/test/data/delegated_ink_pointer_id_1.png b/components/viz/test/data/delegated_ink_pointer_id_1.png
new file mode 100644
index 0000000..516f0d1
--- /dev/null
+++ b/components/viz/test/data/delegated_ink_pointer_id_1.png
Binary files differ
diff --git a/components/viz/test/data/delegated_ink_pointer_id_2.png b/components/viz/test/data/delegated_ink_pointer_id_2.png
new file mode 100644
index 0000000..69ae4a1
--- /dev/null
+++ b/components/viz/test/data/delegated_ink_pointer_id_2.png
Binary files differ
diff --git a/components/webapps/browser/banners/app_banner_manager.cc b/components/webapps/browser/banners/app_banner_manager.cc
index 3b51c2d..4f8f6e2 100644
--- a/components/webapps/browser/banners/app_banner_manager.cc
+++ b/components/webapps/browser/banners/app_banner_manager.cc
@@ -28,6 +28,7 @@
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/web_contents.h"
+#include "content/public/common/url_utils.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "services/service_manager/public/cpp/interface_provider.h"
@@ -233,6 +234,20 @@
 
 AppBannerManager::~AppBannerManager() = default;
 
+bool AppBannerManager::ShouldIgnore(content::RenderFrameHost* render_frame_host,
+                                    const GURL& url) {
+  // Don't start the banner flow unless the main frame has finished loading.
+  // |render_frame_host| can be null during retry attempts.
+  if (render_frame_host && render_frame_host->GetParent())
+    return true;
+
+  // There is never a need to trigger a banner for a WebUI page.
+  if (content::HasWebUIScheme(url))
+    return true;
+
+  return false;
+}
+
 bool AppBannerManager::CheckIfShouldShowBanner() {
   if (ShouldBypassEngagementChecks())
     return true;
@@ -591,9 +606,7 @@
 void AppBannerManager::DidFinishLoad(
     content::RenderFrameHost* render_frame_host,
     const GURL& validated_url) {
-  // Don't start the banner flow unless the main frame has finished loading.
-  // |render_frame_host| can be null during retry attempts.
-  if (render_frame_host && render_frame_host->GetParent())
+  if (ShouldIgnore(render_frame_host, validated_url))
     return;
 
   load_finished_ = true;
diff --git a/components/webapps/browser/banners/app_banner_manager.h b/components/webapps/browser/banners/app_banner_manager.h
index c4ae1e3..7a682aa 100644
--- a/components/webapps/browser/banners/app_banner_manager.h
+++ b/components/webapps/browser/banners/app_banner_manager.h
@@ -200,6 +200,11 @@
   explicit AppBannerManager(content::WebContents* web_contents);
   ~AppBannerManager() override;
 
+  // Returns true if |render_frame_host| and |url| should be ignored and not
+  // trigger the banner flow.
+  bool ShouldIgnore(content::RenderFrameHost* render_frame_host,
+                    const GURL& url);
+
   // Returns true if the banner should be shown. Returns false if the banner has
   // been shown too recently, or if the app has already been installed.
   // GetAppIdentifier() must return a valid value for this method to work.
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index 4f33afdc..1bd4d49 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -486,8 +486,6 @@
     "blob_storage/blob_internals_url_loader.h",
     "blob_storage/blob_registry_wrapper.cc",
     "blob_storage/blob_registry_wrapper.h",
-    "blob_storage/blob_storage_context_wrapper.cc",
-    "blob_storage/blob_storage_context_wrapper.h",
     "blob_storage/chrome_blob_storage_context.cc",
     "blob_storage/chrome_blob_storage_context.h",
     "bluetooth/bluetooth_adapter_factory_wrapper.cc",
@@ -552,6 +550,8 @@
     "browsing_instance.h",
     "byte_stream.cc",
     "byte_stream.h",
+    "cache_storage/blob_storage_context_wrapper.cc",
+    "cache_storage/blob_storage_context_wrapper.h",
     "cache_storage/cache_storage.cc",
     "cache_storage/cache_storage.h",
     "cache_storage/cache_storage_blob_to_disk_cache.cc",
@@ -1938,6 +1938,7 @@
     sources += [
       "../app_shim_remote_cocoa/web_drag_source_mac.h",
       "../app_shim_remote_cocoa/web_drag_source_mac.mm",
+      "accessibility/accessibility_event_recorder_mac.h",
       "accessibility/accessibility_event_recorder_mac.mm",
       "accessibility/accessibility_tools_utils_mac.h",
       "accessibility/accessibility_tools_utils_mac.mm",
@@ -2439,6 +2440,7 @@
   if (use_atk) {
     sources += [
       "accessibility/accessibility_event_recorder_auralinux.cc",
+      "accessibility/accessibility_event_recorder_auralinux.h",
       "accessibility/accessibility_tree_formatter_auralinux.cc",
       "accessibility/accessibility_tree_formatter_auralinux.h",
       "accessibility/accessibility_tree_formatter_utils_auralinux.cc",
diff --git a/content/browser/accessibility/accessibility_event_recorder_auralinux.cc b/content/browser/accessibility/accessibility_event_recorder_auralinux.cc
index 4242bf5..da72155b 100644
--- a/content/browser/accessibility/accessibility_event_recorder_auralinux.cc
+++ b/content/browser/accessibility/accessibility_event_recorder_auralinux.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 "content/browser/accessibility/accessibility_event_recorder.h"
+#include "content/browser/accessibility/accessibility_event_recorder_auralinux.h"
 
 #include <atk/atk.h>
 #include <atk/atkutil.h>
@@ -22,52 +22,6 @@
 
 namespace content {
 
-// This class has two distinct event recording code paths. When we are
-// recording events in-process (typically this is used for
-// DumpAccessibilityEvents tests), we use ATK's global event handlers. Since
-// ATK doesn't support intercepting events from other processes, if we have a
-// non-zero PID or an accessibility application name pattern, we use AT-SPI2
-// directly to intercept events. Since AT-SPI2 should be capable of
-// intercepting events in-process as well, eventually it would be nice to
-// remove the ATK code path entirely.
-class AccessibilityEventRecorderAuraLinux : public AccessibilityEventRecorder {
- public:
-  explicit AccessibilityEventRecorderAuraLinux(
-      BrowserAccessibilityManager* manager,
-      base::ProcessId pid,
-      const AXTreeSelector& selector);
-  ~AccessibilityEventRecorderAuraLinux() override;
-
-  void ProcessATKEvent(const char* event,
-                       unsigned int n_params,
-                       const GValue* params);
-  void ProcessATSPIEvent(const AtspiEvent* event);
-
-  static gboolean OnATKEventReceived(GSignalInvocationHint* hint,
-                                     unsigned int n_params,
-                                     const GValue* params,
-                                     gpointer data);
-
- private:
-  bool ShouldUseATSPI();
-
-  std::string AtkObjectToString(AtkObject* obj, bool include_name);
-  void AddATKEventListener(const char* event_name);
-  void AddATKEventListeners();
-  void RemoveATKEventListeners();
-  bool IncludeState(AtkStateType state_type);
-
-  void AddATSPIEventListeners();
-  void RemoveATSPIEventListeners();
-
-  AtspiEventListener* atspi_event_listener_ = nullptr;
-  base::ProcessId pid_;
-  base::StringPiece application_name_match_pattern_;
-  static AccessibilityEventRecorderAuraLinux* instance_;
-
-  DISALLOW_COPY_AND_ASSIGN(AccessibilityEventRecorderAuraLinux);
-};
-
 // static
 AccessibilityEventRecorderAuraLinux*
     AccessibilityEventRecorderAuraLinux::instance_ = nullptr;
diff --git a/content/browser/accessibility/accessibility_event_recorder_auralinux.h b/content/browser/accessibility/accessibility_event_recorder_auralinux.h
new file mode 100644
index 0000000..e8db8d0
--- /dev/null
+++ b/content/browser/accessibility/accessibility_event_recorder_auralinux.h
@@ -0,0 +1,66 @@
+// 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 CONTENT_BROWSER_ACCESSIBILITY_ACCESSIBILITY_EVENT_RECORDER_AURALINUX_H_
+#define CONTENT_BROWSER_ACCESSIBILITY_ACCESSIBILITY_EVENT_RECORDER_AURALINUX_H_
+
+#include <atk/atk.h>
+#include <atspi/atspi.h>
+
+#include "content/browser/accessibility/accessibility_event_recorder.h"
+#include "content/common/content_export.h"
+
+namespace content {
+
+// This class has two distinct event recording code paths. When we are
+// recording events in-process (typically this is used for
+// DumpAccessibilityEvents tests), we use ATK's global event handlers. Since
+// ATK doesn't support intercepting events from other processes, if we have a
+// non-zero PID or an accessibility application name pattern, we use AT-SPI2
+// directly to intercept events.
+// TODO(crbug.com/1133330) AT-SPI2 should be capable of intercepting events
+// in-process as well, thus it should be possible to remove the ATK code path
+// entirely.
+class CONTENT_EXPORT AccessibilityEventRecorderAuraLinux
+    : public AccessibilityEventRecorder {
+ public:
+  explicit AccessibilityEventRecorderAuraLinux(
+      BrowserAccessibilityManager* manager,
+      base::ProcessId pid,
+      const AXTreeSelector& selector);
+  ~AccessibilityEventRecorderAuraLinux() override;
+
+  void ProcessATKEvent(const char* event,
+                       unsigned int n_params,
+                       const GValue* params);
+  void ProcessATSPIEvent(const AtspiEvent* event);
+
+  static gboolean OnATKEventReceived(GSignalInvocationHint* hint,
+                                     unsigned int n_params,
+                                     const GValue* params,
+                                     gpointer data);
+
+ private:
+  bool ShouldUseATSPI();
+
+  std::string AtkObjectToString(AtkObject* obj, bool include_name);
+  void AddATKEventListener(const char* event_name);
+  void AddATKEventListeners();
+  void RemoveATKEventListeners();
+  bool IncludeState(AtkStateType state_type);
+
+  void AddATSPIEventListeners();
+  void RemoveATSPIEventListeners();
+
+  AtspiEventListener* atspi_event_listener_ = nullptr;
+  base::ProcessId pid_;
+  base::StringPiece application_name_match_pattern_;
+  static AccessibilityEventRecorderAuraLinux* instance_;
+
+  DISALLOW_COPY_AND_ASSIGN(AccessibilityEventRecorderAuraLinux);
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_ACCESSIBILITY_ACCESSIBILITY_EVENT_RECORDER_AURALINUX_H_
diff --git a/content/browser/accessibility/accessibility_event_recorder_mac.h b/content/browser/accessibility/accessibility_event_recorder_mac.h
new file mode 100644
index 0000000..d05178d3
--- /dev/null
+++ b/content/browser/accessibility/accessibility_event_recorder_mac.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 CONTENT_BROWSER_ACCESSIBILITY_ACCESSIBILITY_EVENT_RECORDER_MAC_H_
+#define CONTENT_BROWSER_ACCESSIBILITY_ACCESSIBILITY_EVENT_RECORDER_MAC_H_
+
+#include "content/browser/accessibility/accessibility_event_recorder.h"
+#include "content/browser/accessibility/browser_accessibility_cocoa.h"
+
+@class BrowserAccessibilityCocoa;
+
+namespace content {
+
+// Implementation of AccessibilityEventRecorder that uses AXObserver to
+// watch for NSAccessibility events.
+class CONTENT_EXPORT AccessibilityEventRecorderMac
+    : public AccessibilityEventRecorder {
+ public:
+  AccessibilityEventRecorderMac(BrowserAccessibilityManager* manager,
+                                base::ProcessId pid,
+                                AXUIElementRef node);
+  ~AccessibilityEventRecorderMac() override;
+
+  // Callback executed every time we receive an event notification.
+  void EventReceived(AXUIElementRef element,
+                     CFStringRef notification,
+                     CFDictionaryRef user_info);
+  static std::string SerializeTextSelectionChangedProperties(
+      CFDictionaryRef user_info);
+
+ private:
+  // Add one notification to the list of notifications monitored by our
+  // observer.
+  void AddNotification(NSString* notification);
+
+  // Convenience function to get the value of an AX attribute from
+  // an AXUIElementRef as a string.
+  std::string GetAXAttributeValue(AXUIElementRef element,
+                                  NSString* attribute_name);
+
+  // The AXUIElement for the Chrome application.
+  base::ScopedCFTypeRef<AXUIElementRef> application_;
+
+  // The AXObserver we use to monitor AX notifications.
+  base::ScopedCFTypeRef<AXObserverRef> observer_ref_;
+  CFRunLoopSourceRef observer_run_loop_source_;
+
+  DISALLOW_COPY_AND_ASSIGN(AccessibilityEventRecorderMac);
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_ACCESSIBILITY_ACCESSIBILITY_EVENT_RECORDER_MAC_H_
diff --git a/content/browser/accessibility/accessibility_event_recorder_mac.mm b/content/browser/accessibility/accessibility_event_recorder_mac.mm
index cdf4f8f8..e065a8a77 100644
--- a/content/browser/accessibility/accessibility_event_recorder_mac.mm
+++ b/content/browser/accessibility/accessibility_event_recorder_mac.mm
@@ -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 "content/browser/accessibility/accessibility_event_recorder.h"
+#include "content/browser/accessibility/accessibility_event_recorder_mac.h"
 
 #import <Cocoa/Cocoa.h>
 
@@ -20,42 +20,6 @@
 
 namespace content {
 
-// Implementation of AccessibilityEventRecorder that uses AXObserver to
-// watch for NSAccessibility events.
-class AccessibilityEventRecorderMac : public AccessibilityEventRecorder {
- public:
-  AccessibilityEventRecorderMac(BrowserAccessibilityManager* manager,
-                                base::ProcessId pid,
-                                AXUIElementRef node);
-  ~AccessibilityEventRecorderMac() override;
-
-  // Callback executed every time we receive an event notification.
-  void EventReceived(AXUIElementRef element,
-                     CFStringRef notification,
-                     CFDictionaryRef user_info);
-  static std::string SerializeTextSelectionChangedProperties(
-      CFDictionaryRef user_info);
-
- private:
-  // Add one notification to the list of notifications monitored by our
-  // observer.
-  void AddNotification(NSString* notification);
-
-  // Convenience function to get the value of an AX attribute from
-  // an AXUIElementRef as a string.
-  std::string GetAXAttributeValue(AXUIElementRef element,
-                                  NSString* attribute_name);
-
-  // The AXUIElement for the Chrome application.
-  base::ScopedCFTypeRef<AXUIElementRef> application_;
-
-  // The AXObserver we use to monitor AX notifications.
-  base::ScopedCFTypeRef<AXObserverRef> observer_ref_;
-  CFRunLoopSourceRef observer_run_loop_source_;
-
-  DISALLOW_COPY_AND_ASSIGN(AccessibilityEventRecorderMac);
-};
-
 // Callback function registered using AXObserverCreate.
 static void EventReceivedThunk(AXObserverRef observer_ref,
                                AXUIElementRef element,
diff --git a/content/browser/accessibility/browser_accessibility_state_impl_win.cc b/content/browser/accessibility/browser_accessibility_state_impl_win.cc
index fffcddd9..cb53197 100644
--- a/content/browser/accessibility/browser_accessibility_state_impl_win.cc
+++ b/content/browser/accessibility/browser_accessibility_state_impl_win.cc
@@ -162,7 +162,7 @@
   for (size_t i = 0; i < module_count; i++) {
     TCHAR filename[MAX_PATH];
     GetModuleFileName(modules[i], filename, base::size(filename));
-    base::string16 module_name(base::FilePath(filename).BaseName().value());
+    std::string module_name(base::FilePath(filename).BaseName().AsUTF8Unsafe());
     if (base::LowerCaseEqualsASCII(module_name, "fsdomsrv.dll"))
       g_jaws = true;
     if (base::LowerCaseEqualsASCII(module_name, "vbufbackend_gecko_ia2.dll") ||
diff --git a/content/browser/bad_message.h b/content/browser/bad_message.h
index e094f70..2a0591d 100644
--- a/content/browser/bad_message.h
+++ b/content/browser/bad_message.h
@@ -266,6 +266,7 @@
   CSDH_BAD_OWNER = 238,
   SYNC_COMPOSITOR_NO_LOCAL_SURFACE_ID = 239,
   WCI_INVALID_FULLSCREEN_OPTIONS = 240,
+  PAYMENTS_WITHOUT_PERMISSION = 241,
 
   // Please add new elements here. The naming convention is abbreviated class
   // name (e.g. RenderFrameHost becomes RFH) plus a unique description of the
diff --git a/content/browser/blob_storage/blob_storage_context_wrapper.cc b/content/browser/cache_storage/blob_storage_context_wrapper.cc
similarity index 81%
rename from content/browser/blob_storage/blob_storage_context_wrapper.cc
rename to content/browser/cache_storage/blob_storage_context_wrapper.cc
index a0c57cbb..dfc4a9f7 100644
--- a/content/browser/blob_storage/blob_storage_context_wrapper.cc
+++ b/content/browser/cache_storage/blob_storage_context_wrapper.cc
@@ -2,10 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "content/browser/blob_storage/blob_storage_context_wrapper.h"
-
-#include "base/task/post_task.h"
-#include "content/public/browser/browser_task_traits.h"
+#include "content/browser/cache_storage/blob_storage_context_wrapper.h"
 
 namespace content {
 
diff --git a/content/browser/blob_storage/blob_storage_context_wrapper.h b/content/browser/cache_storage/blob_storage_context_wrapper.h
similarity index 83%
rename from content/browser/blob_storage/blob_storage_context_wrapper.h
rename to content/browser/cache_storage/blob_storage_context_wrapper.h
index 33e2117..c4ef0ec 100644
--- a/content/browser/blob_storage/blob_storage_context_wrapper.h
+++ b/content/browser/cache_storage/blob_storage_context_wrapper.h
@@ -2,15 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CONTENT_BROWSER_BLOB_STORAGE_BLOB_STORAGE_CONTEXT_WRAPPER_H_
-#define CONTENT_BROWSER_BLOB_STORAGE_BLOB_STORAGE_CONTEXT_WRAPPER_H_
+#ifndef CONTENT_BROWSER_CACHE_STORAGE_BLOB_STORAGE_CONTEXT_WRAPPER_H_
+#define CONTENT_BROWSER_CACHE_STORAGE_BLOB_STORAGE_CONTEXT_WRAPPER_H_
 
 #include <string>
 
 #include "base/macros.h"
 #include "components/services/storage/public/mojom/blob_storage_context.mojom.h"
 #include "content/common/content_export.h"
-#include "content/public/browser/browser_thread.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/remote.h"
 
@@ -41,4 +40,4 @@
 
 }  // namespace content
 
-#endif  // CONTENT_BROWSER_BLOB_STORAGE_BLOB_STORAGE_CONTEXT_WRAPPER_H_
+#endif  // CONTENT_BROWSER_CACHE_STORAGE_BLOB_STORAGE_CONTEXT_WRAPPER_H_
diff --git a/content/browser/cache_storage/cache_storage_cache_entry_handler.h b/content/browser/cache_storage/cache_storage_cache_entry_handler.h
index 9a780db..b339787 100644
--- a/content/browser/cache_storage/cache_storage_cache_entry_handler.h
+++ b/content/browser/cache_storage/cache_storage_cache_entry_handler.h
@@ -14,6 +14,7 @@
 #include "base/memory/weak_ptr.h"
 #include "base/types/pass_key.h"
 #include "components/services/storage/public/mojom/cache_storage_control.mojom.h"
+#include "content/browser/cache_storage/blob_storage_context_wrapper.h"
 #include "content/browser/cache_storage/cache_storage_cache.h"
 #include "content/browser/cache_storage/cache_storage_cache_handle.h"
 #include "content/browser/cache_storage/cache_storage_manager.h"
diff --git a/content/browser/cache_storage/cache_storage_context_impl.cc b/content/browser/cache_storage/cache_storage_context_impl.cc
index 37ff15f..60e526d 100644
--- a/content/browser/cache_storage/cache_storage_context_impl.cc
+++ b/content/browser/cache_storage/cache_storage_context_impl.cc
@@ -12,7 +12,7 @@
 #include "build/build_config.h"
 #include "components/services/storage/public/mojom/quota_client.mojom.h"
 #include "components/services/storage/public/mojom/storage_usage_info.mojom.h"
-#include "content/browser/blob_storage/chrome_blob_storage_context.h"
+#include "content/browser/cache_storage/blob_storage_context_wrapper.h"
 #include "content/browser/cache_storage/cache_storage_dispatcher_host.h"
 #include "content/browser/cache_storage/cache_storage_quota_client.h"
 #include "content/browser/cache_storage/cross_sequence/cross_sequence_cache_storage_manager.h"
@@ -20,7 +20,6 @@
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "mojo/public/cpp/bindings/self_owned_receiver.h"
-#include "storage/browser/blob/blob_storage_context.h"
 #include "storage/browser/quota/quota_client_type.h"
 #include "storage/browser/quota/quota_manager_proxy.h"
 #include "storage/browser/quota/special_storage_policy.h"
@@ -53,7 +52,9 @@
 void CacheStorageContextImpl::Init(
     const base::FilePath& user_data_directory,
     scoped_refptr<storage::SpecialStoragePolicy> special_storage_policy,
-    scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy) {
+    scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy,
+    mojo::PendingRemote<storage::mojom::BlobStorageContext>
+        blob_storage_context) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   is_incognito_ = user_data_directory.empty();
@@ -69,18 +70,7 @@
       base::BindOnce(
           &CacheStorageContextImpl::CreateCacheStorageManagerOnTaskRunner, this,
           user_data_directory, std::move(cache_task_runner),
-          quota_manager_proxy));
-
-  // If our target sequence is the IO thread, then the manager is guaranteed to
-  // be created before this task fires to create the quota clients.  If we are
-  // running with a different target sequence then the quota client code will
-  // get a cross-sequence wrapper that is guaranteed to initialize its internal
-  // SequenceBound<> object after the real manager is created.
-  GetIOThreadTaskRunner({})->PostTask(
-      FROM_HERE,
-      base::BindOnce(&CacheStorageContextImpl::CreateQuotaClientsOnIOThread,
-                     base::WrapRefCounted(this),
-                     std::move(quota_manager_proxy)));
+          quota_manager_proxy, std::move(blob_storage_context)));
 }
 
 void CacheStorageContextImpl::Shutdown() {
@@ -151,32 +141,6 @@
                                                                 this);
 }
 
-void CacheStorageContextImpl::SetBlobParametersForCache(
-    ChromeBlobStorageContext* blob_storage_context) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  if (!blob_storage_context)
-    return;
-
-  // TODO(enne): this remote will need to be sent to the storage service when
-  // cache storage is moved.
-  mojo::PendingRemote<storage::mojom::BlobStorageContext> remote;
-  auto receiver = remote.InitWithNewPipeAndPassReceiver();
-  task_runner_->PostTask(
-      FROM_HERE,
-      base::BindOnce(
-          &CacheStorageContextImpl::SetBlobParametersForCacheOnTaskRunner, this,
-          std::move(remote)));
-
-  // We can only bind a mojo interface for BlobStorageContext on the IO thread.
-  // TODO(enne): clean this up in the future to not require this bounce and
-  // to have this mojo context live on the cache storage sequence.
-  GetIOThreadTaskRunner({})->PostTask(
-      FROM_HERE,
-      base::BindOnce(
-          &CacheStorageContextImpl::BindBlobStorageMojoContextOnIOThread, this,
-          base::RetainedRef(blob_storage_context), std::move(receiver)));
-}
-
 void CacheStorageContextImpl::GetAllOriginsInfo(
     storage::mojom::CacheStorageControl::GetAllOriginsInfoCallback callback) {
   // Can be called on any sequence.
@@ -247,13 +211,37 @@
 void CacheStorageContextImpl::CreateCacheStorageManagerOnTaskRunner(
     const base::FilePath& user_data_directory,
     scoped_refptr<base::SequencedTaskRunner> cache_task_runner,
-    scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy) {
+    scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy,
+    mojo::PendingRemote<storage::mojom::BlobStorageContext>
+        blob_storage_context) {
   DCHECK(task_runner_->RunsTasksInCurrentSequence());
 
   DCHECK(!cache_manager_);
   cache_manager_ = LegacyCacheStorageManager::Create(
       user_data_directory, std::move(cache_task_runner), task_runner_,
-      quota_manager_proxy);
+      quota_manager_proxy,
+      base::MakeRefCounted<BlobStorageContextWrapper>(
+          std::move(blob_storage_context)));
+
+  mojo::PendingRemote<storage::mojom::QuotaClient> cache_storage_client;
+  mojo::MakeSelfOwnedReceiver(
+      std::make_unique<CacheStorageQuotaClient>(
+          cache_manager_, storage::mojom::CacheStorageOwner::kCacheAPI),
+      cache_storage_client.InitWithNewPipeAndPassReceiver());
+  quota_manager_proxy->RegisterClient(
+      std::move(cache_storage_client),
+      storage::QuotaClientType::kServiceWorkerCache,
+      {blink::mojom::StorageType::kTemporary});
+
+  mojo::PendingRemote<storage::mojom::QuotaClient> background_fetch_client;
+  mojo::MakeSelfOwnedReceiver(
+      std::make_unique<CacheStorageQuotaClient>(
+          cache_manager_, storage::mojom::CacheStorageOwner::kBackgroundFetch),
+      background_fetch_client.InitWithNewPipeAndPassReceiver());
+  quota_manager_proxy->RegisterClient(
+      std::move(background_fetch_client),
+      storage::QuotaClientType::kBackgroundFetch,
+      {blink::mojom::StorageType::kTemporary});
 }
 
 void CacheStorageContextImpl::ShutdownOnTaskRunner() {
@@ -302,53 +290,4 @@
   cache_manager_ = nullptr;
 }
 
-void CacheStorageContextImpl::BindBlobStorageMojoContextOnIOThread(
-    ChromeBlobStorageContext* blob_storage_context,
-    mojo::PendingReceiver<storage::mojom::BlobStorageContext> receiver) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  DCHECK(blob_storage_context);
-  DCHECK(receiver.is_valid());
-
-  blob_storage_context->BindMojoContext(std::move(receiver));
-}
-
-void CacheStorageContextImpl::SetBlobParametersForCacheOnTaskRunner(
-    mojo::PendingRemote<storage::mojom::BlobStorageContext> remote) {
-  DCHECK(task_runner_->RunsTasksInCurrentSequence());
-  if (!cache_manager_)
-    return;
-  cache_manager_->SetBlobParametersForCache(
-      base::MakeRefCounted<BlobStorageContextWrapper>(std::move(remote)));
-}
-
-void CacheStorageContextImpl::CreateQuotaClientsOnIOThread(
-    scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  if (!quota_manager_proxy.get())
-    return;
-  scoped_refptr<CacheStorageManager> manager = CacheManager();
-  if (!manager)
-    return;
-
-  mojo::PendingRemote<storage::mojom::QuotaClient> cache_storage_client;
-  mojo::MakeSelfOwnedReceiver(
-      std::make_unique<CacheStorageQuotaClient>(
-          manager, storage::mojom::CacheStorageOwner::kCacheAPI),
-      cache_storage_client.InitWithNewPipeAndPassReceiver());
-  quota_manager_proxy->RegisterClient(
-      std::move(cache_storage_client),
-      storage::QuotaClientType::kServiceWorkerCache,
-      {blink::mojom::StorageType::kTemporary});
-
-  mojo::PendingRemote<storage::mojom::QuotaClient> background_fetch_client;
-  mojo::MakeSelfOwnedReceiver(
-      std::make_unique<CacheStorageQuotaClient>(
-          manager, storage::mojom::CacheStorageOwner::kBackgroundFetch),
-      background_fetch_client.InitWithNewPipeAndPassReceiver());
-  quota_manager_proxy->RegisterClient(
-      std::move(background_fetch_client),
-      storage::QuotaClientType::kBackgroundFetch,
-      {blink::mojom::StorageType::kTemporary});
-}
-
 }  // namespace content
diff --git a/content/browser/cache_storage/cache_storage_context_impl.h b/content/browser/cache_storage/cache_storage_context_impl.h
index 74872b7..2fceab1 100644
--- a/content/browser/cache_storage/cache_storage_context_impl.h
+++ b/content/browser/cache_storage/cache_storage_context_impl.h
@@ -39,7 +39,6 @@
 
 namespace content {
 
-class ChromeBlobStorageContext;
 class CacheStorageDispatcherHost;
 class CacheStorageManager;
 
@@ -72,7 +71,9 @@
   // storagepartition is being setup and torn down.
   void Init(const base::FilePath& user_data_directory,
             scoped_refptr<storage::SpecialStoragePolicy> special_storage_policy,
-            scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy);
+            scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy,
+            mojo::PendingRemote<storage::mojom::BlobStorageContext>
+                blob_storage_context);
   void Shutdown();
 
   void Bind(mojo::PendingReceiver<storage::mojom::CacheStorageControl> control);
@@ -103,13 +104,6 @@
 
   bool is_incognito() const { return is_incognito_; }
 
-  // This function must be called after this object is created but before any
-  // CacheStorageCache operations. It must be called on the UI thread. If
-  // |blob_storage_context| is NULL the function immediately returns without
-  // forwarding to the CacheStorageManager.
-  void SetBlobParametersForCache(
-      ChromeBlobStorageContext* blob_storage_context);
-
  protected:
   ~CacheStorageContextImpl() override;
 
@@ -117,20 +111,12 @@
   void CreateCacheStorageManagerOnTaskRunner(
       const base::FilePath& user_data_directory,
       scoped_refptr<base::SequencedTaskRunner> cache_task_runner,
-      scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy);
+      scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy,
+      mojo::PendingRemote<storage::mojom::BlobStorageContext>
+          blob_storage_context);
 
   void ShutdownOnTaskRunner();
 
-  void BindBlobStorageMojoContextOnIOThread(
-      ChromeBlobStorageContext* blob_storage_context,
-      mojo::PendingReceiver<storage::mojom::BlobStorageContext> receiver);
-
-  void SetBlobParametersForCacheOnTaskRunner(
-      mojo::PendingRemote<storage::mojom::BlobStorageContext> remote);
-
-  void CreateQuotaClientsOnIOThread(
-      scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy);
-
   // Initialized at construction.
   const scoped_refptr<base::SequencedTaskRunner> task_runner_;
 
diff --git a/content/browser/cache_storage/cache_storage_manager.h b/content/browser/cache_storage/cache_storage_manager.h
index 52d3f89d5..52d7589 100644
--- a/content/browser/cache_storage/cache_storage_manager.h
+++ b/content/browser/cache_storage/cache_storage_manager.h
@@ -10,7 +10,6 @@
 #include "base/macros.h"
 #include "components/services/storage/public/mojom/cache_storage_control.mojom.h"
 #include "components/services/storage/public/mojom/quota_client.mojom.h"
-#include "content/browser/blob_storage/blob_storage_context_wrapper.h"
 #include "content/browser/cache_storage/cache_storage_handle.h"
 #include "content/common/content_export.h"
 #include "content/public/browser/browser_thread.h"
@@ -60,10 +59,6 @@
   virtual void AddObserver(
       mojo::PendingRemote<storage::mojom::CacheStorageObserver> observer) = 0;
 
-  // This must be called before any of the public Cache functions above.
-  virtual void SetBlobParametersForCache(
-      scoped_refptr<BlobStorageContextWrapper> blob_storage_context) = 0;
-
   static bool IsValidQuotaOrigin(const url::Origin& origin);
 
  protected:
diff --git a/content/browser/cache_storage/cache_storage_manager_unittest.cc b/content/browser/cache_storage/cache_storage_manager_unittest.cc
index 11d921e..15afd2b 100644
--- a/content/browser/cache_storage/cache_storage_manager_unittest.cc
+++ b/content/browser/cache_storage/cache_storage_manager_unittest.cc
@@ -418,8 +418,8 @@
 
     auto legacy_manager = LegacyCacheStorageManager::Create(
         temp_dir_path, base::ThreadTaskRunnerHandle::Get(),
-        base::ThreadTaskRunnerHandle::Get(), quota_manager_proxy_);
-    legacy_manager->SetBlobParametersForCache(blob_storage_context_);
+        base::ThreadTaskRunnerHandle::Get(), quota_manager_proxy_,
+        blob_storage_context_);
 
     switch (ManagerType()) {
       case TestManager::kLegacy:
diff --git a/content/browser/cache_storage/cache_storage_quota_client.cc b/content/browser/cache_storage/cache_storage_quota_client.cc
index 0a9c34a..0e51d008 100644
--- a/content/browser/cache_storage/cache_storage_quota_client.cc
+++ b/content/browser/cache_storage/cache_storage_quota_client.cc
@@ -5,7 +5,6 @@
 #include "content/browser/cache_storage/cache_storage_quota_client.h"
 
 #include "content/browser/cache_storage/cache_storage_manager.h"
-#include "content/public/browser/browser_thread.h"
 #include "storage/browser/quota/quota_client_type.h"
 #include "third_party/blink/public/mojom/quota/quota_types.mojom.h"
 #include "url/origin.h"
@@ -15,14 +14,18 @@
 CacheStorageQuotaClient::CacheStorageQuotaClient(
     scoped_refptr<CacheStorageManager> cache_manager,
     storage::mojom::CacheStorageOwner owner)
-    : cache_manager_(std::move(cache_manager)), owner_(owner) {}
+    : cache_manager_(std::move(cache_manager)), owner_(owner) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+}
 
-CacheStorageQuotaClient::~CacheStorageQuotaClient() = default;
+CacheStorageQuotaClient::~CacheStorageQuotaClient() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+}
 
 void CacheStorageQuotaClient::GetOriginUsage(const url::Origin& origin,
                                              blink::mojom::StorageType type,
                                              GetOriginUsageCallback callback) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK_EQ(type, blink::mojom::StorageType::kTemporary);
 
   if (!CacheStorageManager::IsValidQuotaOrigin(origin)) {
@@ -36,7 +39,7 @@
 void CacheStorageQuotaClient::GetOriginsForType(
     blink::mojom::StorageType type,
     GetOriginsForTypeCallback callback) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK_EQ(type, blink::mojom::StorageType::kTemporary);
 
   cache_manager_->GetOrigins(owner_, std::move(callback));
@@ -46,7 +49,7 @@
     blink::mojom::StorageType type,
     const std::string& host,
     GetOriginsForHostCallback callback) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK_EQ(type, blink::mojom::StorageType::kTemporary);
 
   cache_manager_->GetOriginsForHost(host, owner_, std::move(callback));
@@ -56,7 +59,7 @@
     const url::Origin& origin,
     blink::mojom::StorageType type,
     DeleteOriginDataCallback callback) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK_EQ(type, blink::mojom::StorageType::kTemporary);
 
   if (!CacheStorageManager::IsValidQuotaOrigin(origin)) {
@@ -70,6 +73,7 @@
 void CacheStorageQuotaClient::PerformStorageCleanup(
     blink::mojom::StorageType type,
     PerformStorageCleanupCallback callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   std::move(callback).Run();
 }
 
diff --git a/content/browser/cache_storage/cache_storage_quota_client.h b/content/browser/cache_storage/cache_storage_quota_client.h
index c7ade58..223f1c7 100644
--- a/content/browser/cache_storage/cache_storage_quota_client.h
+++ b/content/browser/cache_storage/cache_storage_quota_client.h
@@ -7,6 +7,7 @@
 
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
+#include "base/sequence_checker.h"
 #include "components/services/storage/public/mojom/cache_storage_control.mojom.h"
 #include "components/services/storage/public/mojom/quota_client.mojom.h"
 #include "content/common/content_export.h"
@@ -18,9 +19,11 @@
 
 class CacheStorageManager;
 
-// CacheStorageQuotaClient is owned by the QuotaManager. There is one per
-// CacheStorageManager/CacheStorageOwner tuple.  Created and accessed on
-// the IO thread.
+// CacheStorageQuotaClient is a self-owned receiver created by
+// CacheStorageContextImpl.  The remote end is owned by QuotaManagerProxy.
+// There is one CacheStorageQuotaClient per CacheStorageManager /
+// CacheStorageOwner tuple.  Created and accessed on the cache storage task
+// runner.
 class CONTENT_EXPORT CacheStorageQuotaClient
     : public storage::mojom::QuotaClient {
  public:
@@ -50,6 +53,8 @@
   const scoped_refptr<CacheStorageManager> cache_manager_;
   const storage::mojom::CacheStorageOwner owner_;
 
+  SEQUENCE_CHECKER(sequence_checker_);
+
   DISALLOW_COPY_AND_ASSIGN(CacheStorageQuotaClient);
 };
 
diff --git a/content/browser/cache_storage/cross_sequence/cross_sequence_cache_storage_manager.cc b/content/browser/cache_storage/cross_sequence/cross_sequence_cache_storage_manager.cc
index dea31758..8c4ddb7 100644
--- a/content/browser/cache_storage/cross_sequence/cross_sequence_cache_storage_manager.cc
+++ b/content/browser/cache_storage/cross_sequence/cross_sequence_cache_storage_manager.cc
@@ -150,14 +150,6 @@
   inner_.Post(FROM_HERE, &Inner::AddObserver, std::move(observer));
 }
 
-void CrossSequenceCacheStorageManager::SetBlobParametersForCache(
-    scoped_refptr<BlobStorageContextWrapper> blob_storage_context) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  // This method is used for initialization of a real manager and should not
-  // be invoked for the cross-sequence wrapper.
-  NOTREACHED();
-}
-
 CrossSequenceCacheStorageManager::~CrossSequenceCacheStorageManager() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 }
diff --git a/content/browser/cache_storage/cross_sequence/cross_sequence_cache_storage_manager.h b/content/browser/cache_storage/cross_sequence/cross_sequence_cache_storage_manager.h
index 9ec3431..c69aaa9 100644
--- a/content/browser/cache_storage/cross_sequence/cross_sequence_cache_storage_manager.h
+++ b/content/browser/cache_storage/cross_sequence/cross_sequence_cache_storage_manager.h
@@ -55,8 +55,6 @@
                         storage::mojom::CacheStorageOwner owner) override;
   void AddObserver(mojo::PendingRemote<storage::mojom::CacheStorageObserver>
                        observer) override;
-  void SetBlobParametersForCache(
-      scoped_refptr<BlobStorageContextWrapper> blob_storage_context) override;
 
  private:
   ~CrossSequenceCacheStorageManager() override;
diff --git a/content/browser/cache_storage/legacy/legacy_cache_storage.h b/content/browser/cache_storage/legacy/legacy_cache_storage.h
index 516ca13..a309fc1 100644
--- a/content/browser/cache_storage/legacy/legacy_cache_storage.h
+++ b/content/browser/cache_storage/legacy/legacy_cache_storage.h
@@ -18,6 +18,7 @@
 #include "build/build_config.h"
 #include "components/services/storage/public/mojom/blob_storage_context.mojom.h"
 #include "components/services/storage/public/mojom/cache_storage_control.mojom.h"
+#include "content/browser/cache_storage/blob_storage_context_wrapper.h"
 #include "content/browser/cache_storage/cache_storage.h"
 #include "content/browser/cache_storage/cache_storage_cache_observer.h"
 #include "content/browser/cache_storage/cache_storage_manager.h"
diff --git a/content/browser/cache_storage/legacy/legacy_cache_storage_cache.cc b/content/browser/cache_storage/legacy/legacy_cache_storage_cache.cc
index c83afd4..e4b630a 100644
--- a/content/browser/cache_storage/legacy/legacy_cache_storage_cache.cc
+++ b/content/browser/cache_storage/legacy/legacy_cache_storage_cache.cc
@@ -51,7 +51,6 @@
 #include "net/http/http_response_headers.h"
 #include "net/http/http_status_code.h"
 #include "services/network/public/mojom/fetch_api.mojom.h"
-#include "storage/browser/blob/blob_storage_context.h"
 #include "storage/browser/quota/padding_key.h"
 #include "storage/browser/quota/quota_manager_proxy.h"
 #include "third_party/blink/public/common/cache_storage/cache_storage_utils.h"
diff --git a/content/browser/cache_storage/legacy/legacy_cache_storage_cache.h b/content/browser/cache_storage/legacy/legacy_cache_storage_cache.h
index a6e7a2b..b6d7e13 100644
--- a/content/browser/cache_storage/legacy/legacy_cache_storage_cache.h
+++ b/content/browser/cache_storage/legacy/legacy_cache_storage_cache.h
@@ -18,6 +18,7 @@
 #include "base/memory/weak_ptr.h"
 #include "base/optional.h"
 #include "components/services/storage/public/mojom/cache_storage_control.mojom.h"
+#include "content/browser/cache_storage/blob_storage_context_wrapper.h"
 #include "content/browser/cache_storage/cache_storage_cache.h"
 #include "content/browser/cache_storage/cache_storage_handle.h"
 #include "content/browser/cache_storage/cache_storage_manager.h"
diff --git a/content/browser/cache_storage/legacy/legacy_cache_storage_manager.cc b/content/browser/cache_storage/legacy/legacy_cache_storage_manager.cc
index bc889a8b..f2e6dc8 100644
--- a/content/browser/cache_storage/legacy/legacy_cache_storage_manager.cc
+++ b/content/browser/cache_storage/legacy/legacy_cache_storage_manager.cc
@@ -244,7 +244,8 @@
     const base::FilePath& path,
     scoped_refptr<base::SequencedTaskRunner> cache_task_runner,
     scoped_refptr<base::SequencedTaskRunner> scheduler_task_runner,
-    scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy) {
+    scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy,
+    scoped_refptr<BlobStorageContextWrapper> blob_storage_context) {
   base::FilePath root_path = path;
   if (!path.empty()) {
     root_path = path.Append(storage::kServiceWorkerDirectory)
@@ -253,7 +254,7 @@
 
   return base::WrapRefCounted(new LegacyCacheStorageManager(
       root_path, std::move(cache_task_runner), std::move(scheduler_task_runner),
-      std::move(quota_manager_proxy)));
+      std::move(quota_manager_proxy), std::move(blob_storage_context)));
 }
 
 // static
@@ -264,8 +265,8 @@
       new LegacyCacheStorageManager(old_manager->root_path(),
                                     old_manager->cache_task_runner(),
                                     old_manager->scheduler_task_runner(),
-                                    old_manager->quota_manager_proxy_.get()));
-  manager->SetBlobParametersForCache(old_manager->blob_storage_context_);
+                                    old_manager->quota_manager_proxy_,
+                                    old_manager->blob_storage_context_));
   return manager;
 }
 
@@ -300,15 +301,6 @@
   return it->second.get()->CreateHandle();
 }
 
-void LegacyCacheStorageManager::SetBlobParametersForCache(
-    scoped_refptr<BlobStorageContextWrapper> blob_storage_context) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(cache_storage_map_.empty());
-  DCHECK(!blob_storage_context_ ||
-         blob_storage_context_ == blob_storage_context);
-  blob_storage_context_ = std::move(blob_storage_context);
-}
-
 void LegacyCacheStorageManager::NotifyCacheListChanged(
     const url::Origin& origin) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
@@ -530,11 +522,13 @@
     const base::FilePath& path,
     scoped_refptr<base::SequencedTaskRunner> cache_task_runner,
     scoped_refptr<base::SequencedTaskRunner> scheduler_task_runner,
-    scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy)
+    scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy,
+    scoped_refptr<BlobStorageContextWrapper> blob_storage_context)
     : root_path_(path),
       cache_task_runner_(std::move(cache_task_runner)),
       scheduler_task_runner_(std::move(scheduler_task_runner)),
-      quota_manager_proxy_(std::move(quota_manager_proxy)) {}
+      quota_manager_proxy_(std::move(quota_manager_proxy)),
+      blob_storage_context_(std::move(blob_storage_context)) {}
 
 // static
 base::FilePath LegacyCacheStorageManager::ConstructOriginPath(
diff --git a/content/browser/cache_storage/legacy/legacy_cache_storage_manager.h b/content/browser/cache_storage/legacy/legacy_cache_storage_manager.h
index 6b7127ad..396323ea 100644
--- a/content/browser/cache_storage/legacy/legacy_cache_storage_manager.h
+++ b/content/browser/cache_storage/legacy/legacy_cache_storage_manager.h
@@ -18,6 +18,7 @@
 #include "components/services/storage/public/mojom/cache_storage_control.mojom.h"
 #include "components/services/storage/public/mojom/quota_client.mojom.h"
 #include "components/services/storage/public/mojom/storage_usage_info.mojom.h"
+#include "content/browser/cache_storage/blob_storage_context_wrapper.h"
 #include "content/browser/cache_storage/cache_storage_context_impl.h"
 #include "content/browser/cache_storage/cache_storage_manager.h"
 #include "content/browser/cache_storage/legacy/legacy_cache_storage.h"
@@ -46,7 +47,8 @@
       const base::FilePath& path,
       scoped_refptr<base::SequencedTaskRunner> cache_task_runner,
       scoped_refptr<base::SequencedTaskRunner> scheduler_task_runner,
-      scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy);
+      scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy,
+      scoped_refptr<BlobStorageContextWrapper> blob_storage_context);
 
   // Create a new manager using the underlying configuration of the given
   // manager, but with its own list of storage objects.  This is only used
@@ -90,9 +92,6 @@
   void AddObserver(mojo::PendingRemote<storage::mojom::CacheStorageObserver>
                        observer) override;
 
-  void SetBlobParametersForCache(
-      scoped_refptr<BlobStorageContextWrapper> blob_storage_context) override;
-
   void NotifyCacheListChanged(const url::Origin& origin);
   void NotifyCacheContentChanged(const url::Origin& origin,
                                  const std::string& name);
@@ -117,7 +116,8 @@
       const base::FilePath& path,
       scoped_refptr<base::SequencedTaskRunner> cache_task_runner,
       scoped_refptr<base::SequencedTaskRunner> scheduler_task_runner,
-      scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy);
+      scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy,
+      scoped_refptr<BlobStorageContextWrapper> blob_storage_context);
 
   ~LegacyCacheStorageManager() override;
 
diff --git a/content/browser/child_process_security_policy_impl.cc b/content/browser/child_process_security_policy_impl.cc
index 6be7f55..5588e67 100644
--- a/content/browser/child_process_security_policy_impl.cc
+++ b/content/browser/child_process_security_policy_impl.cc
@@ -1615,7 +1615,14 @@
         // BrowsingInstances are registered in the process. Allow this for now,
         // to maintain legacy behavior, until we rule out all the ways it can
         // happen.
-        return true;
+        RenderProcessHostImpl* child_host = static_cast<RenderProcessHostImpl*>(
+            RenderProcessHost::FromID(child_id));
+        DCHECK(child_host);
+        size_t keep_alive_count = child_host->keep_alive_ref_count();
+        failure_reason = base::StringPrintf("No BIids, keep_alive_count = %zu",
+                                            keep_alive_count);
+        // This will fall through to the call to the call to
+        // LogCanAccessDataForOriginCrashKeys and log the failure reason.
       }
       for (auto browsing_instance_id :
            security_state->browsing_instance_ids()) {
diff --git a/content/browser/devtools/protocol/browser_handler.cc b/content/browser/devtools/protocol/browser_handler.cc
index db7ee33..c42c3d9 100644
--- a/content/browser/devtools/protocol/browser_handler.cc
+++ b/content/browser/devtools/protocol/browser_handler.cc
@@ -511,7 +511,7 @@
   if (command_line->HasSwitch(switches::kEnableAutomation)) {
     for (const auto& arg : command_line->argv()) {
 #if defined(OS_WIN)
-      (*arguments)->emplace_back(base::UTF16ToUTF8(arg));
+      (*arguments)->emplace_back(base::WideToUTF8(arg));
 #else
       (*arguments)->emplace_back(arg);
 #endif
diff --git a/content/browser/download/drag_download_util.cc b/content/browser/download/drag_download_util.cc
index bc40cd3..92f4328 100644
--- a/content/browser/download/drag_download_util.cc
+++ b/content/browser/download/drag_download_util.cc
@@ -46,11 +46,7 @@
   if (file_name) {
     base::string16 file_name_str = metadata.substr(
         mime_type_end_pos + 1, file_name_end_pos - mime_type_end_pos  - 1);
-#if defined(OS_WIN)
-    *file_name = base::FilePath(file_name_str);
-#else
-    *file_name = base::FilePath(base::UTF16ToUTF8(file_name_str));
-#endif
+    *file_name = base::FilePath::FromUTF16Unsafe(file_name_str);
   }
   if (url)
     *url = parsed_url;
@@ -68,10 +64,9 @@
       new_file_path = *file_path;
     } else {
 #if defined(OS_WIN)
-      base::string16 suffix =
-          base::ASCIIToUTF16("-") + base::NumberToString16(seq);
+      std::wstring suffix = L"-" + base::NumberToWString(seq);
 #else
-      std::string suffix = std::string("-") + base::NumberToString(seq);
+      std::string suffix = "-" + base::NumberToString(seq);
 #endif
       new_file_path = file_path->InsertBeforeExtension(suffix);
     }
diff --git a/content/browser/file_system/file_system_manager_impl.cc b/content/browser/file_system/file_system_manager_impl.cc
index 1e4c18e..92b55f10 100644
--- a/content/browser/file_system/file_system_manager_impl.cc
+++ b/content/browser/file_system/file_system_manager_impl.cc
@@ -86,14 +86,14 @@
     case storage::FileSystemType::kFileSystemTypeUnknown:
     case storage::FileSystemType::kFileSystemInternalTypeEnumStart:
     case storage::FileSystemType::kFileSystemTypeTest:
-    case storage::FileSystemType::kFileSystemTypeNativeLocal:
-    case storage::FileSystemType::kFileSystemTypeRestrictedNativeLocal:
+    case storage::FileSystemType::kFileSystemTypeLocal:
+    case storage::FileSystemType::kFileSystemTypeRestrictedLocal:
     case storage::FileSystemType::kFileSystemTypeDragged:
-    case storage::FileSystemType::kFileSystemTypeNativeMedia:
+    case storage::FileSystemType::kFileSystemTypeLocalMedia:
     case storage::FileSystemType::kFileSystemTypeDeviceMedia:
     case storage::FileSystemType::kFileSystemTypeSyncable:
     case storage::FileSystemType::kFileSystemTypeSyncableForInternalSync:
-    case storage::FileSystemType::kFileSystemTypeNativeForPlatformApp:
+    case storage::FileSystemType::kFileSystemTypeLocalForPlatformApp:
     case storage::FileSystemType::kFileSystemTypeForTransientFile:
     case storage::FileSystemType::kFileSystemTypePluginPrivate:
     case storage::FileSystemType::kFileSystemTypeCloudDevice:
diff --git a/content/browser/file_system_access/file_system_access_directory_handle_impl.cc b/content/browser/file_system_access/file_system_access_directory_handle_impl.cc
index d7d4dccd..dedf355 100644
--- a/content/browser/file_system_access/file_system_access_directory_handle_impl.cc
+++ b/content/browser/file_system_access/file_system_access_directory_handle_impl.cc
@@ -260,7 +260,7 @@
   std::vector<std::string> result;
   result.reserve(components.size());
   for (const auto& component : components) {
-    result.push_back(base::UTF16ToUTF8(component));
+    result.push_back(base::WideToUTF8(component));
   }
   std::move(callback).Run(file_system_access_error::Ok(), std::move(result));
 #else
diff --git a/content/browser/file_system_access/file_system_access_file_writer_impl.cc b/content/browser/file_system_access/file_system_access_file_writer_impl.cc
index 300c7748..714d58fe 100644
--- a/content/browser/file_system_access/file_system_access_file_writer_impl.cc
+++ b/content/browser/file_system_access/file_system_access_file_writer_impl.cc
@@ -654,7 +654,7 @@
          target_url.type() != storage::kFileSystemTypePersistent)
       << target_url.type();
 #else
-  DCHECK(target_url.type() == storage::kFileSystemTypeNativeLocal ||
+  DCHECK(target_url.type() == storage::kFileSystemTypeLocal ||
          target_url.type() == storage::kFileSystemTypeTest)
       << target_url.type();
 #endif
diff --git a/content/browser/file_system_access/file_system_access_file_writer_impl_unittest.cc b/content/browser/file_system_access/file_system_access_file_writer_impl_unittest.cc
index f7bbc798..6821b9e 100644
--- a/content/browser/file_system_access/file_system_access_file_writer_impl_unittest.cc
+++ b/content/browser/file_system_access/file_system_access_file_writer_impl_unittest.cc
@@ -127,7 +127,7 @@
     std::string base_name;
     IsolatedContext::ScopedFSHandle fs =
         isolated_context->RegisterFileSystemForPath(
-            storage::kFileSystemTypeNativeLocal, std::string(), dir_.GetPath(),
+            storage::kFileSystemTypeLocal, std::string(), dir_.GetPath(),
             &base_name);
     base::FilePath root_path =
         isolated_context->CreateVirtualRootPath(fs.id()).AppendASCII(base_name);
diff --git a/content/browser/file_system_access/file_system_access_handle_base.cc b/content/browser/file_system_access/file_system_access_handle_base.cc
index 16c82f47..438d629 100644
--- a/content/browser/file_system_access/file_system_access_handle_base.cc
+++ b/content/browser/file_system_access/file_system_access_handle_base.cc
@@ -28,7 +28,7 @@
       << url_.mount_type();
 
   // We support sandboxed file system and local file systems on all platforms.
-  DCHECK(url_.type() == storage::kFileSystemTypeNativeLocal ||
+  DCHECK(url_.type() == storage::kFileSystemTypeLocal ||
          url_.type() == storage::kFileSystemTypeTemporary ||
          url_.mount_type() == storage::kFileSystemTypeExternal ||
          url_.type() == storage::kFileSystemTypeTest)
@@ -39,7 +39,7 @@
            url_.mount_type() == storage::kFileSystemTypeExternal)
         << url_.mount_type();
     if (url_.mount_type() == storage::kFileSystemTypeIsolated)
-      DCHECK_EQ(url_.type(), storage::kFileSystemTypeNativeLocal);
+      DCHECK_EQ(url_.type(), storage::kFileSystemTypeLocal);
 
     Observe(WebContentsImpl::FromRenderFrameHostID(context_.frame_id));
 
diff --git a/content/browser/file_system_access/file_system_access_manager_impl.cc b/content/browser/file_system_access/file_system_access_manager_impl.cc
index ddffad28..58efc47 100644
--- a/content/browser/file_system_access/file_system_access_manager_impl.cc
+++ b/content/browser/file_system_access/file_system_access_manager_impl.cc
@@ -569,7 +569,7 @@
                            ? FileSystemAccessHandleData::kFile
                            : FileSystemAccessHandleData::kDirectory);
 
-  if (url.type() == storage::kFileSystemTypeNativeLocal ||
+  if (url.type() == storage::kFileSystemTypeLocal ||
       url.mount_type() == storage::kFileSystemTypeExternal) {
     // A url can have mount_type = external and type = native local at the same
     // time. In that case we want to still treat it as an external path.
@@ -1179,7 +1179,7 @@
 
       FileSystemURLAndFSHandle result;
       result.file_system = isolated_context->RegisterFileSystemForPath(
-          storage::kFileSystemTypeNativeLocal, std::string(), path,
+          storage::kFileSystemTypeLocal, std::string(), path,
           &result.base_name);
 
       base::FilePath root_path =
diff --git a/content/browser/file_system_access/file_system_access_manager_impl_unittest.cc b/content/browser/file_system_access/file_system_access_manager_impl_unittest.cc
index c7d944e..41cc077c 100644
--- a/content/browser/file_system_access/file_system_access_manager_impl_unittest.cc
+++ b/content/browser/file_system_access/file_system_access_manager_impl_unittest.cc
@@ -134,7 +134,7 @@
     // path round trips as an external path, even if the path resolves to a
     // local path.
     storage::ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
-        kTestMountPoint, storage::kFileSystemTypeNativeLocal,
+        kTestMountPoint, storage::kFileSystemTypeLocal,
         storage::FileSystemMountOption(), dir_.GetPath());
 
     chrome_blob_context_ = base::MakeRefCounted<ChromeBlobStorageContext>();
@@ -737,7 +737,7 @@
   const storage::FileSystemURL& url = token->url();
   EXPECT_EQ(kTestOrigin, url.origin());
   EXPECT_EQ(kTestPath, url.path());
-  EXPECT_EQ(storage::kFileSystemTypeNativeLocal, url.type());
+  EXPECT_EQ(storage::kFileSystemTypeLocal, url.type());
   EXPECT_EQ(storage::kFileSystemTypeIsolated, url.mount_type());
   EXPECT_EQ(HandleType::kFile, token->type());
   EXPECT_EQ(ask_grant_, token->GetReadGrant());
@@ -773,7 +773,7 @@
   const storage::FileSystemURL& url = token->url();
   EXPECT_EQ(kTestOrigin, url.origin());
   EXPECT_EQ(kTestPath, url.path());
-  EXPECT_EQ(storage::kFileSystemTypeNativeLocal, url.type());
+  EXPECT_EQ(storage::kFileSystemTypeLocal, url.type());
   EXPECT_EQ(storage::kFileSystemTypeIsolated, url.mount_type());
   EXPECT_EQ(HandleType::kDirectory, token->type());
   EXPECT_EQ(ask_grant_, token->GetReadGrant());
@@ -829,7 +829,7 @@
   EXPECT_EQ(kTestOrigin, url.origin());
   EXPECT_EQ(kDirectoryPath.Append(base::FilePath::FromUTF8Unsafe(kTestName)),
             url.path());
-  EXPECT_EQ(storage::kFileSystemTypeNativeLocal, url.type());
+  EXPECT_EQ(storage::kFileSystemTypeLocal, url.type());
   EXPECT_EQ(storage::kFileSystemTypeIsolated, url.mount_type());
   EXPECT_EQ(HandleType::kFile, token->type());
   EXPECT_EQ(ask_grant_, token->GetReadGrant());
@@ -884,7 +884,7 @@
   const storage::FileSystemURL& url = token->url();
   EXPECT_EQ(kTestOrigin, url.origin());
   EXPECT_EQ(kDirectoryPath.AppendASCII(kTestName), url.path());
-  EXPECT_EQ(storage::kFileSystemTypeNativeLocal, url.type());
+  EXPECT_EQ(storage::kFileSystemTypeLocal, url.type());
   EXPECT_EQ(storage::kFileSystemTypeIsolated, url.mount_type());
   EXPECT_EQ(HandleType::kDirectory, token->type());
   EXPECT_EQ(ask_grant_, token->GetReadGrant());
diff --git a/content/browser/file_system_access/file_system_chooser_browsertest.cc b/content/browser/file_system_access/file_system_chooser_browsertest.cc
index 566d9f99..816270d1 100644
--- a/content/browser/file_system_access/file_system_chooser_browsertest.cc
+++ b/content/browser/file_system_access/file_system_chooser_browsertest.cc
@@ -55,7 +55,7 @@
     // on all platforms. We're not testing more complicated ChromeOS specific
     // file system backends here.
     storage::ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
-        kTestMountPoint, storage::kFileSystemTypeNativeLocal,
+        kTestMountPoint, storage::kFileSystemTypeLocal,
         storage::FileSystemMountOption(), temp_dir_.GetPath());
 
     ASSERT_TRUE(embedded_test_server()->Start());
diff --git a/content/browser/gpu/gpu_internals_ui.cc b/content/browser/gpu/gpu_internals_ui.cc
index 7d9f2da..9b39b8a 100644
--- a/content/browser/gpu/gpu_internals_ui.cc
+++ b/content/browser/gpu/gpu_internals_ui.cc
@@ -21,6 +21,7 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/stringize_macros.h"
 #include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
 #include "base/system/sys_info.h"
 #include "base/values.h"
 #include "build/build_config.h"
@@ -827,8 +828,13 @@
   auto dict = std::make_unique<base::DictionaryValue>();
 
   dict->SetString("version", GetContentClient()->browser()->GetProduct());
-  dict->SetString("command_line",
-      base::CommandLine::ForCurrentProcess()->GetCommandLineString());
+  base::CommandLine::StringType command_line =
+      base::CommandLine::ForCurrentProcess()->GetCommandLineString();
+#if defined(OS_WIN)
+  dict->SetString("command_line", base::WideToUTF8(command_line));
+#else
+  dict->SetString("command_line", command_line);
+#endif
   dict->SetString("operating_system",
                   base::SysInfo::OperatingSystemName() + " " +
                   base::SysInfo::OperatingSystemVersion());
diff --git a/content/browser/gpu/gpu_process_host.cc b/content/browser/gpu/gpu_process_host.cc
index d5d68aa5..a15a6e1 100644
--- a/content/browser/gpu/gpu_process_host.cc
+++ b/content/browser/gpu/gpu_process_host.cc
@@ -434,7 +434,7 @@
     policy->AddDllToUnload(L"cmsetac.dll");
 
     if (cmd_line_.HasSwitch(switches::kEnableLogging)) {
-      base::string16 log_file_path = logging::GetLogFileFullPath();
+      std::wstring log_file_path = logging::GetLogFileFullPath();
       if (!log_file_path.empty()) {
         sandbox::ResultCode result = policy->AddRule(
             sandbox::TargetPolicy::SUBSYS_FILES,
@@ -481,7 +481,7 @@
     if (UseOpenGLRenderer())
       return true;
 
-    return base::win::IsRunningUnderDesktopName(STRING16_LITERAL("winlogon"));
+    return base::win::IsRunningUnderDesktopName(L"winlogon");
   }
 
   bool enable_appcontainer_;
@@ -779,6 +779,7 @@
       message = "The info collection GPU process ";
     }
 
+    bool unexpected_exit = false;
     switch (info.status) {
       case base::TERMINATION_STATUS_NORMAL_TERMINATION:
         // Don't block offscreen contexts (and force page reload for webgl)
@@ -789,6 +790,7 @@
         break;
       case base::TERMINATION_STATUS_ABNORMAL_TERMINATION:
         message += base::StringPrintf("exited with code %d.", info.exit_code);
+        unexpected_exit = true;
         break;
       case base::TERMINATION_STATUS_PROCESS_WAS_KILLED:
         UMA_HISTOGRAM_ENUMERATION("GPU.GPUProcessTerminationOrigin",
@@ -798,6 +800,7 @@
         break;
       case base::TERMINATION_STATUS_PROCESS_CRASHED:
         message += "crashed!";
+        unexpected_exit = true;
         break;
       case base::TERMINATION_STATUS_STILL_RUNNING:
         message += "hasn't exited yet.";
@@ -805,28 +808,38 @@
 #if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
       case base::TERMINATION_STATUS_PROCESS_WAS_KILLED_BY_OOM:
         message += "was killed due to out of memory.";
+        unexpected_exit = true;
         break;
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
 #if defined(OS_ANDROID)
       case base::TERMINATION_STATUS_OOM_PROTECTED:
         message += "was protected from out of memory kill.";
+        unexpected_exit = true;
         break;
 #endif  // OS_ANDROID
       case base::TERMINATION_STATUS_LAUNCH_FAILED:
         message += "failed to start!";
+        unexpected_exit = true;
         break;
       case base::TERMINATION_STATUS_OOM:
         message += "died due to out of memory.";
+        unexpected_exit = true;
         break;
 #if defined(OS_WIN)
       case base::TERMINATION_STATUS_INTEGRITY_FAILURE:
         message += "failed integrity checks.";
+        unexpected_exit = true;
         break;
 #endif
       case base::TERMINATION_STATUS_MAX_ENUM:
         NOTREACHED();
         break;
     }
+    if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+            switches::kForceBrowserCrashOnGpuCrash)) {
+      CHECK(!unexpected_exit)
+          << "Force Chrome to crash due to unexpected GPU process crash";
+    }
     GetUIThreadTaskRunner({})->PostTask(
         FROM_HERE,
         base::BindOnce(&OnGpuProcessHostDestroyedOnUI, host_id_, message));
diff --git a/content/browser/indexed_db/indexed_db_context_impl.cc b/content/browser/indexed_db/indexed_db_context_impl.cc
index 542de51..fd11db5 100644
--- a/content/browser/indexed_db/indexed_db_context_impl.cc
+++ b/content/browser/indexed_db/indexed_db_context_impl.cc
@@ -328,7 +328,7 @@
     auto paths = std::make_unique<base::ListValue>();
     if (!is_incognito()) {
       for (const base::FilePath& path : GetStoragePaths(origin))
-        paths->AppendString(path.value());
+        paths->AppendString(path.AsUTF8Unsafe());
     } else {
       paths->AppendString("N/A");
     }
diff --git a/content/browser/indexed_db/indexed_db_internals_ui.cc b/content/browser/indexed_db/indexed_db_internals_ui.cc
index 8ba85ad..b42b2774 100644
--- a/content/browser/indexed_db/indexed_db_internals_ui.cc
+++ b/content/browser/indexed_db/indexed_db_internals_ui.cc
@@ -104,7 +104,7 @@
                                           const base::FilePath& path) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   web_ui()->CallJavascriptFunctionUnsafe("indexeddb.onOriginsReady", origins,
-                                         base::Value(path.value()));
+                                         base::Value(path.AsUTF8Unsafe()));
 }
 
 static void FindControl(const base::FilePath& partition_path,
@@ -122,10 +122,10 @@
     base::FilePath* partition_path,
     Origin* origin,
     storage::mojom::IndexedDBControl** control) {
-  base::FilePath::StringType path_string;
+  std::string path_string;
   if (!args->GetString(0, &path_string))
     return false;
-  *partition_path = base::FilePath(path_string);
+  *partition_path = base::FilePath::FromUTF8Unsafe(path_string);
 
   std::string url_string;
   if (!args->GetString(1, &url_string))
@@ -239,7 +239,7 @@
   if (!success)
     return;
 
-  const GURL url = GURL(FILE_PATH_LITERAL("file://") + zip_path.value());
+  const GURL url = GURL("file://" + zip_path.AsUTF8Unsafe());
   WebContents* web_contents = web_ui()->GetWebContents();
   net::NetworkTrafficAnnotationTag traffic_annotation =
       net::DefineNetworkTrafficAnnotation("indexed_db_internals_handler", R"(
@@ -341,7 +341,8 @@
 
   item->AddObserver(new FileDeleter(temp_path));
   web_ui()->CallJavascriptFunctionUnsafe(
-      "indexeddb.onOriginDownloadReady", base::Value(partition_path.value()),
+      "indexeddb.onOriginDownloadReady",
+      base::Value(partition_path.AsUTF8Unsafe()),
       base::Value(origin.Serialize()),
       base::Value(static_cast<double>(connection_count)));
 }
diff --git a/content/browser/installedapp/installed_app_provider_impl_win.cc b/content/browser/installedapp/installed_app_provider_impl_win.cc
index 7cf00a4..0518912 100644
--- a/content/browser/installedapp/installed_app_provider_impl_win.cc
+++ b/content/browser/installedapp/installed_app_provider_impl_win.cc
@@ -62,7 +62,7 @@
     if (FAILED(hr))
       continue;
 
-    base::string16 app_user_model_id(
+    std::wstring app_user_model_id(
         base::win::ScopedHString(app_user_model_id_native).Get());
 
     size_t windows_app_count = 0;
@@ -84,7 +84,7 @@
       // https://docs.microsoft.com/en-us/uwp/schemas/
       // appinstallerschema/element-package
       if (base::CompareCaseInsensitiveASCII(
-              related_app->id.value(), base::UTF16ToASCII(app_user_model_id)) ==
+              related_app->id.value(), base::WideToASCII(app_user_model_id)) ==
           0) {
         auto application = blink::mojom::RelatedApplication::New();
         application->platform = related_app->platform;
diff --git a/content/browser/loader/file_url_loader_factory.cc b/content/browser/loader/file_url_loader_factory.cc
index beec643..f6a3e6f 100644
--- a/content/browser/loader/file_url_loader_factory.cc
+++ b/content/browser/loader/file_url_loader_factory.cc
@@ -258,12 +258,7 @@
     if (!wrote_header_) {
       wrote_header_ = true;
 
-#if defined(OS_WIN)
-      const base::string16& title = path_.value();
-#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
-      const base::string16& title =
-          base::WideToUTF16(base::SysNativeMBToWide(path_.value()));
-#endif
+      const base::string16& title = path_.AsUTF16Unsafe();
       pending_data_.append(net::GetDirectoryListingHeader(title));
 
       // If not a top-level directory, add a link to the parent directory. To
diff --git a/content/browser/media/capture/desktop_capture_device_mac.cc b/content/browser/media/capture/desktop_capture_device_mac.cc
index af24f75..29711048 100644
--- a/content/browser/media/capture/desktop_capture_device_mac.cc
+++ b/content/browser/media/capture/desktop_capture_device_mac.cc
@@ -6,7 +6,7 @@
 
 #include <CoreGraphics/CoreGraphics.h>
 
-#include "base/threading/thread.h"
+#include "base/threading/thread_checker.h"
 #include "media/capture/video/video_capture_device.h"
 #include "ui/gfx/native_widget_types.h"
 
@@ -21,9 +21,23 @@
 
   ~DesktopCaptureDeviceMac() override = default;
 
+  static float ComputeMinFrameRate(float requested_frame_rate) {
+    // Set a minimum frame rate of 5 fps, unless the requested frame rate is
+    // even lower.
+    constexpr float kMinFrameRate = 5.f;
+
+    // Don't send frames at more than 80% the requested rate, because doing so
+    // can stochastically toggle between repeated and new frames.
+    constexpr float kRequestedFrameRateFactor = 0.8f;
+
+    return std::min(requested_frame_rate * kRequestedFrameRateFactor,
+                    kMinFrameRate);
+  }
+
   // media::VideoCaptureDevice:
   void AllocateAndStart(const media::VideoCaptureParams& params,
                         std::unique_ptr<Client> client) override {
+    DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
     DCHECK(client && !client_);
     client_ = std::move(client);
 
@@ -31,20 +45,14 @@
     requested_format_.pixel_format = media::PIXEL_FORMAT_NV12;
     DCHECK_GT(requested_format_.frame_size.GetArea(), 0);
     DCHECK_GT(requested_format_.frame_rate, 0);
+    min_frame_rate_ = ComputeMinFrameRate(requested_format_.frame_rate);
 
-    auto task_runner = base::SequencedTaskRunnerHandle::Get();
-    CGDisplayStreamFrameAvailableHandler handler = ^(
-        CGDisplayStreamFrameStatus status, uint64_t display_time,
-        IOSurfaceRef frame_surface, CGDisplayStreamUpdateRef update_ref) {
-      if (status == kCGDisplayStreamFrameStatusFrameComplete) {
-        task_runner->PostTask(
-            FROM_HERE,
-            base::BindOnce(&DesktopCaptureDeviceMac::OnReceivedIOSurface,
-                           weak_factory_.GetWeakPtr(),
-                           gfx::ScopedInUseIOSurface(
-                               frame_surface, base::scoped_policy::RETAIN)));
-      }
-    };
+    CGDisplayStreamFrameAvailableHandler handler =
+        ^(CGDisplayStreamFrameStatus status, uint64_t display_time,
+          IOSurfaceRef frame_surface, CGDisplayStreamUpdateRef update_ref) {
+          if (status == kCGDisplayStreamFrameStatusFrameComplete)
+            OnReceivedIOSurfaceFromStream(frame_surface);
+        };
 
     base::ScopedCFTypeRef<CFDictionaryRef> properties;
     {
@@ -93,6 +101,9 @@
     client_->OnStarted();
   }
   void StopAndDeAllocate() override {
+    DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+    min_frame_rate_enforcement_timer_.reset();
     weak_factory_.InvalidateWeakPtrs();
     if (display_stream_) {
       CFRunLoopRemoveSource(CFRunLoopGetCurrent(),
@@ -104,12 +115,22 @@
   }
 
  private:
-  void OnReceivedIOSurface(gfx::ScopedInUseIOSurface io_surface) {
-    // Package |io_surface| as a GpuMemoryBuffer.
+  void OnReceivedIOSurfaceFromStream(IOSurfaceRef io_surface) {
+    DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+    last_received_io_surface_.reset(io_surface, base::scoped_policy::RETAIN);
+
+    // Immediately send the new frame to the client.
+    SendLastReceivedIOSurfaceToClient();
+  }
+  void SendLastReceivedIOSurfaceToClient() {
+    DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+    // Package |last_received_io_surface_| as a GpuMemoryBuffer.
     gfx::GpuMemoryBufferHandle handle;
     handle.id.id = -1;
     handle.type = gfx::GpuMemoryBufferType::IO_SURFACE_BUFFER;
-    handle.io_surface.reset(io_surface, base::scoped_policy::RETAIN);
+    handle.io_surface.reset(last_received_io_surface_,
+                            base::scoped_policy::RETAIN);
 
     const auto now = base::TimeTicks::Now();
     if (first_frame_time_.is_null())
@@ -118,18 +139,40 @@
     client_->OnIncomingCapturedExternalBuffer(
         std::move(handle), requested_format_, gfx::ColorSpace::CreateSRGB(),
         now, now - first_frame_time_);
+
+    // Reset |min_frame_rate_enforcement_timer_|.
+    if (!min_frame_rate_enforcement_timer_) {
+      min_frame_rate_enforcement_timer_ =
+          std::make_unique<base::RepeatingTimer>(
+              FROM_HERE, base::TimeDelta::FromSecondsD(1 / min_frame_rate_),
+              base::BindRepeating(
+                  &DesktopCaptureDeviceMac::SendLastReceivedIOSurfaceToClient,
+                  weak_factory_.GetWeakPtr()));
+    }
+    min_frame_rate_enforcement_timer_->Reset();
   }
 
+  // This class assumes single threaded access.
+  THREAD_CHECKER(thread_checker_);
+
   const CGDirectDisplayID display_id_;
 
   std::unique_ptr<Client> client_;
   base::ScopedCFTypeRef<CGDisplayStreamRef> display_stream_;
   media::VideoCaptureFormat requested_format_;
+  float min_frame_rate_ = 1.f;
+  gfx::ScopedInUseIOSurface last_received_io_surface_;
 
-  // The time of the first call to OnReceivedIOSurface. Used to compute the
-  // timestamp of subsequent frames.
+  // The time of the first call to SendLastReceivedIOSurfaceToClient. Used to
+  // compute the timestamp of subsequent frames.
   base::TimeTicks first_frame_time_;
 
+  // Timer to enforce |min_frame_rate_| by repeatedly calling
+  // SendLastReceivedIOSurfaceToClient.
+  // TODO(https://crbug.com/1171127): Remove the need for the capture device
+  // to re-submit static content.
+  std::unique_ptr<base::RepeatingTimer> min_frame_rate_enforcement_timer_;
+
   base::WeakPtrFactory<DesktopCaptureDeviceMac> weak_factory_;
   DISALLOW_COPY_AND_ASSIGN(DesktopCaptureDeviceMac);
 };
diff --git a/content/browser/media/frameless_media_interface_proxy.cc b/content/browser/media/frameless_media_interface_proxy.cc
index 161e2437..2582c4af 100644
--- a/content/browser/media/frameless_media_interface_proxy.cc
+++ b/content/browser/media/frameless_media_interface_proxy.cc
@@ -73,6 +73,15 @@
         renderer_extension_receiver) {}
 #endif  // defined(OS_ANDROID)
 
+#if defined(OS_WIN)
+// Unimplemented method as this requires CDM and media::Renderer services with
+// frame context.
+void FramelessMediaInterfaceProxy::CreateMediaFoundationRenderer(
+    mojo::PendingReceiver<media::mojom::Renderer> receiver,
+    mojo::PendingReceiver<media::mojom::MediaFoundationRendererExtension>
+        renderer_extension_receiver) {}
+#endif  // defined(OS_WIN)
+
 void FramelessMediaInterfaceProxy::CreateCdm(const std::string& key_system,
                                              const media::CdmConfig& cdm_config,
                                              CreateCdmCallback callback) {
diff --git a/content/browser/media/frameless_media_interface_proxy.h b/content/browser/media/frameless_media_interface_proxy.h
index bdb094bd..7198bc6 100644
--- a/content/browser/media/frameless_media_interface_proxy.h
+++ b/content/browser/media/frameless_media_interface_proxy.h
@@ -26,6 +26,9 @@
 // This implements the media::mojom::InterfaceFactory interface for a
 // RenderProcessHostImpl. It does not support creating services that require a
 // frame context (ie. CDMs and renderers).
+// It is used in cases without a frame context, e.g. WebRTC's
+// RTCVideoDecoderFactory to create hardware video decoders using
+// MojoVideoDecoder, and WebCodecs audio/video decoding in workers.
 class CONTENT_EXPORT FramelessMediaInterfaceProxy final
     : public media::mojom::InterfaceFactory {
  public:
@@ -60,6 +63,12 @@
           client_extension,
       mojo::PendingReceiver<media::mojom::Renderer> receiver) final;
 #endif  // defined(OS_ANDROID)
+#if defined(OS_WIN)
+  void CreateMediaFoundationRenderer(
+      mojo::PendingReceiver<media::mojom::Renderer> receiver,
+      mojo::PendingReceiver<media::mojom::MediaFoundationRendererExtension>
+          renderer_extension_receiver) final;
+#endif  // defined(OS_WIN)
   void CreateCdm(const std::string& key_system,
                  const media::CdmConfig& cdm_config,
                  CreateCdmCallback callback) final;
diff --git a/content/browser/media/media_interface_proxy.cc b/content/browser/media/media_interface_proxy.cc
index 4efe080..f753117 100644
--- a/content/browser/media/media_interface_proxy.cc
+++ b/content/browser/media/media_interface_proxy.cc
@@ -450,6 +450,20 @@
 }
 #endif
 
+#if defined(OS_WIN)
+void MediaInterfaceProxy::CreateMediaFoundationRenderer(
+    mojo::PendingReceiver<media::mojom::Renderer> receiver,
+    mojo::PendingReceiver<media::mojom::MediaFoundationRendererExtension>
+        renderer_extension_receiver) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  DVLOG(1) << __func__ << ": this=" << this;
+
+  // TODO(frankli): Add code to create MediaFoundationRenderer in the "mf_cdm"
+  // sandbox process.
+  NOTIMPLEMENTED();
+}
+#endif  // defined(OS_WIN)
+
 void MediaInterfaceProxy::CreateCdm(const std::string& key_system,
                                     const media::CdmConfig& cdm_config,
                                     CreateCdmCallback callback) {
diff --git a/content/browser/media/media_interface_proxy.h b/content/browser/media/media_interface_proxy.h
index 905ac9f0..86242a9 100644
--- a/content/browser/media/media_interface_proxy.h
+++ b/content/browser/media/media_interface_proxy.h
@@ -76,6 +76,12 @@
       mojo::PendingReceiver<media::mojom::MediaPlayerRendererExtension>
           renderer_extension_request) final;
 #endif  // defined(OS_ANDROID)
+#if defined(OS_WIN)
+  void CreateMediaFoundationRenderer(
+      mojo::PendingReceiver<media::mojom::Renderer> receiver,
+      mojo::PendingReceiver<media::mojom::MediaFoundationRendererExtension>
+          renderer_extension_receiver) final;
+#endif  // defined(OS_WIN)
   void CreateCdm(const std::string& key_system,
                  const media::CdmConfig& cdm_config,
                  CreateCdmCallback callback) final;
diff --git a/content/browser/network_service_instance_impl.cc b/content/browser/network_service_instance_impl.cc
index 02cb492..4baa69f 100644
--- a/content/browser/network_service_instance_impl.cc
+++ b/content/browser/network_service_instance_impl.cc
@@ -370,7 +370,7 @@
 #if defined(OS_WIN)
           // base::Environment returns environment variables in UTF-8 on
           // Windows.
-          ssl_key_log_path = base::FilePath(base::UTF8ToUTF16(env_str));
+          ssl_key_log_path = base::FilePath(base::UTF8ToWide(env_str));
 #else
           ssl_key_log_path = base::FilePath(env_str);
 #endif
diff --git a/content/browser/ppapi_plugin_process_host.cc b/content/browser/ppapi_plugin_process_host.cc
index ae0a8f3..4ee1f46e 100644
--- a/content/browser/ppapi_plugin_process_host.cc
+++ b/content/browser/ppapi_plugin_process_host.cc
@@ -92,7 +92,7 @@
         return false;
     }
 #endif  // !defined(NACL_WIN64)
-    const base::string16& sid =
+    const std::wstring& sid =
         browser_client->GetAppContainerSidForSandboxType(GetSandboxType());
     if (!sid.empty())
       sandbox::policy::SandboxWin::AddAppContainerPolicy(policy, sid.c_str());
diff --git a/content/browser/renderer_host/dwrite_font_file_util_win.cc b/content/browser/renderer_host/dwrite_font_file_util_win.cc
index b998954..6958f633 100644
--- a/content/browser/renderer_host/dwrite_font_file_util_win.cc
+++ b/content/browser/renderer_host/dwrite_font_file_util_win.cc
@@ -12,13 +12,14 @@
 #include "base/metrics/histogram_functions.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
 #include "base/trace_event/trace_event.h"
 #include "content/browser/renderer_host/dwrite_font_uma_logging_win.h"
 
 namespace content {
 
 HRESULT FontFilePathAndTtcIndex(IDWriteFont* font,
-                                base::string16& file_path,
+                                std::wstring& file_path,
                                 uint32_t& ttc_index) {
   Microsoft::WRL::ComPtr<IDWriteFontFace> font_face;
   HRESULT hr;
@@ -34,7 +35,7 @@
 }
 
 HRESULT FontFilePathAndTtcIndex(IDWriteFontFace* font_face,
-                                base::string16& file_path,
+                                std::wstring& file_path,
                                 uint32_t& ttc_index) {
   TRACE_EVENT0("dwrite,fonts",
                "dwrite_font_file_util::FontFilePathAndTtcIndex");
@@ -114,7 +115,7 @@
         MessageFilterError::ADD_LOCAL_FILE_GET_PATH_LENGTH_FAILED);
     return hr;
   }
-  base::string16 retrieve_file_path;
+  std::wstring retrieve_file_path;
   retrieve_file_path.resize(
       ++path_length);  // Reserve space for the null terminator.
   hr = local_loader->GetFilePathFromKey(key, key_size, &retrieve_file_path[0],
@@ -139,16 +140,17 @@
 
 HRESULT AddFilesForFont(IDWriteFont* font,
                         const base::string16& windows_fonts_path,
-                        std::set<base::string16>* path_set,
-                        std::set<base::string16>* custom_font_path_set,
+                        std::set<std::wstring>* path_set,
+                        std::set<std::wstring>* custom_font_path_set,
                         uint32_t* ttc_index) {
-  base::string16 file_path;
+  std::wstring file_path;
   HRESULT hr = FontFilePathAndTtcIndex(font, file_path, *ttc_index);
   if (FAILED(hr)) {
     return hr;
   }
 
-  base::string16 file_path_folded = base::i18n::FoldCase(file_path);
+  base::string16 file_path_folded =
+      base::i18n::FoldCase(base::WideToUTF16(file_path));
 
   if (!file_path_folded.size())
     return kErrorFontFileUtilEmptyFilePath;
@@ -165,14 +167,14 @@
 }
 
 base::string16 GetWindowsFontsPath() {
-  std::vector<base::char16> font_path_chars;
+  std::vector<wchar_t> font_path_chars;
   // SHGetSpecialFolderPath requires at least MAX_PATH characters.
   font_path_chars.resize(MAX_PATH);
   BOOL result = SHGetSpecialFolderPath(nullptr /* hwndOwner - reserved */,
                                        font_path_chars.data(), CSIDL_FONTS,
                                        FALSE /* fCreate */);
   DCHECK(result);
-  return base::i18n::FoldCase(font_path_chars.data());
+  return base::i18n::FoldCase(base::AsStringPiece16(font_path_chars.data()));
 }
 
 }  // namespace content
diff --git a/content/browser/renderer_host/dwrite_font_file_util_win.h b/content/browser/renderer_host/dwrite_font_file_util_win.h
index 4732144..809808c 100644
--- a/content/browser/renderer_host/dwrite_font_file_util_win.h
+++ b/content/browser/renderer_host/dwrite_font_file_util_win.h
@@ -30,8 +30,8 @@
                                 uint32_t& ttc_index);
 HRESULT AddFilesForFont(IDWriteFont* font,
                         const base::string16& windows_fonts_path,
-                        std::set<base::string16>* path_set,
-                        std::set<base::string16>* custom_font_path_set,
+                        std::set<std::wstring>* path_set,
+                        std::set<std::wstring>* custom_font_path_set,
                         uint32_t* ttc_index);
 
 base::string16 GetWindowsFontsPath();
diff --git a/content/browser/renderer_host/dwrite_font_lookup_table_builder_win.cc b/content/browser/renderer_host/dwrite_font_lookup_table_builder_win.cc
index b468671..3b3913c 100644
--- a/content/browser/renderer_host/dwrite_font_lookup_table_builder_win.cc
+++ b/content/browser/renderer_host/dwrite_font_lookup_table_builder_win.cc
@@ -96,7 +96,7 @@
     // https://dxr.mozilla.org/mozilla-central/source/gfx/thebes/gfxDWriteFontList.cpp#90
     // so we'll assume that.
     localized_strings->push_back(base::UTF16ToUTF8(
-        base::i18n::FoldCase(base::string16(localized_name))));
+        base::i18n::FoldCase(base::WideToUTF16(localized_name))));
   }
   return true;
 }
@@ -206,7 +206,7 @@
   DCHECK(dwrite_version_info);
 
   std::string dwrite_version =
-      base::WideToUTF8(dwrite_version_info->product_version());
+      base::UTF16ToUTF8(dwrite_version_info->product_version());
 
   std::string to_hash = dwrite_version;
 
@@ -490,8 +490,8 @@
     if (font->GetSimulations() != DWRITE_FONT_SIMULATIONS_NONE)
       continue;
 
-    std::set<base::string16> path_set;
-    std::set<base::string16> custom_font_path_set;
+    std::set<std::wstring> path_set;
+    std::set<std::wstring> custom_font_path_set;
     uint32_t ttc_index = 0;
     {
       base::ScopedBlockingCall scoped_blocking_call(
diff --git a/content/browser/renderer_host/dwrite_font_proxy_impl_win.cc b/content/browser/renderer_host/dwrite_font_proxy_impl_win.cc
index 7f1710b..d0c33c6f 100644
--- a/content/browser/renderer_host/dwrite_font_proxy_impl_win.cc
+++ b/content/browser/renderer_host/dwrite_font_proxy_impl_win.cc
@@ -52,7 +52,7 @@
     L"Segoe UI", L"Calibri", L"Times New Roman", L"Courier New"};
 
 struct RequiredFontStyle {
-  const wchar_t* family_name;
+  const base::char16* family_name;
   DWRITE_FONT_WEIGHT required_weight;
   DWRITE_FONT_STRETCH required_stretch;
   DWRITE_FONT_STYLE required_style;
@@ -61,12 +61,12 @@
 const RequiredFontStyle kRequiredStyles[] = {
     // The regular version of Gill Sans is actually in the Gill Sans MT family,
     // and the Gill Sans family typically contains just the ultra-bold styles.
-    {L"gill sans", DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STRETCH_NORMAL,
-     DWRITE_FONT_STYLE_NORMAL},
-    {L"helvetica", DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STRETCH_NORMAL,
-     DWRITE_FONT_STYLE_NORMAL},
-    {L"open sans", DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STRETCH_NORMAL,
-     DWRITE_FONT_STYLE_NORMAL},
+    {STRING16_LITERAL("gill sans"), DWRITE_FONT_WEIGHT_NORMAL,
+     DWRITE_FONT_STRETCH_NORMAL, DWRITE_FONT_STYLE_NORMAL},
+    {STRING16_LITERAL("helvetica"), DWRITE_FONT_WEIGHT_NORMAL,
+     DWRITE_FONT_STRETCH_NORMAL, DWRITE_FONT_STYLE_NORMAL},
+    {STRING16_LITERAL("open sans"), DWRITE_FONT_WEIGHT_NORMAL,
+     DWRITE_FONT_STRETCH_NORMAL, DWRITE_FONT_STYLE_NORMAL},
 };
 
 // As a workaround for crbug.com/635932, refuse to load some common fonts that
@@ -137,8 +137,8 @@
   if (collection_) {
     BOOL exists = FALSE;
     UINT32 index = UINT32_MAX;
-    HRESULT hr =
-        collection_->FindFamilyName(family_name.data(), &index, &exists);
+    HRESULT hr = collection_->FindFamilyName(base::as_wcstr(family_name),
+                                             &index, &exists);
     if (SUCCEEDED(hr) && exists &&
         CheckRequiredStylesPresent(collection_.Get(), family_name, index)) {
       family_index = index;
@@ -178,8 +178,8 @@
 
   size_t string_count = localized_names->GetCount();
 
-  std::vector<base::char16> locale;
-  std::vector<base::char16> name;
+  std::vector<wchar_t> locale;
+  std::vector<wchar_t> name;
   std::vector<blink::mojom::DWriteStringPairPtr> family_names;
   for (size_t index = 0; index < string_count; ++index) {
     UINT32 length = 0;
@@ -208,8 +208,8 @@
     }
     CHECK_EQ(L'\0', name[length - 1]);
 
-    family_names.emplace_back(base::in_place, base::string16(locale.data()),
-                              base::string16(name.data()));
+    family_names.emplace_back(base::in_place, base::WideToUTF16(locale.data()),
+                              base::WideToUTF16(name.data()));
   }
   std::move(callback).Run(std::move(family_names));
 }
@@ -235,8 +235,8 @@
 
   UINT32 font_count = family->GetFontCount();
 
-  std::set<base::string16> path_set;
-  std::set<base::string16> custom_font_path_set;
+  std::set<std::wstring> path_set;
+  std::set<std::wstring> custom_font_path_set;
   // Iterate through all the fonts in the family, and all the files for those
   // fonts. If anything goes wrong, bail on the entire family to avoid having
   // a partially-loaded font family.
@@ -264,7 +264,7 @@
   // as file handles. The renderer would be unable to open the files directly
   // due to sandbox policy (it would get ERROR_ACCESS_DENIED instead). Passing
   // handles allows the renderer to bypass the restriction and use the fonts.
-  for (const base::string16& custom_font_path : custom_font_path_set) {
+  for (const auto& custom_font_path : custom_font_path_set) {
     // Specify FLAG_EXCLUSIVE_WRITE to prevent base::File from opening the file
     // with FILE_SHARE_WRITE access. FLAG_EXCLUSIVE_WRITE doesn't actually open
     // the file for write access.
@@ -311,14 +311,15 @@
 
   mswr::ComPtr<IDWriteNumberSubstitution> number_substitution;
   if (FAILED(factory2_->CreateNumberSubstitution(
-          DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE, locale_name.c_str(),
+          DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE, base::as_wcstr(locale_name),
           TRUE /* ignoreUserOverride */, &number_substitution))) {
     DCHECK(false);
     return;
   }
   mswr::ComPtr<IDWriteTextAnalysisSource> analysis_source;
   if (FAILED(gfx::win::TextAnalysisSource::Create(
-          &analysis_source, text, locale_name, number_substitution.Get(),
+          &analysis_source, base::UTF16ToWide(text),
+          base::UTF16ToWide(locale_name), number_substitution.Get(),
           static_cast<DWRITE_READING_DIRECTION>(reading_direction)))) {
     DCHECK(false);
     return;
@@ -331,7 +332,7 @@
                                          DWRITE_FONT_WEIGHT_NORMAL));
   if (FAILED(font_fallback_->MapCharacters(
           analysis_source.Get(), 0, text.length(), collection_.Get(),
-          base_family_name.c_str(),
+          base::as_wcstr(base_family_name),
           static_cast<DWRITE_FONT_WEIGHT>(font_style->font_weight),
           static_cast<DWRITE_FONT_STYLE>(font_style->font_slant),
           static_cast<DWRITE_FONT_STRETCH>(font_style->font_stretch),
@@ -360,7 +361,7 @@
   result->font_style->font_stretch = mapped_font->GetStretch();
   result->font_style->font_weight = mapped_font->GetWeight();
 
-  std::vector<base::char16> name;
+  std::vector<wchar_t> name;
   size_t name_count = family_names->GetCount();
   for (size_t name_index = 0; name_index < name_count; name_index++) {
     UINT32 name_length = 0;
diff --git a/content/browser/renderer_host/media/media_stream_ui_proxy.cc b/content/browser/renderer_host/media/media_stream_ui_proxy.cc
index 28adfe5..d396351 100644
--- a/content/browser/renderer_host/media/media_stream_ui_proxy.cc
+++ b/content/browser/renderer_host/media/media_stream_ui_proxy.cc
@@ -99,6 +99,10 @@
   // cancel media requests.
   base::WeakPtrFactory<Core> weak_factory_{this};
 
+  // Used for calls supplied to `ui_`. Invalidated every time a new UI is
+  // created.
+  base::WeakPtrFactory<Core> weak_factory_for_ui_{this};
+
   DISALLOW_COPY_AND_ASSIGN(Core);
 };
 
@@ -144,20 +148,22 @@
     std::vector<DesktopMediaID> screen_share_ids) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
-  // base::Unretained is safe here because |ui_| is owned by Core.
+  if (!ui_)
+    return;
+
   MediaStreamUI::SourceCallback device_change_cb;
   if (has_source_callback) {
-    device_change_cb = base::BindRepeating(
-        &Core::ProcessChangeSourceRequestFromUI, base::Unretained(this));
+    device_change_cb =
+        base::BindRepeating(&Core::ProcessChangeSourceRequestFromUI,
+                            weak_factory_for_ui_.GetWeakPtr());
   }
 
-  if (ui_) {
-    *window_id = ui_->OnStarted(
-        base::BindOnce(&Core::ProcessStopRequestFromUI, base::Unretained(this)),
-        device_change_cb, label, screen_share_ids,
-        base::BindRepeating(&Core::ProcessStateChangeFromUI,
-                            base::Unretained(this)));
-  }
+  *window_id =
+      ui_->OnStarted(base::BindOnce(&Core::ProcessStopRequestFromUI,
+                                    weak_factory_for_ui_.GetWeakPtr()),
+                     device_change_cb, label, screen_share_ids,
+                     base::BindRepeating(&Core::ProcessStateChangeFromUI,
+                                         weak_factory_for_ui_.GetWeakPtr()));
 }
 
 void MediaStreamUIProxy::Core::OnDeviceStopped(const std::string& label,
@@ -197,13 +203,12 @@
     result = blink::mojom::MediaStreamRequestResult::PERMISSION_DENIED;
 
   if (stream_ui) {
-    // Default TabSharingUIViews always calls stop callback when destroyed
-    // due to security reasons (see crbug.com/1155426 for details).
-    // However here the UI is replaced while the screencast is ongoing.
-    // Clearing the callback here ensures that screencast is not terminated.
-    if (ui_) {
-      ui_->SetStopCallback(base::DoNothing());
-    }
+    // Callbacks that were supplied to the existing `ui_` are no longer
+    // applicable. This is important as some implementions (TabSharingUIViews)
+    // always run the callback when destroyed. However at the point the UI is
+    // replaced while the screencast is ongoing. Invalidating ensures the
+    // screencast is not terminated. See crbug.com/1155426 for details.
+    weak_factory_for_ui_.InvalidateWeakPtrs();
     ui_ = std::move(stream_ui);
   }
 
diff --git a/content/browser/renderer_host/media/media_stream_ui_proxy_unittest.cc b/content/browser/renderer_host/media/media_stream_ui_proxy_unittest.cc
index 8041fbb..abe6bf3 100644
--- a/content/browser/renderer_host/media/media_stream_ui_proxy_unittest.cc
+++ b/content/browser/renderer_host/media/media_stream_ui_proxy_unittest.cc
@@ -81,7 +81,6 @@
   MOCK_METHOD2(MockOnStarted,
                gfx::NativeViewId(base::RepeatingClosure stop,
                                  MediaStreamUI::SourceCallback source));
-  MOCK_METHOD1(SetStopCallback, void(base::OnceClosure));
 };
 
 class MockStopStreamHandler {
diff --git a/content/browser/renderer_host/pepper/pepper_file_io_host.cc b/content/browser/renderer_host/pepper/pepper_file_io_host.cc
index 90caac4..dbf0c9d7 100644
--- a/content/browser/renderer_host/pepper/pepper_file_io_host.cc
+++ b/content/browser/renderer_host/pepper/pepper_file_io_host.cc
@@ -194,7 +194,7 @@
     // Whitelist the supported ones.
     if (file_system_url_.mount_type() == storage::kFileSystemTypeExternal) {
       switch (file_system_url_.type()) {
-        case storage::kFileSystemTypeNativeMedia:
+        case storage::kFileSystemTypeLocalMedia:
         case storage::kFileSystemTypeDeviceMedia:
           break;
         default:
diff --git a/content/browser/renderer_host/render_frame_host_android.cc b/content/browser/renderer_host/render_frame_host_android.cc
index cb08d98..f9b3317b 100644
--- a/content/browser/renderer_host/render_frame_host_android.cc
+++ b/content/browser/renderer_host/render_frame_host_android.cc
@@ -11,13 +11,14 @@
 #include "base/android/unguessable_token_android.h"
 #include "base/bind.h"
 #include "base/check_op.h"
+#include "content/browser/bad_message.h"
 #include "content/browser/renderer_host/render_frame_host_delegate.h"
 #include "content/browser/renderer_host/render_frame_host_impl.h"
 #include "content/public/android/content_jni_headers/RenderFrameHostImpl_jni.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/site_instance.h"
-#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "services/service_manager/public/cpp/interface_provider.h"
 #include "third_party/blink/public/mojom/feature_policy/feature_policy_feature.mojom-shared.h"
 #include "third_party/blink/public/mojom/frame/user_activation_notification_type.mojom.h"
 #include "url/origin.h"
@@ -61,11 +62,8 @@
 }
 
 RenderFrameHostAndroid::RenderFrameHostAndroid(
-    RenderFrameHostImpl* render_frame_host,
-    mojo::PendingRemote<service_manager::mojom::InterfaceProvider>
-        interface_provider_remote)
-    : render_frame_host_(render_frame_host),
-      interface_provider_remote_(std::move(interface_provider_remote)) {}
+    RenderFrameHostImpl* render_frame_host)
+    : render_frame_host_(render_frame_host) {}
 
 RenderFrameHostAndroid::~RenderFrameHostAndroid() {
   // Avoid unnecessarily creating the java object from the destructor.
@@ -89,7 +87,7 @@
     ScopedJavaLocalRef<jobject> local_ref = Java_RenderFrameHostImpl_create(
         env, reinterpret_cast<intptr_t>(this),
         render_frame_host_->delegate()->GetJavaRenderFrameHostDelegate(),
-        is_incognito, interface_provider_remote_.PassPipe().release().value());
+        is_incognito);
     obj_ = JavaObjectWeakGlobalRef(env, local_ref);
     return local_ref;
   }
@@ -147,6 +145,27 @@
   return render_frame_host_->IsRenderFrameCreated();
 }
 
+void RenderFrameHostAndroid::GetInterfaceToRendererFrame(
+    JNIEnv* env,
+    const base::android::JavaParamRef<jobject>&,
+    const base::android::JavaParamRef<jstring>& interface_name,
+    jint message_pipe_raw_handle) const {
+  DCHECK(render_frame_host_->IsRenderFrameCreated());
+  render_frame_host_->GetRemoteInterfaces()->GetInterfaceByName(
+      ConvertJavaStringToUTF8(env, interface_name),
+      mojo::ScopedMessagePipeHandle(
+          mojo::MessagePipeHandle(message_pipe_raw_handle)));
+}
+
+void RenderFrameHostAndroid::TerminateRendererDueToBadMessage(
+    JNIEnv* env,
+    const base::android::JavaParamRef<jobject>&,
+    jint reason) const {
+  DCHECK_LT(reason, bad_message::BAD_MESSAGE_MAX);
+  ReceivedBadMessage(render_frame_host_->GetProcess(),
+                     static_cast<bad_message::BadMessageReason>(reason));
+}
+
 jboolean RenderFrameHostAndroid::IsProcessBlocked(
     JNIEnv* env,
     const base::android::JavaParamRef<jobject>&) const {
diff --git a/content/browser/renderer_host/render_frame_host_android.h b/content/browser/renderer_host/render_frame_host_android.h
index 3dbbe73..7b70f6b 100644
--- a/content/browser/renderer_host/render_frame_host_android.h
+++ b/content/browser/renderer_host/render_frame_host_android.h
@@ -16,8 +16,6 @@
 #include "base/macros.h"
 #include "base/supports_user_data.h"
 #include "content/common/content_export.h"
-#include "mojo/public/cpp/bindings/pending_remote.h"
-#include "services/service_manager/public/mojom/interface_provider.mojom.h"
 
 namespace content {
 
@@ -28,10 +26,7 @@
 // native counterpart.
 class RenderFrameHostAndroid : public base::SupportsUserData::Data {
  public:
-  RenderFrameHostAndroid(
-      RenderFrameHostImpl* render_frame_host,
-      mojo::PendingRemote<service_manager::mojom::InterfaceProvider>
-          interface_provider_remote);
+  RenderFrameHostAndroid(RenderFrameHostImpl* render_frame_host);
   ~RenderFrameHostAndroid() override;
 
   base::android::ScopedJavaLocalRef<jobject> GetJavaObject();
@@ -66,6 +61,17 @@
       JNIEnv* env,
       const base::android::JavaParamRef<jobject>&) const;
 
+  void GetInterfaceToRendererFrame(
+      JNIEnv* env,
+      const base::android::JavaParamRef<jobject>&,
+      const base::android::JavaParamRef<jstring>& interface_name,
+      jint message_pipe_handle) const;
+
+  void TerminateRendererDueToBadMessage(
+      JNIEnv* env,
+      const base::android::JavaParamRef<jobject>&,
+      jint reason) const;
+
   jboolean IsProcessBlocked(JNIEnv* env,
                             const base::android::JavaParamRef<jobject>&) const;
 
@@ -85,8 +91,6 @@
 
  private:
   RenderFrameHostImpl* const render_frame_host_;
-  mojo::PendingRemote<service_manager::mojom::InterfaceProvider>
-      interface_provider_remote_;
   JavaObjectWeakGlobalRef obj_;
 
   DISALLOW_COPY_AND_ASSIGN(RenderFrameHostAndroid);
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc
index 0365c6c..0300a4fa 100644
--- a/content/browser/renderer_host/render_frame_host_impl.cc
+++ b/content/browser/renderer_host/render_frame_host_impl.cc
@@ -8503,48 +8503,13 @@
 }
 
 #if defined(OS_ANDROID)
-
-class RenderFrameHostImpl::JavaInterfaceProvider
-    : public service_manager::mojom::InterfaceProvider {
- public:
-  using BindCallback =
-      base::RepeatingCallback<void(const std::string&,
-                                   mojo::ScopedMessagePipeHandle)>;
-
-  JavaInterfaceProvider(
-      const BindCallback& bind_callback,
-      mojo::PendingReceiver<service_manager::mojom::InterfaceProvider> receiver)
-      : bind_callback_(bind_callback), receiver_(this, std::move(receiver)) {}
-  ~JavaInterfaceProvider() override = default;
-
- private:
-  // service_manager::mojom::InterfaceProvider:
-  void GetInterface(const std::string& interface_name,
-                    mojo::ScopedMessagePipeHandle handle) override {
-    bind_callback_.Run(interface_name, std::move(handle));
-  }
-
-  const BindCallback bind_callback_;
-  mojo::Receiver<service_manager::mojom::InterfaceProvider> receiver_;
-
-  DISALLOW_COPY_AND_ASSIGN(JavaInterfaceProvider);
-};
-
 base::android::ScopedJavaLocalRef<jobject>
 RenderFrameHostImpl::GetJavaRenderFrameHost() {
   RenderFrameHostAndroid* render_frame_host_android =
       static_cast<RenderFrameHostAndroid*>(
           GetUserData(kRenderFrameHostAndroidKey));
   if (!render_frame_host_android) {
-    mojo::PendingRemote<service_manager::mojom::InterfaceProvider>
-        interface_provider_remote;
-    java_interface_registry_ = std::make_unique<JavaInterfaceProvider>(
-        base::BindRepeating(
-            &RenderFrameHostImpl::ForwardGetInterfaceToRenderFrame,
-            weak_ptr_factory_.GetWeakPtr()),
-        interface_provider_remote.InitWithNewPipeAndPassReceiver());
-    render_frame_host_android =
-        new RenderFrameHostAndroid(this, std::move(interface_provider_remote));
+    render_frame_host_android = new RenderFrameHostAndroid(this);
     SetUserData(kRenderFrameHostAndroidKey,
                 base::WrapUnique(render_frame_host_android));
   }
@@ -8562,12 +8527,6 @@
   }
   return java_interfaces_.get();
 }
-
-void RenderFrameHostImpl::ForwardGetInterfaceToRenderFrame(
-    const std::string& interface_name,
-    mojo::ScopedMessagePipeHandle pipe) {
-  GetRemoteInterfaces()->GetInterfaceByName(interface_name, std::move(pipe));
-}
 #endif
 
 void RenderFrameHostImpl::ForEachImmediateLocalRoot(
diff --git a/content/browser/renderer_host/render_frame_host_impl.h b/content/browser/renderer_host/render_frame_host_impl.h
index 2330ad2..ccbfe9e 100644
--- a/content/browser/renderer_host/render_frame_host_impl.h
+++ b/content/browser/renderer_host/render_frame_host_impl.h
@@ -2105,11 +2105,6 @@
       const url::Origin& frame_origin,
       net::IsolationInfo::RequestType request_type) const;
 
-#if defined(OS_ANDROID)
-  void ForwardGetInterfaceToRenderFrame(const std::string& interface_name,
-                                        mojo::ScopedMessagePipeHandle pipe);
-#endif
-
   // mojom::FrameHost:
   void CreateNewWindow(mojom::CreateNewWindowParamsPtr params,
                        CreateNewWindowCallback callback) override;
@@ -3121,12 +3116,6 @@
   // this RenderFrameHost. This provides access to interfaces implemented in
   // Java in the browser process to C++ code in the browser process.
   std::unique_ptr<service_manager::InterfaceProvider> java_interfaces_;
-
-  // An InterfaceRegistry that forwards interface requests from Java to the
-  // RenderFrame. This provides access to interfaces implemented in the renderer
-  // to Java code in the browser process.
-  class JavaInterfaceProvider;
-  std::unique_ptr<JavaInterfaceProvider> java_interface_registry_;
 #endif
 
   // BrowserInterfaceBroker implementation through which this
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index c31df2f..347adca 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -411,7 +411,7 @@
   bool PreSpawnTarget(sandbox::TargetPolicy* policy) override {
     sandbox::policy::SandboxWin::AddBaseHandleClosePolicy(policy);
 
-    const base::string16& sid =
+    const std::wstring& sid =
         GetContentClient()->browser()->GetAppContainerSidForSandboxType(
             GetSandboxType());
     if (!sid.empty())
diff --git a/content/browser/speech/tts_win.cc b/content/browser/speech/tts_win.cc
index 80d9e56..e41cab82 100644
--- a/content/browser/speech/tts_win.cc
+++ b/content/browser/speech/tts_win.cc
@@ -262,12 +262,12 @@
     // The TTS api allows a range of -10 to 10 for speech pitch:
     // https://docs.microsoft.com/en-us/previous-versions/windows/desktop/ms720500(v%3Dvs.85)
     // Note that the API requires an integer value, so be sure to cast the pitch
-    // value to an int before calling NumberToString16. TODO(dtseng): cleanup if
+    // value to an int before calling NumberToWString. TODO(dtseng): cleanup if
     // we ever use any other properties that require xml.
     double adjusted_pitch =
         std::max<double>(-10, std::min<double>(params.pitch * 10 - 10, 10));
     std::wstring adjusted_pitch_string =
-        base::NumberToString16(static_cast<int>(adjusted_pitch));
+        base::NumberToWString(static_cast<int>(adjusted_pitch));
     prefix = L"<pitch absmiddle=\"" + adjusted_pitch_string + L"\">";
     suffix = L"</pitch>";
   }
diff --git a/content/browser/storage_partition_impl.cc b/content/browser/storage_partition_impl.cc
index 50bec12b..e3628104 100644
--- a/content/browser/storage_partition_impl.cc
+++ b/content/browser/storage_partition_impl.cc
@@ -1222,7 +1222,8 @@
 
   cache_storage_context_ = base::MakeRefCounted<CacheStorageContextImpl>();
   cache_storage_context_->Init(
-      path, browser_context_->GetSpecialStoragePolicy(), quota_manager_proxy);
+      path, browser_context_->GetSpecialStoragePolicy(), quota_manager_proxy,
+      ChromeBlobStorageContext::GetRemoteFor(browser_context_));
   cache_storage_context_->Bind(
       cache_storage_control_.BindNewPipeAndPassReceiver());
 
diff --git a/content/browser/storage_partition_impl_map.cc b/content/browser/storage_partition_impl_map.cc
index 865eeae4..e7eee25 100644
--- a/content/browser/storage_partition_impl_map.cc
+++ b/content/browser/storage_partition_impl_map.cc
@@ -469,9 +469,6 @@
 
   // Check first to avoid memory leak in unittests.
   if (BrowserThread::IsThreadInitialized(BrowserThread::IO)) {
-    partition->GetCacheStorageContext()->SetBlobParametersForCache(
-        ChromeBlobStorageContext::GetFor(browser_context_));
-
     // Use PostTask() instead of RunOrPostTaskOnThread() because not posting a
     // task causes it to run before the CacheStorageManager has been
     // initialized, and then CacheStorageContextImpl::CacheManager() ends up
diff --git a/content/browser/tracing/tracing_controller_impl.cc b/content/browser/tracing/tracing_controller_impl.cc
index 4f938ad..4d2ee92 100644
--- a/content/browser/tracing/tracing_controller_impl.cc
+++ b/content/browser/tracing/tracing_controller_impl.cc
@@ -19,6 +19,7 @@
 #include "base/run_loop.h"
 #include "base/strings/string_split.h"
 #include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
 #include "base/system/sys_info.h"
 #include "base/time/time.h"
 #include "base/trace_event/trace_config.h"
@@ -340,9 +341,13 @@
   metadata_dict->SetBoolean("highres-ticks",
                             base::TimeTicks::IsHighResolution());
 
-  metadata_dict->SetString(
-      "command_line",
-      base::CommandLine::ForCurrentProcess()->GetCommandLineString());
+  base::CommandLine::StringType command_line =
+      base::CommandLine::ForCurrentProcess()->GetCommandLineString();
+#if defined(OS_WIN)
+  metadata_dict->SetString("command_line", base::WideToUTF16(command_line));
+#else
+  metadata_dict->SetString("command_line", command_line);
+#endif
 
   base::Time::Exploded ctime;
   TRACE_TIME_NOW().UTCExplode(&ctime);
diff --git a/content/browser/web_contents/web_contents_view_aura.cc b/content/browser/web_contents/web_contents_view_aura.cc
index 6c527e2..4bdcc50 100644
--- a/content/browser/web_contents/web_contents_view_aura.cc
+++ b/content/browser/web_contents/web_contents_view_aura.cc
@@ -190,12 +190,9 @@
   std::string default_name =
       GetContentClient()->browser()->GetDefaultDownloadName();
   base::FilePath generated_download_file_name =
-      net::GenerateFileName(download_url,
-                            std::string(),
-                            std::string(),
-                            base::UTF16ToUTF8(file_name.value()),
-                            base::UTF16ToUTF8(mime_type),
-                            default_name);
+      net::GenerateFileName(download_url, std::string(), std::string(),
+                            base::WideToUTF8(file_name.value()),
+                            base::UTF16ToUTF8(mime_type), default_name);
 
   // http://crbug.com/332579
   base::ThreadRestrictions::ScopedAllowIO allow_file_operations;
diff --git a/content/browser/xr/service/vr_service_impl.cc b/content/browser/xr/service/vr_service_impl.cc
index 2129e3f1..75346867 100644
--- a/content/browser/xr/service/vr_service_impl.cc
+++ b/content/browser/xr/service/vr_service_impl.cc
@@ -571,6 +571,8 @@
       }
     }
 
+    runtime_options->depth_options = std::move(request.options->depth_options);
+
     base::OnceCallback<void(device::mojom::XRSessionPtr)> immersive_callback =
         base::BindOnce(&VRServiceImpl::OnImmersiveSessionCreated,
                        weak_ptr_factory_.GetWeakPtr(), std::move(request));
diff --git a/content/child/dwrite_font_proxy/dwrite_font_proxy_win.cc b/content/child/dwrite_font_proxy/dwrite_font_proxy_win.cc
index 4054c9d5..4ae2c25 100644
--- a/content/child/dwrite_font_proxy/dwrite_font_proxy_win.cc
+++ b/content/child/dwrite_font_proxy/dwrite_font_proxy_win.cc
@@ -46,7 +46,7 @@
     L"Sans",     L"Arial",   L"MS UI Gothic",    L"Microsoft Sans Serif",
     L"Segoe UI", L"Calibri", L"Times New Roman", L"Courier New"};
 
-bool IsLastResortFontName(const base::string16& font_name) {
+bool IsLastResortFontName(const std::wstring& font_name) {
   for (const wchar_t* last_resort_font_name : kLastResortFontNames) {
     if (font_name == last_resort_font_name)
       return true;
@@ -127,7 +127,7 @@
   DCHECK(exists);
   TRACE_EVENT0("dwrite,fonts", "FontProxy::FindFamilyName");
 
-  base::string16 name(family_name);
+  std::wstring name(family_name);
 
   auto iter = family_names_.find(name);
   if (iter != family_names_.end()) {
@@ -347,7 +347,7 @@
 
   mswr::ComPtr<DWriteFontFamilyProxy>& family = families_[family_index];
   if (!family->IsLoaded() || family->GetName().empty())
-    family->SetName(family_name);
+    family->SetName(base::UTF16ToWide(family_name));
 
   family.CopyTo(font_family);
   return true;
@@ -522,11 +522,11 @@
   return SUCCEEDED(hr);
 }
 
-void DWriteFontFamilyProxy::SetName(const base::string16& family_name) {
+void DWriteFontFamilyProxy::SetName(const std::wstring& family_name) {
   family_name_.assign(family_name);
 }
 
-const base::string16& DWriteFontFamilyProxy::GetName() {
+const std::wstring& DWriteFontFamilyProxy::GetName() {
   return family_name_;
 }
 
diff --git a/content/child/dwrite_font_proxy/dwrite_font_proxy_win.h b/content/child/dwrite_font_proxy/dwrite_font_proxy_win.h
index 0e14fa0..2e9619b 100644
--- a/content/child/dwrite_font_proxy/dwrite_font_proxy_win.h
+++ b/content/child/dwrite_font_proxy/dwrite_font_proxy_win.h
@@ -104,7 +104,7 @@
  private:
   Microsoft::WRL::ComPtr<IDWriteFactory> factory_;
   std::vector<Microsoft::WRL::ComPtr<DWriteFontFamilyProxy>> families_;
-  std::map<base::string16, UINT32> family_names_;
+  std::map<std::wstring, UINT32> family_names_;
   UINT32 family_count_ = UINT_MAX;
   scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_;
   // Per-sequence mojo::Remote<DWriteFontProxy>. This is preferred to a
@@ -151,9 +151,9 @@
 
   bool GetFontFromFontFace(IDWriteFontFace* font_face, IDWriteFont** font);
 
-  void SetName(const base::string16& family_name);
+  void SetName(const std::wstring& family_name);
 
-  const base::string16& GetName();
+  const std::wstring& GetName();
 
   bool IsLoaded();
 
@@ -162,7 +162,7 @@
 
  private:
   UINT32 family_index_;
-  base::string16 family_name_;
+  std::wstring family_name_;
   Microsoft::WRL::ComPtr<DWriteFontCollectionProxy> proxy_collection_;
   Microsoft::WRL::ComPtr<IDWriteFontFamily> family_;
   Microsoft::WRL::ComPtr<IDWriteLocalizedStrings> family_names_;
diff --git a/content/child/dwrite_font_proxy/dwrite_localized_strings_win.cc b/content/child/dwrite_font_proxy/dwrite_localized_strings_win.cc
index 987c2c2..87a0efd 100644
--- a/content/child/dwrite_font_proxy/dwrite_localized_strings_win.cc
+++ b/content/child/dwrite_font_proxy/dwrite_localized_strings_win.cc
@@ -38,7 +38,7 @@
                                               UINT32 size) {
   if (index >= strings_.size())
     return E_INVALIDARG;
-  // string16::size does not count the null terminator as part of the string,
+  // wstring::size does not count the null terminator as part of the string,
   // but GetLocaleName requires the caller to reserve space for the null
   // terminator, so we need to ensure |size| is greater than the count of
   // characters.
@@ -64,7 +64,7 @@
                                           UINT32 size) {
   if (index >= strings_.size())
     return E_INVALIDARG;
-  // string16::size does not count the null terminator as part of the string,
+  // wstring::size does not count the null terminator as part of the string,
   // but GetString requires the caller to reserve space for the null terminator,
   // so we need to ensure |size| is greater than the count of characters.
   if (size <= strings_[index].second.size())
@@ -84,7 +84,7 @@
 }
 
 HRESULT DWriteLocalizedStrings::RuntimeClassInitialize(
-    std::vector<std::pair<base::string16, base::string16>>* strings) {
+    std::vector<std::pair<std::wstring, std::wstring>>* strings) {
   strings_.swap(*strings);
   return S_OK;
 }
diff --git a/content/child/dwrite_font_proxy/dwrite_localized_strings_win.h b/content/child/dwrite_font_proxy/dwrite_localized_strings_win.h
index 796f435..0b7a0266 100644
--- a/content/child/dwrite_font_proxy/dwrite_localized_strings_win.h
+++ b/content/child/dwrite_font_proxy/dwrite_localized_strings_win.h
@@ -7,11 +7,11 @@
 
 #include <dwrite.h>
 #include <wrl.h>
+#include <string>
 #include <utility>
 #include <vector>
 
 #include "base/macros.h"
-#include "base/strings/string16.h"
 
 namespace content {
 
@@ -41,14 +41,14 @@
                                             UINT32* length) override;
 
   HRESULT STDMETHODCALLTYPE RuntimeClassInitialize(
-      std::vector<std::pair<base::string16, base::string16>>* strings);
+      std::vector<std::pair<std::wstring, std::wstring>>* strings);
 
  private:
   // List of strings. First element of each pair is the locale, and the second
   // element is the associated value. Use a vector because the expected number
   // of pairs is small (typically 1-2, rarely up to a few dozen?) and we need
   // index-based access.
-  std::vector<std::pair<base::string16, base::string16>> strings_;
+  std::vector<std::pair<std::wstring, std::wstring>> strings_;
 
   DISALLOW_ASSIGN(DWriteLocalizedStrings);
 };
diff --git a/content/child/dwrite_font_proxy/font_fallback_win.cc b/content/child/dwrite_font_proxy/font_fallback_win.cc
index 8afb113..8532bc4 100644
--- a/content/child/dwrite_font_proxy/font_fallback_win.cc
+++ b/content/child/dwrite_font_proxy/font_fallback_win.cc
@@ -11,6 +11,7 @@
 #include "base/metrics/histogram_macros.h"
 #include "base/strings/string16.h"
 #include "base/strings/utf_string_conversion_utils.h"
+#include "base/strings/utf_string_conversions.h"
 #include "base/trace_event/trace_event.h"
 #include "content/child/dwrite_font_proxy/dwrite_font_proxy_win.h"
 
@@ -39,9 +40,9 @@
                             fallback_result, FONT_FALLBACK_RESULT_MAX_VALUE);
 }
 
-base::string16 MakeCacheKey(const wchar_t* base_family_name,
-                            const wchar_t* locale) {
-  base::string16 cache_key(base_family_name);
+std::wstring MakeCacheKey(const wchar_t* base_family_name,
+                          const wchar_t* locale) {
+  std::wstring cache_key(base_family_name);
   return cache_key + L"_" + locale;
 }
 
@@ -77,7 +78,8 @@
     DCHECK(false);
     return E_FAIL;
   }
-  base::string16 text_chunk(text, std::min(chunk_length, text_length));
+  base::string16 text_chunk;
+  base::WideToUTF16(text, std::min(chunk_length, text_length), &text_chunk);
 
   if (text_chunk.size() == 0) {
     DCHECK(false);
@@ -107,12 +109,12 @@
 
   blink::mojom::MapCharactersResultPtr result;
 
-  if (!GetFontProxy().MapCharacters(text_chunk,
-                                    blink::mojom::DWriteFontStyle::New(
-                                        base_weight, base_style, base_stretch),
-                                    locale,
-                                    source->GetParagraphReadingDirection(),
-                                    base_family_name, &result)) {
+  if (!GetFontProxy().MapCharacters(
+          text_chunk,
+          blink::mojom::DWriteFontStyle::New(base_weight, base_style,
+                                             base_stretch),
+          base::WideToUTF16(locale), source->GetParagraphReadingDirection(),
+          base::WideToUTF16(base_family_name), &result)) {
     DCHECK(false);
     return E_FAIL;
   }
diff --git a/content/child/dwrite_font_proxy/font_fallback_win.h b/content/child/dwrite_font_proxy/font_fallback_win.h
index b6f604a..623f76f1 100644
--- a/content/child/dwrite_font_proxy/font_fallback_win.h
+++ b/content/child/dwrite_font_proxy/font_fallback_win.h
@@ -75,7 +75,7 @@
   // of font families that matched a character on a previous call. The list is
   // capped in size and maintained in MRU order. This gives us a good chance of
   // returning a suitable fallback font without having to do an IPC.
-  std::map<base::string16, std::list<Microsoft::WRL::ComPtr<IDWriteFontFamily>>>
+  std::map<std::wstring, std::list<Microsoft::WRL::ComPtr<IDWriteFontFamily>>>
       fallback_family_cache_;
 
   DISALLOW_ASSIGN(FontFallback);
diff --git a/content/common/content_navigation_policy.cc b/content/common/content_navigation_policy.cc
index a05e328..0391d599 100644
--- a/content/common/content_navigation_policy.cc
+++ b/content/common/content_navigation_policy.cc
@@ -9,6 +9,7 @@
 #include "base/command_line.h"
 #include "base/metrics/field_trial_params.h"
 #include "base/system/sys_info.h"
+#include "build/build_config.h"
 #include "content/public/common/content_features.h"
 #include "content/public/common/content_switches.h"
 
@@ -21,9 +22,21 @@
   // It is important to check the base::FeatureList to avoid activating any
   // field trial groups if BFCache is disabled due to memory threshold.
   if (base::FeatureList::IsEnabled(features::kBackForwardCacheMemoryControl)) {
+    // On Android, BackForwardCache is only enabled for 2GB+ high memory
+    // devices. The default threshold value is set to 1700 MB to account for all
+    // 2GB devices which report lower RAM due to carveouts.
+    int default_memory_threshold_mb =
+#if defined(OS_ANDROID)
+        1700;
+#else
+        // Desktop has lower memory limitations compared to Android allowing us
+        // to enable BackForwardCache for all devices.
+        0;
+#endif
     int memory_threshold_mb = base::GetFieldTrialParamByFeatureAsInt(
         features::kBackForwardCacheMemoryControl,
-        "memory_threshold_for_back_forward_cache_in_mb", 0);
+        "memory_threshold_for_back_forward_cache_in_mb",
+        default_memory_threshold_mb);
     return base::SysInfo::AmountOfPhysicalMemoryMB() > memory_threshold_mb;
   }
 
diff --git a/content/common/content_switches_internal.cc b/content/common/content_switches_internal.cc
index 2dfb2310..95eacdb 100644
--- a/content/common/content_switches_internal.cc
+++ b/content/common/content_switches_internal.cc
@@ -40,12 +40,12 @@
 
 #if defined(OS_WIN)
 
-base::string16 ToNativeString(base::StringPiece string) {
-  return base::ASCIIToUTF16(string);
+std::wstring ToNativeString(base::StringPiece string) {
+  return base::ASCIIToWide(string);
 }
 
-std::string FromNativeString(base::StringPiece16 string) {
-  return base::UTF16ToASCII(string);
+std::string FromNativeString(base::WStringPiece string) {
+  return base::WideToASCII(string);
 }
 
 #else  // defined(OS_WIN)
diff --git a/content/common/font_cache_dispatcher_win.cc b/content/common/font_cache_dispatcher_win.cc
index 8a75ec4..9418212e 100644
--- a/content/common/font_cache_dispatcher_win.cc
+++ b/content/common/font_cache_dispatcher_win.cc
@@ -19,7 +19,7 @@
 
 namespace content {
 namespace {
-typedef std::vector<base::string16> FontNameVector;
+typedef std::vector<std::wstring> FontNameVector;
 typedef std::map<FontCacheDispatcher*, FontNameVector> DispatcherToFontNames;
 
 class FontCache {
@@ -43,7 +43,7 @@
     BOOL ret = GetTextMetrics(hdc, &tm);
     DCHECK(ret);
 
-    base::string16 font_name = font.lfFaceName;
+    std::wstring font_name = font.lfFaceName;
     bool inc_ref_count = true;
     if (!base::Contains(dispatcher_font_map_[dispatcher], font_name)) {
       // Requested font is new to cache.
@@ -69,7 +69,7 @@
   }
 
   void ReleaseCachedFonts(FontCacheDispatcher* dispatcher) {
-    typedef std::map<base::string16, FontCache::CacheElement> FontNameToElement;
+    typedef std::map<std::wstring, FontCache::CacheElement> FontNameToElement;
 
     base::AutoLock lock(mutex_);
 
@@ -126,7 +126,7 @@
   FontCache() {
   }
 
-  std::map<base::string16, CacheElement> cache_ GUARDED_BY(mutex_);
+  std::map<std::wstring, CacheElement> cache_ GUARDED_BY(mutex_);
   DispatcherToFontNames dispatcher_font_map_ GUARDED_BY(mutex_);
   base::Lock mutex_;
 
diff --git a/content/common/pepper_plugin_list.cc b/content/common/pepper_plugin_list.cc
index 78f7c5a..1ff53a0 100644
--- a/content/common/pepper_plugin_list.cc
+++ b/content/common/pepper_plugin_list.cc
@@ -81,14 +81,7 @@
 
     PepperPluginInfo plugin;
     plugin.is_out_of_process = out_of_process;
-#if defined(OS_WIN)
-    // This means we can't provide plugins from non-ASCII paths, but
-    // since this switch is only for development I don't think that's
-    // too awful.
-    plugin.path = base::FilePath(base::ASCIIToUTF16(name_parts[0]));
-#else
-    plugin.path = base::FilePath(name_parts[0]);
-#endif
+    plugin.path = base::FilePath::FromUTF8Unsafe(name_parts[0]);
 
     uint64_t index_mask = 1ULL << i;
     if (!(skip_file_check_flags & index_mask)) {
diff --git a/content/public/android/java/src/org/chromium/content/browser/framehost/RenderFrameHostImpl.java b/content/public/android/java/src/org/chromium/content/browser/framehost/RenderFrameHostImpl.java
index ca20ffa..3967e71 100644
--- a/content/public/android/java/src/org/chromium/content/browser/framehost/RenderFrameHostImpl.java
+++ b/content/public/android/java/src/org/chromium/content/browser/framehost/RenderFrameHostImpl.java
@@ -14,8 +14,10 @@
 import org.chromium.blink.mojom.AuthenticatorStatus;
 import org.chromium.content_public.browser.FeaturePolicyFeature;
 import org.chromium.content_public.browser.RenderFrameHost;
+import org.chromium.mojo.bindings.Interface;
+import org.chromium.mojo.bindings.InterfaceRequest;
+import org.chromium.mojo.system.Pair;
 import org.chromium.mojo.system.impl.CoreImpl;
-import org.chromium.services.service_manager.InterfaceProvider;
 import org.chromium.url.Origin;
 
 /**
@@ -28,27 +30,20 @@
     // mDelegate can be null.
     private final RenderFrameHostDelegate mDelegate;
     private final boolean mIncognito;
-    private final InterfaceProvider mInterfaceProvider;
 
     private RenderFrameHostImpl(long nativeRenderFrameHostAndroid, RenderFrameHostDelegate delegate,
-            boolean isIncognito, int nativeInterfaceProviderHandle) {
+            boolean isIncognito) {
         mNativeRenderFrameHostAndroid = nativeRenderFrameHostAndroid;
         mDelegate = delegate;
         mIncognito = isIncognito;
-        mInterfaceProvider =
-                new InterfaceProvider(CoreImpl.getInstance()
-                                              .acquireNativeHandle(nativeInterfaceProviderHandle)
-                                              .toMessagePipeHandle());
 
         mDelegate.renderFrameCreated(this);
     }
 
     @CalledByNative
     private static RenderFrameHostImpl create(long nativeRenderFrameHostAndroid,
-            RenderFrameHostDelegate delegate, boolean isIncognito,
-            int nativeInterfaceProviderHandle) {
-        return new RenderFrameHostImpl(
-                nativeRenderFrameHostAndroid, delegate, isIncognito, nativeInterfaceProviderHandle);
+            RenderFrameHostDelegate delegate, boolean isIncognito) {
+        return new RenderFrameHostImpl(nativeRenderFrameHostAndroid, delegate, isIncognito);
     }
 
     @CalledByNative
@@ -108,11 +103,6 @@
                         mNativeRenderFrameHostAndroid, RenderFrameHostImpl.this, feature);
     }
 
-    @Override
-    public InterfaceProvider getRemoteInterfaces() {
-        return mInterfaceProvider;
-    }
-
     /**
      * TODO(timloh): This function shouldn't really be on here. If we end up
      * needing more logic from the native BrowserContext, we should add a
@@ -125,16 +115,36 @@
 
     @Override
     public void notifyUserActivation() {
+        if (mNativeRenderFrameHostAndroid == 0) return;
         RenderFrameHostImplJni.get().notifyUserActivation(
                 mNativeRenderFrameHostAndroid, RenderFrameHostImpl.this);
     }
 
     @Override
     public boolean isRenderFrameCreated() {
+        if (mNativeRenderFrameHostAndroid == 0) return false;
         return RenderFrameHostImplJni.get().isRenderFrameCreated(
                 mNativeRenderFrameHostAndroid, RenderFrameHostImpl.this);
     }
 
+    @Override
+    public <I extends Interface, P extends Interface.Proxy> P getInterfaceToRendererFrame(
+            Interface.Manager<I, P> manager) {
+        if (mNativeRenderFrameHostAndroid == 0) return null;
+        Pair<P, InterfaceRequest<I>> result = manager.getInterfaceRequest(CoreImpl.getInstance());
+        RenderFrameHostImplJni.get().getInterfaceToRendererFrame(mNativeRenderFrameHostAndroid,
+                RenderFrameHostImpl.this, manager.getName(),
+                result.second.passHandle().releaseNativeHandle());
+        return result.first;
+    }
+
+    @Override
+    public void terminateRendererDueToBadMessage(int reason) {
+        if (mNativeRenderFrameHostAndroid == 0) return;
+        RenderFrameHostImplJni.get().terminateRendererDueToBadMessage(
+                mNativeRenderFrameHostAndroid, RenderFrameHostImpl.this, reason);
+    }
+
     /**
      * Return the AndroidOverlay routing token for this RenderFrameHostImpl.
      */
@@ -183,6 +193,10 @@
                 long nativeRenderFrameHostAndroid, RenderFrameHostImpl caller);
         void notifyUserActivation(long nativeRenderFrameHostAndroid, RenderFrameHostImpl caller);
         boolean isRenderFrameCreated(long nativeRenderFrameHostAndroid, RenderFrameHostImpl caller);
+        void getInterfaceToRendererFrame(long nativeRenderFrameHostAndroid,
+                RenderFrameHostImpl caller, String interfacename, int messagePipeRawHandle);
+        void terminateRendererDueToBadMessage(
+                long nativeRenderFrameHostAndroid, RenderFrameHostImpl caller, int reason);
         boolean isProcessBlocked(long nativeRenderFrameHostAndroid, RenderFrameHostImpl caller);
         int performGetAssertionWebAuthSecurityChecks(long nativeRenderFrameHostAndroid,
                 RenderFrameHostImpl caller, String relyingPartyId, Origin effectiveOrigin);
diff --git a/content/public/android/java/src/org/chromium/content/browser/remoteobjects/RemoteObjectInjector.java b/content/public/android/java/src/org/chromium/content/browser/remoteobjects/RemoteObjectInjector.java
index 0750ca5..1bff2f8 100644
--- a/content/public/android/java/src/org/chromium/content/browser/remoteobjects/RemoteObjectInjector.java
+++ b/content/public/android/java/src/org/chromium/content/browser/remoteobjects/RemoteObjectInjector.java
@@ -104,7 +104,11 @@
 
         List<RenderFrameHost> frames = webContents.getAllRenderFrameHosts();
         for (RenderFrameHost frame : frames) {
-            addInterfaceForFrame(frame, name, object, requiredAnnotation);
+            // If there's no renderer frame yet, we will add the interface when
+            // it is created.
+            if (frame.isRenderFrameCreated()) {
+                addInterfaceForFrame(frame, name, object, requiredAnnotation);
+            }
         }
     }
 
@@ -166,8 +170,8 @@
             RemoteObjectHostImpl host = new RemoteObjectHostImpl(
                     new RemoteObjectAuditorImpl(), registry, mAllowInspection);
 
-            RemoteObjectGatewayFactory factory = frameHost.getRemoteInterfaces().getInterface(
-                    RemoteObjectGatewayFactory.MANAGER);
+            RemoteObjectGatewayFactory factory =
+                    frameHost.getInterfaceToRendererFrame(RemoteObjectGatewayFactory.MANAGER);
 
             Pair<RemoteObjectGateway.Proxy, InterfaceRequest<RemoteObjectGateway>> result =
                     RemoteObjectGateway.MANAGER.getInterfaceRequest(CoreImpl.getInstance());
diff --git a/content/public/android/java/src/org/chromium/content_public/browser/RenderFrameHost.java b/content/public/android/java/src/org/chromium/content_public/browser/RenderFrameHost.java
index cd63bb5..bff32790 100644
--- a/content/public/android/java/src/org/chromium/content_public/browser/RenderFrameHost.java
+++ b/content/public/android/java/src/org/chromium/content_public/browser/RenderFrameHost.java
@@ -7,7 +7,7 @@
 import androidx.annotation.Nullable;
 
 import org.chromium.base.Callback;
-import org.chromium.services.service_manager.InterfaceProvider;
+import org.chromium.mojo.bindings.Interface;
 import org.chromium.url.Origin;
 
 /**
@@ -49,13 +49,28 @@
     boolean isFeatureEnabled(@FeaturePolicyFeature int feature);
 
     /**
-     * Returns an InterfaceProvider that provides access to interface implementations provided by
-     * the corresponding RenderFrame. This provides access to interfaces implemented in the renderer
-     * to Java code in the browser process.
+     * Returns an interface by name to the Frame in the renderer process. This
+     * provides access to interfaces implemented in the renderer to Java code in
+     * the browser process.
      *
-     * @return The InterfaceProvider for the frame.
+     * Callers are responsible to ensure that the renderer Frame exists before
+     * trying to make a mojo connection to it. This can be done via
+     * isRenderFrameCreated() if the caller is not inside the call-stack of an
+     * IPC form the renderer (which would guarantee its existence at that time).
+     *
+     * @param pipe The message pipe to be connected to the renderer. If it fails
+     * to make the connection, the pipe will be closed.
      */
-    InterfaceProvider getRemoteInterfaces();
+    <I extends Interface, P extends Interface.Proxy> P getInterfaceToRendererFrame(
+            Interface.Manager<I, P> manager);
+
+    /**
+     * Kills the renderer process when it is detected to be misbehaving and has
+     * made a bad request.
+     *
+     * @param reason The BadMessageReason code from content::BadMessageReasons.
+     */
+    void terminateRendererDueToBadMessage(int reason);
 
     /**
      * Notifies the native RenderFrameHost about a user activation from the browser side.
diff --git a/content/public/browser/content_browser_client.cc b/content/public/browser/content_browser_client.cc
index adc83e8..7aaf30b 100644
--- a/content/public/browser/content_browser_client.cc
+++ b/content/public/browser/content_browser_client.cc
@@ -714,12 +714,12 @@
   return true;
 }
 
-base::string16 ContentBrowserClient::GetAppContainerSidForSandboxType(
+std::wstring ContentBrowserClient::GetAppContainerSidForSandboxType(
     sandbox::policy::SandboxType sandbox_type) {
   // Embedders should override this method and return different SIDs for each
   // sandbox type. Note: All content level tests will run child processes in the
   // same AppContainer.
-  return base::string16(
+  return std::wstring(
       L"S-1-15-2-3251537155-1984446955-2931258699-841473695-1938553385-"
       L"924012148-129201922");
 }
diff --git a/content/public/browser/content_browser_client.h b/content/public/browser/content_browser_client.h
index b5796830..a2ae9cb 100644
--- a/content/public/browser/content_browser_client.h
+++ b/content/public/browser/content_browser_client.h
@@ -1211,7 +1211,7 @@
   // Returns the AppContainer SID for the specified sandboxed process type, or
   // empty string if this sandboxed process type does not support living inside
   // an AppContainer. Called on PROCESS_LAUNCHER thread.
-  virtual base::string16 GetAppContainerSidForSandboxType(
+  virtual std::wstring GetAppContainerSidForSandboxType(
       sandbox::policy::SandboxType sandbox_type);
 
   // Returns whether renderer code integrity is enabled.
diff --git a/content/public/browser/media_stream_request.h b/content/public/browser/media_stream_request.h
index a85b2b2f..8845ecf 100644
--- a/content/public/browser/media_stream_request.h
+++ b/content/public/browser/media_stream_request.h
@@ -114,9 +114,6 @@
 
   virtual void OnDeviceStopped(const std::string& label,
                                const DesktopMediaID& media_id) = 0;
-
-  // Replaces the stop callback set in OnStarted(), if any.
-  virtual void SetStopCallback(base::OnceClosure stop) = 0;
 };
 
 // Callback used return results of media access requests.
diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc
index 0947f18a..42de19e2 100644
--- a/content/public/common/content_features.cc
+++ b/content/public/common/content_features.cc
@@ -68,8 +68,17 @@
                                      base::FEATURE_ENABLED_BY_DEFAULT};
 
 // Enable using the BackForwardCache.
+// BackForwardCache is enabled only on Android for the moment, as some
+// desktop-specific features (including extensions) are not compatible with
+// bfcache yet. Tracking bug for enabling bfcache on desktop:
+// https://crbug.com/1171298.
+#if defined(OS_ANDROID)
+const base::Feature kBackForwardCache{"BackForwardCache",
+                                      base::FEATURE_ENABLED_BY_DEFAULT};
+#else
 const base::Feature kBackForwardCache{"BackForwardCache",
                                       base::FEATURE_DISABLED_BY_DEFAULT};
+#endif
 
 // BackForwardCache is disabled on low memory devices. The threshold is defined
 // via a field trial param: "memory_threshold_for_back_forward_cache_in_mb"
@@ -78,8 +87,16 @@
 // "BackForwardCacheMemoryControls" is checked before "BackForwardCache". It
 // means the low memory devices will activate neither the control group nor the
 // experimental group of the BackForwardCache field trial.
+
+// BackForwardCacheMemoryControls is enabled only on Android to disable
+// BackForwardCache for lower memory devices due to memory limiations.
+#if defined(OS_ANDROID)
+const base::Feature kBackForwardCacheMemoryControl{
+    "BackForwardCacheMemoryControls", base::FEATURE_ENABLED_BY_DEFAULT};
+#else
 const base::Feature kBackForwardCacheMemoryControl{
     "BackForwardCacheMemoryControls", base::FEATURE_DISABLED_BY_DEFAULT};
+#endif
 
 // Block subresource requests whose URLs contain embedded credentials (e.g.
 // `https://user:pass@example.com/resource`).
diff --git a/content/public/test/android/BUILD.gn b/content/public/test/android/BUILD.gn
index 6449c38..d905a475 100644
--- a/content/public/test/android/BUILD.gn
+++ b/content/public/test/android/BUILD.gn
@@ -22,6 +22,7 @@
     "//base:base_java",
     "//base:base_java_test_support",
     "//content/public/android:content_java",
+    "//mojo/public/java:bindings_java",
     "//net/android:net_java",
     "//services/service_manager/public/java:service_manager_java",
     "//third_party/android_deps:androidx_annotation_annotation_java",
diff --git a/content/public/test/android/javatests/src/org/chromium/content_public/browser/test/mock/MockRenderFrameHost.java b/content/public/test/android/javatests/src/org/chromium/content_public/browser/test/mock/MockRenderFrameHost.java
index 6524bc6..eb77bb7 100644
--- a/content/public/test/android/javatests/src/org/chromium/content_public/browser/test/mock/MockRenderFrameHost.java
+++ b/content/public/test/android/javatests/src/org/chromium/content_public/browser/test/mock/MockRenderFrameHost.java
@@ -7,7 +7,7 @@
 import org.chromium.base.Callback;
 import org.chromium.content_public.browser.FeaturePolicyFeature;
 import org.chromium.content_public.browser.RenderFrameHost;
-import org.chromium.services.service_manager.InterfaceProvider;
+import org.chromium.mojo.bindings.Interface;
 import org.chromium.url.Origin;
 
 /**
@@ -33,11 +33,15 @@
     }
 
     @Override
-    public InterfaceProvider getRemoteInterfaces() {
+    public <I extends Interface, P extends Interface.Proxy> P getInterfaceToRendererFrame(
+            Interface.Manager<I, P> manager) {
         return null;
     }
 
     @Override
+    public void terminateRendererDueToBadMessage(int reason) {}
+
+    @Override
     public void notifyUserActivation() {}
 
     @Override
diff --git a/content/public/test/browser_test_utils.cc b/content/public/test/browser_test_utils.cc
index 148802b..e30f107 100644
--- a/content/public/test/browser_test_utils.cc
+++ b/content/public/test/browser_test_utils.cc
@@ -2190,8 +2190,8 @@
     ASSERT_HRESULT_SUCCEEDED(raw_element_provider_simple->GetPropertyValue(
         UIA_NamePropertyId, name.Receive()));
     ASSERT_EQ(VT_BSTR, name.type());
-    names.push_back(base::UTF16ToUTF8(
-        base::string16(V_BSTR(name.ptr()), SysStringLen(V_BSTR(name.ptr())))));
+    names.push_back(base::WideToUTF8(
+        std::wstring(V_BSTR(name.ptr()), SysStringLen(V_BSTR(name.ptr())))));
   }
 
   ASSERT_THAT(names, testing::UnorderedElementsAreArray(expected_names));
diff --git a/content/renderer/accessibility/blink_ax_tree_source.cc b/content/renderer/accessibility/blink_ax_tree_source.cc
index 8049d80..c7d9dbce 100644
--- a/content/renderer/accessibility/blink_ax_tree_source.cc
+++ b/content/renderer/accessibility/blink_ax_tree_source.cc
@@ -80,22 +80,6 @@
     dst->AddIntListAttribute(attr, ids);
 }
 
-WebAXObject ParentObjectUnignored(WebAXObject child) {
-  WebAXObject parent = child.ParentObject();
-  while (!parent.IsDetached() && !parent.AccessibilityIsIncludedInTree())
-    parent = parent.ParentObject();
-  return parent;
-}
-
-// Returns true if |ancestor| is the first unignored parent of |child|,
-// which means that when walking up the parent chain from |child|,
-// |ancestor| is the *first* ancestor that isn't marked as
-// accessibilityIsIgnored().
-bool IsParentUnignoredOf(WebAXObject ancestor, WebAXObject child) {
-  WebAXObject parent = ParentObjectUnignored(child);
-  return parent.Equals(ancestor);
-}
-
 // Helper function that searches in the subtree of |obj| to a max
 // depth of |max_depth| for an image.
 //
@@ -422,19 +406,15 @@
     WebAXObject child = parent.ChildAt(i);
 
     // The child may be invalid due to issues in blink accessibility code.
-    if (child.IsDetached())
+    if (child.IsDetached()) {
+      NOTREACHED();
       continue;
+    }
 
-    // Skip children whose parent isn't |parent|.
-    // As an exception, include children of an iframe element.
-    if (!is_iframe && !IsParentUnignoredOf(parent, child))
-      continue;
-
-    // Skip table headers and columns, they're only needed on Mac
-    // and soon we'll get rid of this code entirely.
-    if (child.Role() == ax::mojom::Role::kColumn ||
-        child.Role() == ax::mojom::Role::kTableHeaderContainer)
-      continue;
+    // These should not be produced by Blink. They are only needed on Mac and
+    // handled in AXTableInfo on the browser side.
+    DCHECK_NE(child.Role(), ax::mojom::Role::kColumn);
+    DCHECK_NE(child.Role(), ax::mojom::Role::kTableHeaderContainer);
 
     // If an optional exclude_offscreen flag is set (only intended to be
     // used for a one-time snapshot of the accessibility tree), prune any
@@ -539,23 +519,6 @@
       dst->AddBoolAttribute(ax::mojom::BoolAttribute::kHasAriaAttribute, true);
   }
 
-  // Add the ids of *indirect* children - those who are children of this node,
-  // but whose parent is *not* this node. One example is a table
-  // cell, which is a child of both a row and a column. Because the cell's
-  // parent is the row, the row adds it as a child, and the column adds it
-  // as an indirect child.
-  int child_count = src.ChildCount();
-  std::vector<int32_t> indirect_child_ids;
-  for (int i = 0; i < child_count; ++i) {
-    WebAXObject child = src.ChildAt(i);
-    if (!is_iframe && !child.IsDetached() && !IsParentUnignoredOf(src, child))
-      indirect_child_ids.push_back(child.AxID());
-  }
-  if (indirect_child_ids.size() > 0) {
-    dst->AddIntListAttribute(ax::mojom::IntListAttribute::kIndirectChildIds,
-                             indirect_child_ids);
-  }
-
   if (dst->id == image_data_node_id_) {
     // In general, string attributes should be truncated using
     // TruncateAndAddStringAttribute, but ImageDataUrl contains a data url
diff --git a/content/renderer/media/inspector_media_event_handler.cc b/content/renderer/media/inspector_media_event_handler.cc
index 1d3a593..812a929 100644
--- a/content/renderer/media/inspector_media_event_handler.cc
+++ b/content/renderer/media/inspector_media_event_handler.cc
@@ -108,6 +108,7 @@
 
 void InspectorMediaEventHandler::OnWebMediaPlayerDestroyed() {
   video_player_destroyed_ = true;
+  inspector_context_->DestroyPlayer(player_id_);
 }
 
 }  // namespace content
diff --git a/content/renderer/media/inspector_media_event_handler_unittest.cc b/content/renderer/media/inspector_media_event_handler_unittest.cc
index f647f46..85926fb 100644
--- a/content/renderer/media/inspector_media_event_handler_unittest.cc
+++ b/content/renderer/media/inspector_media_event_handler_unittest.cc
@@ -22,6 +22,10 @@
 
   blink::WebString CreatePlayer() override { return "TestPlayer"; }
 
+  void DestroyPlayer(const blink::WebString& playerId) override {
+    MockDestroyPlayer();
+  }
+
   void NotifyPlayerEvents(blink::WebString id,
                           const blink::InspectorPlayerEvents& events) override {
     MockNotifyPlayerEvents(events);
@@ -48,6 +52,9 @@
   MOCK_METHOD1(MockSetPlayerProperties, void(blink::InspectorPlayerProperties));
   MOCK_METHOD1(MockNotifyPlayerErrors, void(blink::InspectorPlayerErrors));
   MOCK_METHOD1(MockNotifyPlayerMessages, void(blink::InspectorPlayerMessages));
+  MOCK_METHOD0(MockDestroyPlayer, void());
+  MOCK_METHOD0(MockIncrementActiveSessionCount, void());
+  MOCK_METHOD0(MockDecrementActiveSessionCount, void());
 };
 
 class InspectorMediaEventHandlerTest : public testing::Test {
diff --git a/content/renderer/media/media_interface_factory.cc b/content/renderer/media/media_interface_factory.cc
index 24a0f6f..6991ed9 100644
--- a/content/renderer/media/media_interface_factory.cc
+++ b/content/renderer/media/media_interface_factory.cc
@@ -138,6 +138,26 @@
 }
 #endif  // defined(OS_ANDROID)
 
+#if defined(OS_WIN)
+void MediaInterfaceFactory::CreateMediaFoundationRenderer(
+    mojo::PendingReceiver<media::mojom::Renderer> receiver,
+    mojo::PendingReceiver<media::mojom::MediaFoundationRendererExtension>
+        renderer_extension_receiver) {
+  if (!task_runner_->BelongsToCurrentThread()) {
+    task_runner_->PostTask(
+        FROM_HERE,
+        base::BindOnce(&MediaInterfaceFactory::CreateMediaFoundationRenderer,
+                       weak_this_, std::move(receiver),
+                       std::move(renderer_extension_receiver)));
+    return;
+  }
+
+  DVLOG(1) << __func__;
+  GetMediaInterfaceFactory()->CreateMediaFoundationRenderer(
+      std::move(receiver), std::move(renderer_extension_receiver));
+}
+#endif  // defined(OS_WIN)
+
 void MediaInterfaceFactory::CreateCdm(const std::string& key_system,
                                       const media::CdmConfig& cdm_config,
                                       CreateCdmCallback callback) {
diff --git a/content/renderer/media/media_interface_factory.h b/content/renderer/media/media_interface_factory.h
index f9e8707..53b425d 100644
--- a/content/renderer/media/media_interface_factory.h
+++ b/content/renderer/media/media_interface_factory.h
@@ -69,6 +69,12 @@
       mojo::PendingReceiver<media::mojom::MediaPlayerRendererExtension>
           renderer_extension_receiver) final;
 #endif  // defined(OS_ANDROID)
+#if defined(OS_WIN)
+  void CreateMediaFoundationRenderer(
+      mojo::PendingReceiver<media::mojom::Renderer> receiver,
+      mojo::PendingReceiver<media::mojom::MediaFoundationRendererExtension>
+          renderer_extension_receiver) final;
+#endif  // defined(OS_WIN)
   void CreateCdm(const std::string& key_system,
                  const media::CdmConfig& cdm_config,
                  CreateCdmCallback callback) final;
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index a52fcdf..f6a784cf 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -923,10 +923,10 @@
   }
 
 #if defined(OS_WIN)
-  base::string16 path = request->Url().GetString().Utf16();
-  const base::string16 file_prefix =
-      base::ASCIIToUTF16(url::kFileScheme) +
-      base::ASCIIToUTF16(url::kStandardSchemeSeparator);
+  std::wstring path = base::UTF16ToWide(request->Url().GetString().Utf16());
+  const std::wstring file_prefix =
+      base::ASCIIToWide(url::kFileScheme) +
+      base::ASCIIToWide(url::kStandardSchemeSeparator);
 #else
   std::string path = request->Url().GetString().Utf8();
   const std::string file_prefix =
diff --git a/content/test/dwrite_font_fake_sender_win.cc b/content/test/dwrite_font_fake_sender_win.cc
index 2145e13..e916c00 100644
--- a/content/test/dwrite_font_fake_sender_win.cc
+++ b/content/test/dwrite_font_fake_sender_win.cc
@@ -10,12 +10,13 @@
 #include <memory>
 
 #include "base/bind.h"
+#include "base/strings/string_util.h"
 
 namespace content {
 
 void AddFamily(const base::FilePath& font_path,
-               const base::string16& family_name,
-               const base::string16& base_family_name,
+               const std::wstring& family_name,
+               const std::wstring& base_family_name,
                FakeFontCollection* collection) {
   collection->AddFont(family_name)
       .AddFamilyName(L"en-us", family_name)
@@ -80,7 +81,8 @@
                                     FindFamilyCallback callback) {
   message_types_.push_back(MessageType::kFindFamily);
   for (size_t n = 0; n < fonts_.size(); n++) {
-    if (_wcsicmp(family_name.data(), fonts_[n].font_name_.data()) == 0) {
+    if (base::EqualsCaseInsensitiveASCII(family_name.data(),
+                                         fonts_[n].font_name_.data())) {
       std::move(callback).Run(n);
       return;
     }
diff --git a/content/test/fuzzer/code_cache_host_mojolpm_fuzzer.cc b/content/test/fuzzer/code_cache_host_mojolpm_fuzzer.cc
index 8877668..9594101 100644
--- a/content/test/fuzzer/code_cache_host_mojolpm_fuzzer.cc
+++ b/content/test/fuzzer/code_cache_host_mojolpm_fuzzer.cc
@@ -209,7 +209,7 @@
       base::MakeRefCounted<content::CacheStorageContextImpl>();
   cache_storage_context_->Init(browser_context_->GetPath(),
                                browser_context_->GetSpecialStoragePolicy(),
-                               nullptr);
+                               nullptr, mojo::NullRemote());
   cache_storage_context_->Bind(
       cache_storage_control_.BindNewPipeAndPassReceiver());
 
diff --git a/content/test/gpu/gpu_tests/common_browser_args.py b/content/test/gpu/gpu_tests/common_browser_args.py
index e2a7426..9230fe49 100644
--- a/content/test/gpu/gpu_tests/common_browser_args.py
+++ b/content/test/gpu/gpu_tests/common_browser_args.py
@@ -28,8 +28,9 @@
 ENABLE_EXPERIMENTAL_WEB_PLATFORM_FEATURES =\
     '--enable-experimental-web-platform-features'
 ENABLE_GPU_BENCHMARKING = '--enable-gpu-benchmarking'
+ENABLE_GPU_RASTERIZATION = '--enable-gpu-rasterization'
 ENABLE_LOGGING = '--enable-logging'
 ENSURE_FORCED_COLOR_PROFILE = '--ensure-forced-color-profile'
+FORCE_BROWSER_CRASH_ON_GPU_CRASH = '--force-browser-crash-on-gpu-crash'
 FORCE_COLOR_PROFILE_SRGB = '--force-color-profile=srgb'
-ENABLE_GPU_RASTERIZATION = '--enable-gpu-rasterization'
 TEST_TYPE_GPU = '--test-type=gpu'
diff --git a/content/test/gpu/gpu_tests/maps_integration_test.py b/content/test/gpu/gpu_tests/maps_integration_test.py
index 4d7e013..a6bdcd89 100644
--- a/content/test/gpu/gpu_tests/maps_integration_test.py
+++ b/content/test/gpu/gpu_tests/maps_integration_test.py
@@ -47,8 +47,9 @@
         options.dont_restore_color_profile_after_test)
     super(MapsIntegrationTest, cls).SetUpProcess()
     cls.CustomizeBrowserArgs([
-        cba.FORCE_COLOR_PROFILE_SRGB,
         cba.ENSURE_FORCED_COLOR_PROFILE,
+        cba.FORCE_BROWSER_CRASH_ON_GPU_CRASH,
+        cba.FORCE_COLOR_PROFILE_SRGB,
     ])
     cloud_storage.GetIfChanged(
         os.path.join(_MAPS_PERF_TEST_PATH, 'load_dataset'),
diff --git a/device/vr/android/arcore/arcore.cc b/device/vr/android/arcore/arcore.cc
index a7db8b7..8fe97945 100644
--- a/device/vr/android/arcore/arcore.cc
+++ b/device/vr/android/arcore/arcore.cc
@@ -26,11 +26,29 @@
 }
 
 ArCore::InitializeResult::InitializeResult(
-    const std::unordered_set<device::mojom::XRSessionFeature>& enabled_features)
-    : enabled_features(enabled_features) {}
+    const std::unordered_set<device::mojom::XRSessionFeature>& enabled_features,
+    base::Optional<device::mojom::XRDepthConfig> depth_configuration)
+    : enabled_features(enabled_features),
+      depth_configuration(std::move(depth_configuration)) {}
 
 ArCore::InitializeResult::InitializeResult(const InitializeResult& other) =
     default;
 ArCore::InitializeResult::~InitializeResult() = default;
 
+ArCore::DepthSensingConfiguration::DepthSensingConfiguration(
+    std::vector<device::mojom::XRDepthUsage> depth_usage_preference,
+    std::vector<device::mojom::XRDepthDataFormat> depth_data_format_preference)
+    : depth_usage_preference(depth_usage_preference),
+      depth_data_format_preference(depth_data_format_preference) {}
+
+ArCore::DepthSensingConfiguration::DepthSensingConfiguration(
+    DepthSensingConfiguration&& other) = default;
+ArCore::DepthSensingConfiguration::DepthSensingConfiguration(
+    const DepthSensingConfiguration& other) = default;
+ArCore::DepthSensingConfiguration::~DepthSensingConfiguration() = default;
+ArCore::DepthSensingConfiguration& ArCore::DepthSensingConfiguration::operator=(
+    const DepthSensingConfiguration& other) = default;
+ArCore::DepthSensingConfiguration& ArCore::DepthSensingConfiguration::operator=(
+    DepthSensingConfiguration&& other) = default;
+
 }  // namespace device
diff --git a/device/vr/android/arcore/arcore.h b/device/vr/android/arcore/arcore.h
index 066a51d4..61b08b8 100644
--- a/device/vr/android/arcore/arcore.h
+++ b/device/vr/android/arcore/arcore.h
@@ -37,12 +37,37 @@
   struct InitializeResult {
     std::unordered_set<device::mojom::XRSessionFeature> enabled_features;
 
-    InitializeResult(const std::unordered_set<device::mojom::XRSessionFeature>&
-                         enabled_features);
+    // If the depth sensing API was requested, the depth_configuration will
+    // contain the device-selected depth API usage and data format.
+
+    base::Optional<device::mojom::XRDepthConfig> depth_configuration;
+
+    InitializeResult(
+        const std::unordered_set<device::mojom::XRSessionFeature>&
+            enabled_features,
+        base::Optional<device::mojom::XRDepthConfig> depth_configuration);
     InitializeResult(const InitializeResult& other);
     ~InitializeResult();
   };
 
+  struct DepthSensingConfiguration {
+    std::vector<device::mojom::XRDepthUsage> depth_usage_preference;
+    std::vector<device::mojom::XRDepthDataFormat> depth_data_format_preference;
+
+    DepthSensingConfiguration(
+        std::vector<device::mojom::XRDepthUsage> depth_usage_preference,
+        std::vector<device::mojom::XRDepthDataFormat>
+            depth_data_format_preference);
+    ~DepthSensingConfiguration();
+
+    DepthSensingConfiguration(const DepthSensingConfiguration& other);
+    DepthSensingConfiguration(DepthSensingConfiguration&& other);
+
+    DepthSensingConfiguration& operator=(
+        const DepthSensingConfiguration& other);
+    DepthSensingConfiguration& operator=(DepthSensingConfiguration&& other);
+  };
+
   // Initializes the runtime and returns whether it was successful.
   // If successful, the runtime must be paused when this method returns.
   virtual base::Optional<InitializeResult> Initialize(
@@ -51,7 +76,8 @@
           required_features,
       const std::unordered_set<device::mojom::XRSessionFeature>&
           optional_features,
-      const std::vector<device::mojom::XRTrackedImagePtr>& tracked_images) = 0;
+      const std::vector<device::mojom::XRTrackedImagePtr>& tracked_images,
+      base::Optional<DepthSensingConfiguration> depth_sensing_config) = 0;
 
   // Returns the target framerate range in Hz. Actual capture frame rate will
   // vary within this range, i.e. lower in low light to increase exposure time.
diff --git a/device/vr/android/arcore/arcore_device.cc b/device/vr/android/arcore/arcore_device.cc
index 4189728..780abbb 100644
--- a/device/vr/android/arcore/arcore_device.cc
+++ b/device/vr/android/arcore/arcore_device.cc
@@ -124,12 +124,14 @@
   session_state_->optional_features_.insert(options->optional_features.begin(),
                                             options->optional_features.end());
 
-  bool use_dom_overlay =
+  const bool use_dom_overlay =
       base::Contains(options->required_features,
                      device::mojom::XRSessionFeature::DOM_OVERLAY) ||
       base::Contains(options->optional_features,
                      device::mojom::XRSessionFeature::DOM_OVERLAY);
 
+  session_state_->depth_options_ = std::move(options->depth_options);
+
   // mailbox_bridge_ is either supplied from the constructor, or recreated in
   // OnSessionEnded().
   DCHECK(mailbox_bridge_);
@@ -247,9 +249,8 @@
 }
 
 void ArCoreDevice::CallDeferredRequestSessionCallback(
-    base::Optional<std::unordered_set<device::mojom::XRSessionFeature>>
-        enabled_features) {
-  DVLOG(1) << __func__ << " success=" << enabled_features.has_value();
+    base::Optional<ArCoreGlInitializeResult> initialize_result) {
+  DVLOG(1) << __func__ << " success=" << initialize_result.has_value();
   DCHECK(IsOnMainThread());
 
   // We might not have any pending session requests, i.e. if destroyed
@@ -260,15 +261,15 @@
   mojom::XRRuntime::RequestSessionCallback deferred_callback =
       std::move(session_state_->pending_request_session_callback_);
 
-  if (!enabled_features) {
+  if (!initialize_result) {
     std::move(deferred_callback).Run(nullptr, mojo::NullRemote());
     return;
   }
 
   // Success case should only happen after GL thread is ready.
-  auto create_callback =
-      base::BindOnce(&ArCoreDevice::OnCreateSessionCallback, GetWeakPtr(),
-                     std::move(deferred_callback), *enabled_features);
+  auto create_callback = base::BindOnce(
+      &ArCoreDevice::OnCreateSessionCallback, GetWeakPtr(),
+      std::move(deferred_callback), std::move(*initialize_result));
 
   auto shutdown_callback =
       base::BindOnce(&ArCoreDevice::OnSessionEnded, GetWeakPtr());
@@ -283,24 +284,26 @@
 
 void ArCoreDevice::OnCreateSessionCallback(
     mojom::XRRuntime::RequestSessionCallback deferred_callback,
-    const std::unordered_set<device::mojom::XRSessionFeature>& enabled_features,
-    mojo::PendingRemote<mojom::XRFrameDataProvider> frame_data_provider,
-    mojom::VRDisplayInfoPtr display_info,
-    mojo::PendingRemote<mojom::XRSessionController> session_controller,
-    mojom::XRPresentationConnectionPtr presentation_connection) {
+    ArCoreGlInitializeResult initialize_result,
+    ArCoreGlCreateSessionResult create_session_result) {
   DVLOG(2) << __func__;
   DCHECK(IsOnMainThread());
 
   mojom::XRSessionPtr session = mojom::XRSession::New();
-  session->data_provider = std::move(frame_data_provider);
-  session->display_info = std::move(display_info);
-  session->submit_frame_sink = std::move(presentation_connection);
-  session->enabled_features.assign(enabled_features.begin(),
-                                   enabled_features.end());
+  session->data_provider = std::move(create_session_result.frame_data_provider);
+  session->display_info = std::move(create_session_result.display_info);
+  session->submit_frame_sink =
+      std::move(create_session_result.presentation_connection);
+  session->enabled_features.assign(initialize_result.enabled_features.begin(),
+                                   initialize_result.enabled_features.end());
   session->device_config = device::mojom::XRSessionDeviceConfig::New();
   auto* config = session->device_config.get();
 
   config->supports_viewport_scaling = true;
+  config->depth_configuration =
+      initialize_result.depth_configuration
+          ? initialize_result.depth_configuration->Clone()
+          : nullptr;
 
   // ARCORE only supports immersive-ar sessions
   session->enviroment_blend_mode =
@@ -308,7 +311,8 @@
   session->interaction_mode = device::mojom::XRInteractionMode::kScreenSpace;
 
   std::move(deferred_callback)
-      .Run(std::move(session), std::move(session_controller));
+      .Run(std::move(session),
+           std::move(create_session_result.session_controller));
 }
 
 void ArCoreDevice::PostTaskToGlThread(base::OnceClosure task) {
@@ -348,27 +352,39 @@
         frame_size, rotation, session_state_->required_features_,
         session_state_->optional_features_,
         std::move(session_state_->tracked_images_),
+        std::move(session_state_->depth_options_),
         CreateMainThreadCallback(base::BindOnce(
             &ArCoreDevice::OnArCoreGlInitializationComplete, GetWeakPtr()))));
     return;
   }
 
-  OnArCoreGlInitializationComplete(session_state_->enabled_features_);
+  // Since the GL is already initialized, we already have session_state_ that we
+  // can pass along.
+  OnArCoreGlInitializationComplete(ArCoreGlInitializeResult(
+      session_state_->enabled_features_, session_state_->depth_configuration_));
 }
 
 void ArCoreDevice::OnArCoreGlInitializationComplete(
-    base::Optional<std::unordered_set<device::mojom::XRSessionFeature>>
-        enabled_features) {
+    base::Optional<ArCoreGlInitializeResult> arcore_initialization_result) {
   DVLOG(1) << __func__;
   DCHECK(IsOnMainThread());
 
-  session_state_->is_arcore_gl_initialized_ = enabled_features.has_value();
-  session_state_->enabled_features_ = enabled_features.value_or(
-      std::unordered_set<device::mojom::XRSessionFeature>{});
+  session_state_->is_arcore_gl_initialized_ =
+      arcore_initialization_result.has_value();
+
+  if (arcore_initialization_result) {
+    session_state_->enabled_features_ =
+        arcore_initialization_result->enabled_features;
+    session_state_->depth_configuration_ =
+        arcore_initialization_result->depth_configuration;
+  } else {
+    session_state_->enabled_features_ = {};
+    session_state_->depth_configuration_ = base::nullopt;
+  }
 
   // We only start GL initialization after the user has granted consent, so we
   // can now start the session.
-  CallDeferredRequestSessionCallback(enabled_features);
+  CallDeferredRequestSessionCallback(std::move(arcore_initialization_result));
 }
 
 }  // namespace device
diff --git a/device/vr/android/arcore/arcore_device.h b/device/vr/android/arcore/arcore_device.h
index 4df2c76..23abf167 100644
--- a/device/vr/android/arcore/arcore_device.h
+++ b/device/vr/android/arcore/arcore_device.h
@@ -15,6 +15,7 @@
 #include "base/callback.h"
 #include "base/macros.h"
 #include "base/optional.h"
+#include "device/vr/android/arcore/arcore_gl.h"
 #include "device/vr/vr_device.h"
 #include "device/vr/vr_device_base.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
@@ -90,8 +91,7 @@
 
   // Replies to the pending mojo RequestSession request.
   void CallDeferredRequestSessionCallback(
-      base::Optional<std::unordered_set<device::mojom::XRSessionFeature>>
-          enabled_features);
+      base::Optional<ArCoreGlInitializeResult> arcore_initialization_result);
 
   // Tells the GL thread to initialize a GL context and other resources,
   // using the supplied window as a drawing surface.
@@ -101,17 +101,12 @@
 
   // Called when the GL thread's GL context initialization completes.
   void OnArCoreGlInitializationComplete(
-      base::Optional<std::unordered_set<device::mojom::XRSessionFeature>>
-          enabled_features);
+      base::Optional<ArCoreGlInitializeResult> arcore_initialization_result);
 
   void OnCreateSessionCallback(
       mojom::XRRuntime::RequestSessionCallback deferred_callback,
-      const std::unordered_set<device::mojom::XRSessionFeature>&
-          enabled_features,
-      mojo::PendingRemote<mojom::XRFrameDataProvider> frame_data_provider,
-      mojom::VRDisplayInfoPtr display_info,
-      mojo::PendingRemote<mojom::XRSessionController> session_controller,
-      mojom::XRPresentationConnectionPtr presentation_connection);
+      ArCoreGlInitializeResult initialize_result,
+      ArCoreGlCreateSessionResult create_session_result);
 
   scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner_;
   std::unique_ptr<ArCoreFactory> arcore_factory_;
@@ -140,10 +135,14 @@
     std::unordered_set<device::mojom::XRSessionFeature> required_features_;
     std::unordered_set<device::mojom::XRSessionFeature> optional_features_;
 
+    device::mojom::XRDepthOptionsPtr depth_options_;
+
     // Collection of features that have been enabled on the session. Initially
     // empty, will be set only once the ArCoreGl has been initialized.
     std::unordered_set<device::mojom::XRSessionFeature> enabled_features_;
 
+    base::Optional<device::mojom::XRDepthConfig> depth_configuration_;
+
     std::vector<device::mojom::XRTrackedImagePtr> tracked_images_;
   };
 
diff --git a/device/vr/android/arcore/arcore_gl.cc b/device/vr/android/arcore/arcore_gl.cc
index b66f3ba..bc175c7 100644
--- a/device/vr/android/arcore/arcore_gl.cc
+++ b/device/vr/android/arcore/arcore_gl.cc
@@ -82,6 +82,28 @@
 
 namespace device {
 
+ArCoreGlCreateSessionResult::ArCoreGlCreateSessionResult(
+    mojo::PendingRemote<mojom::XRFrameDataProvider> frame_data_provider,
+    mojom::VRDisplayInfoPtr display_info,
+    mojo::PendingRemote<mojom::XRSessionController> session_controller,
+    mojom::XRPresentationConnectionPtr presentation_connection)
+    : frame_data_provider(std::move(frame_data_provider)),
+      display_info(std::move(display_info)),
+      session_controller(std::move(session_controller)),
+      presentation_connection(std::move(presentation_connection)) {}
+ArCoreGlCreateSessionResult::~ArCoreGlCreateSessionResult() = default;
+ArCoreGlCreateSessionResult::ArCoreGlCreateSessionResult(
+    ArCoreGlCreateSessionResult&& other) = default;
+
+ArCoreGlInitializeResult::ArCoreGlInitializeResult(
+    std::unordered_set<device::mojom::XRSessionFeature> enabled_features,
+    base::Optional<device::mojom::XRDepthConfig> depth_configuration)
+    : enabled_features(enabled_features),
+      depth_configuration(depth_configuration) {}
+ArCoreGlInitializeResult::ArCoreGlInitializeResult(
+    ArCoreGlInitializeResult&& other) = default;
+ArCoreGlInitializeResult::~ArCoreGlInitializeResult() = default;
+
 ArCoreGl::ArCoreGl(std::unique_ptr<ArImageTransport> ar_image_transport)
     : gl_thread_task_runner_(base::ThreadTaskRunnerHandle::Get()),
       ar_image_transport_(std::move(ar_image_transport)),
@@ -117,6 +139,7 @@
     const std::unordered_set<device::mojom::XRSessionFeature>&
         optional_features,
     const std::vector<device::mojom::XRTrackedImagePtr>& tracked_images,
+    device::mojom::XRDepthOptionsPtr depth_options,
     ArCoreGlInitializeCallback callback) {
   DVLOG(3) << __func__;
 
@@ -142,10 +165,18 @@
     return;
   }
 
+  base::Optional<ArCore::DepthSensingConfiguration> depth_sensing_config;
+  if (depth_options) {
+    depth_sensing_config = ArCore::DepthSensingConfiguration(
+        depth_options->usage_preferences,
+        depth_options->data_format_preferences);
+  }
+
   arcore_ = arcore_factory->Create();
   base::Optional<ArCore::InitializeResult> maybe_initialize_result =
       arcore_->Initialize(application_context, required_features,
-                          optional_features, tracked_images);
+                          optional_features, tracked_images,
+                          std::move(depth_sensing_config));
   if (!maybe_initialize_result) {
     DLOG(ERROR) << "ARCore failed to initialize";
     std::move(callback).Run(base::nullopt);
@@ -156,6 +187,7 @@
   // behavior of local and unbounded spaces & send appropriate data back in
   // GetFrameData().
   enabled_features_ = maybe_initialize_result->enabled_features;
+  depth_configuration_ = maybe_initialize_result->depth_configuration;
 
   DVLOG(3) << "ar_image_transport_->Initialize()...";
   ar_image_transport_->Initialize(
@@ -174,7 +206,8 @@
   DVLOG(3) << __func__;
   is_initialized_ = true;
   webxr_->NotifyMailboxBridgeReady();
-  std::move(callback).Run(enabled_features_);
+  std::move(callback).Run(
+      ArCoreGlInitializeResult(enabled_features_, depth_configuration_));
 }
 
 void ArCoreGl::CreateSession(mojom::VRDisplayInfoPtr display_info,
@@ -217,11 +250,12 @@
 
   display_info_ = std::move(display_info);
 
-  std::move(create_callback)
-      .Run(frame_data_receiver_.BindNewPipeAndPassRemote(),
-           display_info_->Clone(),
-           session_controller_receiver_.BindNewPipeAndPassRemote(),
-           std::move(submit_frame_sink));
+  ArCoreGlCreateSessionResult result(
+      frame_data_receiver_.BindNewPipeAndPassRemote(), display_info_->Clone(),
+      session_controller_receiver_.BindNewPipeAndPassRemote(),
+      std::move(submit_frame_sink));
+
+  std::move(create_callback).Run(std::move(result));
 
   frame_data_receiver_.set_disconnect_handler(base::BindOnce(
       &ArCoreGl::OnBindingDisconnect, weak_ptr_factory_.GetWeakPtr()));
diff --git a/device/vr/android/arcore/arcore_gl.h b/device/vr/android/arcore/arcore_gl.h
index 9c5d704..cd69f1c1 100644
--- a/device/vr/android/arcore/arcore_gl.h
+++ b/device/vr/android/arcore/arcore_gl.h
@@ -50,14 +50,37 @@
 class ArImageTransport;
 class WebXrPresentationState;
 
-using ArCoreGlCreateSessionCallback = base::OnceCallback<void(
-    mojo::PendingRemote<mojom::XRFrameDataProvider> frame_data_provider,
-    mojom::VRDisplayInfoPtr display_info,
-    mojo::PendingRemote<mojom::XRSessionController> session_controller,
-    mojom::XRPresentationConnectionPtr presentation_connection)>;
+struct ArCoreGlCreateSessionResult {
+  mojo::PendingRemote<mojom::XRFrameDataProvider> frame_data_provider;
+  mojom::VRDisplayInfoPtr display_info;
+  mojo::PendingRemote<mojom::XRSessionController> session_controller;
+  mojom::XRPresentationConnectionPtr presentation_connection;
 
-using ArCoreGlInitializeCallback = base::OnceCallback<void(
-    base::Optional<std::unordered_set<device::mojom::XRSessionFeature>>)>;
+  ArCoreGlCreateSessionResult(
+      mojo::PendingRemote<mojom::XRFrameDataProvider> frame_data_provider,
+      mojom::VRDisplayInfoPtr display_info,
+      mojo::PendingRemote<mojom::XRSessionController> session_controller,
+      mojom::XRPresentationConnectionPtr presentation_connection);
+  ArCoreGlCreateSessionResult(ArCoreGlCreateSessionResult&& other);
+  ~ArCoreGlCreateSessionResult();
+};
+
+using ArCoreGlCreateSessionCallback =
+    base::OnceCallback<void(ArCoreGlCreateSessionResult)>;
+
+struct ArCoreGlInitializeResult {
+  std::unordered_set<device::mojom::XRSessionFeature> enabled_features;
+  base::Optional<device::mojom::XRDepthConfig> depth_configuration;
+
+  ArCoreGlInitializeResult(
+      std::unordered_set<device::mojom::XRSessionFeature> enabled_features,
+      base::Optional<device::mojom::XRDepthConfig> depth_configuration);
+  ArCoreGlInitializeResult(ArCoreGlInitializeResult&& other);
+  ~ArCoreGlInitializeResult();
+};
+
+using ArCoreGlInitializeCallback =
+    base::OnceCallback<void(base::Optional<ArCoreGlInitializeResult>)>;
 
 // All of this class's methods must be called on the same valid GL thread with
 // the exception of GetGlThreadTaskRunner() and GetWeakPtr().
@@ -80,6 +103,7 @@
       const std::unordered_set<device::mojom::XRSessionFeature>&
           optional_features,
       const std::vector<device::mojom::XRTrackedImagePtr>& tracked_images,
+      device::mojom::XRDepthOptionsPtr depth_options,
       ArCoreGlInitializeCallback callback);
 
   void CreateSession(mojom::VRDisplayInfoPtr display_info,
@@ -197,6 +221,7 @@
   // the session and only send out necessary data related to reference spaces to
   // blink. Valid after the call to |Initialize()| method.
   std::unordered_set<device::mojom::XRSessionFeature> enabled_features_;
+  base::Optional<device::mojom::XRDepthConfig> depth_configuration_;
 
   base::OnceClosure session_shutdown_callback_;
 
diff --git a/device/vr/android/arcore/arcore_impl.cc b/device/vr/android/arcore/arcore_impl.cc
index 94cb36a6..c0cc152 100644
--- a/device/vr/android/arcore/arcore_impl.cc
+++ b/device/vr/android/arcore/arcore_impl.cc
@@ -352,7 +352,8 @@
         required_features,
     const std::unordered_set<device::mojom::XRSessionFeature>&
         optional_features,
-    const std::vector<device::mojom::XRTrackedImagePtr>& tracked_images) {
+    const std::vector<device::mojom::XRTrackedImagePtr>& tracked_images,
+    base::Optional<ArCore::DepthSensingConfiguration> depth_sensing_config) {
   DCHECK(IsOnGlThread());
   DCHECK(!arcore_session_.is_valid());
 
@@ -383,8 +384,9 @@
   DVLOG(1) << __func__ << ": ARCore incognito mode enabled";
 
   base::Optional<std::unordered_set<device::mojom::XRSessionFeature>>
-      maybe_enabled_features = ConfigureFeatures(
-          session.get(), required_features, optional_features, tracked_images);
+      maybe_enabled_features =
+          ConfigureFeatures(session.get(), required_features, optional_features,
+                            tracked_images, depth_sensing_config);
 
   if (!maybe_enabled_features) {
     DLOG(ERROR) << "Failed to configure session features";
@@ -422,7 +424,9 @@
       base::PassKey<ArCoreImpl>(), arcore_session_.get());
   plane_manager_ = std::make_unique<ArCorePlaneManager>(
       base::PassKey<ArCoreImpl>(), arcore_session_.get());
-  return ArCore::InitializeResult(*maybe_enabled_features);
+
+  return ArCore::InitializeResult(*maybe_enabled_features,
+                                  depth_configuration_);
 }
 
 base::Optional<std::unordered_set<device::mojom::XRSessionFeature>>
@@ -432,7 +436,9 @@
         required_features,
     const std::unordered_set<device::mojom::XRSessionFeature>&
         optional_features,
-    const std::vector<device::mojom::XRTrackedImagePtr>& tracked_images) {
+    const std::vector<device::mojom::XRTrackedImagePtr>& tracked_images,
+    const base::Optional<ArCore::DepthSensingConfiguration>&
+        depth_sensing_config) {
   // Let's assume we will be able to configure a session with all features -
   // this will be adjusted if it turns out we can only create a session w/o some
   // optional features. Currently, only depth sensing is not supported across
@@ -505,15 +511,25 @@
                      device::mojom::XRSessionFeature::DEPTH) ||
       depth_api_optional;
 
-  if (depth_api_requested) {
+  const bool depth_api_configuration_successful =
+      depth_api_requested && ConfigureDepthSensing(depth_sensing_config);
+
+  if (depth_api_configuration_successful) {
+    // Don't try to set the depth mode if we know we won't be able to support
+    // the desired usage and data format.
     ArConfig_setDepthMode(ar_session, arcore_config.get(),
                           AR_DEPTH_MODE_AUTOMATIC);
   }
 
   ArStatus status = ArSession_configure(ar_session, arcore_config.get());
-  if (status != AR_SUCCESS && depth_api_optional) {
-    // Depth API is not available on some ARCore-capable devices - if it was
+  if (status != AR_SUCCESS && depth_api_configuration_successful &&
+      depth_api_optional) {
+    // Configuring an ARCore session failed for some reason.
+    // Depth API may not be available on some ARCore-capable devices - if it was
     // requested optionally, let's try to request the session w/o it.
+    // Currently, Depth API is the only feature that is not supported across the
+    // board, so we assume that it is the reason why the session creation
+    // failed.
 
     DLOG(WARNING) << __func__
                   << ": Depth API was optionally requested and the session "
@@ -535,6 +551,30 @@
   return enabled_features;
 }
 
+bool ArCoreImpl::ConfigureDepthSensing(
+    const base::Optional<ArCore::DepthSensingConfiguration>&
+        depth_sensing_config) {
+  if (!depth_sensing_config) {
+    return false;
+  }
+
+  if (!base::Contains(depth_sensing_config->depth_usage_preference,
+                      device::mojom::XRDepthUsage::kCPUOptimized)) {
+    return false;
+  }
+
+  if (!base::Contains(depth_sensing_config->depth_data_format_preference,
+                      device::mojom::XRDepthDataFormat::kLuminanceAlpha)) {
+    return false;
+  }
+
+  depth_configuration_ = device::mojom::XRDepthConfig(
+      device::mojom::XRDepthUsage::kCPUOptimized,
+      device::mojom::XRDepthDataFormat::kLuminanceAlpha);
+
+  return true;
+}
+
 bool ArCoreImpl::ConfigureCamera(ArSession* ar_session) {
   internal::ScopedArCoreObject<ArCameraConfigFilter*> camera_config_filter;
   ArCameraConfigFilter_create(
@@ -1819,6 +1859,8 @@
     // Transform needed to consume the data:
     result->norm_texture_from_norm_view = GetCameraUvFromScreenUvTransform();
     result->size = gfx::Size(width, height);
+    result->raw_value_to_meters =
+        1.0 / 1000.0;  // DepthInMillimeters * 1/1000 = DepthInMeters
 
     DVLOG(3) << __func__ << ": norm_texture_from_norm_view=\n"
              << result->norm_texture_from_norm_view.ToString();
diff --git a/device/vr/android/arcore/arcore_impl.h b/device/vr/android/arcore/arcore_impl.h
index c6117f5..9051b15 100644
--- a/device/vr/android/arcore/arcore_impl.h
+++ b/device/vr/android/arcore/arcore_impl.h
@@ -112,7 +112,8 @@
           required_features,
       const std::unordered_set<device::mojom::XRSessionFeature>&
           optional_features,
-      const std::vector<device::mojom::XRTrackedImagePtr>& tracked_images)
+      const std::vector<device::mojom::XRTrackedImagePtr>& tracked_images,
+      base::Optional<ArCore::DepthSensingConfiguration> depth_sensing_config)
       override;
   MinMaxRange GetTargetFramerateRange() override;
   void SetDisplayGeometry(const gfx::Size& frame_size,
@@ -222,6 +223,8 @@
   // list of images. The index values are needed for blink communication.
   std::unordered_map<int32_t, uint64_t> tracked_image_arcore_id_to_index_;
 
+  base::Optional<device::mojom::XRDepthConfig> depth_configuration_;
+
   uint64_t next_id_ = 1;
 
   std::map<HitTestSubscriptionId, HitTestSubscriptionData>
@@ -328,7 +331,17 @@
           required_features,
       const std::unordered_set<device::mojom::XRSessionFeature>&
           optional_features,
-      const std::vector<device::mojom::XRTrackedImagePtr>& tracked_images);
+      const std::vector<device::mojom::XRTrackedImagePtr>& tracked_images,
+      const base::Optional<ArCore::DepthSensingConfiguration>&
+          depth_sensing_config);
+
+  // Configures depth sensing API - selects depth sensing usage and mode that is
+  // compatible with the device. Returns false if it was unable to pick a
+  // supported combination of mode and data format. Affects
+  // |depth_sensing_usage_| and |depth_sensing_data_format_| members.
+  bool ConfigureDepthSensing(
+      const base::Optional<ArCore::DepthSensingConfiguration>&
+          depth_sensing_config);
 
   // Must be last.
   base::WeakPtrFactory<ArCoreImpl> weak_ptr_factory_{this};
diff --git a/device/vr/public/mojom/isolated_xr_service.mojom b/device/vr/public/mojom/isolated_xr_service.mojom
index 321ea5a..92c7c31f 100644
--- a/device/vr/public/mojom/isolated_xr_service.mojom
+++ b/device/vr/public/mojom/isolated_xr_service.mojom
@@ -55,6 +55,8 @@
   int32 render_frame_id;
 
   array<XRTrackedImage> tracked_images;
+
+  XRDepthOptions? depth_options;
 };
 
 // An XRRuntime may live in the browser process or a utility process.  The
diff --git a/device/vr/public/mojom/vr_service.mojom b/device/vr/public/mojom/vr_service.mojom
index fd77143d..f83cc24 100644
--- a/device/vr/public/mojom/vr_service.mojom
+++ b/device/vr/public/mojom/vr_service.mojom
@@ -101,6 +101,16 @@
   kAdditive = 3,
 };
 
+enum XRDepthUsage {
+  kCPUOptimized = 1,
+  kGPUOptimized = 2,
+};
+
+enum XRDepthDataFormat {
+  kLuminanceAlpha = 1,
+  kFloat32 = 2,
+};
+
 // These enum names come from the WebXR spec:
 // https://immersive-web.github.io/webxr-ar-module/#enumdef-xrinteractionmode
 // The interactionMode attribute describes the best space
@@ -117,6 +127,25 @@
   float width_in_meters;
 };
 
+// Structure used to configure depth sensing API at session creation.
+// If the device is unable to create the session given the preferences expressed
+// in the options, the session will not have depth sensing API enabled. In case
+// it was a required feature, this means that session creation will fail.
+struct XRDepthOptions {
+  // Ordered array of depth sensing usage preferences. At session creation,
+  // the device will pick the depth sensing usage value that it supports,
+  /// starting from the lowest-indexed one. `kNone` entry is not allowed to
+  // appear in this array, and the array should not be empty.
+  array<XRDepthUsage> usage_preferences;
+  // Ordered array of depth sensing data format preferences. At session
+  // creation, the device will pick the depth sensing data format value that it
+  // supports, starting from the lowest-indexed one. `kNone` entry is not
+  // allowed to appear in this array, and the array should not be empty.
+  // The data format selection happens after the device has selected depth
+  // sensing usage.
+  array<XRDepthDataFormat> data_format_preferences;
+};
+
 struct XRSessionOptions {
   // Represents the type of session that is being requested.
   XRSessionMode mode;
@@ -132,6 +161,15 @@
   array<XRSessionFeature> optional_features;
 
   array<XRTrackedImage> tracked_images;
+
+  // Must be present if either optional or required features contain
+  // depth sensing API.
+  XRDepthOptions? depth_options;
+};
+
+struct XRDepthConfig {
+  XRDepthUsage depth_usage;
+  XRDepthDataFormat depth_data_format;
 };
 
 // This structure contains a description of the device's active configuration
@@ -155,6 +193,10 @@
   // Indicates whether we should enable anti-aliasing for WebGL layers. Value
   // comes from the underlying XR runtime.
   bool enable_anti_aliasing = true;
+
+  // If the session supports depth-sensing API, the depth configuration must be
+  // non-null.
+  XRDepthConfig? depth_configuration;
 };
 
 // This structure contains all the mojo interfaces for different kinds of
@@ -609,6 +651,9 @@
   gfx.mojom.Transform norm_texture_from_norm_view;
   // Size of the pixel array. Valid iff pixel_data is not null.
   gfx.mojom.Size size;
+  // Factor that needs to be applied when converting from raw values in the
+  // |pixel_data| buffer into meters.
+  float raw_value_to_meters;
 };
 
 // Depth data may be the same as the one returned in the previous frame - it is
diff --git a/docs/ios/build_instructions.md b/docs/ios/build_instructions.md
index 8979f13..7920f090 100644
--- a/docs/ios/build_instructions.md
+++ b/docs/ios/build_instructions.md
@@ -353,6 +353,10 @@
 debugger like `base::string16`, ... If you want to use `lldb` directly, name
 the file `~/.lldbinit` instead of `~/.lldbinit-Xcode`.
 
+Note: if you are using `ios/build/tools/setup-gn.py` to generate the Xcode
+project, the script also generate an `.lldbinit` file next to the project and
+configure Xcode to use that file instead of the global one.
+
 ### Changing the version of Xcode
 
 To change the version of Xcode used to build Chromium on iOS, please follow
diff --git a/extensions/browser/api/file_handlers/app_file_handler_util.cc b/extensions/browser/api/file_handlers/app_file_handler_util.cc
index 7db779b5c..3278de1 100644
--- a/extensions/browser/api/file_handlers/app_file_handler_util.cc
+++ b/extensions/browser/api/file_handlers/app_file_handler_util.cc
@@ -422,7 +422,7 @@
 
   storage::IsolatedContext::ScopedFSHandle filesystem =
       isolated_context->RegisterFileSystemForPath(
-          storage::kFileSystemTypeNativeForPlatformApp, std::string(), path,
+          storage::kFileSystemTypeLocalForPlatformApp, std::string(), path,
           &result.registered_name);
   result.filesystem_id = filesystem.id();
 
@@ -501,7 +501,7 @@
   // The file system API is only intended to operate on file entries that
   // correspond to a native file, selected by the user so only allow file
   // systems returned by the file system API or from a drag and drop operation.
-  if (type != storage::kFileSystemTypeNativeForPlatformApp &&
+  if (type != storage::kFileSystemTypeLocalForPlatformApp &&
       type != storage::kFileSystemTypeDragged) {
     *error = kInvalidParameters;
     return false;
diff --git a/extensions/browser/api/file_handlers/mime_util_unittest.cc b/extensions/browser/api/file_handlers/mime_util_unittest.cc
index cbbcad1..6a9d8ba 100644
--- a/extensions/browser/api/file_handlers/mime_util_unittest.cc
+++ b/extensions/browser/api/file_handlers/mime_util_unittest.cc
@@ -46,9 +46,9 @@
 storage::FileSystemURL CreateNativeLocalFileSystemURL(
     storage::FileSystemContext* context,
     const base::FilePath local_path) {
-  return context->CreateCrackedFileSystemURL(
-      url::Origin::Create(GURL(kOrigin)), storage::kFileSystemTypeNativeLocal,
-      local_path);
+  return context->CreateCrackedFileSystemURL(url::Origin::Create(GURL(kOrigin)),
+                                             storage::kFileSystemTypeLocal,
+                                             local_path);
 }
 
 }  // namespace
diff --git a/extensions/browser/api/file_system/file_system_api.cc b/extensions/browser/api/file_system/file_system_api.cc
index a739091f..401c343 100644
--- a/extensions/browser/api/file_system/file_system_api.cc
+++ b/extensions/browser/api/file_system/file_system_api.cc
@@ -484,8 +484,8 @@
   // smoothly, all accessed paths need to be registered in the list of
   // external mount points.
   storage::ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
-      name, storage::kFileSystemTypeNativeLocal,
-      storage::FileSystemMountOption(), path);
+      name, storage::kFileSystemTypeLocal, storage::FileSystemMountOption(),
+      path);
 }
 
 void FileSystemChooseEntryFunction::FilesSelected(
diff --git a/extensions/browser/api/runtime/runtime_api.cc b/extensions/browser/api/runtime/runtime_api.cc
index 7e70eab..cf4f4ae0 100644
--- a/extensions/browser/api/runtime/runtime_api.cc
+++ b/extensions/browser/api/runtime/runtime_api.cc
@@ -733,8 +733,7 @@
   base::FilePath path = extension_->path();
   storage::IsolatedContext::ScopedFSHandle filesystem =
       isolated_context->RegisterFileSystemForPath(
-          storage::kFileSystemTypeNativeLocal, std::string(), path,
-          &relative_path);
+          storage::kFileSystemTypeLocal, std::string(), path, &relative_path);
 
   content::ChildProcessSecurityPolicy* policy =
       content::ChildProcessSecurityPolicy::GetInstance();
diff --git a/extensions/common/api/_manifest_features.json b/extensions/common/api/_manifest_features.json
index ffb970a..77faf04 100644
--- a/extensions/common/api/_manifest_features.json
+++ b/extensions/common/api/_manifest_features.json
@@ -24,7 +24,6 @@
   "app.content_security_policy": {
     "channel": "stable",
     "extension_types": ["platform_app"],
-    "min_manifest_version": 2,
     "allowlist": [
       // Keep this list in sync with extensions_misc::kHangoutsExtensionIds but
       // omit the Packaged App ids.
@@ -38,8 +37,7 @@
   },
   "app.background": {
     "channel": "stable",
-    "extension_types": ["platform_app"],
-    "min_manifest_version": 2
+    "extension_types": ["platform_app"]
   },
   "background": {
     "channel": "stable",
@@ -52,13 +50,11 @@
     "channel": "stable",
     "extension_types": [
       "extension", "legacy_packaged_app", "login_screen_extension"
-    ],
-    "min_manifest_version": 2
+    ]
   },
   "background.service_worker": {
     "channel": "stable",
-    "extension_types": ["extension"],
-    "min_manifest_version": 2
+    "extension_types": ["extension"]
   },
   "bluetooth": [{
     // Note: The "bluetooth" manifest permission is used by the
@@ -122,7 +118,6 @@
   "declarative_net_request": {
     "channel": "stable",
     "extension_types": ["extension"],
-    "min_manifest_version": 2,
     "feature_flag": "DeclarativeNetRequest"
   },
   "default_locale": {
@@ -305,20 +300,17 @@
   },
   "replacement_android_app": {
     "channel": "trunk",
-    "extension_types": ["platform_app"],
-    "min_manifest_version": 2
+    "extension_types": ["platform_app"]
   },
   "replacement_web_app": {
     "channel": "stable",
-    "extension_types": ["extension", "platform_app"],
-    "min_manifest_version": 2
+    "extension_types": ["extension", "platform_app"]
   },
   "sandbox": {
     "channel": "stable",
     "extension_types": [
       "extension", "platform_app", "legacy_packaged_app"
-    ],
-    "min_manifest_version": 2
+    ]
   },
   "sockets": [
     {
@@ -376,7 +368,6 @@
   },
   "webview": {
     "channel": "stable",
-    "extension_types": ["platform_app"],
-    "min_manifest_version": 2
+    "extension_types": ["platform_app"]
   }
 }
diff --git a/extensions/common/api/_permission_features.json b/extensions/common/api/_permission_features.json
index 9a9a665..51f353e 100644
--- a/extensions/common/api/_permission_features.json
+++ b/extensions/common/api/_permission_features.json
@@ -13,8 +13,7 @@
 {
   "alarms": {
     "channel": "stable",
-    "extension_types": ["extension", "legacy_packaged_app", "platform_app"],
-    "min_manifest_version": 2
+    "extension_types": ["extension", "legacy_packaged_app", "platform_app"]
   },
   "app.window.alwaysOnTop": {
     "channel": "stable",
@@ -202,13 +201,11 @@
   "declarativeNetRequest": {
     "channel": "stable",
     "extension_types": ["extension"],
-    "min_manifest_version": 2,
     "feature_flag": "DeclarativeNetRequest"
   },
   "declarativeNetRequestFeedback": {
     "channel": "stable",
     "extension_types": ["extension"],
-    "min_manifest_version": 2,
     "feature_flag": "DeclarativeNetRequest"
   },
   "declarativeWebRequest": {
@@ -556,19 +553,16 @@
   "storage": [
     {
       "channel": "stable",
-      "extension_types": ["extension", "legacy_packaged_app", "platform_app"],
-      "min_manifest_version": 2
+      "extension_types": ["extension", "legacy_packaged_app", "platform_app"]
     },
     {
       "channel": "stable",
       "dependencies": ["behavior:imprivata_login_screen_extension"],
-      "extension_types": ["login_screen_extension"],
-      "min_manifest_version": 2
+      "extension_types": ["login_screen_extension"]
     },
     {
       "channel": "stable",
       "extension_types": ["login_screen_extension"],
-      "min_manifest_version": 2,
       "allowlist": [
         "7FE4A999359A456C4B0FB7B7AD85CEA29CA50519"  // Login screen APIs test extension
       ]
diff --git a/gpu/config/gpu_switches.cc b/gpu/config/gpu_switches.cc
index cb27830..e4f60cc 100644
--- a/gpu/config/gpu_switches.cc
+++ b/gpu/config/gpu_switches.cc
@@ -110,4 +110,8 @@
 // TODO(crbug/1158000): Remove this switch.
 const char kVulkanSyncCpuMemoryLimitMb[] = "vulkan-sync-cpu-memory-limit-mb";
 
+// Crash Chrome if GPU process crashes. This is to force a test to fail when
+// GPU process crashes unexpectedly.
+const char kForceBrowserCrashOnGpuCrash[] = "force-browser-crash-on-gpu-crash";
+
 }  // namespace switches
diff --git a/gpu/config/gpu_switches.h b/gpu/config/gpu_switches.h
index 873dc80..92cfde1 100644
--- a/gpu/config/gpu_switches.h
+++ b/gpu/config/gpu_switches.h
@@ -36,6 +36,7 @@
 GPU_EXPORT extern const char kDisableVulkanFallbackToGLForTesting[];
 GPU_EXPORT extern const char kVulkanHeapMemoryLimitMb[];
 GPU_EXPORT extern const char kVulkanSyncCpuMemoryLimitMb[];
+GPU_EXPORT extern const char kForceBrowserCrashOnGpuCrash[];
 
 }  // namespace switches
 
diff --git a/gpu/vulkan/BUILD.gn b/gpu/vulkan/BUILD.gn
index 058840e5..1de4865 100644
--- a/gpu/vulkan/BUILD.gn
+++ b/gpu/vulkan/BUILD.gn
@@ -60,17 +60,12 @@
       "vma_wrapper.h",
     ]
 
-    defines = [
-      "IS_VULKAN_IMPL",
-      "VMA_DYNAMIC_VULKAN_FUNCTIONS=0",
-      "VMA_STATS_STRING_ENABLED=0",
-      "VMA_IMPLEMENTATION",
-    ]
+    defines = [ "IS_VULKAN_IMPL" ]
 
     deps = [
       ":vulkan_function_pointers",
       "//base",
-      "//third_party/vulkan_memory_allocator",
+      "//third_party/vulkan_memory_allocator:vulkan_memory_allocator_with_usage",
     ]
   }
 
diff --git a/infra/config/generated/commit-queue.cfg b/infra/config/generated/commit-queue.cfg
index ebccaec..5f1aeed 100644
--- a/infra/config/generated/commit-queue.cfg
+++ b/infra/config/generated/commit-queue.cfg
@@ -1109,7 +1109,7 @@
       }
       builders {
         name: "chromium/try/linux-rel-rts"
-        experiment_percentage: 1
+        experiment_percentage: 5
         location_regexp: ".*"
         location_regexp_exclude: ".+/[+]/docs/.+"
         location_regexp_exclude: ".+/[+]/infra/config/.+"
diff --git a/infra/config/generated/cq-builders.md b/infra/config/generated/cq-builders.md
index 68365b1d..71d3b3c 100644
--- a/infra/config/generated/cq-builders.md
+++ b/infra/config/generated/cq-builders.md
@@ -384,5 +384,5 @@
   * [`//services/tracing/.+`](https://cs.chromium.org/chromium/src/services/tracing/)
 
 * [linux-rel-rts](https://ci.chromium.org/p/chromium/builders/try/linux-rel-rts) ([definition](https://cs.chromium.org/search?q=package:%5Echromium$+file:/cq.star$+-file:/beta/+-file:/stable/+linux-rel-rts)) ([matching builders](https://cs.chromium.org/search?q=+file:trybots.py+linux-rel-rts))
-  * Experiment percentage: 1.0
+  * Experiment percentage: 5.0
 
diff --git a/infra/config/generated/cr-buildbucket.cfg b/infra/config/generated/cr-buildbucket.cfg
index fc16033..dba0cd5 100644
--- a/infra/config/generated/cr-buildbucket.cfg
+++ b/infra/config/generated/cr-buildbucket.cfg
@@ -42477,12 +42477,11 @@
       name: "linux-rel-rts"
       swarming_host: "chromium-swarm.appspot.com"
       swarming_tags: "vpython:native-python-wrapper"
-      dimensions: "builderless:1"
+      dimensions: "builder:linux-rel-rts"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "os:Ubuntu-16.04"
       dimensions: "pool:luci.chromium.try"
-      dimensions: "ssd:0"
       exe {
         cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
         cipd_version: "refs/heads/master"
diff --git a/infra/config/subprojects/chromium/try.star b/infra/config/subprojects/chromium/try.star
index df38182..f28a072 100644
--- a/infra/config/subprojects/chromium/try.star
+++ b/infra/config/subprojects/chromium/try.star
@@ -1055,9 +1055,9 @@
 
 try_.chromium_linux_builder(
     name = "linux-rel-rts",
-    builderless = True,
+    builderless = False,
     goma_jobs = goma.jobs.J150,
-    tryjob = try_.job(experiment_percentage = 1),
+    tryjob = try_.job(experiment_percentage = 5),
     use_clang_coverage = True,
 )
 
diff --git a/ios/build/tools/convert_gn_xcodeproj.py b/ios/build/tools/convert_gn_xcodeproj.py
index e7ae0ca3..30932f70 100755
--- a/ios/build/tools/convert_gn_xcodeproj.py
+++ b/ios/build/tools/convert_gn_xcodeproj.py
@@ -16,16 +16,36 @@
 import collections
 import copy
 import filecmp
-import json
 import hashlib
+import json
 import os
 import re
 import shutil
+import string
 import subprocess
 import sys
 import tempfile
 
 
+class Template(string.Template):
+
+  """A subclass of string.Template that changes delimiter."""
+
+  delimiter = '@'
+
+
+def LoadSchemeTemplate(root):
+  """Return a string.Template object for scheme file loaded relative to root."""
+  path = os.path.join(root, 'ios', 'build', 'tools', 'xcodescheme.template')
+  with open(path) as file:
+    return Template(file.read())
+
+
+def CreateIdentifier(str_id):
+  """Return a 24 characters string that can be used as an identifier."""
+  return hashlib.sha1(str_id.encode("utf-8")).hexdigest()[:24].upper()
+
+
 class XcodeProject(object):
 
   def __init__(self, objects, counter = 0):
@@ -36,7 +56,7 @@
     while True:
       self.counter += 1
       str_id = "%s %s %d" % (parent_name, obj['isa'], self.counter)
-      new_id = hashlib.sha1(str_id.encode("utf-8")).hexdigest()[:24].upper()
+      new_id = CreateIdentifier(str_id)
 
       # Make sure ID is unique. It's possible there could be an id conflict
       # since this is run after GN runs.
@@ -103,6 +123,11 @@
   json_data = json.loads(LoadXcodeProjectAsJSON(project_dir))
   project = XcodeProject(json_data['objects'])
 
+  schemes_template = LoadSchemeTemplate(root_dir)
+  schemes_directory = os.path.join(project_dir, 'xcshareddata', 'xcschemes')
+  if not os.path.isdir(schemes_directory):
+    os.makedirs(schemes_directory)
+
   objects_to_remove = []
   for value in list(project.objects.values()):
     isa = value['isa']
@@ -136,6 +161,27 @@
         value['buildConfigurations'].append(
             project.AddObject('products', new_build_config))
 
+    # Create scheme files for application, extensions and framework targets.
+    if isa == 'PBXNativeTarget':
+      product_type = value['productType']
+      if product_type not in (
+          'com.apple.product-type.app-extension',
+          'com.apple.product-type.application',
+          'com.apple.product-type.framework'):
+        continue
+
+      name, product_name = (value['name'], value['productName'])
+      identifier = CreateIdentifier('%s, %s' % (name, product_name))
+      product = project.objects[value['productReference']]
+
+      scheme_path = os.path.join(schemes_directory, value['name'] + '.xcscheme')
+      with open(scheme_path, 'w') as scheme_file:
+        scheme_file.write(
+            schemes_template.substitute(
+                BLUEPRINT_IDENTIFIER=identifier,
+                PRODUCT_NAME=product['path'],
+                TARGET_NAME=name))
+
   for object_id in objects_to_remove:
     del project.objects[object_id]
 
diff --git a/ios/build/tools/setup-gn.py b/ios/build/tools/setup-gn.py
index 9225b4e3..1224742 100755
--- a/ios/build/tools/setup-gn.py
+++ b/ios/build/tools/setup-gn.py
@@ -262,6 +262,25 @@
     if os.path.exists(temp_path):
       shutil.rmtree(temp_path)
 
+  # Generate an .lldbinit file for the project that load the script that fixes
+  # the mapping of source files (see docs/ios/build_instructions.md#debugging).
+  with open(os.path.join(out_dir, 'build', '.lldbinit'), 'w') as lldbinit:
+    lldb_script_dir = os.path.join(os.path.abspath(root_dir), 'tools', 'lldb')
+    lldbinit.write('script sys.path[:0] = [\'%s\']\n' % lldb_script_dir)
+    lldbinit.write('script import lldbinit\n')
+
+    workspace_name = settings.getstring(
+        'gn_args',
+        'ios_internal_citc_workspace_name')
+
+    if workspace_name != '':
+      username = os.environ['USER']
+      for shortname in ('googlemac', 'third_party', 'blaze-out'):
+        lldbinit.write('settings append target.source-map %s %s\n' % (
+            shortname,
+            '/google/src/cloud/%s/%s/google3/%s' % (
+                username, workspace_name, shortname)))
+
 
 def GenerateGnBuildRules(gn_path, root_dir, out_dir, settings):
   '''Generates all template configurations for gn.'''
diff --git a/ios/build/tools/xcodescheme.template b/ios/build/tools/xcodescheme.template
new file mode 100644
index 0000000..3d1a0d9b
--- /dev/null
+++ b/ios/build/tools/xcodescheme.template
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "1220"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "@{BLUEPRINT_IDENTIFIER}"
+               BuildableName = "@{PRODUCT_NAME}"
+               BlueprintName = "@{TARGET_NAME}"
+               ReferencedContainer = "container:all.xcodeproj">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES">
+      <Testables>
+      </Testables>
+   </TestAction>
+   <LaunchAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      customLLDBInitFile = "$(PROJECT_DIR)/.lldbinit"
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      debugServiceExtension = "internal"
+      allowLocationSimulation = "YES">
+      <BuildableProductRunnable
+         runnableDebuggingMode = "0">
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "@{BLUEPRINT_IDENTIFIER}"
+            BuildableName = "@{PRODUCT_NAME}"
+            BlueprintName = "@{TARGET_NAME}"
+            ReferencedContainer = "container:all.xcodeproj">
+         </BuildableReference>
+      </BuildableProductRunnable>
+   </LaunchAction>
+   <ProfileAction
+      buildConfiguration = "Release"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES">
+      <BuildableProductRunnable
+         runnableDebuggingMode = "0">
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "@{BLUEPRINT_IDENTIFIER}"
+            BuildableName = "@{PRODUCT_NAME}"
+            BlueprintName = "@{TARGET_NAME}"
+            ReferencedContainer = "container:all.xcodeproj">
+         </BuildableReference>
+      </BuildableProductRunnable>
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>
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 2be52e9..14ca4c5 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 @@
-e8542718d304c0e86b10d976f5fad98c6981d333
\ No newline at end of file
+8081b4834521c54a2414dc3518b331d248ebc434
\ 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 793fd84..d0dd16c8 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 @@
-9aca207fc758452304da972b06960ec46d12aeb9
\ No newline at end of file
+4ba1907161bc90c8978f6d45b06e65b2a4a0d81c
\ 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 a7c923c..cfbfd2c8 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 @@
-22b0c40ee22d599443df40fc7d7eab7354720f28
\ No newline at end of file
+653067a69fdb2e673ecd94368821c60dd215e851
\ 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 dd1b590f..220b119 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 @@
-967e88ff4fe6408df162ed999f809fe60c4f9ced
\ No newline at end of file
+7971ede669000419bdeac77f500c176ff8f7ee02
\ 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 df8e7eb1..ad0cb82f 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 @@
-9fdd7083dc848d421ae853078b4c9f2c333f9104
\ No newline at end of file
+86de9e89777a99fed13b78029731f00315c0608b
\ 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 4c755fb..d5eee18 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 @@
-b2bcd55d8fa56f7e20c56dcd79df9694f58c31e4
\ No newline at end of file
+6e3d5c3787856957f0d2660b27de705e4884e1f7
\ 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 6afd3d2e..9843d8c 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 @@
-7524514e1bca8fd9ca329deae2dc8aa9f8fc79a3
\ No newline at end of file
+cfa6728e7faf4de93e0d56d2d487932806ff9ab8
\ 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 05f69e4..eb46892c 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 @@
-ae88a9a31696595e53cfc3e2e4398bf1aa5de560
\ No newline at end of file
+08e583e294cd96605e22c92620c770e33f0a856a
\ No newline at end of file
diff --git a/media/base/android/java/src/org/chromium/media/MediaCodecBridgeBuilder.java b/media/base/android/java/src/org/chromium/media/MediaCodecBridgeBuilder.java
index 59aea2a..a73344b 100644
--- a/media/base/android/java/src/org/chromium/media/MediaCodecBridgeBuilder.java
+++ b/media/base/android/java/src/org/chromium/media/MediaCodecBridgeBuilder.java
@@ -30,26 +30,29 @@
         try {
             Log.i(TAG, "create MediaCodec video decoder, mime %s", mime);
             info = MediaCodecUtil.createDecoder(mime, codecType, mediaCrypto);
+
+            if (info.mediaCodec == null) return null;
+
+            MediaCodecBridge bridge =
+                    new MediaCodecBridge(info.mediaCodec, info.bitrateAdjuster, useAsyncApi);
+            byte[][] csds = {csd0, csd1};
+            MediaFormat format = MediaFormatBuilder.createVideoDecoderFormat(mime, width, height,
+                    csds, hdrMetadata, info.supportsAdaptivePlayback && allowAdaptivePlayback);
+
+            if (!bridge.configureVideo(format, surface, mediaCrypto, 0)) return null;
+
+            if (!bridge.start()) {
+                bridge.release();
+                return null;
+            }
+
+            return bridge;
         } catch (Exception e) {
             Log.e(TAG, "Failed to create MediaCodec video decoder: %s, codecType: %d", mime,
                     codecType, e);
         }
 
-        if (info.mediaCodec == null) return null;
-
-        MediaCodecBridge bridge =
-                new MediaCodecBridge(info.mediaCodec, info.bitrateAdjuster, useAsyncApi);
-        byte[][] csds = {csd0, csd1};
-        MediaFormat format = MediaFormatBuilder.createVideoDecoderFormat(mime, width, height, csds,
-                hdrMetadata, info.supportsAdaptivePlayback && allowAdaptivePlayback);
-
-        if (!bridge.configureVideo(format, surface, mediaCrypto, 0)) return null;
-
-        if (!bridge.start()) {
-            bridge.release();
-            return null;
-        }
-        return bridge;
+        return null;
     }
 
     @CalledByNative
diff --git a/media/gpu/vaapi/test/decode.cc b/media/gpu/vaapi/test/decode.cc
index ae4e596..0affb63 100644
--- a/media/gpu/vaapi/test/decode.cc
+++ b/media/gpu/vaapi/test/decode.cc
@@ -41,6 +41,7 @@
     "           --video=<video path>\n"
     "           [--frames=<number of frames to decode>]\n"
     "           [--out-prefix=<path prefix of decoded frame PNGs>]\n"
+    "           [--md5]\n"
     "           [--loop]\n"
     "           [--v=<log verbosity>]\n"
     "           [--help]\n";
@@ -63,6 +64,11 @@
     "        If specified along with --loop (see below), only saves the first\n"
     "        iteration of decoded frames.\n"
     "        If omitted, the output of this binary is error or lack thereof.\n"
+    "    --md5\n"
+    "        Optional. If specified, prints the md5 of each decoded and\n"
+    "        visible frame in I420 format to stdout.\n"
+    "        Only supported when vaDeriveImage() produces I420 and NV12 data\n"
+    "        for all frames in the video.\n"
     "    --loop\n"
     "        Optional. If specified, loops decoding until terminated\n"
     "        externally or until an error occurs, at which point the current\n"
@@ -195,6 +201,9 @@
         dec->LastDecodedFrameToPNG(
             base::StringPrintf("%s_%d.png", output_prefix.c_str(), i));
       }
+      if (cmd->HasSwitch("md5") && dec->LastDecodedFrameVisible()) {
+        std::cout << dec->LastDecodedFrameMD5Sum() << std::endl;
+      }
 
       if (++i == n_frames)
         break;
diff --git a/media/gpu/vaapi/test/shared_va_surface.cc b/media/gpu/vaapi/test/shared_va_surface.cc
index ca866c9..b9da6cf 100644
--- a/media/gpu/vaapi/test/shared_va_surface.cc
+++ b/media/gpu/vaapi/test/shared_va_surface.cc
@@ -5,6 +5,7 @@
 #include <va/va.h>
 
 #include "base/files/file_util.h"
+#include "base/hash/md5.h"
 #include "media/base/video_types.h"
 #include "media/gpu/vaapi/test/macros.h"
 #include "media/gpu/vaapi/test/shared_va_surface.h"
@@ -16,6 +17,13 @@
 
 namespace {
 
+// Returns whether the image format |fourcc| is supported for MD5 hash checking.
+// MD5 golden values are computed from vpxdec based on I420, and only certain
+// format conversions are implemented.
+bool IsSupportedFormat(uint32_t fourcc) {
+  return fourcc == VA_FOURCC_I420 || fourcc == VA_FOURCC_NV12;
+}
+
 // Derives the VAImage metadata and image data from |surface_id| in |display|,
 // returning true on success.
 bool DeriveImage(VADisplay display,
@@ -26,10 +34,13 @@
   VLOG_IF(2, (res != VA_STATUS_SUCCESS))
       << "vaDeriveImage failed, VA error: " << vaErrorStr(res);
 
+  const uint32_t fourcc = image->format.fourcc;
+  DCHECK_NE(fourcc, 0u);
+
   // TODO(jchinlee): Support derivation into 10-bit fourcc.
-  if (image->format.fourcc != VA_FOURCC_NV12) {
+  if (!IsSupportedFormat(fourcc)) {
     VLOG(2) << "Test decoder binary does not support derived surface format "
-            << "with fourcc " << media::FourccToString(image->format.fourcc);
+            << "with fourcc " << media::FourccToString(fourcc);
     res = vaDestroyImage(display, image->image_id);
     VA_LOG_ASSERT(res, "vaDestroyImage");
     return false;
@@ -40,7 +51,7 @@
   return true;
 }
 
-// Returns image format to use given the surface's internal VA format.
+// Returns image format to fall back to given the surface's internal VA format.
 VAImageFormat GetImageFormat(unsigned int va_rt_format) {
   constexpr VAImageFormat kImageFormatNV12{.fourcc = VA_FOURCC_NV12,
                                            .byte_order = VA_LSB_FIRST,
@@ -59,22 +70,15 @@
   }
 }
 
-// Maps the image data from |surface_id| in |display| with given |size| by
-// attempting to derive into |image| and |image_data|, or creating a
-// VAImage to use with vaGetImage as fallback and setting |image| and
-// |image_data| accordingly.
+// Retrieves the image data from |surface_id| in |display| with given |size| by
+// creating a VAImage with |format| to use with vaGetImage and setting |image|
+// and |image_data| accordingly.
 void GetSurfaceImage(VADisplay display,
                      VASurfaceID surface_id,
-                     unsigned int va_rt_format,
+                     VAImageFormat format,
                      const gfx::Size size,
                      VAImage* image,
                      uint8_t** image_data) {
-  // First attempt to derive the image from the surface.
-  if (DeriveImage(display, surface_id, image, image_data))
-    return;
-
-  // Fall back to getting the image with manually passed format.
-  VAImageFormat format = GetImageFormat(va_rt_format);
   VAStatus res =
       vaCreateImage(display, &format, size.width(), size.height(), image);
   VA_LOG_ASSERT(res, "vaCreateImage");
@@ -130,13 +134,15 @@
   VAImage image;
   uint8_t* image_data;
 
-  GetSurfaceImage(va_device_.display(), id_, va_rt_format_, size_, &image,
-                  &image_data);
+  // For saving as PNG and visual comparison, we just try to get the image data
+  // in *some* format, so use the preferred VAImageFormat.
+  GetSurfaceImage(va_device_.display(), id_, GetImageFormat(va_rt_format_),
+                  size_, &image, &image_data);
 
   // Convert the image data to ARGB and write to |path|.
   const size_t argb_stride = image.width * 4;
   auto argb_data = std::make_unique<uint8_t[]>(argb_stride * image.height);
-  int convert_res = 0;
+  int convert_res = -1;
   const uint32_t fourcc = image.format.fourcc;
   DCHECK(fourcc == VA_FOURCC_NV12 || fourcc == VA_FOURCC_P010);
 
@@ -200,5 +206,58 @@
   VA_LOG_ASSERT(res, "vaDestroyImage");
 }
 
+std::string SharedVASurface::GetMD5Sum() const {
+  VAImage image;
+  uint8_t* image_data;
+  LOG_ASSERT(DeriveImage(va_device_.display(), id_, &image, &image_data));
+
+  // Golden values of MD5 sums are computed from vpxdec with packed I420 as the
+  // format, so convert as needed.
+  uint32_t luma_plane_size =
+      base::checked_cast<uint32_t>(image.height * image.width);
+  uint32_t chroma_plane_size = base::checked_cast<uint32_t>(
+      ((image.height + 1) / 2) * ((image.width + 1) / 2));
+  std::vector<uint8_t> i420_data(luma_plane_size + 2 * chroma_plane_size, 0u);
+  int convert_res = -1;
+  const uint32_t fourcc = image.format.fourcc;
+  if (fourcc == VA_FOURCC_I420) {
+    // I420 still needs to be packed.
+    LOG_ASSERT(image.num_planes == 3u);
+    convert_res = libyuv::I420Copy(
+        image_data + image.offsets[0],
+        base::checked_cast<int>(image.pitches[0]),
+        image_data + image.offsets[1],
+        base::checked_cast<int>(image.pitches[1]),
+        image_data + image.offsets[2],
+        base::checked_cast<int>(image.pitches[2]), i420_data.data(),
+        base::strict_cast<int>(image.width), i420_data.data() + luma_plane_size,
+        base::checked_cast<int>((image.width + 1) / 2),
+        i420_data.data() + luma_plane_size + chroma_plane_size,
+        base::checked_cast<int>((image.width + 1) / 2),
+        base::strict_cast<int>(image.width),
+        base::strict_cast<int>(image.height));
+  } else if (fourcc == VA_FOURCC_NV12) {
+    LOG_ASSERT(image.num_planes == 2u);
+    convert_res = libyuv::NV12ToI420(
+        image_data + image.offsets[0],
+        base::checked_cast<int>(image.pitches[0]),
+        image_data + image.offsets[1],
+        base::checked_cast<int>(image.pitches[1]), i420_data.data(),
+        base::strict_cast<int>(image.width), i420_data.data() + luma_plane_size,
+        base::checked_cast<int>((image.width + 1) / 2),
+        i420_data.data() + luma_plane_size + chroma_plane_size,
+        base::checked_cast<int>((image.width + 1) / 2),
+        base::strict_cast<int>(image.width),
+        base::strict_cast<int>(image.height));
+  }
+  LOG_ASSERT(convert_res == 0)
+      << "Failed to convert " << media::FourccToString(fourcc)
+      << " to packed I420.";
+
+  base::MD5Digest md5_digest;
+  base::MD5Sum(i420_data.data(), i420_data.size(), &md5_digest);
+  return MD5DigestToBase16(md5_digest);
+}
+
 }  // namespace vaapi_test
 }  // namespace media
diff --git a/media/gpu/vaapi/test/shared_va_surface.h b/media/gpu/vaapi/test/shared_va_surface.h
index 8c9bfa4d..73a14ac 100644
--- a/media/gpu/vaapi/test/shared_va_surface.h
+++ b/media/gpu/vaapi/test/shared_va_surface.h
@@ -33,6 +33,10 @@
   // NB: vaDeriveImage may succeed but fetch garbage output in AMD.
   void SaveAsPNG(const std::string& path);
 
+  // Computes the MD5 sum of this surface and returns it as a human-readable hex
+  // string.
+  std::string GetMD5Sum() const;
+
   VASurfaceID id() const { return id_; }
   const gfx::Size& size() const { return size_; }
   unsigned int va_rt_format() const { return va_rt_format_; }
diff --git a/media/gpu/vaapi/test/video_decoder.h b/media/gpu/vaapi/test/video_decoder.h
index 2a70e63..2393b7d8a 100644
--- a/media/gpu/vaapi/test/video_decoder.h
+++ b/media/gpu/vaapi/test/video_decoder.h
@@ -33,6 +33,13 @@
   // It is therefore possible that the images outputted do not exactly match
   // what is displayed by playing the video stream directly.
   virtual void LastDecodedFrameToPNG(const std::string& path) = 0;
+
+  // Computes the MD5 sum of the last decoded frame and returns a human-readable
+  // representation.
+  virtual std::string LastDecodedFrameMD5Sum() = 0;
+
+  // Returns whether the last decoded frame was visible.
+  virtual bool LastDecodedFrameVisible() = 0;
 };
 
 }  // namespace vaapi_test
diff --git a/media/gpu/vaapi/test/vp9_decoder.cc b/media/gpu/vaapi/test/vp9_decoder.cc
index 2a4afad..c7db541 100644
--- a/media/gpu/vaapi/test/vp9_decoder.cc
+++ b/media/gpu/vaapi/test/vp9_decoder.cc
@@ -109,6 +109,7 @@
   }
 
   VLOG_IF(2, !frame_hdr.show_frame) << "not displaying frame";
+  last_decoded_frame_visible_ = frame_hdr.show_frame;
 
   if (frame_hdr.show_existing_frame) {
     last_decoded_surface_ = ref_frames_[frame_hdr.frame_to_show_map_idx];
@@ -275,5 +276,13 @@
   last_decoded_surface_->SaveAsPNG(path);
 }
 
+std::string Vp9Decoder::LastDecodedFrameMD5Sum() {
+  return last_decoded_surface_->GetMD5Sum();
+}
+
+bool Vp9Decoder::LastDecodedFrameVisible() {
+  return last_decoded_frame_visible_;
+}
+
 }  // namespace vaapi_test
 }  // namespace media
diff --git a/media/gpu/vaapi/test/vp9_decoder.h b/media/gpu/vaapi/test/vp9_decoder.h
index 888e85a1..4c12cc2 100644
--- a/media/gpu/vaapi/test/vp9_decoder.h
+++ b/media/gpu/vaapi/test/vp9_decoder.h
@@ -28,6 +28,8 @@
   // VideoDecoder implementation.
   VideoDecoder::Result DecodeNextFrame() override;
   void LastDecodedFrameToPNG(const std::string& path) override;
+  std::string LastDecodedFrameMD5Sum() override;
+  bool LastDecodedFrameVisible() override;
 
  private:
   // Reads next frame from IVF stream and its size into |vp9_frame_header| and
@@ -52,6 +54,9 @@
   // VP9-specific data.
   const std::unique_ptr<Vp9Parser> vp9_parser_;
   std::vector<scoped_refptr<SharedVASurface>> ref_frames_;
+
+  // Whether the last decoded frame was visible.
+  bool last_decoded_frame_visible_ = false;
 };
 
 }  // namespace vaapi_test
diff --git a/media/gpu/vaapi/vaapi_wrapper.cc b/media/gpu/vaapi/vaapi_wrapper.cc
index e922cd5f..4759170 100644
--- a/media/gpu/vaapi/vaapi_wrapper.cc
+++ b/media/gpu/vaapi/vaapi_wrapper.cc
@@ -6,6 +6,7 @@
 
 #include <dlfcn.h>
 #include <string.h>
+#include <sys/types.h>
 #include <unistd.h>
 #include <va/va.h>
 #include <va/va_drm.h>
@@ -2030,13 +2031,27 @@
   }
   va_attrib_extbuf.num_planes = num_planes;
 
-  if (pixmap->GetDmaBufFd(0) < 0) {
+  const int dma_buf_fd = pixmap->GetDmaBufFd(0);
+  if (dma_buf_fd < 0) {
     LOG(ERROR) << "Failed to get dmabuf from an Ozone NativePixmap";
     return nullptr;
   }
+  const off_t data_size = lseek(dma_buf_fd, /*offset=*/0, SEEK_END);
+  if (data_size == static_cast<off_t>(-1)) {
+    PLOG(ERROR) << "Failed to get the size of the dma-buf";
+    return nullptr;
+  }
+  if (lseek(dma_buf_fd, /*offset=*/0, SEEK_SET) == static_cast<off_t>(-1)) {
+    PLOG(ERROR) << "Failed to reset the file offset of the dma-buf";
+    return nullptr;
+  }
+  // If the data size doesn't fit in a uint32_t, we probably have bigger
+  // problems.
+  va_attrib_extbuf.data_size = base::checked_cast<uint32_t>(data_size);
+
   // We only have to pass the first file descriptor to a driver. A VA-API driver
   // shall create a VASurface from the single fd correctly.
-  uintptr_t fd = base::checked_cast<uintptr_t>(pixmap->GetDmaBufFd(0));
+  uintptr_t fd = base::checked_cast<uintptr_t>(dma_buf_fd);
   va_attrib_extbuf.buffers = &fd;
   va_attrib_extbuf.num_buffers = 1u;
 
diff --git a/media/mojo/clients/BUILD.gn b/media/mojo/clients/BUILD.gn
index 994a5f1..6a4d995c 100644
--- a/media/mojo/clients/BUILD.gn
+++ b/media/mojo/clients/BUILD.gn
@@ -88,6 +88,15 @@
       "//ui/gl:gl",
     ]
   }
+  if (is_win) {
+    sources += [
+      # TODO(frankli): s/windows/win for consistency.
+      "windows/media_foundation_renderer_client.cc",
+      "windows/media_foundation_renderer_client.h",
+      "windows/media_foundation_renderer_client_factory.cc",
+      "windows/media_foundation_renderer_client_factory.h",
+    ]
+  }
 }
 
 source_set("unit_tests") {
diff --git a/media/mojo/clients/mojo_renderer_factory.cc b/media/mojo/clients/mojo_renderer_factory.cc
index add8975c..3b317f7 100644
--- a/media/mojo/clients/mojo_renderer_factory.cc
+++ b/media/mojo/clients/mojo_renderer_factory.cc
@@ -44,6 +44,25 @@
       std::move(renderer_remote));
 }
 
+#if defined(OS_WIN)
+std::unique_ptr<MojoRenderer>
+MojoRendererFactory::CreateMediaFoundationRenderer(
+    mojo::PendingReceiver<mojom::MediaFoundationRendererExtension>
+        renderer_extension_receiver,
+    const scoped_refptr<base::SingleThreadTaskRunner>& media_task_runner,
+    VideoRendererSink* video_renderer_sink) {
+  DCHECK(interface_factory_);
+  mojo::PendingRemote<mojom::Renderer> renderer_remote;
+  interface_factory_->CreateMediaFoundationRenderer(
+      renderer_remote.InitWithNewPipeAndPassReceiver(),
+      std::move(renderer_extension_receiver));
+
+  return std::make_unique<MojoRenderer>(
+      media_task_runner, /*video_overlay_factory=*/nullptr, video_renderer_sink,
+      std::move(renderer_remote));
+}
+#endif  // defined(OS_WIN)
+
 #if BUILDFLAG(ENABLE_CAST_RENDERER)
 std::unique_ptr<MojoRenderer> MojoRendererFactory::CreateCastRenderer(
     const scoped_refptr<base::SingleThreadTaskRunner>& media_task_runner,
diff --git a/media/mojo/clients/mojo_renderer_factory.h b/media/mojo/clients/mojo_renderer_factory.h
index 2459433e..bab4060 100644
--- a/media/mojo/clients/mojo_renderer_factory.h
+++ b/media/mojo/clients/mojo_renderer_factory.h
@@ -44,6 +44,14 @@
       RequestOverlayInfoCB request_overlay_info_cb,
       const gfx::ColorSpace& target_color_space) final;
 
+#if defined(OS_WIN)
+  std::unique_ptr<MojoRenderer> CreateMediaFoundationRenderer(
+      mojo::PendingReceiver<mojom::MediaFoundationRendererExtension>
+          renderer_extension_receiver,
+      const scoped_refptr<base::SingleThreadTaskRunner>& media_task_runner,
+      VideoRendererSink* video_renderer_sink);
+#endif  // defined (OS_WIN)
+
 #if BUILDFLAG(ENABLE_CAST_RENDERER)
   std::unique_ptr<MojoRenderer> CreateCastRenderer(
       const scoped_refptr<base::SingleThreadTaskRunner>& media_task_runner,
diff --git a/media/mojo/clients/windows/media_foundation_renderer_client.cc b/media/mojo/clients/windows/media_foundation_renderer_client.cc
new file mode 100644
index 0000000..cb65282
--- /dev/null
+++ b/media/mojo/clients/windows/media_foundation_renderer_client.cc
@@ -0,0 +1,401 @@
+// 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 "media/mojo/clients/windows/media_foundation_renderer_client.h"
+
+#include "base/callback_helpers.h"
+#include "base/metrics/histogram_functions.h"
+#include "base/metrics/histogram_macros.h"
+#include "media/base/win/mf_helpers.h"
+
+namespace media {
+
+MediaFoundationRendererClient::MediaFoundationRendererClient(
+    mojo::PendingRemote<RendererExtension> renderer_extension_remote,
+    scoped_refptr<base::SingleThreadTaskRunner> media_task_runner,
+    scoped_refptr<base::SingleThreadTaskRunner> compositor_task_runner,
+    std::unique_ptr<media::MojoRenderer> mojo_renderer,
+    media::VideoRendererSink* sink)
+    : mojo_renderer_(std::move(mojo_renderer)),
+      sink_(sink),
+      media_task_runner_(std::move(media_task_runner)),
+      compositor_task_runner_(std::move(compositor_task_runner)),
+      delayed_bind_renderer_extension_remote_(
+          std::move(renderer_extension_remote)) {
+  DVLOG_FUNC(1);
+}
+
+MediaFoundationRendererClient::~MediaFoundationRendererClient() {
+  DVLOG_FUNC(1);
+  if (video_rendering_started_) {
+    sink_->Stop();
+  }
+}
+
+void MediaFoundationRendererClient::Initialize(MediaResource* media_resource,
+                                               RendererClient* client,
+                                               PipelineStatusCallback init_cb) {
+  DVLOG_FUNC(1);
+  DCHECK(media_task_runner_->BelongsToCurrentThread());
+  DCHECK(!init_cb_);
+
+  // Consume and bind the delayed PendingRemote now that we
+  // are on |media_task_runner_|.
+  renderer_extension_remote_.Bind(
+      std::move(delayed_bind_renderer_extension_remote_), media_task_runner_);
+
+  // Handle unexpected mojo pipe disconnection such as "mf_cdm" utility process
+  // crashed or killed in Browser task manager.
+  renderer_extension_remote_.set_disconnect_handler(
+      base::BindOnce(&MediaFoundationRendererClient::OnConnectionError,
+                     base::Unretained(this)));
+
+  client_ = client;
+  init_cb_ = std::move(init_cb);
+
+  const std::vector<media::DemuxerStream*> media_streams =
+      media_resource->GetAllStreams();
+  for (const media::DemuxerStream* stream : media_streams) {
+    if (stream->type() == media::DemuxerStream::Type::VIDEO) {
+      has_video_ = true;
+      break;
+    }
+  }
+
+  mojo_renderer_->Initialize(
+      media_resource, this,
+      base::BindOnce(
+          &MediaFoundationRendererClient::OnRemoteRendererInitialized,
+          weak_factory_.GetWeakPtr()));
+}
+
+void MediaFoundationRendererClient::OnConnectionError() {
+  DVLOG_FUNC(1);
+  DCHECK(media_task_runner_->BelongsToCurrentThread());
+
+  if (waiting_for_dcomp_surface_handle_) {
+    OnReceivedRemoteDCOMPSurface(mojo::ScopedHandle());
+  }
+}
+
+void MediaFoundationRendererClient::OnRemoteRendererInitialized(
+    PipelineStatus status) {
+  DVLOG_FUNC(1) << "status=" << status;
+
+  DCHECK(media_task_runner_->BelongsToCurrentThread());
+  if (status != media::PipelineStatus::PIPELINE_OK) {
+    DCHECK(!init_cb_.is_null());
+    std::move(init_cb_).Run(status);
+    return;
+  }
+
+  if (has_video_) {
+    // TODO(frankli): Add code to init DCOMPTextureWrapper.
+  } else {
+    std::move(init_cb_).Run(status);
+  }
+}
+
+void MediaFoundationRendererClient::OnDCOMPSurfaceHandleCreated(bool success) {
+  if (!media_task_runner_->BelongsToCurrentThread()) {
+    media_task_runner_->PostTask(
+        FROM_HERE,
+        base::BindOnce(
+            &MediaFoundationRendererClient::OnDCOMPSurfaceHandleCreated,
+            weak_factory_.GetWeakPtr(), success));
+    return;
+  }
+
+  DVLOG_FUNC(1);
+  DCHECK(has_video_);
+
+  dcomp_surface_handle_bound_ = true;
+  return;
+}
+
+void MediaFoundationRendererClient::OnReceivedRemoteDCOMPSurface(
+    mojo::ScopedHandle surface_handle) {
+  DVLOG_FUNC(1);
+  DCHECK(has_video_);
+  DCHECK(surface_handle.is_valid());
+  DCHECK(media_task_runner_->BelongsToCurrentThread());
+
+  waiting_for_dcomp_surface_handle_ = false;
+  base::win::ScopedHandle local_handle =
+      mojo::UnwrapPlatformHandle(std::move(surface_handle)).TakeHandle();
+  RegisterDCOMPSurfaceHandleInGPUProcess(std::move(local_handle));
+}
+
+void MediaFoundationRendererClient::RegisterDCOMPSurfaceHandleInGPUProcess(
+    base::win::ScopedHandle surface_handle) {
+  if (!media_task_runner_->BelongsToCurrentThread()) {
+    media_task_runner_->PostTask(
+        FROM_HERE,
+        base::BindOnce(&MediaFoundationRendererClient::
+                           RegisterDCOMPSurfaceHandleInGPUProcess,
+                       weak_factory_.GetWeakPtr(), std::move(surface_handle)));
+    return;
+  }
+
+  DVLOG_FUNC(1) << "surface_handle=" << surface_handle.Get();
+  DCHECK(has_video_);
+
+  mojo::ScopedHandle mojo_surface_handle =
+      mojo::WrapPlatformHandle(mojo::PlatformHandle(std::move(surface_handle)));
+
+  // TODO(frankli): Pass the |mojo_surface_handle| to Gpu process.
+}
+
+void MediaFoundationRendererClient::OnDCOMPSurfaceRegisteredInGPUProcess(
+    const base::UnguessableToken& token) {
+  DVLOG_FUNC(1);
+  DCHECK(has_video_);
+
+  return;
+}
+
+void MediaFoundationRendererClient::OnDCOMPSurfaceTextureReleased() {
+  DCHECK(has_video_);
+  return;
+}
+
+void MediaFoundationRendererClient::OnDCOMPStreamTextureInitialized(
+    bool success) {
+  DVLOG_FUNC(1) << "success=" << success;
+  DCHECK(media_task_runner_->BelongsToCurrentThread());
+  DCHECK(!init_cb_.is_null());
+  DCHECK(has_video_);
+
+  media::PipelineStatus status = media::PipelineStatus::PIPELINE_OK;
+  if (!success) {
+    status = media::PipelineStatus::PIPELINE_ERROR_INITIALIZATION_FAILED;
+  }
+  if (natural_size_.width() != 0 || natural_size_.height() != 0) {
+    InitializeDCOMPRendering();
+  }
+  std::move(init_cb_).Run(status);
+}
+
+void MediaFoundationRendererClient::OnVideoFrameCreated(
+    scoped_refptr<media::VideoFrame> video_frame) {
+  if (!media_task_runner_->BelongsToCurrentThread()) {
+    media_task_runner_->PostTask(
+        FROM_HERE,
+        base::BindOnce(&MediaFoundationRendererClient::OnVideoFrameCreated,
+                       weak_factory_.GetWeakPtr(), video_frame));
+    return;
+  }
+
+  DVLOG_FUNC(1);
+  DCHECK(has_video_);
+
+  video_frame->metadata().protected_video = true;
+  video_frame->metadata().allow_overlay = true;
+
+  dcomp_frame_ = video_frame;
+
+  sink_->PaintSingleFrame(dcomp_frame_, true);
+}
+
+void MediaFoundationRendererClient::OnCompositionParamsReceived(
+    gfx::Rect output_rect) {
+  DVLOG_FUNC(1) << "output_rect=" << output_rect.ToString();
+  DCHECK(media_task_runner_->BelongsToCurrentThread());
+  DCHECK(has_video_);
+
+  renderer_extension_remote_->SetOutputParams(output_rect);
+  return;
+}
+
+bool MediaFoundationRendererClient::MojoSetDCOMPMode(bool enabled) {
+  DVLOG_FUNC(1) << "enabled=" << enabled;
+  DCHECK(media_task_runner_->BelongsToCurrentThread());
+  DCHECK(renderer_extension_remote_.is_bound());
+
+  bool success = false;
+  if (!renderer_extension_remote_->SetDCOMPMode(enabled, &success)) {
+    return false;
+  }
+  return success;
+}
+
+void MediaFoundationRendererClient::MojoGetDCOMPSurface() {
+  DVLOG_FUNC(1);
+  DCHECK(media_task_runner_->BelongsToCurrentThread());
+  DCHECK(renderer_extension_remote_.is_bound());
+
+  if (!renderer_extension_remote_.is_connected()) {
+    media_task_runner_->PostTask(
+        FROM_HERE,
+        base::BindOnce(
+            &MediaFoundationRendererClient::OnReceivedRemoteDCOMPSurface,
+            weak_factory_.GetWeakPtr(), base::Passed(mojo::ScopedHandle())));
+    return;
+  }
+  waiting_for_dcomp_surface_handle_ = true;
+  renderer_extension_remote_->GetDCOMPSurface(base::BindOnce(
+      &MediaFoundationRendererClient::OnReceivedRemoteDCOMPSurface,
+      weak_factory_.GetWeakPtr()));
+}
+
+void MediaFoundationRendererClient::InitializeDCOMPRendering() {
+  DVLOG_FUNC(1);
+  DCHECK(has_video_);
+
+  if (dcomp_rendering_initialized_) {
+    return;
+  }
+
+  if (!MojoSetDCOMPMode(true)) {
+    DLOG(ERROR) << "Failed to initialize DCOMP mode on remote renderer. this="
+                << this;
+    return;
+  }
+  MojoGetDCOMPSurface();
+
+  dcomp_rendering_initialized_ = true;
+  return;
+}
+
+void MediaFoundationRendererClient::SetCdm(CdmContext* cdm_context,
+                                           CdmAttachedCB cdm_attached_cb) {
+  DVLOG_FUNC(1) << "cdm_context=" << cdm_context;
+  DCHECK(cdm_context);
+
+  if (cdm_context_) {
+    DLOG(ERROR) << "Switching CDM not supported. this=" << this;
+    std::move(cdm_attached_cb).Run(false);
+    return;
+  }
+
+  cdm_context_ = cdm_context;
+  DCHECK(cdm_attached_cb_.is_null());
+  cdm_attached_cb_ = std::move(cdm_attached_cb);
+  mojo_renderer_->SetCdm(
+      cdm_context_,
+      base::BindOnce(&MediaFoundationRendererClient::OnCdmAttached,
+                     weak_factory_.GetWeakPtr()));
+}
+
+void MediaFoundationRendererClient::SetLatencyHint(
+    base::Optional<base::TimeDelta> /*latency_hint*/) {
+  // We do not use the latency hint today
+}
+
+void MediaFoundationRendererClient::OnCdmAttached(bool success) {
+  DCHECK(cdm_attached_cb_);
+  std::move(cdm_attached_cb_).Run(success);
+}
+
+void MediaFoundationRendererClient::Flush(base::OnceClosure flush_cb) {
+  mojo_renderer_->Flush(std::move(flush_cb));
+}
+
+void MediaFoundationRendererClient::StartPlayingFrom(base::TimeDelta time) {
+  mojo_renderer_->StartPlayingFrom(time);
+}
+
+void MediaFoundationRendererClient::SetPlaybackRate(double playback_rate) {
+  mojo_renderer_->SetPlaybackRate(playback_rate);
+}
+
+void MediaFoundationRendererClient::SetVolume(float volume) {
+  mojo_renderer_->SetVolume(volume);
+}
+
+base::TimeDelta MediaFoundationRendererClient::GetMediaTime() {
+  return mojo_renderer_->GetMediaTime();
+}
+
+void MediaFoundationRendererClient::OnSelectedVideoTracksChanged(
+    const std::vector<media::DemuxerStream*>& enabled_tracks,
+    base::OnceClosure change_completed_cb) {
+  bool video_track_selected = (enabled_tracks.size() > 0);
+  DVLOG_FUNC(1) << "video_track_selected=" << video_track_selected;
+  renderer_extension_remote_->SetVideoStreamEnabled(video_track_selected);
+  std::move(change_completed_cb).Run();
+}
+
+void MediaFoundationRendererClient::OnError(PipelineStatus status) {
+  DVLOG_FUNC(1) << "status=" << status;
+  client_->OnError(status);
+}
+
+void MediaFoundationRendererClient::OnEnded() {
+  client_->OnEnded();
+}
+
+void MediaFoundationRendererClient::OnStatisticsUpdate(
+    const media::PipelineStatistics& stats) {
+  client_->OnStatisticsUpdate(stats);
+}
+
+void MediaFoundationRendererClient::OnBufferingStateChange(
+    media::BufferingState state,
+    media::BufferingStateChangeReason reason) {
+  client_->OnBufferingStateChange(state, reason);
+}
+
+void MediaFoundationRendererClient::OnWaiting(WaitingReason reason) {
+  client_->OnWaiting(reason);
+}
+
+void MediaFoundationRendererClient::OnAudioConfigChange(
+    const media::AudioDecoderConfig& config) {
+  client_->OnAudioConfigChange(config);
+}
+void MediaFoundationRendererClient::OnVideoConfigChange(
+    const media::VideoDecoderConfig& config) {
+  client_->OnVideoConfigChange(config);
+}
+
+void MediaFoundationRendererClient::OnVideoNaturalSizeChange(
+    const gfx::Size& size) {
+  DVLOG_FUNC(1) << "size=" << size.ToString();
+  DCHECK(media_task_runner_->BelongsToCurrentThread());
+  DCHECK(has_video_);
+
+  natural_size_ = size;
+  // Skip creation of a new video frame if the DCOMP surface has not yet been
+  // bound to the DCOMP texture as we will create a new frame after binding has
+  // completed.
+  if (dcomp_surface_handle_bound_) {
+    // TODO(frankli): Add code to call DCOMPTextureWrapper::CreateVideoFrame().
+  }
+  InitializeDCOMPRendering();
+  client_->OnVideoNaturalSizeChange(natural_size_);
+}
+
+void MediaFoundationRendererClient::OnVideoOpacityChange(bool opaque) {
+  DVLOG_FUNC(1) << "opaque=" << opaque;
+  DCHECK(has_video_);
+  client_->OnVideoOpacityChange(opaque);
+}
+
+void MediaFoundationRendererClient::OnVideoFrameRateChange(
+    base::Optional<int> fps) {
+  DVLOG_FUNC(1) << "fps=" << (fps ? *fps : -1);
+  DCHECK(has_video_);
+  client_->OnVideoFrameRateChange(fps);
+}
+
+scoped_refptr<media::VideoFrame> MediaFoundationRendererClient::Render(
+    base::TimeTicks deadline_min,
+    base::TimeTicks deadline_max,
+    bool background_rendering) {
+  // Returns no video frame as it is rendered independently by Windows Direct
+  // Composition.
+  return nullptr;
+}
+
+void MediaFoundationRendererClient::OnFrameDropped() {
+  return;
+}
+
+base::TimeDelta MediaFoundationRendererClient::GetPreferredRenderInterval() {
+  // TODO(frankli): use 'viz::BeginFrameArgs::MinInterval()'.
+  return base::TimeDelta::FromSeconds(0);
+}
+
+}  // namespace media
diff --git a/media/mojo/clients/windows/media_foundation_renderer_client.h b/media/mojo/clients/windows/media_foundation_renderer_client.h
new file mode 100644
index 0000000..a34ca83
--- /dev/null
+++ b/media/mojo/clients/windows/media_foundation_renderer_client.h
@@ -0,0 +1,152 @@
+// 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 MEDIA_MOJO_CLIENTS_WINDOWS_MEDIA_FOUNDATION_RENDERER_CLIENT_H_
+#define MEDIA_MOJO_CLIENTS_WINDOWS_MEDIA_FOUNDATION_RENDERER_CLIENT_H_
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/single_thread_task_runner.h"
+#include "media/base/media_resource.h"
+#include "media/base/renderer.h"
+#include "media/base/renderer_client.h"
+#include "media/base/video_renderer_sink.h"
+#include "media/mojo/clients/mojo_renderer.h"
+#include "media/mojo/mojom/renderer_extensions.mojom.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/receiver.h"
+
+namespace media {
+
+// MediaFoundationRendererClient lives in Renderer process and mirrors a
+// MediaFoundationRenderer living in the MF_CDM LPAC Utility process.
+//
+// It is responsible for forwarding media::Renderer calls from WMPI to the
+// MediaFoundationRenderer, using |mojo_renderer|. It also manages a
+// DCOMPTexture, (via |dcomp_texture_wrapper_|) and notifies the
+// VideoRendererSink when new frames are available.
+//
+// This class handles all calls on |media_task_runner_|, except for
+// OnFrameAvailable(), which is called on |compositor_task_runner_|.
+//
+// N.B: This class implements media::RendererClient, in order to intercept
+// OnVideoNaturalSizeChange() events, to update DCOMPTextureWrapper. All events
+// (including OnVideoNaturalSizeChange()) are bubbled up to |client_|.
+//
+class MediaFoundationRendererClient
+    : public media::Renderer,
+      public media::RendererClient,
+      public media::VideoRendererSink::RenderCallback {
+ public:
+  using RendererExtension = media::mojom::MediaFoundationRendererExtension;
+
+  MediaFoundationRendererClient(
+      mojo::PendingRemote<RendererExtension> renderer_extension_remote,
+      scoped_refptr<base::SingleThreadTaskRunner> media_task_runner,
+      scoped_refptr<base::SingleThreadTaskRunner> compositor_task_runner,
+      std::unique_ptr<media::MojoRenderer> mojo_renderer,
+      media::VideoRendererSink* sink);
+
+  ~MediaFoundationRendererClient() override;
+
+  // media::Renderer implementation.
+  void Initialize(MediaResource* media_resource,
+                  RendererClient* client,
+                  PipelineStatusCallback init_cb) override;
+  void SetCdm(CdmContext* cdm_context, CdmAttachedCB cdm_attached_cb) override;
+  void SetLatencyHint(base::Optional<base::TimeDelta> latency_hint) override;
+  void Flush(base::OnceClosure flush_cb) override;
+  void StartPlayingFrom(base::TimeDelta time) override;
+  void SetPlaybackRate(double playback_rate) override;
+  void SetVolume(float volume) override;
+  base::TimeDelta GetMediaTime() override;
+  void OnSelectedVideoTracksChanged(
+      const std::vector<media::DemuxerStream*>& enabled_tracks,
+      base::OnceClosure change_completed_cb) override;
+
+  // media::RendererClient implementation.
+  void OnError(PipelineStatus status) override;
+  void OnEnded() override;
+  void OnStatisticsUpdate(const media::PipelineStatistics& stats) override;
+  void OnBufferingStateChange(media::BufferingState state,
+                              media::BufferingStateChangeReason) override;
+  void OnWaiting(media::WaitingReason reason) override;
+  void OnAudioConfigChange(const media::AudioDecoderConfig& config) override;
+  void OnVideoConfigChange(const media::VideoDecoderConfig& config) override;
+  void OnVideoNaturalSizeChange(const gfx::Size& size) override;
+  void OnVideoOpacityChange(bool opaque) override;
+  void OnVideoFrameRateChange(base::Optional<int>) override;
+
+  // media::VideoRendererSink::RenderCallback implementation.
+  scoped_refptr<media::VideoFrame> Render(base::TimeTicks deadline_min,
+                                          base::TimeTicks deadline_max,
+                                          bool background_rendering) override;
+  void OnFrameDropped() override;
+  base::TimeDelta GetPreferredRenderInterval() override;
+
+ private:
+  void OnConnectionError();
+  void OnRemoteRendererInitialized(media::PipelineStatus status);
+  void OnVideoFrameCreated(scoped_refptr<media::VideoFrame> video_frame);
+  void OnDCOMPStreamTextureInitialized(bool success);
+  void OnDCOMPSurfaceTextureReleased();
+  void OnDCOMPSurfaceHandleCreated(bool success);
+  void OnReceivedRemoteDCOMPSurface(mojo::ScopedHandle surface_handle);
+  void OnDCOMPSurfaceRegisteredInGPUProcess(
+      const base::UnguessableToken& token);
+  void OnCompositionParamsReceived(gfx::Rect output_rect);
+
+  void InitializeDCOMPRendering();
+  void RegisterDCOMPSurfaceHandleInGPUProcess(
+      base::win::ScopedHandle surface_handle);
+  void OnCdmAttached(bool success);
+  void InitializeMojoCdmTelemetryPtrServer();
+  void OnCDMTelemetryPtrConnectionError();
+
+  bool MojoSetDCOMPMode(bool enabled);
+  void MojoGetDCOMPSurface();
+
+  // Used to forward calls to the MediaFoundationRenderer living in the MF_CDM
+  // LPAC Utility process.
+  std::unique_ptr<media::MojoRenderer> mojo_renderer_;
+
+  RendererClient* client_ = nullptr;
+
+  VideoRendererSink* sink_;
+  bool video_rendering_started_ = false;
+  bool dcomp_rendering_initialized_ = false;
+  // video's native size.
+  gfx::Size natural_size_;
+
+  scoped_refptr<base::SingleThreadTaskRunner> media_task_runner_;
+  scoped_refptr<base::SingleThreadTaskRunner> compositor_task_runner_;
+  scoped_refptr<media::VideoFrame> dcomp_frame_;
+  bool dcomp_surface_handle_bound_ = false;
+  bool has_video_ = false;
+
+  PipelineStatusCallback init_cb_;
+  CdmContext* cdm_context_ = nullptr;
+  CdmAttachedCB cdm_attached_cb_;
+
+  // Used temporarily, to delay binding to |renderer_extension_remote_| until we
+  // are on the right sequence, when Initialize() is called.
+  mojo::PendingRemote<RendererExtension>
+      delayed_bind_renderer_extension_remote_;
+
+  // Used to call methods on the MediaFoundationRenderer in the MF_CMD LPAC
+  // Utility process.
+  mojo::Remote<RendererExtension> renderer_extension_remote_;
+
+  bool waiting_for_dcomp_surface_handle_ = false;
+
+  // NOTE: Weak pointers must be invalidated before all other member variables.
+  base::WeakPtrFactory<MediaFoundationRendererClient> weak_factory_{this};
+
+  DISALLOW_COPY_AND_ASSIGN(MediaFoundationRendererClient);
+};
+
+}  // namespace media
+
+#endif  // MEDIA_MOJO_CLIENTS_WINDOWS_MEDIA_FOUNDATION_RENDERER_CLIENT_H_
diff --git a/media/mojo/clients/windows/media_foundation_renderer_client_factory.cc b/media/mojo/clients/windows/media_foundation_renderer_client_factory.cc
new file mode 100644
index 0000000..52f43a27
--- /dev/null
+++ b/media/mojo/clients/windows/media_foundation_renderer_client_factory.cc
@@ -0,0 +1,61 @@
+// 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 "media/mojo/clients/windows/media_foundation_renderer_client_factory.h"
+
+#include "media/base/win/mf_helpers.h"
+#include "media/mojo/clients/mojo_renderer.h"
+#include "media/mojo/clients/mojo_renderer_factory.h"
+#include "media/mojo/clients/windows/media_foundation_renderer_client.h"
+#include "media/mojo/mojom/renderer_extensions.mojom.h"
+
+namespace media {
+
+MediaFoundationRendererClientFactory::MediaFoundationRendererClientFactory(
+    scoped_refptr<base::SingleThreadTaskRunner> compositor_task_runner,
+    std::unique_ptr<media::MojoRendererFactory> mojo_renderer_factory)
+    : compositor_task_runner_(std::move(compositor_task_runner)),
+      mojo_renderer_factory_(std::move(mojo_renderer_factory)) {
+  DVLOG_FUNC(1);
+}
+
+MediaFoundationRendererClientFactory::~MediaFoundationRendererClientFactory() {
+  DVLOG_FUNC(1);
+}
+
+std::unique_ptr<media::Renderer>
+MediaFoundationRendererClientFactory::CreateRenderer(
+    const scoped_refptr<base::SingleThreadTaskRunner>& media_task_runner,
+    const scoped_refptr<base::TaskRunner>& /*worker_task_runner*/,
+    media::AudioRendererSink* /*audio_renderer_sink*/,
+    media::VideoRendererSink* video_renderer_sink,
+    media::RequestOverlayInfoCB /*request_overlay_info_cb*/,
+    const gfx::ColorSpace& /*target_color_space*/) {
+  DVLOG_FUNC(1);
+
+  // Used to send messages from the MediaFoundationRendererClient (Renderer
+  // process), to the MediaFoundationRenderer (MF_CDM LPAC Utility process).
+  // The |renderer_extension_receiver| will be bound in MediaFoundationRenderer.
+  mojo::PendingRemote<media::mojom::MediaFoundationRendererExtension>
+      renderer_extension_remote;
+  auto renderer_extension_receiver =
+      renderer_extension_remote.InitWithNewPipeAndPassReceiver();
+
+  std::unique_ptr<media::MojoRenderer> mojo_renderer =
+      mojo_renderer_factory_->CreateMediaFoundationRenderer(
+          std::move(renderer_extension_receiver), media_task_runner,
+          video_renderer_sink);
+
+  // mojo_renderer's ownership is passed to MediaFoundationRendererClient.
+  return std::make_unique<MediaFoundationRendererClient>(
+      std::move(renderer_extension_remote), media_task_runner,
+      compositor_task_runner_, std::move(mojo_renderer), video_renderer_sink);
+}
+
+media::MediaResource::Type
+MediaFoundationRendererClientFactory::GetRequiredMediaResourceType() {
+  return media::MediaResource::Type::STREAM;
+}
+
+}  // namespace media
\ No newline at end of file
diff --git a/media/mojo/clients/windows/media_foundation_renderer_client_factory.h b/media/mojo/clients/windows/media_foundation_renderer_client_factory.h
new file mode 100644
index 0000000..3117a5f7
--- /dev/null
+++ b/media/mojo/clients/windows/media_foundation_renderer_client_factory.h
@@ -0,0 +1,45 @@
+// 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 MEDIA_MOJO_CLIENTS_WINDOWS_MEDIA_FOUNDATION_RENDERER_CLIENT_FACTORY_H_
+#define MEDIA_MOJO_CLIENTS_WINDOWS_MEDIA_FOUNDATION_RENDERER_CLIENT_FACTORY_H_
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/single_thread_task_runner.h"
+#include "media/base/renderer_factory.h"
+#include "media/mojo/clients/mojo_renderer_factory.h"
+#include "mojo/public/cpp/bindings/interface_request.h"
+
+namespace media {
+
+// The default class for creating a MediaFoundationRendererClient
+// and its associated MediaFoundationRenderer.
+class MediaFoundationRendererClientFactory : public media::RendererFactory {
+ public:
+  MediaFoundationRendererClientFactory(
+      scoped_refptr<base::SingleThreadTaskRunner> compositor_task_runner,
+      std::unique_ptr<media::MojoRendererFactory> mojo_renderer_factory);
+  ~MediaFoundationRendererClientFactory() override;
+
+  std::unique_ptr<media::Renderer> CreateRenderer(
+      const scoped_refptr<base::SingleThreadTaskRunner>& media_task_runner,
+      const scoped_refptr<base::TaskRunner>& worker_task_runner,
+      media::AudioRendererSink* audio_renderer_sink,
+      media::VideoRendererSink* video_renderer_sink,
+      media::RequestOverlayInfoCB request_surface_cb,
+      const gfx::ColorSpace& target_color_space) override;
+
+  // The MediaFoundationRenderer uses a Type::URL.
+  media::MediaResource::Type GetRequiredMediaResourceType() override;
+
+ private:
+  scoped_refptr<base::SingleThreadTaskRunner> compositor_task_runner_;
+
+  std::unique_ptr<media::MojoRendererFactory> mojo_renderer_factory_;
+};
+
+}  // namespace media
+
+#endif  // MEDIA_MOJO_CLIENTS_WINDOWS_MEDIA_FOUNDATION_RENDERER_CLIENT_FACTORY_H_
diff --git a/media/mojo/mojom/interface_factory.mojom b/media/mojo/mojom/interface_factory.mojom
index b478762..09e7328 100644
--- a/media/mojo/mojom/interface_factory.mojom
+++ b/media/mojo/mojom/interface_factory.mojom
@@ -46,6 +46,14 @@
       pending_receiver<Renderer> renderer,
       pending_receiver<MediaPlayerRendererExtension> renderer_extension);
 
+  [EnableIf=is_win]
+  // Creates a MediaFoundationRenderer (MediaFoundationRendererClientFactory).
+  // - |renderer_extension| is bound in MediaFoundationRenderer, and receives
+  //   calls from MediaFoundationRendererClient.
+  CreateMediaFoundationRenderer(
+      pending_receiver<Renderer> renderer,
+      pending_receiver<MediaFoundationRendererExtension> renderer_extension);
+
   [EnableIf=is_android]
   // Creates a FlingingRenderer (FlingingRendererClientFactory).
   // The |presentation_id| is used to find an already set-up RemotePlayback
diff --git a/media/mojo/mojom/renderer_extensions.mojom b/media/mojo/mojom/renderer_extensions.mojom
index 2bd84f4..bd245a41 100644
--- a/media/mojo/mojom/renderer_extensions.mojom
+++ b/media/mojo/mojom/renderer_extensions.mojom
@@ -47,3 +47,28 @@
   // network).
   OnRemotePlayStateChange(MediaStatusState state);
 };
+
+[EnableIf=is_win]
+// Extension of the mojo::RendererClient communication layer for MF-based CDM
+// Renderer.
+// This allows the media::Renderer from Renderer process calling into the
+// MediaFoundationRenderer in the "mf_cdm" sandbox'ed Utility process.
+// Concretely, the MediaFoundationRendererClient uses these methods to send
+// commands to MediaFoundationRenderer, which lives in the mf_cdm LPAC-based
+// Utility process.
+// Please refer to media/renderers/win/media_foundation_renderer_extension.h
+// for its C++ interface equivalence.
+interface MediaFoundationRendererExtension {
+  // Enable Direct Composition video rendering.
+  [Sync]
+  SetDCOMPMode(bool enabled) => (bool succeeded);
+
+  // Get a Direct Composition Surface handle.
+  GetDCOMPSurface() => (handle? dcomp_surface);
+
+  // Notify renderer whether video is enabled.
+  SetVideoStreamEnabled(bool enabled);
+
+  // Notify renderer of output composition parameters
+  SetOutputParams(gfx.mojom.Rect rect);
+};
diff --git a/media/mojo/services/BUILD.gn b/media/mojo/services/BUILD.gn
index 44a9274..80f3ef75 100644
--- a/media/mojo/services/BUILD.gn
+++ b/media/mojo/services/BUILD.gn
@@ -145,6 +145,14 @@
     deps +=
         [ "//chromeos/components/cdm_factory_daemon:cdm_factory_daemon_gpu" ]
   }
+
+  if (is_win) {
+    sources += [
+      "media_foundation_renderer_wrapper.cc",
+      "media_foundation_renderer_wrapper.h",
+    ]
+    deps += [ "//media/base/win:media_foundation_util" ]
+  }
 }
 
 source_set("unit_tests") {
diff --git a/media/mojo/services/interface_factory_impl.cc b/media/mojo/services/interface_factory_impl.cc
index 65731303..d5297d4 100644
--- a/media/mojo/services/interface_factory_impl.cc
+++ b/media/mojo/services/interface_factory_impl.cc
@@ -147,6 +147,21 @@
 }
 #endif  // defined(OS_ANDROID)
 
+#if defined(OS_WIN)
+void InterfaceFactoryImpl::CreateMediaFoundationRenderer(
+    mojo::PendingReceiver<media::mojom::Renderer> receiver,
+    mojo::PendingReceiver<media::mojom::MediaFoundationRendererExtension>
+        renderer_extension_receiver) {
+  DVLOG(1) << __func__ << ": this=" << this;
+
+  scoped_refptr<base::SingleThreadTaskRunner> task_runner =
+      base::ThreadTaskRunnerHandle::Get();
+  CreateMediaFoundationRendererOnTaskRunner(
+      std::move(task_runner), std::move(receiver),
+      std::move(renderer_extension_receiver));
+}
+#endif  // defined (OS_WIN)
+
 void InterfaceFactoryImpl::CreateCdm(const std::string& key_system,
                                      const CdmConfig& cdm_config,
                                      CreateCdmCallback callback) {
@@ -266,4 +281,29 @@
 
 #endif  // BUILDFLAG(ENABLE_MOJO_CDM)
 
+#if defined(OS_WIN)
+void InterfaceFactoryImpl::CreateMediaFoundationRendererOnTaskRunner(
+    scoped_refptr<base::SingleThreadTaskRunner> task_runner,
+    mojo::PendingReceiver<media::mojom::Renderer> receiver,
+    mojo::PendingReceiver<media::mojom::MediaFoundationRendererExtension>
+        renderer_extension_receiver) {
+  DVLOG(1) << __func__ << ": this=" << this;
+
+  if (!task_runner->RunsTasksInCurrentSequence()) {
+    task_runner->PostTask(
+        FROM_HERE,
+        base::BindOnce(
+            &InterfaceFactoryImpl::CreateMediaFoundationRendererOnTaskRunner,
+            base::Unretained(this), std::move(task_runner), std::move(receiver),
+            std::move(renderer_extension_receiver)));
+    return;
+  }
+
+  DVLOG(1) << __func__ << ": this=" << this;
+
+  // TODO(frankli): Invoke media::MojoRendererService::Create() with our
+  // specific parameters.
+}
+#endif  // defined(OS_WIN)
+
 }  // namespace media
diff --git a/media/mojo/services/interface_factory_impl.h b/media/mojo/services/interface_factory_impl.h
index c8341e6a..be55cc5 100644
--- a/media/mojo/services/interface_factory_impl.h
+++ b/media/mojo/services/interface_factory_impl.h
@@ -68,6 +68,13 @@
           client_extension,
       mojo::PendingReceiver<mojom::Renderer> receiver) final;
 #endif  // defined(OS_ANDROID)
+#if defined(OS_WIN)
+  void CreateMediaFoundationRenderer(
+      mojo::PendingReceiver<media::mojom::Renderer> receiver,
+      mojo::PendingReceiver<media::mojom::MediaFoundationRendererExtension>
+          renderer_extension_receiver) final;
+#endif  // defined(OS_WIN)
+
   void CreateCdm(const std::string& key_system,
                  const CdmConfig& cdm_config,
                  CreateCdmCallback callback) final;
@@ -91,6 +98,14 @@
                            const std::string& error_message);
 #endif  // BUILDFLAG(ENABLE_MOJO_CDM)
 
+#if defined(OS_WIN)
+  void CreateMediaFoundationRendererOnTaskRunner(
+      scoped_refptr<base::SingleThreadTaskRunner> task_runner,
+      mojo::PendingReceiver<media::mojom::Renderer> receiver,
+      mojo::PendingReceiver<media::mojom::MediaFoundationRendererExtension>
+          renderer_extension_receiver);
+#endif  // defined(OS_WIN)
+
   // Must be declared before the receivers below because the bound objects might
   // take a raw pointer of |cdm_service_context_| and assume it's always
   // available.
diff --git a/media/mojo/services/media_foundation_renderer_wrapper.cc b/media/mojo/services/media_foundation_renderer_wrapper.cc
new file mode 100644
index 0000000..21953a2
--- /dev/null
+++ b/media/mojo/services/media_foundation_renderer_wrapper.cc
@@ -0,0 +1,101 @@
+// 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 "media/mojo/services/media_foundation_renderer_wrapper.h"
+
+#include "base/callback_helpers.h"
+#include "media/base/win/mf_helpers.h"
+#include "media/mojo/mojom/renderer_extensions.mojom.h"
+#include "mojo/public/cpp/system/platform_handle.h"
+
+namespace media {
+
+MediaFoundationRendererWrapper::MediaFoundationRendererWrapper(
+    bool web_contents_muted,
+    scoped_refptr<base::SequencedTaskRunner> task_runner,
+    mojo::PendingReceiver<RendererExtension> renderer_extension_receiver)
+    : renderer_(std::make_unique<media::MediaFoundationRenderer>(
+          web_contents_muted,
+          std::move(task_runner))),
+      renderer_extension_receiver_(this,
+                                   std::move(renderer_extension_receiver)) {
+  DVLOG_FUNC(1);
+}
+
+MediaFoundationRendererWrapper::~MediaFoundationRendererWrapper() {
+  DVLOG_FUNC(1);
+}
+
+void MediaFoundationRendererWrapper::Initialize(
+    media::MediaResource* media_resource,
+    media::RendererClient* client,
+    media::PipelineStatusCallback init_cb) {
+  renderer_->Initialize(media_resource, client, std::move(init_cb));
+}
+
+void MediaFoundationRendererWrapper::SetCdm(CdmContext* cdm_context,
+                                            CdmAttachedCB cdm_attached_cb) {
+  renderer_->SetCdm(cdm_context, std::move(cdm_attached_cb));
+}
+
+void MediaFoundationRendererWrapper::SetLatencyHint(
+    base::Optional<base::TimeDelta> latency_hint) {
+  renderer_->SetLatencyHint(latency_hint);
+}
+
+void MediaFoundationRendererWrapper::Flush(base::OnceClosure flush_cb) {
+  renderer_->Flush(std::move(flush_cb));
+}
+
+void MediaFoundationRendererWrapper::StartPlayingFrom(base::TimeDelta time) {
+  renderer_->StartPlayingFrom(time);
+}
+
+void MediaFoundationRendererWrapper::SetPlaybackRate(double playback_rate) {
+  renderer_->SetPlaybackRate(playback_rate);
+}
+
+void MediaFoundationRendererWrapper::SetVolume(float volume) {
+  return renderer_->SetVolume(volume);
+}
+
+base::TimeDelta MediaFoundationRendererWrapper::GetMediaTime() {
+  return renderer_->GetMediaTime();
+}
+
+void MediaFoundationRendererWrapper::SetDCOMPMode(
+    bool enabled,
+    SetDCOMPModeCallback callback) {
+  renderer_->SetDCompMode(enabled, std::move(callback));
+}
+
+void MediaFoundationRendererWrapper::GetDCOMPSurface(
+    GetDCOMPSurfaceCallback callback) {
+  get_decomp_surface_cb_ = std::move(callback);
+  renderer_->GetDCompSurface(
+      base::BindOnce(&MediaFoundationRendererWrapper::OnReceiveDCOMPSurface,
+                     weak_factory_.GetWeakPtr()));
+}
+
+void MediaFoundationRendererWrapper::SetVideoStreamEnabled(bool enabled) {
+  renderer_->SetVideoStreamEnabled(enabled);
+}
+
+void MediaFoundationRendererWrapper::SetOutputParams(
+    const gfx::Rect& output_rect) {
+  renderer_->SetOutputParams(output_rect);
+}
+
+void MediaFoundationRendererWrapper::OnReceiveDCOMPSurface(HANDLE handle) {
+  base::win::ScopedHandle local_surface_handle;
+  local_surface_handle.Set(handle);
+  if (get_decomp_surface_cb_) {
+    mojo::ScopedHandle surface_handle;
+    surface_handle = mojo::WrapPlatformHandle(
+        mojo::PlatformHandle(std::move(local_surface_handle)));
+    std::move(get_decomp_surface_cb_).Run(std::move(surface_handle));
+  }
+}
+
+}  // namespace media
diff --git a/media/mojo/services/media_foundation_renderer_wrapper.h b/media/mojo/services/media_foundation_renderer_wrapper.h
new file mode 100644
index 0000000..01f8fa0e
--- /dev/null
+++ b/media/mojo/services/media_foundation_renderer_wrapper.h
@@ -0,0 +1,70 @@
+// 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 MEDIA_MOJO_SERVICES_MEDIA_FOUNDATION_RENDERER_WRAPPER_H_
+#define MEDIA_MOJO_SERVICES_MEDIA_FOUNDATION_RENDERER_WRAPPER_H_
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "media/base/media_resource.h"
+#include "media/base/pipeline_status.h"
+#include "media/base/renderer.h"
+#include "media/base/renderer_client.h"
+#include "media/mojo/mojom/renderer_extensions.mojom.h"
+#include "media/renderers/win/media_foundation_renderer.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/receiver.h"
+
+namespace media {
+
+// Wrap media::MediaFoundationRenderer to remove its dependence on
+// media::mojom::MediaFoundationRendererExtension interface.
+//
+class MediaFoundationRendererWrapper
+    : public media::Renderer,
+      public media::mojom::MediaFoundationRendererExtension {
+ public:
+  using RendererExtension = media::mojom::MediaFoundationRendererExtension;
+
+  MediaFoundationRendererWrapper(
+      bool web_contents_muted,
+      scoped_refptr<base::SequencedTaskRunner> task_runner,
+      mojo::PendingReceiver<RendererExtension> renderer_extension_receiver);
+
+  ~MediaFoundationRendererWrapper() final;
+
+  // media::Renderer implementation.
+  void Initialize(media::MediaResource* media_resource,
+                  media::RendererClient* client,
+                  media::PipelineStatusCallback init_cb) override;
+  void SetCdm(CdmContext* cdm_context, CdmAttachedCB cdm_attached_cb) override;
+  void SetLatencyHint(base::Optional<base::TimeDelta> latency_hint) override;
+  void Flush(base::OnceClosure flush_cb) override;
+  void StartPlayingFrom(base::TimeDelta time) override;
+  void SetPlaybackRate(double playback_rate) override;
+  void SetVolume(float volume) override;
+  base::TimeDelta GetMediaTime() override;
+
+  // media::mojom::MediaFoundationRendererExtension implementation.
+  void SetDCOMPMode(bool enabled, SetDCOMPModeCallback callback) final;
+  void GetDCOMPSurface(GetDCOMPSurfaceCallback callback) final;
+  void SetVideoStreamEnabled(bool enabled) final;
+  void SetOutputParams(const gfx::Rect& output_rect) final;
+
+ private:
+  void OnReceiveDCOMPSurface(HANDLE handle);
+
+  std::unique_ptr<media::MediaFoundationRenderer> renderer_;
+  mojo::Receiver<MediaFoundationRendererExtension> renderer_extension_receiver_;
+  GetDCOMPSurfaceCallback get_decomp_surface_cb_;
+
+  base::WeakPtrFactory<MediaFoundationRendererWrapper> weak_factory_{this};
+
+  DISALLOW_COPY_AND_ASSIGN(MediaFoundationRendererWrapper);
+};
+
+}  // namespace media
+
+#endif  // MEDIA_MOJO_SERVICES_MEDIA_FOUNDATION_RENDERER_WRAPPER_H_
diff --git a/media/renderers/audio_renderer_impl.cc b/media/renderers/audio_renderer_impl.cc
index 0a2905e..9435558 100644
--- a/media/renderers/audio_renderer_impl.cc
+++ b/media/renderers/audio_renderer_impl.cc
@@ -727,7 +727,7 @@
 
 void AudioRendererImpl::SetVolume(float volume) {
   DCHECK(task_runner_->BelongsToCurrentThread());
-    was_unmuted_ = was_unmuted_ || volume != 0;
+  was_unmuted_ = was_unmuted_ || volume != 0;
   if (state_ == kUninitialized || state_ == kInitializing) {
     volume_ = volume;
     return;
diff --git a/media/renderers/win/media_foundation_renderer.cc b/media/renderers/win/media_foundation_renderer.cc
index fd6c928..7c47302 100644
--- a/media/renderers/win/media_foundation_renderer.cc
+++ b/media/renderers/win/media_foundation_renderer.cc
@@ -73,7 +73,7 @@
     scoped_refptr<base::SequencedTaskRunner> task_runner,
     bool force_dcomp_mode_for_testing)
     : muted_(muted),
-      task_runner_(task_runner),
+      task_runner_(std::move(task_runner)),
       force_dcomp_mode_for_testing_(force_dcomp_mode_for_testing) {
   DVLOG_FUNC(1);
 }
diff --git a/mojo/core/BUILD.gn b/mojo/core/BUILD.gn
index ae3c1b23..aeb77dd 100644
--- a/mojo/core/BUILD.gn
+++ b/mojo/core/BUILD.gn
@@ -105,7 +105,6 @@
 
     public_deps = [
       "//base",
-      "//mojo/core/embedder:features",
       "//mojo/core/ports",
       "//mojo/public/c/system:headers",
       "//mojo/public/cpp/platform",
@@ -125,15 +124,6 @@
           "channel_posix.h",
         ]
       }
-
-      if ((is_linux || is_chromeos || is_android) && !is_nacl) {
-        sources += [
-          "channel_linux.cc",
-          "channel_linux.h",
-        ]
-
-        public += [ "channel_linux.h" ]
-      }
     }
 
     if (is_mac) {
diff --git a/mojo/core/channel.cc b/mojo/core/channel.cc
index 8609404..4fe42510 100644
--- a/mojo/core/channel.cc
+++ b/mojo/core/channel.cc
@@ -480,7 +480,9 @@
     data_ = MakeAlignedBuffer(size_);
   }
 
-  ~ReadBuffer() { DCHECK(data_); }
+  ~ReadBuffer() {
+    DCHECK(data_);
+  }
 
   const char* occupied_bytes() const {
     return data_.get() + num_discarded_bytes_;
@@ -727,19 +729,5 @@
   return false;
 }
 
-// Currently only Non-nacl CrOs, Linux, and Android support upgrades.
-#if defined(OS_NACL) || \
-    (!(defined(OS_CHROMEOS) || defined(OS_LINUX) || defined(OS_ANDROID)))
-// static
-MOJO_SYSTEM_IMPL_EXPORT bool Channel::SupportsChannelUpgrade() {
-  return false;
-}
-
-MOJO_SYSTEM_IMPL_EXPORT void Channel::OfferChannelUpgrade() {
-  NOTREACHED();
-  return;
-}
-#endif
-
 }  // namespace core
 }  // namespace mojo
diff --git a/mojo/core/channel.h b/mojo/core/channel.h
index 6e672f05..3b01b39 100644
--- a/mojo/core/channel.h
+++ b/mojo/core/channel.h
@@ -76,16 +76,6 @@
 #endif
       // A normal message that uses Header and can contain extra header values.
       NORMAL,
-
-      // The UPGRADE_OFFER control message offers to upgrade the channel to
-      // another side who has advertised support for an upgraded channel.
-      UPGRADE_OFFER,
-      // The UPGRADE_ACCEPT control message is returned when an upgrade offer is
-      // accepted.
-      UPGRADE_ACCEPT,
-      // The UPGRADE_REJECT control message is returned when the receiver cannot
-      // or chooses not to upgrade the channel.
-      UPGRADE_REJECT,
     };
 
 #pragma pack(push, 1)
@@ -291,15 +281,7 @@
 #if defined(OS_POSIX) && !defined(OS_NACL) && !defined(OS_MAC)
   // At this point only ChannelPosix needs InitFeatures.
   static void set_posix_use_writev(bool use_writev);
-#endif  // defined(OS_POSIX) && !defined(OS_NACL) && !defined(OS_MAC)
-
-  // SupportsChannelUpgrade will return true if this channel is capable of being
-  // upgraded.
-  static bool SupportsChannelUpgrade();
-
-  // OfferChannelUpgrade will inform this channel that it should offer an
-  // upgrade to the remote.
-  void OfferChannelUpgrade();
+#endif
 
   // Allows the caller to change the Channel's HandlePolicy after construction.
   void set_handle_policy(HandlePolicy policy) { handle_policy_ = policy; }
@@ -346,9 +328,6 @@
 
   Delegate* delegate() const { return delegate_; }
 
-  // Allows the caller to determine the current HandlePolicy.
-  HandlePolicy handle_policy() const { return handle_policy_; }
-
   // Called by the implementation when it wants somewhere to stick data.
   // |*buffer_capacity| may be set by the caller to indicate the desired buffer
   // size. If 0, a sane default size will be used instead.
diff --git a/mojo/core/channel_linux.cc b/mojo/core/channel_linux.cc
deleted file mode 100644
index 222cba8..0000000
--- a/mojo/core/channel_linux.cc
+++ /dev/null
@@ -1,869 +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.
-
-#include "mojo/core/channel_linux.h"
-
-#include <fcntl.h>
-#include <linux/futex.h>
-#include <linux/memfd.h>
-#include <sys/eventfd.h>
-#include <sys/mman.h>
-#include <sys/syscall.h>
-#include <unistd.h>
-
-#include <algorithm>
-#include <atomic>
-#include <cstring>
-#include <limits>
-#include <memory>
-
-#include "base/bind.h"
-#include "base/callback.h"
-#include "base/files/scoped_file.h"
-#include "base/location.h"
-#include "base/logging.h"
-#include "base/macros.h"
-#include "base/memory/ptr_util.h"
-#include "base/memory/ref_counted.h"
-#include "base/memory/shared_memory_security_policy.h"
-#include "base/message_loop/message_pump_for_io.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/posix/eintr_wrapper.h"
-#include "base/task_runner.h"
-#include "base/time/time.h"
-#include "build/build_config.h"
-#include "mojo/core/core.h"
-#include "mojo/core/embedder/features.h"
-
-namespace mojo {
-namespace core {
-
-// DataAvailableNotifier is a simple interface which allows us to
-// substitute how we notify the reader that we've made data available,
-// implementations might be EventFDNotifier or FutexNotifier.
-class DataAvailableNotifier {
- public:
-  DataAvailableNotifier() = default;
-  explicit DataAvailableNotifier(base::RepeatingClosure callback)
-      : callback_(std::move(callback)) {}
-
-  virtual ~DataAvailableNotifier() = default;
-
-  // The writer should notify the reader by invoking Notify.
-  virtual bool Notify() = 0;
-
-  // A reader should clear the notification (if appropriate) by calling Clear.
-  virtual bool Clear() = 0;
-
-  // Is_valid will return true if the implementation is valid and can be used.
-  virtual bool is_valid() const = 0;
-
- protected:
-  // DataAvailable will be called by implementations of DataAvailableNotifier to
-  // dispatch this message into the registered callback.
-  void DataAvailable() {
-    DCHECK(callback_);
-    callback_.Run();
-  }
-
-  base::RepeatingClosure callback_;
-};
-
-namespace {
-
-constexpr int kMemFDSeals = F_SEAL_SEAL | F_SEAL_SHRINK | F_SEAL_GROW;
-
-std::atomic_bool g_params_set{false};
-std::atomic_bool g_use_shared_mem{false};
-std::atomic_uint32_t g_shared_mem_pages{4};
-
-struct UpgradeOfferMessage {
-  constexpr static int kEventFdNotifier = 1;
-  constexpr static int kSupportedVersion = kEventFdNotifier;
-
-  constexpr static int kDefaultPages = 4;
-
-  int version = kSupportedVersion;
-  int num_pages = kDefaultPages;
-};
-
-constexpr size_t RoundUpToWordBoundary(size_t size) {
-  return (size + (sizeof(void*) - 1)) & ~(sizeof(void*) - 1);
-}
-
-base::ScopedFD CreateSealedMemFD(size_t size) {
-  CHECK_GT(size, 0u);
-  CHECK_EQ(size % base::GetPageSize(), 0u);
-  base::ScopedFD fd(syscall(__NR_memfd_create, "mojo_channel_linux",
-                            MFD_CLOEXEC | MFD_ALLOW_SEALING));
-  if (!fd.is_valid()) {
-    PLOG(ERROR) << "Unable to create memfd for shared memory channel";
-    return {};
-  }
-
-  if (ftruncate(fd.get(), size) < 0) {
-    PLOG(ERROR) << "Unable to truncate memfd for shared memory channel";
-    return {};
-  }
-
-  // We make sure to use F_SEAL_SEAL to prevent any further changes to the
-  // seals and F_SEAL_SHRINK guarantees that we won't accidentally decrease
-  // the size, and similarly F_SEAL_GROW for increasing size.
-  if (fcntl(fd.get(), F_ADD_SEALS, kMemFDSeals) < 0) {
-    PLOG(ERROR) << "Unable to seal memfd for shared memory channel";
-    return {};
-  }
-
-  return fd;
-}
-
-// It's very important that we always verify that the FD we're passing and the
-// FD we're receive is a properly sealed MemFD.
-bool ValidateFDIsProperlySealedMemFD(const base::ScopedFD& fd) {
-  int seals = 0;
-  if ((seals = fcntl(fd.get(), F_GET_SEALS)) < 0) {
-    PLOG(ERROR) << "Unable to get seals on memfd for shared memory channel";
-    return false;
-  }
-
-  return seals == kMemFDSeals;
-}
-
-// EventFDNotifier is an implementation of the DataAvailableNotifier interface
-// which uses EventFDNotifier to signal the reader.
-class EventFDNotifier : public DataAvailableNotifier,
-                        public base::MessagePumpForIO::FdWatcher {
- public:
-  EventFDNotifier(EventFDNotifier&& efd) = default;
-  ~EventFDNotifier() override { reset(); }
-
-  static constexpr int kEfdFlags = EFD_CLOEXEC | EFD_NONBLOCK;
-
-  static std::unique_ptr<EventFDNotifier> CreateWriteNotifier() {
-    int fd = eventfd(0, kEfdFlags);
-    if (fd < 0) {
-      PLOG(ERROR) << "Unable to create an eventfd";
-      return nullptr;
-    }
-
-    return WrapFD(base::ScopedFD(fd));
-  }
-
-  // The EventFD read notifier MUST be created on the IOThread. Luckily you're
-  // typically creating the read notifier in response to an OFFER_UPGRADE
-  // message which was received on the IOThread.
-  static std::unique_ptr<EventFDNotifier> CreateReadNotifier(
-      base::ScopedFD efd,
-      base::RepeatingClosure cb,
-      scoped_refptr<base::SingleThreadTaskRunner> io_task_runner) {
-    DCHECK(io_task_runner->RunsTasksInCurrentSequence());
-    DCHECK(cb);
-
-    return WrapFDWithCallback(std::move(efd), std::move(cb), io_task_runner);
-  }
-
-  static bool KernelSupported() {
-    // Try to create an eventfd with bad flags if we get -EINVAL it's supported
-    // if we get -ENOSYS it's not, we also support -EPERM because seccomp
-    // policies can cause it to be returned.
-    int ret = eventfd(0, ~0);
-    PCHECK(ret < 0 && (errno == EINVAL || errno == ENOSYS || errno == EPERM));
-    return (ret < 0 && errno == EINVAL);
-  }
-
-  // DataAvailableNotifier impl:
-  bool Clear() override {
-    uint64_t value = 0;
-    ssize_t res = HANDLE_EINTR(
-        read(fd_.get(), reinterpret_cast<void*>(&value), sizeof(value)));
-    if (res < static_cast<int64_t>(sizeof(value))) {
-      PLOG_IF(ERROR, errno != EWOULDBLOCK) << "eventfd read error";
-    }
-    return res == sizeof(value);
-  }
-
-  bool Notify() override {
-    uint64_t value = 1;
-    ssize_t res = HANDLE_EINTR(write(fd_.get(), &value, sizeof(value)));
-    return res == sizeof(value);
-  }
-
-  bool is_valid() const override { return fd_.is_valid(); }
-
-  // base::MessagePumpForIO::FdWatcher impl:
-  void OnFileCanReadWithoutBlocking(int fd) override {
-    DCHECK(fd == fd_.get());
-
-    // Invoke the callback to inform them that data is available to read.
-    DataAvailable();
-  }
-
-  void OnFileCanWriteWithoutBlocking(int fd) override {}
-
-  base::ScopedFD take() { return std::move(fd_); }
-  base::ScopedFD take_dup() {
-    return base::ScopedFD(HANDLE_EINTR(dup(fd_.get())));
-  }
-
-  void reset() {
-    watcher_.reset();
-    fd_.reset();
-  }
-
-  int fd() { return fd_.get(); }
-
- private:
-  explicit EventFDNotifier(base::ScopedFD fd) : fd_(std::move(fd)) {}
-  explicit EventFDNotifier(
-      base::ScopedFD fd,
-      base::RepeatingClosure cb,
-      scoped_refptr<base::SingleThreadTaskRunner> io_task_runner)
-      : DataAvailableNotifier(std::move(cb)),
-        fd_(std::move(fd)),
-        io_task_runner_(io_task_runner) {
-    DCHECK(watcher_);
-    watcher_ =
-        std::make_unique<base::MessagePumpForIO::FdWatchController>(FROM_HERE);
-    WaitForEventFDOnIOThread();
-  }
-
-  static std::unique_ptr<EventFDNotifier> WrapFD(base::ScopedFD fd) {
-    return base::WrapUnique<EventFDNotifier>(
-        new EventFDNotifier(std::move(fd)));
-  }
-
-  static std::unique_ptr<EventFDNotifier> WrapFDWithCallback(
-      base::ScopedFD fd,
-      base::RepeatingClosure cb,
-      scoped_refptr<base::SingleThreadTaskRunner> io_task_runner) {
-    return base::WrapUnique<EventFDNotifier>(
-        new EventFDNotifier(std::move(fd), std::move(cb), io_task_runner));
-  }
-
-  void WaitForEventFDOnIOThread() {
-    DCHECK(io_task_runner_->RunsTasksInCurrentSequence());
-    base::CurrentIOThread::Get()->WatchFileDescriptor(
-        fd_.get(), true, base::MessagePumpForIO::WATCH_READ, watcher_.get(),
-        this);
-  }
-
-  base::ScopedFD fd_;
-  std::unique_ptr<base::MessagePumpForIO::FdWatchController> watcher_;
-  scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
-
-  DISALLOW_COPY_AND_ASSIGN(EventFDNotifier);
-};
-
-}  // namespace
-
-// SharedBuffer is an abstraction around a region of shared memory, it has
-// methods to facilitate safely reading and writing into the shared region.
-// SharedBuffer only handles the access to the shared memory any notifications
-// must be performed separately.
-class ChannelLinux::SharedBuffer {
- public:
-  SharedBuffer(SharedBuffer&& other) = default;
-  ~SharedBuffer() { reset(); }
-
-  enum class Error { kSuccess = 0, kGeneralError = 1, kControlCorruption = 2 };
-
-  static std::unique_ptr<SharedBuffer> Create(const base::ScopedFD& memfd,
-                                              size_t size) {
-    if (!memfd.is_valid()) {
-      return nullptr;
-    }
-
-    // Enforce the system shared memory security policy.
-    if (!base::SharedMemorySecurityPolicy::AcquireReservationForMapping(size)) {
-      LOG(ERROR)
-          << "Unable to create shared buffer: unable to acquire reservation";
-      return nullptr;
-    }
-
-    uint8_t* ptr = reinterpret_cast<uint8_t*>(
-        mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_LOCKED,
-             memfd.get(), 0));
-
-    if (ptr == MAP_FAILED) {
-      PLOG(ERROR) << "Unable to map shared memory";
-
-      // Always clean up our reservation if we actually fail to map.
-      base::SharedMemorySecurityPolicy::ReleaseReservationForMapping(size);
-      return nullptr;
-    }
-
-    return base::WrapUnique<SharedBuffer>(new SharedBuffer(ptr, size));
-  }
-
-  uint8_t* usable_region_ptr() const { return base_ptr_ + kReservedSpace; }
-  size_t usable_len() const { return len_ - kReservedSpace; }
-  bool is_valid() const { return base_ptr_ != nullptr && len_ > 0; }
-
-  void reset() {
-    if (is_valid()) {
-      if (munmap(base_ptr_, len_) < 0) {
-        PLOG(ERROR) << "Unable to unmap shared buffer";
-        return;
-      }
-
-      base::SharedMemorySecurityPolicy::ReleaseReservationForMapping(len_);
-      base_ptr_ = nullptr;
-      len_ = 0;
-    }
-  }
-
-  // Only one side should call Initialize, this will initialize the first
-  // sizeof(ControlStructure) bytes as our control structure. This should be
-  // done when offering fast comms.
-  void Initialize() { new (static_cast<void*>(base_ptr_)) ControlStructure; }
-
-  // TryWrite will attempt to append |data| of |len| to the shared buffer, this
-  // call will only succeed if there is no one else trying to write AND there is
-  // enough space currently in the buffer.
-  Error TryWrite(const void* data, size_t len) {
-    DCHECK(data);
-    DCHECK(len);
-
-    if (len > usable_len()) {
-      UMA_HISTOGRAM_COUNTS_100000(
-          "Mojo.Channel.Linux.SharedMemWriteBytes_Fail_TooLarge", len);
-      return Error::kGeneralError;
-    }
-
-    if (!TryLockForWriting()) {
-      UMA_HISTOGRAM_COUNTS_100000(
-          "Mojo.Channel.Linux.SharedMemWriteBytes_Fail_NoLock", len);
-      return Error::kGeneralError;
-    }
-
-    // At this point we know that the space available can only grow because
-    // we're the only writer we will write from write_pos -> end and 0 -> (len
-    // - (end - write_pos)) where end is usable_len().
-    uint32_t cur_read_pos = read_pos().load();
-    uint32_t cur_write_pos = write_pos().load();
-
-    if (!ValidateReadWritePositions(cur_read_pos, cur_write_pos)) {
-      UnlockForWriting();
-      return Error::kControlCorruption;
-    }
-
-    uint32_t space_available =
-        usable_len() - NumBytesInUse(cur_read_pos, cur_write_pos);
-
-    if (space_available <= len) {
-      UnlockForWriting();
-      UMA_HISTOGRAM_COUNTS_100000(
-          "Mojo.Channel.Linux.SharedMemWriteBytes_Fail_NoSpace", len);
-
-      return Error::kGeneralError;
-    }
-
-    // If we do not have enough space from the current write position to the end
-    // then we will be forced to wrap around. If we do have enough space we can
-    // just start writing at the write position, otherwise we start writing at
-    // the write position up to the end of the usable area and then we write the
-    // remainder of the payload starting at position 0.
-    if ((usable_len() - cur_write_pos) > len) {
-      memcpy(usable_region_ptr() + cur_write_pos, data, len);
-    } else {
-      size_t copy1_len = usable_len() - cur_write_pos;
-      memcpy(usable_region_ptr() + cur_write_pos, data, copy1_len);
-      memcpy(usable_region_ptr(),
-             reinterpret_cast<const uint8_t*>(data) + copy1_len,
-             len - copy1_len);
-    }
-
-    // Atomically update the write position.
-    // We also verify that the write position did not advance, it SHOULD NEVER
-    // advance since we were holding the write lock.
-    if (write_pos().exchange((cur_write_pos + len) % usable_len()) !=
-        cur_write_pos) {
-      UnlockForWriting();
-      return Error::kControlCorruption;
-    }
-
-    UnlockForWriting();
-
-    return Error::kSuccess;
-  }
-
-  Error TryReadLocked(void* data, uint32_t len, uint32_t* bytes_read) {
-    uint32_t cur_read_pos = read_pos().load();
-    uint32_t cur_write_pos = write_pos().load();
-
-    if (!ValidateReadWritePositions(cur_read_pos, cur_write_pos)) {
-      return Error::kControlCorruption;
-    }
-
-    // The most we can read is the smaller of what's in use in the shared memory
-    // usable area and the buffer size we've been passed.
-    uint32_t bytes_available_to_read =
-        NumBytesInUse(cur_read_pos, cur_write_pos);
-    bytes_available_to_read = std::min(bytes_available_to_read, len);
-    if (bytes_available_to_read == 0) {
-      *bytes_read = 0;
-      return Error::kSuccess;
-    }
-
-    // We have two cases when reading, the first is the read position is behind
-    // the write position, in that case we can simply read all data between the
-    // read and write position (up to our buffer size). The second case is when
-    // the write position is behind the read position. In this situation we must
-    // read from the read position to the end of the available area, and
-    // continue reading from the 0 position up to the write position or the
-    // maximum buffer size (bytes_available_to_read).
-    if (cur_read_pos < cur_write_pos) {
-      memcpy(data, usable_region_ptr() + cur_read_pos, bytes_available_to_read);
-    } else {
-      // We first start by reading to the end of the the usable area, if we
-      // cannot read all the way (because our buffer is too small, we're done).
-      uint32_t bytes_from_read_to_end = usable_len() - cur_read_pos;
-      bytes_from_read_to_end =
-          std::min(bytes_from_read_to_end, bytes_available_to_read);
-      memcpy(data, usable_region_ptr() + cur_read_pos, bytes_from_read_to_end);
-
-      if (bytes_from_read_to_end < bytes_available_to_read) {
-        memcpy(reinterpret_cast<uint8_t*>(data) + bytes_from_read_to_end,
-               usable_region_ptr(),
-               bytes_available_to_read - bytes_from_read_to_end);
-      }
-    }
-
-    // Atomically update the read position.
-    // We also verify that the read position did not advance, it SHOULD NEVER
-    // advance since we were holding the read lock.
-    uint32_t new_read_pos =
-        (cur_read_pos + bytes_available_to_read) % usable_len();
-    if (read_pos().exchange(new_read_pos) != cur_read_pos) {
-      *bytes_read = 0;
-      return Error::kControlCorruption;
-    }
-
-    *bytes_read = bytes_available_to_read;
-    return Error::kSuccess;
-  }
-
-  bool TryLockForReading() {
-    // We return true if we set the flag (meaning it was false).
-    return !read_flag().test_and_set(std::memory_order_acquire);
-  }
-
-  void UnlockForReading() { read_flag().clear(std::memory_order_release); }
-
- private:
-  struct ControlStructure {
-    std::atomic_flag write_flag{false};
-    std::atomic_uint32_t write_pos{0};
-
-    std::atomic_flag read_flag{false};
-    std::atomic_uint32_t read_pos{0};
-
-    // If we're using a notification mechanism that relies on futex, make the
-    // space available for one, if not these 32bits are unused. The kernel
-    // requires they be 32bit aligned.
-    alignas(4) volatile uint32_t futex = 0;
-  };
-
-  // This function will only validate that the values provided for write and
-  // read positions are valid based on usable size of the shared memory region.
-  // This should ALWAYS be called before attempting a write or read using
-  // atomically loaded values from the control structure.
-  bool ValidateReadWritePositions(uint32_t read_pos, uint32_t write_pos) {
-    // The only valid values for read and write positions are [0 - usable_len
-    // - 1].
-    if (write_pos >= usable_len()) {
-      LOG(ERROR) << "Write position of shared buffer is currently beyond the "
-                    "usable length";
-      return false;
-    }
-
-    if (read_pos >= usable_len()) {
-      LOG(ERROR) << "Read position of shared buffer is currently beyond the "
-                    "usable length";
-      return false;
-    }
-
-    return true;
-  }
-
-  // NumBytesInUse will calculate how many bytes in the shared buffer are
-  // currently in use.
-  uint32_t NumBytesInUse(uint32_t read_pos, uint32_t write_pos) {
-    uint32_t bytes_in_use = 0;
-    if (read_pos <= write_pos) {
-      bytes_in_use = write_pos - read_pos;
-    } else {
-      bytes_in_use = write_pos + (usable_len() - read_pos);
-    }
-
-    return bytes_in_use;
-  }
-
-  bool TryLockForWriting() {
-    // We return true if we set the flag (meaning it was false).
-    return !write_flag().test_and_set(std::memory_order_acquire);
-  }
-
-  void UnlockForWriting() { write_flag().clear(std::memory_order_release); }
-
-  // This is the space we need to reserve in this shared buffer for our control
-  // structure at the start.
-  constexpr static size_t kReservedSpace =
-      RoundUpToWordBoundary(sizeof(ControlStructure));
-
-  std::atomic_flag& write_flag() {
-    DCHECK(is_valid());
-    return reinterpret_cast<ControlStructure*>(base_ptr_)->write_flag;
-  }
-
-  std::atomic_flag& read_flag() {
-    DCHECK(is_valid());
-    return reinterpret_cast<ControlStructure*>(base_ptr_)->read_flag;
-  }
-
-  std::atomic_uint32_t& read_pos() {
-    DCHECK(is_valid());
-    return reinterpret_cast<ControlStructure*>(base_ptr_)->read_pos;
-  }
-
-  std::atomic_uint32_t& write_pos() {
-    DCHECK(is_valid());
-    return reinterpret_cast<ControlStructure*>(base_ptr_)->write_pos;
-  }
-
-  SharedBuffer(uint8_t* ptr, size_t len) : base_ptr_(ptr), len_(len) {}
-
-  uint8_t* base_ptr_ = nullptr;
-  size_t len_ = 0;
-
-  DISALLOW_COPY_AND_ASSIGN(SharedBuffer);
-};
-
-ChannelLinux::ChannelLinux(
-    Delegate* delegate,
-    ConnectionParams connection_params,
-    HandlePolicy handle_policy,
-    scoped_refptr<base::SingleThreadTaskRunner> io_task_runner)
-    : ChannelPosix(delegate,
-                   std::move(connection_params),
-                   handle_policy,
-                   io_task_runner),
-      num_pages_(g_shared_mem_pages.load()) {}
-
-ChannelLinux::~ChannelLinux() = default;
-
-void ChannelLinux::Write(MessagePtr message) {
-  if (!shared_mem_writer_ || message->has_handles() || reject_writes_) {
-    // Let the ChannelPosix deal with this.
-    return ChannelPosix::Write(std::move(message));
-  }
-
-  // Can we use the fast shared memory buffer?
-  SharedBuffer::Error write_result =
-      write_buffer_->TryWrite(message->data(), message->data_num_bytes());
-  if (write_result == SharedBuffer::Error::kGeneralError) {
-    // We can handle this with the posix channel.
-    return ChannelPosix::Write(std::move(message));
-  } else if (write_result == SharedBuffer::Error::kControlCorruption) {
-    // We will no longer be issuing writes via shared memory, and we will
-    // dispatch a write error.
-    reject_writes_ = true;
-
-    // Theoretically we could fall back to only using PosixChannel::Write
-    // but if this situation happens it's likely something else is going
-    // horribly wrong.
-    io_task_runner_->PostTask(
-        FROM_HERE, base::BindOnce(&ChannelLinux::OnWriteError, this,
-                                  Channel::Error::kReceivedMalformedData));
-    return;
-  }
-
-  //  The write with shared memory was successful.
-  UMA_HISTOGRAM_COUNTS_100000("Mojo.Channel.Linux.SharedMemWriteBytes",
-                              message->data_num_bytes());
-  write_notifier_->Notify();
-}
-
-void ChannelLinux::OfferSharedMemUpgrade() {
-  if (!offered_.test_and_set() && UpgradesEnabled()) {
-    // Before we offer we need to make sure we can send handles, if we can't
-    // then no point in trying.
-    if (handle_policy() == HandlePolicy::kAcceptHandles) {
-      OfferSharedMemUpgradeInternal();
-    }
-  }
-}
-
-bool ChannelLinux::OnControlMessage(Message::MessageType message_type,
-                                    const void* payload,
-                                    size_t payload_size,
-                                    std::vector<PlatformHandle> handles) {
-  switch (message_type) {
-    case Message::MessageType::UPGRADE_OFFER: {
-      const UpgradeOfferMessage* msg =
-          reinterpret_cast<const UpgradeOfferMessage*>(payload);
-      if (payload_size < sizeof(UpgradeOfferMessage)) {
-        LOG(ERROR) << "Received a malformed UPGRADE_OFFER message";
-        return true;
-      }
-
-      if (msg->version != UpgradeOfferMessage::kSupportedVersion) {
-        LOG(ERROR) << "Reject shared mem upgrade unexpected version: "
-                   << msg->version;
-        RejectUpgradeOffer();
-        return true;
-      }
-
-      if (handles.size() != 2) {
-        LOG(ERROR) << "Received an UPGRADE_OFFER without two FDs";
-        RejectUpgradeOffer();
-        return true;
-      }
-
-      if (read_buffer_ || read_notifier_) {
-        LOG(ERROR) << "Received an UPGRADE_OFFER on already upgraded channel";
-        return true;
-      }
-
-      base::ScopedFD memfd(handles[0].TakeFD());
-      if (memfd.is_valid() && !ValidateFDIsProperlySealedMemFD(memfd)) {
-        PLOG(ERROR) << "Passed FD was not properly sealed";
-        DLOG(FATAL) << "MemFD was NOT properly sealed";
-        memfd.reset();
-      }
-
-      if (!memfd.is_valid()) {
-        RejectUpgradeOffer();
-        return true;
-      }
-
-      if (msg->num_pages <= 0 || msg->num_pages > 128) {
-        LOG(ERROR) << "SharedMemory upgrade offer was received with invalid "
-                      "number of pages: "
-                   << msg->num_pages;
-        RejectUpgradeOffer();
-      }
-
-      std::unique_ptr<DataAvailableNotifier> read_notifier;
-      if (msg->version == UpgradeOfferMessage::kEventFdNotifier) {
-        read_notifier = EventFDNotifier::CreateReadNotifier(
-            handles[1].TakeFD(),
-            base::BindRepeating(&ChannelLinux::SharedMemReadReady, this),
-            io_task_runner_);
-      }
-
-      if (!read_notifier) {
-        RejectUpgradeOffer();
-        return true;
-      }
-
-      read_notifier_ = std::move(read_notifier);
-
-      std::unique_ptr<SharedBuffer> read_sb = SharedBuffer::Create(
-          std::move(memfd), msg->num_pages * base::GetPageSize());
-      if (!read_sb || !read_sb->is_valid()) {
-        RejectUpgradeOffer();
-        return true;
-      }
-
-      read_buffer_ = std::move(read_sb);
-
-      read_buf_.resize(read_buffer_->usable_len());
-      AcceptUpgradeOffer();
-
-      // And if we haven't offered ourselves just go ahead and do it now.
-      OfferSharedMemUpgrade();
-      return true;
-    }
-
-    case Message::MessageType::UPGRADE_ACCEPT: {
-      if (!write_buffer_ || !write_notifier_ || !write_notifier_->is_valid()) {
-        LOG(ERROR) << "Received unexpected UPGRADE_ACCEPT";
-
-        // Clean up anything that may have been set.
-        shared_mem_writer_ = false;
-        write_buffer_.reset();
-        write_notifier_.reset();
-        return true;
-      }
-
-      shared_mem_writer_ = true;
-      return true;
-    }
-
-    case Message::MessageType::UPGRADE_REJECT: {
-      // We can free our resources.
-      shared_mem_writer_ = false;
-      write_buffer_.reset();
-      write_notifier_.reset();
-
-      return true;
-    }
-    default:
-      break;
-  }
-
-  return ChannelPosix::OnControlMessage(message_type, payload, payload_size,
-                                        std::move(handles));
-}
-
-void ChannelLinux::SharedMemReadReady() {
-  CHECK(read_buffer_);
-  if (read_buffer_->TryLockForReading()) {
-    read_notifier_->Clear();
-    do {
-      uint32_t bytes_read = 0;
-      SharedBuffer::Error read_res = read_buffer_->TryReadLocked(
-          read_buf_.data(), read_buf_.size(), &bytes_read);
-      if (read_res == SharedBuffer::Error::kControlCorruption) {
-        // This is an error we cannot recover from.
-        OnError(Error::kReceivedMalformedData);
-        read_buffer_->UnlockForReading();
-        break;
-      }
-
-      if (bytes_read == 0) {
-        break;
-      }
-
-      UMA_HISTOGRAM_COUNTS_100000("Mojo.Channel.Linux.SharedMemReadBytes",
-                                  bytes_read);
-
-      // Now dispatch the message, we KNOW it's at least one full message
-      // because we checked the message size before putting it into the
-      // shared buffer, this mechanism can never write a partial message.
-      off_t data_offset = 0;
-      while (bytes_read - data_offset > 0) {
-        size_t read_size_hint;
-        DispatchResult result = TryDispatchMessage(
-            base::make_span(
-                reinterpret_cast<char*>(read_buf_.data() + data_offset),
-                bytes_read - data_offset),
-            &read_size_hint);
-
-        // We cannot have a message parse failure, we KNOW that we wrote a
-        // full message if we get one something has gone horribly wrong.
-        if (result != DispatchResult::kOK) {
-          LOG(ERROR) << "Recevied a bad message via shared memory";
-          OnError(Error::kReceivedMalformedData);
-          break;
-        }
-
-        // The next message will start after read_size_hint bytes the writer
-        // guarantees that we wrote a full message and we've guaranteed that the
-        // message was dispatched correctly so we know where the next message
-        // starts.
-        data_offset += read_size_hint;
-      }
-    } while (true);
-    read_buffer_->UnlockForReading();
-  }
-}
-
-void ChannelLinux::OnWriteError(Error error) {
-  reject_writes_ = true;
-  ChannelPosix::OnWriteError(error);
-}
-
-void ChannelLinux::ShutDownOnIOThread() {
-  reject_writes_ = true;
-  read_notifier_.reset();
-  write_notifier_.reset();
-
-  ChannelPosix::ShutDownOnIOThread();
-}
-
-void ChannelLinux::StartOnIOThread() {
-  ChannelPosix::StartOnIOThread();
-}
-
-void ChannelLinux::OfferSharedMemUpgradeInternal() {
-  if (reject_writes_) {
-    return;
-  }
-
-  if (write_buffer_ || write_notifier_) {
-    LOG(ERROR) << "Upgrade attempted on an already upgraded channel";
-    return;
-  }
-
-  const size_t kSize = num_pages_ * base::GetPageSize();
-  base::ScopedFD memfd = CreateSealedMemFD(kSize);
-  if (!memfd.is_valid()) {
-    PLOG(ERROR) << "Unable to create memfd";
-    return;
-  }
-
-  bool properly_sealed = ValidateFDIsProperlySealedMemFD(memfd);
-  if (!properly_sealed) {
-    // We will not attempt an offer, something has gone wrong.
-    LOG(ERROR) << "FD was not properly sealed we cannot offer upgrade.";
-    return;
-  }
-
-  std::unique_ptr<SharedBuffer> write_buffer =
-      SharedBuffer::Create(memfd, kSize);
-  if (!write_buffer || !write_buffer->is_valid()) {
-    PLOG(ERROR) << "Unable to map shared memory";
-    return;
-  }
-
-  write_buffer->Initialize();
-
-  std::unique_ptr<EventFDNotifier> write_notifier =
-      EventFDNotifier::CreateWriteNotifier();
-  if (!write_notifier) {
-    PLOG(ERROR) << "Failed to create eventfd write notifier";
-    return;
-  }
-
-  std::vector<PlatformHandle> fds;
-  fds.emplace_back(std::move(memfd));
-  fds.emplace_back(write_notifier->take_dup());
-
-  write_notifier_ = std::move(write_notifier);
-  write_buffer_ = std::move(write_buffer);
-
-  UpgradeOfferMessage offer_msg;
-  offer_msg.num_pages = num_pages_;
-  MessagePtr msg(new Channel::Message(sizeof(UpgradeOfferMessage),
-                                      /*num handles=*/fds.size(),
-                                      Message::MessageType::UPGRADE_OFFER));
-  msg->SetHandles(std::move(fds));
-  memcpy(msg->mutable_payload(), &offer_msg, sizeof(offer_msg));
-
-  ChannelPosix::Write(std::move(msg));
-}
-
-// static
-bool ChannelLinux::KernelSupportsUpgradeRequirements() {
-  static bool supported = []() -> bool {
-    // Do we have memfd_create support, we check by seeing if we get an -ENOSYS
-    // or an -EINVAL. We also support -EPERM because of seccomp rules this is
-    // another possible outcome.
-    int ret = syscall(__NR_memfd_create, "", ~0);
-    PCHECK(ret < 0 && (errno == EINVAL || errno == ENOSYS || errno == EPERM));
-    bool memfd_supported = (ret < 0 && errno == EINVAL);
-    return memfd_supported && EventFDNotifier::KernelSupported();
-  }();
-  return supported;
-}
-
-// static
-bool ChannelLinux::UpgradesEnabled() {
-  if (!g_params_set.load())
-    return g_use_shared_mem.load();
-
-  return base::FeatureList::IsEnabled(kMojoLinuxChannelSharedMem);
-}
-
-// static
-void ChannelLinux::SetSharedMemParameters(bool enabled, uint32_t num_pages) {
-  g_params_set.store(true);
-  g_use_shared_mem.store(enabled);
-  g_shared_mem_pages.store(num_pages);
-}
-
-}  // namespace core
-}  // namespace mojo
diff --git a/mojo/core/channel_linux.h b/mojo/core/channel_linux.h
deleted file mode 100644
index 9793804a..0000000
--- a/mojo/core/channel_linux.h
+++ /dev/null
@@ -1,95 +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 MOJO_CORE_CHANNEL_LINUX_H_
-#define MOJO_CORE_CHANNEL_LINUX_H_
-
-#include <atomic>
-#include <memory>
-
-#include "base/macros.h"
-#include "build/build_config.h"
-#include "mojo/core/channel_posix.h"
-
-namespace mojo {
-namespace core {
-
-class DataAvailableNotifier;
-
-// ChannelLinux is a specialization of ChannelPosix which has support for shared
-// memory via Mojo channel upgrades. By default on Linux, CrOS, and Android
-// every channel will be of type ChannelLinux which can be upgraded at runtime
-// to take advantage of shared memory when all required kernel features are
-// present.
-class MOJO_SYSTEM_IMPL_EXPORT ChannelLinux : public ChannelPosix {
- public:
-  ChannelLinux(Delegate* delegate,
-               ConnectionParams connection_params,
-               HandlePolicy handle_policy,
-               scoped_refptr<base::SingleThreadTaskRunner> io_task_runner);
-
-  ChannelLinux(const ChannelLinux&) = delete;
-  ChannelLinux& operator=(const ChannelLinux&) = delete;
-
-  // KernelSupportsUpgradeRequirements will return true if the kernel supports
-  // the features necessary to use an upgrade channel. How the channel will be
-  // upgraded is an implementation detail and this just tells the caller that
-  // calling Channel::UpgradeChannel() will have some effect.
-  static bool KernelSupportsUpgradeRequirements();
-
-  // Will return true if at least one feature that is available via upgrade is
-  // enabled.
-  static bool UpgradesEnabled();
-
-  // SetSharedMemParams will control whether shared memory is used for this
-  // channel.
-  static void SetSharedMemParameters(bool enabled, uint32_t num_pages);
-
-  // ChannelPosix impl:
-  void Write(MessagePtr message) override;
-  void OfferSharedMemUpgrade();
-  bool OnControlMessage(Message::MessageType message_type,
-                        const void* payload,
-                        size_t payload_size,
-                        std::vector<PlatformHandle> handles) override;
-  void OnWriteError(Error error) override;
-
-  void StartOnIOThread() override;
-  void ShutDownOnIOThread() override;
-
- private:
-  ~ChannelLinux() override;
-
-  class SharedBuffer;
-
-  void OfferSharedMemUpgradeInternal();
-  void SharedMemReadReady();
-
-  // We only offer once, we use an atomic flag to guarantee no races to offer.
-  std::atomic_flag offered_{false};
-
-  // This flag keeps track of whether or not we've established a shared memory
-  // channel with the remote. If false we always fall back to the PosixChannel
-  // (socket).
-  bool shared_mem_writer_ = false;
-
-  std::unique_ptr<DataAvailableNotifier> write_notifier_;
-  std::unique_ptr<SharedBuffer> write_buffer_;
-
-  std::unique_ptr<DataAvailableNotifier> read_notifier_;
-  std::unique_ptr<SharedBuffer> read_buffer_;
-
-  uint32_t num_pages_ = 0;
-
-  bool reject_writes_ = false;
-
-  // This is a temporary buffer we use to remove messages from the shared buffer
-  // for validation and dispatching.
-  std::vector<uint8_t> read_buf_;
-};
-
-}  // namespace core
-}  // namespace mojo
-
-#endif
diff --git a/mojo/core/channel_posix.cc b/mojo/core/channel_posix.cc
index 5dd7359..2335e8d2 100644
--- a/mojo/core/channel_posix.cc
+++ b/mojo/core/channel_posix.cc
@@ -31,20 +31,15 @@
 #if !defined(OS_NACL)
 #include <limits.h>
 #include <sys/uio.h>
-
-#if (defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_ANDROID))
-#include "mojo/core/channel_linux.h"
 #endif
 
-#endif  // !defined(OS_NACL)
-
 namespace mojo {
 namespace core {
 
 namespace {
 #if !defined(OS_NACL)
 std::atomic<bool> g_use_writev{false};
-#endif  // !defined(OS_NACL)
+#endif
 
 const size_t kMaxBatchReadCapacity = 256 * 1024;
 }  // namespace
@@ -261,11 +256,11 @@
     ignore_result(server_.TakePlatformHandle());
   }
 #if defined(OS_IOS)
-  fds_to_close_.clear();
+    fds_to_close_.clear();
 #endif
 
-  // May destroy the |this| if it was the last reference.
-  self_ = nullptr;
+    // May destroy the |this| if it was the last reference.
+    self_ = nullptr;
 }
 
 void ChannelPosix::WillDestroyCurrentMessageLoop() {
@@ -278,67 +273,68 @@
   if (server_.is_valid()) {
     CHECK_EQ(fd, server_.platform_handle().GetFD().get());
 #if !defined(OS_NACL)
-    read_watcher_.reset();
-    base::CurrentThread::Get()->RemoveDestructionObserver(this);
+      read_watcher_.reset();
+      base::CurrentThread::Get()->RemoveDestructionObserver(this);
 
-    AcceptSocketConnection(server_.platform_handle().GetFD().get(), &socket_);
-    ignore_result(server_.TakePlatformHandle());
-    if (!socket_.is_valid()) {
-      OnError(Error::kConnectionFailed);
-      return;
-    }
-    StartOnIOThread();
-#else
-    NOTREACHED();
-#endif
-    return;
-  }
-  CHECK_EQ(fd, socket_.get());
-
-  bool validation_error = false;
-  bool read_error = false;
-  size_t next_read_size = 0;
-  size_t buffer_capacity = 0;
-  size_t total_bytes_read = 0;
-  size_t bytes_read = 0;
-  do {
-    buffer_capacity = next_read_size;
-    char* buffer = GetReadBuffer(&buffer_capacity);
-    DCHECK_GT(buffer_capacity, 0u);
-
-    std::vector<base::ScopedFD> incoming_fds;
-    ssize_t read_result =
-        SocketRecvmsg(socket_.get(), buffer, buffer_capacity, &incoming_fds);
-    for (auto& incoming_fd : incoming_fds)
-      incoming_fds_.emplace_back(std::move(incoming_fd));
-
-    if (read_result > 0) {
-      bytes_read = static_cast<size_t>(read_result);
-      total_bytes_read += bytes_read;
-      if (!OnReadComplete(bytes_read, &next_read_size)) {
-        read_error = true;
-        validation_error = true;
-        break;
+      AcceptSocketConnection(server_.platform_handle().GetFD().get(), &socket_);
+      ignore_result(server_.TakePlatformHandle());
+      if (!socket_.is_valid()) {
+        OnError(Error::kConnectionFailed);
+        return;
       }
-    } else if (read_result == 0 || (errno != EAGAIN && errno != EWOULDBLOCK)) {
-      read_error = true;
-      break;
-    } else {
-      // We expect more data but there is none to read. The
-      // FileDescriptorWatcher will wake us up again once there is.
-      DCHECK(errno == EAGAIN || errno == EWOULDBLOCK);
+      StartOnIOThread();
+#else
+      NOTREACHED();
+#endif
       return;
-    }
-  } while (bytes_read == buffer_capacity &&
-           total_bytes_read < kMaxBatchReadCapacity && next_read_size > 0);
-  if (read_error) {
-    // Stop receiving read notifications.
-    read_watcher_.reset();
-    if (validation_error)
-      OnError(Error::kReceivedMalformedData);
-    else
-      OnError(Error::kDisconnected);
   }
+    CHECK_EQ(fd, socket_.get());
+
+    bool validation_error = false;
+    bool read_error = false;
+    size_t next_read_size = 0;
+    size_t buffer_capacity = 0;
+    size_t total_bytes_read = 0;
+    size_t bytes_read = 0;
+    do {
+      buffer_capacity = next_read_size;
+      char* buffer = GetReadBuffer(&buffer_capacity);
+      DCHECK_GT(buffer_capacity, 0u);
+
+      std::vector<base::ScopedFD> incoming_fds;
+      ssize_t read_result =
+          SocketRecvmsg(socket_.get(), buffer, buffer_capacity, &incoming_fds);
+      for (auto& incoming_fd : incoming_fds)
+        incoming_fds_.emplace_back(std::move(incoming_fd));
+
+      if (read_result > 0) {
+        bytes_read = static_cast<size_t>(read_result);
+        total_bytes_read += bytes_read;
+        if (!OnReadComplete(bytes_read, &next_read_size)) {
+          read_error = true;
+          validation_error = true;
+          break;
+        }
+      } else if (read_result == 0 ||
+                 (errno != EAGAIN && errno != EWOULDBLOCK)) {
+        read_error = true;
+        break;
+      } else {
+        // We expect more data but there is none to read. The
+        // FileDescriptorWatcher will wake us up again once there is.
+        DCHECK(errno == EAGAIN || errno == EWOULDBLOCK);
+        return;
+      }
+    } while (bytes_read == buffer_capacity &&
+             total_bytes_read < kMaxBatchReadCapacity && next_read_size > 0);
+    if (read_error) {
+      // Stop receiving read notifications.
+      read_watcher_.reset();
+      if (validation_error)
+        OnError(Error::kReceivedMalformedData);
+      else
+        OnError(Error::kDisconnected);
+    }
 }
 
 void ChannelPosix::OnFileCanWriteWithoutBlocking(int fd) {
@@ -381,29 +377,29 @@
       result = SendmsgWithHandles(socket_.get(), &iov, 1, fds);
       if (result >= 0) {
 #if defined(OS_IOS)
-        // There is a bug in XNU which makes it dangerous to close
-        // a file descriptor while it is in transit. So instead we
-        // store the file descriptor in a set and send a message to
-        // the recipient, which is queued AFTER the message that
-        // sent the FD. The recipient will reply to the message,
-        // letting us know that it is now safe to close the file
-        // descriptor. For more information, see:
-        // http://crbug.com/298276
-        MessagePtr fds_message(new Channel::Message(
-            sizeof(int) * fds.size(), 0, Message::MessageType::HANDLES_SENT));
-        int* fd_data = reinterpret_cast<int*>(fds_message->mutable_payload());
-        for (size_t i = 0; i < fds.size(); ++i)
-          fd_data[i] = fds[i].get();
-        outgoing_messages_.emplace_back(std::move(fds_message), 0);
-        {
-          base::AutoLock l(fds_to_close_lock_);
-          for (auto& fd : fds)
-            fds_to_close_.emplace_back(std::move(fd));
-        }
+          // There is a bug in XNU which makes it dangerous to close
+          // a file descriptor while it is in transit. So instead we
+          // store the file descriptor in a set and send a message to
+          // the recipient, which is queued AFTER the message that
+          // sent the FD. The recipient will reply to the message,
+          // letting us know that it is now safe to close the file
+          // descriptor. For more information, see:
+          // http://crbug.com/298276
+          MessagePtr fds_message(new Channel::Message(
+              sizeof(int) * fds.size(), 0, Message::MessageType::HANDLES_SENT));
+          int* fd_data = reinterpret_cast<int*>(fds_message->mutable_payload());
+          for (size_t i = 0; i < fds.size(); ++i)
+            fd_data[i] = fds[i].get();
+          outgoing_messages_.emplace_back(std::move(fds_message), 0);
+          {
+            base::AutoLock l(fds_to_close_lock_);
+            for (auto& fd : fds)
+              fds_to_close_.emplace_back(std::move(fd));
+          }
 #endif  // defined(OS_IOS)
-        handles_written += num_handles_to_send;
-        DCHECK_LE(handles_written, num_handles);
-        message_view.set_num_handles_sent(handles_written);
+          handles_written += num_handles_to_send;
+          DCHECK_LE(handles_written, num_handles);
+          message_view.set_num_handles_sent(handles_written);
       } else {
         // Message transmission failed, so pull the FDs back into |handles|
         // so they can be held by the Message again.
@@ -417,86 +413,76 @@
                            message_view.data_num_bytes());
     }
 
-    if (result < 0) {
-      if (errno != EAGAIN &&
-          errno != EWOULDBLOCK
+      if (result < 0) {
+        if (errno != EAGAIN &&
+            errno != EWOULDBLOCK
 #if defined(OS_IOS)
-          // On iOS if sendmsg() is trying to send fds between processes and
-          // there isn't enough room in the output buffer to send the fd
-          // structure over atomically then EMSGSIZE is returned.
-          //
-          // EMSGSIZE presents a problem since the system APIs can only call
-          // us when there's room in the socket buffer and not when there is
-          // "enough" room.
-          //
-          // The current behavior is to return to the event loop when EMSGSIZE
-          // is received and hopefully service another FD.  This is however
-          // still technically a busy wait since the event loop will call us
-          // right back until the receiver has read enough data to allow
-          // passing the FD over atomically.
-          && errno != EMSGSIZE
+            // On iOS if sendmsg() is trying to send fds between processes and
+            // there isn't enough room in the output buffer to send the fd
+            // structure over atomically then EMSGSIZE is returned.
+            //
+            // EMSGSIZE presents a problem since the system APIs can only call
+            // us when there's room in the socket buffer and not when there is
+            // "enough" room.
+            //
+            // The current behavior is to return to the event loop when EMSGSIZE
+            // is received and hopefull service another FD.  This is however
+            // still technically a busy wait since the event loop will call us
+            // right back until the receiver has read enough data to allow
+            // passing the FD over atomically.
+            && errno != EMSGSIZE
 #endif
-      ) {
-        return false;
+        ) {
+          return false;
+        }
+        message_view.SetHandles(std::move(handles));
+        outgoing_messages_.emplace_front(std::move(message_view));
+        WaitForWriteOnIOThreadNoLock();
+        return true;
       }
-      message_view.SetHandles(std::move(handles));
-      outgoing_messages_.emplace_front(std::move(message_view));
-      WaitForWriteOnIOThreadNoLock();
-      return true;
-    }
 
-    bytes_written = static_cast<size_t>(result);
+      bytes_written = static_cast<size_t>(result);
   } while (handles_written < num_handles ||
            bytes_written < message_view.data_num_bytes());
 
-  return FlushOutgoingMessagesNoLock();
+    return FlushOutgoingMessagesNoLock();
 }
 
 bool ChannelPosix::FlushOutgoingMessagesNoLock() {
 #if !defined(OS_NACL)
-  if (g_use_writev)
-    return FlushOutgoingMessagesWritevNoLock();
+    if (g_use_writev)
+      return FlushOutgoingMessagesWritevNoLock();
 #endif
 
-  base::circular_deque<MessageView> messages;
-  std::swap(outgoing_messages_, messages);
+    base::circular_deque<MessageView> messages;
+    std::swap(outgoing_messages_, messages);
 
-  if (!messages.empty()) {
-    UMA_HISTOGRAM_COUNTS_1000("Mojo.Channel.WriteQueuePendingMessages",
-                              messages.size());
-  }
-
-  while (!messages.empty()) {
-    if (!WriteNoLock(std::move(messages.front())))
-      return false;
-
-    messages.pop_front();
-    if (!outgoing_messages_.empty()) {
-      // The message was requeued by WriteNoLock(), so we have to wait for
-      // pipe to become writable again. Repopulate the message queue and exit.
-      // If sending the message triggered any control messages, they may be
-      // in |outgoing_messages_| in addition to or instead of the message
-      // being sent.
-      std::swap(messages, outgoing_messages_);
-      while (!messages.empty()) {
-        outgoing_messages_.push_front(std::move(messages.back()));
-        messages.pop_back();
-      }
-      return true;
+    if (!messages.empty()) {
+      UMA_HISTOGRAM_COUNTS_1000("Mojo.Channel.WriteQueuePendingMessages",
+                                messages.size());
     }
-  }
 
-  return true;
-}
+    while (!messages.empty()) {
+      if (!WriteNoLock(std::move(messages.front())))
+        return false;
 
-void ChannelPosix::RejectUpgradeOffer() {
-  Write(std::make_unique<Channel::Message>(
-      0, 0, Message::MessageType::UPGRADE_REJECT));
-}
+      messages.pop_front();
+      if (!outgoing_messages_.empty()) {
+        // The message was requeued by WriteNoLock(), so we have to wait for
+        // pipe to become writable again. Repopulate the message queue and exit.
+        // If sending the message triggered any control messages, they may be
+        // in |outgoing_messages_| in addition to or instead of the message
+        // being sent.
+        std::swap(messages, outgoing_messages_);
+        while (!messages.empty()) {
+          outgoing_messages_.push_front(std::move(messages.back()));
+          messages.pop_back();
+        }
+        return true;
+      }
+    }
 
-void ChannelPosix::AcceptUpgradeOffer() {
-  Write(std::make_unique<Channel::Message>(
-      0, 0, Message::MessageType::UPGRADE_ACCEPT));
+    return true;
 }
 
 void ChannelPosix::OnWriteError(Error error) {
@@ -621,19 +607,12 @@
 }
 #endif  // !defined(OS_NACL)
 
+#if defined(OS_IOS)
 bool ChannelPosix::OnControlMessage(Message::MessageType message_type,
                                     const void* payload,
                                     size_t payload_size,
                                     std::vector<PlatformHandle> handles) {
   switch (message_type) {
-    case Message::MessageType::UPGRADE_OFFER: {
-      // ChannelPosix itself does not support upgrades, if the message was
-      // delivered here it could have been when this channel was created we
-      // didn't support upgrades but another process does.
-      RejectUpgradeOffer();
-      return true;
-    }
-#if defined(OS_IOS)
     case Message::MessageType::HANDLES_SENT: {
       if (payload_size == 0)
         break;
@@ -654,7 +633,7 @@
         break;
       return true;
     }
-#endif
+
     default:
       break;
   }
@@ -662,7 +641,6 @@
   return false;
 }
 
-#if defined(OS_IOS)
 // Closes handles referenced by |fds|. Returns false if |num_fds| is 0, or if
 // |fds| does not match a sequence of handles in |fds_to_close_|.
 bool ChannelPosix::CloseHandles(const int* fds, size_t num_fds) {
@@ -697,12 +675,12 @@
 }
 #endif  // defined(OS_IOS)
 
-#if !defined(OS_NACL)
 // static
+#if !defined(OS_NACL)
 void Channel::set_posix_use_writev(bool use_writev) {
   g_use_writev = use_writev;
 }
-#endif  // !defined(OS_NACL)
+#endif
 
 // static
 scoped_refptr<Channel> Channel::Create(
@@ -710,33 +688,9 @@
     ConnectionParams connection_params,
     HandlePolicy handle_policy,
     scoped_refptr<base::SingleThreadTaskRunner> io_task_runner) {
-#if !defined(OS_NACL)
-#if (defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_ANDROID))
-  return new ChannelLinux(delegate, std::move(connection_params), handle_policy,
-                          io_task_runner);
-#endif
-#endif
-
   return new ChannelPosix(delegate, std::move(connection_params), handle_policy,
                           io_task_runner);
 }
 
-#if !defined(OS_NACL)
-#if (defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_ANDROID))
-// static
-bool Channel::SupportsChannelUpgrade() {
-  return ChannelLinux::KernelSupportsUpgradeRequirements() &&
-         ChannelLinux::UpgradesEnabled();
-}
-
-void Channel::OfferChannelUpgrade() {
-  if (!SupportsChannelUpgrade()) {
-    return;
-  }
-  static_cast<ChannelLinux*>(this)->OfferSharedMemUpgrade();
-}
-#endif  // defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_ANDROID)
-#endif  // !defined(OS_NACL)
-
 }  // namespace core
 }  // namespace mojo
diff --git a/mojo/core/channel_posix.h b/mojo/core/channel_posix.h
index e64e64c..33a33aa 100644
--- a/mojo/core/channel_posix.h
+++ b/mojo/core/channel_posix.h
@@ -43,28 +43,14 @@
                               size_t extra_header_size,
                               std::vector<PlatformHandle>* handles,
                               bool* deferred) override;
-  bool OnControlMessage(Message::MessageType message_type,
-                        const void* payload,
-                        size_t payload_size,
-                        std::vector<PlatformHandle> handles) override;
-
- protected:
-  ~ChannelPosix() override;
-  virtual void StartOnIOThread();
-  virtual void ShutDownOnIOThread();
-  virtual void OnWriteError(Error error);
-
-  void RejectUpgradeOffer();
-  void AcceptUpgradeOffer();
-
-  // Keeps the Channel alive at least until explicit shutdown on the IO thread.
-  scoped_refptr<Channel> self_;
-
-  scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
 
  private:
+  ~ChannelPosix() override;
+
+  void StartOnIOThread();
   void WaitForWriteOnIOThread();
   void WaitForWriteOnIOThreadNoLock();
+  void ShutDownOnIOThread();
 
   // base::CurrentThread::DestructionObserver:
   void WillDestroyCurrentMessageLoop() override;
@@ -92,9 +78,18 @@
 #endif  // !defined(OS_NACL)
 
 #if defined(OS_IOS)
+  bool OnControlMessage(Message::MessageType message_type,
+                        const void* payload,
+                        size_t payload_size,
+                        std::vector<PlatformHandle> handles) override;
   bool CloseHandles(const int* fds, size_t num_fds);
 #endif  // defined(OS_IOS)
 
+  void OnWriteError(Error error);
+
+  // Keeps the Channel alive at least until explicit shutdown on the IO thread.
+  scoped_refptr<Channel> self_;
+
   // We may be initialized with a server socket, in which case this will be
   // valid until it accepts an incoming connection.
   PlatformChannelServerEndpoint server_;
@@ -103,6 +98,8 @@
   // or accepted over |server_|.
   base::ScopedFD socket_;
 
+  scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
+
   // These watchers must only be accessed on the IO thread.
   std::unique_ptr<base::MessagePumpForIO::FdWatchController> read_watcher_;
   std::unique_ptr<base::MessagePumpForIO::FdWatchController> write_watcher_;
diff --git a/mojo/core/embedder/BUILD.gn b/mojo/core/embedder/BUILD.gn
index 59495848..a2b8772 100644
--- a/mojo/core/embedder/BUILD.gn
+++ b/mojo/core/embedder/BUILD.gn
@@ -21,19 +21,7 @@
   public_deps = [ "//base" ]
 
   deps = [
-    ":features",
     "//mojo/core:embedder_internal",
     "//mojo/public/c/system",
   ]
 }
-
-component("features") {
-  output_name = "mojo_core_embedder_features"
-
-  defines = [ "IS_MOJO_CORE_EMBEDDER_FEATURES_IMPL" ]
-
-  public = [ "features.h" ]
-  sources = [ "features.cc" ]
-
-  public_deps = [ "//base" ]
-}
diff --git a/mojo/core/embedder/embedder.cc b/mojo/core/embedder/embedder.cc
index 30189c494..29b6d05 100644
--- a/mojo/core/embedder/embedder.cc
+++ b/mojo/core/embedder/embedder.cc
@@ -15,40 +15,26 @@
 #include "mojo/core/channel.h"
 #include "mojo/core/configuration.h"
 #include "mojo/core/core.h"
-#include "mojo/core/embedder/features.h"
 #include "mojo/core/entrypoints.h"
 #include "mojo/core/node_controller.h"
 #include "mojo/public/c/system/thunks.h"
 
-#if !defined(OS_NACL)
-#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_ANDROID)
-#include "mojo/core/channel_linux.h"
-#endif  // defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_ANDROID)
-#endif  // !defined(OS_NACL)
-
 namespace mojo {
 namespace core {
 
+namespace {
+#if defined(OS_POSIX) && !defined(OS_NACL) && !defined(OS_MAC)
+const base::Feature kMojoPosixUseWritev{"MojoPosixUseWritev",
+                                        base::FEATURE_DISABLED_BY_DEFAULT};
+#endif
+}  // namespace
+
 // InitFeatures will be called as soon as the base::FeatureList is initialized.
 void InitFeatures() {
 #if defined(OS_POSIX) && !defined(OS_NACL) && !defined(OS_MAC)
   Channel::set_posix_use_writev(
       base::FeatureList::IsEnabled(kMojoPosixUseWritev));
-
-#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_ANDROID)
-  bool shared_mem_enabled =
-      base::FeatureList::IsEnabled(kMojoLinuxChannelSharedMem);
-  int num_pages = kMojoLinuxChannelSharedMemPages.Get();
-  if (num_pages < 0) {
-    num_pages = 4;
-  } else if (num_pages > 128) {
-    num_pages = 128;
-  }
-
-  ChannelLinux::SetSharedMemParameters(shared_mem_enabled,
-                                       static_cast<unsigned int>(num_pages));
-#endif  // defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_ANDROID)
-#endif  // defined(OS_POSIX) && !defined(OS_NACL) && !defined(OS_MAC)
+#endif
 }
 
 void Init(const Configuration& configuration) {
diff --git a/mojo/core/embedder/features.cc b/mojo/core/embedder/features.cc
deleted file mode 100644
index ed3f4f2..0000000
--- a/mojo/core/embedder/features.cc
+++ /dev/null
@@ -1,26 +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 "mojo/core/embedder/features.h"
-
-namespace mojo {
-namespace core {
-
-#if defined(OS_POSIX) && !defined(OS_NACL) && !defined(OS_MAC)
-#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_ANDROID)
-COMPONENT_EXPORT(MOJO_CORE_EMBEDDER_FEATURES)
-const base::Feature kMojoLinuxChannelSharedMem{
-    "MojoLinuxChannelSharedMem", base::FEATURE_DISABLED_BY_DEFAULT};
-COMPONENT_EXPORT(MOJO_CORE_EMBEDDER_FEATURES)
-const base::FeatureParam<int> kMojoLinuxChannelSharedMemPages{
-    &kMojoLinuxChannelSharedMem, "MojoLinuxChannelSharedMemPages", 4};
-#endif  // defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_ANDROID)
-
-COMPONENT_EXPORT(MOJO_CORE_EMBEDDER_FEATURES)
-const base::Feature kMojoPosixUseWritev{"MojoPosixUseWritev",
-                                        base::FEATURE_DISABLED_BY_DEFAULT};
-#endif  // defined(OS_POSIX) && !defined(OS_NACL) && !defined(OS_MAC)
-
-}  // namespace core
-}  // namespace mojo
diff --git a/mojo/core/embedder/features.h b/mojo/core/embedder/features.h
deleted file mode 100644
index 5125335..0000000
--- a/mojo/core/embedder/features.h
+++ /dev/null
@@ -1,27 +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 MOJO_CORE_EMBEDDER_FEATURES_H_
-#define MOJO_CORE_EMBEDDER_FEATURES_H_
-
-#include "base/component_export.h"
-#include "base/feature_list.h"
-#include "build/build_config.h"
-
-namespace mojo {
-namespace core {
-
-#if defined(OS_POSIX) && !defined(OS_NACL) && !defined(OS_MAC)
-#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_ANDROID)
-extern const base::Feature kMojoLinuxChannelSharedMem;
-extern const base::FeatureParam<int> kMojoLinuxChannelSharedMemPages;
-#endif  // defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_ANDROID)
-
-extern const base::Feature kMojoPosixUseWritev;
-#endif  // defined(OS_POSIX) && !defined(OS_NACL) && !defined(OS_MAC)
-
-}  // namespace core
-}  // namespace mojo
-
-#endif  // MOJO_CORE_EMBEDDER_FEATURES_H_
diff --git a/mojo/core/node_channel.cc b/mojo/core/node_channel.cc
index 33fb00ec..c48fb57 100644
--- a/mojo/core/node_channel.cc
+++ b/mojo/core/node_channel.cc
@@ -59,22 +59,14 @@
   ports::NodeName token;
 };
 
-struct alignas(8) AcceptInviteeDataV1 : AcceptInviteeDataV0 {
-  uint64_t capabilities = kNodeCapabilityNone;
-};
-
-using AcceptInviteeData = AcceptInviteeDataV1;
+using AcceptInviteeData = AcceptInviteeDataV0;
 
 struct alignas(8) AcceptInvitationDataV0 {
   ports::NodeName token;
   ports::NodeName invitee_name;
 };
 
-struct alignas(8) AcceptInvitationDataV1 : AcceptInvitationDataV0 {
-  uint64_t capabilities = kNodeCapabilityNone;
-};
-
-using AcceptInvitationData = AcceptInvitationDataV1;
+using AcceptInvitationData = AcceptInvitationDataV0;
 
 struct alignas(8) AcceptPeerDataV0 {
   ports::NodeName token;
@@ -92,11 +84,7 @@
 #endif
 };
 
-struct alignas(8) AddBrokerClientDataV1 : AddBrokerClientDataV0 {
-  uint64_t capabilities = kNodeCapabilityNone;
-};
-
-using AddBrokerClientData = AddBrokerClientDataV1;
+using AddBrokerClientData = AddBrokerClientDataV0;
 
 #if !defined(OS_WIN)
 static_assert(sizeof(base::ProcessHandle) == sizeof(uint32_t),
@@ -118,12 +106,7 @@
   ports::NodeName broker_name;
 };
 
-struct alignas(8) AcceptBrokerClientDataV1 : AcceptBrokerClientDataV0 {
-  uint64_t capabilities = kNodeCapabilityNone;
-  uint64_t broker_capabilities = kNodeCapabilityNone;
-};
-
-using AcceptBrokerClientData = AcceptBrokerClientDataV1;
+using AcceptBrokerClientData = AcceptBrokerClientDataV0;
 
 // This is followed by arbitrary payload data which is interpreted as a token
 // string for port location.
@@ -142,11 +125,7 @@
   ports::NodeName name;
 };
 
-struct alignas(8) IntroductionDataV1 : IntroductionDataV0 {
-  uint64_t capabilities = kNodeCapabilityNone;
-};
-
-using IntroductionData = IntroductionDataV1;
+using IntroductionData = IntroductionDataV0;
 
 // This message is just a PlatformHandle. The data struct alignas(8) here has
 // only a padding field to ensure an aligned, non-zero-length payload.
@@ -343,7 +322,6 @@
       MessageType::ACCEPT_INVITEE, sizeof(AcceptInviteeData), 0, &data);
   data->inviter_name = inviter_name;
   data->token = token;
-  data->capabilities = local_capabilities_;
   WriteChannelMessage(std::move(message));
 }
 
@@ -354,7 +332,6 @@
       MessageType::ACCEPT_INVITATION, sizeof(AcceptInvitationData), 0, &data);
   data->token = token;
   data->invitee_name = invitee_name;
-  data->capabilities = local_capabilities_;
   WriteChannelMessage(std::move(message));
 }
 
@@ -385,7 +362,6 @@
 #if !defined(OS_WIN)
   data->process_handle = process_handle.Handle();
 #endif
-  data->capabilities = local_capabilities_;
   WriteChannelMessage(std::move(message));
 }
 
@@ -404,8 +380,7 @@
 }
 
 void NodeChannel::AcceptBrokerClient(const ports::NodeName& broker_name,
-                                     PlatformHandle broker_channel,
-                                     const uint64_t broker_capabilities) {
+                                     PlatformHandle broker_channel) {
   AcceptBrokerClientData* data;
   std::vector<PlatformHandle> handles;
   if (broker_channel.is_valid())
@@ -415,8 +390,6 @@
                     sizeof(AcceptBrokerClientData), handles.size(), &data);
   message->SetHandles(std::move(handles));
   data->broker_name = broker_name;
-  data->broker_capabilities = broker_capabilities;
-  data->capabilities = local_capabilities_;
   WriteChannelMessage(std::move(message));
 }
 
@@ -440,8 +413,7 @@
 }
 
 void NodeChannel::Introduce(const ports::NodeName& name,
-                            PlatformHandle channel_handle,
-                            uint64_t capabilities) {
+                            PlatformHandle channel_handle) {
   IntroductionData* data;
   std::vector<PlatformHandle> handles;
   if (channel_handle.is_valid())
@@ -450,9 +422,6 @@
       MessageType::INTRODUCE, sizeof(IntroductionData), handles.size(), &data);
   message->SetHandles(std::move(handles));
   data->name = name;
-  // Note that these are not our capabilities, but the capabilities of the peer
-  // we're introducing.
-  data->capabilities = capabilities;
   WriteChannelMessage(std::move(message));
 }
 
@@ -547,7 +516,6 @@
                                std::move(io_task_runner)))
 #endif
 {
-  InitializeLocalCapabilities();
 }
 
 NodeChannel::~NodeChannel() {
@@ -581,10 +549,7 @@
   switch (header->type) {
     case MessageType::ACCEPT_INVITEE: {
       AcceptInviteeData data;
-      if (GetMessagePayloadMinimumSized<AcceptInviteeData, AcceptInviteeDataV0>(
-              payload, payload_size, &data)) {
-        // Attach any capabilities that the other side advertised.
-        SetRemoteCapabilities(data.capabilities);
+      if (GetMessagePayload(payload, payload_size, &data)) {
         delegate_->OnAcceptInvitee(remote_node_name_, data.inviter_name,
                                    data.token);
         return;
@@ -594,11 +559,7 @@
 
     case MessageType::ACCEPT_INVITATION: {
       AcceptInvitationData data;
-      if (GetMessagePayloadMinimumSized<AcceptInvitationData,
-                                        AcceptInvitationDataV0>(
-              payload, payload_size, &data)) {
-        // Attach any capabilities that the other side advertised.
-        SetRemoteCapabilities(data.capabilities);
+      if (GetMessagePayload(payload, payload_size, &data)) {
         delegate_->OnAcceptInvitation(remote_node_name_, data.token,
                                       data.invitee_name);
         return;
@@ -645,9 +606,7 @@
 
     case MessageType::ACCEPT_BROKER_CLIENT: {
       AcceptBrokerClientData data;
-      if (GetMessagePayloadMinimumSized<AcceptBrokerClientData,
-                                        AcceptBrokerClientDataV0>(
-              payload, payload_size, &data)) {
+      if (GetMessagePayload(payload, payload_size, &data)) {
         PlatformHandle broker_channel;
         if (handles.size() > 1) {
           DLOG(ERROR) << "Dropping invalid AcceptBrokerClient message.";
@@ -656,11 +615,8 @@
         if (handles.size() == 1)
           broker_channel = std::move(handles[0]);
 
-        // Attach any capabilities that the other side advertised.
-        SetRemoteCapabilities(data.capabilities);
         delegate_->OnAcceptBrokerClient(remote_node_name_, data.broker_name,
-                                        std::move(broker_channel),
-                                        data.broker_capabilities);
+                                        std::move(broker_channel));
         return;
       }
       break;
@@ -703,8 +659,7 @@
 
     case MessageType::INTRODUCE: {
       IntroductionData data;
-      if (GetMessagePayloadMinimumSized<IntroductionData, IntroductionDataV0>(
-              payload, payload_size, &data)) {
+      if (GetMessagePayload(payload, payload_size, &data)) {
         if (handles.size() > 1) {
           DLOG(ERROR) << "Dropping invalid introduction message.";
           break;
@@ -713,11 +668,8 @@
         if (handles.size() == 1)
           channel_handle = std::move(handles[0]);
 
-        // The node channel for this introduction will be created later, so we
-        // can only pass up the capabilities we received from the broker for
-        // that remote.
         delegate_->OnIntroduce(remote_node_name_, data.name,
-                               std::move(channel_handle), data.capabilities);
+                               std::move(channel_handle));
         return;
       }
       break;
@@ -851,42 +803,5 @@
     channel_->Write(std::move(message));
 }
 
-void NodeChannel::OfferChannelUpgrade() {
-#if !defined(OS_NACL)
-  base::AutoLock lock(channel_lock_);
-  channel_->OfferChannelUpgrade();
-#endif
-}
-
-uint64_t NodeChannel::RemoteCapabilities() const {
-  return remote_capabilities_;
-}
-
-bool NodeChannel::HasRemoteCapability(const uint64_t capability) const {
-  return (remote_capabilities_ & capability) == capability;
-}
-
-void NodeChannel::SetRemoteCapabilities(const uint64_t capabilities) {
-  remote_capabilities_ |= capabilities;
-}
-
-uint64_t NodeChannel::LocalCapabilities() const {
-  return local_capabilities_;
-}
-
-bool NodeChannel::HasLocalCapability(const uint64_t capability) const {
-  return (local_capabilities_ & capability) == capability;
-}
-
-void NodeChannel::SetLocalCapabilities(const uint64_t capabilities) {
-  local_capabilities_ |= capabilities;
-}
-
-void NodeChannel::InitializeLocalCapabilities() {
-  if (core::Channel::SupportsChannelUpgrade()) {
-    SetLocalCapabilities(kNodeCapabilitySupportsUpgrade);
-  }
-}
-
 }  // namespace core
 }  // namespace mojo
diff --git a/mojo/core/node_channel.h b/mojo/core/node_channel.h
index b037054..74306a5 100644
--- a/mojo/core/node_channel.h
+++ b/mojo/core/node_channel.h
@@ -26,9 +26,6 @@
 namespace mojo {
 namespace core {
 
-constexpr uint64_t kNodeCapabilityNone = 0;
-constexpr uint64_t kNodeCapabilitySupportsUpgrade = 1;
-
 // Wraps a Channel to send and receive Node control messages.
 class MOJO_SYSTEM_IMPL_EXPORT NodeChannel
     : public base::RefCountedDeleteOnSequence<NodeChannel>,
@@ -51,8 +48,7 @@
                                      PlatformHandle broker_channel) = 0;
     virtual void OnAcceptBrokerClient(const ports::NodeName& from_node,
                                       const ports::NodeName& broker_name,
-                                      PlatformHandle broker_channel,
-                                      const uint64_t broker_capabilities) = 0;
+                                      PlatformHandle broker_channel) = 0;
     virtual void OnEventMessage(const ports::NodeName& from_node,
                                 Channel::MessagePtr message) = 0;
     virtual void OnRequestPortMerge(const ports::NodeName& from_node,
@@ -62,8 +58,7 @@
                                        const ports::NodeName& name) = 0;
     virtual void OnIntroduce(const ports::NodeName& from_node,
                              const ports::NodeName& name,
-                             PlatformHandle channel_handle,
-                             const uint64_t remote_capabilities) = 0;
+                             PlatformHandle channel_handle) = 0;
     virtual void OnBroadcast(const ports::NodeName& from_node,
                              Channel::MessagePtr message) = 0;
 #if defined(OS_WIN)
@@ -135,26 +130,15 @@
   void BrokerClientAdded(const ports::NodeName& client_name,
                          PlatformHandle broker_channel);
   void AcceptBrokerClient(const ports::NodeName& broker_name,
-                          PlatformHandle broker_channel,
-                          const uint64_t broker_capabilities);
+                          PlatformHandle broker_channel);
   void RequestPortMerge(const ports::PortName& connector_port_name,
                         const std::string& token);
   void RequestIntroduction(const ports::NodeName& name);
-  void Introduce(const ports::NodeName& name,
-                 PlatformHandle channel_handle,
-                 uint64_t capabilities);
+  void Introduce(const ports::NodeName& name, PlatformHandle channel_handle);
   void SendChannelMessage(Channel::MessagePtr message);
   void Broadcast(Channel::MessagePtr message);
   void BindBrokerHost(PlatformHandle broker_host_handle);
 
-  uint64_t RemoteCapabilities() const;
-  bool HasRemoteCapability(const uint64_t capability) const;
-  void SetRemoteCapabilities(const uint64_t capability);
-
-  uint64_t LocalCapabilities() const;
-  bool HasLocalCapability(const uint64_t capability) const;
-  void SetLocalCapabilities(const uint64_t capability);
-
 #if defined(OS_WIN)
   // Relay the message to the specified node via this channel.  This is used to
   // pass windows handles between two processes that do not have permission to
@@ -170,8 +154,6 @@
                              Channel::MessagePtr message);
 #endif
 
-  void OfferChannelUpgrade();
-
  private:
   friend class base::RefCountedDeleteOnSequence<NodeChannel>;
   friend class base::DeleteHelper<NodeChannel>;
@@ -199,10 +181,6 @@
 
   void WriteChannelMessage(Channel::MessagePtr message);
 
-  // This method is responsible for setting up the default set of capabilities
-  // for this channel.
-  void InitializeLocalCapabilities();
-
   Delegate* const delegate_;
   const ProcessErrorCallback process_error_callback_;
 
@@ -212,9 +190,6 @@
   // Must only be accessed from the owning task runner's thread.
   ports::NodeName remote_node_name_;
 
-  uint64_t remote_capabilities_ = kNodeCapabilityNone;
-  uint64_t local_capabilities_ = kNodeCapabilityNone;
-
   base::Lock remote_process_handle_lock_;
   base::Process remote_process_handle_;
 
diff --git a/mojo/core/node_controller.cc b/mojo/core/node_controller.cc
index 014dfd9a..86b397df 100644
--- a/mojo/core/node_controller.cc
+++ b/mojo/core/node_controller.cc
@@ -867,8 +867,7 @@
 
     if (!inviter) {
       // Yes, we're the broker. We can initialize the client directly.
-      channel->AcceptBrokerClient(name_, PlatformHandle(),
-                                  channel->LocalCapabilities());
+      channel->AcceptBrokerClient(name_, PlatformHandle());
     } else {
       // We aren't the broker, so wait for a broker connection.
       base::AutoLock lock(broker_lock_);
@@ -936,14 +935,12 @@
 
   DVLOG(1) << "Client " << client_name << " accepted by broker " << from_node;
 
-  client->AcceptBrokerClient(from_node, std::move(broker_channel),
-                             GetBrokerChannel()->RemoteCapabilities());
+  client->AcceptBrokerClient(from_node, std::move(broker_channel));
 }
 
 void NodeController::OnAcceptBrokerClient(const ports::NodeName& from_node,
                                           const ports::NodeName& broker_name,
-                                          PlatformHandle broker_channel,
-                                          const uint64_t broker_capabilities) {
+                                          PlatformHandle broker_channel) {
   DCHECK(!GetConfiguration().is_broker_process);
 
   // This node should already have an inviter in bootstrap mode.
@@ -982,7 +979,6 @@
         ConnectionParams(PlatformChannelEndpoint(std::move(broker_channel))),
         Channel::HandlePolicy::kAcceptHandles, io_task_runner_,
         ProcessErrorCallback());
-    broker->SetRemoteCapabilities(broker_capabilities);
     AddPeer(broker_name, broker, true /* start_channel */);
   }
 
@@ -1018,10 +1014,6 @@
     }
   }
 #endif
-  if (inviter->HasLocalCapability(kNodeCapabilitySupportsUpgrade) &&
-      inviter->HasRemoteCapability(kNodeCapabilitySupportsUpgrade)) {
-    inviter->OfferChannelUpgrade();
-  }
 
   DVLOG(1) << "Client " << name_ << " accepted by broker " << broker_name;
 }
@@ -1100,22 +1092,19 @@
   scoped_refptr<NodeChannel> new_friend = GetPeerChannel(name);
   if (!new_friend) {
     // We don't know who they're talking about!
-    requestor->Introduce(name, PlatformHandle(), kNodeCapabilityNone);
+    requestor->Introduce(name, PlatformHandle());
   } else {
     PlatformChannel new_channel;
     requestor->Introduce(name,
-                         new_channel.TakeLocalEndpoint().TakePlatformHandle(),
-                         new_friend->RemoteCapabilities());
-    new_friend->Introduce(from_node,
-                          new_channel.TakeRemoteEndpoint().TakePlatformHandle(),
-                          requestor->RemoteCapabilities());
+                         new_channel.TakeLocalEndpoint().TakePlatformHandle());
+    new_friend->Introduce(
+        from_node, new_channel.TakeRemoteEndpoint().TakePlatformHandle());
   }
 }
 
 void NodeController::OnIntroduce(const ports::NodeName& from_node,
                                  const ports::NodeName& name,
-                                 PlatformHandle channel_handle,
-                                 const uint64_t remote_capabilities) {
+                                 PlatformHandle channel_handle) {
   DCHECK(io_task_runner_->RunsTasksInCurrentSequence());
 
   if (!channel_handle.is_valid()) {
@@ -1142,13 +1131,6 @@
 
   DVLOG(1) << "Adding new peer " << name << " via broker introduction.";
   AddPeer(name, channel, true /* start_channel */);
-
-  channel->SetRemoteCapabilities(remote_capabilities);
-
-  if (channel->HasLocalCapability(kNodeCapabilitySupportsUpgrade) &&
-      channel->HasRemoteCapability(kNodeCapabilitySupportsUpgrade)) {
-    channel->OfferChannelUpgrade();
-  }
 }
 
 void NodeController::OnBroadcast(const ports::NodeName& from_node,
@@ -1357,13 +1339,11 @@
 
 NodeController::IsolatedConnection::~IsolatedConnection() = default;
 
-NodeController::IsolatedConnection&
-NodeController::IsolatedConnection::operator=(const IsolatedConnection& other) =
-    default;
+NodeController::IsolatedConnection& NodeController::IsolatedConnection::
+operator=(const IsolatedConnection& other) = default;
 
-NodeController::IsolatedConnection&
-NodeController::IsolatedConnection::operator=(IsolatedConnection&& other) =
-    default;
+NodeController::IsolatedConnection& NodeController::IsolatedConnection::
+operator=(IsolatedConnection&& other) = default;
 
 }  // namespace core
 }  // namespace mojo
diff --git a/mojo/core/node_controller.h b/mojo/core/node_controller.h
index affbeda..254efe4 100644
--- a/mojo/core/node_controller.h
+++ b/mojo/core/node_controller.h
@@ -206,8 +206,7 @@
                            PlatformHandle broker_channel) override;
   void OnAcceptBrokerClient(const ports::NodeName& from_node,
                             const ports::NodeName& broker_name,
-                            PlatformHandle broker_channel,
-                            const uint64_t broker_capabilities) override;
+                            PlatformHandle broker_channel) override;
   void OnEventMessage(const ports::NodeName& from_node,
                       Channel::MessagePtr message) override;
   void OnRequestPortMerge(const ports::NodeName& from_node,
@@ -217,8 +216,7 @@
                              const ports::NodeName& name) override;
   void OnIntroduce(const ports::NodeName& from_node,
                    const ports::NodeName& name,
-                   PlatformHandle channel_handle,
-                   const uint64_t remote_capailities) override;
+                   PlatformHandle channel_handle) override;
   void OnBroadcast(const ports::NodeName& from_node,
                    Channel::MessagePtr message) override;
 #if defined(OS_WIN)
diff --git a/mojo/core/test/mock_node_channel_delegate.h b/mojo/core/test/mock_node_channel_delegate.h
index f58552f..06ca968 100644
--- a/mojo/core/test/mock_node_channel_delegate.h
+++ b/mojo/core/test/mock_node_channel_delegate.h
@@ -54,8 +54,7 @@
               OnAcceptBrokerClient,
               (const NodeName& from_node,
                const NodeName& broker_name,
-               PlatformHandle broker_channel,
-               const uint64_t capabilities),
+               PlatformHandle broker_channel),
               (override));
   MOCK_METHOD(void,
               OnEventMessage,
@@ -75,8 +74,7 @@
               OnIntroduce,
               (const NodeName& from_node,
                const NodeName& name,
-               PlatformHandle channel_handle,
-               const uint64_t remote_capabilites),
+               PlatformHandle channel_handle),
               (override));
   MOCK_METHOD(void,
               OnBroadcast,
diff --git a/net/android/java/src/org/chromium/net/AndroidKeyStore.java b/net/android/java/src/org/chromium/net/AndroidKeyStore.java
index c1b155d0..fde3ba1 100644
--- a/net/android/java/src/org/chromium/net/AndroidKeyStore.java
+++ b/net/android/java/src/org/chromium/net/AndroidKeyStore.java
@@ -77,9 +77,6 @@
      * @param algorithm The signature algorithm to use.
      * @param message The message to sign.
      * @return signature as a byte buffer.
-     *
-     * Note: NONEwithRSA is not implemented in Android < 4.2. See
-     * getOpenSSLHandleForPrivateKey() below for a work-around.
      */
     @CalledByNative
     private static byte[] signWithPrivateKey(
@@ -114,9 +111,6 @@
      * @param algorithm The cipher to use.
      * @param input The input to encrypt.
      * @return ciphertext as a byte buffer.
-     *
-     * Note: NONEwithRSA is not implemented in Android < 4.2. See
-     * getOpenSSLHandleForPrivateKey() below for a work-around.
      */
     @CalledByNative
     private static byte[] encryptWithPrivateKey(
diff --git a/net/base/network_change_notifier.cc b/net/base/network_change_notifier.cc
index 7e7c64c..f98d4a7 100644
--- a/net/base/network_change_notifier.cc
+++ b/net/base/network_change_notifier.cc
@@ -10,7 +10,7 @@
 #include <utility>
 
 #include "base/memory/ref_counted.h"
-#include "base/metrics/histogram_functions.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/no_destructor.h"
 #include "base/optional.h"
 #include "base/sequence_checker.h"
@@ -156,8 +156,8 @@
       return;
     }
 
-    base::UmaHistogramEnumeration("Net.NetworkChangeNotifier.NewConnectionType",
-                                  pending_connection_type_, CONNECTION_LAST);
+    UMA_HISTOGRAM_ENUMERATION("Net.NetworkChangeNotifier.NewConnectionType",
+                              pending_connection_type_, CONNECTION_LAST + 1);
 
     have_announced_ = true;
     last_announced_connection_type_ = pending_connection_type_;
diff --git a/sandbox/linux/seccomp-bpf-helpers/baseline_policy.cc b/sandbox/linux/seccomp-bpf-helpers/baseline_policy.cc
index 5c70e1f..e00e3125 100644
--- a/sandbox/linux/seccomp-bpf-helpers/baseline_policy.cc
+++ b/sandbox/linux/seccomp-bpf-helpers/baseline_policy.cc
@@ -40,11 +40,9 @@
 namespace {
 
 bool IsBaselinePolicyAllowed(int sysno) {
-  // clang-format off
   return SyscallSets::IsAllowedAddressSpaceAccess(sysno) ||
          SyscallSets::IsAllowedBasicScheduler(sysno) ||
          SyscallSets::IsAllowedEpoll(sysno) ||
-         SyscallSets::IsEventFd(sysno) ||
          SyscallSets::IsAllowedFileSystemAccessViaFd(sysno) ||
          SyscallSets::IsAllowedFutex(sysno) ||
          SyscallSets::IsAllowedGeneralIo(sysno) ||
@@ -61,7 +59,6 @@
          SyscallSets::IsMipsPrivate(sysno) ||
 #endif
          SyscallSets::IsAllowedOperationOnFd(sysno);
-  // clang-format on
 }
 
 // System calls that will trigger the crashing SIGSYS handler.
@@ -71,6 +68,7 @@
          SyscallSets::IsAdvancedTimer(sysno) ||
          SyscallSets::IsAsyncIo(sysno) ||
          SyscallSets::IsDebug(sysno) ||
+         SyscallSets::IsEventFd(sysno) ||
          SyscallSets::IsExtendedAttributes(sysno) ||
          SyscallSets::IsFaNotify(sysno) ||
          SyscallSets::IsFsControl(sysno) ||
diff --git a/sandbox/linux/seccomp-bpf-helpers/baseline_policy_android.cc b/sandbox/linux/seccomp-bpf-helpers/baseline_policy_android.cc
index c9d598c..35ab90e 100644
--- a/sandbox/linux/seccomp-bpf-helpers/baseline_policy_android.cc
+++ b/sandbox/linux/seccomp-bpf-helpers/baseline_policy_android.cc
@@ -151,6 +151,11 @@
       break;
   }
 
+  // https://crbug.com/772441 and https://crbug.com/760020.
+  if (SyscallSets::IsEventFd(sysno)) {
+    return Allow();
+  }
+
   // Ptrace is allowed so the crash reporter can fork in a renderer
   // and then ptrace the parent. https://crbug.com/933418
   if (sysno == __NR_ptrace) {
diff --git a/sandbox/linux/seccomp-bpf-helpers/baseline_policy_unittest.cc b/sandbox/linux/seccomp-bpf-helpers/baseline_policy_unittest.cc
index 68c29b5..01c046d 100644
--- a/sandbox/linux/seccomp-bpf-helpers/baseline_policy_unittest.cc
+++ b/sandbox/linux/seccomp-bpf-helpers/baseline_policy_unittest.cc
@@ -303,6 +303,7 @@
 TEST_BASELINE_SIGSYS(__NR_timer_create)
 
 #if !defined(__aarch64__)
+TEST_BASELINE_SIGSYS(__NR_eventfd)
 TEST_BASELINE_SIGSYS(__NR_inotify_init)
 TEST_BASELINE_SIGSYS(__NR_vserver)
 #endif
diff --git a/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc b/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc
index 8fa54f5..2a97d39 100644
--- a/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc
+++ b/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc
@@ -215,7 +215,7 @@
   // TODO(davidung), remove MAP_DENYWRITE with updated Tegra libraries.
   const uint64_t kAllowedMask = MAP_SHARED | MAP_PRIVATE | MAP_ANONYMOUS |
                                 MAP_STACK | MAP_NORESERVE | MAP_FIXED |
-                                MAP_DENYWRITE | MAP_LOCKED;
+                                MAP_DENYWRITE;
   const Arg<int> flags(3);
   return If((flags & ~kAllowedMask) == 0, Allow()).Else(CrashSIGSYS());
 }
@@ -245,12 +245,9 @@
 
   const uint64_t kAllowedMask = O_ACCMODE | O_APPEND | O_NONBLOCK | O_SYNC |
                                 kOLargeFileFlag | O_CLOEXEC | O_NOATIME;
-  const uint64_t kAllowedSeals = F_SEAL_SEAL | F_SEAL_GROW | F_SEAL_SHRINK;
-  // clang-format off
   return Switch(cmd)
       .CASES((F_GETFL,
               F_GETFD,
-              F_GET_SEALS,
               F_SETFD,
               F_SETLK,
               F_SETLKW,
@@ -260,10 +257,7 @@
              Allow())
       .Case(F_SETFL,
             If((long_arg & ~kAllowedMask) == 0, Allow()).Else(CrashSIGSYS()))
-      .Case(F_ADD_SEALS,
-            If((long_arg & ~kAllowedSeals) == 0, Allow()).Else(CrashSIGSYS()))
       .Default(CrashSIGSYS());
-  // clang-format on
 }
 
 #if defined(__i386__) || defined(__mips__)
diff --git a/sandbox/linux/seccomp-bpf-helpers/syscall_sets.cc b/sandbox/linux/seccomp-bpf-helpers/syscall_sets.cc
index f40d436..d9d18822 100644
--- a/sandbox/linux/seccomp-bpf-helpers/syscall_sets.cc
+++ b/sandbox/linux/seccomp-bpf-helpers/syscall_sets.cc
@@ -168,11 +168,9 @@
 bool SyscallSets::IsAllowedFileSystemAccessViaFd(int sysno) {
   switch (sysno) {
     case __NR_fstat:
-    case __NR_ftruncate:
 #if defined(__i386__) || defined(__arm__) || \
     (defined(ARCH_CPU_MIPS_FAMILY) && defined(ARCH_CPU_32_BITS))
     case __NR_fstat64:
-    case __NR_ftruncate64:
 #endif
       return true;
 // TODO(jln): these should be denied gracefully as well (moved below).
@@ -213,9 +211,14 @@
     case __NR_fallocate:
     case __NR_fchmod:
     case __NR_fchown:
+    case __NR_ftruncate:
 #if defined(__i386__) || defined(__arm__)
     case __NR_fchown32:
 #endif
+#if defined(__i386__) || defined(__arm__) || \
+    (defined(ARCH_CPU_MIPS_FAMILY) && defined(ARCH_CPU_32_BITS))
+    case __NR_ftruncate64:
+#endif
 #if !defined(__aarch64__)
     case __NR_getdents:    // EPERM not a valid errno.
 #endif
diff --git a/sandbox/policy/linux/bpf_base_policy_linux.cc b/sandbox/policy/linux/bpf_base_policy_linux.cc
index 04bd9c84..90164ea 100644
--- a/sandbox/policy/linux/bpf_base_policy_linux.cc
+++ b/sandbox/policy/linux/bpf_base_policy_linux.cc
@@ -31,11 +31,6 @@
 ResultExpr BPFBasePolicy::EvaluateSyscall(int system_call_number) const {
   DCHECK(baseline_policy_);
 
-  // Used for shared memory Mojo channels on Linux.
-  if (system_call_number == __NR_memfd_create) {
-    return Allow();
-  }
-
   // set_robust_list(2) is part of the futex(2) infrastructure.
   // Chrome on Linux/Chrome OS will call set_robust_list(2) frequently.
   // The baseline policy will EPERM set_robust_list(2), but on systems with
diff --git a/sandbox/policy/linux/bpf_gpu_policy_linux.cc b/sandbox/policy/linux/bpf_gpu_policy_linux.cc
index dd4c3116..829efee8b 100644
--- a/sandbox/policy/linux/bpf_gpu_policy_linux.cc
+++ b/sandbox/policy/linux/bpf_gpu_policy_linux.cc
@@ -93,6 +93,8 @@
     default:
       break;
   }
+  if (SyscallSets::IsEventFd(sysno))
+    return Allow();
 
 #if (defined(OS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)) && defined(USE_X11)
   if (SyscallSets::IsSystemVSharedMemory(sysno))
diff --git a/sandbox/policy/sandbox.cc b/sandbox/policy/sandbox.cc
index 590ecc9c..95482e8 100644
--- a/sandbox/policy/sandbox.cc
+++ b/sandbox/policy/sandbox.cc
@@ -58,16 +58,19 @@
                          SandboxInterfaceInfo* sandbox_info) {
   BrokerServices* broker_services = sandbox_info->broker_services;
   if (broker_services) {
+    const base::CommandLine& command_line =
+        *base::CommandLine::ForCurrentProcess();
     if (!SandboxWin::InitBrokerServices(broker_services))
       return false;
 
-    // IMPORTANT: This piece of code needs to run as early as possible in the
-    // process because it will initialize the sandbox broker, which requires the
-    // process to swap its window station. During this time all the UI will be
-    // broken. This has to run before threads and windows are created.
-    if (!IsUnsandboxedSandboxType(sandbox_type)) {
-      // Precreate the desktop and window station used by the renderers.
+    // Only pre-create alternate desktop if there will be sandboxed processes in
+    // the future.
+    if (!command_line.HasSwitch(switches::kNoSandbox)) {
       scoped_refptr<TargetPolicy> policy = broker_services->CreatePolicy();
+      // IMPORTANT: This piece of code needs to run as early as possible in the
+      // process because it will initialize the sandbox broker, which requires
+      // the process to swap its window station. During this time all the UI
+      // will be broken. This has to run before threads and windows are created.
       ResultCode result = policy->CreateAlternateDesktop(true);
       CHECK(SBOX_ERROR_FAILED_TO_SWITCH_BACK_WINSTATION != result);
     }
diff --git a/services/network/public/cpp/cors/cors_unittest.cc b/services/network/public/cpp/cors/cors_unittest.cc
index 2113d78..58667f1 100644
--- a/services/network/public/cpp/cors/cors_unittest.cc
+++ b/services/network/public/cpp/cors/cors_unittest.cc
@@ -6,6 +6,7 @@
 
 #include <limits.h>
 
+#include "build/build_config.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
 #include "url/origin.h"
@@ -359,7 +360,12 @@
   EXPECT_FALSE(IsCorsSafelistedHeader("AccepT", std::string(129, 'a')));
 }
 
-TEST_F(CorsTest, SafelistedAcceptLanguage) {
+#if defined(OS_ANDROID)
+#define MAYBE_SafelistedAcceptLanguage DISABLED_SafelistedAcceptLanguage
+#else
+#define MAYBE_SafelistedAcceptLanguage SafelistedAcceptLanguage
+#endif
+TEST_F(CorsTest, MAYBE_SafelistedAcceptLanguage) {
   EXPECT_TRUE(IsCorsSafelistedHeader("accept-language", "en,ja"));
   EXPECT_TRUE(IsCorsSafelistedHeader("aCcEPT-lAngUAge", "en,ja"));
 
@@ -400,7 +406,12 @@
   // https://crbug.com/924969
 }
 
-TEST_F(CorsTest, SafelistedContentLanguage) {
+#if defined(OS_ANDROID)
+#define MAYBE_SafelistedContentLanguage DISABLED_SafelistedContentLanguage
+#else
+#define MAYBE_SafelistedContentLanguage SafelistedContentLanguage
+#endif
+TEST_F(CorsTest, MAYBE_SafelistedContentLanguage) {
   EXPECT_TRUE(IsCorsSafelistedHeader("content-language", "en,ja"));
   EXPECT_TRUE(IsCorsSafelistedHeader("cONTent-LANguaGe", "en,ja"));
 
diff --git a/storage/browser/file_system/external_mount_points.cc b/storage/browser/file_system/external_mount_points.cc
index 0c2e804..985a050e 100644
--- a/storage/browser/file_system/external_mount_points.cc
+++ b/storage/browser/file_system/external_mount_points.cc
@@ -36,7 +36,7 @@
 }
 
 bool IsOverlappingMountPathForbidden(FileSystemType type) {
-  return type != kFileSystemTypeNativeMedia &&
+  return type != kFileSystemTypeLocalMedia &&
          type != kFileSystemTypeDeviceMedia;
 }
 
@@ -115,7 +115,7 @@
 bool ExternalMountPoints::HandlesFileSystemMountType(
     FileSystemType type) const {
   return type == kFileSystemTypeExternal ||
-         type == kFileSystemTypeNativeForPlatformApp;
+         type == kFileSystemTypeLocalForPlatformApp;
 }
 
 bool ExternalMountPoints::RevokeFileSystem(const std::string& mount_name) {
@@ -264,7 +264,7 @@
     return FileSystemURL();
 
   base::FilePath virtual_path = url.path();
-  if (url.type() == kFileSystemTypeNativeForPlatformApp) {
+  if (url.type() == kFileSystemTypeLocalForPlatformApp) {
 #if BUILDFLAG(IS_CHROMEOS_ASH)
     // On Chrome OS, find a mount point and virtual path for the external fs.
     if (!GetVirtualPath(url.path(), &virtual_path))
@@ -272,7 +272,7 @@
 #else
     // On other OS, it is simply a native local path.
     return FileSystemURL(url.origin(), url.mount_type(), url.virtual_path(),
-                         url.mount_filesystem_id(), kFileSystemTypeNativeLocal,
+                         url.mount_filesystem_id(), kFileSystemTypeLocal,
                          url.path(), url.filesystem_id(), url.mount_option());
 #endif
   }
diff --git a/storage/browser/file_system/external_mount_points_unittest.cc b/storage/browser/file_system/external_mount_points_unittest.cc
index 7911a42..e3df989 100644
--- a/storage/browser/file_system/external_mount_points_unittest.cc
+++ b/storage/browser/file_system/external_mount_points_unittest.cc
@@ -111,9 +111,9 @@
   // Test adding mount points.
   for (const auto& test : kTestCases) {
     EXPECT_EQ(test.success,
-              mount_points->RegisterFileSystem(
-                  test.name, kFileSystemTypeNativeLocal,
-                  FileSystemMountOption(), base::FilePath(test.path)))
+              mount_points->RegisterFileSystem(test.name, kFileSystemTypeLocal,
+                                               FileSystemMountOption(),
+                                               base::FilePath(test.path)))
         << "Adding mount point: " << test.name << " with path " << test.path;
   }
 
@@ -135,25 +135,25 @@
   scoped_refptr<ExternalMountPoints> mount_points =
       ExternalMountPoints::CreateRefCounted();
 
-  mount_points->RegisterFileSystem("c", kFileSystemTypeNativeLocal,
+  mount_points->RegisterFileSystem("c", kFileSystemTypeLocal,
                                    FileSystemMountOption(),
                                    base::FilePath(DRIVE FPL("/a/b/c")));
   // Note that "/a/b/c" < "/a/b/c(1)" < "/a/b/c/".
-  mount_points->RegisterFileSystem("c(1)", kFileSystemTypeNativeLocal,
+  mount_points->RegisterFileSystem("c(1)", kFileSystemTypeLocal,
                                    FileSystemMountOption(),
                                    base::FilePath(DRIVE FPL("/a/b/c(1)")));
-  mount_points->RegisterFileSystem("x", kFileSystemTypeNativeLocal,
+  mount_points->RegisterFileSystem("x", kFileSystemTypeLocal,
                                    FileSystemMountOption(),
                                    base::FilePath(DRIVE FPL("/z/y/x")));
-  mount_points->RegisterFileSystem("o", kFileSystemTypeNativeLocal,
+  mount_points->RegisterFileSystem("o", kFileSystemTypeLocal,
                                    FileSystemMountOption(),
                                    base::FilePath(DRIVE FPL("/m/n/o")));
   // A mount point whose name does not match its path base name.
-  mount_points->RegisterFileSystem("mount", kFileSystemTypeNativeLocal,
+  mount_points->RegisterFileSystem("mount", kFileSystemTypeLocal,
                                    FileSystemMountOption(),
                                    base::FilePath(DRIVE FPL("/root/foo")));
   // A mount point with an empty path.
-  mount_points->RegisterFileSystem("empty_path", kFileSystemTypeNativeLocal,
+  mount_points->RegisterFileSystem("empty_path", kFileSystemTypeLocal,
                                    FileSystemMountOption(), base::FilePath());
 
   struct TestCase {
@@ -246,10 +246,9 @@
       mount_points->HandlesFileSystemMountType(kFileSystemTypePersistent));
   EXPECT_FALSE(mount_points->HandlesFileSystemMountType(kFileSystemTypeTest));
   // Not even if it's external subtype.
+  EXPECT_FALSE(mount_points->HandlesFileSystemMountType(kFileSystemTypeLocal));
   EXPECT_FALSE(
-      mount_points->HandlesFileSystemMountType(kFileSystemTypeNativeLocal));
-  EXPECT_FALSE(mount_points->HandlesFileSystemMountType(
-      kFileSystemTypeRestrictedNativeLocal));
+      mount_points->HandlesFileSystemMountType(kFileSystemTypeRestrictedLocal));
   EXPECT_FALSE(
       mount_points->HandlesFileSystemMountType(kFileSystemTypeDriveFs));
   EXPECT_FALSE(
@@ -263,7 +262,7 @@
   const url::Origin kTestOrigin =
       url::Origin::Create(GURL("http://chromium.org"));
 
-  mount_points->RegisterFileSystem("c", kFileSystemTypeNativeLocal,
+  mount_points->RegisterFileSystem("c", kFileSystemTypeLocal,
                                    FileSystemMountOption(),
                                    base::FilePath(DRIVE FPL("/a/b/c")));
   mount_points->RegisterFileSystem("c(1)", kFileSystemTypeDriveFs,
@@ -286,7 +285,7 @@
 
   // Try native local which is not cracked.
   FileSystemURL native_local = mount_points->CreateCrackedFileSystemURL(
-      kTestOrigin, kFileSystemTypeNativeLocal, base::FilePath(FPL("c")));
+      kTestOrigin, kFileSystemTypeLocal, base::FilePath(FPL("c")));
   EXPECT_FALSE(native_local.is_valid());
 
   struct TestCase {
@@ -298,8 +297,7 @@
   };
 
   const TestCase kTestCases[] = {
-    {FPL("c/d/e"), true, kFileSystemTypeNativeLocal, DRIVE FPL("/a/b/c/d/e"),
-     "c"},
+    {FPL("c/d/e"), true, kFileSystemTypeLocal, DRIVE FPL("/a/b/c/d/e"), "c"},
     {FPL("c(1)/d/e"), true, kFileSystemTypeDriveFs, DRIVE FPL("/a/b/c(1)/d/e"),
      "c(1)"},
     {FPL("c(1)"), true, kFileSystemTypeDriveFs, DRIVE FPL("/a/b/c(1)"), "c(1)"},
@@ -320,8 +318,7 @@
     {FPL("c/d/../e"), false, kFileSystemTypeUnknown, FPL(""), ""},
     {FPL("/empty_path/a/../b"), false, kFileSystemTypeUnknown, FPL(""), ""},
 #if defined(FILE_PATH_USES_WIN_SEPARATORS)
-    {FPL("c/d\\e"), true, kFileSystemTypeNativeLocal, DRIVE FPL("/a/b/c/d/e"),
-     "c"},
+    {FPL("c/d\\e"), true, kFileSystemTypeLocal, DRIVE FPL("/a/b/c/d/e"), "c"},
     {FPL("mount\\a\\b"), true, kFileSystemTypeDriveFs, DRIVE FPL("/root/a/b"),
      "mount"},
 #endif
@@ -361,7 +358,7 @@
 
   const GURL kTestOrigin("http://chromium.org");
 
-  mount_points->RegisterFileSystem("c", kFileSystemTypeNativeLocal,
+  mount_points->RegisterFileSystem("c", kFileSystemTypeLocal,
                                    FileSystemMountOption(),
                                    base::FilePath(DRIVE FPL("/a/b/c")));
   mount_points->RegisterFileSystem("c(1)", kFileSystemTypeDriveFs,
@@ -382,8 +379,7 @@
   };
 
   const TestCase kTestCases[] = {
-    {FPL("c/d/e"), true, kFileSystemTypeNativeLocal, DRIVE FPL("/a/b/c/d/e"),
-     "c"},
+    {FPL("c/d/e"), true, kFileSystemTypeLocal, DRIVE FPL("/a/b/c/d/e"), "c"},
     {FPL("c(1)/d/e"), true, kFileSystemTypeDriveFs, DRIVE FPL("/a/b/c(1)/d/e"),
      "c(1)"},
     {FPL("c(1)"), true, kFileSystemTypeDriveFs, DRIVE FPL("/a/b/c(1)"), "c(1)"},
@@ -404,8 +400,7 @@
     {FPL("c/d/../e"), false, kFileSystemTypeUnknown, FPL(""), ""},
     {FPL("/empty_path/a/../b"), false, kFileSystemTypeUnknown, FPL(""), ""},
 #if defined(FILE_PATH_USES_WIN_SEPARATORS)
-    {FPL("c/d\\e"), true, kFileSystemTypeNativeLocal, DRIVE FPL("/a/b/c/d/e"),
-     "c"},
+    {FPL("c/d\\e"), true, kFileSystemTypeLocal, DRIVE FPL("/a/b/c/d/e"), "c"},
     {FPL("mount\\a\\b"), true, kFileSystemTypeDriveFs, DRIVE FPL("/root/a/b"),
      "mount"},
 #endif
@@ -445,11 +440,11 @@
       ExternalMountPoints::CreateRefCounted();
 
   mount_points->RegisterFileSystem(
-      "nosync", kFileSystemTypeNativeLocal,
+      "nosync", kFileSystemTypeLocal,
       FileSystemMountOption(FlushPolicy::NO_FLUSH_ON_COMPLETION),
       base::FilePath(DRIVE FPL("/nosync")));
   mount_points->RegisterFileSystem(
-      "sync", kFileSystemTypeNativeLocal,
+      "sync", kFileSystemTypeLocal,
       FileSystemMountOption(FlushPolicy::FLUSH_ON_COMPLETION),
       base::FilePath(DRIVE FPL("/sync")));
 
diff --git a/storage/browser/file_system/file_system_context.cc b/storage/browser/file_system/file_system_context.cc
index 0146db06..51aacf1 100644
--- a/storage/browser/file_system/file_system_context.cc
+++ b/storage/browser/file_system/file_system_context.cc
@@ -90,8 +90,8 @@
     case kFileSystemTypeSyncable:
       return FILE_PERMISSION_SANDBOX;
 
-    case kFileSystemTypeNativeForPlatformApp:
-    case kFileSystemTypeNativeLocal:
+    case kFileSystemTypeLocalForPlatformApp:
+    case kFileSystemTypeLocal:
     case kFileSystemTypeCloudDevice:
     case kFileSystemTypeProvided:
     case kFileSystemTypeDeviceMediaAsFileStorage:
@@ -101,11 +101,11 @@
     case kFileSystemTypeSmbFs:
       return FILE_PERMISSION_USE_FILE_PERMISSION;
 
-    case kFileSystemTypeRestrictedNativeLocal:
+    case kFileSystemTypeRestrictedLocal:
       return FILE_PERMISSION_READ_ONLY | FILE_PERMISSION_USE_FILE_PERMISSION;
 
     case kFileSystemTypeDeviceMedia:
-    case kFileSystemTypeNativeMedia:
+    case kFileSystemTypeLocalMedia:
       return FILE_PERMISSION_USE_FILE_PERMISSION;
 
     // Following types are only accessed via IsolatedFileSystem, and
@@ -180,13 +180,13 @@
     RegisterBackend(backend.get());
 
   // If the embedder's additional backends already provide support for
-  // kFileSystemTypeNativeLocal and kFileSystemTypeNativeForPlatformApp then
+  // kFileSystemTypeLocal and kFileSystemTypeLocalForPlatformApp then
   // IsolatedFileSystemBackend does not need to handle them. For example, on
   // Chrome OS the additional backend chromeos::FileSystemBackend handles these
   // types.
   isolated_backend_ = std::make_unique<IsolatedFileSystemBackend>(
-      !base::Contains(backend_map_, kFileSystemTypeNativeLocal),
-      !base::Contains(backend_map_, kFileSystemTypeNativeForPlatformApp));
+      !base::Contains(backend_map_, kFileSystemTypeLocal),
+      !base::Contains(backend_map_, kFileSystemTypeLocalForPlatformApp));
   RegisterBackend(isolated_backend_.get());
 
   if (quota_manager_proxy) {
diff --git a/storage/browser/file_system/file_system_context_unittest.cc b/storage/browser/file_system/file_system_context_unittest.cc
index 0f2199e..76aa905b0 100644
--- a/storage/browser/file_system/file_system_context_unittest.cc
+++ b/storage/browser/file_system/file_system_context_unittest.cc
@@ -107,12 +107,12 @@
   std::string isolated_name = "root";
   IsolatedContext::ScopedFSHandle isolated_fs =
       IsolatedContext::GetInstance()->RegisterFileSystemForPath(
-          kFileSystemTypeNativeLocal, std::string(),
+          kFileSystemTypeLocal, std::string(),
           base::FilePath(DRIVE FPL("/test/isolated/root")), &isolated_name);
   std::string isolated_id = isolated_fs.id();
   // Register system external mount point.
   ASSERT_TRUE(ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
-      "system", kFileSystemTypeNativeLocal, FileSystemMountOption(),
+      "system", kFileSystemTypeLocal, FileSystemMountOption(),
       base::FilePath(DRIVE FPL("/test/sys/"))));
 
   FileSystemURL cracked_isolated = file_system_context->CrackURL(
@@ -120,7 +120,7 @@
 
   ExpectFileSystemURLMatches(
       cracked_isolated, GURL(kTestOrigin), kFileSystemTypeIsolated,
-      kFileSystemTypeNativeLocal,
+      kFileSystemTypeLocal,
       base::FilePath(DRIVE FPL("/test/isolated/root/file"))
           .NormalizePathSeparators(),
       base::FilePath::FromUTF8Unsafe(isolated_id)
@@ -133,7 +133,7 @@
 
   ExpectFileSystemURLMatches(
       cracked_external, GURL(kTestOrigin), kFileSystemTypeExternal,
-      kFileSystemTypeNativeLocal,
+      kFileSystemTypeLocal,
       base::FilePath(DRIVE FPL("/test/sys/root/file"))
           .NormalizePathSeparators(),
       base::FilePath(FPL("system/root/file")).NormalizePathSeparators(),
@@ -150,7 +150,7 @@
 
   // Register system external mount point.
   ASSERT_TRUE(mount_points->RegisterFileSystem(
-      "system", kFileSystemTypeNativeLocal, FileSystemMountOption(),
+      "system", kFileSystemTypeLocal, FileSystemMountOption(),
       base::FilePath(DRIVE FPL("/test/sys/"))));
 
   scoped_refptr<FileSystemContext> file_system_context(
@@ -166,7 +166,7 @@
 
   ExpectFileSystemURLMatches(
       cracked_external, GURL(kTestOrigin), kFileSystemTypeExternal,
-      kFileSystemTypeNativeLocal,
+      kFileSystemTypeLocal,
       base::FilePath(DRIVE FPL("/test/sys/root/file"))
           .NormalizePathSeparators(),
       base::FilePath(FPL("system/root/file")).NormalizePathSeparators(),
@@ -186,27 +186,27 @@
   std::string isolated_file_system_name = "root";
   IsolatedContext::ScopedFSHandle isolated_fs =
       IsolatedContext::GetInstance()->RegisterFileSystemForPath(
-          kFileSystemTypeNativeLocal, std::string(),
+          kFileSystemTypeLocal, std::string(),
           base::FilePath(DRIVE FPL("/test/isolated/root")),
           &isolated_file_system_name);
   const std::string kIsolatedFileSystemID = isolated_fs.id();
   // Register system external mount point.
   ASSERT_TRUE(ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
-      "system", kFileSystemTypeNativeLocal, FileSystemMountOption(),
+      "system", kFileSystemTypeLocal, FileSystemMountOption(),
       base::FilePath(DRIVE FPL("/test/sys/"))));
   ASSERT_TRUE(ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
-      "ext", kFileSystemTypeNativeLocal, FileSystemMountOption(),
+      "ext", kFileSystemTypeLocal, FileSystemMountOption(),
       base::FilePath(DRIVE FPL("/test/ext"))));
   // Register a system external mount point with the same name/id as the
   // registered isolated mount point.
   ASSERT_TRUE(ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
-      kIsolatedFileSystemID, kFileSystemTypeRestrictedNativeLocal,
+      kIsolatedFileSystemID, kFileSystemTypeRestrictedLocal,
       FileSystemMountOption(),
       base::FilePath(DRIVE FPL("/test/system/isolated"))));
   // Add a mount points with the same name as a system mount point to
   // FileSystemContext's external mount points.
   ASSERT_TRUE(external_mount_points->RegisterFileSystem(
-      "ext", kFileSystemTypeNativeLocal, FileSystemMountOption(),
+      "ext", kFileSystemTypeLocal, FileSystemMountOption(),
       base::FilePath(DRIVE FPL("/test/local/ext/"))));
 
   const GURL kTestOrigin = GURL("http://chromium.org/");
@@ -239,18 +239,17 @@
       },
       // Should be cracked by isolated mount points:
       {kIsolatedFileSystemID, "isolated", true /* is_valid */,
-       kFileSystemTypeIsolated, kFileSystemTypeNativeLocal,
+       kFileSystemTypeIsolated, kFileSystemTypeLocal,
        DRIVE FPL("/test/isolated/root/file"), kIsolatedFileSystemID},
       // Should be cracked by system mount points:
       {"system", "external", true /* is_valid */, kFileSystemTypeExternal,
-       kFileSystemTypeNativeLocal, DRIVE FPL("/test/sys/root/file"), "system"},
+       kFileSystemTypeLocal, DRIVE FPL("/test/sys/root/file"), "system"},
       {kIsolatedFileSystemID, "external", true /* is_valid */,
-       kFileSystemTypeExternal, kFileSystemTypeRestrictedNativeLocal,
+       kFileSystemTypeExternal, kFileSystemTypeRestrictedLocal,
        DRIVE FPL("/test/system/isolated/root/file"), kIsolatedFileSystemID},
       // Should be cracked by FileSystemContext's ExternalMountPoints.
       {"ext", "external", true /* is_valid */, kFileSystemTypeExternal,
-       kFileSystemTypeNativeLocal, DRIVE FPL("/test/local/ext/root/file"),
-       "ext"},
+       kFileSystemTypeLocal, DRIVE FPL("/test/local/ext/root/file"), "ext"},
       // Test for invalid filesystem url (made invalid by adding invalid
       // filesystem type).
       {"sytem", "external", false /* is_valid */,
@@ -310,7 +309,7 @@
   std::string isolated_fs_name = "root";
   IsolatedContext::ScopedFSHandle isolated_fs =
       IsolatedContext::GetInstance()->RegisterFileSystemForPath(
-          kFileSystemTypeNativeLocal, std::string(),
+          kFileSystemTypeLocal, std::string(),
           base::FilePath(DRIVE FPL("/test/isolated/root")), &isolated_fs_name);
   std::string isolated_fs_id = isolated_fs.id();
   cracked_url =
@@ -321,7 +320,7 @@
   // A request for an external mount point should be served.
   const std::string kExternalMountName = "ext_mount";
   ASSERT_TRUE(ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
-      kExternalMountName, kFileSystemTypeNativeLocal, FileSystemMountOption(),
+      kExternalMountName, kFileSystemTypeLocal, FileSystemMountOption(),
       base::FilePath()));
   cracked_url =
       context->CrackURL(CreateRawFileSystemURL("external", kExternalMountName));
@@ -348,10 +347,9 @@
       file_system_context->GetFileSystemBackend(kFileSystemTypeDragged));
   EXPECT_TRUE(file_system_context->GetFileSystemBackend(
       kFileSystemTypeForTransientFile));
-  EXPECT_TRUE(
-      file_system_context->GetFileSystemBackend(kFileSystemTypeNativeLocal));
+  EXPECT_TRUE(file_system_context->GetFileSystemBackend(kFileSystemTypeLocal));
   EXPECT_TRUE(file_system_context->GetFileSystemBackend(
-      kFileSystemTypeNativeForPlatformApp));
+      kFileSystemTypeLocalForPlatformApp));
 }
 
 }  // namespace
diff --git a/storage/browser/file_system/isolated_context_unittest.cc b/storage/browser/file_system/isolated_context_unittest.cc
index 2c28720..b8544bd 100644
--- a/storage/browser/file_system/isolated_context_unittest.cc
+++ b/storage/browser/file_system/isolated_context_unittest.cc
@@ -119,7 +119,7 @@
 
   IsolatedContext::ScopedFSHandle fs2 =
       isolated_context()->RegisterFileSystemForPath(
-          kFileSystemTypeNativeLocal, std::string(),
+          kFileSystemTypeLocal, std::string(),
           base::FilePath(DRIVE FPL("/foo")), nullptr);
 
   // Make sure the GetDraggedFileInfo returns false for both ones.
@@ -133,13 +133,13 @@
   // Try registering three more file systems for the same path as id2.
   IsolatedContext::ScopedFSHandle fs3 =
       isolated_context()->RegisterFileSystemForPath(
-          kFileSystemTypeNativeLocal, std::string(), path, nullptr);
+          kFileSystemTypeLocal, std::string(), path, nullptr);
   IsolatedContext::ScopedFSHandle fs4 =
       isolated_context()->RegisterFileSystemForPath(
-          kFileSystemTypeNativeLocal, std::string(), path, nullptr);
+          kFileSystemTypeLocal, std::string(), path, nullptr);
   IsolatedContext::ScopedFSHandle fs5 =
       isolated_context()->RegisterFileSystemForPath(
-          kFileSystemTypeNativeLocal, std::string(), path, nullptr);
+          kFileSystemTypeLocal, std::string(), path, nullptr);
 
   // Remove file system for id4.
   fs4 = IsolatedContext::ScopedFSHandle();
@@ -317,12 +317,12 @@
   EXPECT_FALSE(
       isolated_context()->HandlesFileSystemMountType(kFileSystemTypeTest));
   // Not even if it's isolated subtype.
-  EXPECT_FALSE(isolated_context()->HandlesFileSystemMountType(
-      kFileSystemTypeNativeLocal));
+  EXPECT_FALSE(
+      isolated_context()->HandlesFileSystemMountType(kFileSystemTypeLocal));
   EXPECT_FALSE(
       isolated_context()->HandlesFileSystemMountType(kFileSystemTypeDragged));
   EXPECT_FALSE(isolated_context()->HandlesFileSystemMountType(
-      kFileSystemTypeNativeMedia));
+      kFileSystemTypeLocalMedia));
   EXPECT_FALSE(isolated_context()->HandlesFileSystemMountType(
       kFileSystemTypeDeviceMedia));
 }
diff --git a/storage/browser/file_system/isolated_file_system_backend.cc b/storage/browser/file_system/isolated_file_system_backend.cc
index a3c04881..49e2e3d 100644
--- a/storage/browser/file_system/isolated_file_system_backend.cc
+++ b/storage/browser/file_system/isolated_file_system_backend.cc
@@ -53,9 +53,9 @@
     case kFileSystemTypeDragged:
     case kFileSystemTypeForTransientFile:
       return true;
-    case kFileSystemTypeNativeLocal:
+    case kFileSystemTypeLocal:
       return use_for_type_native_local_;
-    case kFileSystemTypeNativeForPlatformApp:
+    case kFileSystemTypeLocalForPlatformApp:
       return use_for_type_platform_app_;
     default:
       return false;
@@ -76,7 +76,7 @@
 AsyncFileUtil* IsolatedFileSystemBackend::GetAsyncFileUtil(
     FileSystemType type) {
   switch (type) {
-    case kFileSystemTypeNativeLocal:
+    case kFileSystemTypeLocal:
       return isolated_file_util_.get();
     case kFileSystemTypeDragged:
       return dragged_file_util_.get();
@@ -117,7 +117,7 @@
 
 bool IsolatedFileSystemBackend::HasInplaceCopyImplementation(
     FileSystemType type) const {
-  DCHECK(type == kFileSystemTypeNativeLocal || type == kFileSystemTypeDragged ||
+  DCHECK(type == kFileSystemTypeLocal || type == kFileSystemTypeDragged ||
          type == kFileSystemTypeForTransientFile);
   return false;
 }
diff --git a/storage/common/file_system/file_system_types.h b/storage/common/file_system/file_system_types.h
index 26eac0f..c171e0f 100644
--- a/storage/common/file_system/file_system_types.h
+++ b/storage/common/file_system/file_system_types.h
@@ -54,14 +54,13 @@
   // Should be used only for testing.
   kFileSystemTypeTest,
 
-  // Indicates a local filesystem where we can access files using native
-  // local path.
-  kFileSystemTypeNativeLocal,
+  // Indicates a local filesystem where we can access files using local path.
+  kFileSystemTypeLocal,
 
-  // Indicates a local filesystem where we can access files using native
-  // local path, but with restricted access.
-  // Restricted native local file system is in read-only mode.
-  kFileSystemTypeRestrictedNativeLocal,
+  // Indicates a local filesystem where we can access files using local path,
+  // but with restricted access.
+  // Restricted local file system is in read-only mode.
+  kFileSystemTypeRestrictedLocal,
 
   // Indicates a transient, isolated file system for dragged files (which could
   // contain multiple dragged paths in the virtual root).
@@ -69,7 +68,7 @@
 
   // Indicates media filesystem which we can access with same manner to
   // regular filesystem.
-  kFileSystemTypeNativeMedia,
+  kFileSystemTypeLocalMedia,
 
   // Indicates media filesystem to which we need special protocol to access,
   // such as MTP or PTP.
@@ -88,9 +87,9 @@
 
   // Indicates an external filesystem accessible by file paths from platform
   // Apps. As of writing, on non Chrome OS platform, this is merely a
-  // kFileSystemTypeNativeLocal. On Chrome OS, the path is parsed by
+  // kFileSystemTypeLocal. On Chrome OS, the path is parsed by
   // the handlers of kFileSystemTypeExternal.
-  kFileSystemTypeNativeForPlatformApp,
+  kFileSystemTypeLocalForPlatformApp,
 
   // Indicates an isolated filesystem which is supposed to contain one
   // temporary which is supposed to go away when the last reference of
diff --git a/storage/common/file_system/file_system_util.cc b/storage/common/file_system/file_system_util.cc
index 3bead66..0e22469 100644
--- a/storage/common/file_system/file_system_util.cc
+++ b/storage/common/file_system/file_system_util.cc
@@ -282,21 +282,21 @@
       return "External";
     case kFileSystemTypeTest:
       return "Test";
-    case kFileSystemTypeNativeLocal:
-      return "NativeLocal";
-    case kFileSystemTypeRestrictedNativeLocal:
-      return "RestrictedNativeLocal";
+    case kFileSystemTypeLocal:
+      return "Local";
+    case kFileSystemTypeRestrictedLocal:
+      return "RestrictedLocal";
     case kFileSystemTypeDragged:
       return "Dragged";
-    case kFileSystemTypeNativeMedia:
-      return "NativeMedia";
+    case kFileSystemTypeLocalMedia:
+      return "LocalMedia";
     case kFileSystemTypeDeviceMedia:
       return "DeviceMedia";
     case kFileSystemTypeSyncable:
     case kFileSystemTypeSyncableForInternalSync:
       return "Syncable";
-    case kFileSystemTypeNativeForPlatformApp:
-      return "NativeForPlatformApp";
+    case kFileSystemTypeLocalForPlatformApp:
+      return "LocalForPlatformApp";
     case kFileSystemTypeForTransientFile:
       return "TransientFile";
     case kFileSystemTypePluginPrivate:
diff --git a/testing/buildbot/chromium.android.fyi.json b/testing/buildbot/chromium.android.fyi.json
index df088176..f1ecfa1 100644
--- a/testing/buildbot/chromium.android.fyi.json
+++ b/testing/buildbot/chromium.android.fyi.json
@@ -336,11 +336,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_apk_Client Tests For 89.0.4389.23"
+            "weblayer_instrumentation_test_versions_apk_Client Tests For 89.0.4389.25"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_apk_Client Tests For 89.0.4389.23",
+        "name": "weblayer_instrumentation_test_versions_apk_Client Tests For 89.0.4389.25",
         "resultdb": {
           "enable": true
         },
@@ -350,7 +350,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M89",
-              "revision": "version:89.0.4389.23"
+              "revision": "version:89.0.4389.25"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -573,11 +573,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_apk_Implementation Tests For 89.0.4389.23"
+            "weblayer_instrumentation_test_versions_apk_Implementation Tests For 89.0.4389.25"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_apk_Implementation Tests For 89.0.4389.23",
+        "name": "weblayer_instrumentation_test_versions_apk_Implementation Tests For 89.0.4389.25",
         "resultdb": {
           "enable": true
         },
@@ -587,7 +587,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M89",
-              "revision": "version:89.0.4389.23"
+              "revision": "version:89.0.4389.25"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -814,11 +814,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_apk_Client Tests For 89.0.4389.23"
+            "weblayer_instrumentation_test_versions_apk_Client Tests For 89.0.4389.25"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_apk_Client Tests For 89.0.4389.23",
+        "name": "weblayer_instrumentation_test_versions_apk_Client Tests For 89.0.4389.25",
         "resultdb": {
           "enable": true
         },
@@ -828,7 +828,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M89",
-              "revision": "version:89.0.4389.23"
+              "revision": "version:89.0.4389.25"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -1051,11 +1051,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_apk_Implementation Tests For 89.0.4389.23"
+            "weblayer_instrumentation_test_versions_apk_Implementation Tests For 89.0.4389.25"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_apk_Implementation Tests For 89.0.4389.23",
+        "name": "weblayer_instrumentation_test_versions_apk_Implementation Tests For 89.0.4389.25",
         "resultdb": {
           "enable": true
         },
@@ -1065,7 +1065,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M89",
-              "revision": "version:89.0.4389.23"
+              "revision": "version:89.0.4389.25"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -1355,11 +1355,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_apk_Client Tests For 89.0.4389.23"
+            "weblayer_instrumentation_test_versions_apk_Client Tests For 89.0.4389.25"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_apk_Client Tests For 89.0.4389.23",
+        "name": "weblayer_instrumentation_test_versions_apk_Client Tests For 89.0.4389.25",
         "resultdb": {
           "enable": true
         },
@@ -1369,7 +1369,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M89",
-              "revision": "version:89.0.4389.23"
+              "revision": "version:89.0.4389.25"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -1592,11 +1592,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_apk_Implementation Tests For 89.0.4389.23"
+            "weblayer_instrumentation_test_versions_apk_Implementation Tests For 89.0.4389.25"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_apk_Implementation Tests For 89.0.4389.23",
+        "name": "weblayer_instrumentation_test_versions_apk_Implementation Tests For 89.0.4389.25",
         "resultdb": {
           "enable": true
         },
@@ -1606,7 +1606,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M89",
-              "revision": "version:89.0.4389.23"
+              "revision": "version:89.0.4389.25"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -1896,11 +1896,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_apk_Client Tests For 89.0.4389.23"
+            "weblayer_instrumentation_test_versions_apk_Client Tests For 89.0.4389.25"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_apk_Client Tests For 89.0.4389.23",
+        "name": "weblayer_instrumentation_test_versions_apk_Client Tests For 89.0.4389.25",
         "resultdb": {
           "enable": true
         },
@@ -1910,7 +1910,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M89",
-              "revision": "version:89.0.4389.23"
+              "revision": "version:89.0.4389.25"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -2133,11 +2133,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_apk_Implementation Tests For 89.0.4389.23"
+            "weblayer_instrumentation_test_versions_apk_Implementation Tests For 89.0.4389.25"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_apk_Implementation Tests For 89.0.4389.23",
+        "name": "weblayer_instrumentation_test_versions_apk_Implementation Tests For 89.0.4389.25",
         "resultdb": {
           "enable": true
         },
@@ -2147,7 +2147,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M89",
-              "revision": "version:89.0.4389.23"
+              "revision": "version:89.0.4389.25"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
diff --git a/testing/buildbot/chromium.mac.json b/testing/buildbot/chromium.mac.json
index 52d08780..5f25cc6 100644
--- a/testing/buildbot/chromium.mac.json
+++ b/testing/buildbot/chromium.mac.json
@@ -3789,27 +3789,6 @@
         "test_id_prefix": "ninja://third_party/boringssl:boringssl_ssl_tests/"
       },
       {
-        "experiment_percentage": 10,
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "none",
-              "os": "Mac-10.13.6"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 40
-        },
-        "test": "browser_tests",
-        "test_id_prefix": "ninja://chrome/test:browser_tests/"
-      },
-      {
         "args": [
           "--gtest_filter=-*UsingRealWebcam*"
         ],
diff --git a/testing/buildbot/filters/cast-linux.content_browsertests.filter b/testing/buildbot/filters/cast-linux.content_browsertests.filter
index 141a6b7..5a2575b 100644
--- a/testing/buildbot/filters/cast-linux.content_browsertests.filter
+++ b/testing/buildbot/filters/cast-linux.content_browsertests.filter
@@ -1,3 +1,2 @@
 # crbug.com/503735: Some pepper font-related tests fail on cast_shell trybot
--OutOfProcessPPAPITest.TrueTypeFont
 -OutOfProcessPPAPITest.BrowserFont
\ No newline at end of file
diff --git a/testing/buildbot/filters/lacros.browser_tests.filter b/testing/buildbot/filters/lacros.browser_tests.filter
index 224d619c..aa99fa3 100644
--- a/testing/buildbot/filters/lacros.browser_tests.filter
+++ b/testing/buildbot/filters/lacros.browser_tests.filter
@@ -136,8 +136,6 @@
 -PictureInPictureWindowControllerBrowserTest.*
 -PopupTrackerBrowserTest.PopupInWindow_IsWindowTrue
 -PopupTrackerBrowserTest.ShiftClick_HasTracker
--PPAPINaClNewlibTest.TrueTypeFont
--PPAPINaClPNaClNonSfiTest.TrueTypeFont
 -PresentationReceiverWindowControllerBrowserTest.CreatesWindow
 -PreservedWindowPlacement.Test
 -PrintPreviewDestinationSelectTest.ChangeIconDeprecationWarnings
diff --git a/testing/buildbot/filters/lacros.content_browsertests.filter b/testing/buildbot/filters/lacros.content_browsertests.filter
index 81c99ff9..a04c255 100644
--- a/testing/buildbot/filters/lacros.content_browsertests.filter
+++ b/testing/buildbot/filters/lacros.content_browsertests.filter
@@ -1,6 +1,4 @@
-
 # TODO(crbug.com/1111979) Enable all tests on lacros.
--OutOfProcessPPAPITest.TrueTypeFont
 
 # Following tests are flaky.
 -All/WebContentsVideoCaptureDeviceBrowserTestP.CapturesContentChanges*
diff --git a/testing/buildbot/test_suite_exceptions.pyl b/testing/buildbot/test_suite_exceptions.pyl
index 94b0e5f..1e17a93 100644
--- a/testing/buildbot/test_suite_exceptions.pyl
+++ b/testing/buildbot/test_suite_exceptions.pyl
@@ -441,6 +441,7 @@
       'CrWinAsan(dll)', # https://crbug.com/935598
       'linux-win_cross-rel',
       'ToTLinuxTSan',  # https://crbug.com/368525
+      'Mac10.13 Tests', # https://crbug.com/1042757
       'Linux TSan Tests',  # https://crbug.com/368525
       'Win10 Tests x64 (dbg)',
     ],
@@ -510,12 +511,6 @@
           'shards': 30,
         },
       },
-      'Mac10.13 Tests':{
-        'experiment_percentage': 10, # https://crbug.com/1042757
-        'swarming': {
-          'shards': 40,
-        },
-      },
       'Mac10.13 Tests (dbg)': {
         # https://crbug.com/1152770
         'swarming': {
diff --git a/testing/buildbot/variants.pyl b/testing/buildbot/variants.pyl
index 131e88c..0295dfbd 100644
--- a/testing/buildbot/variants.pyl
+++ b/testing/buildbot/variants.pyl
@@ -320,13 +320,13 @@
       '../../weblayer/browser/android/javatests/skew/expectations.txt',
       '--impl-version=89',
     ],
-    'identifier': 'Implementation Tests For 89.0.4389.23',
+    'identifier': 'Implementation Tests For 89.0.4389.25',
     'swarming': {
       'cipd_packages': [
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M89',
-          'revision': 'version:89.0.4389.23',
+          'revision': 'version:89.0.4389.25',
         }
       ],
     },
@@ -392,13 +392,13 @@
       '../../weblayer/browser/android/javatests/skew/expectations.txt',
       '--impl-version=89',
     ],
-    'identifier': 'Implementation Tests For 89.0.4389.23',
+    'identifier': 'Implementation Tests For 89.0.4389.25',
     'swarming': {
       'cipd_packages': [
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M89',
-          'revision': 'version:89.0.4389.23',
+          'revision': 'version:89.0.4389.25',
         }
       ],
     },
@@ -464,13 +464,13 @@
       '../../weblayer/browser/android/javatests/skew/expectations.txt',
       '--client-version=89',
     ],
-    'identifier': 'Client Tests For 89.0.4389.23',
+    'identifier': 'Client Tests For 89.0.4389.25',
     'swarming': {
       'cipd_packages': [
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M89',
-          'revision': 'version:89.0.4389.23',
+          'revision': 'version:89.0.4389.25',
         }
       ],
     },
@@ -538,4 +538,4 @@
     },
     'identifier': 'OCTOPUS_TOT-1',
   },
-}
+}
\ No newline at end of file
diff --git a/testing/test.gni b/testing/test.gni
index a9a0ece..a522190 100644
--- a/testing/test.gni
+++ b/testing/test.gni
@@ -16,8 +16,10 @@
   import("//build/config/sanitizers/sanitizers.gni")
 } else if (is_fuchsia) {
   import("//build/config/chromecast_build.gni")
+  import("//build/config/coverage/coverage.gni")
   import("//build/config/fuchsia/generate_runner_scripts.gni")
   import("//build/config/fuchsia/package.gni")
+  import("//third_party/fuchsia-sdk/sdk/build/cmc.gni")
 } else if (is_chromeos_ash) {
   import("//build/config/chromeos/rules.gni")
   import("//build/config/sanitizers/sanitizers.gni")
@@ -245,6 +247,26 @@
     _pkg_target = "${_output_name}_pkg"
     _exec_target = "${_output_name}__exec"
 
+    # TODO(1019938): switch the default to tests.cmx which doesn't request
+    # the deprecated-ambient-replace-as-executable feature.
+    if (!defined(invoker.manifest)) {
+      manifest = "//build/config/fuchsia/tests-with-exec.cmx"
+    } else {
+      manifest = invoker.manifest
+    }
+
+    if (use_clang_coverage) {
+      component_with_coverage_manifest = "${target_name}-coverage.test-cmx"
+      cmc_merge(component_with_coverage_manifest) {
+        sources = [
+          "//build/config/fuchsia/add_DebugData_service.test-cmx",
+          manifest,
+        ]
+        output_name = target_name
+      }
+      manifest = "${target_out_dir}/${component_with_coverage_manifest}"
+    }
+
     fuchsia_package_runner(target_name) {
       is_test_exe = true
       forward_variables_from(invoker,
@@ -261,9 +283,11 @@
     }
 
     cr_fuchsia_package(_pkg_target) {
-      forward_variables_from(invoker, [ "manifest" ])
       binary = ":$_exec_target"
       package_name_override = _output_name
+      if (use_clang_coverage) {
+        deps = [ ":$component_with_coverage_manifest" ]
+      }
     }
 
     executable(_exec_target) {
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 372bba0..bc175d3d 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -1936,13 +1936,40 @@
             ],
             "experiments": [
                 {
-                    "name": "Enabled",
+                    "name": "PSIThreshold_10_Percent",
+                    "params": {
+                        "CrOSLowMemoryPSIThreshold": "10"
+                    },
+                    "enable_features": [
+                        "CrOSLowMemoryNotificationPSI"
+                    ]
+                },
+                {
+                    "name": "PSIThreshold_20_Percent",
                     "params": {
                         "CrOSLowMemoryPSIThreshold": "20"
                     },
                     "enable_features": [
                         "CrOSLowMemoryNotificationPSI"
                     ]
+                },
+                {
+                    "name": "PSIThreshold_30_Percent",
+                    "params": {
+                        "CrOSLowMemoryPSIThreshold": "30"
+                    },
+                    "enable_features": [
+                        "CrOSLowMemoryNotificationPSI"
+                    ]
+                },
+                {
+                    "name": "PSIThreshold_40_Percent",
+                    "params": {
+                        "CrOSLowMemoryPSIThreshold": "40"
+                    },
+                    "enable_features": [
+                        "CrOSLowMemoryNotificationPSI"
+                    ]
                 }
             ]
         }
diff --git a/third_party/androidx/build.gradle.template b/third_party/androidx/build.gradle.template
index dd7b0c7f..b1ec2ad 100644
--- a/third_party/androidx/build.gradle.template
+++ b/third_party/androidx/build.gradle.template
@@ -23,6 +23,7 @@
     compile "androidx.lifecycle:lifecycle-livedata:{{androidx_dependency_version}}"
     compile "androidx.lifecycle:lifecycle-livedata-core:{{androidx_dependency_version}}"
     compile "androidx.lifecycle:lifecycle-viewmodel:{{androidx_dependency_version}}"
+    compile "androidx.lifecycle:lifecycle-viewmodel-savedstate:{{androidx_dependency_version}}"
 
     compile "androidx.core:core:{{androidx_dependency_version}}"
     compile "androidx.core:core-animation:{{androidx_dependency_version}}"
@@ -56,6 +57,7 @@
     compile "androidx.tvprovider:tvprovider:{{androidx_dependency_version}}"
     compile "androidx.viewpager:viewpager:{{androidx_dependency_version}}"
     compile "androidx.exifinterface:exifinterface:{{androidx_dependency_version}}"
+    compile "androidx.window:window:{{androidx_dependency_version}}"
 
     // Those are for use by doubledown libraries.
     compile "androidx.arch.core:core-common:{{androidx_dependency_version}}"
diff --git a/third_party/blink/public/mojom/web_feature/web_feature.mojom b/third_party/blink/public/mojom/web_feature/web_feature.mojom
index a5eb64e..60a41ec 100644
--- a/third_party/blink/public/mojom/web_feature/web_feature.mojom
+++ b/third_party/blink/public/mojom/web_feature/web_feature.mojom
@@ -3002,8 +3002,8 @@
   kUndeferrableThirdPartySubresourceRequestWithCookie = 3682,
   kXRDepthSensing = 3683,
   kXRFrameGetDepthInformation = 3684,
-  kXRDepthInformationGetDepth = 3685,
-  kXRDepthInformationDataAttribute = 3686,
+  kXRCPUDepthInformationGetDepth = 3685,
+  kXRCPUDepthInformationDataAttribute = 3686,
   kInterestCohortAPI_interestCohort_Method = 3687,
   kOBSOLETE_AddressSpaceLocalEmbeddedInPrivateSecureContext = 3688,
   kOBSOLETE_AddressSpaceLocalEmbeddedInPrivateNonSecureContext = 3689,
@@ -3111,6 +3111,8 @@
   kRTCPeerConnectionUsingComplexUnifiedPlan = 3788,
   kWindowScreenIsExtended = 3789,
   kWindowScreenChange = 3790,
+  kXRWebGLDepthInformationTextureAttribute = 3791,
+  kXRWebGLBindingGetDepthInformation = 3792,
 
   // Add new features immediately above this line. Don't change assigned
   // numbers of any item, and don't reuse removed slots.
diff --git a/third_party/blink/public/web/web_media_inspector.h b/third_party/blink/public/web/web_media_inspector.h
index 5a8d99f..1af4bbc 100644
--- a/third_party/blink/public/web/web_media_inspector.h
+++ b/third_party/blink/public/web/web_media_inspector.h
@@ -43,7 +43,8 @@
  public:
   virtual WebString CreatePlayer() = 0;
 
-  // These methods DCHECK if the player id is invalid.
+  virtual void DestroyPlayer(const WebString& playerId) = 0;
+
   virtual void NotifyPlayerEvents(WebString player_id,
                                   const InspectorPlayerEvents&) = 0;
   virtual void NotifyPlayerErrors(WebString player_id,
diff --git a/third_party/blink/renderer/bindings/generated_in_modules.gni b/third_party/blink/renderer/bindings/generated_in_modules.gni
index 099be40..06a5e19 100644
--- a/third_party/blink/renderer/bindings/generated_in_modules.gni
+++ b/third_party/blink/renderer/bindings/generated_in_modules.gni
@@ -851,6 +851,8 @@
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_websocket_stream_options.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_write_params.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_write_params.h",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_xr_depth_state_init.cc",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_xr_depth_state_init.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_xr_dom_overlay_init.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_xr_dom_overlay_init.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_xr_hit_test_options_init.cc",
@@ -2218,6 +2220,10 @@
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_xr_bounded_reference_space.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_xr_depth_information.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_xr_depth_information.h",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_xr_cpu_depth_information.cc",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_xr_cpu_depth_information.h",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_xr_webgl_depth_information.cc",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_xr_webgl_depth_information.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_xr_dom_overlay_state.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_xr_dom_overlay_state.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_xr_frame.cc",
diff --git a/third_party/blink/renderer/bindings/idl_in_modules.gni b/third_party/blink/renderer/bindings/idl_in_modules.gni
index 5dd8326..de7a1ae 100644
--- a/third_party/blink/renderer/bindings/idl_in_modules.gni
+++ b/third_party/blink/renderer/bindings/idl_in_modules.gni
@@ -1006,7 +1006,9 @@
           "//third_party/blink/renderer/modules/xr/xr_anchor.idl",
           "//third_party/blink/renderer/modules/xr/xr_anchor_set.idl",
           "//third_party/blink/renderer/modules/xr/xr_bounded_reference_space.idl",
+          "//third_party/blink/renderer/modules/xr/xr_cpu_depth_information.idl",
           "//third_party/blink/renderer/modules/xr/xr_depth_information.idl",
+          "//third_party/blink/renderer/modules/xr/xr_depth_state_init.idl",
           "//third_party/blink/renderer/modules/xr/xr_dom_overlay_init.idl",
           "//third_party/blink/renderer/modules/xr/xr_dom_overlay_state.idl",
           "//third_party/blink/renderer/modules/xr/xr_frame.idl",
@@ -1054,6 +1056,7 @@
           "//third_party/blink/renderer/modules/xr/xr_viewport.idl",
           "//third_party/blink/renderer/modules/xr/xr_webgl_binding.idl",
           "//third_party/blink/renderer/modules/xr/xr_webgl_context.idl",
+          "//third_party/blink/renderer/modules/xr/xr_webgl_depth_information.idl",
           "//third_party/blink/renderer/modules/xr/xr_webgl_layer.idl",
           "//third_party/blink/renderer/modules/xr/xr_webgl_layer_init.idl",
         ],
diff --git a/third_party/blink/renderer/core/BUILD.gn b/third_party/blink/renderer/core/BUILD.gn
index 24777b6c..f20764b 100644
--- a/third_party/blink/renderer/core/BUILD.gn
+++ b/third_party/blink/renderer/core/BUILD.gn
@@ -1297,6 +1297,7 @@
     "input/touch_event_manager_test.cc",
     "inspector/inspector_emulation_agent_test.cc",
     "inspector/inspector_history_test.cc",
+    "inspector/inspector_media_context_impl_unittest.cc",
     "inspector/inspector_session_state_test.cc",
     "inspector/inspector_style_resolver_test.cc",
     "inspector/main_thread_debugger_test.cc",
@@ -1344,6 +1345,7 @@
     "layout/line/abstract_inline_text_box_test.cc",
     "layout/line/inline_text_box_test.cc",
     "layout/line/line_orientation_utils_test.cc",
+    "layout/list_marker_test.cc",
     "layout/map_coordinates_test.cc",
     "layout/min_max_size_test.cc",
     "layout/multi_column_fragmentainer_group_test.cc",
diff --git a/third_party/blink/renderer/core/css/counter_style.cc b/third_party/blink/renderer/core/css/counter_style.cc
index 51a70d8..7562dcc 100644
--- a/third_party/blink/renderer/core/css/counter_style.cc
+++ b/third_party/blink/renderer/core/css/counter_style.cc
@@ -388,6 +388,8 @@
 }
 
 String CounterStyle::GenerateRepresentation(int value) const {
+  DCHECK(!IsDirty());
+
   if (pad_length_ > kCounterLengthLimit)
     return GenerateFallbackRepresentation(value);
 
diff --git a/third_party/blink/renderer/core/css/counter_style_map.cc b/third_party/blink/renderer/core/css/counter_style_map.cc
index f5063351..352cd22 100644
--- a/third_party/blink/renderer/core/css/counter_style_map.cc
+++ b/third_party/blink/renderer/core/css/counter_style_map.cc
@@ -82,12 +82,21 @@
 }
 
 void CounterStyleMap::AddCounterStyles(const RuleSet& rule_set) {
+  if (!rule_set.CounterStyleRules().size())
+    return;
+
   for (StyleRuleCounterStyle* rule : rule_set.CounterStyleRules()) {
     CounterStyle* counter_style = CounterStyle::Create(*rule);
     if (!counter_style)
       continue;
+    AtomicString name = rule->GetName();
+    if (CounterStyle* replaced = counter_styles_.at(name))
+      replaced->SetIsDirty();
     counter_styles_.Set(rule->GetName(), counter_style);
   }
+
+  if (owner_document_)
+    owner_document_->GetStyleEngine().MarkCounterStylesNeedUpdate();
 }
 
 CounterStyleMap* CounterStyleMap::GetAncestorMap() const {
@@ -264,9 +273,15 @@
 }
 
 void CounterStyleMap::Dispose() {
+  if (!counter_styles_.size())
+    return;
+
   for (CounterStyle* counter_style : counter_styles_.Values())
     counter_style->SetIsDirty();
   counter_styles_.clear();
+
+  if (owner_document_)
+    owner_document_->GetStyleEngine().MarkCounterStylesNeedUpdate();
 }
 
 void CounterStyleMap::Trace(Visitor* visitor) const {
diff --git a/third_party/blink/renderer/core/css/style_engine.cc b/third_party/blink/renderer/core/css/style_engine.cc
index 63c8414..dd570c4e 100644
--- a/third_party/blink/renderer/core/css/style_engine.cc
+++ b/third_party/blink/renderer/core/css/style_engine.cc
@@ -542,11 +542,17 @@
   }
 
   if (RuntimeEnabledFeatures::CSSAtRuleCounterStyleEnabled()) {
-    // TODO(crbug.com/687225): Add a flag to indicate whether counter styles
-    // need updates, so that we don't update them every time.
-    CounterStyleMap::MarkAllDirtyCounterStyles(GetDocument(),
-                                               active_tree_scopes_);
-    CounterStyleMap::ResolveAllReferences(GetDocument(), active_tree_scopes_);
+    // TODO(crbug.com/687225): We initialize the predefined counter styles here.
+    // Moving the initialization to other places causes test failures, which
+    // needs investigation and fixing.
+    CounterStyleMap::GetUACounterStyleMap();
+
+    if (counter_styles_need_update_) {
+      CounterStyleMap::MarkAllDirtyCounterStyles(GetDocument(),
+                                                 active_tree_scopes_);
+      CounterStyleMap::ResolveAllReferences(GetDocument(), active_tree_scopes_);
+      counter_styles_need_update_ = false;
+    }
   }
 
   probe::ActiveStyleSheetsUpdated(document_);
@@ -858,6 +864,7 @@
     root->MarkSubtreeNeedsStyleRecalcForFontUpdates();
   }
 
+  // TODO(xiaochengh): Move layout invalidation after style update.
   if (LayoutView* layout_view = GetDocument().GetLayoutView()) {
     TRACE_EVENT0("blink", "LayoutObject::InvalidateSubtreeForFontUpdates");
     layout_view->InvalidateSubtreeLayoutForFontUpdates();
@@ -869,6 +876,13 @@
   GetDocument().ScheduleLayoutTreeUpdateIfNeeded();
 }
 
+void StyleEngine::MarkCounterStylesNeedUpdate() {
+  counter_styles_need_update_ = true;
+  if (LayoutView* layout_view = GetDocument().GetLayoutView())
+    layout_view->SetNeedsMarkerOrCounterUpdate();
+  GetDocument().ScheduleLayoutTreeUpdateIfNeeded();
+}
+
 void StyleEngine::FontsNeedUpdate(FontSelector*, FontInvalidationReason) {
   if (!GetDocument().IsActive())
     return;
@@ -1572,7 +1586,7 @@
         EnsureUserCounterStyleMap().AddCounterStyles(*it->second);
     }
 
-    // TODO(crbug.com/687225): Trigger style/Layout invalidations.
+    MarkCounterStylesNeedUpdate();
   }
 
   if (changed_rule_flags & (kPropertyRules | kScrollTimelineRules)) {
@@ -1638,7 +1652,8 @@
   if (changed_rule_flags & kKeyframesRules)
     ScopedStyleResolver::KeyframesRulesAdded(tree_scope);
 
-  // TODO(crbug.com/687725): Style/layout invalidation for counter style rules.
+  if (changed_rule_flags & kCounterStyleRules)
+    MarkCounterStylesNeedUpdate();
 
   if ((changed_rule_flags & kPropertyRules) || rebuild_at_property_registry) {
     // @property rules are (for now) ignored in shadow trees, per spec.
diff --git a/third_party/blink/renderer/core/css/style_engine.h b/third_party/blink/renderer/core/css/style_engine.h
index 3e534ba..16f6c6c2 100644
--- a/third_party/blink/renderer/core/css/style_engine.h
+++ b/third_party/blink/renderer/core/css/style_engine.h
@@ -377,6 +377,8 @@
   void MarkFontsNeedUpdate();
   void InvalidateStyleAndLayoutForFontUpdates();
 
+  void MarkCounterStylesNeedUpdate();
+
   StyleRuleKeyframes* KeyframeStylesForAnimation(
       const AtomicString& animation_name);
   StyleRuleScrollTimeline* FindScrollTimelineRule(const AtomicString& name);
@@ -599,6 +601,7 @@
   bool in_dom_removal_{false};
   bool viewport_style_dirty_{false};
   bool fonts_need_update_{false};
+  bool counter_styles_need_update_{false};
 
   // Set to true if we allow marking style dirty from style recalc. Ideally, we
   // should get rid of this, but we keep track of where we allow it with
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc
index e1fb80f5..6f56eeb 100644
--- a/third_party/blink/renderer/core/dom/document.cc
+++ b/third_party/blink/renderer/core/dom/document.cc
@@ -2461,7 +2461,7 @@
 
   PropagateStyleToViewport();
 
-  View()->UpdateCountersAfterStyleChange();
+  GetLayoutView()->UpdateMarkersAndCountersAfterStyleChange();
   GetLayoutView()->RecalcLayoutOverflow();
 
   DCHECK(!NeedsStyleRecalc());
diff --git a/third_party/blink/renderer/core/frame/fullscreen_controller.h b/third_party/blink/renderer/core/frame/fullscreen_controller.h
index 61a5c81..8e65226 100644
--- a/third_party/blink/renderer/core/frame/fullscreen_controller.h
+++ b/third_party/blink/renderer/core/frame/fullscreen_controller.h
@@ -37,6 +37,7 @@
 #include "third_party/blink/renderer/platform/geometry/float_point.h"
 #include "third_party/blink/renderer/platform/geometry/int_size.h"
 #include "third_party/blink/renderer/platform/graphics/color.h"
+#include "third_party/blink/renderer/platform/heap/heap_allocator.h"
 #include "third_party/blink/renderer/platform/heap/persistent.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
 
diff --git a/third_party/blink/renderer/core/frame/local_frame_view.cc b/third_party/blink/renderer/core/frame/local_frame_view.cc
index a5814bb3..0e493f4a 100644
--- a/third_party/blink/renderer/core/frame/local_frame_view.cc
+++ b/third_party/blink/renderer/core/frame/local_frame_view.cc
@@ -579,12 +579,6 @@
   }
 }
 
-void LocalFrameView::UpdateCountersAfterStyleChange() {
-  auto* layout_view = GetLayoutView();
-  DCHECK(layout_view);
-  layout_view->UpdateCounters();
-}
-
 void LocalFrameView::CountObjectsNeedingLayout(unsigned& needs_layout_objects,
                                                unsigned& total_objects,
                                                bool& is_subtree) {
diff --git a/third_party/blink/renderer/core/frame/local_frame_view.h b/third_party/blink/renderer/core/frame/local_frame_view.h
index 055d99f2..bbff09c 100644
--- a/third_party/blink/renderer/core/frame/local_frame_view.h
+++ b/third_party/blink/renderer/core/frame/local_frame_view.h
@@ -255,8 +255,6 @@
   bool GetIntrinsicSizingInfo(IntrinsicSizingInfo&) const override;
   bool HasIntrinsicSizingInfo() const override;
 
-  void UpdateCountersAfterStyleChange();
-
   void Dispose() override;
   void PropagateFrameRects() override;
   void InvalidateAllCustomScrollbarsOnActiveChanged();
diff --git a/third_party/blink/renderer/core/html/forms/html_opt_group_element.h b/third_party/blink/renderer/core/html/forms/html_opt_group_element.h
index a03db9b..25d52a7 100644
--- a/third_party/blink/renderer/core/html/forms/html_opt_group_element.h
+++ b/third_party/blink/renderer/core/html/forms/html_opt_group_element.h
@@ -37,6 +37,7 @@
 
  public:
   explicit HTMLOptGroupElement(Document&);
+  ~HTMLOptGroupElement() override;
 
   bool IsDisabledFormControl() const override;
   String DefaultToolTip() const override;
@@ -49,8 +50,6 @@
   static bool CanAssignToOptGroupSlot(const Node&);
 
  private:
-  ~HTMLOptGroupElement() override;
-
   bool SupportsFocus() const override;
   void ChildrenChanged(const ChildrenChange& change) override;
   bool ChildrenChangedAllChildrenRemovedNeedsList() const override;
diff --git a/third_party/blink/renderer/core/html/html_area_element.h b/third_party/blink/renderer/core/html/html_area_element.h
index b513888..863d489 100644
--- a/third_party/blink/renderer/core/html/html_area_element.h
+++ b/third_party/blink/renderer/core/html/html_area_element.h
@@ -39,6 +39,7 @@
 
  public:
   explicit HTMLAreaElement(Document&);
+  ~HTMLAreaElement() override;
 
   bool IsDefault() const { return shape_ == kDefault; }
 
@@ -58,8 +59,6 @@
   HTMLImageElement* ImageElement() const;
 
  private:
-  ~HTMLAreaElement() override;
-
   void ParseAttribute(const AttributeModificationParams&) override;
   bool IsKeyboardFocusable() const override;
   bool IsMouseFocusable() const override;
diff --git a/third_party/blink/renderer/core/html/html_meter_element.h b/third_party/blink/renderer/core/html/html_meter_element.h
index 620f2cb..ab6ddfc 100644
--- a/third_party/blink/renderer/core/html/html_meter_element.h
+++ b/third_party/blink/renderer/core/html/html_meter_element.h
@@ -33,6 +33,7 @@
 
  public:
   explicit HTMLMeterElement(Document&);
+  ~HTMLMeterElement() override;
 
   enum GaugeRegion {
     kGaugeRegionOptimum,
@@ -66,8 +67,6 @@
   void Trace(Visitor*) const override;
 
  private:
-  ~HTMLMeterElement() override;
-
   bool AreAuthorShadowsAllowed() const override { return false; }
 
   bool IsLabelable() const override { return true; }
diff --git a/third_party/blink/renderer/core/html/html_progress_element.h b/third_party/blink/renderer/core/html/html_progress_element.h
index 61bce3b..756bf25 100644
--- a/third_party/blink/renderer/core/html/html_progress_element.h
+++ b/third_party/blink/renderer/core/html/html_progress_element.h
@@ -37,6 +37,7 @@
   static const double kInvalidPosition;
 
   explicit HTMLProgressElement(Document&);
+  ~HTMLProgressElement() override;
 
   double value() const;
   void setValue(double);
@@ -51,8 +52,6 @@
   void Trace(Visitor*) const override;
 
  private:
-  ~HTMLProgressElement() override;
-
   bool AreAuthorShadowsAllowed() const override { return false; }
   bool ShouldAppearIndeterminate() const override;
   bool IsLabelable() const override { return true; }
diff --git a/third_party/blink/renderer/core/html/track/html_track_element.h b/third_party/blink/renderer/core/html/track/html_track_element.h
index eb01eb5..9c207bb 100644
--- a/third_party/blink/renderer/core/html/track/html_track_element.h
+++ b/third_party/blink/renderer/core/html/track/html_track_element.h
@@ -42,6 +42,7 @@
 
  public:
   explicit HTMLTrackElement(Document&);
+  ~HTMLTrackElement() override;
 
   const AtomicString& kind();
   void setKind(const AtomicString&);
@@ -55,8 +56,6 @@
   void Trace(Visitor*) const override;
 
  private:
-  ~HTMLTrackElement() override;
-
   void ParseAttribute(const AttributeModificationParams&) override;
 
   InsertionNotificationRequest InsertedInto(ContainerNode&) override;
diff --git a/third_party/blink/renderer/core/inspector/inspector_media_agent.cc b/third_party/blink/renderer/core/inspector/inspector_media_agent.cc
index 77acdfa..4993ce6 100644
--- a/third_party/blink/renderer/core/inspector/inspector_media_agent.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_media_agent.cc
@@ -81,7 +81,7 @@
 }  // namespace
 
 InspectorMediaAgent::InspectorMediaAgent(InspectedFrames* inspected_frames)
-    : local_frame_(inspected_frames->Root()),
+    : frame_(inspected_frames->Root()),
       enabled_(&agent_state_, /*default_value = */ false) {}
 
 InspectorMediaAgent::~InspectorMediaAgent() = default;
@@ -95,8 +95,8 @@
 void InspectorMediaAgent::RegisterAgent() {
   instrumenting_agents_->AddInspectorMediaAgent(this);
   auto* cache = MediaInspectorContextImpl::From(
-      *local_frame_->DomWindow()->GetExecutionContext());
-  Vector<WebString> players = cache->AllPlayerIds();
+      *frame_->DomWindow()->GetExecutionContext());
+  Vector<WebString> players = cache->AllPlayerIdsAndMarkSent();
   PlayersCreated(players);
   for (const auto& player_id : players) {
     const auto& media_player = cache->MediaPlayerFromId(player_id);
@@ -165,7 +165,7 @@
 }
 
 void InspectorMediaAgent::Trace(Visitor* visitor) const {
-  visitor->Trace(local_frame_);
+  visitor->Trace(frame_);
   InspectorBaseAgent::Trace(visitor);
 }
 
diff --git a/third_party/blink/renderer/core/inspector/inspector_media_agent.h b/third_party/blink/renderer/core/inspector/inspector_media_agent.h
index 830a3b9..f15aaa04 100644
--- a/third_party/blink/renderer/core/inspector/inspector_media_agent.h
+++ b/third_party/blink/renderer/core/inspector/inspector_media_agent.h
@@ -46,7 +46,7 @@
  private:
   void RegisterAgent();
 
-  Member<LocalFrame> local_frame_;
+  Member<LocalFrame> frame_;
   InspectorAgentState::Boolean enabled_;
   DISALLOW_COPY_AND_ASSIGN(InspectorMediaAgent);
 };
diff --git a/third_party/blink/renderer/core/inspector/inspector_media_context_impl.cc b/third_party/blink/renderer/core/inspector/inspector_media_context_impl.cc
index 0223f9b..a7ff21b 100644
--- a/third_party/blink/renderer/core/inspector/inspector_media_context_impl.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_media_context_impl.cc
@@ -49,11 +49,11 @@
   visitor->Trace(players_);
 }
 
-Vector<WebString> MediaInspectorContextImpl::AllPlayerIds() {
+Vector<WebString> MediaInspectorContextImpl::AllPlayerIdsAndMarkSent() {
   Vector<WebString> existing_players;
-  existing_players.ReserveCapacity(players_.size());
-  for (const auto& player_id : players_.Keys())
-    existing_players.push_back(player_id);
+  const auto& keys = players_.Keys();
+  existing_players.AppendRange(keys.begin(), keys.end());
+  unsent_players_.clear();
   return existing_players;
 }
 
@@ -69,16 +69,100 @@
       String::FromUTF8(base::UnguessableToken::Create().ToString());
   players_.insert(next_player_id, MakeGarbageCollected<MediaPlayer>());
   probe::PlayersCreated(GetSupplementable(), {next_player_id});
+  if (!GetSupplementable()->GetProbeSink()->HasInspectorMediaAgents())
+    unsent_players_.push_back(next_player_id);
   return next_player_id;
 }
 
+void MediaInspectorContextImpl::RemovePlayer(const WebString& playerId) {
+  const auto& player = players_.Take(playerId);
+  if (player) {
+    total_event_count_ -=
+        player->errors.size() + player->events.size() + player->messages.size();
+    DCHECK_GE(total_event_count_, 0);
+  }
+}
+
+void MediaInspectorContextImpl::TrimPlayer(const WebString& playerId) {
+  MediaPlayer* player = players_.Take(playerId);
+  wtf_size_t overage = total_event_count_ - kMaxCachedPlayerEvents;
+
+  wtf_size_t excess = std::min<wtf_size_t>(overage, player->events.size());
+  player->events.EraseAt(0, excess);
+  total_event_count_ -= excess;
+  overage -= excess;
+
+  excess = std::min(overage, player->messages.size());
+  player->messages.EraseAt(0, excess);
+  total_event_count_ -= excess;
+  overage -= excess;
+
+  excess = std::min(overage, player->errors.size());
+  player->errors.EraseAt(0, excess);
+  total_event_count_ -= excess;
+  overage -= excess;
+
+  players_.insert(playerId, player);
+}
+
+void MediaInspectorContextImpl::CullPlayers(const WebString& prefer_keep) {
+  // Erase all the dead players, but only erase the required number of others.
+  for (const auto& playerId : dead_players_)
+    RemovePlayer(playerId);
+  dead_players_.clear();
+
+  while (!expendable_players_.IsEmpty()) {
+    if (total_event_count_ <= kMaxCachedPlayerEvents)
+      return;
+    RemovePlayer(expendable_players_.back());
+    expendable_players_.pop_back();
+  }
+
+  while (!unsent_players_.IsEmpty()) {
+    if (total_event_count_ <= kMaxCachedPlayerEvents)
+      return;
+    RemovePlayer(unsent_players_.back());
+    unsent_players_.pop_back();
+  }
+
+  // TODO(tmathmeyer) keep last event time stamps for players to remove the
+  // most stale one.
+  while (players_.size() > 1) {
+    if (total_event_count_ <= kMaxCachedPlayerEvents)
+      return;
+    auto iterator = players_.begin();
+    // Make sure not to delete the item that is preferred to keep.
+    if (WTF::String(prefer_keep) == iterator->key)
+      ++iterator;
+    RemovePlayer(iterator->key);
+  }
+
+  // When there is only one player, selectively remove the oldest events.
+  if (players_.size() == 1 && total_event_count_ > kMaxCachedPlayerEvents)
+    TrimPlayer(players_.begin()->key);
+}
+
+void MediaInspectorContextImpl::DestroyPlayer(const WebString& playerId) {
+  if (unsent_players_.Contains(String(playerId))) {
+    // unsent players become dead when destroyed.
+    unsent_players_.EraseAt(unsent_players_.Find(String(playerId)));
+    dead_players_.push_back(playerId);
+  } else {
+    expendable_players_.push_back(playerId);
+  }
+}
+
 // Convert public version of event to protocol version, and send it.
 void MediaInspectorContextImpl::NotifyPlayerErrors(
     WebString playerId,
     const InspectorPlayerErrors& errors) {
   const auto& player = players_.find(playerId);
-  DCHECK_NE(player, players_.end());
-  player->value->errors.AppendRange(errors.begin(), errors.end());
+  if (player != players_.end()) {
+    player->value->errors.AppendRange(errors.begin(), errors.end());
+    total_event_count_ += errors.size();
+    if (total_event_count_ > kMaxCachedPlayerEvents)
+      CullPlayers(playerId);
+  }
 
   Vector<InspectorPlayerError> vector =
       Iter2Vector<InspectorPlayerError>(errors);
@@ -89,8 +173,12 @@
     WebString playerId,
     const InspectorPlayerEvents& events) {
   const auto& player = players_.find(playerId);
-  DCHECK_NE(player, players_.end());
-  player->value->events.AppendRange(events.begin(), events.end());
+  if (player != players_.end()) {
+    player->value->events.AppendRange(events.begin(), events.end());
+    total_event_count_ += events.size();
+    if (total_event_count_ > kMaxCachedPlayerEvents)
+      CullPlayers(playerId);
+  }
 
   Vector<InspectorPlayerEvent> vector =
       Iter2Vector<InspectorPlayerEvent>(events);
@@ -101,9 +189,10 @@
     WebString playerId,
     const InspectorPlayerProperties& props) {
   const auto& player = players_.find(playerId);
-  DCHECK_NE(player, players_.end());
-  for (const auto& property : props)
-    player->value->properties.insert(property.name, property);
+  if (player != players_.end()) {
+    for (const auto& property : props)
+      player->value->properties.insert(property.name, property);
+  }
 
   Vector<InspectorPlayerProperty> vector =
       Iter2Vector<InspectorPlayerProperty>(props);
@@ -114,12 +203,21 @@
     WebString playerId,
     const InspectorPlayerMessages& messages) {
   const auto& player = players_.find(playerId);
-  DCHECK_NE(player, players_.end());
-  player->value->messages.AppendRange(messages.begin(), messages.end());
+  if (player != players_.end()) {
+    player->value->messages.AppendRange(messages.begin(), messages.end());
+    total_event_count_ += messages.size();
+    if (total_event_count_ > kMaxCachedPlayerEvents)
+      CullPlayers(playerId);
+  }
 
   Vector<InspectorPlayerMessage> vector =
       Iter2Vector<InspectorPlayerMessage>(messages);
   probe::PlayerMessagesLogged(GetSupplementable(), playerId, vector);
 }
 
+HeapHashMap<String, Member<MediaPlayer>>*
+MediaInspectorContextImpl::GetPlayersForTesting() {
+  return &players_;
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/inspector/inspector_media_context_impl.h b/third_party/blink/renderer/core/inspector/inspector_media_context_impl.h
index ff5b740..6fe4613 100644
--- a/third_party/blink/renderer/core/inspector/inspector_media_context_impl.h
+++ b/third_party/blink/renderer/core/inspector/inspector_media_context_impl.h
@@ -5,6 +5,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_INSPECTOR_INSPECTOR_MEDIA_CONTEXT_IMPL_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_INSPECTOR_INSPECTOR_MEDIA_CONTEXT_IMPL_H_
 
+#include "build/build_config.h"
 #include "third_party/blink/public/web/web_media_inspector.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/platform/supplementable.h"
@@ -13,6 +14,13 @@
 
 namespace blink {
 
+#if defined(OS_ANDROID)
+// Players are cached per tab.
+constexpr int kMaxCachedPlayerEvents = 128;
+#else
+constexpr int kMaxCachedPlayerEvents = 512;
+#endif
+
 class ExecutionContext;
 
 struct MediaPlayer final : public GarbageCollected<MediaPlayer> {
@@ -38,6 +46,8 @@
 
   // MediaInspectorContext methods.
   WebString CreatePlayer() override;
+  void DestroyPlayer(const WebString& playerId) override;
+
   void NotifyPlayerErrors(WebString playerId,
                           const InspectorPlayerErrors&) override;
   void NotifyPlayerEvents(WebString playerId,
@@ -50,11 +60,37 @@
   // GarbageCollected methods.
   void Trace(Visitor*) const override;
 
-  Vector<WebString> AllPlayerIds();
+  Vector<WebString> AllPlayerIdsAndMarkSent();
   const MediaPlayer& MediaPlayerFromId(const WebString&);
 
+  HeapHashMap<String, Member<MediaPlayer>>* GetPlayersForTesting();
+  int GetTotalEventCountForTesting() { return total_event_count_; }
+
  private:
+  // When a player is added, its ID is stored in |unsent_players_| if no
+  // connections are open. When an unsent player is destroyed, its ID is moved
+  // to |dead_players_| and is first to be deleted if there is memory pressure.
+  // If it has already been sent when it is destroyed, it gets moved to
+  // |expendable_players_|, which is the second group of players to be deleted
+  // on memory pressure.
+
+  // If there are no dead or expendable players when it's time to start removing
+  // players, then a player from |unsent_players_| will be removed. As a last
+  // resort, remaining unended, already-sent players will be removed from
+  // |players_| until the total event size is within the limit.
+
+  // All events will be sent to any open clients regardless of players existing
+  // because the clients can handle dead players and may have their own cache.
+  void CullPlayers(const WebString& prefer_keep);
+  void TrimPlayer(const WebString& playerId);
+  void RemovePlayer(const WebString& playerId);
+
   HeapHashMap<String, Member<MediaPlayer>> players_;
+  Vector<String> unsent_players_;
+  Vector<String> dead_players_;
+  Vector<String> expendable_players_;
+
+  int total_event_count_ = 0;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/inspector/inspector_media_context_impl_unittest.cc b/third_party/blink/renderer/core/inspector/inspector_media_context_impl_unittest.cc
new file mode 100644
index 0000000..e23ca6b0
--- /dev/null
+++ b/third_party/blink/renderer/core/inspector/inspector_media_context_impl_unittest.cc
@@ -0,0 +1,143 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/core/inspector/inspector_media_context_impl.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/core/frame/local_dom_window.h"
+#include "third_party/blink/renderer/core/testing/dummy_page_holder.h"
+#include "third_party/blink/renderer/core/testing/null_execution_context.h"
+
+namespace blink {
+namespace {
+
+class InspectorMediaContextImplTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    dummy_page_holder_ =
+        std::make_unique<DummyPageHolder>(IntSize(), nullptr, nullptr);
+    impl = MediaInspectorContextImpl::From(
+        *dummy_page_holder_->GetFrame().DomWindow());
+  }
+
+  InspectorPlayerEvents MakeEvents(size_t ev_count) {
+    InspectorPlayerEvents to_add;
+    while (ev_count-- > 0) {
+      blink::InspectorPlayerEvent ev = {base::TimeTicks::Now(), "foo"};
+      to_add.emplace_back(std::move(ev));
+    }
+    return to_add;
+  }
+
+  Persistent<MediaInspectorContextImpl> impl;
+  std::unique_ptr<DummyPageHolder> dummy_page_holder_;
+};
+
+TEST_F(InspectorMediaContextImplTest, CanCreatePlayerAndAddEvents) {
+  auto id = impl->CreatePlayer();
+  auto* players = impl->GetPlayersForTesting();
+  EXPECT_EQ(players->size(), 1u);
+  EXPECT_TRUE(players->at(id)->errors.IsEmpty());
+  EXPECT_TRUE(players->at(id)->events.IsEmpty());
+  EXPECT_TRUE(players->at(id)->messages.IsEmpty());
+  EXPECT_TRUE(players->at(id)->properties.IsEmpty());
+
+  impl->NotifyPlayerEvents(id, MakeEvents(10));
+  EXPECT_EQ(players->at(id)->events.size(), wtf_size_t{10});
+}
+
+TEST_F(InspectorMediaContextImplTest, KillsPlayersInCorrectOrder) {
+  auto alive_player_id = impl->CreatePlayer();
+  auto expendable_player_id = impl->CreatePlayer();
+  // Also marks the alive / expendable players as sent.
+  ASSERT_EQ(impl->AllPlayerIdsAndMarkSent().size(), wtf_size_t{2});
+
+  // These are created, but unsent.
+  auto dead_player_id = impl->CreatePlayer();
+  auto unsent_player_id = impl->CreatePlayer();
+
+  // check that there are 4.
+  EXPECT_EQ(impl->GetPlayersForTesting()->size(), wtf_size_t{4});
+
+  // mark these as dead to get them into their respective states.
+  impl->DestroyPlayer(dead_player_id);
+  impl->DestroyPlayer(expendable_player_id);
+
+  // check that there are still 4.
+  EXPECT_EQ(impl->GetPlayersForTesting()->size(), wtf_size_t{4});
+
+  // Almost fill up the total cache size.
+  impl->NotifyPlayerEvents(dead_player_id, MakeEvents(10));
+  impl->NotifyPlayerEvents(unsent_player_id, MakeEvents(10));
+  impl->NotifyPlayerEvents(expendable_player_id, MakeEvents(10));
+  impl->NotifyPlayerEvents(alive_player_id,
+                           MakeEvents(kMaxCachedPlayerEvents - 32));
+
+  EXPECT_EQ(impl->GetTotalEventCountForTesting(), kMaxCachedPlayerEvents - 2);
+  EXPECT_EQ(impl->GetPlayersForTesting()->size(), wtf_size_t{4});
+
+  // If we keep adding events to the alive player in groups of 10, it should
+  // delete the other players in the order: dead, expendable, unsent.
+  impl->NotifyPlayerEvents(alive_player_id, MakeEvents(10));
+
+  // The number of events remains unchanged, players at 3, and no dead id.
+  EXPECT_EQ(impl->GetTotalEventCountForTesting(), kMaxCachedPlayerEvents - 2);
+  EXPECT_EQ(impl->GetPlayersForTesting()->size(), wtf_size_t{3});
+  EXPECT_FALSE(impl->GetPlayersForTesting()->Contains(dead_player_id));
+
+  // Kill the expendable player.
+  impl->NotifyPlayerEvents(alive_player_id, MakeEvents(10));
+
+  // The number of events remains unchanged, players at 2, and no expendable id.
+  EXPECT_EQ(impl->GetTotalEventCountForTesting(), kMaxCachedPlayerEvents - 2);
+  EXPECT_EQ(impl->GetPlayersForTesting()->size(), wtf_size_t{2});
+  EXPECT_FALSE(impl->GetPlayersForTesting()->Contains(expendable_player_id));
+
+  // Kill the unsent player.
+  impl->NotifyPlayerEvents(alive_player_id, MakeEvents(10));
+
+  // The number of events remains unchanged, players at 1, and no unsent id.
+  EXPECT_EQ(impl->GetTotalEventCountForTesting(), kMaxCachedPlayerEvents - 2);
+  EXPECT_EQ(impl->GetPlayersForTesting()->size(), wtf_size_t{1});
+  EXPECT_FALSE(impl->GetPlayersForTesting()->Contains(unsent_player_id));
+
+  // Overflow the the cache and start trimming events.
+  impl->NotifyPlayerEvents(alive_player_id, MakeEvents(10));
+
+  // The number of events remains unchanged, players at 1, and no unsent id.
+  EXPECT_EQ(impl->GetTotalEventCountForTesting(), kMaxCachedPlayerEvents);
+  EXPECT_EQ(impl->GetPlayersForTesting()->size(), wtf_size_t{1});
+  EXPECT_TRUE(impl->GetPlayersForTesting()->Contains(alive_player_id));
+}
+
+TEST_F(InspectorMediaContextImplTest, OkToSendForDeadPlayers) {
+  auto player_1 = impl->CreatePlayer();
+  auto player_2 = impl->CreatePlayer();
+  ASSERT_EQ(impl->AllPlayerIdsAndMarkSent().size(), wtf_size_t{2});
+
+  // This should evict player1.
+  impl->NotifyPlayerEvents(player_1, MakeEvents(kMaxCachedPlayerEvents - 1));
+  impl->NotifyPlayerEvents(player_2, MakeEvents(10));
+  EXPECT_EQ(impl->GetPlayersForTesting()->size(), wtf_size_t{1});
+  EXPECT_FALSE(impl->GetPlayersForTesting()->Contains(player_1));
+
+  // Sending events to an evicted player shouldn't cause the cache size to
+  // increase, or any new evictions to happen.
+  impl->NotifyPlayerEvents(player_1, MakeEvents(kMaxCachedPlayerEvents - 1));
+  EXPECT_EQ(impl->GetPlayersForTesting()->size(), wtf_size_t{1});
+  EXPECT_FALSE(impl->GetPlayersForTesting()->Contains(player_1));
+}
+
+TEST_F(InspectorMediaContextImplTest, TrimLastRemainingPlayer) {
+  auto player_1 = impl->CreatePlayer();
+  ASSERT_EQ(impl->AllPlayerIdsAndMarkSent().size(), wtf_size_t{1});
+
+  impl->NotifyPlayerEvents(player_1, MakeEvents(kMaxCachedPlayerEvents - 1));
+  impl->NotifyPlayerEvents(player_1, MakeEvents(kMaxCachedPlayerEvents - 1));
+  EXPECT_EQ(impl->GetPlayersForTesting()->size(), wtf_size_t{1});
+  EXPECT_TRUE(impl->GetPlayersForTesting()->Contains(player_1));
+  EXPECT_EQ(impl->GetTotalEventCountForTesting(), kMaxCachedPlayerEvents);
+}
+
+}  // namespace
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/inspector/inspector_trace_events.cc b/third_party/blink/renderer/core/inspector/inspector_trace_events.cc
index 9d8847c..a588a2e 100644
--- a/third_party/blink/renderer/core/inspector/inspector_trace_events.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_trace_events.cc
@@ -719,6 +719,7 @@
 const char kChildChanged[] = "Child changed";
 const char kListValueChange[] = "List value change";
 const char kListStyleTypeChange[] = "List style type change";
+const char kCounterStyleChange[] = "Counter style change";
 const char kImageChanged[] = "Image changed";
 const char kLineBoxesChanged[] = "Line boxes changed";
 const char kSliderValueChanged[] = "Slider value changed";
diff --git a/third_party/blink/renderer/core/inspector/inspector_trace_events.h b/third_party/blink/renderer/core/inspector/inspector_trace_events.h
index 757e45a..a9b75306 100644
--- a/third_party/blink/renderer/core/inspector/inspector_trace_events.h
+++ b/third_party/blink/renderer/core/inspector/inspector_trace_events.h
@@ -240,6 +240,7 @@
 extern const char kChildChanged[];
 extern const char kListValueChange[];
 extern const char kListStyleTypeChange[];
+extern const char kCounterStyleChange[];
 extern const char kImageChanged[];
 extern const char kLineBoxesChanged[];
 extern const char kSliderValueChanged[];
diff --git a/third_party/blink/renderer/core/layout/layout_box.cc b/third_party/blink/renderer/core/layout/layout_box.cc
index 6c47f590..ccab445 100644
--- a/third_party/blink/renderer/core/layout/layout_box.cc
+++ b/third_party/blink/renderer/core/layout/layout_box.cc
@@ -336,9 +336,12 @@
   if (new_fragment.HasItems())
     DCHECK(box.ChildrenInline());
 
+  wtf_size_t index = 0;
   for (const NGPhysicalBoxFragment& fragment : box.PhysicalFragments()) {
+    DCHECK_EQ(fragment.IsFirstForNode(), index == 0);
     if (const NGFragmentItems* fragment_items = fragment.Items())
       fragment_items->CheckAllItemsAreValid();
+    ++index;
   }
 }
 #else
@@ -3160,6 +3163,22 @@
   return false;
 }
 
+wtf_size_t LayoutBox::NGPhysicalFragmentList::IndexOf(
+    const NGPhysicalBoxFragment& fragment) const {
+  wtf_size_t index = 0;
+  for (const scoped_refptr<const NGLayoutResult>& result : layout_results_) {
+    if (&result->PhysicalFragment() == &fragment)
+      return index;
+    ++index;
+  }
+  return kNotFound;
+}
+
+bool LayoutBox::NGPhysicalFragmentList::Contains(
+    const NGPhysicalBoxFragment& fragment) const {
+  return IndexOf(fragment) != kNotFound;
+}
+
 void LayoutBox::SetCachedLayoutResult(
     scoped_refptr<const NGLayoutResult> result) {
   NOT_DESTROYED();
@@ -3198,7 +3217,11 @@
     return;
   }
 
-  DCHECK_EQ(index, layout_results_.size());
+  DCHECK(index == layout_results_.size() || index == kNotFound);
+  AddLayoutResult(std::move(result));
+}
+
+void LayoutBox::AddLayoutResult(scoped_refptr<const NGLayoutResult> result) {
   const auto& fragment = To<NGPhysicalBoxFragment>(result->PhysicalFragment());
   layout_results_.push_back(std::move(result));
   CheckDidAddFragment(*this, fragment);
@@ -3244,6 +3267,21 @@
     NGFragmentItems::FinalizeAfterLayout(layout_results_);
 }
 
+void LayoutBox::ReplaceLayoutResult(scoped_refptr<const NGLayoutResult> result,
+                                    const NGPhysicalBoxFragment& old_fragment) {
+  DCHECK_EQ(this, old_fragment.OwnerLayoutBox());
+  DCHECK_EQ(result->PhysicalFragment().GetSelfOrContainerLayoutObject(),
+            old_fragment.GetSelfOrContainerLayoutObject());
+  // TODO(kojii): |IndexOf| is O(n). Consider if we can avoid this.
+  const wtf_size_t index = PhysicalFragments().IndexOf(old_fragment);
+  if (index != kNotFound) {
+    ReplaceLayoutResult(std::move(result), index);
+    return;
+  }
+  NOTREACHED();
+  AddLayoutResult(std::move(result));
+}
+
 void LayoutBox::ClearLayoutResults() {
   NOT_DESTROYED();
   if (measure_result_)
diff --git a/third_party/blink/renderer/core/layout/layout_box.h b/third_party/blink/renderer/core/layout/layout_box.h
index 2f7a56d..b7bdc87 100644
--- a/third_party/blink/renderer/core/layout/layout_box.h
+++ b/third_party/blink/renderer/core/layout/layout_box.h
@@ -1183,8 +1183,11 @@
   // Store one layout result (with its physical fragment) at the specified
   // index, and delete all entries following it.
   void AddLayoutResult(scoped_refptr<const NGLayoutResult>, wtf_size_t index);
+  void AddLayoutResult(scoped_refptr<const NGLayoutResult>);
   void ReplaceLayoutResult(scoped_refptr<const NGLayoutResult>,
                            wtf_size_t index);
+  void ReplaceLayoutResult(scoped_refptr<const NGLayoutResult>,
+                           const NGPhysicalBoxFragment& old_fragment);
 
   void ShrinkLayoutResults(wtf_size_t results_to_keep);
   void ClearLayoutResults();
@@ -1222,6 +1225,9 @@
 
     bool HasFragmentItems() const;
 
+    wtf_size_t IndexOf(const NGPhysicalBoxFragment& fragment) const;
+    bool Contains(const NGPhysicalBoxFragment& fragment) const;
+
     class CORE_EXPORT Iterator : public std::iterator<std::forward_iterator_tag,
                                                       NGPhysicalBoxFragment> {
      public:
diff --git a/third_party/blink/renderer/core/layout/layout_counter.cc b/third_party/blink/renderer/core/layout/layout_counter.cc
index 67164bb..e3738559 100644
--- a/third_party/blink/renderer/core/layout/layout_counter.cc
+++ b/third_party/blink/renderer/core/layout/layout_counter.cc
@@ -674,7 +674,7 @@
   maps.erase(maps_iterator);
   owner.SetHasCounterNodeMap(false);
   if (owner.View())
-    owner.View()->SetNeedsCounterUpdate();
+    owner.View()->SetNeedsMarkerOrCounterUpdate();
 }
 
 void LayoutCounter::DestroyCounterNode(LayoutObject& owner,
diff --git a/third_party/blink/renderer/core/layout/layout_list_item.cc b/third_party/blink/renderer/core/layout/layout_list_item.cc
index 027b7ef..b8bb7ecc 100644
--- a/third_party/blink/renderer/core/layout/layout_list_item.cc
+++ b/third_party/blink/renderer/core/layout/layout_list_item.cc
@@ -28,6 +28,7 @@
 #include "third_party/blink/renderer/core/html/html_olist_element.h"
 #include "third_party/blink/renderer/core/layout/layout_list_marker.h"
 #include "third_party/blink/renderer/core/layout/layout_outside_list_marker.h"
+#include "third_party/blink/renderer/core/layout/layout_view.h"
 #include "third_party/blink/renderer/core/layout/list_marker.h"
 #include "third_party/blink/renderer/core/paint/list_item_painter.h"
 #include "third_party/blink/renderer/core/paint/paint_layer.h"
@@ -43,6 +44,14 @@
 
   SetConsumesSubtreeChangeNotification();
   RegisterSubtreeChangeListenerOnDescendants(true);
+  View()->AddLayoutListItem();
+}
+
+void LayoutListItem::WillBeDestroyed() {
+  NOT_DESTROYED();
+  if (View())
+    View()->RemoveLayoutListItem();
+  LayoutBlockFlow::WillBeDestroyed();
 }
 
 void LayoutListItem::StyleDidChange(StyleDifference diff,
@@ -85,6 +94,27 @@
   }
 }
 
+void LayoutListItem::UpdateCounterStyle() {
+  NOT_DESTROYED();
+
+  if (!StyleRef().GetListStyleType() ||
+      StyleRef().GetListStyleType()->IsCounterStyleReferenceValid(
+          GetDocument())) {
+    return;
+  }
+
+  LayoutObject* marker = Marker();
+  if (!marker)
+    return;
+
+  if (auto* legacy_marker = DynamicTo<LayoutListMarker>(marker)) {
+    legacy_marker->CounterStyleChanged();
+    return;
+  }
+
+  ListMarker::Get(marker)->CounterStyleChanged(*marker);
+}
+
 void LayoutListItem::InsertedIntoTree() {
   NOT_DESTROYED();
   LayoutBlockFlow::InsertedIntoTree();
diff --git a/third_party/blink/renderer/core/layout/layout_list_item.h b/third_party/blink/renderer/core/layout/layout_list_item.h
index a7fdeeaa..d5e56c3 100644
--- a/third_party/blink/renderer/core/layout/layout_list_item.h
+++ b/third_party/blink/renderer/core/layout/layout_list_item.h
@@ -58,6 +58,8 @@
 
   void UpdateMarkerTextIfNeeded();
 
+  void UpdateCounterStyle();
+
  private:
   bool IsOfType(LayoutObjectType type) const override {
     NOT_DESTROYED();
@@ -82,6 +84,8 @@
 
   void AddLayoutOverflowFromChildren() override;
 
+  void WillBeDestroyed() override;
+
   void AlignMarkerInBlockDirection();
 
   bool PrepareForBlockDirectionAlign(const LayoutObject*);
diff --git a/third_party/blink/renderer/core/layout/layout_list_marker.cc b/third_party/blink/renderer/core/layout/layout_list_marker.cc
index e226c75..4cbdc40 100644
--- a/third_party/blink/renderer/core/layout/layout_list_marker.cc
+++ b/third_party/blink/renderer/core/layout/layout_list_marker.cc
@@ -87,6 +87,14 @@
       layout_invalidation_reason::kListStyleTypeChange);
 }
 
+void LayoutListMarker::CounterStyleChanged() {
+  NOT_DESTROYED();
+  if (IsImage())
+    return;
+  SetNeedsLayoutAndIntrinsicWidthsRecalcAndFullPaintInvalidation(
+      layout_invalidation_reason::kCounterStyleChange);
+}
+
 void LayoutListMarker::UpdateMarkerImageIfNeeded(StyleImage* image) {
   NOT_DESTROYED();
   if (image_ != image) {
@@ -346,8 +354,7 @@
   const ListStyleTypeData* list_style_data = StyleRef().GetListStyleType();
   DCHECK(list_style_data);
   DCHECK(list_style_data->IsCounterStyle());
-  return GetDocument().GetStyleEngine().FindCounterStyleAcrossScopes(
-      list_style_data->GetCounterStyleName(), list_style_data->GetTreeScope());
+  return list_style_data->GetCounterStyle(GetDocument());
 }
 
 bool LayoutListMarker::IsInside() const {
diff --git a/third_party/blink/renderer/core/layout/layout_list_marker.h b/third_party/blink/renderer/core/layout/layout_list_marker.h
index 5104a2b..6042c09 100644
--- a/third_party/blink/renderer/core/layout/layout_list_marker.h
+++ b/third_party/blink/renderer/core/layout/layout_list_marker.h
@@ -117,6 +117,7 @@
 
   void UpdateMarkerImageIfNeeded(StyleImage* image);
   void ListStyleTypeChanged();
+  void CounterStyleChanged();
 
   String text_;
   Persistent<StyleImage> image_;
diff --git a/third_party/blink/renderer/core/layout/layout_object.cc b/third_party/blink/renderer/core/layout/layout_object.cc
index 96153f3..f446fdc 100644
--- a/third_party/blink/renderer/core/layout/layout_object.cc
+++ b/third_party/blink/renderer/core/layout/layout_object.cc
@@ -3027,7 +3027,7 @@
       return;
   }
   LayoutCounter::LayoutObjectStyleChanged(*this, old_style, *new_style);
-  View()->SetNeedsCounterUpdate();
+  View()->SetNeedsMarkerOrCounterUpdate();
 }
 
 PhysicalRect LayoutObject::ViewRect() const {
diff --git a/third_party/blink/renderer/core/layout/layout_view.cc b/third_party/blink/renderer/core/layout/layout_view.cc
index c21e406..74d67fa 100644
--- a/third_party/blink/renderer/core/layout/layout_view.cc
+++ b/third_party/blink/renderer/core/layout/layout_view.cc
@@ -43,6 +43,8 @@
 #include "third_party/blink/renderer/core/layout/layout_counter.h"
 #include "third_party/blink/renderer/core/layout/layout_embedded_content.h"
 #include "third_party/blink/renderer/core/layout/layout_geometry_map.h"
+#include "third_party/blink/renderer/core/layout/layout_list_item.h"
+#include "third_party/blink/renderer/core/layout/ng/list/layout_ng_list_item.h"
 #include "third_party/blink/renderer/core/layout/svg/layout_svg_root.h"
 #include "third_party/blink/renderer/core/layout/view_fragmentation_context.h"
 #include "third_party/blink/renderer/core/page/chrome_client.h"
@@ -956,19 +958,25 @@
   return CompositingReason::kNone;
 }
 
-void LayoutView::UpdateCounters() {
+void LayoutView::UpdateMarkersAndCountersAfterStyleChange() {
   NOT_DESTROYED();
-  if (!needs_counter_update_)
+  if (!needs_marker_counter_update_)
     return;
 
-  needs_counter_update_ = false;
-  if (!HasLayoutCounters())
+  needs_marker_counter_update_ = false;
+  if (!HasLayoutCounters() && !HasLayoutListItems())
     return;
 
   for (LayoutObject* layout_object = this; layout_object;
        layout_object = layout_object->NextInPreOrder()) {
-    if (auto* counter = DynamicTo<LayoutCounter>(layout_object))
+    if (auto* list_item = DynamicTo<LayoutListItem>(layout_object)) {
+      list_item->UpdateCounterStyle();
+    } else if (auto* ng_list_item =
+                   DynamicTo<LayoutNGListItem>(layout_object)) {
+      ng_list_item->UpdateCounterStyle();
+    } else if (auto* counter = DynamicTo<LayoutCounter>(layout_object)) {
       counter->UpdateCounter();
+    }
   }
 }
 
diff --git a/third_party/blink/renderer/core/layout/layout_view.h b/third_party/blink/renderer/core/layout/layout_view.h
index 51d607f..a8b21ba 100644
--- a/third_party/blink/renderer/core/layout/layout_view.h
+++ b/third_party/blink/renderer/core/layout/layout_view.h
@@ -246,11 +246,12 @@
   // FIXME: This is a work around because the current implementation of counters
   // requires walking the entire tree repeatedly and most pages don't actually
   // use either feature so we shouldn't take the performance hit when not
-  // needed. Long term we should rewrite the counter and quotes code.
+  // needed. Long term we should rewrite the counter code.
+  // TODO(xiaochengh): Or do we keep it as is?
   void AddLayoutCounter() {
     NOT_DESTROYED();
     layout_counter_count_++;
-    SetNeedsCounterUpdate();
+    SetNeedsMarkerOrCounterUpdate();
   }
   void RemoveLayoutCounter() {
     NOT_DESTROYED();
@@ -261,11 +262,25 @@
     NOT_DESTROYED();
     return layout_counter_count_;
   }
-  void SetNeedsCounterUpdate() {
+  void AddLayoutListItem() {
     NOT_DESTROYED();
-    needs_counter_update_ = true;
+    layout_list_item_count_++;
+    SetNeedsMarkerOrCounterUpdate();
   }
-  void UpdateCounters();
+  void RemoveLayoutListItem() {
+    NOT_DESTROYED();
+    DCHECK_GT(layout_list_item_count_, 0u);
+    layout_list_item_count_--;
+  }
+  bool HasLayoutListItems() {
+    NOT_DESTROYED();
+    return layout_list_item_count_;
+  }
+  void SetNeedsMarkerOrCounterUpdate() {
+    NOT_DESTROYED();
+    needs_marker_counter_update_ = true;
+  }
+  void UpdateMarkersAndCountersAfterStyleChange();
 
   bool BackgroundIsKnownToBeOpaqueInRect(
       const PhysicalRect& local_rect) const override;
@@ -378,8 +393,9 @@
   scoped_refptr<IntervalArena> interval_arena_;
 
   LayoutQuote* layout_quote_head_;
-  unsigned layout_counter_count_;
-  bool needs_counter_update_ = false;
+  unsigned layout_counter_count_ = 0;
+  unsigned layout_list_item_count_ = 0;
+  bool needs_marker_counter_update_ = false;
 
   unsigned hit_test_count_;
   unsigned hit_test_cache_hits_;
diff --git a/third_party/blink/renderer/core/layout/list_marker.cc b/third_party/blink/renderer/core/layout/list_marker.cc
index 7ab6db2a..c1c7be0d 100644
--- a/third_party/blink/renderer/core/layout/list_marker.cc
+++ b/third_party/blink/renderer/core/layout/list_marker.cc
@@ -93,8 +93,7 @@
   return 0;
 }
 
-// If the value of ListStyleType changed, we need to the marker text has been
-// updated.
+// If the value of ListStyleType changed, we need to update the marker text.
 void ListMarker::ListStyleTypeChanged(LayoutObject& marker) {
   DCHECK_EQ(Get(&marker), this);
   if (marker_text_type_ == kNotText || marker_text_type_ == kUnresolved)
@@ -105,6 +104,17 @@
       layout_invalidation_reason::kListStyleTypeChange);
 }
 
+// If the @counter-style in use has changed, we need to update the marker text.
+void ListMarker::CounterStyleChanged(LayoutObject& marker) {
+  DCHECK_EQ(Get(&marker), this);
+  if (marker_text_type_ == kNotText || marker_text_type_ == kUnresolved)
+    return;
+
+  marker_text_type_ = kUnresolved;
+  marker.SetNeedsLayoutAndIntrinsicWidthsRecalcAndFullPaintInvalidation(
+      layout_invalidation_reason::kCounterStyleChange);
+}
+
 void ListMarker::OrdinalValueChanged(LayoutObject& marker) {
   DCHECK_EQ(Get(&marker), this);
   if (marker_text_type_ == kOrdinalValue) {
@@ -420,9 +430,7 @@
   DCHECK(RuntimeEnabledFeatures::CSSAtRuleCounterStyleEnabled());
   DCHECK(style.GetListStyleType());
   DCHECK(style.GetListStyleType()->IsCounterStyle());
-  const ListStyleTypeData& list_style_data = *style.GetListStyleType();
-  return document.GetStyleEngine().FindCounterStyleAcrossScopes(
-      list_style_data.GetCounterStyleName(), list_style_data.GetTreeScope());
+  return style.GetListStyleType()->GetCounterStyle(document);
 }
 
 ListMarker::ListStyleCategory ListMarker::GetListStyleCategory(
diff --git a/third_party/blink/renderer/core/layout/list_marker.h b/third_party/blink/renderer/core/layout/list_marker.h
index 1f3fd85..e9ed1b3 100644
--- a/third_party/blink/renderer/core/layout/list_marker.h
+++ b/third_party/blink/renderer/core/layout/list_marker.h
@@ -94,6 +94,7 @@
 
   void ListStyleTypeChanged(LayoutObject&);
   void OrdinalValueChanged(LayoutObject&);
+  void CounterStyleChanged(LayoutObject&);
 
   int ListItemValue(const LayoutObject&) const;
 
diff --git a/third_party/blink/renderer/core/layout/list_marker_test.cc b/third_party/blink/renderer/core/layout/list_marker_test.cc
new file mode 100644
index 0000000..aa70c9a
--- /dev/null
+++ b/third_party/blink/renderer/core/layout/list_marker_test.cc
@@ -0,0 +1,312 @@
+// 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 "third_party/blink/renderer/core/layout/list_marker.h"
+
+#include "third_party/blink/renderer/core/layout/ng/list/layout_ng_list_item.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_layout_test.h"
+
+namespace blink {
+
+// We don't test legacy layout because it's deprecated, and we don't want to
+// complicate the test with the legacy LayoutListMarker here.
+class ListMarkerTest : public NGLayoutTest {
+ protected:
+  LayoutObject* GetMarker(const char* list_item_id) {
+    LayoutNGListItem* list_item =
+        To<LayoutNGListItem>(GetLayoutObjectByElementId(list_item_id));
+    return list_item->Marker();
+  }
+
+  LayoutObject* GetMarker(TreeScope& scope, const char* list_item_id) {
+    Element* list_item = scope.getElementById(list_item_id);
+    return To<LayoutNGListItem>(list_item->GetLayoutObject())->Marker();
+  }
+
+  String GetMarkerText(TreeScope& scope, const char* list_item_id) {
+    return To<LayoutText>(GetMarker(scope, list_item_id)->SlowFirstChild())
+        ->GetText();
+  }
+
+  String GetMarkerText(const char* list_item_id) {
+    return GetMarkerText(GetDocument(), list_item_id);
+  }
+
+  void AddCounterStyle(const AtomicString& name, const String& descriptors) {
+    StringBuilder declaration;
+    declaration.Append("@counter-style ");
+    declaration.Append(name);
+    declaration.Append("{");
+    declaration.Append(descriptors);
+    declaration.Append("}");
+    Element* sheet = GetDocument().CreateElementForBinding("style");
+    sheet->setInnerHTML(declaration.ToString());
+    GetDocument().body()->appendChild(sheet);
+  }
+};
+
+TEST_F(ListMarkerTest, AddCounterStyle) {
+  GetDocument().body()->setInnerHTML(R"HTML(
+    <style>
+      @counter-style foo {
+        system: fixed;
+        symbols: W X Y Z;
+      }
+    </style>
+    <ol>
+      <li id="decimal" style="list-style-type: decimal"></li>
+      <li id="foo" style="list-style-type: foo"></li>
+      <li id="bar" style="list-style-type: bar"></li>
+    </ol>
+  )HTML");
+  UpdateAllLifecyclePhasesForTest();
+
+  EXPECT_EQ("1. ", GetMarkerText("decimal"));
+  EXPECT_EQ("X. ", GetMarkerText("foo"));
+  EXPECT_EQ("3. ", GetMarkerText("bar"));
+
+  // Add @counter-style 'bar'. Should not affect 'decimal' and 'foo'.
+  AddCounterStyle("bar", "system: fixed; symbols: A B C;");
+  GetDocument().UpdateStyleAndLayoutTree();
+
+  EXPECT_FALSE(GetMarker("decimal")->NeedsLayout());
+  EXPECT_FALSE(GetMarker("foo")->NeedsLayout());
+  EXPECT_TRUE(GetMarker("bar")->NeedsLayout());
+
+  UpdateAllLifecyclePhasesForTest();
+  EXPECT_EQ("1. ", GetMarkerText("decimal"));
+  EXPECT_EQ("X. ", GetMarkerText("foo"));
+  EXPECT_EQ("C. ", GetMarkerText("bar"));
+}
+
+TEST_F(ListMarkerTest, RemoveCounterStyle) {
+  GetDocument().body()->setInnerHTML(R"HTML(
+    <style id="foo-sheet">
+      @counter-style foo {
+        system: fixed;
+        symbols: W X Y Z;
+      }
+    </style>
+    <ol>
+      <li id="decimal" style="list-style-type: decimal"></li>
+      <li id="foo" style="list-style-type: foo"></li>
+    </ol>
+  )HTML");
+  UpdateAllLifecyclePhasesForTest();
+
+  EXPECT_EQ("1. ", GetMarkerText("decimal"));
+  EXPECT_EQ("X. ", GetMarkerText("foo"));
+
+  // Remove @counter-style 'foo'. Should not affect 'decimal'.
+  GetElementById("foo-sheet")->remove();
+  GetDocument().UpdateStyleAndLayoutTree();
+
+  EXPECT_FALSE(GetMarker("decimal")->NeedsLayout());
+  EXPECT_TRUE(GetMarker("foo")->NeedsLayout());
+
+  UpdateAllLifecyclePhasesForTest();
+  EXPECT_EQ("1. ", GetMarkerText("decimal"));
+  EXPECT_EQ("2. ", GetMarkerText("foo"));
+}
+
+TEST_F(ListMarkerTest, OverridePredefinedCounterStyle) {
+  GetDocument().body()->setInnerHTML(R"HTML(
+    <ol>
+      <li id="decimal" style="list-style-type: decimal"></li>
+      <li id="upper-roman" style="list-style-type: upper-roman"></li>
+    </ol>
+  )HTML");
+  UpdateAllLifecyclePhasesForTest();
+
+  EXPECT_EQ("1. ", GetMarkerText("decimal"));
+  EXPECT_EQ("II. ", GetMarkerText("upper-roman"));
+
+  // Override 'upper-roman'. Should not affect 'decimal'.
+  AddCounterStyle("upper-roman", "system: fixed; symbols: A B C;");
+  GetDocument().UpdateStyleAndLayoutTree();
+
+  EXPECT_FALSE(GetMarker("decimal")->NeedsLayout());
+  EXPECT_TRUE(GetMarker("upper-roman")->NeedsLayout());
+
+  UpdateAllLifecyclePhasesForTest();
+  EXPECT_EQ("1. ", GetMarkerText("decimal"));
+  EXPECT_EQ("B. ", GetMarkerText("upper-roman"));
+}
+
+TEST_F(ListMarkerTest, RemoveOverrideOfPredefinedCounterStyle) {
+  GetDocument().body()->setInnerHTML(R"HTML(
+    <style id="to-remove">
+      @counter-style upper-roman {
+        system: fixed;
+        symbols: A B C;
+      }
+    </style>
+    <ol>
+      <li id="decimal" style="list-style-type: decimal"></li>
+      <li id="upper-roman" style="list-style-type: upper-roman"></li>
+    </ol>
+  )HTML");
+  UpdateAllLifecyclePhasesForTest();
+
+  EXPECT_EQ("1. ", GetMarkerText("decimal"));
+  EXPECT_EQ("B. ", GetMarkerText("upper-roman"));
+
+  // Remove override of 'upper-roman'. Should not affect 'decimal'.
+  GetElementById("to-remove")->remove();
+  GetDocument().UpdateStyleAndLayoutTree();
+
+  EXPECT_FALSE(GetMarker("decimal")->NeedsLayout());
+  EXPECT_TRUE(GetMarker("upper-roman")->NeedsLayout());
+
+  UpdateAllLifecyclePhasesForTest();
+  EXPECT_EQ("1. ", GetMarkerText("decimal"));
+  EXPECT_EQ("II. ", GetMarkerText("upper-roman"));
+}
+
+TEST_F(ListMarkerTest, OverrideSameScopeCounterStyle) {
+  GetDocument().body()->setInnerHTML(R"HTML(
+    <style>
+      @counter-style foo {
+        system: fixed;
+        symbols: W X Y Z;
+      }
+    </style>
+    <ol>
+      <li id="decimal" style="list-style-type: decimal"></li>
+      <li id="foo" style="list-style-type: foo"></li>
+    </ol>
+  )HTML");
+  UpdateAllLifecyclePhasesForTest();
+
+  EXPECT_EQ("1. ", GetMarkerText("decimal"));
+  EXPECT_EQ("X. ", GetMarkerText("foo"));
+
+  // Override 'foo'. Should not affect 'decimal'.
+  AddCounterStyle("foo", "system: fixed; symbols: A B C;");
+  GetDocument().UpdateStyleAndLayoutTree();
+
+  EXPECT_FALSE(GetMarker("decimal")->NeedsLayout());
+  EXPECT_TRUE(GetMarker("foo")->NeedsLayout());
+
+  UpdateAllLifecyclePhasesForTest();
+  EXPECT_EQ("1. ", GetMarkerText("decimal"));
+  EXPECT_EQ("B. ", GetMarkerText("foo"));
+}
+
+TEST_F(ListMarkerTest, RemoveOverrideOfSameScopeCounterStyle) {
+  GetDocument().body()->setInnerHTML(R"HTML(
+    <style>
+      @counter-style foo {
+        system: fixed;
+        symbols: W X Y Z;
+      }
+    </style>
+    <style id="to-remove">
+      @counter-style foo {
+        system: fixed;
+        symbols: A B C;
+      }
+    </style>
+    <ol>
+      <li id="decimal" style="list-style-type: decimal"></li>
+      <li id="foo" style="list-style-type: foo"></li>
+    </ol>
+  )HTML");
+  UpdateAllLifecyclePhasesForTest();
+
+  EXPECT_EQ("1. ", GetMarkerText("decimal"));
+  EXPECT_EQ("B. ", GetMarkerText("foo"));
+
+  // Remove the override of 'foo'. Should not affect 'decimal'.
+  GetElementById("to-remove")->remove();
+  GetDocument().UpdateStyleAndLayoutTree();
+
+  EXPECT_FALSE(GetMarker("decimal")->NeedsLayout());
+  EXPECT_TRUE(GetMarker("foo")->NeedsLayout());
+
+  UpdateAllLifecyclePhasesForTest();
+  EXPECT_EQ("1. ", GetMarkerText("decimal"));
+  EXPECT_EQ("X. ", GetMarkerText("foo"));
+}
+
+TEST_F(ListMarkerTest, ModifyShadowDOMWithOwnCounterStyles) {
+  GetDocument().body()->setInnerHTML(R"HTML(
+    <style>
+      @counter-style foo {
+        system: fixed;
+        symbols: W X Y Z;
+      }
+    </style>
+    <ol>
+      <li id="decimal" style="list-style-type: decimal"></li>
+      <li id="foo" style="list-style-type: foo"></li>
+    </ol>
+    <div id="host1"></div>
+    <div id="host2"></div>
+  )HTML");
+  UpdateAllLifecyclePhasesForTest();
+
+  EXPECT_EQ("1. ", GetMarkerText("decimal"));
+  EXPECT_EQ("X. ", GetMarkerText("foo"));
+
+  // Attach a shadow tree with counter styles. Shouldn't affect anything outside
+  ShadowRoot& shadow1 =
+      GetElementById("host1")->AttachShadowRootInternal(ShadowRootType::kOpen);
+  shadow1.setInnerHTML(R"HTML(
+    <style>
+      @counter-style foo {
+        system: fixed;
+        symbols: A B C;
+      }
+    </style>
+    <ol>
+      <li id="shadow-foo" style="list-style-type: foo"></li>
+    </ol>
+  )HTML");
+  GetDocument().UpdateStyleAndLayoutTree();
+  EXPECT_FALSE(GetMarker("decimal")->NeedsLayout());
+  EXPECT_FALSE(GetMarker("foo")->NeedsLayout());
+  UpdateAllLifecyclePhasesForTest();
+  EXPECT_EQ("1. ", GetMarkerText("decimal"));
+  EXPECT_EQ("X. ", GetMarkerText("foo"));
+  EXPECT_EQ("A. ", GetMarkerText(shadow1, "shadow-foo"));
+
+  // Attach another shadow tree with counter styles. Shouldn't affect anything
+  // outside.
+  ShadowRoot& shadow2 =
+      GetElementById("host2")->AttachShadowRootInternal(ShadowRootType::kOpen);
+  shadow2.setInnerHTML(R"HTML(
+    <style>
+      @counter-style foo {
+        system: fixed;
+        symbols: D E F;
+      }
+    </style>
+    <ol>
+      <li id="shadow-foo" style="list-style-type: foo"></li>
+    </ol>
+  )HTML");
+  GetDocument().UpdateStyleAndLayoutTree();
+  EXPECT_FALSE(GetMarker("decimal")->NeedsLayout());
+  EXPECT_FALSE(GetMarker("foo")->NeedsLayout());
+  EXPECT_FALSE(GetMarker(shadow1, "shadow-foo")->NeedsLayout());
+  UpdateAllLifecyclePhasesForTest();
+  EXPECT_EQ("1. ", GetMarkerText("decimal"));
+  EXPECT_EQ("X. ", GetMarkerText("foo"));
+  EXPECT_EQ("A. ", GetMarkerText(shadow1, "shadow-foo"));
+  EXPECT_EQ("D. ", GetMarkerText(shadow2, "shadow-foo"));
+
+  // Remove one of the shadow trees. Shouldn't affect anything outside.
+  GetElementById("host1")->remove();
+  GetDocument().UpdateStyleAndLayoutTree();
+  EXPECT_FALSE(GetMarker("decimal")->NeedsLayout());
+  EXPECT_FALSE(GetMarker("foo")->NeedsLayout());
+  EXPECT_FALSE(GetMarker(shadow2, "shadow-foo")->NeedsLayout());
+  UpdateAllLifecyclePhasesForTest();
+  EXPECT_EQ("1. ", GetMarkerText("decimal"));
+  EXPECT_EQ("X. ", GetMarkerText("foo"));
+  EXPECT_EQ("D. ", GetMarkerText(shadow2, "shadow-foo"));
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/ng/list/layout_ng_list_item.cc b/third_party/blink/renderer/core/layout/ng/list/layout_ng_list_item.cc
index ff8111a..60eadd0e 100644
--- a/third_party/blink/renderer/core/layout/ng/list/layout_ng_list_item.cc
+++ b/third_party/blink/renderer/core/layout/ng/list/layout_ng_list_item.cc
@@ -4,6 +4,7 @@
 
 #include "third_party/blink/renderer/core/layout/ng/list/layout_ng_list_item.h"
 
+#include "third_party/blink/renderer/core/layout/layout_view.h"
 #include "third_party/blink/renderer/core/layout/list_marker.h"
 
 namespace blink {
@@ -14,6 +15,14 @@
 
   SetConsumesSubtreeChangeNotification();
   RegisterSubtreeChangeListenerOnDescendants(true);
+  View()->AddLayoutListItem();
+}
+
+void LayoutNGListItem::WillBeDestroyed() {
+  NOT_DESTROYED();
+  if (View())
+    View()->RemoveLayoutListItem();
+  LayoutNGBlockFlow::WillBeDestroyed();
 }
 
 bool LayoutNGListItem::IsOfType(LayoutObjectType type) const {
@@ -55,6 +64,21 @@
   }
 }
 
+void LayoutNGListItem::UpdateCounterStyle() {
+  if (!StyleRef().GetListStyleType() ||
+      StyleRef().GetListStyleType()->IsCounterStyleReferenceValid(
+          GetDocument())) {
+    return;
+  }
+
+  LayoutObject* marker = Marker();
+  ListMarker* list_marker = ListMarker::Get(marker);
+  if (!list_marker)
+    return;
+
+  list_marker->CounterStyleChanged(*marker);
+}
+
 void LayoutNGListItem::OrdinalValueChanged() {
   LayoutObject* marker = Marker();
   if (ListMarker* list_marker = ListMarker::Get(marker))
diff --git a/third_party/blink/renderer/core/layout/ng/list/layout_ng_list_item.h b/third_party/blink/renderer/core/layout/ng/list/layout_ng_list_item.h
index 02ddf45e..ebef26d1 100644
--- a/third_party/blink/renderer/core/layout/ng/list/layout_ng_list_item.h
+++ b/third_party/blink/renderer/core/layout/ng/list/layout_ng_list_item.h
@@ -26,6 +26,7 @@
   }
 
   void UpdateMarkerTextIfNeeded();
+  void UpdateCounterStyle();
 
   void OrdinalValueChanged();
   void WillCollectInlines() override;
@@ -41,6 +42,7 @@
   void WillBeRemovedFromTree() override;
   void StyleDidChange(StyleDifference, const ComputedStyle* old_style) override;
   void SubtreeDidChange() final;
+  void WillBeDestroyed() override;
 
   ListItemOrdinal ordinal_;
 };
diff --git a/third_party/blink/renderer/core/layout/ng/ng_block_node.cc b/third_party/blink/renderer/core/layout/ng/ng_block_node.cc
index 351ff7c7..1f7e9623 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_block_node.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_block_node.cc
@@ -1766,4 +1766,16 @@
   GetFlowThread(To<LayoutBlockFlow>(box_))->AddLayoutResult(result, index);
 }
 
+void NGBlockNode::AddColumnResult(
+    scoped_refptr<const NGLayoutResult> result) const {
+  GetFlowThread(To<LayoutBlockFlow>(box_))->AddLayoutResult(std::move(result));
+}
+
+void NGBlockNode::ReplaceColumnResult(
+    scoped_refptr<const NGLayoutResult> result,
+    const NGPhysicalBoxFragment& old_fragment) const {
+  GetFlowThread(To<LayoutBlockFlow>(box_))
+      ->ReplaceLayoutResult(std::move(result), old_fragment);
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/ng/ng_block_node.h b/third_party/blink/renderer/core/layout/ng/ng_block_node.h
index dd62a12..e16f9e3 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_block_node.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_block_node.h
@@ -176,6 +176,11 @@
   // somewhere.
   void AddColumnResult(scoped_refptr<const NGLayoutResult>,
                        const NGBlockBreakToken* incoming_break_token) const;
+  // Add a column layout result to this node.
+  void AddColumnResult(scoped_refptr<const NGLayoutResult>) const;
+  // Replace an existing column layout result with a new one.
+  void ReplaceColumnResult(scoped_refptr<const NGLayoutResult>,
+                           const NGPhysicalBoxFragment& old_fragment) const;
 
   static bool CanUseNewLayout(const LayoutBox&);
   bool CanUseNewLayout() const;
diff --git a/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc b/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc
index c084d4e..8c03202 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc
@@ -1225,11 +1225,14 @@
       offset = fragmentainer.offset;
       offset.inline_offset += column_inline_progression_;
     }
-    container_builder_->AddChild(algorithm.Layout()->PhysicalFragment(),
-                                 offset);
+    scoped_refptr<const NGLayoutResult> new_result = algorithm.Layout();
+    node.AddColumnResult(new_result);
+    container_builder_->AddChild(new_result->PhysicalFragment(), offset);
   } else {
-    container_builder_->ReplaceChild(
-        index, algorithm.Layout()->PhysicalFragment(), fragmentainer.offset);
+    scoped_refptr<const NGLayoutResult> new_result = algorithm.Layout();
+    node.ReplaceColumnResult(new_result, fragment);
+    container_builder_->ReplaceChild(index, new_result->PhysicalFragment(),
+                                     fragmentainer.offset);
   }
 }
 
diff --git a/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.cc b/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.cc
index 1503e220..4b9eb71 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.cc
@@ -375,6 +375,22 @@
   return builder.ToBoxFragment();
 }
 
+const LayoutBox* NGPhysicalBoxFragment::OwnerLayoutBox() const {
+  const LayoutBox* owner_box =
+      DynamicTo<LayoutBox>(GetSelfOrContainerLayoutObject());
+  DCHECK(owner_box);
+  if (UNLIKELY(IsColumnBox())) {
+    owner_box = To<LayoutBox>(owner_box->SlowFirstChild());
+    DCHECK(owner_box && owner_box->IsLayoutFlowThread());
+  }
+  DCHECK(owner_box->PhysicalFragments().Contains(*this));
+  return owner_box;
+}
+
+LayoutBox* NGPhysicalBoxFragment::MutableOwnerLayoutBox() const {
+  return const_cast<LayoutBox*>(OwnerLayoutBox());
+}
+
 const NGPhysicalBoxFragment* NGPhysicalBoxFragment::PostLayout() const {
   const auto* layout_object = GetSelfOrContainerLayoutObject();
   if (UNLIKELY(!layout_object)) {
diff --git a/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h b/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h
index b0356143..0d3f5b7 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h
@@ -206,6 +206,11 @@
     return is_inline_formatting_context_;
   }
 
+  // The |LayoutBox| whose |PhysicalFragments()| contains |this|. This is
+  // different from |GetLayoutObject()| if |this.IsColumnBox()|.
+  const LayoutBox* OwnerLayoutBox() const;
+  LayoutBox* MutableOwnerLayoutBox() const;
+
   PhysicalRect ScrollableOverflow(TextHeightType height_type) const;
   PhysicalRect ScrollableOverflowFromChildren(TextHeightType height_type) const;
 
diff --git a/third_party/blink/renderer/core/layout/ng/ng_simplified_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/ng_simplified_layout_algorithm.cc
index 036a8ce..7fcf63c0 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_simplified_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_simplified_layout_algorithm.cc
@@ -156,7 +156,11 @@
   } else {
     LayoutUnit old_block_size =
         NGFragment(writing_direction_, physical_fragment).BlockSize();
-    DCHECK_EQ(old_block_size, new_block_size);
+#if DCHECK_IS_ON()
+    // Tables don't respect the typical block-sizing rules.
+    if (!physical_fragment.IsTableNG())
+      DCHECK_EQ(old_block_size, new_block_size);
+#endif
     container_builder_.SetFragmentBlockSize(old_block_size);
   }
 
diff --git a/third_party/blink/renderer/core/layout/ng/ng_simplified_oof_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/ng_simplified_oof_layout_algorithm.cc
index aa04c58..4c0508e 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_simplified_oof_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_simplified_oof_layout_algorithm.cc
@@ -30,9 +30,12 @@
   if (is_new_fragment) {
     children_ = {};
     iterator_ = children_.end();
+    container_builder_.SetIsFirstForNode(false);
     return;
   }
 
+  container_builder_.SetIsFirstForNode(fragment.IsFirstForNode());
+
   // We need the previous physical container size to calculate the position of
   // any child fragments.
   previous_physical_container_size_ = fragment.Size();
diff --git a/third_party/blink/renderer/core/page/scrolling/root_scroller_controller.h b/third_party/blink/renderer/core/page/scrolling/root_scroller_controller.h
index dd5e111..adb661f 100644
--- a/third_party/blink/renderer/core/page/scrolling/root_scroller_controller.h
+++ b/third_party/blink/renderer/core/page/scrolling/root_scroller_controller.h
@@ -13,6 +13,7 @@
 class Document;
 class Element;
 class HTMLFrameOwnerElement;
+class Node;
 
 // Manages the root scroller associated with a given document. The root
 // scroller causes browser controls movement, overscroll effects and prevents
diff --git a/third_party/blink/renderer/core/paint/pre_paint_tree_walk.cc b/third_party/blink/renderer/core/paint/pre_paint_tree_walk.cc
index 529c167..44c8f9c 100644
--- a/third_party/blink/renderer/core/paint/pre_paint_tree_walk.cc
+++ b/third_party/blink/renderer/core/paint/pre_paint_tree_walk.cc
@@ -737,6 +737,13 @@
         containing_block_context = &context.current;
         containing_block_context->paint_offset += offset;
       }
+
+      // Check |box_fragment| and |LayoutBox| who produced it are in sync.
+      DCHECK(box_fragment->OwnerLayoutBox());
+      DCHECK_EQ(box_fragment->IsFirstForNode(),
+                box_fragment ==
+                    box_fragment->OwnerLayoutBox()->GetPhysicalFragment(0));
+
       WalkChildren(/* parent */ nullptr, iterator);
       if (containing_block_context)
         containing_block_context->paint_offset -= offset;
diff --git a/third_party/blink/renderer/core/style/computed_style.h b/third_party/blink/renderer/core/style/computed_style.h
index b570332..15516a5 100644
--- a/third_party/blink/renderer/core/style/computed_style.h
+++ b/third_party/blink/renderer/core/style/computed_style.h
@@ -2264,8 +2264,10 @@
 
   // Table layout utility functions.
   bool IsFixedTableLayout() const {
+    // https://www.w3.org/TR/css-tables-3/#table-layout-property
     return TableLayout() == ETableLayout::kFixed &&
-           !LogicalWidth().IsAutoOrContentOrIntrinsic();
+           (LogicalWidth().IsSpecified() || LogicalWidth().IsMinContent() ||
+            LogicalWidth().IsFitContent());
   }
 
   LogicalSize TableBorderSpacing() const {
diff --git a/third_party/blink/renderer/core/style/list_style_type_data.cc b/third_party/blink/renderer/core/style/list_style_type_data.cc
index 92f22d2..16c299e 100644
--- a/third_party/blink/renderer/core/style/list_style_type_data.cc
+++ b/third_party/blink/renderer/core/style/list_style_type_data.cc
@@ -4,7 +4,10 @@
 
 #include "third_party/blink/renderer/core/style/list_style_type_data.h"
 
+#include "third_party/blink/renderer/core/css/counter_style.h"
 #include "third_party/blink/renderer/core/css/css_value_id_mappings.h"
+#include "third_party/blink/renderer/core/css/style_engine.h"
+#include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/dom/tree_scope.h"
 #include "third_party/blink/renderer/core/style/content_data.h"
 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
@@ -44,6 +47,7 @@
 
 void ListStyleTypeData::Trace(Visitor* visitor) const {
   visitor->Trace(tree_scope_);
+  visitor->Trace(counter_style_);
 }
 
 // static
@@ -73,4 +77,31 @@
   return CounterStyleNameToDeprecatedEnum(ListStyle());
 }
 
+bool ListStyleTypeData::IsCounterStyleReferenceValid(Document& document) const {
+  if (!IsCounterStyle()) {
+    DCHECK(!counter_style_);
+    return true;
+  }
+
+  if (!counter_style_ || counter_style_->IsDirty())
+    return false;
+
+  // Even if the referenced counter style is clean, it may still be stale if new
+  // counter styles have been inserted, in which case the same (scope, name) now
+  // refers to a different counter style. So we make an extra lookup to verify.
+  return counter_style_ ==
+         &document.GetStyleEngine().FindCounterStyleAcrossScopes(
+             GetCounterStyleName(), GetTreeScope());
+}
+
+const CounterStyle& ListStyleTypeData::GetCounterStyle(
+    Document& document) const {
+  DCHECK(IsCounterStyle());
+  if (!IsCounterStyleReferenceValid(document)) {
+    counter_style_ = document.GetStyleEngine().FindCounterStyleAcrossScopes(
+        GetCounterStyleName(), GetTreeScope());
+  }
+  return *counter_style_;
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/style/list_style_type_data.h b/third_party/blink/renderer/core/style/list_style_type_data.h
index 3922b29..118f7985 100644
--- a/third_party/blink/renderer/core/style/list_style_type_data.h
+++ b/third_party/blink/renderer/core/style/list_style_type_data.h
@@ -11,6 +11,8 @@
 
 namespace blink {
 
+class CounterStyle;
+class Document;
 class TreeScope;
 
 class ListStyleTypeData final : public GarbageCollected<ListStyleTypeData> {
@@ -55,6 +57,10 @@
 
   const TreeScope* GetTreeScope() const { return tree_scope_; }
 
+  // TODO(crbug.com/687225): Try not to pass a Document, which is cumbersome.
+  bool IsCounterStyleReferenceValid(Document&) const;
+  const CounterStyle& GetCounterStyle(Document&) const;
+
   EListStyleType ToDeprecatedListStyleTypeEnum() const;
 
  private:
@@ -63,6 +69,10 @@
 
   // The tree scope for looking up the custom counter style name
   Member<const TreeScope> tree_scope_;
+
+  // The CounterStyle that we are using. The reference is updated on demand.
+  // Note: this is NOT part of the computed value of 'list-style-type'.
+  mutable Member<const CounterStyle> counter_style_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/timing/performance_long_task_timing.h b/third_party/blink/renderer/core/timing/performance_long_task_timing.h
index 2132ed1..fb00065 100644
--- a/third_party/blink/renderer/core/timing/performance_long_task_timing.h
+++ b/third_party/blink/renderer/core/timing/performance_long_task_timing.h
@@ -30,6 +30,7 @@
                             const AtomicString& culprit_src,
                             const AtomicString& culprit_id,
                             const AtomicString& culprit_name);
+  ~PerformanceLongTaskTiming() override;
 
   AtomicString entryType() const override;
   PerformanceEntryType EntryTypeEnum() const override;
@@ -39,8 +40,6 @@
   void Trace(Visitor*) const override;
 
  private:
-  ~PerformanceLongTaskTiming() override;
-
   void BuildJSONValue(V8ObjectBuilder&) const override;
 
   TaskAttributionVector attribution_;
diff --git a/third_party/blink/renderer/core/timing/performance_navigation_timing.h b/third_party/blink/renderer/core/timing/performance_navigation_timing.h
index c566ccb..22eb990 100644
--- a/third_party/blink/renderer/core/timing/performance_navigation_timing.h
+++ b/third_party/blink/renderer/core/timing/performance_navigation_timing.h
@@ -35,6 +35,7 @@
                               ResourceTimingInfo*,
                               base::TimeTicks time_origin,
                               HeapVector<Member<PerformanceServerTiming>>);
+  ~PerformanceNavigationTiming() override;
 
   // Attributes inherited from PerformanceEntry.
   DOMHighResTimeStamp duration() const override;
@@ -67,8 +68,6 @@
   void BuildJSONValue(V8ObjectBuilder&) const override;
 
  private:
-  ~PerformanceNavigationTiming() override;
-
   static AtomicString GetNavigationType(WebNavigationType, const Document*);
 
   const DocumentTiming* GetDocumentTiming() const;
diff --git a/third_party/blink/renderer/modules/accessibility/ax_media_element.cc b/third_party/blink/renderer/modules/accessibility/ax_media_element.cc
index 8abff1fd..e12b63f 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_media_element.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_media_element.cc
@@ -15,6 +15,7 @@
     LayoutObject* layout_object,
     AXObjectCacheImpl& ax_object_cache) {
   DCHECK(layout_object->GetNode());
+  DCHECK(IsA<HTMLMediaElement>(layout_object->GetNode()));
   return MakeGarbageCollected<AccessibilityMediaElement>(layout_object,
                                                          ax_object_cache);
 }
@@ -60,6 +61,11 @@
 bool AccessibilityMediaElement::HasControls() const {
   if (IsDetached())
     return false;
+  if (!IsA<HTMLMediaElement>(GetNode()) || !GetNode()->isConnected()) {
+    NOTREACHED() << "Accessible media element not ready: " << GetNode()
+                 << "  isConnected? " << GetNode()->isConnected();
+    return false;
+  }
   return To<HTMLMediaElement>(GetNode())->ShouldShowControls();
 }
 
diff --git a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
index 755821d..3a7f1bb4 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
@@ -3410,8 +3410,7 @@
     int new_index = index;
     for (wtf_size_t i = 0; i < length; ++i) {
       // If the child was owned, it will be added elsewhere as a direct
-      // child of the object owning it, and not as an indirect child under
-      // an object not included in the tree.
+      // child of the object owning it.
       if (!AXObjectCache().IsAriaOwned(children[i])) {
         DCHECK(!children[i]->IsDetached()) << "Cannot add a detached child: "
                                            << children[i]->ToString(true, true);
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object.cc b/third_party/blink/renderer/modules/accessibility/ax_object.cc
index a9eec38..b54520f 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_object.cc
@@ -2474,10 +2474,6 @@
   return parent->AncestorExposesActiveDescendant();
 }
 
-bool AXObject::HasIndirectChildren() const {
-  return RoleValue() == ax::mojom::blink::Role::kTableHeaderContainer;
-}
-
 bool AXObject::CanSetSelectedAttribute() const {
   // Sub-widget elements can be selected if not disabled (native or ARIA)
   return IsSubWidget() && Restriction() != kRestrictionDisabled;
@@ -3309,16 +3305,12 @@
 }
 
 int AXObject::ChildCountIncludingIgnored() const {
-  return HasIndirectChildren() ? 0 : int{ChildrenIncludingIgnored().size()};
+  return int{ChildrenIncludingIgnored().size()};
 }
 
 AXObject* AXObject::ChildAtIncludingIgnored(int index) const {
-  // We need to use "ChildCountIncludingIgnored()" and
-  // "ChildrenIncludingIgnored()" instead of using the "children_" member
-  // directly, because we might need to update children and check for the
-  // presence of indirect children.
-  if (index < 0 || index >= ChildCountIncludingIgnored())
-    return nullptr;
+  DCHECK_GE(index, 0);
+  DCHECK_LT(index, ChildCountIncludingIgnored());
   return ChildrenIncludingIgnored()[index];
 }
 
@@ -4701,7 +4693,6 @@
     case ax::mojom::blink::Role::kCaret:
     case ax::mojom::blink::Role::kClient:
     case ax::mojom::blink::Role::kColorWell:
-    case ax::mojom::blink::Role::kColumn:
     case ax::mojom::blink::Role::kComboBoxMenuButton:  // Only value from
                                                        // content.
     case ax::mojom::blink::Role::kComboBoxGrouping:
@@ -4796,7 +4787,6 @@
     case ax::mojom::blink::Role::kSuggestion:
     case ax::mojom::blink::Role::kSvgRoot:
     case ax::mojom::blink::Role::kTable:
-    case ax::mojom::blink::Role::kTableHeaderContainer:
     case ax::mojom::blink::Role::kTabList:
     case ax::mojom::blink::Role::kTabPanel:
     case ax::mojom::blink::Role::kTerm:
@@ -4920,8 +4910,10 @@
       return is_inside_portal && is_main_frame;
     }
 
+    case ax::mojom::blink::Role::kColumn:
+    case ax::mojom::blink::Role::kTableHeaderContainer:
     case ax::mojom::blink::Role::kUnknown:
-      NOTREACHED() << "Illegal Role::kUnknown for " << ToString(true, true);
+      NOTREACHED() << "Role shouldn't occur in Blink: " << ToString(true, true);
       break;
   }
 
diff --git a/third_party/blink/renderer/modules/accessibility/ax_position.cc b/third_party/blink/renderer/modules/accessibility/ax_position.cc
index 17268a2b..783a72e 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_position.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_position.cc
@@ -392,6 +392,9 @@
 const AXObject* AXPosition::ChildAfterTreePosition() const {
   if (!IsValid() || IsTextPosition())
     return nullptr;
+  if (ChildIndex() == container_object_->ChildCountIncludingIgnored())
+    return nullptr;
+  DCHECK_LT(ChildIndex(), container_object_->ChildCountIncludingIgnored());
   return container_object_->ChildAtIncludingIgnored(ChildIndex());
 }
 
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.cc b/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.cc
index e8b9146..fa49edc 100644
--- a/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.cc
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.cc
@@ -145,11 +145,11 @@
   DCHECK(state_stack_.begin() < state_stack_.end());
   for (curr_state = state_stack_.begin(); curr_state < state_stack_.end();
        curr_state++) {
-    c->setMatrix(SkMatrix::I());
+    c->setMatrix(SkM44());
     if (curr_state->Get()) {
       curr_state->Get()->PlaybackClips(c);
       c->setMatrix(
-          TransformationMatrixToSkMatrix(curr_state->Get()->GetTransform()));
+          TransformationMatrix::ToSkM44(curr_state->Get()->GetTransform()));
     }
     c->save();
   }
@@ -593,7 +593,7 @@
   if (!GetState().IsTransformInvertible())
     return;
 
-  c->concat(TransformationMatrixToSkMatrix(transform));
+  c->concat(TransformationMatrix::ToSkM44(transform));
   path_.Transform(transform.Inverse());
 }
 
diff --git a/third_party/blink/renderer/modules/webaudio/oscillator_options.idl b/third_party/blink/renderer/modules/webaudio/oscillator_options.idl
index d9e7ef88b..caa24013 100644
--- a/third_party/blink/renderer/modules/webaudio/oscillator_options.idl
+++ b/third_party/blink/renderer/modules/webaudio/oscillator_options.idl
@@ -7,5 +7,5 @@
     OscillatorType type = "sine";
     float detune = 0;
     float frequency = 440;
-    PeriodicWave? periodicWave;
-};
\ No newline at end of file
+    PeriodicWave periodicWave;
+};
diff --git a/third_party/blink/renderer/modules/webcodecs/audio_decoder_broker_test.cc b/third_party/blink/renderer/modules/webcodecs/audio_decoder_broker_test.cc
index a10395a9..3a7869c 100644
--- a/third_party/blink/renderer/modules/webcodecs/audio_decoder_broker_test.cc
+++ b/third_party/blink/renderer/modules/webcodecs/audio_decoder_broker_test.cc
@@ -143,6 +143,12 @@
     std::move(callback).Run(mojo::NullRemote(), base::nullopt,
                             mojo::NullRemote(), "CDM creation not supported");
   }
+#if defined(OS_WIN)
+  void CreateMediaFoundationRenderer(
+      mojo::PendingReceiver<media::mojom::Renderer> receiver,
+      mojo::PendingReceiver<media::mojom::MediaFoundationRendererExtension>
+          renderer_extension_receiver) override {}
+#endif  // defined(OS_WIN)
 
  private:
   media::MojoCdmServiceContext cdm_service_context_;
diff --git a/third_party/blink/renderer/modules/webcodecs/video_decoder_broker_test.cc b/third_party/blink/renderer/modules/webcodecs/video_decoder_broker_test.cc
index ea0d524ed..60ea9d8 100644
--- a/third_party/blink/renderer/modules/webcodecs/video_decoder_broker_test.cc
+++ b/third_party/blink/renderer/modules/webcodecs/video_decoder_broker_test.cc
@@ -149,6 +149,13 @@
                             mojo::NullRemote(), "CDM creation not supported");
   }
 
+#if defined(OS_WIN)
+  void CreateMediaFoundationRenderer(
+      mojo::PendingReceiver<media::mojom::Renderer> receiver,
+      mojo::PendingReceiver<media::mojom::MediaFoundationRendererExtension>
+          renderer_extension_receiver) override {}
+#endif  // defined(OS_WIN)
+
  private:
   media::MojoCdmServiceContext cdm_service_context_;
   FakeMojoMediaClient mojo_media_client_;
diff --git a/third_party/blink/renderer/modules/webcodecs/video_frame.cc b/third_party/blink/renderer/modules/webcodecs/video_frame.cc
index e872d63..03378c0 100644
--- a/third_party/blink/renderer/modules/webcodecs/video_frame.cc
+++ b/third_party/blink/renderer/modules/webcodecs/video_frame.cc
@@ -14,16 +14,19 @@
 #include "media/base/timestamp_constants.h"
 #include "media/base/video_frame.h"
 #include "media/base/video_frame_metadata.h"
+#include "media/base/video_frame_pool.h"
 #include "media/base/wait_and_replace_sync_token_client.h"
 #include "media/renderers/paint_canvas_video_renderer.h"
 #include "media/renderers/video_frame_yuv_converter.h"
 #include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_plane_init.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_video_frame_init.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_video_pixel_format.h"
 #include "third_party/blink/renderer/core/html/canvas/image_data.h"
 #include "third_party/blink/renderer/core/imagebitmap/image_bitmap.h"
 #include "third_party/blink/renderer/core/imagebitmap/image_bitmap_factories.h"
 #include "third_party/blink/renderer/core/inspector/console_message.h"
+#include "third_party/blink/renderer/core/typed_arrays/dom_array_piece.h"
 #include "third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.h"
 #include "third_party/blink/renderer/platform/graphics/gpu/shared_gpu_context.h"
 #include "third_party/blink/renderer/platform/graphics/image.h"
@@ -102,6 +105,103 @@
           std::move(async_result))));
 }
 
+media::VideoPixelFormat ToMediaPixelFormat(V8VideoPixelFormat::Enum fmt) {
+  switch (fmt) {
+    case V8VideoPixelFormat::Enum::kI420:
+      return media::PIXEL_FORMAT_I420;
+    case V8VideoPixelFormat::Enum::kNV12:
+      return media::PIXEL_FORMAT_NV12;
+    case V8VideoPixelFormat::Enum::kABGR:
+      return media::PIXEL_FORMAT_ABGR;
+    case V8VideoPixelFormat::Enum::kXBGR:
+      return media::PIXEL_FORMAT_XBGR;
+    case V8VideoPixelFormat::Enum::kARGB:
+      return media::PIXEL_FORMAT_ARGB;
+    case V8VideoPixelFormat::Enum::kXRGB:
+      return media::PIXEL_FORMAT_XRGB;
+  }
+}
+
+class CachedVideoFramePool : public GarbageCollected<CachedVideoFramePool>,
+                             public Supplement<ExecutionContext> {
+ public:
+  static const char kSupplementName[];
+
+  static CachedVideoFramePool& From(ExecutionContext& context) {
+    CachedVideoFramePool* supplement =
+        Supplement<ExecutionContext>::From<CachedVideoFramePool>(context);
+    if (!supplement) {
+      supplement = MakeGarbageCollected<CachedVideoFramePool>(context);
+      Supplement<ExecutionContext>::ProvideTo(context, supplement);
+    }
+    return *supplement;
+  }
+
+  explicit CachedVideoFramePool(ExecutionContext& context)
+      : Supplement<ExecutionContext>(context),
+        task_runner_(Thread::Current()->GetTaskRunner()) {}
+  virtual ~CachedVideoFramePool() = default;
+
+  // Disallow copy and assign.
+  CachedVideoFramePool& operator=(const CachedVideoFramePool&) = delete;
+  CachedVideoFramePool(const CachedVideoFramePool&) = delete;
+
+  scoped_refptr<media::VideoFrame> CreateFrame(media::VideoPixelFormat format,
+                                               const gfx::Size& coded_size,
+                                               const gfx::Rect& visible_rect,
+                                               const gfx::Size& natural_size,
+                                               base::TimeDelta timestamp) {
+    if (!frame_pool_)
+      CreatePoolAndStartIdleObsever();
+
+    last_frame_creation_ = base::TimeTicks::Now();
+    return frame_pool_->CreateFrame(format, coded_size, visible_rect,
+                                    natural_size, timestamp);
+  }
+
+  void Trace(Visitor* visitor) const override {
+    Supplement<ExecutionContext>::Trace(visitor);
+  }
+
+ private:
+  static const base::TimeDelta kIdleTimeout;
+
+  void PostMonitoringTask() {
+    DCHECK(!task_handle_.IsActive());
+    task_handle_ = PostDelayedCancellableTask(
+        *task_runner_, FROM_HERE,
+        WTF::Bind(&CachedVideoFramePool::PurgeIdleFramePool,
+                  WrapWeakPersistent(this)),
+        kIdleTimeout);
+  }
+
+  void CreatePoolAndStartIdleObsever() {
+    DCHECK(!frame_pool_);
+    frame_pool_ = std::make_unique<media::VideoFramePool>();
+    PostMonitoringTask();
+  }
+
+  // We don't want a VideoFramePool to stick around forever wasting memory, so
+  // once we haven't issued any VideoFrames for a while, turn down the pool.
+  void PurgeIdleFramePool() {
+    if (base::TimeTicks::Now() - last_frame_creation_ > kIdleTimeout) {
+      frame_pool_.reset();
+      return;
+    }
+    PostMonitoringTask();
+  }
+
+  scoped_refptr<base::SequencedTaskRunner> task_runner_;
+  std::unique_ptr<media::VideoFramePool> frame_pool_;
+  base::TimeTicks last_frame_creation_;
+  TaskHandle task_handle_;
+};
+
+// static -- defined out of line to satisfy link time requirements.
+const char CachedVideoFramePool::kSupplementName[] = "CachedVideoFramePool";
+const base::TimeDelta CachedVideoFramePool::kIdleTimeout =
+    base::TimeDelta::FromSeconds(10);
+
 }  // namespace
 
 VideoFrame::VideoFrame(scoped_refptr<media::VideoFrame> frame,
@@ -229,6 +329,177 @@
 }
 
 // static
+VideoFrame* VideoFrame::Create(ScriptState* script_state,
+                               const String& format,
+                               const HeapVector<Member<PlaneInit>>& planes,
+                               VideoFrameInit* init,
+                               ExceptionState& exception_state) {
+  if (!init->hasCodedWidth() || !init->hasCodedHeight()) {
+    exception_state.ThrowDOMException(
+        DOMExceptionCode::kConstraintError,
+        "Coded size is required for planar construction");
+    return nullptr;
+  }
+
+  // Type formats are enforced by V8.
+  auto typed_fmt = V8VideoPixelFormat::Create(format);
+  DCHECK(typed_fmt);
+
+  auto media_fmt = ToMediaPixelFormat(typed_fmt->AsEnum());
+
+  // There's no I420A pixel format, so treat I420 + 4 planes as I420A.
+  if (media_fmt == media::PIXEL_FORMAT_I420 && planes.size() == 4u)
+    media_fmt = media::PIXEL_FORMAT_I420A;
+
+  if (media::VideoFrame::NumPlanes(media_fmt) != planes.size()) {
+    exception_state.ThrowDOMException(
+        DOMExceptionCode::kConstraintError,
+        String::Format("Invalid number of planes for format %s; expected %zu, "
+                       "received %u",
+                       format.Ascii().c_str(),
+                       media::VideoFrame::NumPlanes(media_fmt), planes.size()));
+    return nullptr;
+  }
+
+  const gfx::Size coded_size(init->codedWidth(), init->codedHeight());
+  if (coded_size.IsEmpty()) {
+    exception_state.ThrowDOMException(
+        DOMExceptionCode::kConstraintError,
+        String::Format("Invalid coded size (%d, %d) provided",
+                       init->codedWidth(), init->codedHeight()));
+    return nullptr;
+  }
+
+  for (size_t i = 0; i < planes.size(); ++i) {
+    const auto minimum_size =
+        media::VideoFrame::PlaneSize(media_fmt, i, coded_size);
+    if (planes[i]->stride() < uint32_t{minimum_size.width()}) {
+      exception_state.ThrowDOMException(
+          DOMExceptionCode::kConstraintError,
+          String::Format(
+              "The stride of plane %zu is too small for the given coded size "
+              "(%s); expected at least %d, received %u",
+              i, coded_size.ToString().c_str(), minimum_size.width(),
+              planes[i]->stride()));
+      return nullptr;
+    }
+    if (planes[i]->rows() != uint32_t{minimum_size.height()}) {
+      exception_state.ThrowDOMException(
+          DOMExceptionCode::kConstraintError,
+          String::Format(
+              "The row count for plane %zu is incorrect for the given coded "
+              "size (%s); expected %d, received %u",
+              i, coded_size.ToString().c_str(), minimum_size.height(),
+              planes[i]->rows()));
+      return nullptr;
+    }
+
+    // This requires the full stride to be provided for every row.
+    gfx::Size provided_size(planes[i]->stride(), planes[i]->rows());
+    const auto required_byte_size = provided_size.GetCheckedArea();
+    if (!required_byte_size.IsValid()) {
+      exception_state.ThrowDOMException(
+          DOMExceptionCode::kConstraintError,
+          String::Format("The size of plane %zu is too large", i));
+      return nullptr;
+    }
+
+    DOMArrayPiece buffer(planes[i]->src());
+    if (buffer.ByteLength() < required_byte_size.ValueOrDie()) {
+      // Note: We use GetArea() below instead of area.ValueOrDie() since the
+      // base::StrictNumeric seems to confuse the printf() format checks.
+      exception_state.ThrowDOMException(
+          DOMExceptionCode::kConstraintError,
+          String::Format(
+              "The size of plane %zu is too small for the given coded "
+              "size (%s); expected at least %d, received %zu",
+              i, coded_size.ToString().c_str(), provided_size.GetArea(),
+              buffer.ByteLength()));
+      return nullptr;
+    }
+  }
+
+  auto visible_rect = gfx::Rect(coded_size);
+  if (init->hasCropLeft() || init->hasCropTop() || init->hasCropWidth() ||
+      init->hasCropHeight()) {
+    const auto crop_left = init->hasCropLeft() ? init->cropLeft() : 0;
+    const auto crop_top = init->hasCropTop() ? init->cropTop() : 0;
+    const auto crop_w =
+        init->hasCropWidth() ? visible_rect.width() - init->cropWidth() : 0;
+    const auto crop_h =
+        init->hasCropHeight() ? visible_rect.height() - init->cropHeight() : 0;
+    if (crop_w < 0 || crop_h < 0 || crop_w > unsigned{visible_rect.width()} ||
+        crop_h > unsigned{visible_rect.height()}) {
+      visible_rect = gfx::Rect();
+    } else {
+      visible_rect.Inset(crop_left, crop_top, crop_w, crop_h);
+    }
+
+    if (visible_rect.IsEmpty()) {
+      exception_state.ThrowDOMException(
+          DOMExceptionCode::kConstraintError,
+          String::Format(
+              "Invalid visble rect (%s) after crop (%d, %d, %d, %d) applied",
+              visible_rect.ToString().c_str(), crop_left, crop_top, crop_w,
+              crop_h));
+      return nullptr;
+    }
+  }
+
+  auto natural_size = visible_rect.size();
+  if (init->hasDisplayWidth())
+    natural_size.set_width(init->displayWidth());
+  if (init->hasDisplayHeight())
+    natural_size.set_height(init->displayHeight());
+  if (coded_size.IsEmpty()) {
+    exception_state.ThrowDOMException(
+        DOMExceptionCode::kConstraintError,
+        String::Format("Invalid display size (%s) provided",
+                       natural_size.ToString().c_str()));
+    return nullptr;
+  }
+
+  const auto timestamp = base::TimeDelta::FromMicroseconds(init->timestamp());
+  auto& frame_pool =
+      CachedVideoFramePool::From(*ExecutionContext::From(script_state));
+  auto frame = frame_pool.CreateFrame(media_fmt, coded_size, visible_rect,
+                                      natural_size, timestamp);
+  if (!frame) {
+    exception_state.ThrowDOMException(
+        DOMExceptionCode::kConstraintError,
+        String::Format(
+            "Failed to create a video frame with configuration {format:%s, "
+            "coded_size:%s, visible_rect:%s, display_size:%s}",
+            VideoPixelFormatToString(media_fmt).c_str(),
+            coded_size.ToString().c_str(), visible_rect.ToString().c_str(),
+            natural_size.ToString().c_str()));
+    return nullptr;
+  }
+
+  for (size_t i = 0; i < planes.size(); ++i) {
+    const auto minimum_size =
+        media::VideoFrame::PlaneSize(media_fmt, i, coded_size);
+
+    DOMArrayPiece buffer(planes[i]->src());
+
+    uint8_t* dest_ptr = frame->visible_data(i);
+    const uint8_t* src_ptr = reinterpret_cast<uint8_t*>(buffer.Data());
+    for (size_t r = 0; r < planes[i]->rows(); ++r) {
+      DCHECK_LE(
+          src_ptr + planes[i]->stride(),
+          reinterpret_cast<uint8_t*>(buffer.Data()) + buffer.ByteLength());
+
+      memcpy(dest_ptr, src_ptr, minimum_size.width());
+      src_ptr += planes[i]->stride();
+      dest_ptr += frame->stride(i);
+    }
+  }
+
+  return MakeGarbageCollected<VideoFrame>(std::move(frame),
+                                          ExecutionContext::From(script_state));
+}
+
+// static
 bool VideoFrame::IsSupportedPlanarFormat(media::VideoFrame* frame) {
   if (!frame)
     return false;
diff --git a/third_party/blink/renderer/modules/webcodecs/video_frame.h b/third_party/blink/renderer/modules/webcodecs/video_frame.h
index 94d70fe..b8421b9 100644
--- a/third_party/blink/renderer/modules/webcodecs/video_frame.h
+++ b/third_party/blink/renderer/modules/webcodecs/video_frame.h
@@ -23,6 +23,7 @@
 class ImageBitmap;
 class ExceptionState;
 class ExecutionContext;
+class PlaneInit;
 class ScriptPromise;
 class ScriptState;
 class VideoFrameInit;
@@ -45,6 +46,11 @@
                             ImageBitmap*,
                             VideoFrameInit*,
                             ExceptionState&);
+  static VideoFrame* Create(ScriptState*,
+                            const String& format,
+                            const HeapVector<Member<PlaneInit>>& planes,
+                            VideoFrameInit* init,
+                            ExceptionState&);
 
   String format() const;
   base::Optional<HeapVector<Member<Plane>>> planes();
diff --git a/third_party/blink/renderer/modules/webcodecs/video_frame.idl b/third_party/blink/renderer/modules/webcodecs/video_frame.idl
index ed293de..fa99541 100644
--- a/third_party/blink/renderer/modules/webcodecs/video_frame.idl
+++ b/third_party/blink/renderer/modules/webcodecs/video_frame.idl
@@ -9,7 +9,12 @@
     Serializable,
     RuntimeEnabled=WebCodecs
 ] interface VideoFrame {
-  [CallWith=ScriptState, RaisesException] constructor(ImageBitmap source, VideoFrameInit init);
+  [CallWith=ScriptState, RaisesException]
+  constructor(ImageBitmap source, VideoFrameInit init);
+  [CallWith=ScriptState, RaisesException]
+  constructor(VideoPixelFormat format,
+              sequence<PlaneInit> planes,
+              VideoFrameInit init);
 
   // TODO(sandersd): Provide a way to find out what pixel formats are supported.
   // TODO(sandersd): Provide a way to convert to a specific pixel format, and to
@@ -59,6 +64,6 @@
 
   // Create an ImageBitmap from the crop region, scaled to the display size.
   // TODO(sandersd): Should use the global createImageBitmap() instead.
-  [CallWith=ScriptState, RaisesException] Promise<ImageBitmap> createImageBitmap(
-    optional ImageBitmapOptions options = {});
+  [CallWith=ScriptState, RaisesException] Promise<ImageBitmap>
+  createImageBitmap(optional ImageBitmapOptions options = {});
 };
diff --git a/third_party/blink/renderer/modules/webcodecs/video_frame_init.idl b/third_party/blink/renderer/modules/webcodecs/video_frame_init.idl
index b1c7c21a..17dee99 100644
--- a/third_party/blink/renderer/modules/webcodecs/video_frame_init.idl
+++ b/third_party/blink/renderer/modules/webcodecs/video_frame_init.idl
@@ -7,4 +7,13 @@
 dictionary VideoFrameInit {
   required unsigned long long timestamp;  // microseconds
   unsigned long long duration;  // microseconds
-};
\ No newline at end of file
+
+  unsigned long codedWidth;
+  unsigned long codedHeight;
+  unsigned long cropLeft;
+  unsigned long cropTop;
+  unsigned long cropWidth;
+  unsigned long cropHeight;
+  unsigned long displayWidth;
+  unsigned long displayHeight;
+};
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_device.cc b/third_party/blink/renderer/modules/webgpu/gpu_device.cc
index 5642879..dc35b63 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu_device.cc
+++ b/third_party/blink/renderer/modules/webgpu/gpu_device.cc
@@ -33,6 +33,7 @@
 #include "third_party/blink/renderer/modules/webgpu/gpu_texture.h"
 #include "third_party/blink/renderer/modules/webgpu/gpu_uncaptured_error_event.h"
 #include "third_party/blink/renderer/modules/webgpu/gpu_validation_error.h"
+#include "third_party/blink/renderer/platform/graphics/gpu/webgpu_image_bitmap_handler.h"
 #include "third_party/blink/renderer/platform/heap/heap.h"
 
 namespace blink {
diff --git a/third_party/blink/renderer/modules/xr/BUILD.gn b/third_party/blink/renderer/modules/xr/BUILD.gn
index bcf82f00..7f0c302 100644
--- a/third_party/blink/renderer/modules/xr/BUILD.gn
+++ b/third_party/blink/renderer/modules/xr/BUILD.gn
@@ -17,6 +17,8 @@
     "xr_bounded_reference_space.h",
     "xr_canvas_input_provider.cc",
     "xr_canvas_input_provider.h",
+    "xr_cpu_depth_information.cc",
+    "xr_cpu_depth_information.h",
     "xr_cube_map.cc",
     "xr_cube_map.h",
     "xr_depth_information.cc",
@@ -106,6 +108,8 @@
     "xr_viewport.h",
     "xr_webgl_binding.cc",
     "xr_webgl_binding.h",
+    "xr_webgl_depth_information.cc",
+    "xr_webgl_depth_information.h",
     "xr_webgl_layer.cc",
     "xr_webgl_layer.h",
     "xr_webgl_rendering_context.h",
diff --git a/third_party/blink/renderer/modules/xr/idls.gni b/third_party/blink/renderer/modules/xr/idls.gni
index 97a038bc..e542fe2 100644
--- a/third_party/blink/renderer/modules/xr/idls.gni
+++ b/third_party/blink/renderer/modules/xr/idls.gni
@@ -8,6 +8,7 @@
   "xr_anchor.idl",
   "xr_anchor_set.idl",
   "xr_bounded_reference_space.idl",
+  "xr_cpu_depth_information.idl",
   "xr_depth_information.idl",
   "xr_dom_overlay_state.idl",
   "xr_frame.idl",
@@ -42,12 +43,14 @@
   "xr_viewer_pose.idl",
   "xr_viewport.idl",
   "xr_webgl_binding.idl",
+  "xr_webgl_depth_information.idl",
   "xr_webgl_layer.idl",
 ]
 
 modules_callback_function_idl_files = [ "xr_frame_request_callback.idl" ]
 
 modules_dictionary_idl_files = [
+  "xr_depth_state_init.idl",
   "xr_dom_overlay_init.idl",
   "xr_hit_test_options_init.idl",
   "xr_input_source_event_init.idl",
diff --git a/third_party/blink/renderer/modules/xr/xr_cpu_depth_information.cc b/third_party/blink/renderer/modules/xr/xr_cpu_depth_information.cc
new file mode 100644
index 0000000..c02306c
--- /dev/null
+++ b/third_party/blink/renderer/modules/xr/xr_cpu_depth_information.cc
@@ -0,0 +1,85 @@
+// 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 "third_party/blink/renderer/modules/xr/xr_cpu_depth_information.h"
+
+#include <cstdlib>
+
+#include "base/numerics/checked_math.h"
+#include "third_party/blink/renderer/core/dom/dom_exception.h"
+#include "third_party/blink/renderer/platform/bindings/exception_state.h"
+
+namespace {
+constexpr char kOutOfBoundsAccess[] =
+    "Attempted to access data that is out-of-bounds.";
+}
+
+namespace blink {
+
+XRCPUDepthInformation::XRCPUDepthInformation(
+    const XRFrame* xr_frame,
+    const gfx::Size& size,
+    const gfx::Transform& norm_texture_from_norm_view,
+    float raw_value_to_meters,
+    DOMUint16Array* data)
+    : XRDepthInformation(xr_frame,
+                         size,
+                         norm_texture_from_norm_view,
+                         raw_value_to_meters),
+      data_(data) {
+  DVLOG(3) << __func__;
+
+  CHECK_EQ(base::CheckMul(2, size_.width(), size_.height()).ValueOrDie(),
+           data_->byteLength());
+}
+
+DOMUint16Array* XRCPUDepthInformation::data(
+    ExceptionState& exception_state) const {
+  if (!ValidateFrame(exception_state)) {
+    return nullptr;
+  }
+
+  return data_;
+}
+
+float XRCPUDepthInformation::getDepthInMeters(
+    uint32_t column,
+    uint32_t row,
+    ExceptionState& exception_state) const {
+  DVLOG(3) << __func__ << ": column=" << column << ", row=" << row;
+
+  if (!ValidateFrame(exception_state)) {
+    return 0.0;
+  }
+
+  if (column >= static_cast<size_t>(size_.width())) {
+    exception_state.ThrowDOMException(DOMExceptionCode::kNotAllowedError,
+                                      kOutOfBoundsAccess);
+    return 0.0;
+  }
+
+  if (row >= static_cast<size_t>(size_.height())) {
+    exception_state.ThrowDOMException(DOMExceptionCode::kNotAllowedError,
+                                      kOutOfBoundsAccess);
+    return 0.0;
+  }
+
+  auto checked_index =
+      base::CheckAdd(column, base::CheckMul(row, size_.width()));
+  size_t index = checked_index.ValueOrDie();
+
+  // Convert from data's native units to meters when accessing:
+  float result = data_->Item(index) * rawValueToMeters_;
+
+  DVLOG(3) << __func__ << ": index=" << index << ", result=" << result;
+
+  return result;
+}
+
+void XRCPUDepthInformation::Trace(Visitor* visitor) const {
+  visitor->Trace(data_);
+  XRDepthInformation::Trace(visitor);
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/xr/xr_cpu_depth_information.h b/third_party/blink/renderer/modules/xr/xr_cpu_depth_information.h
new file mode 100644
index 0000000..790918c
--- /dev/null
+++ b/third_party/blink/renderer/modules/xr/xr_cpu_depth_information.h
@@ -0,0 +1,46 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_CPU_DEPTH_INFORMATION_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_CPU_DEPTH_INFORMATION_H_
+
+#include "third_party/blink/renderer/core/typed_arrays/dom_typed_array.h"
+#include "third_party/blink/renderer/modules/xr/xr_depth_information.h"
+
+namespace gfx {
+class Size;
+class Transform;
+}  // namespace gfx
+
+namespace blink {
+
+class ExceptionState;
+class XRFrame;
+
+class XRCPUDepthInformation final : public XRDepthInformation {
+  DEFINE_WRAPPERTYPEINFO();
+
+ public:
+  explicit XRCPUDepthInformation(
+      const XRFrame* xr_frame,
+      const gfx::Size& size,
+      const gfx::Transform& norm_texture_from_norm_view,
+      float raw_value_to_meters,
+      DOMUint16Array* data);
+
+  DOMUint16Array* data(ExceptionState& exception_state) const;
+
+  float getDepthInMeters(uint32_t column,
+                         uint32_t row,
+                         ExceptionState& exception_state) const;
+
+  void Trace(Visitor* visitor) const override;
+
+ private:
+  const Member<DOMUint16Array> data_;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_CPU_DEPTH_INFORMATION_H_
diff --git a/third_party/blink/renderer/modules/xr/xr_cpu_depth_information.idl b/third_party/blink/renderer/modules/xr/xr_cpu_depth_information.idl
new file mode 100644
index 0000000..b2a98d2
--- /dev/null
+++ b/third_party/blink/renderer/modules/xr/xr_cpu_depth_information.idl
@@ -0,0 +1,16 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+[
+    SecureContext,
+    Exposed=Window,
+    RuntimeEnabled=WebXRDepth
+] interface XRCPUDepthInformation : XRDepthInformation {
+  [RaisesException, SameObject, MeasureAs=XRCPUDepthInformationDataAttribute]
+  readonly attribute DataView data;
+
+  [RaisesException, MeasureAs=XRCPUDepthInformationGetDepth]
+  float getDepthInMeters(unsigned long column, unsigned long row);
+};
+
diff --git a/third_party/blink/renderer/modules/xr/xr_depth_information.cc b/third_party/blink/renderer/modules/xr/xr_depth_information.cc
index 77bae6c..e55237c 100644
--- a/third_party/blink/renderer/modules/xr/xr_depth_information.cc
+++ b/third_party/blink/renderer/modules/xr/xr_depth_information.cc
@@ -6,8 +6,6 @@
 
 #include <cstdlib>
 
-#include "base/numerics/checked_math.h"
-#include "device/vr/public/mojom/vr_service.mojom-blink.h"
 #include "third_party/blink/renderer/core/dom/dom_exception.h"
 #include "third_party/blink/renderer/modules/xr/xr_frame.h"
 #include "third_party/blink/renderer/modules/xr/xr_rigid_transform.h"
@@ -15,8 +13,7 @@
 #include "third_party/blink/renderer/platform/transforms/transformation_matrix.h"
 
 namespace {
-constexpr char kOutOfBoundsAccess[] =
-    "Attempted to access data that is out-of-bounds.";
+
 constexpr char kFrameInactive[] =
     "XRDepthInformation members are only accessible when their XRFrame's "
     "`active` boolean is `true`.";
@@ -31,26 +28,15 @@
     const XRFrame* xr_frame,
     const gfx::Size& size,
     const gfx::Transform& norm_texture_from_norm_view,
-    DOMUint16Array* data)
+    float raw_value_to_meters)
     : xr_frame_(xr_frame),
       size_(size),
-      data_(data),
-      norm_texture_from_norm_view_(norm_texture_from_norm_view) {
+      norm_texture_from_norm_view_(norm_texture_from_norm_view),
+      rawValueToMeters_(raw_value_to_meters) {
   DVLOG(3) << __func__ << ": size_=" << size_.ToString()
            << ", norm_texture_from_norm_view_="
-           << norm_texture_from_norm_view_.ToString();
-
-  CHECK_EQ(base::CheckMul(2, size_.width(), size_.height()).ValueOrDie(),
-           data_->byteLength());
-}
-
-DOMUint16Array* XRDepthInformation::data(
-    ExceptionState& exception_state) const {
-  if (!ValidateFrame(exception_state)) {
-    return nullptr;
-  }
-
-  return data_;
+           << norm_texture_from_norm_view_.ToString()
+           << ", raw_value_to_meters=" << raw_value_to_meters;
 }
 
 uint32_t XRDepthInformation::width(ExceptionState& exception_state) const {
@@ -69,37 +55,13 @@
   return size_.height();
 }
 
-float XRDepthInformation::getDepth(uint32_t column,
-                                   uint32_t row,
-                                   ExceptionState& exception_state) const {
-  DVLOG(3) << __func__ << ": column=" << column << ", row=" << row;
-
+float XRDepthInformation::rawValueToMeters(
+    ExceptionState& exception_state) const {
   if (!ValidateFrame(exception_state)) {
     return 0.0;
   }
 
-  if (column >= static_cast<size_t>(size_.width())) {
-    exception_state.ThrowDOMException(DOMExceptionCode::kNotAllowedError,
-                                      kOutOfBoundsAccess);
-    return 0.0;
-  }
-
-  if (row >= static_cast<size_t>(size_.height())) {
-    exception_state.ThrowDOMException(DOMExceptionCode::kNotAllowedError,
-                                      kOutOfBoundsAccess);
-    return 0.0;
-  }
-
-  auto checked_index =
-      base::CheckAdd(column, base::CheckMul(row, size_.width()));
-  size_t index = checked_index.ValueOrDie();
-
-  // Data is stored in millimeters, convert to meters when accessing:
-  float result = data_->Item(index) / 1000.0;
-
-  DVLOG(3) << __func__ << ": index=" << index << ", result=" << result;
-
-  return result;
+  return rawValueToMeters_;
 }
 
 XRRigidTransform* XRDepthInformation::normTextureFromNormView(
@@ -130,7 +92,6 @@
 
 void XRDepthInformation::Trace(Visitor* visitor) const {
   visitor->Trace(xr_frame_);
-  visitor->Trace(data_);
   ScriptWrappable::Trace(visitor);
 }
 
diff --git a/third_party/blink/renderer/modules/xr/xr_depth_information.h b/third_party/blink/renderer/modules/xr/xr_depth_information.h
index ee5ab0f5..37388c2 100644
--- a/third_party/blink/renderer/modules/xr/xr_depth_information.h
+++ b/third_party/blink/renderer/modules/xr/xr_depth_information.h
@@ -6,9 +6,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_DEPTH_INFORMATION_H_
 
 #include "device/vr/public/mojom/vr_service.mojom-blink-forward.h"
-#include "third_party/blink/renderer/core/typed_arrays/dom_typed_array.h"
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
-#include "third_party/blink/renderer/platform/wtf/forward.h"
 #include "ui/gfx/geometry/size.h"
 #include "ui/gfx/transform.h"
 
@@ -18,17 +16,22 @@
 class XRFrame;
 class XRRigidTransform;
 
-class XRDepthInformation final : public ScriptWrappable {
+class XRDepthInformation : public ScriptWrappable {
   DEFINE_WRAPPERTYPEINFO();
 
- public:
+ protected:
   explicit XRDepthInformation(const XRFrame* xr_frame,
                               const gfx::Size& size,
                               const gfx::Transform& norm_texture_from_norm_view,
-                              DOMUint16Array* data);
+                              float raw_value_to_meters);
 
-  DOMUint16Array* data(ExceptionState& exception_state) const;
+  // Helper to validate whether a frame is in a correct state. Should be invoked
+  // before every member access. If the validation returns `false`, it means the
+  // validation failed & an exception is going to be thrown and the rest of the
+  // member access code should not run.
+  bool ValidateFrame(ExceptionState& exception_state) const;
 
+ public:
   uint32_t width(ExceptionState& exception_state) const;
 
   uint32_t height(ExceptionState& exception_state) const;
@@ -36,25 +39,17 @@
   XRRigidTransform* normTextureFromNormView(
       ExceptionState& exception_state) const;
 
-  float getDepth(uint32_t column,
-                 uint32_t row,
-                 ExceptionState& exception_state) const;
+  float rawValueToMeters(ExceptionState& exception_state) const;
 
   void Trace(Visitor* visitor) const override;
 
- private:
+ protected:
   const Member<const XRFrame> xr_frame_;
 
   const gfx::Size size_;
 
-  const Member<DOMUint16Array> data_;
   const gfx::Transform norm_texture_from_norm_view_;
-
-  // Helper to validate whether a frame is in a correct state. Should be invoked
-  // before every member access. If the validation returns `false`, it means the
-  // validation failed & an exception is going to be thrown and the rest of the
-  // member access code should not run.
-  bool ValidateFrame(ExceptionState& exception_state) const;
+  const float rawValueToMeters_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/xr/xr_depth_information.idl b/third_party/blink/renderer/modules/xr/xr_depth_information.idl
index 51492921..dea2323 100644
--- a/third_party/blink/renderer/modules/xr/xr_depth_information.idl
+++ b/third_party/blink/renderer/modules/xr/xr_depth_information.idl
@@ -7,15 +7,11 @@
     Exposed=Window,
     RuntimeEnabled=WebXRDepth
 ] interface XRDepthInformation {
-  [RaisesException, SameObject, MeasureAs=XRDepthInformationDataAttribute]
-  readonly attribute Uint16Array data;
-
   [RaisesException] readonly attribute unsigned long width;
   [RaisesException] readonly attribute unsigned long height;
 
   [RaisesException, SameObject]
   readonly attribute XRRigidTransform normTextureFromNormView;
-
-  [RaisesException, MeasureAs=XRDepthInformationGetDepth]
-  float getDepth(unsigned long column, unsigned long row);
+  [RaisesException]
+  readonly attribute float rawValueToMeters;
 };
diff --git a/third_party/blink/renderer/modules/xr/xr_depth_manager.cc b/third_party/blink/renderer/modules/xr/xr_depth_manager.cc
index 9834c72..2b8d314 100644
--- a/third_party/blink/renderer/modules/xr/xr_depth_manager.cc
+++ b/third_party/blink/renderer/modules/xr/xr_depth_manager.cc
@@ -5,14 +5,45 @@
 #include "third_party/blink/renderer/modules/xr/xr_depth_manager.h"
 
 #include "base/trace_event/trace_event.h"
-#include "third_party/blink/renderer/modules/xr/xr_depth_information.h"
+#include "third_party/blink/renderer/modules/xr/xr_cpu_depth_information.h"
 #include "third_party/blink/renderer/modules/xr/xr_session.h"
 
+namespace {
+
+String UsageToString(device::mojom::XRDepthUsage usage) {
+  switch (usage) {
+    case device::mojom::XRDepthUsage::kCPUOptimized:
+      return "cpu-optimized";
+    case device::mojom::XRDepthUsage::kGPUOptimized:
+      return "gpu-optimized";
+  }
+}
+
+String DataFormatToString(device::mojom::XRDepthDataFormat data_format) {
+  switch (data_format) {
+    case device::mojom::XRDepthDataFormat::kLuminanceAlpha:
+      return "luminance-alpha";
+    case device::mojom::XRDepthDataFormat::kFloat32:
+      return "float32";
+  }
+}
+
+}  // namespace
+
 namespace blink {
 
-XRDepthManager::XRDepthManager(base::PassKey<XRSession> pass_key,
-                               XRSession* session)
-    : session_(session) {}
+XRDepthManager::XRDepthManager(
+    base::PassKey<XRSession> pass_key,
+    XRSession* session,
+    const device::mojom::blink::XRDepthConfig& depth_configuration)
+    : session_(session),
+      usage_(depth_configuration.depth_usage),
+      data_format_(depth_configuration.depth_data_format),
+      usage_str_(UsageToString(usage_)),
+      data_format_str_(DataFormatToString(data_format_)) {
+  DVLOG(3) << __func__ << ": usage_=" << usage_
+           << ", data_format_=" << data_format_;
+}
 
 XRDepthManager::~XRDepthManager() = default;
 
@@ -45,7 +76,7 @@
   }
 }
 
-XRDepthInformation* XRDepthManager::GetDepthInformation(
+XRCPUDepthInformation* XRDepthManager::GetDepthInformation(
     const XRFrame* xr_frame) {
   if (!depth_data_) {
     return nullptr;
@@ -53,9 +84,9 @@
 
   EnsureData();
 
-  return MakeGarbageCollected<XRDepthInformation>(
+  return MakeGarbageCollected<XRCPUDepthInformation>(
       xr_frame, depth_data_->size, depth_data_->norm_texture_from_norm_view,
-      data_);
+      depth_data_->raw_value_to_meters, data_);
 }
 
 void XRDepthManager::EnsureData() {
diff --git a/third_party/blink/renderer/modules/xr/xr_depth_manager.h b/third_party/blink/renderer/modules/xr/xr_depth_manager.h
index 4a37bb6..9fb6168f 100644
--- a/third_party/blink/renderer/modules/xr/xr_depth_manager.h
+++ b/third_party/blink/renderer/modules/xr/xr_depth_manager.h
@@ -12,7 +12,7 @@
 
 namespace blink {
 
-class XRDepthInformation;
+class XRCPUDepthInformation;
 class XRFrame;
 class XRSession;
 
@@ -20,19 +20,30 @@
 // out of XRSession.
 class XRDepthManager : public GarbageCollected<XRDepthManager> {
  public:
-  explicit XRDepthManager(base::PassKey<XRSession> pass_key,
-                          XRSession* session);
+  explicit XRDepthManager(
+      base::PassKey<XRSession> pass_key,
+      XRSession* session,
+      const device::mojom::blink::XRDepthConfig& device_configuration);
   virtual ~XRDepthManager();
 
   void ProcessDepthInformation(device::mojom::blink::XRDepthDataPtr depth_data);
 
-  XRDepthInformation* GetDepthInformation(const XRFrame* xr_frame);
+  const String& depthUsage() const { return usage_str_; }
+  const String& depthDataFormat() const { return data_format_str_; }
+
+  XRCPUDepthInformation* GetDepthInformation(const XRFrame* xr_frame);
 
   void Trace(Visitor* visitor) const;
 
  private:
   Member<XRSession> session_;
 
+  const device::mojom::XRDepthUsage usage_;
+  const device::mojom::XRDepthDataFormat data_format_;
+
+  const String usage_str_;
+  const String data_format_str_;
+
   // Current depth data buffer.
   device::mojom::blink::XRDepthDataUpdatedPtr depth_data_;
 
diff --git a/third_party/blink/renderer/modules/xr/xr_depth_state_init.idl b/third_party/blink/renderer/modules/xr/xr_depth_state_init.idl
new file mode 100644
index 0000000..801ed9b
--- /dev/null
+++ b/third_party/blink/renderer/modules/xr/xr_depth_state_init.idl
@@ -0,0 +1,18 @@
+// 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.
+
+enum XRDepthUsage {
+  "cpu-optimized",
+  "gpu-optimized"
+};
+
+enum XRDepthDataFormat {
+  "luminance-alpha",
+  "float32",
+};
+
+dictionary XRDepthStateInit {
+  required sequence<XRDepthUsage> usagePreference;
+  required sequence<XRDepthDataFormat> dataFormatPreference;
+};
diff --git a/third_party/blink/renderer/modules/xr/xr_frame.cc b/third_party/blink/renderer/modules/xr/xr_frame.cc
index 012ccd3..091e33ac 100644
--- a/third_party/blink/renderer/modules/xr/xr_frame.cc
+++ b/third_party/blink/renderer/modules/xr/xr_frame.cc
@@ -155,7 +155,7 @@
   return light_probe->getLightEstimate();
 }
 
-XRDepthInformation* XRFrame::getDepthInformation(
+XRCPUDepthInformation* XRFrame::getDepthInformation(
     XRView* view,
     ExceptionState& exception_state) const {
   DVLOG(2) << __func__;
@@ -178,7 +178,7 @@
     return nullptr;
   }
 
-  return session_->GetDepthInformation(this);
+  return session_->GetDepthInformation(this, exception_state);
 }
 
 // Return an XRPose that has a transform of basespace_from_space, while
diff --git a/third_party/blink/renderer/modules/xr/xr_frame.h b/third_party/blink/renderer/modules/xr/xr_frame.h
index 8224e4d..04e75a2 100644
--- a/third_party/blink/renderer/modules/xr/xr_frame.h
+++ b/third_party/blink/renderer/modules/xr/xr_frame.h
@@ -21,7 +21,7 @@
 
 class ExceptionState;
 class XRAnchorSet;
-class XRDepthInformation;
+class XRCPUDepthInformation;
 class XRHitTestResult;
 class XRHitTestSource;
 class XRImageTrackingResult;
@@ -53,7 +53,7 @@
   XRPose* getPose(XRSpace*, XRSpace*, ExceptionState&);
   XRAnchorSet* trackedAnchors() const;
   XRLightEstimate* getLightEstimate(XRLightProbe*, ExceptionState&) const;
-  XRDepthInformation* getDepthInformation(
+  XRCPUDepthInformation* getDepthInformation(
       XRView* view,
       ExceptionState& exception_state) const;
   XRPlaneSet* detectedPlanes(ExceptionState& exception_state) const;
diff --git a/third_party/blink/renderer/modules/xr/xr_frame.idl b/third_party/blink/renderer/modules/xr/xr_frame.idl
index 0bbee6db..c6c65b9 100644
--- a/third_party/blink/renderer/modules/xr/xr_frame.idl
+++ b/third_party/blink/renderer/modules/xr/xr_frame.idl
@@ -27,7 +27,7 @@
   XRLightEstimate? getLightEstimate(XRLightProbe lightProbe);
 
   [RuntimeEnabled=WebXRDepth, RaisesException, MeasureAs=XRFrameGetDepthInformation]
-  XRDepthInformation getDepthInformation(XRView view);
+  XRCPUDepthInformation? getDepthInformation(XRView view);
 
   [RuntimeEnabled=WebXRImageTracking, RaisesException]
   FrozenArray<XRImageTrackingResult> getImageTrackingResults();
diff --git a/third_party/blink/renderer/modules/xr/xr_session.cc b/third_party/blink/renderer/modules/xr/xr_session.cc
index a320965..1ca67a4 100644
--- a/third_party/blink/renderer/modules/xr/xr_session.cc
+++ b/third_party/blink/renderer/modules/xr/xr_session.cc
@@ -105,6 +105,9 @@
 const char kEntityTypesNotSpecified[] =
     "No entityTypes specified: the array cannot be empty!";
 
+const char kDepthSensingFeatureNotSupported[] =
+    "Depth sensing feature is not supported by the session.";
+
 const double kDegToRad = M_PI / 180.0;
 
 const float kMinDefaultFramebufferScale = 0.1f;
@@ -311,6 +314,16 @@
   }
 }
 
+XRDepthManager* XRSession::CreateDepthManagerIfEnabled(
+    const device::mojom::blink::XRSessionDeviceConfig& device_config) {
+  if (!device_config.depth_configuration) {
+    return nullptr;
+  }
+
+  return MakeGarbageCollected<XRDepthManager>(
+      base::PassKey<XRSession>{}, this, *device_config.depth_configuration);
+}
+
 XRSession::XRSession(
     XRSystem* xr,
     mojo::PendingReceiver<device::mojom::blink::XRSessionClient>
@@ -329,10 +342,7 @@
       plane_manager_(
           MakeGarbageCollected<XRPlaneManager>(base::PassKey<XRSession>{},
                                                this)),
-      depth_manager_(
-          MakeGarbageCollected<XRDepthManager>(base::PassKey<XRSession>{},
-                                               this)),
-
+      depth_manager_(CreateDepthManagerIfEnabled(*device_config)),
       input_sources_(MakeGarbageCollected<XRInputSourceArray>()),
       client_receiver_(this, xr->GetExecutionContext()),
       input_receiver_(this, xr->GetExecutionContext()),
@@ -478,6 +488,26 @@
   MaybeRequestFrame();
 }
 
+const String& XRSession::depthUsage(ExceptionState& exception_state) {
+  if (!depth_manager_) {
+    exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
+                                      kDepthSensingFeatureNotSupported);
+    return g_empty_string;
+  }
+
+  return depth_manager_->depthUsage();
+}
+
+const String& XRSession::depthDataFormat(ExceptionState& exception_state) {
+  if (!depth_manager_) {
+    exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
+                                      kDepthSensingFeatureNotSupported);
+    return g_empty_string;
+  }
+
+  return depth_manager_->depthDataFormat();
+}
+
 void XRSession::UpdateEyeParameters(
     const device::mojom::blink::VREyeParametersPtr& left_eye,
     const device::mojom::blink::VREyeParametersPtr& right_eye) {
@@ -1218,8 +1248,15 @@
   }
 }
 
-XRDepthInformation* XRSession::GetDepthInformation(
-    const XRFrame* xr_frame) const {
+XRCPUDepthInformation* XRSession::GetDepthInformation(
+    const XRFrame* xr_frame,
+    ExceptionState& exception_state) const {
+  if (!depth_manager_) {
+    exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
+                                      kDepthSensingFeatureNotSupported);
+    return nullptr;
+  }
+
   return depth_manager_->GetDepthInformation(xr_frame);
 }
 
@@ -1731,7 +1768,12 @@
         frame_data->detected_planes_data.get(), timestamp);
     ProcessAnchorsData(frame_data->anchors_data.get(), timestamp);
     ProcessHitTestData(frame_data->hit_test_subscription_results.get());
-    depth_manager_->ProcessDepthInformation(std::move(frame_data->depth_data));
+
+    if (depth_manager_) {
+      depth_manager_->ProcessDepthInformation(
+          std::move(frame_data->depth_data));
+    }
+
     ProcessTrackedImagesData(frame_data->tracked_images.get());
 
     const device::mojom::blink::XRLightEstimationData* light_data =
@@ -1743,7 +1785,11 @@
     plane_manager_->ProcessPlaneInformation(nullptr, timestamp);
     ProcessAnchorsData(nullptr, timestamp);
     ProcessHitTestData(nullptr);
-    depth_manager_->ProcessDepthInformation(nullptr);
+
+    if (depth_manager_) {
+      depth_manager_->ProcessDepthInformation(nullptr);
+    }
+
     ProcessTrackedImagesData(nullptr);
 
     if (world_light_probe_) {
diff --git a/third_party/blink/renderer/modules/xr/xr_session.h b/third_party/blink/renderer/modules/xr/xr_session.h
index 8ef610b..b9e2a5fc 100644
--- a/third_party/blink/renderer/modules/xr/xr_session.h
+++ b/third_party/blink/renderer/modules/xr/xr_session.h
@@ -42,7 +42,7 @@
 class XRAnchor;
 class XRAnchorSet;
 class XRCanvasInputProvider;
-class XRDepthInformation;
+class XRCPUDepthInformation;
 class XRDepthManager;
 class XRDOMOverlayState;
 class XRHitTestOptionsInit;
@@ -149,6 +149,10 @@
 
   void updateRenderState(XRRenderStateInit* render_state_init,
                          ExceptionState& exception_state);
+
+  const String& depthUsage(ExceptionState& exception_state);
+  const String& depthDataFormat(ExceptionState& exception_state);
+
   ScriptPromise requestReferenceSpace(ScriptState* script_state,
                                       const String& type,
                                       ExceptionState&);
@@ -337,7 +341,9 @@
   base::Optional<TransformationMatrix> GetMojoFrom(
       device::mojom::blink::XRReferenceSpaceType space_type) const;
 
-  XRDepthInformation* GetDepthInformation(const XRFrame* xr_frame) const;
+  XRCPUDepthInformation* GetDepthInformation(
+      const XRFrame* xr_frame,
+      ExceptionState& exception_state) const;
 
   XRPlaneSet* GetDetectedPlanes() const;
 
@@ -450,6 +456,11 @@
 
   void ExecuteVideoFrameCallbacks(double timestamp);
 
+  // Helper, creates an instance of depth manager if depth sensing API is
+  // enabled in the session configuration.
+  XRDepthManager* CreateDepthManagerIfEnabled(
+      const device::mojom::blink::XRSessionDeviceConfig& device_config);
+
   const Member<XRSystem> xr_;
   const device::mojom::blink::XRSessionMode mode_;
   const bool environment_integration_;
diff --git a/third_party/blink/renderer/modules/xr/xr_session.idl b/third_party/blink/renderer/modules/xr/xr_session.idl
index bc44f22..9dc54c9 100644
--- a/third_party/blink/renderer/modules/xr/xr_session.idl
+++ b/third_party/blink/renderer/modules/xr/xr_session.idl
@@ -81,4 +81,9 @@
 
   [RuntimeEnabled=WebXRImageTracking, CallWith=ScriptState, RaisesException]
   Promise<FrozenArray<XRImageTrackingScore>> getTrackedImageScores();
+
+  [RuntimeEnabled=WebXRDepth, RaisesException]
+  readonly attribute XRDepthUsage? depthUsage;
+  [RuntimeEnabled=WebXRDepth, RaisesException]
+  readonly attribute XRDepthDataFormat? depthDataFormat;
 };
diff --git a/third_party/blink/renderer/modules/xr/xr_session_init.idl b/third_party/blink/renderer/modules/xr/xr_session_init.idl
index 2e1b5d7..24076b8 100644
--- a/third_party/blink/renderer/modules/xr/xr_session_init.idl
+++ b/third_party/blink/renderer/modules/xr/xr_session_init.idl
@@ -8,4 +8,5 @@
   sequence<any> optionalFeatures;
   XRDOMOverlayInit domOverlay;
   [RuntimeEnabled=WebXRImageTracking] sequence<XRTrackedImageInit> trackedImages;
+  [RuntimeEnabled=WebXRDepth] XRDepthStateInit depthSensing;
 };
diff --git a/third_party/blink/renderer/modules/xr/xr_system.cc b/third_party/blink/renderer/modules/xr/xr_system.cc
index 0716ffd3..2dd32b00 100644
--- a/third_party/blink/renderer/modules/xr/xr_system.cc
+++ b/third_party/blink/renderer/modules/xr/xr_system.cc
@@ -6,6 +6,7 @@
 
 #include <utility>
 
+#include "device/vr/public/mojom/vr_service.mojom-blink.h"
 #include "services/metrics/public/cpp/ukm_builders.h"
 #include "third_party/blink/public/common/browser_interface_broker_proxy.h"
 #include "third_party/blink/public/mojom/feature_policy/feature_policy.mojom-blink.h"
@@ -29,6 +30,7 @@
 #include "third_party/blink/renderer/core/loader/document_loader.h"
 #include "third_party/blink/renderer/modules/event_modules.h"
 #include "third_party/blink/renderer/modules/event_target_modules.h"
+#include "third_party/blink/renderer/modules/xr/xr_depth_state_init.h"
 #include "third_party/blink/renderer/modules/xr/xr_frame_provider.h"
 #include "third_party/blink/renderer/modules/xr/xr_session.h"
 #include "third_party/blink/renderer/modules/xr/xr_session_viewport_scaler.h"
@@ -69,6 +71,10 @@
 const char kTrackedImageWidthInvalid[] =
     "trackedImages[%d].widthInMeters invalid, must be a positive number.";
 
+const char kDepthSensingConfigurationNotSupported[] =
+    "The provided preferences depth sensing usage and format are not "
+    "supported, unable to create the session.";
+
 constexpr device::mojom::XRSessionFeature kDefaultImmersiveVrFeatures[] = {
     device::mojom::XRSessionFeature::REF_SPACE_VIEWER,
     device::mojom::XRSessionFeature::REF_SPACE_LOCAL,
@@ -113,6 +119,48 @@
   return "";
 }
 
+device::mojom::XRDepthUsage ParseDepthUsage(const String& usage) {
+  if (usage == "cpu-optimized") {
+    return device::mojom::XRDepthUsage::kCPUOptimized;
+  } else if (usage == "gpu-optimized") {
+    return device::mojom::XRDepthUsage::kGPUOptimized;
+  }
+
+  NOTREACHED() << "Only strings in the enum are allowed by IDL";
+  return device::mojom::XRDepthUsage::kCPUOptimized;
+}
+
+Vector<device::mojom::XRDepthUsage> ParseDepthUsages(
+    const Vector<String>& usages) {
+  Vector<device::mojom::XRDepthUsage> result;
+
+  std::transform(usages.begin(), usages.end(), std::back_inserter(result),
+                 ParseDepthUsage);
+
+  return result;
+}
+
+device::mojom::XRDepthDataFormat ParseDepthFormat(const String& format) {
+  if (format == "luminance-alpha") {
+    return device::mojom::XRDepthDataFormat::kLuminanceAlpha;
+  } else if (format == "float32") {
+    return device::mojom::XRDepthDataFormat::kFloat32;
+  }
+
+  NOTREACHED() << "Only strings in the enum are allowed by IDL";
+  return device::mojom::XRDepthDataFormat::kLuminanceAlpha;
+}
+
+Vector<device::mojom::XRDepthDataFormat> ParseDepthFormats(
+    const Vector<String>& formats) {
+  Vector<device::mojom::XRDepthDataFormat> result;
+
+  std::transform(formats.begin(), formats.end(), std::back_inserter(result),
+                 ParseDepthFormat);
+
+  return result;
+}
+
 // Converts the given string to an XRSessionFeature. If the string is
 // unrecognized, returns nullopt. Based on the spec:
 // https://immersive-web.github.io/webxr/#feature-name
@@ -202,9 +250,17 @@
     case device::mojom::XRSessionFeature::LIGHT_ESTIMATION:
     case device::mojom::XRSessionFeature::CAMERA_ACCESS:
     case device::mojom::XRSessionFeature::PLANE_DETECTION:
+      // Fallthrough - light estimation, camera access, and plane detection are
+      // all valid only for immersive AR mode for now.
+      return mode == device::mojom::blink::XRSessionMode::kImmersiveAr;
     case device::mojom::XRSessionFeature::DEPTH:
-      // Fallthrough - light estimation, camera access, plane detection and
-      // depth APIs are all valid only for immersive AR mode for now.
+      if (!session_init->hasDepthSensing()) {
+        execution_context->AddConsoleMessage(
+            MakeGarbageCollected<ConsoleMessage>(
+                mojom::blink::ConsoleMessageSource::kJavaScript, error_level,
+                "Must provide a depthSensing dictionary in XRSessionInit"));
+        return false;
+      }
       return mode == device::mojom::blink::XRSessionMode::kImmersiveAr;
   }
 }
@@ -800,6 +856,15 @@
         device::mojom::blink::XRTrackedImage::New();
     *session_options->tracked_images[i] = query.TrackedImages()[i];
   }
+
+  if (query.HasFeature(device::mojom::XRSessionFeature::DEPTH)) {
+    session_options->depth_options =
+        device::mojom::blink::XRDepthOptions::New();
+    session_options->depth_options->usage_preferences = query.PreferredUsage();
+    session_options->depth_options->data_format_preferences =
+        query.PreferredFormat();
+  }
+
   return session_options;
 }
 
@@ -1369,6 +1434,36 @@
     query->SetTrackedImages(images);
   }
 
+  if (query->HasFeature(device::mojom::XRSessionFeature::DEPTH)) {
+    // Prerequisites were checked by IsFeatureValidForMode and IDL.
+    DCHECK(session_init);
+    DCHECK(session_init->hasDepthSensing());
+    DCHECK(session_init->depthSensing()->hasUsagePreference())
+        << "required in IDL";
+    DCHECK(session_init->depthSensing()->hasDataFormatPreference())
+        << "required in IDL";
+
+    Vector<device::mojom::XRDepthUsage> preferred_usage =
+        ParseDepthUsages(session_init->depthSensing()->usagePreference());
+    Vector<device::mojom::XRDepthDataFormat> preferred_format =
+        ParseDepthFormats(session_init->depthSensing()->dataFormatPreference());
+
+    // If the depth API is required and either preferred usages or preferred
+    // formats are empty, we already know that the session creation will fail
+    // (as we won't be able to pick a supported usage & format combination), so
+    // let's fail it already:
+    if (query->RequiredFeatures().Contains(
+            device::mojom::XRSessionFeature::DEPTH) &&
+        (preferred_usage.IsEmpty() || preferred_format.IsEmpty())) {
+      query->RejectWithDOMException(DOMExceptionCode::kNotSupportedError,
+                                    kDepthSensingConfigurationNotSupported,
+                                    &exception_state);
+      return promise;
+    }
+
+    query->SetDepthSensingConfiguration(preferred_usage, preferred_format);
+  }
+
   // The various session request methods may have other checks that would reject
   // before needing to create the vr service, so we don't try to create it here.
   switch (session_mode) {
diff --git a/third_party/blink/renderer/modules/xr/xr_system.h b/third_party/blink/renderer/modules/xr/xr_system.h
index 4640a3b..fb649c8 100644
--- a/third_party/blink/renderer/modules/xr/xr_system.h
+++ b/third_party/blink/renderer/modules/xr/xr_system.h
@@ -228,6 +228,21 @@
       return tracked_images_;
     }
 
+    void SetDepthSensingConfiguration(
+        const Vector<device::mojom::XRDepthUsage>& preferred_usage,
+        const Vector<device::mojom::XRDepthDataFormat>& preferred_format) {
+      preferred_usage_ = preferred_usage;
+      preferred_format_ = preferred_format;
+    }
+
+    const Vector<device::mojom::XRDepthUsage>& PreferredUsage() const {
+      return preferred_usage_;
+    }
+
+    const Vector<device::mojom::XRDepthDataFormat>& PreferredFormat() const {
+      return preferred_format_;
+    }
+
     virtual void Trace(Visitor*) const;
 
    private:
@@ -253,6 +268,9 @@
 
     Vector<device::mojom::blink::XRTrackedImage> tracked_images_;
 
+    Vector<device::mojom::XRDepthUsage> preferred_usage_;
+    Vector<device::mojom::XRDepthDataFormat> preferred_format_;
+
     DISALLOW_COPY_AND_ASSIGN(PendingRequestSessionQuery);
   };
 
diff --git a/third_party/blink/renderer/modules/xr/xr_webgl_binding.cc b/third_party/blink/renderer/modules/xr/xr_webgl_binding.cc
index 111a122a..56d987b 100644
--- a/third_party/blink/renderer/modules/xr/xr_webgl_binding.cc
+++ b/third_party/blink/renderer/modules/xr/xr_webgl_binding.cc
@@ -157,6 +157,12 @@
   return texture;
 }
 
+XRWebGLDepthInformation* XRWebGLBinding::getDepthInformation(
+    XRView* view,
+    ExceptionState& exception_state) {
+  return nullptr;
+}
+
 void XRWebGLBinding::Trace(Visitor* visitor) const {
   visitor->Trace(session_);
   visitor->Trace(webgl_context_);
diff --git a/third_party/blink/renderer/modules/xr/xr_webgl_binding.h b/third_party/blink/renderer/modules/xr/xr_webgl_binding.h
index 8d2bdce..55f88c3 100644
--- a/third_party/blink/renderer/modules/xr/xr_webgl_binding.h
+++ b/third_party/blink/renderer/modules/xr/xr_webgl_binding.h
@@ -19,6 +19,7 @@
 class XRLightProbe;
 class XRSession;
 class XRView;
+class XRWebGLDepthInformation;
 
 class XRWebGLBinding final : public ScriptWrappable {
   DEFINE_WRAPPERTYPEINFO();
@@ -36,6 +37,9 @@
   WebGLTexture* getReflectionCubeMap(XRLightProbe*, ExceptionState&);
   WebGLTexture* getCameraImage(XRFrame*, XRView*);
 
+  XRWebGLDepthInformation* getDepthInformation(XRView* view,
+                                               ExceptionState& exception_state);
+
   void Trace(Visitor*) const override;
 
  private:
diff --git a/third_party/blink/renderer/modules/xr/xr_webgl_binding.idl b/third_party/blink/renderer/modules/xr/xr_webgl_binding.idl
index 4b350b22..a2b0851b 100644
--- a/third_party/blink/renderer/modules/xr/xr_webgl_binding.idl
+++ b/third_party/blink/renderer/modules/xr/xr_webgl_binding.idl
@@ -14,4 +14,7 @@
   WebGLTexture? getReflectionCubeMap(XRLightProbe lightProbe);
 
   [RuntimeEnabled=WebXRCameraAccess] WebGLTexture? getCameraImage(XRFrame frame, XRView view);
+
+  [RuntimeEnabled=WebXRDepth, RaisesException, MeasureAs=XRWebGLBindingGetDepthInformation]
+  XRWebGLDepthInformation? getDepthInformation(XRView view);
 };
diff --git a/third_party/blink/renderer/modules/xr/xr_webgl_depth_information.cc b/third_party/blink/renderer/modules/xr/xr_webgl_depth_information.cc
new file mode 100644
index 0000000..afc2197
--- /dev/null
+++ b/third_party/blink/renderer/modules/xr/xr_webgl_depth_information.cc
@@ -0,0 +1,14 @@
+// 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 "third_party/blink/renderer/modules/xr/xr_webgl_depth_information.h"
+
+namespace blink {
+
+WebGLTexture* XRWebGLDepthInformation::texture(
+    ExceptionState& exception_state) {
+  return nullptr;
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/xr/xr_webgl_depth_information.h b/third_party/blink/renderer/modules/xr/xr_webgl_depth_information.h
new file mode 100644
index 0000000..4ed0ed6
--- /dev/null
+++ b/third_party/blink/renderer/modules/xr/xr_webgl_depth_information.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 THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_WEBGL_DEPTH_INFORMATION_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_WEBGL_DEPTH_INFORMATION_H_
+
+#include "third_party/blink/renderer/modules/xr/xr_depth_information.h"
+
+namespace blink {
+
+class ExceptionState;
+class WebGLTexture;
+
+class XRWebGLDepthInformation final : public XRDepthInformation {
+  DEFINE_WRAPPERTYPEINFO();
+
+ public:
+  WebGLTexture* texture(ExceptionState& exception_state);
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_WEBGL_DEPTH_INFORMATION_H_
diff --git a/third_party/blink/renderer/modules/xr/xr_webgl_depth_information.idl b/third_party/blink/renderer/modules/xr/xr_webgl_depth_information.idl
new file mode 100644
index 0000000..e86904769
--- /dev/null
+++ b/third_party/blink/renderer/modules/xr/xr_webgl_depth_information.idl
@@ -0,0 +1,13 @@
+// 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.
+
+[
+    SecureContext,
+    Exposed=Window,
+    RuntimeEnabled=WebXRDepth
+] interface XRWebGLDepthInformation : XRDepthInformation {
+  [RaisesException, SameObject, MeasureAs=XRWebGLDepthInformationTextureAttribute]
+  readonly attribute WebGLTexture texture;
+};
+
diff --git a/third_party/blink/renderer/platform/heap/BUILD.gn b/third_party/blink/renderer/platform/heap/BUILD.gn
index f7e78ee..58652b5 100644
--- a/third_party/blink/renderer/platform/heap/BUILD.gn
+++ b/third_party/blink/renderer/platform/heap/BUILD.gn
@@ -100,6 +100,7 @@
   if (enable_blink_heap_use_v8_oilpan) {
     sources += [
       "v8_wrapper/blink_gc.h",
+      "v8_wrapper/blink_gc_memory_dump_provider.cc",
       "v8_wrapper/blink_gc_memory_dump_provider.h",
       "v8_wrapper/collection_support/heap_hash_table_backing.h",
       "v8_wrapper/collection_support/heap_vector_backing.h",
diff --git a/third_party/blink/renderer/platform/heap/v8_wrapper/blink_gc_memory_dump_provider.cc b/third_party/blink/renderer/platform/heap/v8_wrapper/blink_gc_memory_dump_provider.cc
new file mode 100644
index 0000000..1f08954
--- /dev/null
+++ b/third_party/blink/renderer/platform/heap/v8_wrapper/blink_gc_memory_dump_provider.cc
@@ -0,0 +1,56 @@
+// 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 "third_party/blink/renderer/platform/heap/v8_wrapper/blink_gc_memory_dump_provider.h"
+
+#include <inttypes.h>
+
+#include "base/strings/stringprintf.h"
+#include "base/trace_event/memory_dump_manager.h"
+
+namespace blink {
+namespace {
+
+constexpr const char* HeapTypeString(
+    BlinkGCMemoryDumpProvider::HeapType heap_type) {
+  switch (heap_type) {
+    case BlinkGCMemoryDumpProvider::HeapType::kBlinkMainThread:
+      return "main";
+    case BlinkGCMemoryDumpProvider::HeapType::kBlinkWorkerThread:
+      return "workers";
+  }
+}
+
+}  // namespace
+
+BlinkGCMemoryDumpProvider::BlinkGCMemoryDumpProvider(
+    ThreadState* thread_state,
+    scoped_refptr<base::SingleThreadTaskRunner> task_runner,
+    BlinkGCMemoryDumpProvider::HeapType heap_type)
+    : thread_state_(thread_state),
+      heap_type_(heap_type),
+      dump_base_name_(
+          "blink_gc/" + std::string(HeapTypeString(heap_type_)) + "/heap" +
+          (heap_type_ == HeapType::kBlinkWorkerThread
+               ? "/" + base::StringPrintf(
+                           "worker_0x%" PRIXPTR,
+                           reinterpret_cast<uintptr_t>(thread_state_))
+               : "")) {
+  base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
+      this, "BlinkGC", task_runner);
+}
+
+BlinkGCMemoryDumpProvider::~BlinkGCMemoryDumpProvider() {
+  base::trace_event::MemoryDumpManager::GetInstance()->UnregisterDumpProvider(
+      this);
+}
+
+bool BlinkGCMemoryDumpProvider::OnMemoryDump(
+    const base::trace_event::MemoryDumpArgs& args,
+    base::trace_event::ProcessMemoryDump* process_memory_dump) {
+  // TODO(chromium:1056170): Provide implementation.
+  return false;
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/heap/v8_wrapper/blink_gc_memory_dump_provider.h b/third_party/blink/renderer/platform/heap/v8_wrapper/blink_gc_memory_dump_provider.h
index 8773bd7..0bb476f 100644
--- a/third_party/blink/renderer/platform/heap/v8_wrapper/blink_gc_memory_dump_provider.h
+++ b/third_party/blink/renderer/platform/heap/v8_wrapper/blink_gc_memory_dump_provider.h
@@ -5,6 +5,40 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_V8_WRAPPER_BLINK_GC_MEMORY_DUMP_PROVIDER_H_
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_V8_WRAPPER_BLINK_GC_MEMORY_DUMP_PROVIDER_H_
 
-// TODO(chromium:1056170): Implement wrapper.
+#include "base/trace_event/memory_dump_provider.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
+
+namespace base {
+class SingleThreadTaskRunner;
+}  // namespace base
+
+namespace blink {
+
+class ThreadState;
+
+class PLATFORM_EXPORT BlinkGCMemoryDumpProvider final
+    : public base::trace_event::MemoryDumpProvider {
+  USING_FAST_MALLOC(BlinkGCMemoryDumpProvider);
+
+ public:
+  enum class HeapType { kBlinkMainThread, kBlinkWorkerThread };
+
+  ~BlinkGCMemoryDumpProvider() final;
+  BlinkGCMemoryDumpProvider(ThreadState*,
+                            scoped_refptr<base::SingleThreadTaskRunner>,
+                            HeapType);
+
+  // MemoryDumpProvider implementation.
+  bool OnMemoryDump(const base::trace_event::MemoryDumpArgs&,
+                    base::trace_event::ProcessMemoryDump*) final;
+
+ private:
+  ThreadState* const thread_state_;
+  const HeapType heap_type_;
+  const std::string dump_base_name_;
+};
+
+}  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_V8_WRAPPER_BLINK_GC_MEMORY_DUMP_PROVIDER_H_
diff --git a/third_party/blink/renderer/platform/heap/v8_wrapper/heap.h b/third_party/blink/renderer/platform/heap/v8_wrapper/heap.h
index b3ee5ed..e81eb2f4 100644
--- a/third_party/blink/renderer/platform/heap/v8_wrapper/heap.h
+++ b/third_party/blink/renderer/platform/heap/v8_wrapper/heap.h
@@ -5,6 +5,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_V8_WRAPPER_HEAP_H_
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_V8_WRAPPER_HEAP_H_
 
+#include "third_party/blink/renderer/platform/heap/v8_wrapper/process_heap.h"
 #include "third_party/blink/renderer/platform/heap/v8_wrapper/thread_state.h"
 #include "v8/include/cppgc/allocation.h"
 #include "v8/include/cppgc/garbage-collected.h"
diff --git a/third_party/blink/renderer/platform/heap/v8_wrapper/process_heap.h b/third_party/blink/renderer/platform/heap/v8_wrapper/process_heap.h
index 254f163b..a0876e35 100644
--- a/third_party/blink/renderer/platform/heap/v8_wrapper/process_heap.h
+++ b/third_party/blink/renderer/platform/heap/v8_wrapper/process_heap.h
@@ -5,6 +5,23 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_V8_WRAPPER_PROCESS_HEAP_H_
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_V8_WRAPPER_PROCESS_HEAP_H_
 
-// TODO(chromium:1056170): Implement wrapper.
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
+
+namespace blink {
+
+// TODO(1056170): Implement wrapper.
+class PLATFORM_EXPORT ProcessHeap {
+  STATIC_ONLY(ProcessHeap);
+
+ public:
+  static void Init() {}
+
+  static size_t TotalAllocatedObjectSize() { return 0; }
+
+  static size_t TotalAllocatedSpace() { return 0; }
+};
+
+}  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_V8_WRAPPER_PROCESS_HEAP_H_
diff --git a/third_party/blink/renderer/platform/heap/v8_wrapper/thread_state.h b/third_party/blink/renderer/platform/heap/v8_wrapper/thread_state.h
index 640b6eca..51f5bd3d 100644
--- a/third_party/blink/renderer/platform/heap/v8_wrapper/thread_state.h
+++ b/third_party/blink/renderer/platform/heap/v8_wrapper/thread_state.h
@@ -98,6 +98,9 @@
     // TODO(1056170): Implement, if necessary for testing.
   }
 
+  bool IsMainThread() const { return this == MainThreadState(); }
+  bool IsCreationThread() const { return thread_id_ == CurrentThread(); }
+
  private:
   // Main-thread ThreadState avoids TLS completely by using a regular global.
   // The object is manually managed and should not rely on global ctor/dtor.
@@ -110,9 +113,6 @@
   explicit ThreadState(v8::CppHeap&);
   ~ThreadState();
 
-  bool IsMainThread() const { return this == MainThreadState(); }
-  bool IsCreationThread() const { return thread_id_ == CurrentThread(); }
-
   // Handle is the most frequently accessed field as it is required for
   // MakeGarbageCollected().
   cppgc::AllocationHandle& allocation_handle_;
diff --git a/third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder_test.cc b/third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder_test.cc
index 123ad3c6..06050bc 100644
--- a/third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder_test.cc
+++ b/third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder_test.cc
@@ -11,11 +11,18 @@
 
 #include "base/bit_cast.h"
 #include "base/strings/stringprintf.h"
+#include "media/media_buildflags.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/renderer/platform/image-decoders/image_decoder_test_helpers.h"
 #include "third_party/blink/renderer/platform/wtf/shared_buffer.h"
 #include "third_party/libavif/src/include/avif/avif.h"
 
+// If the AV1 decoder library supports the bit depth 12, define
+// HAVE_AVIF_BIT_DEPTH_12_SUPPORT.
+#if BUILDFLAG(ENABLE_DAV1D_DECODER)
+#define HAVE_AVIF_BIT_DEPTH_12_SUPPORT
+#endif
+
 #define FIXME_SUPPORT_ICC_PROFILE_NO_TRANSFORM 0
 #define FIXME_SUPPORT_ICC_PROFILE_TRANSFORM 0
 #define FIXME_DISTINGUISH_LOSSY_OR_LOSSLESS 0
@@ -726,6 +733,10 @@
                 const IntSize& expected_uv_size,
                 SkColorType color_type = kGray_8_SkColorType,
                 int bit_depth = 8) {
+#if !defined(HAVE_AVIF_BIT_DEPTH_12_SUPPORT)
+  if (bit_depth == 12)
+    return;
+#endif
   SCOPED_TRACE(base::StringPrintf("file_name=%s, color_type=%d", file_name,
                                   int{color_type}));
 
@@ -769,6 +780,7 @@
       &CreateAVIFDecoder,
       "/images/resources/avif/star-animated-10bpc-with-alpha.avif", 5u,
       kAnimationLoopInfinite);
+#if defined(HAVE_AVIF_BIT_DEPTH_12_SUPPORT)
   TestByteByByteDecode(&CreateAVIFDecoder,
                        "/images/resources/avif/star-animated-12bpc.avif", 5u,
                        kAnimationLoopInfinite);
@@ -776,6 +788,7 @@
       &CreateAVIFDecoder,
       "/images/resources/avif/star-animated-12bpc-with-alpha.avif", 5u,
       kAnimationLoopInfinite);
+#endif
   // TODO(ryoh): Add animated avif files with EditListBox.
 }
 
@@ -822,10 +835,12 @@
       &CreateAVIFDecoder,
       "/images/resources/avif/red-at-12-oclock-with-color-profile-10bpc.avif",
       1, kAnimationNone);
+#if defined(HAVE_AVIF_BIT_DEPTH_12_SUPPORT)
   TestByteByByteDecode(
       &CreateAVIFDecoder,
       "/images/resources/avif/red-at-12-oclock-with-color-profile-12bpc.avif",
       1, kAnimationNone);
+#endif
 }
 
 TEST(StaticAVIFTests, YUV) {
@@ -880,6 +895,10 @@
 
 TEST_P(StaticAVIFColorTests, InspectImage) {
   const StaticColorCheckParam& param = GetParam();
+#if !defined(HAVE_AVIF_BIT_DEPTH_12_SUPPORT)
+  if (param.bit_depth == 12)
+    return;
+#endif
   // TODO(ryoh): Add tests with ImageDecoder::kHighBitDepthToHalfFloat
   std::unique_ptr<ImageDecoder> decoder = CreateAVIFDecoderWithOptions(
       param.alpha_option, ImageDecoder::kDefaultBitDepth, param.color_behavior,
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index aae587af..22bb0c5a 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -139,7 +139,7 @@
     },
     {
       name: "AddEventListenerAbortSignal",
-      status: "experimental",
+      status: "stable",
     },
     {
       name: "AddressSpace",
diff --git a/third_party/blink/renderer/platform/wtf/allocator/allocator.h b/third_party/blink/renderer/platform/wtf/allocator/allocator.h
index 4d25949c..13f4753 100644
--- a/third_party/blink/renderer/platform/wtf/allocator/allocator.h
+++ b/third_party/blink/renderer/platform/wtf/allocator/allocator.h
@@ -28,10 +28,7 @@
 // non-garbage-collected objects to avoid unintended allocations.
 //
 // STACK_ALLOCATED(): Use if the object is only stack allocated.
-// Garbage-collected objects should be in Members but you do not need the
-// trace method as they are on the stack.  (Down the line these might turn
-// in to raw pointers, but for now Members indicate that we have thought
-// about them and explicitly taken care of them.)
+// Garbage-collected objects should be in raw pointers.
 //
 // DISALLOW_NEW(): Cannot be allocated with new operators but can be a
 // part of object, a value object in collections or stack allocated. If it has
diff --git a/third_party/blink/web_tests/MSANExpectations b/third_party/blink/web_tests/MSANExpectations
index 39b5ade6..c8ee7e47d 100644
--- a/third_party/blink/web_tests/MSANExpectations
+++ b/third_party/blink/web_tests/MSANExpectations
@@ -149,7 +149,7 @@
 crbug.com/1095518 [ Linux ] crbug.com/840659 external/wpt/webrtc/simulcast/h264.https.html [ Crash ]
 
 # Sheriff 2020-10-05
-crbug.com/1134580 [ Linux ] virtual/eye-dropper/color-picker-show-eye-dropper.html [ Pass Timeout ]
+crbug.com/1134580 [ Linux ] virtual/eye-dropper/http/tests/eye-dropper/color-picker-show-eye-dropper.html [ Pass Timeout ]
 
 # Sheriff 2020-11-25
 crbug.com/1152088 [ Linux ] fast/dom/cssTarget-crash.html [ Pass Timeout ]
diff --git a/third_party/blink/web_tests/NeverFixTests b/third_party/blink/web_tests/NeverFixTests
index 8b7d95d6..f4ed037 100644
--- a/third_party/blink/web_tests/NeverFixTests
+++ b/third_party/blink/web_tests/NeverFixTests
@@ -2254,3 +2254,9 @@
 crbug.com/626703 external/wpt/uievents/order-of-events/mouse-events/mouseevents-mousemove-manual.htm [ Skip ]
 crbug.com/626703 external/wpt/uievents/order-of-events/focus-events/legacy-manual.html [ Skip ]
 crbug.com/626703 external/wpt/pointerevents/pointerevent_pointerId_scope-manual.html [ Skip ]
+
+# Requires EyeDropper feature to pass. Remove from virtual tests and this file
+# once that feature is on by default.
+http/tests/eye-dropper/color-picker-show-eye-dropper.html [ Skip ]
+virtual/eye-dropper/http/tests/eye-dropper/color-picker-show-eye-dropper.html [ Pass ]
+virtual/dark-color-scheme/http/tests/eye-dropper/color-picker-show-eye-dropper.html [ Pass ]
diff --git a/third_party/blink/web_tests/SlowTests b/third_party/blink/web_tests/SlowTests
index cad854cb..bf6ad01 100644
--- a/third_party/blink/web_tests/SlowTests
+++ b/third_party/blink/web_tests/SlowTests
@@ -525,7 +525,7 @@
 crbug.com/1104091 [ Linux ] webcodecs/videoframe-imagebitmap.html [ Slow ]
 crbug.com/1093478 external/wpt/quirks/unitless-length/limited-quirks.html [ Slow ]
 crbug.com/1133836 external/wpt/scroll-to-text-fragment/redirects.html [ Slow ]
-crbug.com/1134580 virtual/eye-dropper/color-picker-show-eye-dropper.html [ Slow ]
+crbug.com/1134580 virtual/eye-dropper/http/tests/eye-dropper/color-picker-show-eye-dropper.html [ Slow ]
 crbug.com/1145716 fast/forms/calendar-picker/datetimelocal-picker-open-to-focused-field.html [ Slow ]
 
 # These began to hit timeouts when moving from Windows-10-15063 to Windows-10-18363
diff --git a/third_party/blink/web_tests/VirtualTestSuites b/third_party/blink/web_tests/VirtualTestSuites
index 4a8c14c6..05fd9cb 100644
--- a/third_party/blink/web_tests/VirtualTestSuites
+++ b/third_party/blink/web_tests/VirtualTestSuites
@@ -552,7 +552,9 @@
   },
   {
     "prefix": "eye-dropper",
-    "bases": ["fast/forms/color-scheme/color"],
+    "bases": [
+      "fast/forms/color-scheme/color",
+      "http/tests/eye-dropper"],
     "args": ["--enable-features=EyeDropper"]
   },
   {
@@ -582,7 +584,7 @@
     "prefix": "dark-color-scheme",
     "bases": ["external/wpt/css/css-color-adjust/rendering/dark-color-scheme",
               "fast/forms/color-scheme",
-              "virtual/eye-dropper"],
+              "http/tests/eye-dropper"],
     "args": ["--force-dark-mode",
              "--enable-blink-features=CSSColorScheme,CSSColorSchemeUARendering",
              "--enable-features=EyeDropper"]
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
index 7f8befb..ddf35be 100644
--- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
+++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
@@ -64640,6 +64640,71 @@
        {}
       ]
      ],
+     "flex-minimum-height-flex-items-026.html": [
+      "9f46c953c4a8396c7dcb58012b16235c0b9e5377",
+      [
+       null,
+       [
+        [
+         "/css/reference/ref-filled-green-100px-square-only.html",
+         "=="
+        ]
+       ],
+       {}
+      ]
+     ],
+     "flex-minimum-height-flex-items-027.html": [
+      "052ff6a5bd89c56b51d76a1782ac87d1a9d1e2c3",
+      [
+       null,
+       [
+        [
+         "/css/reference/ref-filled-green-100px-square-only.html",
+         "=="
+        ]
+       ],
+       {}
+      ]
+     ],
+     "flex-minimum-height-flex-items-028.html": [
+      "45cf76837d345b8e84daec72462f8ae8c42f19b5",
+      [
+       null,
+       [
+        [
+         "/css/reference/ref-filled-green-100px-square-only.html",
+         "=="
+        ]
+       ],
+       {}
+      ]
+     ],
+     "flex-minimum-height-flex-items-029.html": [
+      "62b77b92fc91f206d96b6208d65bfb3bd524088f",
+      [
+       null,
+       [
+        [
+         "/css/reference/ref-filled-green-100px-square-only.html",
+         "=="
+        ]
+       ],
+       {}
+      ]
+     ],
+     "flex-minimum-height-flex-items-030.html": [
+      "a3dc56e59e433e64a6f024c5d9ea8264cb658210",
+      [
+       null,
+       [
+        [
+         "/css/reference/ref-filled-green-100px-square-only.html",
+         "=="
+        ]
+       ],
+       {}
+      ]
+     ],
      "flex-minimum-width-flex-items-001.xht": [
       "cd18483ba414160c46e30bc282dec0c2fcd2f418",
       [
@@ -64809,6 +64874,32 @@
        {}
       ]
      ],
+     "flex-minimum-width-flex-items-015.html": [
+      "d4020fe75e3ee1e52b07576132f8db2f880ebfcd",
+      [
+       null,
+       [
+        [
+         "/css/reference/ref-filled-green-100px-square.xht",
+         "=="
+        ]
+       ],
+       {}
+      ]
+     ],
+     "flex-minimum-width-flex-items-016.html": [
+      "a3f4eb4a85cebf937e5568f7431358af71e388e3",
+      [
+       null,
+       [
+        [
+         "/css/reference/ref-filled-green-100px-square-only.html",
+         "=="
+        ]
+       ],
+       {}
+      ]
+     ],
      "flex-order.html": [
       "be24b2817e232b7ad7fc936b7b22787591d3d9db",
       [
@@ -66083,6 +66174,19 @@
        {}
       ]
      ],
+     "flexbox-definite-sizes-006.html": [
+      "4b5aaf8dc443ed986dafc334c79dd3d7627698f8",
+      [
+       null,
+       [
+        [
+         "/css/reference/ref-filled-green-100px-square-only.html",
+         "=="
+        ]
+       ],
+       {}
+      ]
+     ],
      "flexbox-dyn-resize-001.html": [
       "d64c4bdf28ecb783af4f342d515dcf63134602c6",
       [
@@ -76403,6 +76507,84 @@
         {}
        ]
       ],
+      "replaced-alignment-with-aspect-ratio-004.html": [
+       "437b379332e90ab7ae4a48c17d3f9c7730ba3b98",
+       [
+        null,
+        [
+         [
+          "/css/reference/ref-filled-green-100px-square.xht",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ],
+      "replaced-alignment-with-aspect-ratio-005.html": [
+       "b14c45d0c2d9cb93577b71df90d430aff37734d6",
+       [
+        null,
+        [
+         [
+          "/css/reference/ref-filled-green-100px-square.xht",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ],
+      "replaced-alignment-with-aspect-ratio-006.html": [
+       "ed14e36057948ad5c90f702b6181adba8a72cca7",
+       [
+        null,
+        [
+         [
+          "/css/reference/ref-filled-green-100px-square.xht",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ],
+      "replaced-alignment-with-aspect-ratio-007.html": [
+       "0c841c7654c237dd5cb17f5790cd738a79caef24",
+       [
+        null,
+        [
+         [
+          "/css/reference/ref-filled-green-100px-square.xht",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ],
+      "replaced-alignment-with-aspect-ratio-008.html": [
+       "9227332851a46f597fbe8bb9d12f5d0b45975cfa",
+       [
+        null,
+        [
+         [
+          "/css/reference/ref-filled-green-100px-square.xht",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ],
+      "replaced-alignment-with-aspect-ratio-009.html": [
+       "ff721b8f19b4c2c781fd0c3c69d3e44e541d63a8",
+       [
+        null,
+        [
+         [
+          "/css/reference/ref-filled-green-100px-square.xht",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ],
       "self-baseline": {
        "grid-self-baseline-001.html": [
         "2473bb5d4fc8adc351af441bf39cffcacc076273",
@@ -163483,6 +163665,49 @@
        ]
       }
      },
+     "interactive-elements": {
+      "the-popup-element": {
+       "popup-hidden-display.tentative.html": [
+        "c80aecf1d30a3686584bd0bea5988a0ddcc32e19",
+        [
+         null,
+         [
+          [
+           "/html/semantics/interactive-elements/the-popup-element/popup-hidden-display-ref.tentative.html",
+           "=="
+          ]
+         ],
+         {}
+        ]
+       ],
+       "popup-inside-display-none.tentative.html": [
+        "824e7005e9b4fc2037c1273b0ad526441a7acb0c",
+        [
+         null,
+         [
+          [
+           "/html/semantics/interactive-elements/the-popup-element/popup-hidden-display-ref.tentative.html",
+           "=="
+          ]
+         ],
+         {}
+        ]
+       ],
+       "popup-open-display.tentative.html": [
+        "0f6bd930212e2e9dd210c9af5814af5072706588",
+        [
+         null,
+         [
+          [
+           "/html/semantics/interactive-elements/the-popup-element/popup-open-display-ref.tentative.html",
+           "=="
+          ]
+         ],
+         {}
+        ]
+       ]
+      }
+     },
      "links": {
       "linktypes": {
        "alternate-css.html": [
@@ -176355,10 +176580,6 @@
      ]
     },
     "sandbox": {
-     "meta-element.sub-expected.txt": [
-      "329b19aeb83a744ca44e05ddf619002ffbadf33e",
-      []
-     ],
      "service-worker-sandbox.https-expected.txt": [
       "6a5fa36ef548243fa1317b0a99f9c0bb5a79ae8e",
       []
@@ -195917,6 +196138,14 @@
         "25b76c3c6f216793a36b1f29287dafd993898c67",
         []
        ],
+       "25x50-green.png": [
+        "6ab02fce0fa9b708880d4dbb2dc2b927fd30f59b",
+        []
+       ],
+       "50x50-green.png": [
+        "6c1406b7dfd7c59f413a183c8896b8c2b3395bf5",
+        []
+       ],
        "style-change.js": [
         "766d140d2b239bcc0c11e92481c5ff302d12db5c",
         []
@@ -215016,7 +215245,7 @@
           []
          ],
          "test-common.css": [
-          "dcbf4e1e0240bf622edcea2b7f68f58d89c5ff82",
+          "218dfcb77060426b0588dd5b28979a4a038befd4",
           []
          ]
         },
@@ -233237,6 +233466,16 @@
          []
         ]
        }
+      },
+      "the-popup-element": {
+       "popup-hidden-display-ref.tentative.html": [
+        "7f4ced38c511785382e7304004a6fac5fc9ff581",
+        []
+       ],
+       "popup-open-display-ref.tentative.html": [
+        "1b063444c3b9f0da6cf5ea4c0262d83d6dff0834",
+        []
+       ]
       }
      },
      "interfaces.js": [
@@ -237796,7 +238035,7 @@
     ]
    },
    "lint.ignore": [
-    "0c12d66c04586cf2a807da9b25100fd925e01fa4",
+    "e296376fb69cdef8d293d3b13fb620eb20016553",
     []
    ],
    "loading": {
@@ -244323,7 +244562,7 @@
      []
     ],
     "testharness.js": [
-     "3623aa4d57e6bdab1fcab1b235646331381df31e",
+     "adfb692ccd9bf202cc5642558c588ab168af7906",
      []
     ],
     "testharness.js.headers": [
@@ -279114,6 +279353,13 @@
        {}
       ]
      ],
+     "worker-data-set-timeout.sub.html": [
+      "ac4b608b08c4ed3867f587847cc699a91cedc5c4",
+      [
+       null,
+       {}
+      ]
+     ],
      "worker-eval-blocked.sub.html": [
       "9a264f2a240bfb89b29aeee7ec39fb1e035b0f52",
       [
@@ -286193,6 +286439,13 @@
        {}
       ]
      ],
+     "flex-minimum-height-flex-items-025.html": [
+      "2c1f6fa64c6711b5c4c6dd32d5e2055ff6e47689",
+      [
+       null,
+       {}
+      ]
+     ],
      "flex-minimum-size-001.html": [
       "c2eea80ca5b79818f6b7a9a36ab1ff64826b5218",
       [
@@ -287000,6 +287253,13 @@
        {}
       ]
      ],
+     "justify-content-006.html": [
+      "354763c90ce19d4d41a3b1b2b33db14759b2ab45",
+      [
+       null,
+       {}
+      ]
+     ],
      "justify-content_space-between-002.html": [
       "fde1a7312408d02fe3843452585de2ee660ae6f7",
       [
@@ -312217,7 +312477,7 @@
       ]
      ],
      "aria-element-reflection.tentative.html": [
-      "9073e52075a502943cf9dfb7586174e0533b63fa",
+      "fc20df6696e975d368b5e18cfad776c4284615fe",
       [
        null,
        {}
@@ -363008,7 +363268,7 @@
         ]
        ],
        "canvas-aspect-ratio.html": [
-        "f523fd7219b204f1a7ac91805c206563d354e3ba",
+        "91fdc6c86c55595c2484a88a4e026827c3608581",
         [
          null,
          {}
@@ -369810,14 +370070,14 @@
       },
       "the-popup-element": {
        "popup-element-basic.tentative.html": [
-        "41fa7d4e7f1adefc790e2e39b3b1e1a450499516",
+        "3be3fae9ac9a5bbccc6784a1efb87240574207b8",
         [
          null,
          {}
         ]
        ],
-       "popup-open.tentative.html": [
-        "d622b7d10538625d9db3858a933d7740e323986f",
+       "popup-shadow-dom.tentative.html": [
+        "f17bdabac4b2bdd1f2e615e3bf90cf6ee1018059",
         [
          null,
          {}
@@ -385800,7 +386060,7 @@
      ]
     ],
     "showPicker-errors.https.window.js": [
-     "ecc64dfe66d6dd9611b33c75cd2504d11814eaf7",
+     "2310c323d9f37d317a65e2bb6cdcc001ebfa3568",
      [
       "native-file-system/showPicker-errors.https.window.html",
       {
@@ -392887,7 +393147,7 @@
    },
    "raw-sockets": {
     "open-consume-activation.https.html": [
-     "f6dda4488bafef83fddb4af0e9958ae8a5892153",
+     "6cbf017712b197886fb2d8d620927ee03506f6d4",
      [
       null,
       {
@@ -423915,7 +424175,7 @@
      ]
     ],
     "request-video-frame-callback-webrtc.https.html": [
-     "ce5b5ad6be699a917b3dce5559cdbfa8e334127d",
+     "dcf97e4ca9105d7e29e579bdb632db554c525933",
      [
       null,
       {}
@@ -431024,7 +431284,7 @@
    },
    "webcodecs": {
     "audio-decoder.any.js": [
-     "a6367231b234a6e078bd96dc2e689ffb429593c6",
+     "17f5520d2c7bfd40d2949d96d452f3142c70f438",
      [
       "webcodecs/audio-decoder.any.html",
       {
@@ -431075,7 +431335,7 @@
      ]
     ],
     "video-decoder.any.js": [
-     "44e7375a78c013d0a9d0f60a33a04651473c48fb",
+     "48568c170fc6195d924ac2b14be85dff00d3709d",
      [
       "webcodecs/video-decoder.any.html",
       {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-tables/absolute-crash.html b/third_party/blink/web_tests/external/wpt/css/css-tables/absolute-crash.html
new file mode 100644
index 0000000..d4558256
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-tables/absolute-crash.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<link rel="help" href="https://crbug.com/958381">
+<table style="position: relative; max-height: 10px;">
+  <caption>caption</caption>
+  <td><div id=target style="position: absolute;"></div></td>
+</table>
+<script>
+  document.body.offsetTop;
+  document.getElementById('target').style.top = '10px';
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-tables/fixed-layout-2-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-tables/fixed-layout-2-expected.txt
new file mode 100644
index 0000000..e74c8c8
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-tables/fixed-layout-2-expected.txt
@@ -0,0 +1,8 @@
+This is a testharness.js-based test.
+PASS Table-layout:fixed is not applied when width is auto
+PASS Table-layout:fixed reports fixed when width is auto
+PASS Table-layout:fixed is not applied when width is max-content
+PASS Table-layout:fixed reports fixed when width is max-content
+FAIL Table-layout:fixed is applied when width is min-content assert_equals: expected 100 but got 50
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-tables/fixed-layout-2.html b/third_party/blink/web_tests/external/wpt/css/css-tables/fixed-layout-2.html
index 13a46642..dcbabb1f 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-tables/fixed-layout-2.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-tables/fixed-layout-2.html
@@ -30,8 +30,8 @@
 
     <hr/>
     <p>This should be a 100px-wide blue square:</p>
-    <p>Table-layout:fixed does not apply to width:min-content/fit-content tables</p>
-    <x-table style="table-layout: auto; width: fit-content; border-spacing: 0px">
+    <p>Table-layout:fixed does apply to width:min-content/fit-content tables</p>
+    <x-table style="table-layout: fixed; width: fit-content; border-spacing: 0px">
         <x-tr>
             <x-td style="padding: 0; background: blue; height: 50px;"><div style="width: 100px"></div></x-td>
             <x-td style="padding: 0"></x-td>
@@ -39,8 +39,8 @@
     </x-table>
     <x-table style="table-layout: fixed; width: min-content; border-spacing: 0px">
         <x-tr>
-            <x-td style="padding: 0; background: blue; height: 50px;"><div style="width: 100px"></div></x-td>
-            <x-td style="padding: 0"></x-td>
+            <x-td style="padding: 0; background: blue; height: 50px;width:100px;"><div style="width: 100px"></div></x-td>
+            <x-td style="padding: 0;height:50px"><div style="width: 100px"></div></x-td>
         </x-tr>
     </x-table>
 
@@ -75,10 +75,10 @@
             'fixed'
         ],
         [
-            "Table-layout:fixed is not applied when width is min-content",
+            "Table-layout:fixed is applied when width is min-content",
             document.querySelector("x-table:nth-of-type(3) > x-tr:first-child > x-td:first-child").offsetWidth,
             document.querySelector("x-table:nth-of-type(4) > x-tr:first-child > x-td:first-child").offsetWidth
-        ],
+        ]
     ])
 
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/resources/testharness.js b/third_party/blink/web_tests/external/wpt/resources/testharness.js
index 3623aa4..adfb692c 100644
--- a/third_party/blink/web_tests/external/wpt/resources/testharness.js
+++ b/third_party/blink/web_tests/external/wpt/resources/testharness.js
@@ -3570,9 +3570,11 @@
                 escape_html(test.message ? tests[i].message : " ") +
                 (tests[i].stack ? "<pre>" +
                  escape_html(tests[i].stack) +
-                 "</pre>": "") +
-                 "<details><summary>Asserts run</summary>" + get_asserts_output(test) + "</details>"
-                "</td></tr>";
+                 "</pre>": "");
+            if (!(test instanceof RemoteTest)) {
+                 html += "<details><summary>Asserts run</summary>" + get_asserts_output(test) + "</details>"
+            }
+            html += "</td></tr>";
         }
         html += "</tbody></table>";
         try {
diff --git a/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-oscillatornode-interface/ctor-oscillator.html b/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-oscillatornode-interface/ctor-oscillator.html
index 36bf604..bf50195 100644
--- a/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-oscillatornode-interface/ctor-oscillator.html
+++ b/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-oscillatornode-interface/ctor-oscillator.html
@@ -95,8 +95,14 @@
         };
         should(() => {
           node = new OscillatorNode(context, options);
-        }, 'new OscillatorNode(, ' + JSON.stringify(options) + ')').notThrow();
+        }, 'new OscillatorNode(c, ' + JSON.stringify(options) + ')').notThrow();
 
+        should(
+            () => {
+              node = new OscillatorNode(context, {periodicWave: null});
+            },
+            'new OscillatorNode(c, {periodicWave: null}')
+            .throw(DOMException, 'TypeError');
         task.done();
       });
 
diff --git a/third_party/blink/web_tests/external/wpt/webcodecs/utils.js b/third_party/blink/web_tests/external/wpt/webcodecs/utils.js
index 940c2c2..9b0a022 100644
--- a/third_party/blink/web_tests/external/wpt/webcodecs/utils.js
+++ b/third_party/blink/web_tests/external/wpt/webcodecs/utils.js
@@ -123,4 +123,20 @@
   encodeOrDecodeShouldThrow(codec, codecInput);
 
   return promise_rejects_dom(test, 'InvalidStateError', codec.flush(), 'flush');
-}
\ No newline at end of file
+}
+
+// Verifies a PlaneInit structure matches the actual constructed plane.
+function verifyPlane(expected, actual) {
+  assert_less_than_equal(expected.stride, actual.stride, 'plane strides');
+  assert_equals(expected.rows, actual.rows, 'plane rows');
+  assert_less_than_equal(
+      expected.stride * expected.rows, actual.length, 'plane size');
+
+  var testBuffer = new Uint8Array(actual.length);
+  actual.readInto(testBuffer);
+  for (var h = 0; h < actual.rows; ++h) {
+    assert_array_equals(
+        expected.src.slice(h * expected.stride, expected.stride),
+        testBuffer.slice(h * actual.stride, expected.stride), 'plane data');
+  }
+}
diff --git a/third_party/blink/web_tests/external/wpt/webcodecs/video-frame.any.js b/third_party/blink/web_tests/external/wpt/webcodecs/video-frame.any.js
index 14cce43..b02badc 100644
--- a/third_party/blink/web_tests/external/wpt/webcodecs/video-frame.any.js
+++ b/third_party/blink/web_tests/external/wpt/webcodecs/video-frame.any.js
@@ -97,3 +97,324 @@
     let frame = new VideoFrame(image, {timestamp: 10});
   })
 }, 'Test constructing VideoFrames from closed ImageBitmap throws.');
+
+test(t => {
+  let vfInit = {timestamp: 1234, codedWidth: 4, codedHeight: 2};
+  assert_throws_js(TypeError, () => {
+    let frame = new VideoFrame('ABCD', [], vfInit);
+  }, 'invalid pixel format');
+
+  assert_throws_dom('ConstraintError', () => {
+    let frame = new VideoFrame('ARGB', [], {timestamp: 1234});
+  }, 'missing coded size');
+
+  function constructFrame(init) {
+    let yPlaneData = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]);  // 4x2
+    let uPlaneData = new Uint8Array([1, 2]);                    // 2x1
+    let yPlane = {src: yPlaneData, stride: 4, rows: 2};
+    let uPlane = vPlane = {src: uPlaneData, stride: 2, rows: 1};
+    let frame = new VideoFrame('I420', [yPlane, uPlane, vPlane], init);
+  }
+
+  assert_throws_dom(
+      'ConstraintError', () => {constructFrame({
+                           timestamp: 1234,
+                           codedWidth: 1 << 32 - 1,
+                           codedHeight: 1 << 32 - 1
+                         })},
+      'invalid coded size');
+  assert_throws_dom(
+      'ConstraintError',
+      () => {constructFrame({timestamp: 1234, codedWidth: 4, codedHeight: 0})},
+      'invalid coded height');
+  assert_throws_dom(
+      'ConstraintError',
+      () => {constructFrame({timestamp: 1234, codedWidth: 0, codedHeight: 4})},
+      'invalid coded width');
+  assert_throws_dom(
+      'ConstraintError', () => {constructFrame({
+                           timestamp: 1234,
+                           codedWidth: 4,
+                           codedHeight: 2,
+                           cropLeft: 100,
+                           cropRight: 100
+                         })},
+      'invalid crop left/right');
+  assert_throws_dom(
+      'ConstraintError',
+      () => {constructFrame(
+          {timestamp: 1234, codedWidth: 4, codedHeight: 2, cropWidth: 0})},
+      'invalid crop width');
+  assert_throws_dom(
+      'ConstraintError',
+      () => {constructFrame(
+          {timestamp: 1234, codedWidth: 4, codedHeight: 2, cropHeight: 0})},
+      'invalid crop height');
+  assert_throws_dom(
+      'ConstraintError', () => {constructFrame({
+                           timestamp: 1234,
+                           codedWidth: 4,
+                           codedHeight: 2,
+                           cropHeight: -1,
+                           cropWidth: -100
+                         })},
+      'invalid negative crop');
+  assert_throws_dom(
+      'ConstraintError', () => {constructFrame({
+                           timestamp: 1234,
+                           codedWidth: 4,
+                           codedHeight: 2,
+                           displayWidth: 1 << 32 - 1
+                         })},
+      'invalid display width');
+  assert_throws_dom(
+      'ConstraintError', () => {constructFrame({
+                           timestamp: 1234,
+                           codedWidth: 4,
+                           codedHeight: 2,
+                           displayWidth: 1 << 32 - 1,
+                           displayHeight: 1 << 32
+                         })},
+      'invalid display height');
+}, 'Test invalid planar constructed VideoFrames');
+
+test(t => {
+  let fmt = 'I420';
+  let vfInit = {timestamp: 1234, codedWidth: 4, codedHeight: 2};
+  let yPlaneData = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]);  // 4x2
+  let uPlaneData = new Uint8Array([1, 2]);                    // 2x1
+  let yPlane = {src: yPlaneData, stride: 4, rows: 2};
+  let uPlane = vPlane = {src: uPlaneData, stride: 2, rows: 1};
+  let frame = new VideoFrame(fmt, [yPlane, uPlane, vPlane], vfInit);
+  assert_equals(frame.planes.length, 3, 'plane count');
+  assert_equals(frame.format, fmt, 'plane format');
+  verifyPlane(yPlane, frame.planes[0]);
+  verifyPlane(uPlane, frame.planes[1]);
+  verifyPlane(vPlane, frame.planes[2]);
+  frame.close();
+
+  assert_throws_dom('ConstraintError', () => {
+    let frame = new VideoFrame(fmt, [yPlane, uPlane], vfInit);
+  }, 'too few planes');
+  assert_throws_dom('ConstraintError', () => {
+    let badYPlane = {...yPlane};
+    badYPlane.stride = 1;
+    let frame = new VideoFrame(fmt, [badYPlane, uPlane, vPlane], vfInit);
+  }, 'y stride too small');
+  assert_throws_dom('ConstraintError', () => {
+    let badUPlane = {...uPlane};
+    badUPlane.stride = 1;
+    let frame = new VideoFrame(fmt, [yPlane, badUPlane, vPlane], vfInit);
+  }, 'u stride too small');
+  assert_throws_dom('ConstraintError', () => {
+    let badVPlane = {...vPlane};
+    badVPlane.stride = 1;
+    let frame = new VideoFrame(fmt, [yPlane, uPlane, badVPlane], vfInit);
+  }, 'v stride too small');
+  assert_throws_dom('ConstraintError', () => {
+    let badYPlane = {...yPlane};
+    badYPlane.rows = 1;
+    let frame = new VideoFrame(fmt, [badYPlane, uPlane, vPlane], vfInit);
+  }, 'y height too small');
+  assert_throws_dom('ConstraintError', () => {
+    let badUPlane = {...uPlane};
+    badUPlane.rows = 0;
+    let frame = new VideoFrame(fmt, [yPlane, badUPlane, vPlane], vfInit);
+  }, 'u height too small');
+  assert_throws_dom('ConstraintError', () => {
+    let badVPlane = {...vPlane};
+    badVPlane.rows = 0;
+    let frame = new VideoFrame(fmt, [yPlane, uPlane, badVPlane], vfInit);
+  }, 'v height too small');
+  assert_throws_dom('ConstraintError', () => {
+    let badYPlane = {...yPlane};
+    badYPlane.rows = 100;
+    let frame = new VideoFrame(fmt, [badYPlane, uPlane, vPlane], vfInit);
+  }, 'y height too large');
+  assert_throws_dom('ConstraintError', () => {
+    let badUPlane = {...uPlane};
+    badUPlane.rows = 100;
+    let frame = new VideoFrame(fmt, [yPlane, badUPlane, vPlane], vfInit);
+  }, 'u height too large');
+  assert_throws_dom('ConstraintError', () => {
+    let badVPlane = {...vPlane};
+    badVPlane.rows = 100;
+    let frame = new VideoFrame(fmt, [yPlane, uPlane, badVPlane], vfInit);
+  }, 'v height too large');
+  assert_throws_dom('ConstraintError', () => {
+    let badYPlane = {...yPlane};
+    badYPlane.src = yPlaneData.slice(1, 4);
+    let frame = new VideoFrame(fmt, [badYPlane, uPlane, vPlane], vfInit);
+  }, 'y plane size too small');
+  assert_throws_dom('ConstraintError', () => {
+    let badUPlane = {...uPlane};
+    badUPlane.src = uPlaneData.slice(1, 1);
+    let frame = new VideoFrame(fmt, [yPlane, badUPlane, vPlane], vfInit);
+  }, 'u plane size too small');
+  assert_throws_dom('ConstraintError', () => {
+    let badVPlane = {...vPlane};
+    badVPlane.src = uPlaneData.slice(1, 1);
+    let frame = new VideoFrame(fmt, [yPlane, uPlane, badVPlane], vfInit);
+  }, 'v plane size too small');
+}, 'Test planar constructed I420 VideoFrame');
+
+test(t => {
+  let fmt = 'I420';
+  let vfInit = {timestamp: 1234, codedWidth: 4, codedHeight: 2};
+  let yPlaneData = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]);  // 4x2
+  let uPlaneData = new Uint8Array([1, 2]);                    // 2x1
+  let yPlane = {src: yPlaneData, stride: 4, rows: 2};
+  let uPlane = vPlane = {src: uPlaneData, stride: 2, rows: 1};
+  let aPlaneData = yPlaneData.reverse();
+  let aPlane = {src: aPlaneData, stride: 4, rows: 2};
+  let frame = new VideoFrame(fmt, [yPlane, uPlane, vPlane, aPlane], vfInit);
+  assert_equals(frame.planes.length, 4, 'plane count');
+  assert_equals(frame.format, fmt, 'plane format');
+  verifyPlane(yPlane, frame.planes[0]);
+  verifyPlane(uPlane, frame.planes[1]);
+  verifyPlane(vPlane, frame.planes[2]);
+  verifyPlane(aPlane, frame.planes[3]);
+  frame.close();
+
+  // Most constraints are tested as part of I420 above.
+
+  assert_throws_dom('ConstraintError', () => {
+    let badAPlane = {...aPlane};
+    badAPlane.stride = 1;
+    let frame =
+        new VideoFrame(fmt, [yPlane, uPlane, vPlane, badAPlane], vfInit);
+  }, 'a stride too small');
+  assert_throws_dom('ConstraintError', () => {
+    let badAPlane = {...aPlane};
+    badAPlane.rows = 1;
+    let frame =
+        new VideoFrame(fmt, [yPlane, uPlane, vPlane, badAPlane], vfInit);
+  }, 'a height too small');
+  assert_throws_dom('ConstraintError', () => {
+    let badAPlane = {...aPlane};
+    badAPlane.rows = 100;
+    let frame =
+        new VideoFrame(fmt, [yPlane, uPlane, vPlane, badAPlane], vfInit);
+  }, 'a height too large');
+  assert_throws_dom('ConstraintError', () => {
+    let badAPlane = {...yPlane};
+    badAPlane.src = aPlaneData.slice(1, 4);
+    let frame =
+        new VideoFrame(fmt, [yPlane, uPlane, vPlane, badAPlane], vfInit);
+  }, 'a plane size too small');
+}, 'Test planar constructed I420+Alpha VideoFrame');
+
+test(t => {
+  let fmt = 'NV12';
+  let vfInit = {timestamp: 1234, codedWidth: 4, codedHeight: 2};
+  let yPlaneData = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]);  // 4x2
+  let yPlane = {src: yPlaneData, stride: 4, rows: 2};
+  let uvPlaneData = new Uint8Array([1, 2, 3, 4]);
+  let uvPlane = {src: uvPlaneData, stride: 4, rows: 1};
+  frame = new VideoFrame(fmt, [yPlane, uvPlane], vfInit);
+  assert_equals(frame.planes.length, 2, 'plane count');
+  assert_equals(frame.format, fmt, 'plane format');
+  verifyPlane(yPlane, frame.planes[0]);
+  verifyPlane(uvPlane, frame.planes[1]);
+  frame.close();
+
+  assert_throws_dom('ConstraintError', () => {
+    let frame = new VideoFrame(fmt, [yPlane], vfInit);
+  }, 'too few planes');
+  assert_throws_dom('ConstraintError', () => {
+    let badYPlane = {...yPlane};
+    badYPlane.stride = 1;
+    let frame = new VideoFrame(fmt, [badYPlane, uvPlane], vfInit);
+  }, 'y stride too small');
+  assert_throws_dom('ConstraintError', () => {
+    let badUVPlane = {...uvPlane};
+    badUVPlane.stride = 2;
+    let frame = new VideoFrame(fmt, [yPlane, badUVPlane], vfInit);
+  }, 'uv stride too small');
+  assert_throws_dom('ConstraintError', () => {
+    let badYPlane = {...yPlane};
+    badYPlane.rows = 1;
+    let frame = new VideoFrame(fmt, [badYPlane, uvPlane], vfInit);
+  }, 'y height too small');
+  assert_throws_dom('ConstraintError', () => {
+    let badUVPlane = {...uvPlane};
+    badUVPlane.rows = 0;
+    let frame = new VideoFrame(fmt, [yPlane, badUVPlane], vfInit);
+  }, 'uv height too small');
+  assert_throws_dom('ConstraintError', () => {
+    let badYPlane = {...yPlane};
+    badYPlane.rows = 100;
+    let frame = new VideoFrame(fmt, [badYPlane, uvPlane], vfInit);
+  }, 'y height too large');
+  assert_throws_dom('ConstraintError', () => {
+    let badUVPlane = {...uvPlane};
+    badUVPlane.rows = 100;
+    let frame = new VideoFrame(fmt, [yPlane, badUVPlane], vfInit);
+  }, 'u height too large');
+  assert_throws_dom('ConstraintError', () => {
+    let badYPlane = {...yPlane};
+    badYPlane.src = yPlaneData.slice(1, 4);
+    let frame = new VideoFrame(fmt, [badYPlane, uvPlane], vfInit);
+  }, 'y plane size too small');
+  assert_throws_dom('ConstraintError', () => {
+    let badUVPlane = {...uvPlane};
+    badUVPlane.src = uvPlaneData.slice(1, 1);
+    let frame = new VideoFrame(fmt, [yPlane, badUVPlane], vfInit);
+  }, 'u plane size too small');
+}, 'Test planar constructed NV12 VideoFrame');
+
+test(t => {
+  let vfInit = {timestamp: 1234, codedWidth: 4, codedHeight: 2};
+  let argbPlaneData =
+      new Uint8Array(new Uint32Array([1, 2, 3, 4, 5, 6, 7, 8]).buffer);
+  let argbPlane = {src: argbPlaneData, stride: 4 * 4, rows: 2};
+  frame = new VideoFrame('ABGR', [argbPlane], vfInit);
+  assert_equals(frame.planes.length, 1, 'plane count');
+  assert_equals(frame.format, 'ABGR', 'plane format');
+  verifyPlane(argbPlane, frame.planes[0]);
+  frame.close();
+
+  frame = new VideoFrame('ARGB', [argbPlane], vfInit);
+  assert_equals(frame.planes.length, 1, 'plane count');
+  assert_equals(frame.format, 'ARGB', 'plane format');
+  verifyPlane(argbPlane, frame.planes[0]);
+  frame.close();
+
+  frame = new VideoFrame('XBGR', [argbPlane], vfInit);
+  assert_equals(frame.planes.length, 1, 'plane count');
+  assert_equals(frame.format, 'XBGR', 'plane format');
+  verifyPlane(argbPlane, frame.planes[0]);
+  frame.close();
+
+  frame = new VideoFrame('XRGB', [argbPlane], vfInit);
+  assert_equals(frame.planes.length, 1, 'plane count');
+  assert_equals(frame.format, 'XRGB', 'plane format');
+  verifyPlane(argbPlane, frame.planes[0]);
+  frame.close();
+
+  ['ABGR', 'ARGB', 'XBGR', 'XRGB'].forEach(fmt => {
+    assert_throws_dom('ConstraintError', () => {
+      let frame = new VideoFrame(fmt, [], vfInit);
+    }, fmt + ': too few planes');
+    assert_throws_dom('ConstraintError', () => {
+      let badARGBPlane = {...argbPlane};
+      badARGBPlane.stride = 1;
+      let frame = new VideoFrame(fmt, [badARGBPlane], vfInit);
+    }, fmt + ': stride too small');
+    assert_throws_dom('ConstraintError', () => {
+      let badARGBPlane = {...argbPlane};
+      badARGBPlane.rows = 1;
+      let frame = new VideoFrame(fmt, [badARGBPlane], vfInit);
+    }, fmt + ': height too small');
+    assert_throws_dom('ConstraintError', () => {
+      let badARGBPlane = {...argbPlane};
+      badARGBPlane.rows = 100;
+      let frame = new VideoFrame(fmt, [badARGBPlane], vfInit);
+    }, fmt + ': height too large');
+    assert_throws_dom('ConstraintError', () => {
+      let badARGBPlane = {...argbPlane};
+      badARGBPlane.src = argbPlaneData.slice(1, 4);
+      let frame = new VideoFrame(fmt, [badARGBPlane], vfInit);
+    }, fmt + ': plane size too small');
+  });
+}, 'Test planar constructed RGB VideoFrames');
diff --git a/third_party/blink/web_tests/flag-specific/disable-layout-ng/external/wpt/css/css-tables/fixed-layout-2-expected.txt b/third_party/blink/web_tests/flag-specific/disable-layout-ng/external/wpt/css/css-tables/fixed-layout-2-expected.txt
new file mode 100644
index 0000000..24414c3
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/disable-layout-ng/external/wpt/css/css-tables/fixed-layout-2-expected.txt
@@ -0,0 +1,8 @@
+This is a testharness.js-based test.
+PASS Table-layout:fixed is not applied when width is auto
+PASS Table-layout:fixed reports fixed when width is auto
+PASS Table-layout:fixed is not applied when width is max-content
+PASS Table-layout:fixed reports fixed when width is max-content
+FAIL Table-layout:fixed is applied when width is min-content assert_equals: expected 100 but got 0
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/http/tests/eye-dropper/color-picker-show-eye-dropper.html b/third_party/blink/web_tests/http/tests/eye-dropper/color-picker-show-eye-dropper.html
new file mode 100644
index 0000000..54f9831
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/eye-dropper/color-picker-show-eye-dropper.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta name="color-scheme" content="light dark">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/forms-test-resources/picker-common.js"></script>
+</head>
+<body>
+<input type='color' id='color' value='#7EFFC9'>
+
+<p id='description' style='opacity: 0'></p>
+<div id='console' style='opacity: 0'></div>
+
+<script type="module">
+import {waitUntilEyeDropperShown} from './resources/mock-eyedropperchooser.js';
+
+promise_test(async () => {
+  await new Promise((resolve, reject) => {
+    openPicker(document.getElementById('color'), resolve, reject);
+  });
+
+  popupWindow.focus();
+  const popupDocument = popupWindow.document;
+  const eyeDropper = popupDocument.querySelector('eye-dropper');
+  const eyeDropperRect = eyeDropper.getBoundingClientRect();
+  eventSender.mouseMoveTo(eyeDropperRect.left + (eyeDropperRect.width / 2), eyeDropperRect.top + (eyeDropperRect.height / 2));
+  eventSender.mouseDown();
+  eventSender.mouseUp();
+  await waitUntilEyeDropperShown();
+}, 'eye dropper is shown when clicked in color chooser');
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/http/tests/eye-dropper/resources/mock-eyedropperchooser.js b/third_party/blink/web_tests/http/tests/eye-dropper/resources/mock-eyedropperchooser.js
new file mode 100644
index 0000000..c8d6636
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/eye-dropper/resources/mock-eyedropperchooser.js
@@ -0,0 +1,43 @@
+import {EyeDropperChooser, EyeDropperChooserReceiver} from '/gen/third_party/blink/public/mojom/choosers/color_chooser.mojom.m.js';
+
+class MockEyeDropperChooser {
+  constructor() {
+    this.receiver_ = new EyeDropperChooserReceiver(this);
+    this.interceptor_ =
+        new MojoInterfaceInterceptor(EyeDropperChooser.$interfaceName);
+    this.interceptor_.oninterfacerequest =
+        e => this.receiver_.$.bindHandle(e.handle);
+    this.interceptor_.start();
+
+    this.receiver_.onConnectionError.addListener(() => {
+      this.count_--;
+    });
+    this.count_ = 0;
+    this.shownResolvers_ = [];
+  }
+
+  choose() {
+    this.count_++;
+    if (this.count_ == 1) {
+      this.shownResolvers_.forEach(r => r());
+    }
+    return new Promise((resolve, reject) => {
+      // TODO(crbug.com/992297): handle value chosen.
+    });
+  }
+
+  async waitUntilShown() {
+    if (this.count_ > 0) {
+      return;
+    }
+    return new Promise(resolve => {
+      this.shownResolvers_.push(resolve);
+    });
+  }
+}
+
+let mockEyeDropperChooser = new MockEyeDropperChooser();
+
+export async function waitUntilEyeDropperShown() {
+  return mockEyeDropperChooser.waitUntilShown();
+}
diff --git a/third_party/blink/web_tests/virtual/eye-dropper/README.txt b/third_party/blink/web_tests/virtual/eye-dropper/README.txt
deleted file mode 100644
index 4119e71..0000000
--- a/third_party/blink/web_tests/virtual/eye-dropper/README.txt
+++ /dev/null
@@ -1 +0,0 @@
-# This suite runs tests with --enable-features=EyeDropper
diff --git a/third_party/blink/web_tests/virtual/eye-dropper/color-picker-show-eye-dropper.html b/third_party/blink/web_tests/virtual/eye-dropper/color-picker-show-eye-dropper.html
deleted file mode 100644
index cdb02901..0000000
--- a/third_party/blink/web_tests/virtual/eye-dropper/color-picker-show-eye-dropper.html
+++ /dev/null
@@ -1,34 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-<meta name="color-scheme" content="light dark">
-<script>
-testRunner.waitUntilDone();
-</script>
-<script src='../../fast/forms/resources/picker-common.js'></script>
-<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
-<script src="file:///gen/third_party/blink/public/mojom/choosers/color_chooser.mojom.js"></script>
-<script src='mock-eyedropperchooser.js'></script>
-</head>
-<body>
-<input type='color' id='color' value='#7EFFC9'>
-
-<p id='description' style='opacity: 0'></p>
-<div id='console' style='opacity: 0'></div>
-
-<script>
-openPicker(document.getElementById('color'), openPickerSuccessfulCallback, () => testRunner.notifyDone());
-
-function openPickerSuccessfulCallback() {
-  popupWindow.focus();
-  const popupDocument = popupWindow.document;
-  waitUntilEyeDropperShown(() => testRunner.notifyDone());
-  const eyeDropper = popupDocument.querySelector('eye-dropper');
-  const eyeDropperRect = eyeDropper.getBoundingClientRect();
-  eventSender.mouseMoveTo(eyeDropperRect.left + (eyeDropperRect.width / 2), eyeDropperRect.top + (eyeDropperRect.height / 2));
-  eventSender.mouseDown();
-  eventSender.mouseUp();
-}
-</script>
-</body>
-</html>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/virtual/eye-dropper/mock-eyedropperchooser.js b/third_party/blink/web_tests/virtual/eye-dropper/mock-eyedropperchooser.js
deleted file mode 100644
index 2af9fe6..0000000
--- a/third_party/blink/web_tests/virtual/eye-dropper/mock-eyedropperchooser.js
+++ /dev/null
@@ -1,44 +0,0 @@
-'use strict';
-
-class MockEyeDropperChooser {
-  constructor() {
-    this.bindingSet_ = new mojo.BindingSet(blink.mojom.EyeDropperChooser);
-    this.interceptor_ =
-        new MojoInterfaceInterceptor(blink.mojom.EyeDropperChooser.name);
-    this.interceptor_.oninterfacerequest =
-        e => this.bindingSet_.addBinding(this, e.handle);
-    this.interceptor_.start();
-
-    this.bindingSet_.setConnectionErrorHandler(() => {
-      this.count_--;
-    });
-    this.count_ = 0;
-  }
-
-  choose() {
-    this.count_++;
-    return new Promise((resolve, reject) => {
-      // TODO(crbug.com/992297): handle value chosen.
-    });
-  }
-
-  isChooserShown() {
-    return this.count_ > 0;
-  }
-}
-
-let mockEyeDropperChooser = new MockEyeDropperChooser();
-
-function waitUntilEyeDropperShown(then) {
-  if (!mockEyeDropperChooser.isChooserShown())
-    return setTimeout(() => { waitUntilEyeDropperShown(then); }, 0);
-  if (then)
-    then();
-}
-
-function waitUntilEyeDropperClosed(then) {
-  if (mockEyeDropperChooser.isChooserShown())
-    return setTimeout(() => { waitUntilEyeDropperClosed(then); }, 0);
-  if (then)
-    then();
-}
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
index 05eea9c..c64875a 100644
--- a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
+++ b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
@@ -10512,18 +10512,22 @@
     attribute @@toStringTag
     getter boundsGeometry
     method constructor
+interface XRCPUDepthInformation : XRDepthInformation
+    attribute @@toStringTag
+    getter data
+    method constructor
+    method getDepthInMeters
 interface XRDOMOverlayState
     attribute @@toStringTag
     getter type
     method constructor
 interface XRDepthInformation
     attribute @@toStringTag
-    getter data
     getter height
     getter normTextureFromNormView
+    getter rawValueToMeters
     getter width
     method constructor
-    method getDepth
 interface XRFrame
     attribute @@toStringTag
     getter detectedPlanes
@@ -10676,6 +10680,8 @@
     method constructor
 interface XRSession : EventTarget
     attribute @@toStringTag
+    getter depthDataFormat
+    getter depthUsage
     getter domOverlayState
     getter environmentBlendMode
     getter inputSources
@@ -10760,7 +10766,12 @@
     attribute @@toStringTag
     method constructor
     method getCameraImage
+    method getDepthInformation
     method getReflectionCubeMap
+interface XRWebGLDepthInformation : XRDepthInformation
+    attribute @@toStringTag
+    getter texture
+    method constructor
 interface XRWebGLLayer : XRLayer
     static method getNativeFramebufferScaleFactor
     attribute @@toStringTag
diff --git a/third_party/cldr/LICENSE b/third_party/cldr/LICENSE
new file mode 100644
index 0000000..a9f3977
--- /dev/null
+++ b/third_party/cldr/LICENSE
@@ -0,0 +1,46 @@
+UNICODE, INC. LICENSE AGREEMENT - DATA FILES AND SOFTWARE
+
+See Terms of Use for definitions of Unicode Inc.'s
+Data Files and Software.
+
+NOTICE TO USER: Carefully read the following legal agreement.
+BY DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING UNICODE INC.'S
+DATA FILES ("DATA FILES"), AND/OR SOFTWARE ("SOFTWARE"),
+YOU UNEQUIVOCALLY ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE
+TERMS AND CONDITIONS OF THIS AGREEMENT.
+IF YOU DO NOT AGREE, DO NOT DOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE
+THE DATA FILES OR SOFTWARE.
+
+COPYRIGHT AND PERMISSION NOTICE
+
+Copyright © 1991-2021 Unicode, Inc. All rights reserved.
+Distributed under the Terms of Use in https://www.unicode.org/copyright.html.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the Unicode data files and any associated documentation
+(the "Data Files") or Unicode software and any associated documentation
+(the "Software") to deal in the Data Files or Software
+without restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, and/or sell copies of
+the Data Files or Software, and to permit persons to whom the Data Files
+or Software are furnished to do so, provided that either
+(a) this copyright and permission notice appear with all copies
+of the Data Files or Software, or
+(b) this copyright and permission notice appear in associated
+Documentation.
+
+THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF
+ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS
+NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL
+DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+PERFORMANCE OF THE DATA FILES OR SOFTWARE.
+
+Except as contained in this notice, the name of a copyright holder
+shall not be used in advertising or otherwise to promote the sale,
+use or other dealings in these Data Files or Software without prior
+written authorization of the copyright holder.
diff --git a/third_party/cldr/OWNERS b/third_party/cldr/OWNERS
new file mode 100644
index 0000000..ea320a3
--- /dev/null
+++ b/third_party/cldr/OWNERS
@@ -0,0 +1,3 @@
+jopalmer@chromium.org
+keithlee@chromium.org
+file://chrome/browser/chromeos/input_method/OWNERS
diff --git a/third_party/cldr/README.chromium b/third_party/cldr/README.chromium
new file mode 100644
index 0000000..e3d445d1
--- /dev/null
+++ b/third_party/cldr/README.chromium
@@ -0,0 +1,16 @@
+Name: Unicode Common Locale Data Repository
+Short Name: cldr
+URL: http://cldr.unicode.org/index/downloads
+Version: 38.1
+License: MIT-like
+License File: LICENSE
+Security Critical: yes
+
+Description:
+The Unicode CLDR provides data files to support internationalisation.
+Currently, this is used to provide emoji keywords and names to facilitate
+search in the Chrome OS emoji picker.
+
+Local Modifications:
+Only the files relevant to English emoji keywords are currently checked out.
+See ./update.sh for details and how to update.
diff --git a/third_party/cldr/src/common/annotations/en.xml b/third_party/cldr/src/common/annotations/en.xml
new file mode 100644
index 0000000..843a3af6
--- /dev/null
+++ b/third_party/cldr/src/common/annotations/en.xml
@@ -0,0 +1,3771 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE ldml SYSTEM "../../common/dtd/ldml.dtd">
+<!-- Copyright © 1991-2020 Unicode, Inc.
+For terms of use, see http://www.unicode.org/copyright.html
+Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in the U.S. and other countries.
+CLDR data files are interpreted according to the LDML specification (http://unicode.org/reports/tr35/)
+
+the U.S. and other countries. CLDR data files are interpreted according to
+the LDML specification (http://unicode.org/reports/tr35/) Warnings: All cp
+values have U+FE0F characters removed. See /annotationsDerived/ for derived
+annotations.
+-->
+<ldml>
+	<identity>
+		<version number="$Revision$"/>
+		<language type="en"/>
+	</identity>
+	<annotations>
+		<annotation cp="{">brace | bracket | curly brace | curly bracket | gullwing | open curly bracket</annotation>
+		<annotation cp="{" type="tts">open curly bracket</annotation>
+		<annotation cp="🏻">light skin tone | skin tone | type 1–2</annotation>
+		<annotation cp="🏻" type="tts">light skin tone</annotation>
+		<annotation cp="🏼">medium-light skin tone | skin tone | type 3</annotation>
+		<annotation cp="🏼" type="tts">medium-light skin tone</annotation>
+		<annotation cp="🏽">medium skin tone | skin tone | type 4</annotation>
+		<annotation cp="🏽" type="tts">medium skin tone</annotation>
+		<annotation cp="🏾">medium-dark skin tone | skin tone | type 5</annotation>
+		<annotation cp="🏾" type="tts">medium-dark skin tone</annotation>
+		<annotation cp="🏿">dark skin tone | skin tone | type 6</annotation>
+		<annotation cp="🏿" type="tts">dark skin tone</annotation>
+		<annotation cp="‾">overline | overstrike | vinculum</annotation>
+		<annotation cp="‾" type="tts">overline</annotation>
+		<annotation cp="_">dash | line | low dash | low line | underdash | underline</annotation>
+		<annotation cp="_" type="tts">low line</annotation>
+		<annotation cp="-">dash | hyphen | hyphen-minus | minus</annotation>
+		<annotation cp="-" type="tts">hyphen-minus</annotation>
+		<annotation cp="‐">dash | hyphen</annotation>
+		<annotation cp="‐" type="tts">hyphen</annotation>
+		<annotation cp="–">dash | en</annotation>
+		<annotation cp="–" type="tts">en dash</annotation>
+		<annotation cp="—">dash | em</annotation>
+		<annotation cp="—" type="tts">em dash</annotation>
+		<annotation cp="―">bar | dash | horizontal bar | line</annotation>
+		<annotation cp="―" type="tts">horizontal bar</annotation>
+		<annotation cp="・">dots | interpunct | katakana | katakana middle dot | middot</annotation>
+		<annotation cp="・" type="tts">katakana middle dot</annotation>
+		<!-- causes tool breakage
+			 <annotation cp="">whitespace</annotation>
+			 <annotation cp="" type="tts">space</annotation>
+		-->
+		<annotation cp=",">comma</annotation>
+		<annotation cp="," type="tts">comma</annotation>
+		<annotation cp="،">arabic | comma</annotation>
+		<annotation cp="،" type="tts">arabic comma</annotation>
+		<annotation cp="、">comma | ideographic</annotation>
+		<annotation cp="、" type="tts">ideographic comma</annotation>
+		<annotation cp=";">semi-colon | semicolon</annotation>
+		<annotation cp=";" type="tts">semicolon</annotation>
+		<annotation cp="؛">arabic | arabic semicolon | semi-colon</annotation>
+		<annotation cp="؛" type="tts">arabic semicolon</annotation>
+		<annotation cp=":">colon</annotation>
+		<annotation cp=":" type="tts">colon</annotation>
+		<annotation cp="!">bang | exclamation | exclamation mark | exclamation point</annotation>
+		<annotation cp="!" type="tts">exclamation mark</annotation>
+		<annotation cp="¡">bang | exclamation | exclamation mark | exclamation point | inverted | inverted exclamation mark</annotation>
+		<annotation cp="¡" type="tts">inverted exclamation mark</annotation>
+		<annotation cp="?">question | question mark</annotation>
+		<annotation cp="?" type="tts">question mark</annotation>
+		<annotation cp="¿">inverted | inverted question mark | question | question mark</annotation>
+		<annotation cp="¿" type="tts">inverted question mark</annotation>
+		<annotation cp="؟">arabic | arabic question mark | question | question mark</annotation>
+		<annotation cp="؟" type="tts">arabic question mark</annotation>
+		<annotation cp="‽">interabang | interrobang</annotation>
+		<annotation cp="‽" type="tts">interrobang</annotation>
+		<annotation cp=".">dot | full stop | period</annotation>
+		<annotation cp="." type="tts">period</annotation>
+		<annotation cp="…">dots | ellipsis | omission</annotation>
+		<annotation cp="…" type="tts">ellipsis</annotation>
+		<annotation cp="。">full stop | ideographic | period</annotation>
+		<annotation cp="。" type="tts">ideographic period</annotation>
+		<annotation cp="·">dot | interpunct | middle | middot</annotation>
+		<annotation cp="·" type="tts">middle dot</annotation>
+		<annotation cp="'">apostrophe | quote | single quote | typewriter apostrophe</annotation>
+		<annotation cp="'" type="tts">typewriter apostrophe</annotation>
+		<annotation cp="‘">apostrophe | left apostrophe | quote | single quote | smart quote</annotation>
+		<annotation cp="‘" type="tts">left apostrophe</annotation>
+		<annotation cp="’">apostrophe | quote | right apostrophe | single quote | smart quote</annotation>
+		<annotation cp="’" type="tts">right apostrophe</annotation>
+		<annotation cp="‚">apostrophe | low quote | low right apostrophe | quote</annotation>
+		<annotation cp="‚" type="tts">low right apostrophe</annotation>
+		<annotation cp="“">double quote | left quotation mark | quotation | quote | smart quotation</annotation>
+		<annotation cp="“" type="tts">left quotation mark</annotation>
+		<annotation cp="”">double quote | quotation | quote | right quotation mark | smart quotation</annotation>
+		<annotation cp="”" type="tts">right quotation mark</annotation>
+		<annotation cp="„">double quote | low quotation | low right quotation mark | quotation | quote | smart quotation</annotation>
+		<annotation cp="„" type="tts">low right quotation mark</annotation>
+		<annotation cp="«">angle | brackets | carets | chevron | left | left guillemet | quote</annotation>
+		<annotation cp="«" type="tts">left guillemet</annotation>
+		<annotation cp="»">angle | brackets | carets | chevron | quote | right | right guillemet</annotation>
+		<annotation cp="»" type="tts">right guillemet</annotation>
+		<annotation cp=")">bracket | close parenthesis | paren | parens | parenthesis | round bracket</annotation>
+		<annotation cp=")" type="tts">close parenthesis</annotation>
+		<annotation cp="[">bracket | crotchet | open square bracket | square bracket</annotation>
+		<annotation cp="[" type="tts">open square bracket</annotation>
+		<annotation cp="]">bracket | close square bracket | crotchet | square bracket</annotation>
+		<annotation cp="]" type="tts">close square bracket</annotation>
+		<annotation cp="}">brace | bracket | close curly bracket | curly brace | curly bracket | gullwing</annotation>
+		<annotation cp="}" type="tts">close curly bracket</annotation>
+		<annotation cp="〈">angle bracket | bracket | chevron | diamond brackets | open angle bracket | pointy bracket | tuple</annotation>
+		<annotation cp="〈" type="tts">open angle bracket</annotation>
+		<annotation cp="〉">angle bracket | bracket | chevron | close angle bracket | diamond brackets | pointy bracket | tuple</annotation>
+		<annotation cp="〉" type="tts">close angle bracket</annotation>
+		<annotation cp="《">bracket | double angle bracket | open double angle bracket</annotation>
+		<annotation cp="《" type="tts">open double angle bracket</annotation>
+		<annotation cp="》">bracket | close double angle bracket | double angle bracket</annotation>
+		<annotation cp="》" type="tts">close double angle bracket</annotation>
+		<annotation cp="「">bracket | corner bracket | open corner bracket</annotation>
+		<annotation cp="「" type="tts">open corner bracket</annotation>
+		<annotation cp="」">bracket | close corner bracket | corner bracket</annotation>
+		<annotation cp="」" type="tts">close corner bracket</annotation>
+		<annotation cp="『">bracket | hollow corner bracket | open hollow corner bracket</annotation>
+		<annotation cp="『" type="tts">open hollow corner bracket</annotation>
+		<annotation cp="』">bracket | close hollow corner bracket | hollow corner bracket</annotation>
+		<annotation cp="』" type="tts">close hollow corner bracket</annotation>
+		<annotation cp="【">bracket | lens bracket | lenticular bracket | open black lens bracket</annotation>
+		<annotation cp="【" type="tts">open black lens bracket</annotation>
+		<annotation cp="】">bracket | close black lens bracket | lens bracket | lenticular bracket</annotation>
+		<annotation cp="】" type="tts">close black lens bracket</annotation>
+		<annotation cp="〔">bracket | open tortoise shell bracket | shell bracket | tortoise shell bracket</annotation>
+		<annotation cp="〔" type="tts">open tortoise shell bracket</annotation>
+		<annotation cp="〕">bracket | close tortoise shell bracket | shell bracket | tortoise shell bracket</annotation>
+		<annotation cp="〕" type="tts">close tortoise shell bracket</annotation>
+		<annotation cp="〖">bracket | hollow lens bracket | hollow lenticular bracket | open hollow lens bracket</annotation>
+		<annotation cp="〖" type="tts">open hollow lens bracket</annotation>
+		<annotation cp="〗">bracket | close hollow lens bracket | hollow lens bracket | hollow lenticular bracket</annotation>
+		<annotation cp="〗" type="tts">close hollow lens bracket</annotation>
+		<annotation cp="§">paragraph | part | section | silcrow</annotation>
+		<annotation cp="§" type="tts">section</annotation>
+		<annotation cp="¶">alinea | paragraph | paragraph mark | paraph | pilcrow</annotation>
+		<annotation cp="¶" type="tts">paragraph mark</annotation>
+		<annotation cp="@">ampersat | arobase | arroba | at mark | at sign | at-sign | commercial at | strudel</annotation>
+		<annotation cp="@" type="tts">at-sign</annotation>
+		<annotation cp="*">asterisk | star | wildcard</annotation>
+		<annotation cp="*" type="tts">asterisk</annotation>
+		<annotation cp="/">oblique | slash | solidus | stroke | virgule | whack</annotation>
+		<annotation cp="/" type="tts">slash</annotation>
+		<annotation cp="\">backslash | reverse solidus | whack</annotation>
+		<annotation cp="\" type="tts">backslash</annotation>
+		<annotation cp="&amp;">ampersand | and | et</annotation>
+		<annotation cp="&amp;" type="tts">ampersand</annotation>
+		<annotation cp="#">hash | hash sign | hashtag | lb | number | pound</annotation>
+		<annotation cp="#" type="tts">hash sign</annotation>
+		<annotation cp="%">per cent | per-cent | percent</annotation>
+		<annotation cp="%" type="tts">percent</annotation>
+		<annotation cp="‰">per mil | per mille | permil | permille</annotation>
+		<annotation cp="‰" type="tts">per mille</annotation>
+		<annotation cp="†">dagger | dagger sign | obelisk | obelus</annotation>
+		<annotation cp="†" type="tts">dagger sign</annotation>
+		<annotation cp="‡">dagger | double | double dagger sign | obelisk | obelus</annotation>
+		<annotation cp="‡" type="tts">double dagger sign</annotation>
+		<annotation cp="•">bullet | dot</annotation>
+		<annotation cp="•" type="tts">bullet</annotation>
+		<annotation cp="‧">dash | hyphen | hyphenation point | point</annotation>
+		<annotation cp="‧" type="tts">hyphenation point</annotation>
+		<annotation cp="′">prime</annotation>
+		<annotation cp="′" type="tts">prime</annotation>
+		<annotation cp="″">double prime | prime</annotation>
+		<annotation cp="″" type="tts">double prime</annotation>
+		<annotation cp="‴">prime | triple prime</annotation>
+		<annotation cp="‴" type="tts">triple prime</annotation>
+		<annotation cp="‸">caret</annotation>
+		<annotation cp="‸" type="tts">caret</annotation>
+		<annotation cp="※">reference mark</annotation>
+		<annotation cp="※" type="tts">reference mark</annotation>
+		<annotation cp="⁂">asterism | dinkus | stars</annotation>
+		<annotation cp="⁂" type="tts">asterism</annotation>
+		<annotation cp="`">accent | grave | tone</annotation>
+		<annotation cp="`" type="tts">grave accent</annotation>
+		<annotation cp="´">accent | acute | tone</annotation>
+		<annotation cp="´" type="tts">acute accent</annotation>
+		<annotation cp="^">accent | caret | chevron | circumflex | control | hat | pointer | power | wedge | xor sign</annotation>
+		<annotation cp="^" type="tts">circumflex accent</annotation>
+		<annotation cp="¨">diaeresis | tréma | umlaut</annotation>
+		<annotation cp="¨" type="tts">diaeresis</annotation>
+		<annotation cp="°">degree | hour | proof</annotation>
+		<annotation cp="°" type="tts">degree</annotation>
+		<annotation cp="℗">copyright | recording | sound</annotation>
+		<annotation cp="℗" type="tts">sound recording copyright</annotation>
+		<annotation cp="←">arrow | left | left-pointing arrow</annotation>
+		<annotation cp="←" type="tts">left-pointing arrow</annotation>
+		<annotation cp="↚">leftwards arrow stroke</annotation>
+		<annotation cp="↚" type="tts">leftwards arrow stroke</annotation>
+		<annotation cp="→">arrow | right | right-pointing arrow</annotation>
+		<annotation cp="→" type="tts">right-pointing arrow</annotation>
+		<annotation cp="↛">rightwards arrow stroke</annotation>
+		<annotation cp="↛" type="tts">rightwards arrow stroke</annotation>
+		<annotation cp="↑">arrow | up | up-pointing arrow</annotation>
+		<annotation cp="↑" type="tts">up-pointing arrow</annotation>
+		<annotation cp="↓">arrow | down | down-pointing arrow</annotation>
+		<annotation cp="↓" type="tts">down-pointing arrow</annotation>
+		<annotation cp="↜">leftwards wave arrow</annotation>
+		<annotation cp="↜" type="tts">leftwards wave arrow</annotation>
+		<annotation cp="↝">rightwards wave arrow</annotation>
+		<annotation cp="↝" type="tts">rightwards wave arrow</annotation>
+		<annotation cp="↞">leftwards two headed arrow</annotation>
+		<annotation cp="↞" type="tts">leftwards two headed arrow</annotation>
+		<annotation cp="↟">upwards two headed arrow</annotation>
+		<annotation cp="↟" type="tts">upwards two headed arrow</annotation>
+		<annotation cp="↠">rightwards two headed arrow</annotation>
+		<annotation cp="↠" type="tts">rightwards two headed arrow</annotation>
+		<annotation cp="↡">downwards two headed arrow</annotation>
+		<annotation cp="↡" type="tts">downwards two headed arrow</annotation>
+		<annotation cp="↢">leftwards arrow tail</annotation>
+		<annotation cp="↢" type="tts">leftwards arrow tail</annotation>
+		<annotation cp="↣">rightwards arrow tail</annotation>
+		<annotation cp="↣" type="tts">rightwards arrow tail</annotation>
+		<annotation cp="↤">leftwards arrow from bar</annotation>
+		<annotation cp="↤" type="tts">leftwards arrow from bar</annotation>
+		<annotation cp="↥">upwards arrow from bar</annotation>
+		<annotation cp="↥" type="tts">upwards arrow from bar</annotation>
+		<annotation cp="↦">rightwards arrow from bar</annotation>
+		<annotation cp="↦" type="tts">rightwards arrow from bar</annotation>
+		<annotation cp="↧">downwards arrow from bar</annotation>
+		<annotation cp="↧" type="tts">downwards arrow from bar</annotation>
+		<annotation cp="↨">up down arrow with base</annotation>
+		<annotation cp="↨" type="tts">up down arrow with base</annotation>
+		<annotation cp="↫">leftwards arrow loop</annotation>
+		<annotation cp="↫" type="tts">leftwards arrow loop</annotation>
+		<annotation cp="↬">rightwards arrow loop</annotation>
+		<annotation cp="↬" type="tts">rightwards arrow loop</annotation>
+		<annotation cp="↭">left right wave arrow</annotation>
+		<annotation cp="↭" type="tts">left right wave arrow</annotation>
+		<annotation cp="↯">downwards zigzag arrow | zigzag</annotation>
+		<annotation cp="↯" type="tts">downwards zigzag arrow</annotation>
+		<annotation cp="↰">upwards arrow tip leftwards</annotation>
+		<annotation cp="↰" type="tts">upwards arrow tip leftwards</annotation>
+		<annotation cp="↱">upwards arrow tip rightwards</annotation>
+		<annotation cp="↱" type="tts">upwards arrow tip rightwards</annotation>
+		<annotation cp="↲">downwards arrow tip leftwards</annotation>
+		<annotation cp="↲" type="tts">downwards arrow tip leftwards</annotation>
+		<annotation cp="↳">downwards arrow tip rightwards</annotation>
+		<annotation cp="↳" type="tts">downwards arrow tip rightwards</annotation>
+		<annotation cp="↴">rightwards arrow corner downwards</annotation>
+		<annotation cp="↴" type="tts">rightwards arrow corner downwards</annotation>
+		<annotation cp="↵">downwards arrow corner leftwards</annotation>
+		<annotation cp="↵" type="tts">downwards arrow corner leftwards</annotation>
+		<annotation cp="↶">anticlockwise top semicircle arrow</annotation>
+		<annotation cp="↶" type="tts">anticlockwise top semicircle arrow</annotation>
+		<annotation cp="↷">clockwise top semicircle arrow</annotation>
+		<annotation cp="↷" type="tts">clockwise top semicircle arrow</annotation>
+		<annotation cp="↸">north west arrow long bar</annotation>
+		<annotation cp="↸" type="tts">north west arrow long bar</annotation>
+		<annotation cp="↹">leftwards arrow bar over rightwards arrow bar</annotation>
+		<annotation cp="↹" type="tts">leftwards arrow bar over rightwards arrow bar</annotation>
+		<annotation cp="↺">anticlockwise open circle arrow</annotation>
+		<annotation cp="↺" type="tts">anticlockwise open circle arrow</annotation>
+		<annotation cp="↻">clockwise open circle arrow</annotation>
+		<annotation cp="↻" type="tts">clockwise open circle arrow</annotation>
+		<annotation cp="↼">barb | leftwards harpoon barb upwards</annotation>
+		<annotation cp="↼" type="tts">leftwards harpoon barb upwards</annotation>
+		<annotation cp="↽">leftwards harpoon barb downwards</annotation>
+		<annotation cp="↽" type="tts">leftwards harpoon barb downwards</annotation>
+		<annotation cp="↾">barb | upwards harpoon barb rightwards</annotation>
+		<annotation cp="↾" type="tts">upwards harpoon barb rightwards</annotation>
+		<annotation cp="↿">barb | upwards harpoon barb leftwards</annotation>
+		<annotation cp="↿" type="tts">upwards harpoon barb leftwards</annotation>
+		<annotation cp="⇀">barb | rightwards harpoon barb upwards</annotation>
+		<annotation cp="⇀" type="tts">rightwards harpoon barb upwards</annotation>
+		<annotation cp="⇁">barb | rightwards harpoon barb downwards</annotation>
+		<annotation cp="⇁" type="tts">rightwards harpoon barb downwards</annotation>
+		<annotation cp="⇂">barb | downwards harpoon barb rightwards</annotation>
+		<annotation cp="⇂" type="tts">downwards harpoon barb rightwards</annotation>
+		<annotation cp="⇃">barb | downwards harpoon barb leftwards</annotation>
+		<annotation cp="⇃" type="tts">downwards harpoon barb leftwards</annotation>
+		<annotation cp="⇄">rightwards arrow over leftwards arrow</annotation>
+		<annotation cp="⇄" type="tts">rightwards arrow over leftwards arrow</annotation>
+		<annotation cp="⇅">arrow | down | up | up-pointing and down-pointing arrows</annotation>
+		<annotation cp="⇅" type="tts">up-pointing and down-pointing arrows</annotation>
+		<annotation cp="⇆">arrow | left | left-pointing over right-pointing arrows | right</annotation>
+		<annotation cp="⇆" type="tts">left-pointing over right-pointing arrows</annotation>
+		<annotation cp="⇇">leftwards paired arrows</annotation>
+		<annotation cp="⇇" type="tts">leftwards paired arrows</annotation>
+		<annotation cp="⇈">upwards paired arrows</annotation>
+		<annotation cp="⇈" type="tts">upwards paired arrows</annotation>
+		<annotation cp="⇉">rightwards paired arrows</annotation>
+		<annotation cp="⇉" type="tts">rightwards paired arrows</annotation>
+		<annotation cp="⇊">downwards paired arrows</annotation>
+		<annotation cp="⇊" type="tts">downwards paired arrows</annotation>
+		<annotation cp="⇋">leftwards harpoon over rightwards harpoon</annotation>
+		<annotation cp="⇋" type="tts">leftwards harpoon over rightwards harpoon</annotation>
+		<annotation cp="⇌">rightwards harpoon over leftwards harpoon</annotation>
+		<annotation cp="⇌" type="tts">rightwards harpoon over leftwards harpoon</annotation>
+		<annotation cp="⇐">leftwards double arrow</annotation>
+		<annotation cp="⇐" type="tts">leftwards double arrow</annotation>
+		<annotation cp="⇍">leftwards double arrow stroke</annotation>
+		<annotation cp="⇍" type="tts">leftwards double arrow stroke</annotation>
+		<annotation cp="⇑">upwards double arrow</annotation>
+		<annotation cp="⇑" type="tts">upwards double arrow</annotation>
+		<annotation cp="⇒">rightwards double arrow</annotation>
+		<annotation cp="⇒" type="tts">rightwards double arrow</annotation>
+		<annotation cp="⇏">rightwards double arrow stroke</annotation>
+		<annotation cp="⇏" type="tts">rightwards double arrow stroke</annotation>
+		<annotation cp="⇓">downwards double arrow</annotation>
+		<annotation cp="⇓" type="tts">downwards double arrow</annotation>
+		<annotation cp="⇔">left right double arrow</annotation>
+		<annotation cp="⇔" type="tts">left right double arrow</annotation>
+		<annotation cp="⇎">left right double arrow stroke</annotation>
+		<annotation cp="⇎" type="tts">left right double arrow stroke</annotation>
+		<annotation cp="⇖">north west double arrow</annotation>
+		<annotation cp="⇖" type="tts">north west double arrow</annotation>
+		<annotation cp="⇗">north east double arrow</annotation>
+		<annotation cp="⇗" type="tts">north east double arrow</annotation>
+		<annotation cp="⇘">south east double arrow</annotation>
+		<annotation cp="⇘" type="tts">south east double arrow</annotation>
+		<annotation cp="⇙">south west double arrow</annotation>
+		<annotation cp="⇙" type="tts">south west double arrow</annotation>
+		<annotation cp="⇚">leftwards triple arrow</annotation>
+		<annotation cp="⇚" type="tts">leftwards triple arrow</annotation>
+		<annotation cp="⇛">rightwards triple arrow</annotation>
+		<annotation cp="⇛" type="tts">rightwards triple arrow</annotation>
+		<annotation cp="⇜">leftwards squiggle arrow</annotation>
+		<annotation cp="⇜" type="tts">leftwards squiggle arrow</annotation>
+		<annotation cp="⇝">rightwards squiggle arrow</annotation>
+		<annotation cp="⇝" type="tts">rightwards squiggle arrow</annotation>
+		<annotation cp="⇞">upwards arrow double stroke</annotation>
+		<annotation cp="⇞" type="tts">upwards arrow double stroke</annotation>
+		<annotation cp="⇟">downwards arrow double stroke</annotation>
+		<annotation cp="⇟" type="tts">downwards arrow double stroke</annotation>
+		<annotation cp="⇠">leftwards dashed arrow</annotation>
+		<annotation cp="⇠" type="tts">leftwards dashed arrow</annotation>
+		<annotation cp="⇡">upwards dashed arrow</annotation>
+		<annotation cp="⇡" type="tts">upwards dashed arrow</annotation>
+		<annotation cp="⇢">rightwards dashed arrow</annotation>
+		<annotation cp="⇢" type="tts">rightwards dashed arrow</annotation>
+		<annotation cp="⇣">downwards dashed arrow</annotation>
+		<annotation cp="⇣" type="tts">downwards dashed arrow</annotation>
+		<annotation cp="⇤">leftwards arrow bar</annotation>
+		<annotation cp="⇤" type="tts">leftwards arrow bar</annotation>
+		<annotation cp="⇥">rightwards arrow bar</annotation>
+		<annotation cp="⇥" type="tts">rightwards arrow bar</annotation>
+		<annotation cp="⇦">leftwards hollow arrow</annotation>
+		<annotation cp="⇦" type="tts">leftwards hollow arrow</annotation>
+		<annotation cp="⇧">upwards hollow arrow</annotation>
+		<annotation cp="⇧" type="tts">upwards hollow arrow</annotation>
+		<annotation cp="⇨">rightwards hollow arrow</annotation>
+		<annotation cp="⇨" type="tts">rightwards hollow arrow</annotation>
+		<annotation cp="⇩">downwards hollow arrow</annotation>
+		<annotation cp="⇩" type="tts">downwards hollow arrow</annotation>
+		<annotation cp="⇪">upwards hollow arrow from bar</annotation>
+		<annotation cp="⇪" type="tts">upwards hollow arrow from bar</annotation>
+		<annotation cp="⇵">downwards arrow leftwards upwards arrow</annotation>
+		<annotation cp="⇵" type="tts">downwards arrow leftwards upwards arrow</annotation>
+		<annotation cp="∀">all | any | for all | given | universal</annotation>
+		<annotation cp="∀" type="tts">for all</annotation>
+		<annotation cp="∂">differential | partial differential</annotation>
+		<annotation cp="∂" type="tts">partial differential</annotation>
+		<annotation cp="∃">there exists</annotation>
+		<annotation cp="∃" type="tts">there exists</annotation>
+		<annotation cp="∅">empty set | mathematics | set operator</annotation>
+		<annotation cp="∅" type="tts">empty set</annotation>
+		<annotation cp="∆">increment | triangle</annotation>
+		<annotation cp="∆" type="tts">increment</annotation>
+		<annotation cp="∇">nabla | triangle</annotation>
+		<annotation cp="∇" type="tts">nabla</annotation>
+		<annotation cp="∈">contains | element | element of | membership | set</annotation>
+		<annotation cp="∈" type="tts">element of</annotation>
+		<annotation cp="∉">element | not an element</annotation>
+		<annotation cp="∉" type="tts">not an element</annotation>
+		<annotation cp="∋">contains as member | element</annotation>
+		<annotation cp="∋" type="tts">contains as member</annotation>
+		<annotation cp="∎">end proof | halmos | q.e.d. | qed | tombstone</annotation>
+		<annotation cp="∎" type="tts">end proof</annotation>
+		<annotation cp="∏">logic | n-ary product | product</annotation>
+		<annotation cp="∏" type="tts">n-ary product</annotation>
+		<annotation cp="∑">mathematics | n-ary summation | summation</annotation>
+		<annotation cp="∑" type="tts">n-ary summation</annotation>
+		<annotation cp="+">add | plus | plus sign</annotation>
+		<annotation cp="+" type="tts">plus sign</annotation>
+		<annotation cp="±">plus-minus</annotation>
+		<annotation cp="±" type="tts">plus-minus</annotation>
+		<annotation cp="÷">divide | division | division sign | obelus</annotation>
+		<annotation cp="÷" type="tts">division sign</annotation>
+		<annotation cp="×">multiplication | multiplication sign | multiply | times</annotation>
+		<annotation cp="×" type="tts">multiplication sign</annotation>
+		<annotation cp="&lt;">less than | less-than | open tag | tag</annotation>
+		<annotation cp="&lt;" type="tts">less-than</annotation>
+		<annotation cp="≮">inequality | mathematics | not less-than</annotation>
+		<annotation cp="≮" type="tts">not less-than</annotation>
+		<annotation cp="=">equal | equals</annotation>
+		<annotation cp="=" type="tts">equal</annotation>
+		<annotation cp="≠">inequality | inequation | not equal</annotation>
+		<annotation cp="≠" type="tts">not equal</annotation>
+		<annotation cp="&gt;">close tag | greater than | greater-than | tag</annotation>
+		<annotation cp="&gt;" type="tts">greater-than</annotation>
+		<annotation cp="≯">inequality | mathematics | not greater-than</annotation>
+		<annotation cp="≯" type="tts">not greater-than</annotation>
+		<annotation cp="¬">negation | not</annotation>
+		<annotation cp="¬" type="tts">negation</annotation>
+		<annotation cp="|">bar | line | pike | pipe | sheffer stroke | stick | stroke | vbar | vertical bar | vertical line</annotation>
+		<annotation cp="|" type="tts">vertical line</annotation>
+		<annotation cp="~">tilde</annotation>
+		<annotation cp="~" type="tts">tilde</annotation>
+		<annotation cp="−">minus | minus sign | subtract</annotation>
+		<annotation cp="−" type="tts">minus sign</annotation>
+		<annotation cp="⁻">minus | superscript</annotation>
+		<annotation cp="⁻" type="tts">superscript minus</annotation>
+		<annotation cp="∓">minus-or-plus | plus-minus</annotation>
+		<annotation cp="∓" type="tts">minus-or-plus</annotation>
+		<annotation cp="∕">division slash | slash | stroke | virgule</annotation>
+		<annotation cp="∕" type="tts">division slash</annotation>
+		<annotation cp="⁄">fraction slash | stroke | virgule</annotation>
+		<annotation cp="⁄" type="tts">fraction slash</annotation>
+		<annotation cp="∗">asterisk operator | star</annotation>
+		<annotation cp="∗" type="tts">asterisk operator</annotation>
+		<annotation cp="∘">composition | operator | ring operator</annotation>
+		<annotation cp="∘" type="tts">ring operator</annotation>
+		<annotation cp="∙">bullet operator | operator</annotation>
+		<annotation cp="∙" type="tts">bullet operator</annotation>
+		<annotation cp="√">radical | radix | root | square | surd</annotation>
+		<annotation cp="√" type="tts">square root</annotation>
+		<annotation cp="∝">proportional | proportionality</annotation>
+		<annotation cp="∝" type="tts">proportional</annotation>
+		<annotation cp="∞">infinity | infinity sign</annotation>
+		<annotation cp="∞" type="tts">infinity sign</annotation>
+		<annotation cp="∟">mathematics | right angle</annotation>
+		<annotation cp="∟" type="tts">right angle</annotation>
+		<annotation cp="∠">acute | angle</annotation>
+		<annotation cp="∠" type="tts">angle</annotation>
+		<annotation cp="∣">divides | divisor</annotation>
+		<annotation cp="∣" type="tts">divides</annotation>
+		<annotation cp="∥">parallel</annotation>
+		<annotation cp="∥" type="tts">parallel</annotation>
+		<annotation cp="∧">ac | atque | logical and | wedge</annotation>
+		<annotation cp="∧" type="tts">logical and</annotation>
+		<annotation cp="∩">intersection | set</annotation>
+		<annotation cp="∩" type="tts">intersection</annotation>
+		<annotation cp="∪">collection | set | union</annotation>
+		<annotation cp="∪" type="tts">union</annotation>
+		<annotation cp="∫">calculus | integral</annotation>
+		<annotation cp="∫" type="tts">integral</annotation>
+		<annotation cp="∬">calculus | double integral</annotation>
+		<annotation cp="∬" type="tts">double integral</annotation>
+		<annotation cp="∮">contour integral</annotation>
+		<annotation cp="∮" type="tts">contour integral</annotation>
+		<annotation cp="∴">logic | therefore</annotation>
+		<annotation cp="∴" type="tts">therefore</annotation>
+		<annotation cp="∵">because | logic</annotation>
+		<annotation cp="∵" type="tts">because</annotation>
+		<annotation cp="∶">ratio</annotation>
+		<annotation cp="∶" type="tts">ratio</annotation>
+		<annotation cp="∷">proportion | proportionality</annotation>
+		<annotation cp="∷" type="tts">proportion</annotation>
+		<annotation cp="∼">operator | tilde operator</annotation>
+		<annotation cp="∼" type="tts">tilde operator</annotation>
+		<annotation cp="∽">reversed tilde | tilde</annotation>
+		<annotation cp="∽" type="tts">reversed tilde</annotation>
+		<annotation cp="∾">inverted lazy s</annotation>
+		<annotation cp="∾" type="tts">inverted lazy s</annotation>
+		<annotation cp="≃">asymptote | asymptotically equal | mathematics</annotation>
+		<annotation cp="≃" type="tts">asymptotically equal</annotation>
+		<annotation cp="≅">approximately equal | congruence | equality | isomorphism | mathematics</annotation>
+		<annotation cp="≅" type="tts">approximately equal</annotation>
+		<annotation cp="≈">almost equal | approximate | approximation</annotation>
+		<annotation cp="≈" type="tts">almost equal</annotation>
+		<annotation cp="≌">all equal | equality | mathematics</annotation>
+		<annotation cp="≌" type="tts">all equal</annotation>
+		<annotation cp="≒">approximately equal the image</annotation>
+		<annotation cp="≒" type="tts">approximately equal the image</annotation>
+		<annotation cp="≖">equality | mathematics | ring in equal</annotation>
+		<annotation cp="≖" type="tts">ring in equal</annotation>
+		<annotation cp="≡">exact | identical | identical to | triple</annotation>
+		<annotation cp="≡" type="tts">identical to</annotation>
+		<annotation cp="≣">equality | mathematics | strictly equivalent</annotation>
+		<annotation cp="≣" type="tts">strictly equivalent</annotation>
+		<annotation cp="≤">equal | equals | inequality | less-than or equal | less-then</annotation>
+		<annotation cp="≤" type="tts">less-than or equal</annotation>
+		<annotation cp="≥">equal | equals | greater-than | greater-than or equal | inequality</annotation>
+		<annotation cp="≥" type="tts">greater-than or equal</annotation>
+		<annotation cp="≦">inequality | less-than over equal | mathematics</annotation>
+		<annotation cp="≦" type="tts">less-than over equal</annotation>
+		<annotation cp="≧">greater-than over equal | inequality | mathematics</annotation>
+		<annotation cp="≧" type="tts">greater-than over equal</annotation>
+		<annotation cp="≪">inequality | mathematics | much less-than</annotation>
+		<annotation cp="≪" type="tts">much less-than</annotation>
+		<annotation cp="≫">inequality | mathematics | much greater-than</annotation>
+		<annotation cp="≫" type="tts">much greater-than</annotation>
+		<annotation cp="≬">between</annotation>
+		<annotation cp="≬" type="tts">between</annotation>
+		<annotation cp="≳">greater-than equivalent | inequality | mathematics</annotation>
+		<annotation cp="≳" type="tts">greater-than equivalent</annotation>
+		<annotation cp="≺">mathematics | precedes | set operator</annotation>
+		<annotation cp="≺" type="tts">precedes</annotation>
+		<annotation cp="≻">mathematics | set operator | succeeds</annotation>
+		<annotation cp="≻" type="tts">succeeds</annotation>
+		<annotation cp="⊁">does not succeed | mathematics | set operator</annotation>
+		<annotation cp="⊁" type="tts">does not succeed</annotation>
+		<annotation cp="⊂">set | subset | subset of</annotation>
+		<annotation cp="⊂" type="tts">subset of</annotation>
+		<annotation cp="⊃">mathematics | set operator | superset</annotation>
+		<annotation cp="⊃" type="tts">superset</annotation>
+		<annotation cp="⊆">subset equal</annotation>
+		<annotation cp="⊆" type="tts">subset equal</annotation>
+		<annotation cp="⊇">equality | mathematics | superset equal</annotation>
+		<annotation cp="⊇" type="tts">superset equal</annotation>
+		<annotation cp="⊕">circled plus | plus</annotation>
+		<annotation cp="⊕" type="tts">circled plus</annotation>
+		<annotation cp="⊖">circled minus | erosion | symmetric difference</annotation>
+		<annotation cp="⊖" type="tts">circled minus</annotation>
+		<annotation cp="⊗">circled times | product | tensor</annotation>
+		<annotation cp="⊗" type="tts">circled times</annotation>
+		<annotation cp="⊘">circled division slash | division-like | mathematics</annotation>
+		<annotation cp="⊘" type="tts">circled division slash</annotation>
+		<annotation cp="⊙">circled dot operator | operator | XNOR</annotation>
+		<annotation cp="⊙" type="tts">circled dot operator</annotation>
+		<annotation cp="⊚">circled ring operator</annotation>
+		<annotation cp="⊚" type="tts">circled ring operator</annotation>
+		<annotation cp="⊛">asterisk | circled asterisk operator | operator</annotation>
+		<annotation cp="⊛" type="tts">circled asterisk operator</annotation>
+		<annotation cp="⊞">addition-like | mathematics | squared plus</annotation>
+		<annotation cp="⊞" type="tts">squared plus</annotation>
+		<annotation cp="⊟">mathematics | squared minus | subtraction-like</annotation>
+		<annotation cp="⊟" type="tts">squared minus</annotation>
+		<annotation cp="⊥">eet | falsum | tack | up tack</annotation>
+		<annotation cp="⊥" type="tts">up tack</annotation>
+		<annotation cp="⊮">does not force</annotation>
+		<annotation cp="⊮" type="tts">does not force</annotation>
+		<annotation cp="⊰">mathematics | precedes under relation | set operator</annotation>
+		<annotation cp="⊰" type="tts">precedes under relation</annotation>
+		<annotation cp="⊱">mathematics | set operator | succeeds under relation</annotation>
+		<annotation cp="⊱" type="tts">succeeds under relation</annotation>
+		<annotation cp="⋭">does not contain as normal subgroup equal | group theory | mathematics</annotation>
+		<annotation cp="⋭" type="tts">does not contain as normal subgroup equal</annotation>
+		<annotation cp="⊶">original</annotation>
+		<annotation cp="⊶" type="tts">original</annotation>
+		<annotation cp="⊹">hermitian conjugate matrix | mathematics | self-adjoint matrix | square matrix</annotation>
+		<annotation cp="⊹" type="tts">hermitian conjugate matrix</annotation>
+		<annotation cp="⊿">mathematics | right triangle | right-angled triangle</annotation>
+		<annotation cp="⊿" type="tts">right triangle</annotation>
+		<annotation cp="⋁">disjunction | logic | n-ary logical or</annotation>
+		<annotation cp="⋁" type="tts">n-ary logical or</annotation>
+		<annotation cp="⋂">intersection | mathematics | n-ary intersection | set operator</annotation>
+		<annotation cp="⋂" type="tts">n-ary intersection</annotation>
+		<annotation cp="⋃">mathematics | n-ary union | set operator | union</annotation>
+		<annotation cp="⋃" type="tts">n-ary union</annotation>
+		<annotation cp="⋅">dot operator | operator</annotation>
+		<annotation cp="⋅" type="tts">dot operator</annotation>
+		<annotation cp="⋆">operator | star operator</annotation>
+		<annotation cp="⋆" type="tts">star operator</annotation>
+		<annotation cp="⋈">binary operator | bowtie | natural join</annotation>
+		<annotation cp="⋈" type="tts">natural join</annotation>
+		<annotation cp="⋒">double intersection | intersection | mathematics | set operator</annotation>
+		<annotation cp="⋒" type="tts">double intersection</annotation>
+		<annotation cp="⋘">inequality | mathematics | very much less-than</annotation>
+		<annotation cp="⋘" type="tts">very much less-than</annotation>
+		<annotation cp="⋙">inequality | mathematics | very much greater-than</annotation>
+		<annotation cp="⋙" type="tts">very much greater-than</annotation>
+		<annotation cp="⋮">ellipsis | mathematics | vertical ellipsis</annotation>
+		<annotation cp="⋮" type="tts">vertical ellipsis</annotation>
+		<annotation cp="⋯">ellipsis | midline horizontal ellipsis</annotation>
+		<annotation cp="⋯" type="tts">midline horizontal ellipsis</annotation>
+		<annotation cp="⋰">ellipsis | mathematics | up right diagonal ellipsis</annotation>
+		<annotation cp="⋰" type="tts">up right diagonal ellipsis</annotation>
+		<annotation cp="⋱">down right diagonal ellipsis | ellipsis | mathematics</annotation>
+		<annotation cp="⋱" type="tts">down right diagonal ellipsis</annotation>
+		<annotation cp="■">filled square</annotation>
+		<annotation cp="■" type="tts">filled square</annotation>
+		<annotation cp="□">hollow square</annotation>
+		<annotation cp="□" type="tts">hollow square</annotation>
+		<annotation cp="▢">hollow square with rounded corners</annotation>
+		<annotation cp="▢" type="tts">hollow square with rounded corners</annotation>
+		<annotation cp="▣">hollow square containing filled square</annotation>
+		<annotation cp="▣" type="tts">hollow square containing filled square</annotation>
+		<annotation cp="▤">square with horizontal fill</annotation>
+		<annotation cp="▤" type="tts">square with horizontal fill</annotation>
+		<annotation cp="▥">square with vertical fill</annotation>
+		<annotation cp="▥" type="tts">square with vertical fill</annotation>
+		<annotation cp="▦">square orthogonal crosshatch fill</annotation>
+		<annotation cp="▦" type="tts">square orthogonal crosshatch fill</annotation>
+		<annotation cp="▧">square upper left lower right fill</annotation>
+		<annotation cp="▧" type="tts">square upper left lower right fill</annotation>
+		<annotation cp="▨">square upper right lower left fill</annotation>
+		<annotation cp="▨" type="tts">square upper right lower left fill</annotation>
+		<annotation cp="▩">square diagonal crosshatch fill</annotation>
+		<annotation cp="▩" type="tts">square diagonal crosshatch fill</annotation>
+		<annotation cp="▬">filled rectangle</annotation>
+		<annotation cp="▬" type="tts">filled rectangle</annotation>
+		<annotation cp="▭">hollow rectangle</annotation>
+		<annotation cp="▭" type="tts">hollow rectangle</annotation>
+		<annotation cp="▮">filled vertical rectangle</annotation>
+		<annotation cp="▮" type="tts">filled vertical rectangle</annotation>
+		<annotation cp="▰">filled parallelogram</annotation>
+		<annotation cp="▰" type="tts">filled parallelogram</annotation>
+		<annotation cp="▲">arrow | filled | filled up-pointing triangle | triangle | up</annotation>
+		<annotation cp="▲" type="tts">filled up-pointing triangle</annotation>
+		<annotation cp="△">hollow up-pointing triangle</annotation>
+		<annotation cp="△" type="tts">hollow up-pointing triangle</annotation>
+		<annotation cp="▴">filled up-pointing small triangle</annotation>
+		<annotation cp="▴" type="tts">filled up-pointing small triangle</annotation>
+		<annotation cp="▵">hollow up-pointing small triangle</annotation>
+		<annotation cp="▵" type="tts">hollow up-pointing small triangle</annotation>
+		<annotation cp="▷">hollow right-pointing triangle</annotation>
+		<annotation cp="▷" type="tts">hollow right-pointing triangle</annotation>
+		<annotation cp="▸">filled right-pointing small triangle</annotation>
+		<annotation cp="▸" type="tts">filled right-pointing small triangle</annotation>
+		<annotation cp="▹">hollow right-pointing small triangle</annotation>
+		<annotation cp="▹" type="tts">hollow right-pointing small triangle</annotation>
+		<annotation cp="►">filled right-pointing pointer</annotation>
+		<annotation cp="►" type="tts">filled right-pointing pointer</annotation>
+		<annotation cp="▻">hollow right-pointing pointer</annotation>
+		<annotation cp="▻" type="tts">hollow right-pointing pointer</annotation>
+		<annotation cp="▼">arrow | down | filled | filled down-pointing triangle | triangle</annotation>
+		<annotation cp="▼" type="tts">filled down-pointing triangle</annotation>
+		<annotation cp="▽">hollow down-pointing triangle</annotation>
+		<annotation cp="▽" type="tts">hollow down-pointing triangle</annotation>
+		<annotation cp="▾">filled down-pointing small triangle</annotation>
+		<annotation cp="▾" type="tts">filled down-pointing small triangle</annotation>
+		<annotation cp="▿">hollow down-pointing small triangle</annotation>
+		<annotation cp="▿" type="tts">hollow down-pointing small triangle</annotation>
+		<annotation cp="◁">hollow left-pointing triangle</annotation>
+		<annotation cp="◁" type="tts">hollow left-pointing triangle</annotation>
+		<annotation cp="◂">filled left-pointing small triangle</annotation>
+		<annotation cp="◂" type="tts">filled left-pointing small triangle</annotation>
+		<annotation cp="◃">hollow left-pointing small triangle</annotation>
+		<annotation cp="◃" type="tts">hollow left-pointing small triangle</annotation>
+		<annotation cp="◄">filled left-pointing pointer</annotation>
+		<annotation cp="◄" type="tts">filled left-pointing pointer</annotation>
+		<annotation cp="◅">hollow left-pointing pointer</annotation>
+		<annotation cp="◅" type="tts">hollow left-pointing pointer</annotation>
+		<annotation cp="◆">filled diamond</annotation>
+		<annotation cp="◆" type="tts">filled diamond</annotation>
+		<annotation cp="◇">hollow diamond</annotation>
+		<annotation cp="◇" type="tts">hollow diamond</annotation>
+		<annotation cp="◈">hollow diamond containing filled diamond</annotation>
+		<annotation cp="◈" type="tts">hollow diamond containing filled diamond</annotation>
+		<annotation cp="◉">circled dot | concentric cicles filled | fisheye | hollow circle containing filled circle | ward</annotation>
+		<annotation cp="◉" type="tts">hollow circle containing filled circle</annotation>
+		<annotation cp="◊">diamond | lozenge | rhombus</annotation>
+		<annotation cp="◊" type="tts">lozenge</annotation>
+		<annotation cp="○">circle | hollow circle | ring</annotation>
+		<annotation cp="○" type="tts">hollow circle</annotation>
+		<annotation cp="◌">dotted circle</annotation>
+		<annotation cp="◌" type="tts">dotted circle</annotation>
+		<annotation cp="◍">circle with vertical fill</annotation>
+		<annotation cp="◍" type="tts">circle with vertical fill</annotation>
+		<annotation cp="◎">concentric circles | double circle | target</annotation>
+		<annotation cp="◎" type="tts">concentric circles</annotation>
+		<annotation cp="●">circle | filled circle</annotation>
+		<annotation cp="●" type="tts">filled circle</annotation>
+		<annotation cp="◐">circle left half filled</annotation>
+		<annotation cp="◐" type="tts">circle left half filled</annotation>
+		<annotation cp="◑">circle right half filled</annotation>
+		<annotation cp="◑" type="tts">circle right half filled</annotation>
+		<annotation cp="◒">circle lower half filled</annotation>
+		<annotation cp="◒" type="tts">circle lower half filled</annotation>
+		<annotation cp="◓">circle upper half filled</annotation>
+		<annotation cp="◓" type="tts">circle upper half filled</annotation>
+		<annotation cp="◔">circle upper right quadrant filled</annotation>
+		<annotation cp="◔" type="tts">circle upper right quadrant filled</annotation>
+		<annotation cp="◕">circle all but upper left quadrant filled</annotation>
+		<annotation cp="◕" type="tts">circle all but upper left quadrant filled</annotation>
+		<annotation cp="◖">left half filled circle</annotation>
+		<annotation cp="◖" type="tts">left half filled circle</annotation>
+		<annotation cp="◗">right half filled circle</annotation>
+		<annotation cp="◗" type="tts">right half filled circle</annotation>
+		<annotation cp="◘">inverse bullet</annotation>
+		<annotation cp="◘" type="tts">inverse bullet</annotation>
+		<annotation cp="◙">filled square containing hollow circle | inverse hollow circle</annotation>
+		<annotation cp="◙" type="tts">filled square containing hollow circle</annotation>
+		<annotation cp="◜">upper left quadrant circular arc</annotation>
+		<annotation cp="◜" type="tts">upper left quadrant circular arc</annotation>
+		<annotation cp="◝">upper right quadrant circular arc</annotation>
+		<annotation cp="◝" type="tts">upper right quadrant circular arc</annotation>
+		<annotation cp="◞">lower right quadrant circular arc</annotation>
+		<annotation cp="◞" type="tts">lower right quadrant circular arc</annotation>
+		<annotation cp="◟">lower left quadrant circular arc</annotation>
+		<annotation cp="◟" type="tts">lower left quadrant circular arc</annotation>
+		<annotation cp="◠">upper half circle</annotation>
+		<annotation cp="◠" type="tts">upper half circle</annotation>
+		<annotation cp="◡">lower half circle</annotation>
+		<annotation cp="◡" type="tts">lower half circle</annotation>
+		<annotation cp="◢">filled lower right triangle</annotation>
+		<annotation cp="◢" type="tts">filled lower right triangle</annotation>
+		<annotation cp="◣">filled lower left triangle</annotation>
+		<annotation cp="◣" type="tts">filled lower left triangle</annotation>
+		<annotation cp="◤">filled upper left triangle</annotation>
+		<annotation cp="◤" type="tts">filled upper left triangle</annotation>
+		<annotation cp="◥">filled upper right triangle</annotation>
+		<annotation cp="◥" type="tts">filled upper right triangle</annotation>
+		<annotation cp="◦">hollow bullet</annotation>
+		<annotation cp="◦" type="tts">hollow bullet</annotation>
+		<annotation cp="◯">circle | large hollow circle | ring</annotation>
+		<annotation cp="◯" type="tts">large hollow circle</annotation>
+		<annotation cp="◳">hollow square upper right quadrant</annotation>
+		<annotation cp="◳" type="tts">hollow square upper right quadrant</annotation>
+		<annotation cp="◷">hollow circle with upper right quadrant</annotation>
+		<annotation cp="◷" type="tts">hollow circle with upper right quadrant</annotation>
+		<annotation cp="◿">lower right triangle</annotation>
+		<annotation cp="◿" type="tts">lower right triangle</annotation>
+		<annotation cp="♪">eighth | music | note</annotation>
+		<annotation cp="♪" type="tts">eighth note</annotation>
+		<annotation cp="⨧">plus subscript two</annotation>
+		<annotation cp="⨧" type="tts">plus subscript two</annotation>
+		<annotation cp="⨯">vector cross product</annotation>
+		<annotation cp="⨯" type="tts">vector cross product</annotation>
+		<annotation cp="⨼">interior product</annotation>
+		<annotation cp="⨼" type="tts">interior product</annotation>
+		<annotation cp="⩣">logical or double underbar</annotation>
+		<annotation cp="⩣" type="tts">logical or double underbar</annotation>
+		<annotation cp="⩽">less-than slanted equal</annotation>
+		<annotation cp="⩽" type="tts">less-than slanted equal</annotation>
+		<annotation cp="⪍">less-than above similar equal</annotation>
+		<annotation cp="⪍" type="tts">less-than above similar equal</annotation>
+		<annotation cp="⪚">double-line equal greater-than | inequality | mathematics</annotation>
+		<annotation cp="⪚" type="tts">double-line equal greater-than</annotation>
+		<annotation cp="⪺">succeeds above not almost equal</annotation>
+		<annotation cp="⪺" type="tts">succeeds above not almost equal</annotation>
+		<annotation cp="♭">bemolle | flat | music | note</annotation>
+		<annotation cp="♭" type="tts">flat</annotation>
+		<annotation cp="♯">dièse | diesis | music | note | sharp</annotation>
+		<annotation cp="♯" type="tts">sharp</annotation>
+		<annotation cp="😀">face | grin | grinning face</annotation>
+		<annotation cp="😀" type="tts">grinning face</annotation>
+		<annotation cp="😃">face | grinning face with big eyes | mouth | open | smile</annotation>
+		<annotation cp="😃" type="tts">grinning face with big eyes</annotation>
+		<annotation cp="😄">eye | face | grinning face with smiling eyes | mouth | open | smile</annotation>
+		<annotation cp="😄" type="tts">grinning face with smiling eyes</annotation>
+		<annotation cp="😁">beaming face with smiling eyes | eye | face | grin | smile</annotation>
+		<annotation cp="😁" type="tts">beaming face with smiling eyes</annotation>
+		<annotation cp="😆">face | grinning squinting face | laugh | mouth | satisfied | smile</annotation>
+		<annotation cp="😆" type="tts">grinning squinting face</annotation>
+		<annotation cp="😅">cold | face | grinning face with sweat | open | smile | sweat</annotation>
+		<annotation cp="😅" type="tts">grinning face with sweat</annotation>
+		<annotation cp="🤣">face | floor | laugh | rofl | rolling | rolling on the floor laughing | rotfl</annotation>
+		<annotation cp="🤣" type="tts">rolling on the floor laughing</annotation>
+		<annotation cp="😂">face | face with tears of joy | joy | laugh | tear</annotation>
+		<annotation cp="😂" type="tts">face with tears of joy</annotation>
+		<annotation cp="🙂">face | slightly smiling face | smile</annotation>
+		<annotation cp="🙂" type="tts">slightly smiling face</annotation>
+		<annotation cp="🙃">face | upside-down</annotation>
+		<annotation cp="🙃" type="tts">upside-down face</annotation>
+		<annotation cp="😉">face | wink | winking face</annotation>
+		<annotation cp="😉" type="tts">winking face</annotation>
+		<annotation cp="😊">blush | eye | face | smile | smiling face with smiling eyes</annotation>
+		<annotation cp="😊" type="tts">smiling face with smiling eyes</annotation>
+		<annotation cp="😇">angel | face | fantasy | halo | innocent | smiling face with halo</annotation>
+		<annotation cp="😇" type="tts">smiling face with halo</annotation>
+		<annotation cp="🥰">adore | crush | hearts | in love | smiling face with hearts</annotation>
+		<annotation cp="🥰" type="tts">smiling face with hearts</annotation>
+		<annotation cp="😍">eye | face | love | smile | smiling face with heart-eyes</annotation>
+		<annotation cp="😍" type="tts">smiling face with heart-eyes</annotation>
+		<annotation cp="🤩">eyes | face | grinning | star | star-struck</annotation>
+		<annotation cp="🤩" type="tts">star-struck</annotation>
+		<annotation cp="😘">face | face blowing a kiss | kiss</annotation>
+		<annotation cp="😘" type="tts">face blowing a kiss</annotation>
+		<annotation cp="😗">face | kiss | kissing face</annotation>
+		<annotation cp="😗" type="tts">kissing face</annotation>
+		<annotation cp="☺">face | outlined | relaxed | smile | smiling face</annotation>
+		<annotation cp="☺" type="tts">smiling face</annotation>
+		<annotation cp="😚">closed | eye | face | kiss | kissing face with closed eyes</annotation>
+		<annotation cp="😚" type="tts">kissing face with closed eyes</annotation>
+		<annotation cp="😙">eye | face | kiss | kissing face with smiling eyes | smile</annotation>
+		<annotation cp="😙" type="tts">kissing face with smiling eyes</annotation>
+		<annotation cp="🥲">grateful | proud | relieved | smiling | smiling face with tear | tear | touched</annotation>
+		<annotation cp="🥲" type="tts">smiling face with tear</annotation>
+		<annotation cp="😋">delicious | face | face savoring food | savouring | smile | yum</annotation>
+		<annotation cp="😋" type="tts">face savoring food</annotation>
+		<annotation cp="😛">face | face with tongue | tongue</annotation>
+		<annotation cp="😛" type="tts">face with tongue</annotation>
+		<annotation cp="😜">eye | face | joke | tongue | wink | winking face with tongue</annotation>
+		<annotation cp="😜" type="tts">winking face with tongue</annotation>
+		<annotation cp="🤪">eye | goofy | large | small | zany face</annotation>
+		<annotation cp="🤪" type="tts">zany face</annotation>
+		<annotation cp="😝">eye | face | horrible | squinting face with tongue | taste | tongue</annotation>
+		<annotation cp="😝" type="tts">squinting face with tongue</annotation>
+		<annotation cp="🤑">face | money | money-mouth face | mouth</annotation>
+		<annotation cp="🤑" type="tts">money-mouth face</annotation>
+		<annotation cp="🤗">face | hug | hugging</annotation>
+		<annotation cp="🤗" type="tts">hugging face</annotation>
+		<annotation cp="🤭">face with hand over mouth | whoops</annotation>
+		<annotation cp="🤭" type="tts">face with hand over mouth</annotation>
+		<annotation cp="🤫">quiet | shush | shushing face</annotation>
+		<annotation cp="🤫" type="tts">shushing face</annotation>
+		<annotation cp="🤔">face | thinking</annotation>
+		<annotation cp="🤔" type="tts">thinking face</annotation>
+		<annotation cp="🤐">face | mouth | zipper | zipper-mouth face</annotation>
+		<annotation cp="🤐" type="tts">zipper-mouth face</annotation>
+		<annotation cp="🤨">distrust | face with raised eyebrow | skeptic</annotation>
+		<annotation cp="🤨" type="tts">face with raised eyebrow</annotation>
+		<annotation cp="😐">deadpan | face | meh | neutral</annotation>
+		<annotation cp="😐" type="tts">neutral face</annotation>
+		<annotation cp="😑">expressionless | face | inexpressive | meh | unexpressive</annotation>
+		<annotation cp="😑" type="tts">expressionless face</annotation>
+		<annotation cp="😶">face | face without mouth | mouth | quiet | silent</annotation>
+		<annotation cp="😶" type="tts">face without mouth</annotation>
+		<annotation cp="😶‍🌫">absentminded | face in clouds | face in the fog | head in clouds</annotation> <!-- 1F636 200D 1F32B FE0F: face in clouds -->
+		<annotation cp="😶‍🌫" type="tts">face in clouds</annotation>
+		<annotation cp="😏">face | smirk | smirking face</annotation>
+		<annotation cp="😏" type="tts">smirking face</annotation>
+		<annotation cp="😒">face | unamused | unhappy</annotation>
+		<annotation cp="😒" type="tts">unamused face</annotation>
+		<annotation cp="🙄">eyeroll | eyes | face | face with rolling eyes | rolling</annotation>
+		<annotation cp="🙄" type="tts">face with rolling eyes</annotation>
+		<annotation cp="😬">face | grimace | grimacing face</annotation>
+		<annotation cp="😬" type="tts">grimacing face</annotation>
+		<annotation cp="😮‍💨">exhale | face exhaling | gasp | groan | relief | whisper | whistle</annotation> <!-- 1F62E 200D 1F4A8 -->
+		<annotation cp="😮‍💨" type="tts">face exhaling</annotation>
+		<annotation cp="🤥">face | lie | lying face | pinocchio</annotation>
+		<annotation cp="🤥" type="tts">lying face</annotation>
+		<annotation cp="😌">face | relieved</annotation>
+		<annotation cp="😌" type="tts">relieved face</annotation>
+		<annotation cp="😔">dejected | face | pensive</annotation>
+		<annotation cp="😔" type="tts">pensive face</annotation>
+		<annotation cp="😪">face | sleep | sleepy face</annotation>
+		<annotation cp="😪" type="tts">sleepy face</annotation>
+		<annotation cp="🤤">drooling | face</annotation>
+		<annotation cp="🤤" type="tts">drooling face</annotation>
+		<annotation cp="😴">face | sleep | sleeping face | zzz</annotation>
+		<annotation cp="😴" type="tts">sleeping face</annotation>
+		<annotation cp="😷">cold | doctor | face | face with medical mask | mask | sick</annotation>
+		<annotation cp="😷" type="tts">face with medical mask</annotation>
+		<annotation cp="🤒">face | face with thermometer | ill | sick | thermometer</annotation>
+		<annotation cp="🤒" type="tts">face with thermometer</annotation>
+		<annotation cp="🤕">bandage | face | face with head-bandage | hurt | injury</annotation>
+		<annotation cp="🤕" type="tts">face with head-bandage</annotation>
+		<annotation cp="🤢">face | nauseated | vomit</annotation>
+		<annotation cp="🤢" type="tts">nauseated face</annotation>
+		<annotation cp="🤮">face vomiting | sick | vomit | puke</annotation>
+		<annotation cp="🤮" type="tts">face vomiting</annotation>
+		<annotation cp="🤧">face | gesundheit | sneeze | sneezing face</annotation>
+		<annotation cp="🤧" type="tts">sneezing face</annotation>
+		<annotation cp="🥵">feverish | heat stroke | hot | hot face | red-faced | sweating</annotation>
+		<annotation cp="🥵" type="tts">hot face</annotation>
+		<annotation cp="🥶">blue-faced | cold | cold face | freezing | frostbite | icicles</annotation>
+		<annotation cp="🥶" type="tts">cold face</annotation>
+		<annotation cp="🥴">dizzy | intoxicated | tipsy | uneven eyes | wavy mouth | woozy face</annotation>
+		<annotation cp="🥴" type="tts">woozy face</annotation>
+		<annotation cp="😵">dead | face | knocked out | knocked-out face</annotation>
+		<annotation cp="😵" type="tts">knocked-out face</annotation>
+		<annotation cp="😵‍💫">dizzy | face with spiral eyes | hypnotized | spiral | trouble | whoa</annotation> <!-- 1F635 200D 1F4AB -->
+		<annotation cp="😵‍💫" type="tts">face with spiral eyes</annotation>
+		<annotation cp="🤯">exploding head | mind blown | shocked</annotation>
+		<annotation cp="🤯" type="tts">exploding head</annotation>
+		<annotation cp="🤠">cowboy | cowgirl | face | hat</annotation>
+		<annotation cp="🤠" type="tts">cowboy hat face</annotation>
+		<annotation cp="🥳">celebration | hat | horn | party | partying face</annotation>
+		<annotation cp="🥳" type="tts">partying face</annotation>
+		<annotation cp="🥸">disguise | disguised face | face | glasses | incognito | nose</annotation>
+		<annotation cp="🥸" type="tts">disguised face</annotation>
+		<annotation cp="😎">bright | cool | face | smiling face with sunglasses | sun | sunglasses</annotation>
+		<annotation cp="😎" type="tts">smiling face with sunglasses</annotation>
+		<annotation cp="🤓">face | geek | nerd</annotation>
+		<annotation cp="🤓" type="tts">nerd face</annotation>
+		<annotation cp="🧐">face with monocle | stuffy</annotation>
+		<annotation cp="🧐" type="tts">face with monocle</annotation>
+		<annotation cp="😕">confused | face | meh</annotation>
+		<annotation cp="😕" type="tts">confused face</annotation>
+		<annotation cp="😟">face | worried</annotation>
+		<annotation cp="😟" type="tts">worried face</annotation>
+		<annotation cp="🙁">face | frown | slightly frowning face</annotation>
+		<annotation cp="🙁" type="tts">slightly frowning face</annotation>
+		<annotation cp="☹">face | frown | frowning face</annotation>
+		<annotation cp="☹" type="tts">frowning face</annotation>
+		<annotation cp="😮">face | face with open mouth | mouth | open | sympathy</annotation>
+		<annotation cp="😮" type="tts">face with open mouth</annotation>
+		<annotation cp="😯">face | hushed | stunned | surprised</annotation>
+		<annotation cp="😯" type="tts">hushed face</annotation>
+		<annotation cp="😲">astonished | face | shocked | totally</annotation>
+		<annotation cp="😲" type="tts">astonished face</annotation>
+		<annotation cp="😳">dazed | face | flushed</annotation>
+		<annotation cp="😳" type="tts">flushed face</annotation>
+		<annotation cp="🥺">begging | mercy | pleading face | puppy eyes</annotation>
+		<annotation cp="🥺" type="tts">pleading face</annotation>
+		<annotation cp="😦">face | frown | frowning face with open mouth | mouth | open</annotation>
+		<annotation cp="😦" type="tts">frowning face with open mouth</annotation>
+		<annotation cp="😧">anguished | face</annotation>
+		<annotation cp="😧" type="tts">anguished face</annotation>
+		<annotation cp="😨">face | fear | fearful | scared</annotation>
+		<annotation cp="😨" type="tts">fearful face</annotation>
+		<annotation cp="😰">anxious face with sweat | blue | cold | face | rushed | sweat</annotation>
+		<annotation cp="😰" type="tts">anxious face with sweat</annotation>
+		<annotation cp="😥">disappointed | face | relieved | sad but relieved face | whew</annotation>
+		<annotation cp="😥" type="tts">sad but relieved face</annotation>
+		<annotation cp="😢">cry | crying face | face | sad | tear</annotation>
+		<annotation cp="😢" type="tts">crying face</annotation>
+		<annotation cp="😭">cry | face | loudly crying face | sad | sob | tear</annotation>
+		<annotation cp="😭" type="tts">loudly crying face</annotation>
+		<annotation cp="😱">face | face screaming in fear | fear | munch | scared | scream</annotation>
+		<annotation cp="😱" type="tts">face screaming in fear</annotation>
+		<annotation cp="😖">confounded | face</annotation>
+		<annotation cp="😖" type="tts">confounded face</annotation>
+		<annotation cp="😣">face | persevere | persevering face</annotation>
+		<annotation cp="😣" type="tts">persevering face</annotation>
+		<annotation cp="😞">disappointed | face</annotation>
+		<annotation cp="😞" type="tts">disappointed face</annotation>
+		<annotation cp="😓">cold | downcast face with sweat | face | sweat</annotation>
+		<annotation cp="😓" type="tts">downcast face with sweat</annotation>
+		<annotation cp="😩">face | tired | weary</annotation>
+		<annotation cp="😩" type="tts">weary face</annotation>
+		<annotation cp="😫">face | tired</annotation>
+		<annotation cp="😫" type="tts">tired face</annotation>
+		<annotation cp="🥱">bored | tired | yawn | yawning face</annotation>
+		<annotation cp="🥱" type="tts">yawning face</annotation>
+		<annotation cp="😤">face | face with steam from nose | triumph | won</annotation>
+		<annotation cp="😤" type="tts">face with steam from nose</annotation>
+		<annotation cp="😡">angry | face | mad | pouting | rage | red</annotation>
+		<annotation cp="😡" type="tts">pouting face</annotation>
+		<annotation cp="😠">angry | face | mad | anger</annotation>
+		<annotation cp="😠" type="tts">angry face</annotation>
+		<annotation cp="🤬">face with symbols on mouth | swearing</annotation>
+		<annotation cp="🤬" type="tts">face with symbols on mouth</annotation>
+		<annotation cp="😈">face | fairy tale | fantasy | horns | smile | smiling face with horns</annotation>
+		<annotation cp="😈" type="tts">smiling face with horns</annotation>
+		<annotation cp="👿">angry face with horns | demon | devil | face | fantasy | imp</annotation>
+		<annotation cp="👿" type="tts">angry face with horns</annotation>
+		<annotation cp="💀">death | face | fairy tale | monster | skull</annotation>
+		<annotation cp="💀" type="tts">skull</annotation>
+		<annotation cp="☠">crossbones | death | face | monster | skull | skull and crossbones</annotation>
+		<annotation cp="☠" type="tts">skull and crossbones</annotation>
+		<annotation cp="💩">dung | face | monster | pile of poo | poo | poop</annotation>
+		<annotation cp="💩" type="tts">pile of poo</annotation>
+		<annotation cp="🤡">clown | face</annotation>
+		<annotation cp="🤡" type="tts">clown face</annotation>
+		<annotation cp="👹">creature | face | fairy tale | fantasy | monster | ogre</annotation>
+		<annotation cp="👹" type="tts">ogre</annotation>
+		<annotation cp="👺">creature | face | fairy tale | fantasy | goblin | monster</annotation>
+		<annotation cp="👺" type="tts">goblin</annotation>
+		<annotation cp="👻">creature | face | fairy tale | fantasy | ghost | monster</annotation>
+		<annotation cp="👻" type="tts">ghost</annotation>
+		<annotation cp="👽">alien | creature | extraterrestrial | face | fantasy | ufo</annotation>
+		<annotation cp="👽" type="tts">alien</annotation>
+		<annotation cp="👾">alien | creature | extraterrestrial | face | monster | ufo</annotation>
+		<annotation cp="👾" type="tts">alien monster</annotation>
+		<annotation cp="🤖">face | monster | robot</annotation>
+		<annotation cp="🤖" type="tts">robot</annotation>
+		<annotation cp="😺">cat | face | grinning | mouth | open | smile</annotation>
+		<annotation cp="😺" type="tts">grinning cat</annotation>
+		<annotation cp="😸">cat | eye | face | grin | grinning cat with smiling eyes | smile</annotation>
+		<annotation cp="😸" type="tts">grinning cat with smiling eyes</annotation>
+		<annotation cp="😹">cat | cat with tears of joy | face | joy | tear</annotation>
+		<annotation cp="😹" type="tts">cat with tears of joy</annotation>
+		<annotation cp="😻">cat | eye | face | heart | love | smile | smiling cat with heart-eyes</annotation>
+		<annotation cp="😻" type="tts">smiling cat with heart-eyes</annotation>
+		<annotation cp="😼">cat | cat with wry smile | face | ironic | smile | wry</annotation>
+		<annotation cp="😼" type="tts">cat with wry smile</annotation>
+		<annotation cp="😽">cat | eye | face | kiss | kissing cat</annotation>
+		<annotation cp="😽" type="tts">kissing cat</annotation>
+		<annotation cp="🙀">cat | face | oh | surprised | weary</annotation>
+		<annotation cp="🙀" type="tts">weary cat</annotation>
+		<annotation cp="😿">cat | cry | crying cat | face | sad | tear</annotation>
+		<annotation cp="😿" type="tts">crying cat</annotation>
+		<annotation cp="😾">cat | face | pouting</annotation>
+		<annotation cp="😾" type="tts">pouting cat</annotation>
+		<annotation cp="🙈">evil | face | forbidden | monkey | see | see-no-evil monkey</annotation>
+		<annotation cp="🙈" type="tts">see-no-evil monkey</annotation>
+		<annotation cp="🙉">evil | face | forbidden | hear | hear-no-evil monkey | monkey</annotation>
+		<annotation cp="🙉" type="tts">hear-no-evil monkey</annotation>
+		<annotation cp="🙊">evil | face | forbidden | monkey | speak | speak-no-evil monkey</annotation>
+		<annotation cp="🙊" type="tts">speak-no-evil monkey</annotation>
+		<annotation cp="💋">kiss | kiss mark | lips</annotation>
+		<annotation cp="💋" type="tts">kiss mark</annotation>
+		<annotation cp="💌">heart | letter | love | mail</annotation>
+		<annotation cp="💌" type="tts">love letter</annotation>
+		<annotation cp="💘">arrow | cupid | heart with arrow</annotation>
+		<annotation cp="💘" type="tts">heart with arrow</annotation>
+		<annotation cp="💝">heart with ribbon | ribbon | valentine</annotation>
+		<annotation cp="💝" type="tts">heart with ribbon</annotation>
+		<annotation cp="💖">excited | sparkle | sparkling heart</annotation>
+		<annotation cp="💖" type="tts">sparkling heart</annotation>
+		<annotation cp="💗">excited | growing | growing heart | nervous | pulse</annotation>
+		<annotation cp="💗" type="tts">growing heart</annotation>
+		<annotation cp="💓">beating | beating heart | heartbeat | pulsating</annotation>
+		<annotation cp="💓" type="tts">beating heart</annotation>
+		<annotation cp="💞">revolving | revolving hearts</annotation>
+		<annotation cp="💞" type="tts">revolving hearts</annotation>
+		<annotation cp="💕">love | two hearts</annotation>
+		<annotation cp="💕" type="tts">two hearts</annotation>
+		<annotation cp="💟">heart | heart decoration</annotation>
+		<annotation cp="💟" type="tts">heart decoration</annotation>
+		<annotation cp="❣">exclamation | heart exclamation | mark | punctuation</annotation>
+		<annotation cp="❣" type="tts">heart exclamation</annotation>
+		<annotation cp="💔">break | broken | broken heart</annotation>
+		<annotation cp="💔" type="tts">broken heart</annotation>
+		<annotation cp="❤‍🔥">burn | heart | heart on fire | love | lust | sacred heart</annotation> <!-- 2764 200D 1F525 -->
+		<annotation cp="❤‍🔥" type="tts">heart on fire</annotation>
+		<annotation cp="❤‍🩹">healthier | improving | mending | mending heart | recovering | recuperating | well</annotation> <!-- 2764 200D 1FA79 -->
+		<annotation cp="❤‍🩹" type="tts">mending heart</annotation>
+		<annotation cp="❤">heart | red heart</annotation>
+		<annotation cp="❤" type="tts">red heart</annotation>
+		<annotation cp="🧡">orange | orange heart</annotation>
+		<annotation cp="🧡" type="tts">orange heart</annotation>
+		<annotation cp="💛">yellow | yellow heart</annotation>
+		<annotation cp="💛" type="tts">yellow heart</annotation>
+		<annotation cp="💚">green | green heart</annotation>
+		<annotation cp="💚" type="tts">green heart</annotation>
+		<annotation cp="💙">blue | blue heart</annotation>
+		<annotation cp="💙" type="tts">blue heart</annotation>
+		<annotation cp="💜">purple | purple heart</annotation>
+		<annotation cp="💜" type="tts">purple heart</annotation>
+		<annotation cp="🤎">brown | heart</annotation>
+		<annotation cp="🤎" type="tts">brown heart</annotation>
+		<annotation cp="🖤">black | black heart | evil | wicked</annotation>
+		<annotation cp="🖤" type="tts">black heart</annotation>
+		<annotation cp="🤍">heart | white</annotation>
+		<annotation cp="🤍" type="tts">white heart</annotation>
+		<annotation cp="💯">100 | full | hundred | hundred points | score</annotation>
+		<annotation cp="💯" type="tts">hundred points</annotation>
+		<annotation cp="💢">anger symbol | angry | comic | mad</annotation>
+		<annotation cp="💢" type="tts">anger symbol</annotation>
+		<annotation cp="💥">boom | collision | comic</annotation>
+		<annotation cp="💥" type="tts">collision</annotation>
+		<annotation cp="💫">comic | dizzy | star</annotation>
+		<annotation cp="💫" type="tts">dizzy</annotation>
+		<annotation cp="💦">comic | splashing | sweat | sweat droplets</annotation>
+		<annotation cp="💦" type="tts">sweat droplets</annotation>
+		<annotation cp="💨">comic | dash | dashing away | running</annotation>
+		<annotation cp="💨" type="tts">dashing away</annotation>
+		<annotation cp="🕳">hole</annotation>
+		<annotation cp="🕳" type="tts">hole</annotation>
+		<annotation cp="💣">bomb | comic</annotation>
+		<annotation cp="💣" type="tts">bomb</annotation>
+		<annotation cp="💬">balloon | bubble | comic | dialog | speech</annotation>
+		<annotation cp="💬" type="tts">speech balloon</annotation>
+		<annotation cp="👁‍🗨">eye | eye in speech bubble | speech bubble | witness</annotation>
+		<annotation cp="👁‍🗨" type="tts">eye in speech bubble</annotation>
+		<annotation cp="🗨">dialog | left speech bubble | speech</annotation>
+		<annotation cp="🗨" type="tts">left speech bubble</annotation>
+		<annotation cp="🗯">angry | balloon | bubble | mad | right anger bubble</annotation>
+		<annotation cp="🗯" type="tts">right anger bubble</annotation>
+		<annotation cp="💭">balloon | bubble | comic | thought</annotation>
+		<annotation cp="💭" type="tts">thought balloon</annotation>
+		<annotation cp="💤">comic | sleep | zzz</annotation>
+		<annotation cp="💤" type="tts">zzz</annotation>
+		<annotation cp="👋">hand | wave | waving</annotation>
+		<annotation cp="👋" type="tts">waving hand</annotation>
+		<annotation cp="🤚">backhand | raised | raised back of hand</annotation>
+		<annotation cp="🤚" type="tts">raised back of hand</annotation>
+		<annotation cp="🖐">finger | hand | hand with fingers splayed | splayed</annotation>
+		<annotation cp="🖐" type="tts">hand with fingers splayed</annotation>
+		<annotation cp="✋">hand | high 5 | high five | raised hand</annotation>
+		<annotation cp="✋" type="tts">raised hand</annotation>
+		<annotation cp="🖖">finger | hand | spock | vulcan | vulcan salute</annotation>
+		<annotation cp="🖖" type="tts">vulcan salute</annotation>
+		<annotation cp="👌">hand | OK</annotation>
+		<annotation cp="👌" type="tts">OK hand</annotation>
+		<annotation cp="🤌">fingers | hand gesture | interrogation | pinched | sarcastic</annotation>
+		<annotation cp="🤌" type="tts">pinched fingers</annotation>
+		<annotation cp="🤏">pinching hand | small amount</annotation>
+		<annotation cp="🤏" type="tts">pinching hand</annotation>
+		<annotation cp="✌">hand | v | victory</annotation>
+		<annotation cp="✌" type="tts">victory hand</annotation>
+		<annotation cp="🤞">cross | crossed fingers | finger | hand | luck</annotation>
+		<annotation cp="🤞" type="tts">crossed fingers</annotation>
+		<annotation cp="🤟">hand | ILY | love-you gesture</annotation>
+		<annotation cp="🤟" type="tts">love-you gesture</annotation>
+		<annotation cp="🤘">finger | hand | horns | rock-on | sign of the horns</annotation>
+		<annotation cp="🤘" type="tts">sign of the horns</annotation>
+		<annotation cp="🤙">call | call me hand | hand</annotation>
+		<annotation cp="🤙" type="tts">call me hand</annotation>
+		<annotation cp="👈">backhand | backhand index pointing left | finger | hand | index | point</annotation>
+		<annotation cp="👈" type="tts">backhand index pointing left</annotation>
+		<annotation cp="👉">backhand | backhand index pointing right | finger | hand | index | point</annotation>
+		<annotation cp="👉" type="tts">backhand index pointing right</annotation>
+		<annotation cp="👆">backhand | backhand index pointing up | finger | hand | point | up</annotation>
+		<annotation cp="👆" type="tts">backhand index pointing up</annotation>
+		<annotation cp="🖕">finger | hand | middle finger</annotation>
+		<annotation cp="🖕" type="tts">middle finger</annotation>
+		<annotation cp="👇">backhand | backhand index pointing down | down | finger | hand | point</annotation>
+		<annotation cp="👇" type="tts">backhand index pointing down</annotation>
+		<annotation cp="☝">finger | hand | index | index pointing up | point | up</annotation>
+		<annotation cp="☝" type="tts">index pointing up</annotation>
+		<annotation cp="👍">+1 | hand | thumb | thumbs up | up</annotation>
+		<annotation cp="👍" type="tts">thumbs up</annotation>
+		<annotation cp="👎">-1 | down | hand | thumb | thumbs down</annotation>
+		<annotation cp="👎" type="tts">thumbs down</annotation>
+		<annotation cp="✊">clenched | fist | hand | punch | raised fist</annotation>
+		<annotation cp="✊" type="tts">raised fist</annotation>
+		<annotation cp="👊">clenched | fist | hand | oncoming fist | punch</annotation>
+		<annotation cp="👊" type="tts">oncoming fist</annotation>
+		<annotation cp="🤛">fist | left-facing fist | leftwards</annotation>
+		<annotation cp="🤛" type="tts">left-facing fist</annotation>
+		<annotation cp="🤜">fist | right-facing fist | rightwards</annotation>
+		<annotation cp="🤜" type="tts">right-facing fist</annotation>
+		<annotation cp="👏">clap | clapping hands | hand</annotation>
+		<annotation cp="👏" type="tts">clapping hands</annotation>
+		<annotation cp="🙌">celebration | gesture | hand | hooray | raised | raising hands</annotation>
+		<annotation cp="🙌" type="tts">raising hands</annotation>
+		<annotation cp="👐">hand | open | open hands</annotation>
+		<annotation cp="👐" type="tts">open hands</annotation>
+		<annotation cp="🤲">palms up together | prayer</annotation>
+		<annotation cp="🤲" type="tts">palms up together</annotation>
+		<annotation cp="🤝">agreement | hand | handshake | meeting | shake</annotation>
+		<annotation cp="🤝" type="tts">handshake</annotation>
+		<annotation cp="🙏">ask | folded hands | hand | high 5 | high five | please | pray | thanks</annotation>
+		<annotation cp="🙏" type="tts">folded hands</annotation>
+		<annotation cp="✍">hand | write | writing hand</annotation>
+		<annotation cp="✍" type="tts">writing hand</annotation>
+		<annotation cp="💅">care | cosmetics | manicure | nail | polish</annotation>
+		<annotation cp="💅" type="tts">nail polish</annotation>
+		<annotation cp="🤳">camera | phone | selfie</annotation>
+		<annotation cp="🤳" type="tts">selfie</annotation>
+		<annotation cp="💪">biceps | comic | flex | flexed biceps | muscle</annotation>
+		<annotation cp="💪" type="tts">flexed biceps</annotation>
+		<annotation cp="🦾">accessibility | mechanical arm | prosthetic</annotation>
+		<annotation cp="🦾" type="tts">mechanical arm</annotation>
+		<annotation cp="🦿">accessibility | mechanical leg | prosthetic</annotation>
+		<annotation cp="🦿" type="tts">mechanical leg</annotation>
+		<annotation cp="🦵">kick | leg | limb</annotation>
+		<annotation cp="🦵" type="tts">leg</annotation>
+		<annotation cp="🦶">foot | kick | stomp</annotation>
+		<annotation cp="🦶" type="tts">foot</annotation>
+		<annotation cp="👂">body | ear</annotation>
+		<annotation cp="👂" type="tts">ear</annotation>
+		<annotation cp="🦻">accessibility | ear with hearing aid | hard of hearing</annotation>
+		<annotation cp="🦻" type="tts">ear with hearing aid</annotation>
+		<annotation cp="👃">body | nose</annotation>
+		<annotation cp="👃" type="tts">nose</annotation>
+		<annotation cp="🧠">brain | intelligent</annotation>
+		<annotation cp="🧠" type="tts">brain</annotation>
+		<annotation cp="🫀">anatomical | cardiology | heart | organ | pulse</annotation>
+		<annotation cp="🫀" type="tts">anatomical heart</annotation>
+		<annotation cp="🫁">breath | exhalation | inhalation | lungs | organ | respiration</annotation>
+		<annotation cp="🫁" type="tts">lungs</annotation>
+		<annotation cp="🦷">dentist | tooth</annotation>
+		<annotation cp="🦷" type="tts">tooth</annotation>
+		<annotation cp="🦴">bone | skeleton</annotation>
+		<annotation cp="🦴" type="tts">bone</annotation>
+		<annotation cp="👀">eye | eyes | face</annotation>
+		<annotation cp="👀" type="tts">eyes</annotation>
+		<annotation cp="👁">body | eye</annotation>
+		<annotation cp="👁" type="tts">eye</annotation>
+		<annotation cp="👅">body | tongue</annotation>
+		<annotation cp="👅" type="tts">tongue</annotation>
+		<annotation cp="👄">lips | mouth</annotation>
+		<annotation cp="👄" type="tts">mouth</annotation>
+		<annotation cp="👶">baby | young</annotation>
+		<annotation cp="👶" type="tts">baby</annotation>
+		<annotation cp="🧒">child | gender-neutral | unspecified gender | young</annotation>
+		<annotation cp="🧒" type="tts">child</annotation>
+		<annotation cp="👦">boy | young</annotation>
+		<annotation cp="👦" type="tts">boy</annotation>
+		<annotation cp="👧">girl | Virgo | young | zodiac</annotation>
+		<annotation cp="👧" type="tts">girl</annotation>
+		<annotation cp="🧑">adult | gender-neutral | person | unspecified gender</annotation>
+		<annotation cp="🧑" type="tts">person</annotation>
+		<annotation cp="👱">blond | blond-haired person | hair | person: blond hair</annotation>
+		<annotation cp="👱" type="tts">person: blond hair</annotation>
+		<annotation cp="👨">adult | man</annotation>
+		<annotation cp="👨" type="tts">man</annotation>
+		<annotation cp="🧔">beard | person | person: beard</annotation>
+		<annotation cp="🧔" type="tts">person: beard</annotation>
+		<annotation cp="🧔‍♂">beard | man | man: beard</annotation>
+		<annotation cp="🧔‍♂" type="tts">man: beard</annotation>
+		<annotation cp="👱‍♂">blond | blond-haired man | hair | man | man: blond hair</annotation>
+		<annotation cp="👱‍♂" type="tts">man: blond hair</annotation>
+		<annotation cp="👩">adult | woman</annotation>
+		<annotation cp="👩" type="tts">woman</annotation>
+		<annotation cp="🧔‍♀">beard | woman | woman: beard</annotation>
+		<annotation cp="🧔‍♀" type="tts">woman: beard</annotation>
+		<annotation cp="👱‍♀">blond-haired woman | blonde | hair | woman | woman: blond hair</annotation>
+		<annotation cp="👱‍♀" type="tts">woman: blond hair</annotation>
+		<annotation cp="🧓">adult | gender-neutral | old | older person | unspecified gender</annotation>
+		<annotation cp="🧓" type="tts">older person</annotation>
+		<annotation cp="👴">adult | man | old</annotation>
+		<annotation cp="👴" type="tts">old man</annotation>
+		<annotation cp="👵">adult | old | woman</annotation>
+		<annotation cp="👵" type="tts">old woman</annotation>
+		<annotation cp="🙍">frown | gesture | person frowning</annotation>
+		<annotation cp="🙍" type="tts">person frowning</annotation>
+		<annotation cp="🙍‍♂">frowning | gesture | man</annotation>
+		<annotation cp="🙍‍♂" type="tts">man frowning</annotation>
+		<annotation cp="🙍‍♀">frowning | gesture | woman</annotation>
+		<annotation cp="🙍‍♀" type="tts">woman frowning</annotation>
+		<annotation cp="🙎">gesture | person pouting | pouting</annotation>
+		<annotation cp="🙎" type="tts">person pouting</annotation>
+		<annotation cp="🙎‍♂">gesture | man | pouting</annotation>
+		<annotation cp="🙎‍♂" type="tts">man pouting</annotation>
+		<annotation cp="🙎‍♀">gesture | pouting | woman</annotation>
+		<annotation cp="🙎‍♀" type="tts">woman pouting</annotation>
+		<annotation cp="🙅">forbidden | gesture | hand | person gesturing NO | prohibited</annotation>
+		<annotation cp="🙅" type="tts">person gesturing NO</annotation>
+		<annotation cp="🙅‍♂">forbidden | gesture | hand | man | man gesturing NO | prohibited</annotation>
+		<annotation cp="🙅‍♂" type="tts">man gesturing NO</annotation>
+		<annotation cp="🙅‍♀">forbidden | gesture | hand | prohibited | woman | woman gesturing NO</annotation>
+		<annotation cp="🙅‍♀" type="tts">woman gesturing NO</annotation>
+		<annotation cp="🙆">gesture | hand | OK | person gesturing OK</annotation>
+		<annotation cp="🙆" type="tts">person gesturing OK</annotation>
+		<annotation cp="🙆‍♂">gesture | hand | man | man gesturing OK | OK</annotation>
+		<annotation cp="🙆‍♂" type="tts">man gesturing OK</annotation>
+		<annotation cp="🙆‍♀">gesture | hand | OK | woman | woman gesturing OK</annotation>
+		<annotation cp="🙆‍♀" type="tts">woman gesturing OK</annotation>
+		<annotation cp="💁">hand | help | information | person tipping hand | sassy | tipping</annotation>
+		<annotation cp="💁" type="tts">person tipping hand</annotation>
+		<annotation cp="💁‍♂">man | man tipping hand | sassy | tipping hand</annotation>
+		<annotation cp="💁‍♂" type="tts">man tipping hand</annotation>
+		<annotation cp="💁‍♀">sassy | tipping hand | woman | woman tipping hand</annotation>
+		<annotation cp="💁‍♀" type="tts">woman tipping hand</annotation>
+		<annotation cp="🙋">gesture | hand | happy | person raising hand | raised</annotation>
+		<annotation cp="🙋" type="tts">person raising hand</annotation>
+		<annotation cp="🙋‍♂">gesture | man | man raising hand | raising hand</annotation>
+		<annotation cp="🙋‍♂" type="tts">man raising hand</annotation>
+		<annotation cp="🙋‍♀">gesture | raising hand | woman | woman raising hand</annotation>
+		<annotation cp="🙋‍♀" type="tts">woman raising hand</annotation>
+		<annotation cp="🧏">accessibility | deaf | deaf person | ear | hear</annotation>
+		<annotation cp="🧏" type="tts">deaf person</annotation>
+		<annotation cp="🧏‍♂">deaf | man</annotation>
+		<annotation cp="🧏‍♂" type="tts">deaf man</annotation>
+		<annotation cp="🧏‍♀">deaf | woman</annotation>
+		<annotation cp="🧏‍♀" type="tts">deaf woman</annotation>
+		<annotation cp="🙇">apology | bow | gesture | person bowing | sorry</annotation>
+		<annotation cp="🙇" type="tts">person bowing</annotation>
+		<annotation cp="🙇‍♂">apology | bowing | favor | gesture | man | sorry</annotation>
+		<annotation cp="🙇‍♂" type="tts">man bowing</annotation>
+		<annotation cp="🙇‍♀">apology | bowing | favor | gesture | sorry | woman</annotation>
+		<annotation cp="🙇‍♀" type="tts">woman bowing</annotation>
+		<annotation cp="🤦">disbelief | exasperation | face | palm | person facepalming</annotation>
+		<annotation cp="🤦" type="tts">person facepalming</annotation>
+		<annotation cp="🤦‍♂">disbelief | exasperation | facepalm | man | man facepalming</annotation>
+		<annotation cp="🤦‍♂" type="tts">man facepalming</annotation>
+		<annotation cp="🤦‍♀">disbelief | exasperation | facepalm | woman | woman facepalming</annotation>
+		<annotation cp="🤦‍♀" type="tts">woman facepalming</annotation>
+		<annotation cp="🤷">doubt | ignorance | indifference | person shrugging | shrug</annotation>
+		<annotation cp="🤷" type="tts">person shrugging</annotation>
+		<annotation cp="🤷‍♂">doubt | ignorance | indifference | man | man shrugging | shrug</annotation>
+		<annotation cp="🤷‍♂" type="tts">man shrugging</annotation>
+		<annotation cp="🤷‍♀">doubt | ignorance | indifference | shrug | woman | woman shrugging</annotation>
+		<annotation cp="🤷‍♀" type="tts">woman shrugging</annotation>
+		<annotation cp="🧑‍⚕">doctor | health worker | healthcare | nurse | therapist</annotation>
+		<annotation cp="🧑‍⚕" type="tts">health worker</annotation>
+		<annotation cp="👨‍⚕">doctor | healthcare | man | man health worker | nurse | therapist</annotation>
+		<annotation cp="👨‍⚕" type="tts">man health worker</annotation>
+		<annotation cp="👩‍⚕">doctor | healthcare | nurse | therapist | woman | woman health worker</annotation>
+		<annotation cp="👩‍⚕" type="tts">woman health worker</annotation>
+		<annotation cp="🧑‍🎓">graduate | student</annotation>
+		<annotation cp="🧑‍🎓" type="tts">student</annotation>
+		<annotation cp="👨‍🎓">graduate | man | student</annotation>
+		<annotation cp="👨‍🎓" type="tts">man student</annotation>
+		<annotation cp="👩‍🎓">graduate | student | woman</annotation>
+		<annotation cp="👩‍🎓" type="tts">woman student</annotation>
+		<annotation cp="🧑‍🏫">instructor | professor | teacher</annotation>
+		<annotation cp="🧑‍🏫" type="tts">teacher</annotation>
+		<annotation cp="👨‍🏫">instructor | man | professor | teacher</annotation>
+		<annotation cp="👨‍🏫" type="tts">man teacher</annotation>
+		<annotation cp="👩‍🏫">instructor | professor | teacher | woman</annotation>
+		<annotation cp="👩‍🏫" type="tts">woman teacher</annotation>
+		<annotation cp="🧑‍⚖">judge | justice | scales</annotation>
+		<annotation cp="🧑‍⚖" type="tts">judge</annotation>
+		<annotation cp="👨‍⚖">judge | justice | man | scales</annotation>
+		<annotation cp="👨‍⚖" type="tts">man judge</annotation>
+		<annotation cp="👩‍⚖">judge | justice | scales | woman</annotation>
+		<annotation cp="👩‍⚖" type="tts">woman judge</annotation>
+		<annotation cp="🧑‍🌾">farmer | gardener | rancher</annotation>
+		<annotation cp="🧑‍🌾" type="tts">farmer</annotation>
+		<annotation cp="👨‍🌾">farmer | gardener | man | rancher</annotation>
+		<annotation cp="👨‍🌾" type="tts">man farmer</annotation>
+		<annotation cp="👩‍🌾">farmer | gardener | rancher | woman</annotation>
+		<annotation cp="👩‍🌾" type="tts">woman farmer</annotation>
+		<annotation cp="🧑‍🍳">chef | cook</annotation>
+		<annotation cp="🧑‍🍳" type="tts">cook</annotation>
+		<annotation cp="👨‍🍳">chef | cook | man</annotation>
+		<annotation cp="👨‍🍳" type="tts">man cook</annotation>
+		<annotation cp="👩‍🍳">chef | cook | woman</annotation>
+		<annotation cp="👩‍🍳" type="tts">woman cook</annotation>
+		<annotation cp="🧑‍🔧">electrician | mechanic | plumber | tradesperson</annotation>
+		<annotation cp="🧑‍🔧" type="tts">mechanic</annotation>
+		<annotation cp="👨‍🔧">electrician | man | mechanic | plumber | tradesperson</annotation>
+		<annotation cp="👨‍🔧" type="tts">man mechanic</annotation>
+		<annotation cp="👩‍🔧">electrician | mechanic | plumber | tradesperson | woman</annotation>
+		<annotation cp="👩‍🔧" type="tts">woman mechanic</annotation>
+		<annotation cp="🧑‍🏭">assembly | factory | industrial | worker</annotation>
+		<annotation cp="🧑‍🏭" type="tts">factory worker</annotation>
+		<annotation cp="👨‍🏭">assembly | factory | industrial | man | worker</annotation>
+		<annotation cp="👨‍🏭" type="tts">man factory worker</annotation>
+		<annotation cp="👩‍🏭">assembly | factory | industrial | woman | worker</annotation>
+		<annotation cp="👩‍🏭" type="tts">woman factory worker</annotation>
+		<annotation cp="🧑‍💼">architect | business | manager | office worker | white-collar</annotation>
+		<annotation cp="🧑‍💼" type="tts">office worker</annotation>
+		<annotation cp="👨‍💼">architect | business | man | man office worker | manager | white-collar</annotation>
+		<annotation cp="👨‍💼" type="tts">man office worker</annotation>
+		<annotation cp="👩‍💼">architect | business | manager | white-collar | woman | woman office worker</annotation>
+		<annotation cp="👩‍💼" type="tts">woman office worker</annotation>
+		<annotation cp="🧑‍🔬">biologist | chemist | engineer | physicist | scientist</annotation>
+		<annotation cp="🧑‍🔬" type="tts">scientist</annotation>
+		<annotation cp="👨‍🔬">biologist | chemist | engineer | man | physicist | scientist</annotation>
+		<annotation cp="👨‍🔬" type="tts">man scientist</annotation>
+		<annotation cp="👩‍🔬">biologist | chemist | engineer | physicist | scientist | woman</annotation>
+		<annotation cp="👩‍🔬" type="tts">woman scientist</annotation>
+		<annotation cp="🧑‍💻">coder | developer | inventor | software | technologist</annotation>
+		<annotation cp="🧑‍💻" type="tts">technologist</annotation>
+		<annotation cp="👨‍💻">coder | developer | inventor | man | software | technologist</annotation>
+		<annotation cp="👨‍💻" type="tts">man technologist</annotation>
+		<annotation cp="👩‍💻">coder | developer | inventor | software | technologist | woman</annotation>
+		<annotation cp="👩‍💻" type="tts">woman technologist</annotation>
+		<annotation cp="🧑‍🎤">actor | entertainer | rock | singer | star</annotation>
+		<annotation cp="🧑‍🎤" type="tts">singer</annotation>
+		<annotation cp="👨‍🎤">actor | entertainer | man | rock | singer | star</annotation>
+		<annotation cp="👨‍🎤" type="tts">man singer</annotation>
+		<annotation cp="👩‍🎤">actor | entertainer | rock | singer | star | woman</annotation>
+		<annotation cp="👩‍🎤" type="tts">woman singer</annotation>
+		<annotation cp="🧑‍🎨">artist | palette</annotation>
+		<annotation cp="🧑‍🎨" type="tts">artist</annotation>
+		<annotation cp="👨‍🎨">artist | man | palette</annotation>
+		<annotation cp="👨‍🎨" type="tts">man artist</annotation>
+		<annotation cp="👩‍🎨">artist | palette | woman</annotation>
+		<annotation cp="👩‍🎨" type="tts">woman artist</annotation>
+		<annotation cp="🧑‍✈">pilot | plane</annotation>
+		<annotation cp="🧑‍✈" type="tts">pilot</annotation>
+		<annotation cp="👨‍✈">man | pilot | plane</annotation>
+		<annotation cp="👨‍✈" type="tts">man pilot</annotation>
+		<annotation cp="👩‍✈">pilot | plane | woman</annotation>
+		<annotation cp="👩‍✈" type="tts">woman pilot</annotation>
+		<annotation cp="🧑‍🚀">astronaut | rocket</annotation>
+		<annotation cp="🧑‍🚀" type="tts">astronaut</annotation>
+		<annotation cp="👨‍🚀">astronaut | man | rocket</annotation>
+		<annotation cp="👨‍🚀" type="tts">man astronaut</annotation>
+		<annotation cp="👩‍🚀">astronaut | rocket | woman</annotation>
+		<annotation cp="👩‍🚀" type="tts">woman astronaut</annotation>
+		<annotation cp="🧑‍🚒">firefighter | firetruck</annotation>
+		<annotation cp="🧑‍🚒" type="tts">firefighter</annotation>
+		<annotation cp="👨‍🚒">firefighter | firetruck | man</annotation>
+		<annotation cp="👨‍🚒" type="tts">man firefighter</annotation>
+		<annotation cp="👩‍🚒">firefighter | firetruck | woman</annotation>
+		<annotation cp="👩‍🚒" type="tts">woman firefighter</annotation>
+		<annotation cp="👮">cop | officer | police</annotation>
+		<annotation cp="👮" type="tts">police officer</annotation>
+		<annotation cp="👮‍♂">cop | man | officer | police</annotation>
+		<annotation cp="👮‍♂" type="tts">man police officer</annotation>
+		<annotation cp="👮‍♀">cop | officer | police | woman</annotation>
+		<annotation cp="👮‍♀" type="tts">woman police officer</annotation>
+		<annotation cp="🕵">detective | sleuth | spy</annotation>
+		<annotation cp="🕵" type="tts">detective</annotation>
+		<annotation cp="🕵‍♂">detective | man | sleuth | spy</annotation>
+		<annotation cp="🕵‍♂" type="tts">man detective</annotation>
+		<annotation cp="🕵‍♀">detective | sleuth | spy | woman</annotation>
+		<annotation cp="🕵‍♀" type="tts">woman detective</annotation>
+		<annotation cp="💂">guard</annotation>
+		<annotation cp="💂" type="tts">guard</annotation>
+		<annotation cp="💂‍♂">guard | man</annotation>
+		<annotation cp="💂‍♂" type="tts">man guard</annotation>
+		<annotation cp="💂‍♀">guard | woman</annotation>
+		<annotation cp="💂‍♀" type="tts">woman guard</annotation>
+		<annotation cp="🥷">fighter | hidden | ninja | stealth</annotation>
+		<annotation cp="🥷" type="tts">ninja</annotation>
+		<annotation cp="👷">construction | hat | worker</annotation>
+		<annotation cp="👷" type="tts">construction worker</annotation>
+		<annotation cp="👷‍♂">construction | man | worker</annotation>
+		<annotation cp="👷‍♂" type="tts">man construction worker</annotation>
+		<annotation cp="👷‍♀">construction | woman | worker</annotation>
+		<annotation cp="👷‍♀" type="tts">woman construction worker</annotation>
+		<annotation cp="🤴">prince</annotation>
+		<annotation cp="🤴" type="tts">prince</annotation>
+		<annotation cp="👸">fairy tale | fantasy | princess</annotation>
+		<annotation cp="👸" type="tts">princess</annotation>
+		<annotation cp="👳">person wearing turban | turban</annotation>
+		<annotation cp="👳" type="tts">person wearing turban</annotation>
+		<annotation cp="👳‍♂">man | man wearing turban | turban</annotation>
+		<annotation cp="👳‍♂" type="tts">man wearing turban</annotation>
+		<annotation cp="👳‍♀">turban | woman | woman wearing turban</annotation>
+		<annotation cp="👳‍♀" type="tts">woman wearing turban</annotation>
+		<annotation cp="👲">cap | gua pi mao | hat | person | person with skullcap | skullcap</annotation>
+		<annotation cp="👲" type="tts">person with skullcap</annotation>
+		<annotation cp="🧕">headscarf | hijab | mantilla | tichel | woman with headscarf</annotation>
+		<annotation cp="🧕" type="tts">woman with headscarf</annotation>
+		<annotation cp="🤵">groom | person | person in tuxedo | tuxedo</annotation>
+		<annotation cp="🤵" type="tts">person in tuxedo</annotation>
+		<!-- (more) Generated lines from Emoji Data v13, using GenerateCldrData.java -->
+		<annotation cp="🤵‍♂">man | man in tuxedo | tuxedo</annotation> <!-- 1F935 200D 2642 -->
+		<annotation cp="🤵‍♂" type="tts">man in tuxedo</annotation>
+		<annotation cp="🤵‍♀">tuxedo | woman | woman in tuxedo</annotation> <!-- 1F935 200D 2640 -->
+		<annotation cp="🤵‍♀" type="tts">woman in tuxedo</annotation>
+		<annotation cp="👰">bride | person | person with veil | veil | wedding</annotation>
+		<annotation cp="👰" type="tts">person with veil</annotation>
+		<annotation cp="👰‍♂">man | man with veil | veil</annotation> <!-- 1F470 200D 2642 -->
+		<annotation cp="👰‍♂" type="tts">man with veil</annotation>
+		<annotation cp="👰‍♀">veil | woman | woman with veil</annotation> <!-- 1F470 200D 2640 -->
+		<annotation cp="👰‍♀" type="tts">woman with veil</annotation>
+		<annotation cp="🤰">pregnant | woman</annotation>
+		<annotation cp="🤰" type="tts">pregnant woman</annotation>
+		<annotation cp="🤱">baby | breast | breast-feeding | nursing</annotation>
+		<annotation cp="🤱" type="tts">breast-feeding</annotation>
+		<annotation cp="👩‍🍼">baby | feeding | nursing | woman</annotation> <!-- 1F469 200D 1F37C -->
+		<annotation cp="👩‍🍼" type="tts">woman feeding baby</annotation>
+		<annotation cp="👨‍🍼">baby | feeding | man | nursing</annotation> <!-- 1F468 200D 1F37C -->
+		<annotation cp="👨‍🍼" type="tts">man feeding baby</annotation>
+		<annotation cp="🧑‍🍼">baby | feeding | nursing | person</annotation> <!-- 1F9D1 200D 1F37C -->
+		<annotation cp="🧑‍🍼" type="tts">person feeding baby</annotation>
+		<annotation cp="👼">angel | baby | face | fairy tale | fantasy</annotation>
+		<annotation cp="👼" type="tts">baby angel</annotation>
+		<annotation cp="🎅">celebration | Christmas | claus | father | santa | Santa Claus</annotation>
+		<annotation cp="🎅" type="tts">Santa Claus</annotation>
+		<annotation cp="🤶">celebration | Christmas | claus | mother | Mrs. | Mrs. Claus</annotation>
+		<annotation cp="🤶" type="tts">Mrs. Claus</annotation>
+		<annotation cp="🧑‍🎄">Claus, christmas | mx claus</annotation> <!-- 1F9D1 200D 1F384 -->
+		<annotation cp="🧑‍🎄" type="tts">mx claus</annotation>
+		<annotation cp="🦸">good | hero | heroine | superhero | superpower</annotation>
+		<annotation cp="🦸" type="tts">superhero</annotation>
+		<annotation cp="🦸‍♂">good | hero | man | man superhero | superpower</annotation>
+		<annotation cp="🦸‍♂" type="tts">man superhero</annotation>
+		<annotation cp="🦸‍♀">good | hero | heroine | superpower | woman | woman superhero</annotation>
+		<annotation cp="🦸‍♀" type="tts">woman superhero</annotation>
+		<annotation cp="🦹">criminal | evil | superpower | supervillain | villain</annotation>
+		<annotation cp="🦹" type="tts">supervillain</annotation>
+		<annotation cp="🦹‍♂">criminal | evil | man | man supervillain | superpower | villain</annotation>
+		<annotation cp="🦹‍♂" type="tts">man supervillain</annotation>
+		<annotation cp="🦹‍♀">criminal | evil | superpower | villain | woman | woman supervillain</annotation>
+		<annotation cp="🦹‍♀" type="tts">woman supervillain</annotation>
+		<annotation cp="🧙">mage | sorcerer | sorceress | witch | wizard</annotation>
+		<annotation cp="🧙" type="tts">mage</annotation>
+		<annotation cp="🧙‍♂">man mage | sorcerer | wizard</annotation>
+		<annotation cp="🧙‍♂" type="tts">man mage</annotation>
+		<annotation cp="🧙‍♀">sorceress | witch | woman mage</annotation>
+		<annotation cp="🧙‍♀" type="tts">woman mage</annotation>
+		<annotation cp="🧚">fairy | Oberon | Puck | Titania</annotation>
+		<annotation cp="🧚" type="tts">fairy</annotation>
+		<annotation cp="🧚‍♂">man fairy | Oberon | Puck</annotation>
+		<annotation cp="🧚‍♂" type="tts">man fairy</annotation>
+		<annotation cp="🧚‍♀">Titania | woman fairy</annotation>
+		<annotation cp="🧚‍♀" type="tts">woman fairy</annotation>
+		<annotation cp="🧛">Dracula | undead | vampire</annotation>
+		<annotation cp="🧛" type="tts">vampire</annotation>
+		<annotation cp="🧛‍♂">Dracula | man vampire | undead</annotation>
+		<annotation cp="🧛‍♂" type="tts">man vampire</annotation>
+		<annotation cp="🧛‍♀">undead | woman vampire</annotation>
+		<annotation cp="🧛‍♀" type="tts">woman vampire</annotation>
+		<annotation cp="🧜">mermaid | merman | merperson | merwoman</annotation>
+		<annotation cp="🧜" type="tts">merperson</annotation>
+		<annotation cp="🧜‍♂">merman | Triton</annotation>
+		<annotation cp="🧜‍♂" type="tts">merman</annotation>
+		<annotation cp="🧜‍♀">mermaid | merwoman</annotation>
+		<annotation cp="🧜‍♀" type="tts">mermaid</annotation>
+		<annotation cp="🧝">elf | magical</annotation>
+		<annotation cp="🧝" type="tts">elf</annotation>
+		<annotation cp="🧝‍♂">magical | man elf</annotation>
+		<annotation cp="🧝‍♂" type="tts">man elf</annotation>
+		<annotation cp="🧝‍♀">magical | woman elf</annotation>
+		<annotation cp="🧝‍♀" type="tts">woman elf</annotation>
+		<annotation cp="🧞">djinn | genie</annotation>
+		<annotation cp="🧞" type="tts">genie</annotation>
+		<annotation cp="🧞‍♂">djinn | man genie</annotation>
+		<annotation cp="🧞‍♂" type="tts">man genie</annotation>
+		<annotation cp="🧞‍♀">djinn | woman genie</annotation>
+		<annotation cp="🧞‍♀" type="tts">woman genie</annotation>
+		<annotation cp="🧟">undead | walking dead | zombie</annotation>
+		<annotation cp="🧟" type="tts">zombie</annotation>
+		<annotation cp="🧟‍♂">man zombie | undead | walking dead</annotation>
+		<annotation cp="🧟‍♂" type="tts">man zombie</annotation>
+		<annotation cp="🧟‍♀">undead | walking dead | woman zombie</annotation>
+		<annotation cp="🧟‍♀" type="tts">woman zombie</annotation>
+		<annotation cp="💆">face | massage | person getting massage | salon</annotation>
+		<annotation cp="💆" type="tts">person getting massage</annotation>
+		<annotation cp="💆‍♂">face | man | man getting massage | massage</annotation>
+		<annotation cp="💆‍♂" type="tts">man getting massage</annotation>
+		<annotation cp="💆‍♀">face | massage | woman | woman getting massage</annotation>
+		<annotation cp="💆‍♀" type="tts">woman getting massage</annotation>
+		<annotation cp="💇">barber | beauty | haircut | parlor | person getting haircut</annotation>
+		<annotation cp="💇" type="tts">person getting haircut</annotation>
+		<annotation cp="💇‍♂">haircut | man | man getting haircut</annotation>
+		<annotation cp="💇‍♂" type="tts">man getting haircut</annotation>
+		<annotation cp="💇‍♀">haircut | woman | woman getting haircut</annotation>
+		<annotation cp="💇‍♀" type="tts">woman getting haircut</annotation>
+		<annotation cp="🚶">hike | person walking | walk | walking</annotation>
+		<annotation cp="🚶" type="tts">person walking</annotation>
+		<annotation cp="🚶‍♂">hike | man | man walking | walk</annotation>
+		<annotation cp="🚶‍♂" type="tts">man walking</annotation>
+		<annotation cp="🚶‍♀">hike | walk | woman | woman walking</annotation>
+		<annotation cp="🚶‍♀" type="tts">woman walking</annotation>
+		<annotation cp="🧍">person standing | stand | standing</annotation>
+		<annotation cp="🧍" type="tts">person standing</annotation>
+		<annotation cp="🧍‍♂">man | standing</annotation>
+		<annotation cp="🧍‍♂" type="tts">man standing</annotation>
+		<annotation cp="🧍‍♀">standing | woman</annotation>
+		<annotation cp="🧍‍♀" type="tts">woman standing</annotation>
+		<annotation cp="🧎">kneel | kneeling | person kneeling</annotation>
+		<annotation cp="🧎" type="tts">person kneeling</annotation>
+		<annotation cp="🧎‍♂">kneeling | man</annotation>
+		<annotation cp="🧎‍♂" type="tts">man kneeling</annotation>
+		<annotation cp="🧎‍♀">kneeling | woman</annotation>
+		<annotation cp="🧎‍♀" type="tts">woman kneeling</annotation>
+		<annotation cp="🧑‍🦯">accessibility | blind | person with white cane</annotation>
+		<annotation cp="🧑‍🦯" type="tts">person with white cane</annotation>
+		<annotation cp="👨‍🦯">accessibility | blind | man | man with white cane</annotation>
+		<annotation cp="👨‍🦯" type="tts">man with white cane</annotation>
+		<annotation cp="👩‍🦯">accessibility | blind | woman | woman with white cane</annotation>
+		<annotation cp="👩‍🦯" type="tts">woman with white cane</annotation>
+		<annotation cp="🧑‍🦼">accessibility | person in motorized wheelchair | wheelchair</annotation>
+		<annotation cp="🧑‍🦼" type="tts">person in motorized wheelchair</annotation>
+		<annotation cp="👨‍🦼">accessibility | man | man in motorized wheelchair | wheelchair</annotation>
+		<annotation cp="👨‍🦼" type="tts">man in motorized wheelchair</annotation>
+		<annotation cp="👩‍🦼">accessibility | wheelchair | woman | woman in motorized wheelchair</annotation>
+		<annotation cp="👩‍🦼" type="tts">woman in motorized wheelchair</annotation>
+		<annotation cp="🧑‍🦽">accessibility | person in manual wheelchair | wheelchair</annotation>
+		<annotation cp="🧑‍🦽" type="tts">person in manual wheelchair</annotation>
+		<annotation cp="👨‍🦽">accessibility | man | man in manual wheelchair | wheelchair</annotation>
+		<annotation cp="👨‍🦽" type="tts">man in manual wheelchair</annotation>
+		<annotation cp="👩‍🦽">accessibility | wheelchair | woman | woman in manual wheelchair</annotation>
+		<annotation cp="👩‍🦽" type="tts">woman in manual wheelchair</annotation>
+		<annotation cp="🏃">marathon | person running | running</annotation>
+		<annotation cp="🏃" type="tts">person running</annotation>
+		<annotation cp="🏃‍♂">man | marathon | racing | running</annotation>
+		<annotation cp="🏃‍♂" type="tts">man running</annotation>
+		<annotation cp="🏃‍♀">marathon | racing | running | woman</annotation>
+		<annotation cp="🏃‍♀" type="tts">woman running</annotation>
+		<annotation cp="💃">dance | dancing | woman</annotation>
+		<annotation cp="💃" type="tts">woman dancing</annotation>
+		<annotation cp="🕺">dance | dancing | man</annotation>
+		<annotation cp="🕺" type="tts">man dancing</annotation>
+		<annotation cp="🕴">business | person | person in suit levitating | suit</annotation>
+		<annotation cp="🕴" type="tts">person in suit levitating</annotation>
+		<annotation cp="👯">bunny ear | dancer | partying | people with bunny ears</annotation>
+		<annotation cp="👯" type="tts">people with bunny ears</annotation>
+		<annotation cp="👯‍♂">bunny ear | dancer | men | men with bunny ears | partying</annotation>
+		<annotation cp="👯‍♂" type="tts">men with bunny ears</annotation>
+		<annotation cp="👯‍♀">bunny ear | dancer | partying | women | women with bunny ears</annotation>
+		<annotation cp="👯‍♀" type="tts">women with bunny ears</annotation>
+		<annotation cp="🧖">person in steamy room | sauna | steam room</annotation>
+		<annotation cp="🧖" type="tts">person in steamy room</annotation>
+		<annotation cp="🧖‍♂">man in steamy room | sauna | steam room</annotation>
+		<annotation cp="🧖‍♂" type="tts">man in steamy room</annotation>
+		<annotation cp="🧖‍♀">sauna | steam room | woman in steamy room</annotation>
+		<annotation cp="🧖‍♀" type="tts">woman in steamy room</annotation>
+		<annotation cp="🧗">climber | person climbing</annotation>
+		<annotation cp="🧗" type="tts">person climbing</annotation>
+		<annotation cp="🧗‍♂">climber | man climbing</annotation>
+		<annotation cp="🧗‍♂" type="tts">man climbing</annotation>
+		<annotation cp="🧗‍♀">climber | woman climbing</annotation>
+		<annotation cp="🧗‍♀" type="tts">woman climbing</annotation>
+		<annotation cp="🤺">fencer | fencing | person fencing | sword</annotation>
+		<annotation cp="🤺" type="tts">person fencing</annotation>
+		<annotation cp="🏇">horse | jockey | racehorse | racing</annotation>
+		<annotation cp="🏇" type="tts">horse racing</annotation>
+		<annotation cp="⛷">ski | skier | snow</annotation>
+		<annotation cp="⛷" type="tts">skier</annotation>
+		<annotation cp="🏂">ski | snow | snowboard | snowboarder</annotation>
+		<annotation cp="🏂" type="tts">snowboarder</annotation>
+		<annotation cp="🏌">ball | golf | person golfing</annotation>
+		<annotation cp="🏌" type="tts">person golfing</annotation>
+		<annotation cp="🏌‍♂">golf | man | man golfing</annotation>
+		<annotation cp="🏌‍♂" type="tts">man golfing</annotation>
+		<annotation cp="🏌‍♀">golf | woman | woman golfing</annotation>
+		<annotation cp="🏌‍♀" type="tts">woman golfing</annotation>
+		<annotation cp="🏄">person surfing | surfing</annotation>
+		<annotation cp="🏄" type="tts">person surfing</annotation>
+		<annotation cp="🏄‍♂">man | surfing</annotation>
+		<annotation cp="🏄‍♂" type="tts">man surfing</annotation>
+		<annotation cp="🏄‍♀">surfing | woman</annotation>
+		<annotation cp="🏄‍♀" type="tts">woman surfing</annotation>
+		<annotation cp="🚣">boat | person rowing boat | rowboat</annotation>
+		<annotation cp="🚣" type="tts">person rowing boat</annotation>
+		<annotation cp="🚣‍♂">boat | man | man rowing boat | rowboat</annotation>
+		<annotation cp="🚣‍♂" type="tts">man rowing boat</annotation>
+		<annotation cp="🚣‍♀">boat | rowboat | woman | woman rowing boat</annotation>
+		<annotation cp="🚣‍♀" type="tts">woman rowing boat</annotation>
+		<annotation cp="🏊">person swimming | swim</annotation>
+		<annotation cp="🏊" type="tts">person swimming</annotation>
+		<annotation cp="🏊‍♂">man | man swimming | swim</annotation>
+		<annotation cp="🏊‍♂" type="tts">man swimming</annotation>
+		<annotation cp="🏊‍♀">swim | woman | woman swimming</annotation>
+		<annotation cp="🏊‍♀" type="tts">woman swimming</annotation>
+		<annotation cp="⛹">ball | person bouncing ball</annotation>
+		<annotation cp="⛹" type="tts">person bouncing ball</annotation>
+		<annotation cp="⛹‍♂">ball | man | man bouncing ball</annotation>
+		<annotation cp="⛹‍♂" type="tts">man bouncing ball</annotation>
+		<annotation cp="⛹‍♀">ball | woman | woman bouncing ball</annotation>
+		<annotation cp="⛹‍♀" type="tts">woman bouncing ball</annotation>
+		<annotation cp="🏋">lifter | person lifting weights | weight</annotation>
+		<annotation cp="🏋" type="tts">person lifting weights</annotation>
+		<annotation cp="🏋‍♂">man | man lifting weights | weight lifter</annotation>
+		<annotation cp="🏋‍♂" type="tts">man lifting weights</annotation>
+		<annotation cp="🏋‍♀">weight lifter | woman | woman lifting weights</annotation>
+		<annotation cp="🏋‍♀" type="tts">woman lifting weights</annotation>
+		<annotation cp="🚴">bicycle | biking | cyclist | person biking</annotation>
+		<annotation cp="🚴" type="tts">person biking</annotation>
+		<annotation cp="🚴‍♂">bicycle | biking | cyclist | man</annotation>
+		<annotation cp="🚴‍♂" type="tts">man biking</annotation>
+		<annotation cp="🚴‍♀">bicycle | biking | cyclist | woman</annotation>
+		<annotation cp="🚴‍♀" type="tts">woman biking</annotation>
+		<annotation cp="🚵">bicycle | bicyclist | bike | cyclist | mountain | person mountain biking</annotation>
+		<annotation cp="🚵" type="tts">person mountain biking</annotation>
+		<annotation cp="🚵‍♂">bicycle | bike | cyclist | man | man mountain biking | mountain</annotation>
+		<annotation cp="🚵‍♂" type="tts">man mountain biking</annotation>
+		<annotation cp="🚵‍♀">bicycle | bike | biking | cyclist | mountain | woman</annotation>
+		<annotation cp="🚵‍♀" type="tts">woman mountain biking</annotation>
+		<annotation cp="🤸">cartwheel | gymnastics | person cartwheeling</annotation>
+		<annotation cp="🤸" type="tts">person cartwheeling</annotation>
+		<annotation cp="🤸‍♂">cartwheel | gymnastics | man | man cartwheeling</annotation>
+		<annotation cp="🤸‍♂" type="tts">man cartwheeling</annotation>
+		<annotation cp="🤸‍♀">cartwheel | gymnastics | woman | woman cartwheeling</annotation>
+		<annotation cp="🤸‍♀" type="tts">woman cartwheeling</annotation>
+		<annotation cp="🤼">people wrestling | wrestle | wrestler</annotation>
+		<annotation cp="🤼" type="tts">people wrestling</annotation>
+		<annotation cp="🤼‍♂">men | men wrestling | wrestle</annotation>
+		<annotation cp="🤼‍♂" type="tts">men wrestling</annotation>
+		<annotation cp="🤼‍♀">women | women wrestling | wrestle</annotation>
+		<annotation cp="🤼‍♀" type="tts">women wrestling</annotation>
+		<annotation cp="🤽">person playing water polo | polo | water</annotation>
+		<annotation cp="🤽" type="tts">person playing water polo</annotation>
+		<annotation cp="🤽‍♂">man | man playing water polo | water polo</annotation>
+		<annotation cp="🤽‍♂" type="tts">man playing water polo</annotation>
+		<annotation cp="🤽‍♀">water polo | woman | woman playing water polo</annotation>
+		<annotation cp="🤽‍♀" type="tts">woman playing water polo</annotation>
+		<annotation cp="🤾">ball | handball | person playing handball</annotation>
+		<annotation cp="🤾" type="tts">person playing handball</annotation>
+		<annotation cp="🤾‍♂">handball | man | man playing handball</annotation>
+		<annotation cp="🤾‍♂" type="tts">man playing handball</annotation>
+		<annotation cp="🤾‍♀">handball | woman | woman playing handball</annotation>
+		<annotation cp="🤾‍♀" type="tts">woman playing handball</annotation>
+		<annotation cp="🤹">balance | juggle | multitask | person juggling | skill</annotation>
+		<annotation cp="🤹" type="tts">person juggling</annotation>
+		<annotation cp="🤹‍♂">juggling | man | multitask</annotation>
+		<annotation cp="🤹‍♂" type="tts">man juggling</annotation>
+		<annotation cp="🤹‍♀">juggling | multitask | woman</annotation>
+		<annotation cp="🤹‍♀" type="tts">woman juggling</annotation>
+		<annotation cp="🧘">meditation | person in lotus position | yoga</annotation>
+		<annotation cp="🧘" type="tts">person in lotus position</annotation>
+		<annotation cp="🧘‍♂">man in lotus position | meditation | yoga</annotation>
+		<annotation cp="🧘‍♂" type="tts">man in lotus position</annotation>
+		<annotation cp="🧘‍♀">meditation | woman in lotus position | yoga</annotation>
+		<annotation cp="🧘‍♀" type="tts">woman in lotus position</annotation>
+		<annotation cp="🛀">bath | bathtub | person taking bath</annotation>
+		<annotation cp="🛀" type="tts">person taking bath</annotation>
+		<annotation cp="🛌">hotel | person in bed | sleep</annotation>
+		<annotation cp="🛌" type="tts">person in bed</annotation>
+		<annotation cp="🧑‍🤝‍🧑">couple | hand | hold | holding hands | people holding hands | person</annotation>
+		<annotation cp="🧑‍🤝‍🧑" type="tts">people holding hands</annotation>
+		<annotation cp="👭">couple | hand | holding hands | women | women holding hands</annotation>
+		<annotation cp="👭" type="tts">women holding hands</annotation>
+		<annotation cp="👫">couple | hand | hold | holding hands | man | woman | woman and man holding hands</annotation>
+		<annotation cp="👫" type="tts">woman and man holding hands</annotation>
+		<annotation cp="👬">couple | Gemini | holding hands | man | men | men holding hands | twins | zodiac</annotation>
+		<annotation cp="👬" type="tts">men holding hands</annotation>
+		<annotation cp="💏">couple | kiss</annotation>
+		<annotation cp="💏" type="tts">kiss</annotation>
+		<annotation cp="💑">couple | couple with heart | love</annotation>
+		<annotation cp="💑" type="tts">couple with heart</annotation>
+		<annotation cp="👪">family</annotation>
+		<annotation cp="👪" type="tts">family</annotation>
+		<annotation cp="🗣">face | head | silhouette | speak | speaking</annotation>
+		<annotation cp="🗣" type="tts">speaking head</annotation>
+		<annotation cp="👤">bust | bust in silhouette | silhouette</annotation>
+		<annotation cp="👤" type="tts">bust in silhouette</annotation>
+		<annotation cp="👥">bust | busts in silhouette | silhouette</annotation>
+		<annotation cp="👥" type="tts">busts in silhouette</annotation>
+		<annotation cp="🫂">goodbye | hello | hug | people hugging | thanks</annotation>
+		<annotation cp="🫂" type="tts">people hugging</annotation>
+		<annotation cp="👣">clothing | footprint | footprints | print</annotation>
+		<annotation cp="👣" type="tts">footprints</annotation>
+		<annotation cp="🦰">ginger | red hair | redhead</annotation>
+		<annotation cp="🦰" type="tts">red hair</annotation>
+		<annotation cp="🦱">afro | curly | curly hair | ringlets</annotation>
+		<annotation cp="🦱" type="tts">curly hair</annotation>
+		<annotation cp="🦳">gray | hair | old | white</annotation>
+		<annotation cp="🦳" type="tts">white hair</annotation>
+		<annotation cp="🦲">bald | chemotherapy | hairless | no hair | shaven</annotation>
+		<annotation cp="🦲" type="tts">bald</annotation>
+		<annotation cp="🐵">face | monkey</annotation>
+		<annotation cp="🐵" type="tts">monkey face</annotation>
+		<annotation cp="🐒">monkey</annotation>
+		<annotation cp="🐒" type="tts">monkey</annotation>
+		<annotation cp="🦍">gorilla</annotation>
+		<annotation cp="🦍" type="tts">gorilla</annotation>
+		<annotation cp="🦧">ape | orangutan</annotation>
+		<annotation cp="🦧" type="tts">orangutan</annotation>
+		<annotation cp="🐶">dog | face | pet</annotation>
+		<annotation cp="🐶" type="tts">dog face</annotation>
+		<annotation cp="🐕">dog | pet</annotation>
+		<annotation cp="🐕" type="tts">dog</annotation>
+		<annotation cp="🦮">accessibility | blind | guide | guide dog</annotation>
+		<annotation cp="🦮" type="tts">guide dog</annotation>
+		<annotation cp="🐕‍🦺">accessibility | assistance | dog | service</annotation>
+		<annotation cp="🐕‍🦺" type="tts">service dog</annotation>
+		<annotation cp="🐩">dog | poodle</annotation>
+		<annotation cp="🐩" type="tts">poodle</annotation>
+		<annotation cp="🐺">face | wolf</annotation>
+		<annotation cp="🐺" type="tts">wolf</annotation>
+		<annotation cp="🦊">face | fox</annotation>
+		<annotation cp="🦊" type="tts">fox</annotation>
+		<annotation cp="🦝">curious | raccoon | sly</annotation>
+		<annotation cp="🦝" type="tts">raccoon</annotation>
+		<annotation cp="🐱">cat | face | pet</annotation>
+		<annotation cp="🐱" type="tts">cat face</annotation>
+		<annotation cp="🐈">cat | pet</annotation>
+		<annotation cp="🐈" type="tts">cat</annotation>
+		<annotation cp="🐈‍⬛">black | cat | unlucky</annotation> <!-- 1F408 200D 2B1B -->
+		<annotation cp="🐈‍⬛" type="tts">black cat</annotation>
+		<annotation cp="🦁">face | Leo | lion | zodiac</annotation>
+		<annotation cp="🦁" type="tts">lion</annotation>
+		<annotation cp="🐯">face | tiger</annotation>
+		<annotation cp="🐯" type="tts">tiger face</annotation>
+		<annotation cp="🐅">tiger</annotation>
+		<annotation cp="🐅" type="tts">tiger</annotation>
+		<annotation cp="🐆">leopard</annotation>
+		<annotation cp="🐆" type="tts">leopard</annotation>
+		<annotation cp="🐴">face | horse</annotation>
+		<annotation cp="🐴" type="tts">horse face</annotation>
+		<annotation cp="🐎">equestrian | horse | racehorse | racing</annotation>
+		<annotation cp="🐎" type="tts">horse</annotation>
+		<annotation cp="🦄">face | unicorn</annotation>
+		<annotation cp="🦄" type="tts">unicorn</annotation>
+		<annotation cp="🦓">stripe | zebra</annotation>
+		<annotation cp="🦓" type="tts">zebra</annotation>
+		<annotation cp="🦌">deer</annotation>
+		<annotation cp="🦌" type="tts">deer</annotation>
+		<annotation cp="🦬">bison | buffalo | herd | wisent</annotation>
+		<annotation cp="🦬" type="tts">bison</annotation>
+		<annotation cp="🐮">cow | face</annotation>
+		<annotation cp="🐮" type="tts">cow face</annotation>
+		<annotation cp="🐂">bull | ox | Taurus | zodiac</annotation>
+		<annotation cp="🐂" type="tts">ox</annotation>
+		<annotation cp="🐃">buffalo | water</annotation>
+		<annotation cp="🐃" type="tts">water buffalo</annotation>
+		<annotation cp="🐄">cow</annotation>
+		<annotation cp="🐄" type="tts">cow</annotation>
+		<annotation cp="🐷">face | pig</annotation>
+		<annotation cp="🐷" type="tts">pig face</annotation>
+		<annotation cp="🐖">pig | sow</annotation>
+		<annotation cp="🐖" type="tts">pig</annotation>
+		<annotation cp="🐗">boar | pig</annotation>
+		<annotation cp="🐗" type="tts">boar</annotation>
+		<annotation cp="🐽">face | nose | pig</annotation>
+		<annotation cp="🐽" type="tts">pig nose</annotation>
+		<annotation cp="🐏">Aries | male | ram | sheep | zodiac</annotation>
+		<annotation cp="🐏" type="tts">ram</annotation>
+		<annotation cp="🐑">ewe | female | sheep</annotation>
+		<annotation cp="🐑" type="tts">ewe</annotation>
+		<annotation cp="🐐">Capricorn | goat | zodiac</annotation>
+		<annotation cp="🐐" type="tts">goat</annotation>
+		<annotation cp="🐪">camel | dromedary | hump</annotation>
+		<annotation cp="🐪" type="tts">camel</annotation>
+		<annotation cp="🐫">bactrian | camel | hump | two-hump camel</annotation>
+		<annotation cp="🐫" type="tts">two-hump camel</annotation>
+		<annotation cp="🦙">alpaca | guanaco | llama | vicuña | wool</annotation>
+		<annotation cp="🦙" type="tts">llama</annotation>
+		<annotation cp="🦒">giraffe | spots</annotation>
+		<annotation cp="🦒" type="tts">giraffe</annotation>
+		<annotation cp="🐘">elephant</annotation>
+		<annotation cp="🐘" type="tts">elephant</annotation>
+		<annotation cp="🦣">extinction | large | mammoth | tusk | woolly</annotation>
+		<annotation cp="🦣" type="tts">mammoth</annotation>
+		<annotation cp="🦏">rhinoceros</annotation>
+		<annotation cp="🦏" type="tts">rhinoceros</annotation>
+		<annotation cp="🦛">hippo | hippopotamus</annotation>
+		<annotation cp="🦛" type="tts">hippopotamus</annotation>
+		<annotation cp="🐭">face | mouse</annotation>
+		<annotation cp="🐭" type="tts">mouse face</annotation>
+		<annotation cp="🐁">mouse</annotation>
+		<annotation cp="🐁" type="tts">mouse</annotation>
+		<annotation cp="🐀">rat</annotation>
+		<annotation cp="🐀" type="tts">rat</annotation>
+		<annotation cp="🐹">face | hamster | pet</annotation>
+		<annotation cp="🐹" type="tts">hamster</annotation>
+		<annotation cp="🐰">bunny | face | pet | rabbit</annotation>
+		<annotation cp="🐰" type="tts">rabbit face</annotation>
+		<annotation cp="🐇">bunny | pet | rabbit</annotation>
+		<annotation cp="🐇" type="tts">rabbit</annotation>
+		<annotation cp="🐿">chipmunk | squirrel</annotation>
+		<annotation cp="🐿" type="tts">chipmunk</annotation>
+		<annotation cp="🦫">beaver | dam</annotation>
+		<annotation cp="🦫" type="tts">beaver</annotation>
+		<annotation cp="🦔">hedgehog | spiny</annotation>
+		<annotation cp="🦔" type="tts">hedgehog</annotation>
+		<annotation cp="🦇">bat | vampire</annotation>
+		<annotation cp="🦇" type="tts">bat</annotation>
+		<annotation cp="🐻">bear | face</annotation>
+		<annotation cp="🐻" type="tts">bear</annotation>
+		<annotation cp="🐻‍❄">arctic | bear | polar bear | white</annotation> <!-- 1F43B 200D 2744 -->
+		<annotation cp="🐻‍❄" type="tts">polar bear</annotation>
+		<annotation cp="🐨">bear | koala</annotation>
+		<annotation cp="🐨" type="tts">koala</annotation>
+		<annotation cp="🐼">face | panda</annotation>
+		<annotation cp="🐼" type="tts">panda</annotation>
+		<annotation cp="🦥">lazy | sloth | slow</annotation>
+		<annotation cp="🦥" type="tts">sloth</annotation>
+		<annotation cp="🦦">fishing | otter | playful</annotation>
+		<annotation cp="🦦" type="tts">otter</annotation>
+		<annotation cp="🦨">skunk | stink</annotation>
+		<annotation cp="🦨" type="tts">skunk</annotation>
+		<annotation cp="🦘">Australia | joey | jump | kangaroo | marsupial</annotation>
+		<annotation cp="🦘" type="tts">kangaroo</annotation>
+		<annotation cp="🦡">badger | honey badger | pester</annotation>
+		<annotation cp="🦡" type="tts">badger</annotation>
+		<annotation cp="🐾">feet | paw | paw prints | print</annotation>
+		<annotation cp="🐾" type="tts">paw prints</annotation>
+		<annotation cp="🦃">bird | turkey</annotation>
+		<annotation cp="🦃" type="tts">turkey</annotation>
+		<annotation cp="🐔">bird | chicken</annotation>
+		<annotation cp="🐔" type="tts">chicken</annotation>
+		<annotation cp="🐓">bird | rooster</annotation>
+		<annotation cp="🐓" type="tts">rooster</annotation>
+		<annotation cp="🐣">baby | bird | chick | hatching</annotation>
+		<annotation cp="🐣" type="tts">hatching chick</annotation>
+		<annotation cp="🐤">baby | bird | chick</annotation>
+		<annotation cp="🐤" type="tts">baby chick</annotation>
+		<annotation cp="🐥">baby | bird | chick | front-facing baby chick</annotation>
+		<annotation cp="🐥" type="tts">front-facing baby chick</annotation>
+		<annotation cp="🐦">bird</annotation>
+		<annotation cp="🐦" type="tts">bird</annotation>
+		<annotation cp="🐧">bird | penguin</annotation>
+		<annotation cp="🐧" type="tts">penguin</annotation>
+		<annotation cp="🕊">bird | dove | fly | peace</annotation>
+		<annotation cp="🕊" type="tts">dove</annotation>
+		<annotation cp="🦅">bird | eagle</annotation>
+		<annotation cp="🦅" type="tts">eagle</annotation>
+		<annotation cp="🦆">bird | duck</annotation>
+		<annotation cp="🦆" type="tts">duck</annotation>
+		<annotation cp="🦢">bird | cygnet | swan | ugly duckling</annotation>
+		<annotation cp="🦢" type="tts">swan</annotation>
+		<annotation cp="🦉">bird | owl | wise</annotation>
+		<annotation cp="🦉" type="tts">owl</annotation>
+		<annotation cp="🦤">dodo | extinction | large | Mauritius</annotation>
+		<annotation cp="🦤" type="tts">dodo</annotation>
+		<annotation cp="🪶">bird | feather | flight | light | plumage</annotation>
+		<annotation cp="🪶" type="tts">feather</annotation>
+		<annotation cp="🦩">flamboyant | flamingo | tropical</annotation>
+		<annotation cp="🦩" type="tts">flamingo</annotation>
+		<annotation cp="🦚">bird | ostentatious | peacock | peahen | proud</annotation>
+		<annotation cp="🦚" type="tts">peacock</annotation>
+		<annotation cp="🦜">bird | parrot | pirate | talk</annotation>
+		<annotation cp="🦜" type="tts">parrot</annotation>
+		<annotation cp="🐸">face | frog</annotation>
+		<annotation cp="🐸" type="tts">frog</annotation>
+		<annotation cp="🐊">crocodile</annotation>
+		<annotation cp="🐊" type="tts">crocodile</annotation>
+		<annotation cp="🐢">terrapin | tortoise | turtle</annotation>
+		<annotation cp="🐢" type="tts">turtle</annotation>
+		<annotation cp="🦎">lizard | reptile</annotation>
+		<annotation cp="🦎" type="tts">lizard</annotation>
+		<annotation cp="🐍">bearer | Ophiuchus | serpent | snake | zodiac</annotation>
+		<annotation cp="🐍" type="tts">snake</annotation>
+		<annotation cp="🐲">dragon | face | fairy tale</annotation>
+		<annotation cp="🐲" type="tts">dragon face</annotation>
+		<annotation cp="🐉">dragon | fairy tale</annotation>
+		<annotation cp="🐉" type="tts">dragon</annotation>
+		<annotation cp="🦕">brachiosaurus | brontosaurus | diplodocus | sauropod</annotation>
+		<annotation cp="🦕" type="tts">sauropod</annotation>
+		<annotation cp="🦖">T-Rex | Tyrannosaurus Rex</annotation>
+		<annotation cp="🦖" type="tts">T-Rex</annotation>
+		<annotation cp="🐳">face | spouting | whale</annotation>
+		<annotation cp="🐳" type="tts">spouting whale</annotation>
+		<annotation cp="🐋">whale</annotation>
+		<annotation cp="🐋" type="tts">whale</annotation>
+		<annotation cp="🐬">dolphin | flipper</annotation>
+		<annotation cp="🐬" type="tts">dolphin</annotation>
+		<annotation cp="🦭">sea Lion | seal</annotation>
+		<annotation cp="🦭" type="tts">seal</annotation>
+		<annotation cp="🐟">fish | Pisces | zodiac</annotation>
+		<annotation cp="🐟" type="tts">fish</annotation>
+		<annotation cp="🐠">fish | tropical</annotation>
+		<annotation cp="🐠" type="tts">tropical fish</annotation>
+		<annotation cp="🐡">blowfish | fish</annotation>
+		<annotation cp="🐡" type="tts">blowfish</annotation>
+		<annotation cp="🦈">fish | shark</annotation>
+		<annotation cp="🦈" type="tts">shark</annotation>
+		<annotation cp="🐙">octopus</annotation>
+		<annotation cp="🐙" type="tts">octopus</annotation>
+		<annotation cp="🐚">shell | spiral</annotation>
+		<annotation cp="🐚" type="tts">spiral shell</annotation>
+		<annotation cp="🐌">snail</annotation>
+		<annotation cp="🐌" type="tts">snail</annotation>
+		<annotation cp="🦋">butterfly | insect | pretty</annotation>
+		<annotation cp="🦋" type="tts">butterfly</annotation>
+		<annotation cp="🐛">bug | insect</annotation>
+		<annotation cp="🐛" type="tts">bug</annotation>
+		<annotation cp="🐜">ant | insect</annotation>
+		<annotation cp="🐜" type="tts">ant</annotation>
+		<annotation cp="🐝">bee | honeybee | insect</annotation>
+		<annotation cp="🐝" type="tts">honeybee</annotation>
+		<annotation cp="🪲">beetle | bug | insect</annotation>
+		<annotation cp="🪲" type="tts">beetle</annotation>
+		<annotation cp="🐞">beetle | insect | lady beetle | ladybird | ladybug</annotation>
+		<annotation cp="🐞" type="tts">lady beetle</annotation>
+		<annotation cp="🦗">cricket | grasshopper</annotation>
+		<annotation cp="🦗" type="tts">cricket</annotation>
+		<annotation cp="🪳">cockroach | insect | pest | roach</annotation>
+		<annotation cp="🪳" type="tts">cockroach</annotation>
+		<annotation cp="🕷">insect | spider</annotation>
+		<annotation cp="🕷" type="tts">spider</annotation>
+		<annotation cp="🕸">spider | web</annotation>
+		<annotation cp="🕸" type="tts">spider web</annotation>
+		<annotation cp="🦂">scorpio | Scorpio | scorpion | zodiac</annotation>
+		<annotation cp="🦂" type="tts">scorpion</annotation>
+		<annotation cp="🦟">disease | fever | malaria | mosquito | pest | virus</annotation>
+		<annotation cp="🦟" type="tts">mosquito</annotation>
+		<annotation cp="🪰">disease | fly | maggot | pest | rotting</annotation>
+		<annotation cp="🪰" type="tts">fly</annotation>
+		<annotation cp="🪱">annelid | earthworm | parasite | worm</annotation>
+		<annotation cp="🪱" type="tts">worm</annotation>
+		<annotation cp="🦠">amoeba | bacteria | microbe | virus</annotation>
+		<annotation cp="🦠" type="tts">microbe</annotation>
+		<annotation cp="💐">bouquet | flower</annotation>
+		<annotation cp="💐" type="tts">bouquet</annotation>
+		<annotation cp="🌸">blossom | cherry | flower</annotation>
+		<annotation cp="🌸" type="tts">cherry blossom</annotation>
+		<annotation cp="💮">flower | white flower</annotation>
+		<annotation cp="💮" type="tts">white flower</annotation>
+		<annotation cp="🏵">plant | rosette</annotation>
+		<annotation cp="🏵" type="tts">rosette</annotation>
+		<annotation cp="🌹">flower | rose</annotation>
+		<annotation cp="🌹" type="tts">rose</annotation>
+		<annotation cp="🥀">flower | wilted</annotation>
+		<annotation cp="🥀" type="tts">wilted flower</annotation>
+		<annotation cp="🌺">flower | hibiscus</annotation>
+		<annotation cp="🌺" type="tts">hibiscus</annotation>
+		<annotation cp="🌻">flower | sun | sunflower</annotation>
+		<annotation cp="🌻" type="tts">sunflower</annotation>
+		<annotation cp="🌼">blossom | flower</annotation>
+		<annotation cp="🌼" type="tts">blossom</annotation>
+		<annotation cp="🌷">flower | tulip</annotation>
+		<annotation cp="🌷" type="tts">tulip</annotation>
+		<annotation cp="🌱">seedling | young</annotation>
+		<annotation cp="🌱" type="tts">seedling</annotation>
+		<annotation cp="🪴">boring | grow | house | nurturing | plant | potted plant | useless</annotation>
+		<annotation cp="🪴" type="tts">potted plant</annotation>
+		<annotation cp="🌲">evergreen tree | tree</annotation>
+		<annotation cp="🌲" type="tts">evergreen tree</annotation>
+		<annotation cp="🌳">deciduous | shedding | tree</annotation>
+		<annotation cp="🌳" type="tts">deciduous tree</annotation>
+		<annotation cp="🌴">palm | tree</annotation>
+		<annotation cp="🌴" type="tts">palm tree</annotation>
+		<annotation cp="🌵">cactus | plant</annotation>
+		<annotation cp="🌵" type="tts">cactus</annotation>
+		<annotation cp="🌾">ear | grain | rice | sheaf of rice</annotation>
+		<annotation cp="🌾" type="tts">sheaf of rice</annotation>
+		<annotation cp="🌿">herb | leaf</annotation>
+		<annotation cp="🌿" type="tts">herb</annotation>
+		<annotation cp="☘">plant | shamrock</annotation>
+		<annotation cp="☘" type="tts">shamrock</annotation>
+		<annotation cp="🍀">4 | clover | four | four-leaf clover | leaf</annotation>
+		<annotation cp="🍀" type="tts">four leaf clover</annotation>
+		<annotation cp="🍁">falling | leaf | maple</annotation>
+		<annotation cp="🍁" type="tts">maple leaf</annotation>
+		<annotation cp="🍂">fallen leaf | falling | leaf</annotation>
+		<annotation cp="🍂" type="tts">fallen leaf</annotation>
+		<annotation cp="🍃">blow | flutter | leaf | leaf fluttering in wind | wind</annotation>
+		<annotation cp="🍃" type="tts">leaf fluttering in wind</annotation>
+		<annotation cp="🍇">fruit | grape | grapes</annotation>
+		<annotation cp="🍇" type="tts">grapes</annotation>
+		<annotation cp="🍈">fruit | melon</annotation>
+		<annotation cp="🍈" type="tts">melon</annotation>
+		<annotation cp="🍉">fruit | watermelon</annotation>
+		<annotation cp="🍉" type="tts">watermelon</annotation>
+		<annotation cp="🍊">fruit | orange | tangerine</annotation>
+		<annotation cp="🍊" type="tts">tangerine</annotation>
+		<annotation cp="🍋">citrus | fruit | lemon</annotation>
+		<annotation cp="🍋" type="tts">lemon</annotation>
+		<annotation cp="🍌">banana | fruit</annotation>
+		<annotation cp="🍌" type="tts">banana</annotation>
+		<annotation cp="🍍">fruit | pineapple</annotation>
+		<annotation cp="🍍" type="tts">pineapple</annotation>
+		<annotation cp="🥭">fruit | mango | tropical</annotation>
+		<annotation cp="🥭" type="tts">mango</annotation>
+		<annotation cp="🍎">apple | fruit | red</annotation>
+		<annotation cp="🍎" type="tts">red apple</annotation>
+		<annotation cp="🍏">apple | fruit | green</annotation>
+		<annotation cp="🍏" type="tts">green apple</annotation>
+		<annotation cp="🍐">fruit | pear</annotation>
+		<annotation cp="🍐" type="tts">pear</annotation>
+		<annotation cp="🍑">fruit | peach</annotation>
+		<annotation cp="🍑" type="tts">peach</annotation>
+		<annotation cp="🍒">berries | cherries | cherry | fruit | red</annotation>
+		<annotation cp="🍒" type="tts">cherries</annotation>
+		<annotation cp="🍓">berry | fruit | strawberry</annotation>
+		<annotation cp="🍓" type="tts">strawberry</annotation>
+		<annotation cp="🫐">berry | bilberry | blue | blueberries | blueberry</annotation>
+		<annotation cp="🫐" type="tts">blueberries</annotation>
+		<annotation cp="🥝">food | fruit | kiwi</annotation>
+		<annotation cp="🥝" type="tts">kiwi fruit</annotation>
+		<annotation cp="🍅">fruit | tomato | vegetable</annotation>
+		<annotation cp="🍅" type="tts">tomato</annotation>
+		<annotation cp="🫒">food | olive</annotation>
+		<annotation cp="🫒" type="tts">olive</annotation>
+		<annotation cp="🥥">coconut | palm | piña colada</annotation>
+		<annotation cp="🥥" type="tts">coconut</annotation>
+		<annotation cp="🥑">avocado | food | fruit</annotation>
+		<annotation cp="🥑" type="tts">avocado</annotation>
+		<annotation cp="🍆">aubergine | eggplant | vegetable</annotation>
+		<annotation cp="🍆" type="tts">eggplant</annotation>
+		<annotation cp="🥔">food | potato | vegetable</annotation>
+		<annotation cp="🥔" type="tts">potato</annotation>
+		<annotation cp="🥕">carrot | food | vegetable</annotation>
+		<annotation cp="🥕" type="tts">carrot</annotation>
+		<annotation cp="🌽">corn | ear | ear of corn | maize | maze</annotation>
+		<annotation cp="🌽" type="tts">ear of corn</annotation>
+		<annotation cp="🌶">hot | pepper</annotation>
+		<annotation cp="🌶" type="tts">hot pepper</annotation>
+		<annotation cp="🫑">bell pepper | capsicum | pepper | vegetable</annotation>
+		<annotation cp="🫑" type="tts">bell pepper</annotation>
+		<annotation cp="🥒">cucumber | food | pickle | vegetable</annotation>
+		<annotation cp="🥒" type="tts">cucumber</annotation>
+		<annotation cp="🥬">bok choy | cabbage | kale | leafy green | lettuce</annotation>
+		<annotation cp="🥬" type="tts">leafy green</annotation>
+		<annotation cp="🥦">broccoli | wild cabbage</annotation>
+		<annotation cp="🥦" type="tts">broccoli</annotation>
+		<annotation cp="🧄">flavoring | garlic</annotation>
+		<annotation cp="🧄" type="tts">garlic</annotation>
+		<annotation cp="🧅">flavoring | onion</annotation>
+		<annotation cp="🧅" type="tts">onion</annotation>
+		<annotation cp="🍄">mushroom | toadstool</annotation>
+		<annotation cp="🍄" type="tts">mushroom</annotation>
+		<annotation cp="🥜">food | nut | peanut | peanuts | vegetable</annotation>
+		<annotation cp="🥜" type="tts">peanuts</annotation>
+		<annotation cp="🌰">chestnut | plant</annotation>
+		<annotation cp="🌰" type="tts">chestnut</annotation>
+		<annotation cp="🍞">bread | loaf</annotation>
+		<annotation cp="🍞" type="tts">bread</annotation>
+		<annotation cp="🥐">bread | breakfast | croissant | food | french | roll</annotation>
+		<annotation cp="🥐" type="tts">croissant</annotation>
+		<annotation cp="🥖">baguette | bread | food | french</annotation>
+		<annotation cp="🥖" type="tts">baguette bread</annotation>
+		<annotation cp="🫓">arepa | flatbread | lavash | naan | pita</annotation>
+		<annotation cp="🫓" type="tts">flatbread</annotation>
+		<annotation cp="🥨">pretzel | twisted</annotation>
+		<annotation cp="🥨" type="tts">pretzel</annotation>
+		<annotation cp="🥯">bagel | bakery | breakfast | schmear</annotation>
+		<annotation cp="🥯" type="tts">bagel</annotation>
+		<annotation cp="🥞">breakfast | crêpe | food | hotcake | pancake | pancakes</annotation>
+		<annotation cp="🥞" type="tts">pancakes</annotation>
+		<annotation cp="🧇">breakfast | indecisive | iron | waffle</annotation>
+		<annotation cp="🧇" type="tts">waffle</annotation>
+		<annotation cp="🧀">cheese | cheese wedge</annotation>
+		<annotation cp="🧀" type="tts">cheese wedge</annotation>
+		<annotation cp="🍖">bone | meat | meat on bone</annotation>
+		<annotation cp="🍖" type="tts">meat on bone</annotation>
+		<annotation cp="🍗">bone | chicken | drumstick | leg | poultry</annotation>
+		<annotation cp="🍗" type="tts">poultry leg</annotation>
+		<annotation cp="🥩">chop | cut of meat | lambchop | porkchop | steak</annotation>
+		<annotation cp="🥩" type="tts">cut of meat</annotation>
+		<annotation cp="🥓">bacon | breakfast | food | meat</annotation>
+		<annotation cp="🥓" type="tts">bacon</annotation>
+		<annotation cp="🍔">burger | hamburger</annotation>
+		<annotation cp="🍔" type="tts">hamburger</annotation>
+		<annotation cp="🍟">french | fries</annotation>
+		<annotation cp="🍟" type="tts">french fries</annotation>
+		<annotation cp="🍕">cheese | pizza | slice</annotation>
+		<annotation cp="🍕" type="tts">pizza</annotation>
+		<annotation cp="🌭">frankfurter | hot dog | hotdog | sausage</annotation>
+		<annotation cp="🌭" type="tts">hot dog</annotation>
+		<annotation cp="🥪">bread | sandwich</annotation>
+		<annotation cp="🥪" type="tts">sandwich</annotation>
+		<annotation cp="🌮">mexican | taco</annotation>
+		<annotation cp="🌮" type="tts">taco</annotation>
+		<annotation cp="🌯">burrito | mexican | wrap</annotation>
+		<annotation cp="🌯" type="tts">burrito</annotation>
+		<annotation cp="🫔">mexican | tamale | wrapped</annotation>
+		<annotation cp="🫔" type="tts">tamale</annotation>
+		<annotation cp="🥙">falafel | flatbread | food | gyro | kebab | stuffed</annotation>
+		<annotation cp="🥙" type="tts">stuffed flatbread</annotation>
+		<annotation cp="🧆">chickpea | falafel | meatball</annotation>
+		<annotation cp="🧆" type="tts">falafel</annotation>
+		<annotation cp="🥚">breakfast | egg | food</annotation>
+		<annotation cp="🥚" type="tts">egg</annotation>
+		<annotation cp="🍳">breakfast | cooking | egg | frying | pan</annotation>
+		<annotation cp="🍳" type="tts">cooking</annotation>
+		<annotation cp="🥘">casserole | food | paella | pan | shallow | shallow pan of food</annotation>
+		<annotation cp="🥘" type="tts">shallow pan of food</annotation>
+		<annotation cp="🍲">pot | pot of food | stew</annotation>
+		<annotation cp="🍲" type="tts">pot of food</annotation>
+		<annotation cp="🫕">cheese | chocolate | fondue | melted | pot | Swiss</annotation>
+		<annotation cp="🫕" type="tts">fondue</annotation>
+		<annotation cp="🥣">bowl with spoon | breakfast | cereal | congee</annotation>
+		<annotation cp="🥣" type="tts">bowl with spoon</annotation>
+		<annotation cp="🥗">food | green | salad</annotation>
+		<annotation cp="🥗" type="tts">green salad</annotation>
+		<annotation cp="🍿">popcorn</annotation>
+		<annotation cp="🍿" type="tts">popcorn</annotation>
+		<annotation cp="🧈">butter | dairy</annotation>
+		<annotation cp="🧈" type="tts">butter</annotation>
+		<annotation cp="🧂">condiment | salt | shaker</annotation>
+		<annotation cp="🧂" type="tts">salt</annotation>
+		<annotation cp="🥫">can | canned food</annotation>
+		<annotation cp="🥫" type="tts">canned food</annotation>
+		<annotation cp="🍱">bento | box</annotation>
+		<annotation cp="🍱" type="tts">bento box</annotation>
+		<annotation cp="🍘">cracker | rice</annotation>
+		<annotation cp="🍘" type="tts">rice cracker</annotation>
+		<annotation cp="🍙">ball | Japanese | rice</annotation>
+		<annotation cp="🍙" type="tts">rice ball</annotation>
+		<annotation cp="🍚">cooked | rice</annotation>
+		<annotation cp="🍚" type="tts">cooked rice</annotation>
+		<annotation cp="🍛">curry | rice</annotation>
+		<annotation cp="🍛" type="tts">curry rice</annotation>
+		<annotation cp="🍜">bowl | noodle | ramen | steaming</annotation>
+		<annotation cp="🍜" type="tts">steaming bowl</annotation>
+		<annotation cp="🍝">pasta | spaghetti</annotation>
+		<annotation cp="🍝" type="tts">spaghetti</annotation>
+		<annotation cp="🍠">potato | roasted | sweet</annotation>
+		<annotation cp="🍠" type="tts">roasted sweet potato</annotation>
+		<annotation cp="🍢">kebab | oden | seafood | skewer | stick</annotation>
+		<annotation cp="🍢" type="tts">oden</annotation>
+		<annotation cp="🍣">sushi</annotation>
+		<annotation cp="🍣" type="tts">sushi</annotation>
+		<annotation cp="🍤">fried | prawn | shrimp | tempura</annotation>
+		<annotation cp="🍤" type="tts">fried shrimp</annotation>
+		<annotation cp="🍥">cake | fish | fish cake with swirl | pastry | swirl</annotation>
+		<annotation cp="🍥" type="tts">fish cake with swirl</annotation>
+		<annotation cp="🥮">autumn | festival | moon cake | yuèbǐng</annotation>
+		<annotation cp="🥮" type="tts">moon cake</annotation>
+		<annotation cp="🍡">dango | dessert | Japanese | skewer | stick | sweet</annotation>
+		<annotation cp="🍡" type="tts">dango</annotation>
+		<annotation cp="🥟">dumpling | empanada | gyōza | jiaozi | pierogi | potsticker</annotation>
+		<annotation cp="🥟" type="tts">dumpling</annotation>
+		<annotation cp="🥠">fortune cookie | prophecy</annotation>
+		<annotation cp="🥠" type="tts">fortune cookie</annotation>
+		<annotation cp="🥡">oyster pail | takeout box</annotation>
+		<annotation cp="🥡" type="tts">takeout box</annotation>
+		<annotation cp="🦀">Cancer | crab | zodiac</annotation>
+		<annotation cp="🦀" type="tts">crab</annotation>
+		<annotation cp="🦞">bisque | claws | lobster | seafood</annotation>
+		<annotation cp="🦞" type="tts">lobster</annotation>
+		<annotation cp="🦐">food | shellfish | shrimp | small</annotation>
+		<annotation cp="🦐" type="tts">shrimp</annotation>
+		<annotation cp="🦑">food | molusc | squid</annotation>
+		<annotation cp="🦑" type="tts">squid</annotation>
+		<annotation cp="🦪">diving | oyster | pearl</annotation>
+		<annotation cp="🦪" type="tts">oyster</annotation>
+		<annotation cp="🍦">cream | dessert | ice | icecream | soft | sweet</annotation>
+		<annotation cp="🍦" type="tts">soft ice cream</annotation>
+		<annotation cp="🍧">dessert | ice | shaved | sweet</annotation>
+		<annotation cp="🍧" type="tts">shaved ice</annotation>
+		<annotation cp="🍨">cream | dessert | ice | sweet</annotation>
+		<annotation cp="🍨" type="tts">ice cream</annotation>
+		<annotation cp="🍩">breakfast | dessert | donut | doughnut | sweet</annotation>
+		<annotation cp="🍩" type="tts">doughnut</annotation>
+		<annotation cp="🍪">cookie | dessert | sweet</annotation>
+		<annotation cp="🍪" type="tts">cookie</annotation>
+		<annotation cp="🎂">birthday | cake | celebration | dessert | pastry | sweet</annotation>
+		<annotation cp="🎂" type="tts">birthday cake</annotation>
+		<annotation cp="🍰">cake | dessert | pastry | shortcake | slice | sweet</annotation>
+		<annotation cp="🍰" type="tts">shortcake</annotation>
+		<annotation cp="🧁">bakery | cupcake | sweet</annotation>
+		<annotation cp="🧁" type="tts">cupcake</annotation>
+		<annotation cp="🥧">filling | pastry | pie</annotation>
+		<annotation cp="🥧" type="tts">pie</annotation>
+		<annotation cp="🍫">bar | chocolate | dessert | sweet</annotation>
+		<annotation cp="🍫" type="tts">chocolate bar</annotation>
+		<annotation cp="🍬">candy | dessert | sweet</annotation>
+		<annotation cp="🍬" type="tts">candy</annotation>
+		<annotation cp="🍭">candy | dessert | lollipop | sweet</annotation>
+		<annotation cp="🍭" type="tts">lollipop</annotation>
+		<annotation cp="🍮">custard | dessert | pudding | sweet</annotation>
+		<annotation cp="🍮" type="tts">custard</annotation>
+		<annotation cp="🍯">honey | honeypot | pot | sweet</annotation>
+		<annotation cp="🍯" type="tts">honey pot</annotation>
+		<annotation cp="🍼">baby | bottle | drink | milk</annotation>
+		<annotation cp="🍼" type="tts">baby bottle</annotation>
+		<annotation cp="🥛">drink | glass | glass of milk | milk</annotation>
+		<annotation cp="🥛" type="tts">glass of milk</annotation>
+		<annotation cp="☕">beverage | coffee | drink | hot | steaming | tea</annotation>
+		<annotation cp="☕" type="tts">hot beverage</annotation>
+		<annotation cp="🫖">drink | pot | tea | teapot</annotation>
+		<annotation cp="🫖" type="tts">teapot</annotation>
+		<annotation cp="🍵">beverage | cup | drink | tea | teacup | teacup without handle</annotation>
+		<annotation cp="🍵" type="tts">teacup without handle</annotation>
+		<annotation cp="🍶">bar | beverage | bottle | cup | drink | sake</annotation>
+		<annotation cp="🍶" type="tts">sake</annotation>
+		<annotation cp="🍾">bar | bottle | bottle with popping cork | cork | drink | popping</annotation>
+		<annotation cp="🍾" type="tts">bottle with popping cork</annotation>
+		<annotation cp="🍷">bar | beverage | drink | glass | wine</annotation>
+		<annotation cp="🍷" type="tts">wine glass</annotation>
+		<annotation cp="🍸">bar | cocktail | drink | glass</annotation>
+		<annotation cp="🍸" type="tts">cocktail glass</annotation>
+		<annotation cp="🍹">bar | drink | tropical</annotation>
+		<annotation cp="🍹" type="tts">tropical drink</annotation>
+		<annotation cp="🍺">bar | beer | drink | mug</annotation>
+		<annotation cp="🍺" type="tts">beer mug</annotation>
+		<annotation cp="🍻">bar | beer | clink | clinking beer mugs | drink | mug</annotation>
+		<annotation cp="🍻" type="tts">clinking beer mugs</annotation>
+		<annotation cp="🥂">celebrate | clink | clinking glasses | drink | glass</annotation>
+		<annotation cp="🥂" type="tts">clinking glasses</annotation>
+		<annotation cp="🥃">glass | liquor | shot | tumbler | whisky</annotation>
+		<annotation cp="🥃" type="tts">tumbler glass</annotation>
+		<annotation cp="🥤">cup with straw | juice | soda</annotation>
+		<annotation cp="🥤" type="tts">cup with straw</annotation>
+		<annotation cp="🧋">bubble | milk | pearl | tea</annotation>
+		<annotation cp="🧋" type="tts">bubble tea</annotation>
+		<annotation cp="🧃">beverage | box | juice | straw | sweet</annotation>
+		<annotation cp="🧃" type="tts">beverage box</annotation>
+		<annotation cp="🧉">drink | mate</annotation>
+		<annotation cp="🧉" type="tts">mate</annotation>
+		<annotation cp="🧊">cold | ice | ice cube | iceberg</annotation>
+		<annotation cp="🧊" type="tts">ice</annotation>
+		<annotation cp="🥢">chopsticks | hashi</annotation>
+		<annotation cp="🥢" type="tts">chopsticks</annotation>
+		<annotation cp="🍽">cooking | fork | fork and knife with plate | knife | plate</annotation>
+		<annotation cp="🍽" type="tts">fork and knife with plate</annotation>
+		<annotation cp="🍴">cooking | cutlery | fork | fork and knife | knife</annotation>
+		<annotation cp="🍴" type="tts">fork and knife</annotation>
+		<annotation cp="🥄">spoon | tableware</annotation>
+		<annotation cp="🥄" type="tts">spoon</annotation>
+		<annotation cp="🔪">cooking | hocho | kitchen knife | knife | tool | weapon</annotation>
+		<annotation cp="🔪" type="tts">kitchen knife</annotation>
+		<annotation cp="🏺">amphora | Aquarius | cooking | drink | jug | zodiac</annotation>
+		<annotation cp="🏺" type="tts">amphora</annotation>
+		<annotation cp="🌍">Africa | earth | Europe | globe | globe showing Europe-Africa | world</annotation>
+		<annotation cp="🌍" type="tts">globe showing Europe-Africa</annotation>
+		<annotation cp="🌎">Americas | earth | globe | globe showing Americas | world</annotation>
+		<annotation cp="🌎" type="tts">globe showing Americas</annotation>
+		<annotation cp="🌏">Asia | Australia | earth | globe | globe showing Asia-Australia | world</annotation>
+		<annotation cp="🌏" type="tts">globe showing Asia-Australia</annotation>
+		<annotation cp="🌐">earth | globe | globe with meridians | meridians | world</annotation>
+		<annotation cp="🌐" type="tts">globe with meridians</annotation>
+		<annotation cp="🗺">map | world</annotation>
+		<annotation cp="🗺" type="tts">world map</annotation>
+		<annotation cp="🗾">Japan | map | map of Japan</annotation>
+		<annotation cp="🗾" type="tts">map of Japan</annotation>
+		<annotation cp="🧭">compass | magnetic | navigation | orienteering</annotation>
+		<annotation cp="🧭" type="tts">compass</annotation>
+		<annotation cp="🏔">cold | mountain | snow | snow-capped mountain</annotation>
+		<annotation cp="🏔" type="tts">snow-capped mountain</annotation>
+		<annotation cp="⛰">mountain</annotation>
+		<annotation cp="⛰" type="tts">mountain</annotation>
+		<annotation cp="🌋">eruption | mountain | volcano</annotation>
+		<annotation cp="🌋" type="tts">volcano</annotation>
+		<annotation cp="🗻">fuji | mount fuji | mountain</annotation>
+		<annotation cp="🗻" type="tts">mount fuji</annotation>
+		<annotation cp="🏕">camping</annotation>
+		<annotation cp="🏕" type="tts">camping</annotation>
+		<annotation cp="🏖">beach | beach with umbrella | umbrella</annotation>
+		<annotation cp="🏖" type="tts">beach with umbrella</annotation>
+		<annotation cp="🏜">desert</annotation>
+		<annotation cp="🏜" type="tts">desert</annotation>
+		<annotation cp="🏝">desert | island</annotation>
+		<annotation cp="🏝" type="tts">desert island</annotation>
+		<annotation cp="🏞">national park | park</annotation>
+		<annotation cp="🏞" type="tts">national park</annotation>
+		<annotation cp="🏟">stadium</annotation>
+		<annotation cp="🏟" type="tts">stadium</annotation>
+		<annotation cp="🏛">classical | classical building</annotation>
+		<annotation cp="🏛" type="tts">classical building</annotation>
+		<annotation cp="🏗">building construction | construction</annotation>
+		<annotation cp="🏗" type="tts">building construction</annotation>
+		<annotation cp="🧱">brick | bricks | clay | mortar | wall</annotation>
+		<annotation cp="🧱" type="tts">brick</annotation>
+		<annotation cp="🪨">boulder | heavy | rock | solid | stone</annotation>
+		<annotation cp="🪨" type="tts">rock</annotation>
+		<annotation cp="🪵">log | lumber | timber | wood</annotation>
+		<annotation cp="🪵" type="tts">wood</annotation>
+		<annotation cp="🛖">house | hut | roundhouse | yurt</annotation>
+		<annotation cp="🛖" type="tts">hut</annotation>
+		<annotation cp="🏘">houses</annotation>
+		<annotation cp="🏘" type="tts">houses</annotation>
+		<annotation cp="🏚">derelict | house</annotation>
+		<annotation cp="🏚" type="tts">derelict house</annotation>
+		<annotation cp="🏠">home | house</annotation>
+		<annotation cp="🏠" type="tts">house</annotation>
+		<annotation cp="🏡">garden | home | house | house with garden</annotation>
+		<annotation cp="🏡" type="tts">house with garden</annotation>
+		<annotation cp="🏢">building | office building</annotation>
+		<annotation cp="🏢" type="tts">office building</annotation>
+		<annotation cp="🏣">Japanese | Japanese post office | post</annotation>
+		<annotation cp="🏣" type="tts">Japanese post office</annotation>
+		<annotation cp="🏤">European | post | post office</annotation>
+		<annotation cp="🏤" type="tts">post office</annotation>
+		<annotation cp="🏥">doctor | hospital | medicine</annotation>
+		<annotation cp="🏥" type="tts">hospital</annotation>
+		<annotation cp="🏦">bank | building</annotation>
+		<annotation cp="🏦" type="tts">bank</annotation>
+		<annotation cp="🏨">building | hotel</annotation>
+		<annotation cp="🏨" type="tts">hotel</annotation>
+		<annotation cp="🏩">hotel | love</annotation>
+		<annotation cp="🏩" type="tts">love hotel</annotation>
+		<annotation cp="🏪">convenience | store</annotation>
+		<annotation cp="🏪" type="tts">convenience store</annotation>
+		<annotation cp="🏫">building | school</annotation>
+		<annotation cp="🏫" type="tts">school</annotation>
+		<annotation cp="🏬">department | store</annotation>
+		<annotation cp="🏬" type="tts">department store</annotation>
+		<annotation cp="🏭">building | factory</annotation>
+		<annotation cp="🏭" type="tts">factory</annotation>
+		<annotation cp="🏯">castle | Japanese</annotation>
+		<annotation cp="🏯" type="tts">Japanese castle</annotation>
+		<annotation cp="🏰">castle | European</annotation>
+		<annotation cp="🏰" type="tts">castle</annotation>
+		<annotation cp="💒">chapel | romance | wedding</annotation>
+		<annotation cp="💒" type="tts">wedding</annotation>
+		<annotation cp="🗼">Tokyo | tower</annotation>
+		<annotation cp="🗼" type="tts">Tokyo tower</annotation>
+		<annotation cp="🗽">liberty | statue | Statue of Liberty</annotation>
+		<annotation cp="🗽" type="tts">Statue of Liberty</annotation>
+		<annotation cp="⛪">Christian | church | cross | religion</annotation>
+		<annotation cp="⛪" type="tts">church</annotation>
+		<annotation cp="🕌">islam | mosque | Muslim | religion</annotation>
+		<annotation cp="🕌" type="tts">mosque</annotation>
+		<annotation cp="🛕">hindu | temple</annotation>
+		<annotation cp="🛕" type="tts">hindu temple</annotation>
+		<annotation cp="🕍">Jew | Jewish | religion | synagogue | temple</annotation>
+		<annotation cp="🕍" type="tts">synagogue</annotation>
+		<annotation cp="⛩">religion | shinto | shrine</annotation>
+		<annotation cp="⛩" type="tts">shinto shrine</annotation>
+		<annotation cp="🕋">islam | kaaba | Muslim | religion</annotation>
+		<annotation cp="🕋" type="tts">kaaba</annotation>
+		<annotation cp="⛲">fountain</annotation>
+		<annotation cp="⛲" type="tts">fountain</annotation>
+		<annotation cp="⛺">camping | tent</annotation>
+		<annotation cp="⛺" type="tts">tent</annotation>
+		<annotation cp="🌁">fog | foggy</annotation>
+		<annotation cp="🌁" type="tts">foggy</annotation>
+		<annotation cp="🌃">night | night with stars | star</annotation>
+		<annotation cp="🌃" type="tts">night with stars</annotation>
+		<annotation cp="🏙">city | cityscape</annotation>
+		<annotation cp="🏙" type="tts">cityscape</annotation>
+		<annotation cp="🌄">morning | mountain | sun | sunrise | sunrise over mountains</annotation>
+		<annotation cp="🌄" type="tts">sunrise over mountains</annotation>
+		<annotation cp="🌅">morning | sun | sunrise</annotation>
+		<annotation cp="🌅" type="tts">sunrise</annotation>
+		<annotation cp="🌆">city | cityscape at dusk | dusk | evening | landscape | sunset</annotation>
+		<annotation cp="🌆" type="tts">cityscape at dusk</annotation>
+		<annotation cp="🌇">dusk | sun | sunset</annotation>
+		<annotation cp="🌇" type="tts">sunset</annotation>
+		<annotation cp="🌉">bridge | bridge at night | night</annotation>
+		<annotation cp="🌉" type="tts">bridge at night</annotation>
+		<annotation cp="♨">hot | hotsprings | springs | steaming</annotation>
+		<annotation cp="♨" type="tts">hot springs</annotation>
+		<annotation cp="🎠">carousel | horse</annotation>
+		<annotation cp="🎠" type="tts">carousel horse</annotation>
+		<annotation cp="🎡">amusement park | ferris | wheel</annotation>
+		<annotation cp="🎡" type="tts">ferris wheel</annotation>
+		<annotation cp="🎢">amusement park | coaster | roller</annotation>
+		<annotation cp="🎢" type="tts">roller coaster</annotation>
+		<annotation cp="💈">barber | haircut | pole</annotation>
+		<annotation cp="💈" type="tts">barber pole</annotation>
+		<annotation cp="🎪">circus | tent</annotation>
+		<annotation cp="🎪" type="tts">circus tent</annotation>
+		<annotation cp="🚂">engine | locomotive | railway | steam | train</annotation>
+		<annotation cp="🚂" type="tts">locomotive</annotation>
+		<annotation cp="🚃">car | electric | railway | train | tram | trolleybus</annotation>
+		<annotation cp="🚃" type="tts">railway car</annotation>
+		<annotation cp="🚄">high-speed train | railway | shinkansen | speed | train</annotation>
+		<annotation cp="🚄" type="tts">high-speed train</annotation>
+		<annotation cp="🚅">bullet | railway | shinkansen | speed | train</annotation>
+		<annotation cp="🚅" type="tts">bullet train</annotation>
+		<annotation cp="🚆">railway | train</annotation>
+		<annotation cp="🚆" type="tts">train</annotation>
+		<annotation cp="🚇">metro | subway</annotation>
+		<annotation cp="🚇" type="tts">metro</annotation>
+		<annotation cp="🚈">light rail | railway</annotation>
+		<annotation cp="🚈" type="tts">light rail</annotation>
+		<annotation cp="🚉">railway | station | train</annotation>
+		<annotation cp="🚉" type="tts">station</annotation>
+		<annotation cp="🚊">tram | trolleybus</annotation>
+		<annotation cp="🚊" type="tts">tram</annotation>
+		<annotation cp="🚝">monorail | vehicle</annotation>
+		<annotation cp="🚝" type="tts">monorail</annotation>
+		<annotation cp="🚞">car | mountain | railway</annotation>
+		<annotation cp="🚞" type="tts">mountain railway</annotation>
+		<annotation cp="🚋">car | tram | trolleybus</annotation>
+		<annotation cp="🚋" type="tts">tram car</annotation>
+		<annotation cp="🚌">bus | vehicle</annotation>
+		<annotation cp="🚌" type="tts">bus</annotation>
+		<annotation cp="🚍">bus | oncoming</annotation>
+		<annotation cp="🚍" type="tts">oncoming bus</annotation>
+		<annotation cp="🚎">bus | tram | trolley | trolleybus</annotation>
+		<annotation cp="🚎" type="tts">trolleybus</annotation>
+		<annotation cp="🚐">bus | minibus</annotation>
+		<annotation cp="🚐" type="tts">minibus</annotation>
+		<annotation cp="🚑">ambulance | vehicle</annotation>
+		<annotation cp="🚑" type="tts">ambulance</annotation>
+		<annotation cp="🚒">engine | fire | truck</annotation>
+		<annotation cp="🚒" type="tts">fire engine</annotation>
+		<annotation cp="🚓">car | patrol | police</annotation>
+		<annotation cp="🚓" type="tts">police car</annotation>
+		<annotation cp="🚔">car | oncoming | police</annotation>
+		<annotation cp="🚔" type="tts">oncoming police car</annotation>
+		<annotation cp="🚕">taxi | vehicle</annotation>
+		<annotation cp="🚕" type="tts">taxi</annotation>
+		<annotation cp="🚖">oncoming | taxi</annotation>
+		<annotation cp="🚖" type="tts">oncoming taxi</annotation>
+		<annotation cp="🚗">automobile | car</annotation>
+		<annotation cp="🚗" type="tts">automobile</annotation>
+		<annotation cp="🚘">automobile | car | oncoming</annotation>
+		<annotation cp="🚘" type="tts">oncoming automobile</annotation>
+		<annotation cp="🚙">recreational | sport utility | sport utility vehicle</annotation>
+		<annotation cp="🚙" type="tts">sport utility vehicle</annotation>
+		<annotation cp="🛻">pick-up | pickup | truck</annotation>
+		<annotation cp="🛻" type="tts">pickup truck</annotation>
+		<annotation cp="🚚">delivery | truck</annotation>
+		<annotation cp="🚚" type="tts">delivery truck</annotation>
+		<annotation cp="🚛">articulated lorry | lorry | semi | truck</annotation>
+		<annotation cp="🚛" type="tts">articulated lorry</annotation>
+		<annotation cp="🚜">tractor | vehicle</annotation>
+		<annotation cp="🚜" type="tts">tractor</annotation>
+		<annotation cp="🏎">car | racing</annotation>
+		<annotation cp="🏎" type="tts">racing car</annotation>
+		<annotation cp="🏍">motorcycle | racing</annotation>
+		<annotation cp="🏍" type="tts">motorcycle</annotation>
+		<annotation cp="🛵">motor | scooter</annotation>
+		<annotation cp="🛵" type="tts">motor scooter</annotation>
+		<annotation cp="🦽">accessibility | manual wheelchair</annotation>
+		<annotation cp="🦽" type="tts">manual wheelchair</annotation>
+		<annotation cp="🦼">accessibility | motorized wheelchair</annotation>
+		<annotation cp="🦼" type="tts">motorized wheelchair</annotation>
+		<annotation cp="🛺">auto rickshaw | tuk tuk</annotation>
+		<annotation cp="🛺" type="tts">auto rickshaw</annotation>
+		<annotation cp="🚲">bicycle | bike</annotation>
+		<annotation cp="🚲" type="tts">bicycle</annotation>
+		<annotation cp="🛴">kick | scooter</annotation>
+		<annotation cp="🛴" type="tts">kick scooter</annotation>
+		<annotation cp="🛹">board | skateboard</annotation>
+		<annotation cp="🛹" type="tts">skateboard</annotation>
+		<annotation cp="🛼">roller | skate</annotation>
+		<annotation cp="🛼" type="tts">roller skate</annotation>
+		<annotation cp="🚏">bus | busstop | stop</annotation>
+		<annotation cp="🚏" type="tts">bus stop</annotation>
+		<annotation cp="🛣">highway | motorway | road</annotation>
+		<annotation cp="🛣" type="tts">motorway</annotation>
+		<annotation cp="🛤">railway | railway track | train</annotation>
+		<annotation cp="🛤" type="tts">railway track</annotation>
+		<annotation cp="🛢">drum | oil</annotation>
+		<annotation cp="🛢" type="tts">oil drum</annotation>
+		<annotation cp="⛽">diesel | fuel | fuelpump | gas | pump | station</annotation>
+		<annotation cp="⛽" type="tts">fuel pump</annotation>
+		<annotation cp="🚨">beacon | car | light | police | revolving</annotation>
+		<annotation cp="🚨" type="tts">police car light</annotation>
+		<annotation cp="🚥">horizontal traffic light | light | signal | traffic</annotation>
+		<annotation cp="🚥" type="tts">horizontal traffic light</annotation>
+		<annotation cp="🚦">light | signal | traffic | vertical traffic light</annotation>
+		<annotation cp="🚦" type="tts">vertical traffic light</annotation>
+		<annotation cp="🛑">octagonal | sign | stop</annotation>
+		<annotation cp="🛑" type="tts">stop sign</annotation>
+		<annotation cp="🚧">barrier | construction</annotation>
+		<annotation cp="🚧" type="tts">construction</annotation>
+		<annotation cp="⚓">anchor | ship | tool</annotation>
+		<annotation cp="⚓" type="tts">anchor</annotation>
+		<annotation cp="⛵">boat | resort | sailboat | sea | yacht</annotation>
+		<annotation cp="⛵" type="tts">sailboat</annotation>
+		<annotation cp="🛶">boat | canoe</annotation>
+		<annotation cp="🛶" type="tts">canoe</annotation>
+		<annotation cp="🚤">boat | speedboat</annotation>
+		<annotation cp="🚤" type="tts">speedboat</annotation>
+		<annotation cp="🛳">passenger | ship</annotation>
+		<annotation cp="🛳" type="tts">passenger ship</annotation>
+		<annotation cp="⛴">boat | ferry | passenger</annotation>
+		<annotation cp="⛴" type="tts">ferry</annotation>
+		<annotation cp="🛥">boat | motor boat | motorboat</annotation>
+		<annotation cp="🛥" type="tts">motor boat</annotation>
+		<annotation cp="🚢">boat | passenger | ship</annotation>
+		<annotation cp="🚢" type="tts">ship</annotation>
+		<annotation cp="✈">aeroplane | airplane</annotation>
+		<annotation cp="✈" type="tts">airplane</annotation>
+		<annotation cp="🛩">aeroplane | airplane | small airplane</annotation>
+		<annotation cp="🛩" type="tts">small airplane</annotation>
+		<annotation cp="🛫">aeroplane | airplane | check-in | departure | departures</annotation>
+		<annotation cp="🛫" type="tts">airplane departure</annotation>
+		<annotation cp="🛬">aeroplane | airplane | airplane arrival | arrivals | arriving | landing</annotation>
+		<annotation cp="🛬" type="tts">airplane arrival</annotation>
+		<annotation cp="🪂">hang-glide | parachute | parasail | skydive</annotation>
+		<annotation cp="🪂" type="tts">parachute</annotation>
+		<annotation cp="💺">chair | seat</annotation>
+		<annotation cp="💺" type="tts">seat</annotation>
+		<annotation cp="🚁">helicopter | vehicle</annotation>
+		<annotation cp="🚁" type="tts">helicopter</annotation>
+		<annotation cp="🚟">railway | suspension</annotation>
+		<annotation cp="🚟" type="tts">suspension railway</annotation>
+		<annotation cp="🚠">cable | gondola | mountain | mountain cableway</annotation>
+		<annotation cp="🚠" type="tts">mountain cableway</annotation>
+		<annotation cp="🚡">aerial | cable | car | gondola | tramway</annotation>
+		<annotation cp="🚡" type="tts">aerial tramway</annotation>
+		<annotation cp="🛰">satellite | space</annotation>
+		<annotation cp="🛰" type="tts">satellite</annotation>
+		<annotation cp="🚀">rocket | space</annotation>
+		<annotation cp="🚀" type="tts">rocket</annotation>
+		<annotation cp="🛸">flying saucer | UFO</annotation>
+		<annotation cp="🛸" type="tts">flying saucer</annotation>
+		<annotation cp="🛎">bell | bellhop | hotel</annotation>
+		<annotation cp="🛎" type="tts">bellhop bell</annotation>
+		<annotation cp="🧳">luggage | packing | travel</annotation>
+		<annotation cp="🧳" type="tts">luggage</annotation>
+		<annotation cp="⌛">hourglass done | sand | timer</annotation>
+		<annotation cp="⌛" type="tts">hourglass done</annotation>
+		<annotation cp="⏳">hourglass | hourglass not done | sand | timer</annotation>
+		<annotation cp="⏳" type="tts">hourglass not done</annotation>
+		<annotation cp="⌚">clock | watch</annotation>
+		<annotation cp="⌚" type="tts">watch</annotation>
+		<annotation cp="⏰">alarm | clock</annotation>
+		<annotation cp="⏰" type="tts">alarm clock</annotation>
+		<annotation cp="⏱">clock | stopwatch</annotation>
+		<annotation cp="⏱" type="tts">stopwatch</annotation>
+		<annotation cp="⏲">clock | timer</annotation>
+		<annotation cp="⏲" type="tts">timer clock</annotation>
+		<annotation cp="🕰">clock | mantelpiece clock</annotation>
+		<annotation cp="🕰" type="tts">mantelpiece clock</annotation>
+		<annotation cp="🕛">00 | 12 | 12:00 | clock | o’clock | twelve</annotation>
+		<annotation cp="🕛" type="tts">twelve o’clock</annotation>
+		<annotation cp="🕧">12 | 12:30 | clock | thirty | twelve | twelve-thirty</annotation>
+		<annotation cp="🕧" type="tts">twelve-thirty</annotation>
+		<annotation cp="🕐">00 | 1 | 1:00 | clock | o’clock | one</annotation>
+		<annotation cp="🕐" type="tts">one o’clock</annotation>
+		<annotation cp="🕜">1 | 1:30 | clock | one | one-thirty | thirty</annotation>
+		<annotation cp="🕜" type="tts">one-thirty</annotation>
+		<annotation cp="🕑">00 | 2 | 2:00 | clock | o’clock | two</annotation>
+		<annotation cp="🕑" type="tts">two o’clock</annotation>
+		<annotation cp="🕝">2 | 2:30 | clock | thirty | two | two-thirty</annotation>
+		<annotation cp="🕝" type="tts">two-thirty</annotation>
+		<annotation cp="🕒">00 | 3 | 3:00 | clock | o’clock | three</annotation>
+		<annotation cp="🕒" type="tts">three o’clock</annotation>
+		<annotation cp="🕞">3 | 3:30 | clock | thirty | three | three-thirty</annotation>
+		<annotation cp="🕞" type="tts">three-thirty</annotation>
+		<annotation cp="🕓">00 | 4 | 4:00 | clock | four | o’clock</annotation>
+		<annotation cp="🕓" type="tts">four o’clock</annotation>
+		<annotation cp="🕟">4 | 4:30 | clock | four | four-thirty | thirty</annotation>
+		<annotation cp="🕟" type="tts">four-thirty</annotation>
+		<annotation cp="🕔">00 | 5 | 5:00 | clock | five | o’clock</annotation>
+		<annotation cp="🕔" type="tts">five o’clock</annotation>
+		<annotation cp="🕠">5 | 5:30 | clock | five | five-thirty | thirty</annotation>
+		<annotation cp="🕠" type="tts">five-thirty</annotation>
+		<annotation cp="🕕">00 | 6 | 6:00 | clock | o’clock | six</annotation>
+		<annotation cp="🕕" type="tts">six o’clock</annotation>
+		<annotation cp="🕡">6 | 6:30 | clock | six | six-thirty | thirty</annotation>
+		<annotation cp="🕡" type="tts">six-thirty</annotation>
+		<annotation cp="🕖">00 | 7 | 7:00 | clock | o’clock | seven</annotation>
+		<annotation cp="🕖" type="tts">seven o’clock</annotation>
+		<annotation cp="🕢">7 | 7:30 | clock | seven | seven-thirty | thirty</annotation>
+		<annotation cp="🕢" type="tts">seven-thirty</annotation>
+		<annotation cp="🕗">00 | 8 | 8:00 | clock | eight | o’clock</annotation>
+		<annotation cp="🕗" type="tts">eight o’clock</annotation>
+		<annotation cp="🕣">8 | 8:30 | clock | eight | eight-thirty | thirty</annotation>
+		<annotation cp="🕣" type="tts">eight-thirty</annotation>
+		<annotation cp="🕘">00 | 9 | 9:00 | clock | nine | o’clock</annotation>
+		<annotation cp="🕘" type="tts">nine o’clock</annotation>
+		<annotation cp="🕤">9 | 9:30 | clock | nine | nine-thirty | thirty</annotation>
+		<annotation cp="🕤" type="tts">nine-thirty</annotation>
+		<annotation cp="🕙">00 | 10 | 10:00 | clock | o’clock | ten</annotation>
+		<annotation cp="🕙" type="tts">ten o’clock</annotation>
+		<annotation cp="🕥">10 | 10:30 | clock | ten | ten-thirty | thirty</annotation>
+		<annotation cp="🕥" type="tts">ten-thirty</annotation>
+		<annotation cp="🕚">00 | 11 | 11:00 | clock | eleven | o’clock</annotation>
+		<annotation cp="🕚" type="tts">eleven o’clock</annotation>
+		<annotation cp="🕦">11 | 11:30 | clock | eleven | eleven-thirty | thirty</annotation>
+		<annotation cp="🕦" type="tts">eleven-thirty</annotation>
+		<annotation cp="🌑">dark | moon | new moon</annotation>
+		<annotation cp="🌑" type="tts">new moon</annotation>
+		<annotation cp="🌒">crescent | moon | waxing</annotation>
+		<annotation cp="🌒" type="tts">waxing crescent moon</annotation>
+		<annotation cp="🌓">first quarter moon | moon | quarter</annotation>
+		<annotation cp="🌓" type="tts">first quarter moon</annotation>
+		<annotation cp="🌔">gibbous | moon | waxing</annotation>
+		<annotation cp="🌔" type="tts">waxing gibbous moon</annotation>
+		<annotation cp="🌕">full | moon</annotation>
+		<annotation cp="🌕" type="tts">full moon</annotation>
+		<annotation cp="🌖">gibbous | moon | waning</annotation>
+		<annotation cp="🌖" type="tts">waning gibbous moon</annotation>
+		<annotation cp="🌗">last quarter moon | moon | quarter</annotation>
+		<annotation cp="🌗" type="tts">last quarter moon</annotation>
+		<annotation cp="🌘">crescent | moon | waning</annotation>
+		<annotation cp="🌘" type="tts">waning crescent moon</annotation>
+		<annotation cp="🌙">crescent | moon</annotation>
+		<annotation cp="🌙" type="tts">crescent moon</annotation>
+		<annotation cp="🌚">face | moon | new moon face</annotation>
+		<annotation cp="🌚" type="tts">new moon face</annotation>
+		<annotation cp="🌛">face | first quarter moon face | moon | quarter</annotation>
+		<annotation cp="🌛" type="tts">first quarter moon face</annotation>
+		<annotation cp="🌜">face | last quarter moon face | moon | quarter</annotation>
+		<annotation cp="🌜" type="tts">last quarter moon face</annotation>
+		<annotation cp="🌡">thermometer | weather</annotation>
+		<annotation cp="🌡" type="tts">thermometer</annotation>
+		<annotation cp="☀">bright | rays | sun | sunny</annotation>
+		<annotation cp="☀" type="tts">sun</annotation>
+		<annotation cp="🌝">bright | face | full | moon</annotation>
+		<annotation cp="🌝" type="tts">full moon face</annotation>
+		<annotation cp="🌞">bright | face | sun | sun with face</annotation>
+		<annotation cp="🌞" type="tts">sun with face</annotation>
+		<annotation cp="🪐">ringed planet | saturn | saturnine</annotation>
+		<annotation cp="🪐" type="tts">ringed planet</annotation>
+		<annotation cp="⭐">star</annotation>
+		<annotation cp="⭐" type="tts">star</annotation>
+		<annotation cp="🌟">glittery | glow | glowing star | shining | sparkle | star</annotation>
+		<annotation cp="🌟" type="tts">glowing star</annotation>
+		<annotation cp="🌠">falling | shooting | star</annotation>
+		<annotation cp="🌠" type="tts">shooting star</annotation>
+		<annotation cp="🌌">milky way | space</annotation>
+		<annotation cp="🌌" type="tts">milky way</annotation>
+		<annotation cp="☁">cloud | weather</annotation>
+		<annotation cp="☁" type="tts">cloud</annotation>
+		<annotation cp="⛅">cloud | sun | sun behind cloud</annotation>
+		<annotation cp="⛅" type="tts">sun behind cloud</annotation>
+		<annotation cp="⛈">cloud | cloud with lightning and rain | rain | thunder</annotation>
+		<annotation cp="⛈" type="tts">cloud with lightning and rain</annotation>
+		<annotation cp="🌤">cloud | sun | sun behind small cloud</annotation>
+		<annotation cp="🌤" type="tts">sun behind small cloud</annotation>
+		<annotation cp="🌥">cloud | sun | sun behind large cloud</annotation>
+		<annotation cp="🌥" type="tts">sun behind large cloud</annotation>
+		<annotation cp="🌦">cloud | rain | sun | sun behind rain cloud</annotation>
+		<annotation cp="🌦" type="tts">sun behind rain cloud</annotation>
+		<annotation cp="🌧">cloud | cloud with rain | rain</annotation>
+		<annotation cp="🌧" type="tts">cloud with rain</annotation>
+		<annotation cp="🌨">cloud | cloud with snow | cold | snow</annotation>
+		<annotation cp="🌨" type="tts">cloud with snow</annotation>
+		<annotation cp="🌩">cloud | cloud with lightning | lightning</annotation>
+		<annotation cp="🌩" type="tts">cloud with lightning</annotation>
+		<annotation cp="🌪">cloud | tornado | whirlwind</annotation>
+		<annotation cp="🌪" type="tts">tornado</annotation>
+		<annotation cp="🌫">cloud | fog</annotation>
+		<annotation cp="🌫" type="tts">fog</annotation>
+		<annotation cp="🌬">blow | cloud | face | wind</annotation>
+		<annotation cp="🌬" type="tts">wind face</annotation>
+		<annotation cp="🌀">cyclone | dizzy | hurricane | twister | typhoon</annotation>
+		<annotation cp="🌀" type="tts">cyclone</annotation>
+		<annotation cp="🌈">rain | rainbow</annotation>
+		<annotation cp="🌈" type="tts">rainbow</annotation>
+		<annotation cp="🌂">closed umbrella | clothing | rain | umbrella</annotation>
+		<annotation cp="🌂" type="tts">closed umbrella</annotation>
+		<annotation cp="☂">clothing | rain | umbrella</annotation>
+		<annotation cp="☂" type="tts">umbrella</annotation>
+		<annotation cp="☔">clothing | drop | rain | umbrella | umbrella with rain drops</annotation>
+		<annotation cp="☔" type="tts">umbrella with rain drops</annotation>
+		<annotation cp="⛱">rain | sun | umbrella | umbrella on ground</annotation>
+		<annotation cp="⛱" type="tts">umbrella on ground</annotation>
+		<annotation cp="⚡">danger | electric | high voltage | lightning | voltage | zap</annotation>
+		<annotation cp="⚡" type="tts">high voltage</annotation>
+		<annotation cp="❄">cold | snow | snowflake</annotation>
+		<annotation cp="❄" type="tts">snowflake</annotation>
+		<annotation cp="☃">cold | snow | snowman</annotation>
+		<annotation cp="☃" type="tts">snowman</annotation>
+		<annotation cp="⛄">cold | snow | snowman | snowman without snow</annotation>
+		<annotation cp="⛄" type="tts">snowman without snow</annotation>
+		<annotation cp="☄">comet | space</annotation>
+		<annotation cp="☄" type="tts">comet</annotation>
+		<annotation cp="🔥">fire | flame | tool</annotation>
+		<annotation cp="🔥" type="tts">fire</annotation>
+		<annotation cp="💧">cold | comic | drop | droplet | sweat</annotation>
+		<annotation cp="💧" type="tts">droplet</annotation>
+		<annotation cp="🌊">ocean | water | wave</annotation>
+		<annotation cp="🌊" type="tts">water wave</annotation>
+		<annotation cp="🎃">celebration | halloween | jack | jack-o-lantern | lantern</annotation>
+		<annotation cp="🎃" type="tts">jack-o-lantern</annotation>
+		<annotation cp="🎄">celebration | Christmas | tree</annotation>
+		<annotation cp="🎄" type="tts">Christmas tree</annotation>
+		<annotation cp="🎆">celebration | fireworks</annotation>
+		<annotation cp="🎆" type="tts">fireworks</annotation>
+		<annotation cp="🎇">celebration | fireworks | sparkle | sparkler</annotation>
+		<annotation cp="🎇" type="tts">sparkler</annotation>
+		<annotation cp="🧨">dynamite | explosive | firecracker | fireworks</annotation>
+		<annotation cp="🧨" type="tts">firecracker</annotation>
+		<annotation cp="✨">* | sparkle | sparkles | star</annotation>
+		<annotation cp="✨" type="tts">sparkles</annotation>
+		<annotation cp="🎈">balloon | celebration</annotation>
+		<annotation cp="🎈" type="tts">balloon</annotation>
+		<annotation cp="🎉">celebration | party | popper | tada</annotation>
+		<annotation cp="🎉" type="tts">party popper</annotation>
+		<annotation cp="🎊">ball | celebration | confetti</annotation>
+		<annotation cp="🎊" type="tts">confetti ball</annotation>
+		<annotation cp="🎋">banner | celebration | Japanese | tanabata tree | tree</annotation>
+		<annotation cp="🎋" type="tts">tanabata tree</annotation>
+		<annotation cp="🎍">bamboo | celebration | Japanese | pine | pine decoration</annotation>
+		<annotation cp="🎍" type="tts">pine decoration</annotation>
+		<annotation cp="🎎">celebration | doll | festival | Japanese | Japanese dolls</annotation>
+		<annotation cp="🎎" type="tts">Japanese dolls</annotation>
+		<annotation cp="🎏">carp | celebration | streamer</annotation>
+		<annotation cp="🎏" type="tts">carp streamer</annotation>
+		<annotation cp="🎐">bell | celebration | chime | wind</annotation>
+		<annotation cp="🎐" type="tts">wind chime</annotation>
+		<annotation cp="🎑">celebration | ceremony | moon | moon viewing ceremony</annotation>
+		<annotation cp="🎑" type="tts">moon viewing ceremony</annotation>
+		<annotation cp="🧧">gift | good luck | hóngbāo | lai see | money | red envelope</annotation>
+		<annotation cp="🧧" type="tts">red envelope</annotation>
+		<annotation cp="🎀">celebration | ribbon</annotation>
+		<annotation cp="🎀" type="tts">ribbon</annotation>
+		<annotation cp="🎁">box | celebration | gift | present | wrapped</annotation>
+		<annotation cp="🎁" type="tts">wrapped gift</annotation>
+		<annotation cp="🎗">celebration | reminder | ribbon</annotation>
+		<annotation cp="🎗" type="tts">reminder ribbon</annotation>
+		<annotation cp="🎟">admission | admission tickets | ticket</annotation>
+		<annotation cp="🎟" type="tts">admission tickets</annotation>
+		<annotation cp="🎫">admission | ticket</annotation>
+		<annotation cp="🎫" type="tts">ticket</annotation>
+		<annotation cp="🎖">celebration | medal | military</annotation>
+		<annotation cp="🎖" type="tts">military medal</annotation>
+		<annotation cp="🏆">prize | trophy</annotation>
+		<annotation cp="🏆" type="tts">trophy</annotation>
+		<annotation cp="🏅">medal | sports medal</annotation>
+		<annotation cp="🏅" type="tts">sports medal</annotation>
+		<annotation cp="🥇">1st place medal | first | gold | medal</annotation>
+		<annotation cp="🥇" type="tts">1st place medal</annotation>
+		<annotation cp="🥈">2nd place medal | medal | second | silver</annotation>
+		<annotation cp="🥈" type="tts">2nd place medal</annotation>
+		<annotation cp="🥉">3rd place medal | bronze | medal | third</annotation>
+		<annotation cp="🥉" type="tts">3rd place medal</annotation>
+		<annotation cp="⚽">ball | football | soccer</annotation>
+		<annotation cp="⚽" type="tts">soccer ball</annotation>
+		<annotation cp="⚾">ball | baseball</annotation>
+		<annotation cp="⚾" type="tts">baseball</annotation>
+		<annotation cp="🥎">ball | glove | softball | underarm</annotation>
+		<annotation cp="🥎" type="tts">softball</annotation>
+		<annotation cp="🏀">ball | basketball | hoop</annotation>
+		<annotation cp="🏀" type="tts">basketball</annotation>
+		<annotation cp="🏐">ball | game | volleyball</annotation>
+		<annotation cp="🏐" type="tts">volleyball</annotation>
+		<annotation cp="🏈">american | ball | football</annotation>
+		<annotation cp="🏈" type="tts">american football</annotation>
+		<annotation cp="🏉">ball | football | rugby</annotation>
+		<annotation cp="🏉" type="tts">rugby football</annotation>
+		<annotation cp="🎾">ball | racquet | tennis</annotation>
+		<annotation cp="🎾" type="tts">tennis</annotation>
+		<annotation cp="🥏">flying disc | ultimate</annotation>
+		<annotation cp="🥏" type="tts">flying disc</annotation>
+		<annotation cp="🎳">ball | bowling | game</annotation>
+		<annotation cp="🎳" type="tts">bowling</annotation>
+		<annotation cp="🏏">ball | bat | cricket game | game</annotation>
+		<annotation cp="🏏" type="tts">cricket game</annotation>
+		<annotation cp="🏑">ball | field | game | hockey | stick</annotation>
+		<annotation cp="🏑" type="tts">field hockey</annotation>
+		<annotation cp="🏒">game | hockey | ice | puck | stick</annotation>
+		<annotation cp="🏒" type="tts">ice hockey</annotation>
+		<annotation cp="🥍">ball | goal | lacrosse | stick</annotation>
+		<annotation cp="🥍" type="tts">lacrosse</annotation>
+		<annotation cp="🏓">ball | bat | game | paddle | ping pong | table tennis</annotation>
+		<annotation cp="🏓" type="tts">ping pong</annotation>
+		<annotation cp="🏸">badminton | birdie | game | racquet | shuttlecock</annotation>
+		<annotation cp="🏸" type="tts">badminton</annotation>
+		<annotation cp="🥊">boxing | glove</annotation>
+		<annotation cp="🥊" type="tts">boxing glove</annotation>
+		<annotation cp="🥋">judo | karate | martial arts | martial arts uniform | taekwondo | uniform</annotation>
+		<annotation cp="🥋" type="tts">martial arts uniform</annotation>
+		<annotation cp="🥅">goal | net</annotation>
+		<annotation cp="🥅" type="tts">goal net</annotation>
+		<annotation cp="⛳">flag in hole | golf | hole</annotation>
+		<annotation cp="⛳" type="tts">flag in hole</annotation>
+		<annotation cp="⛸">ice | skate</annotation>
+		<annotation cp="⛸" type="tts">ice skate</annotation>
+		<annotation cp="🎣">fish | fishing pole | pole</annotation>
+		<annotation cp="🎣" type="tts">fishing pole</annotation>
+		<annotation cp="🤿">diving | diving mask | scuba | snorkeling</annotation>
+		<annotation cp="🤿" type="tts">diving mask</annotation>
+		<annotation cp="🎽">athletics | running | sash | shirt</annotation>
+		<annotation cp="🎽" type="tts">running shirt</annotation>
+		<annotation cp="🎿">ski | skis | snow</annotation>
+		<annotation cp="🎿" type="tts">skis</annotation>
+		<annotation cp="🛷">sled | sledge | sleigh</annotation>
+		<annotation cp="🛷" type="tts">sled</annotation>
+		<annotation cp="🥌">curling stone | game | rock</annotation>
+		<annotation cp="🥌" type="tts">curling stone</annotation>
+		<annotation cp="🎯">bullseye | dart | direct hit | game | hit | target</annotation>
+		<annotation cp="🎯" type="tts">bullseye</annotation>
+		<annotation cp="🪀">fluctuate | toy | yo-yo</annotation>
+		<annotation cp="🪀" type="tts">yo-yo</annotation>
+		<annotation cp="🪁">fly | kite | soar</annotation>
+		<annotation cp="🪁" type="tts">kite</annotation>
+		<annotation cp="🎱">8 | ball | billiard | eight | game | pool 8 ball</annotation>
+		<annotation cp="🎱" type="tts">pool 8 ball</annotation>
+		<annotation cp="🔮">ball | crystal | fairy tale | fantasy | fortune | tool</annotation>
+		<annotation cp="🔮" type="tts">crystal ball</annotation>
+		<annotation cp="🪄">magic | magic wand | witch | wizard</annotation>
+		<annotation cp="🪄" type="tts">magic wand</annotation>
+		<annotation cp="🧿">bead | charm | evil-eye | nazar | nazar amulet | talisman</annotation>
+		<annotation cp="🧿" type="tts">nazar amulet</annotation>
+		<annotation cp="🎮">controller | game | video game</annotation>
+		<annotation cp="🎮" type="tts">video game</annotation>
+		<annotation cp="🕹">game | joystick | video game</annotation>
+		<annotation cp="🕹" type="tts">joystick</annotation>
+		<annotation cp="🎰">game | slot | slot machine</annotation>
+		<annotation cp="🎰" type="tts">slot machine</annotation>
+		<annotation cp="🎲">dice | die | game</annotation>
+		<annotation cp="🎲" type="tts">game die</annotation>
+		<annotation cp="🧩">clue | interlocking | jigsaw | piece | puzzle</annotation>
+		<annotation cp="🧩" type="tts">puzzle piece</annotation>
+		<annotation cp="🧸">plaything | plush | stuffed | teddy bear | toy</annotation>
+		<annotation cp="🧸" type="tts">teddy bear</annotation>
+		<annotation cp="🪅">celebration | party | piñata</annotation>
+		<annotation cp="🪅" type="tts">piñata</annotation>
+		<annotation cp="🪆">doll | nesting | nesting dolls | russia</annotation>
+		<annotation cp="🪆" type="tts">nesting dolls</annotation>
+		<annotation cp="♠">card | game | spade suit</annotation>
+		<annotation cp="♠" type="tts">spade suit</annotation>
+		<annotation cp="♥">card | game | heart suit</annotation>
+		<annotation cp="♥" type="tts">heart suit</annotation>
+		<annotation cp="♦">card | diamond suit | game</annotation>
+		<annotation cp="♦" type="tts">diamond suit</annotation>
+		<annotation cp="♣">card | club suit | game</annotation>
+		<annotation cp="♣" type="tts">club suit</annotation>
+		<annotation cp="♟">chess | chess pawn | dupe | expendable</annotation>
+		<annotation cp="♟" type="tts">chess pawn</annotation>
+		<annotation cp="🃏">card | game | joker | wildcard</annotation>
+		<annotation cp="🃏" type="tts">joker</annotation>
+		<annotation cp="🀄">game | mahjong | mahjong red dragon | red</annotation>
+		<annotation cp="🀄" type="tts">mahjong red dragon</annotation>
+		<annotation cp="🎴">card | flower | flower playing cards | game | Japanese | playing</annotation>
+		<annotation cp="🎴" type="tts">flower playing cards</annotation>
+		<annotation cp="🎭">art | mask | performing | performing arts | theater | theatre</annotation>
+		<annotation cp="🎭" type="tts">performing arts</annotation>
+		<annotation cp="🖼">art | frame | framed picture | museum | painting | picture</annotation>
+		<annotation cp="🖼" type="tts">framed picture</annotation>
+		<annotation cp="🎨">art | artist palette | museum | painting | palette</annotation>
+		<annotation cp="🎨" type="tts">artist palette</annotation>
+		<annotation cp="🧵">needle | sewing | spool | string | thread</annotation>
+		<annotation cp="🧵" type="tts">thread</annotation>
+		<annotation cp="🪡">embroidery | needle | sewing | stitches | sutures | tailoring</annotation>
+		<annotation cp="🪡" type="tts">sewing needle</annotation>
+		<annotation cp="🧶">ball | crochet | knit | yarn</annotation>
+		<annotation cp="🧶" type="tts">yarn</annotation>
+		<annotation cp="🪢">knot | rope | tangled | tie | twine | twist</annotation>
+		<annotation cp="🪢" type="tts">knot</annotation>
+		<annotation cp="👓">clothing | eye | eyeglasses | eyewear | glasses</annotation>
+		<annotation cp="👓" type="tts">glasses</annotation>
+		<annotation cp="🕶">dark | eye | eyewear | glasses | sunglasses</annotation>
+		<annotation cp="🕶" type="tts">sunglasses</annotation>
+		<annotation cp="🥽">eye protection | goggles | swimming | welding</annotation>
+		<annotation cp="🥽" type="tts">goggles</annotation>
+		<annotation cp="🥼">doctor | experiment | lab coat | scientist</annotation>
+		<annotation cp="🥼" type="tts">lab coat</annotation>
+		<annotation cp="🦺">emergency | safety | vest</annotation>
+		<annotation cp="🦺" type="tts">safety vest</annotation>
+		<annotation cp="👔">clothing | necktie | tie</annotation>
+		<annotation cp="👔" type="tts">necktie</annotation>
+		<annotation cp="👕">clothing | shirt | t-shirt | tshirt</annotation>
+		<annotation cp="👕" type="tts">t-shirt</annotation>
+		<annotation cp="👖">clothing | jeans | pants | trousers</annotation>
+		<annotation cp="👖" type="tts">jeans</annotation>
+		<annotation cp="🧣">neck | scarf</annotation>
+		<annotation cp="🧣" type="tts">scarf</annotation>
+		<annotation cp="🧤">gloves | hand</annotation>
+		<annotation cp="🧤" type="tts">gloves</annotation>
+		<annotation cp="🧥">coat | jacket</annotation>
+		<annotation cp="🧥" type="tts">coat</annotation>
+		<annotation cp="🧦">socks | stocking</annotation>
+		<annotation cp="🧦" type="tts">socks</annotation>
+		<annotation cp="👗">clothing | dress</annotation>
+		<annotation cp="👗" type="tts">dress</annotation>
+		<annotation cp="👘">clothing | kimono</annotation>
+		<annotation cp="👘" type="tts">kimono</annotation>
+		<annotation cp="🥻">clothing | dress | sari</annotation>
+		<annotation cp="🥻" type="tts">sari</annotation>
+		<annotation cp="🩱">bathing suit | one-piece swimsuit</annotation>
+		<annotation cp="🩱" type="tts">one-piece swimsuit</annotation>
+		<annotation cp="🩲">bathing suit | briefs | one-piece | swimsuit | underwear</annotation>
+		<annotation cp="🩲" type="tts">briefs</annotation>
+		<annotation cp="🩳">bathing suit | pants | shorts | underwear</annotation>
+		<annotation cp="🩳" type="tts">shorts</annotation>
+		<annotation cp="👙">bikini | clothing | swim</annotation>
+		<annotation cp="👙" type="tts">bikini</annotation>
+		<annotation cp="👚">clothing | woman | woman’s clothes</annotation>
+		<annotation cp="👚" type="tts">woman’s clothes</annotation>
+		<annotation cp="👛">clothing | coin | purse</annotation>
+		<annotation cp="👛" type="tts">purse</annotation>
+		<annotation cp="👜">bag | clothing | handbag | purse</annotation>
+		<annotation cp="👜" type="tts">handbag</annotation>
+		<annotation cp="👝">bag | clothing | clutch bag | pouch</annotation>
+		<annotation cp="👝" type="tts">clutch bag</annotation>
+		<annotation cp="🛍">bag | hotel | shopping | shopping bags</annotation>
+		<annotation cp="🛍" type="tts">shopping bags</annotation>
+		<annotation cp="🎒">backpack | bag | rucksack | satchel | school</annotation>
+		<annotation cp="🎒" type="tts">backpack</annotation>
+		<annotation cp="🩴">beach sandals | sandals | thong sandal | thong sandals | thongs | zōri</annotation>
+		<annotation cp="🩴" type="tts">thong sandal</annotation>
+		<annotation cp="👞">clothing | man | man’s shoe | shoe</annotation>
+		<annotation cp="👞" type="tts">man’s shoe</annotation>
+		<annotation cp="👟">athletic | clothing | running shoe | shoe | sneaker</annotation>
+		<annotation cp="👟" type="tts">running shoe</annotation>
+		<annotation cp="🥾">backpacking | boot | camping | hiking</annotation>
+		<annotation cp="🥾" type="tts">hiking boot</annotation>
+		<annotation cp="🥿">ballet flat | flat shoe | slip-on | slipper</annotation>
+		<annotation cp="🥿" type="tts">flat shoe</annotation>
+		<annotation cp="👠">clothing | heel | high-heeled shoe | shoe | woman</annotation>
+		<annotation cp="👠" type="tts">high-heeled shoe</annotation>
+		<annotation cp="👡">clothing | sandal | shoe | woman | woman’s sandal</annotation>
+		<annotation cp="👡" type="tts">woman’s sandal</annotation>
+		<annotation cp="🩰">ballet | ballet shoes | dance</annotation>
+		<annotation cp="🩰" type="tts">ballet shoes</annotation>
+		<annotation cp="👢">boot | clothing | shoe | woman | woman’s boot</annotation>
+		<annotation cp="👢" type="tts">woman’s boot</annotation>
+		<annotation cp="👑">clothing | crown | king | queen</annotation>
+		<annotation cp="👑" type="tts">crown</annotation>
+		<annotation cp="👒">clothing | hat | woman | woman’s hat</annotation>
+		<annotation cp="👒" type="tts">woman’s hat</annotation>
+		<annotation cp="🎩">clothing | hat | top | tophat</annotation>
+		<annotation cp="🎩" type="tts">top hat</annotation>
+		<annotation cp="🎓">cap | celebration | clothing | graduation | hat</annotation>
+		<annotation cp="🎓" type="tts">graduation cap</annotation>
+		<annotation cp="🧢">baseball cap | billed cap</annotation>
+		<annotation cp="🧢" type="tts">billed cap</annotation>
+		<annotation cp="🪖">army | helmet | military | soldier | warrior</annotation>
+		<annotation cp="🪖" type="tts">military helmet</annotation>
+		<annotation cp="⛑">aid | cross | face | hat | helmet | rescue worker’s helmet</annotation>
+		<annotation cp="⛑" type="tts">rescue worker’s helmet</annotation>
+		<annotation cp="📿">beads | clothing | necklace | prayer | religion</annotation>
+		<annotation cp="📿" type="tts">prayer beads</annotation>
+		<annotation cp="💄">cosmetics | lipstick | makeup</annotation>
+		<annotation cp="💄" type="tts">lipstick</annotation>
+		<annotation cp="💍">diamond | ring</annotation>
+		<annotation cp="💍" type="tts">ring</annotation>
+		<annotation cp="💎">diamond | gem | gem stone | jewel</annotation>
+		<annotation cp="💎" type="tts">gem stone</annotation>
+		<annotation cp="🔇">mute | muted speaker | quiet | silent | speaker</annotation>
+		<annotation cp="🔇" type="tts">muted speaker</annotation>
+		<annotation cp="🔈">soft | speaker low volume</annotation>
+		<annotation cp="🔈" type="tts">speaker low volume</annotation>
+		<annotation cp="🔉">medium | speaker medium volume</annotation>
+		<annotation cp="🔉" type="tts">speaker medium volume</annotation>
+		<annotation cp="🔊">loud | speaker high volume</annotation>
+		<annotation cp="🔊" type="tts">speaker high volume</annotation>
+		<annotation cp="📢">loud | loudspeaker | public address</annotation>
+		<annotation cp="📢" type="tts">loudspeaker</annotation>
+		<annotation cp="📣">cheering | megaphone</annotation>
+		<annotation cp="📣" type="tts">megaphone</annotation>
+		<annotation cp="📯">horn | post | postal</annotation>
+		<annotation cp="📯" type="tts">postal horn</annotation>
+		<annotation cp="🔔">bell</annotation>
+		<annotation cp="🔔" type="tts">bell</annotation>
+		<annotation cp="🔕">bell | bell with slash | forbidden | mute | quiet | silent</annotation>
+		<annotation cp="🔕" type="tts">bell with slash</annotation>
+		<annotation cp="🎼">music | musical score | score</annotation>
+		<annotation cp="🎼" type="tts">musical score</annotation>
+		<annotation cp="🎵">music | musical note | note</annotation>
+		<annotation cp="🎵" type="tts">musical note</annotation>
+		<annotation cp="🎶">music | musical notes | note | notes</annotation>
+		<annotation cp="🎶" type="tts">musical notes</annotation>
+		<annotation cp="🎙">mic | microphone | music | studio</annotation>
+		<annotation cp="🎙" type="tts">studio microphone</annotation>
+		<annotation cp="🎚">level | music | slider</annotation>
+		<annotation cp="🎚" type="tts">level slider</annotation>
+		<annotation cp="🎛">control | knobs | music</annotation>
+		<annotation cp="🎛" type="tts">control knobs</annotation>
+		<annotation cp="🎤">karaoke | mic | microphone</annotation>
+		<annotation cp="🎤" type="tts">microphone</annotation>
+		<annotation cp="🎧">earbud | headphone</annotation>
+		<annotation cp="🎧" type="tts">headphone</annotation>
+		<annotation cp="📻">radio | video</annotation>
+		<annotation cp="📻" type="tts">radio</annotation>
+		<annotation cp="🎷">instrument | music | sax | saxophone</annotation>
+		<annotation cp="🎷" type="tts">saxophone</annotation>
+		<annotation cp="🪗">accordian | accordion | concertina | squeeze box</annotation>
+		<annotation cp="🪗" type="tts">accordion</annotation>
+		<annotation cp="🎸">guitar | instrument | music</annotation>
+		<annotation cp="🎸" type="tts">guitar</annotation>
+		<annotation cp="🎹">instrument | keyboard | music | musical keyboard | piano</annotation>
+		<annotation cp="🎹" type="tts">musical keyboard</annotation>
+		<annotation cp="🎺">instrument | music | trumpet</annotation>
+		<annotation cp="🎺" type="tts">trumpet</annotation>
+		<annotation cp="🎻">instrument | music | violin</annotation>
+		<annotation cp="🎻" type="tts">violin</annotation>
+		<annotation cp="🪕">banjo | music | stringed</annotation>
+		<annotation cp="🪕" type="tts">banjo</annotation>
+		<annotation cp="🥁">drum | drumsticks | music</annotation>
+		<annotation cp="🥁" type="tts">drum</annotation>
+		<annotation cp="🪘">beat | conga | drum | long drum | rhythm</annotation>
+		<annotation cp="🪘" type="tts">long drum</annotation>
+		<annotation cp="📱">cell | mobile | phone | telephone</annotation>
+		<annotation cp="📱" type="tts">mobile phone</annotation>
+		<annotation cp="📲">arrow | cell | mobile | mobile phone with arrow | phone | receive</annotation>
+		<annotation cp="📲" type="tts">mobile phone with arrow</annotation>
+		<annotation cp="☎">phone | telephone</annotation>
+		<annotation cp="☎" type="tts">telephone</annotation>
+		<annotation cp="📞">phone | receiver | telephone</annotation>
+		<annotation cp="📞" type="tts">telephone receiver</annotation>
+		<annotation cp="📟">pager</annotation>
+		<annotation cp="📟" type="tts">pager</annotation>
+		<annotation cp="📠">fax | fax machine</annotation>
+		<annotation cp="📠" type="tts">fax machine</annotation>
+		<annotation cp="🔋">battery</annotation>
+		<annotation cp="🔋" type="tts">battery</annotation>
+		<annotation cp="🔌">electric | electricity | plug</annotation>
+		<annotation cp="🔌" type="tts">electric plug</annotation>
+		<annotation cp="💻">computer | laptop | pc | personal</annotation>
+		<annotation cp="💻" type="tts">laptop</annotation>
+		<annotation cp="🖥">computer | desktop</annotation>
+		<annotation cp="🖥" type="tts">desktop computer</annotation>
+		<annotation cp="🖨">computer | printer</annotation>
+		<annotation cp="🖨" type="tts">printer</annotation>
+		<annotation cp="⌨">computer | keyboard</annotation>
+		<annotation cp="⌨" type="tts">keyboard</annotation>
+		<annotation cp="🖱">computer | computer mouse</annotation>
+		<annotation cp="🖱" type="tts">computer mouse</annotation>
+		<annotation cp="🖲">computer | trackball</annotation>
+		<annotation cp="🖲" type="tts">trackball</annotation>
+		<annotation cp="💽">computer | disk | minidisk | optical</annotation>
+		<annotation cp="💽" type="tts">computer disk</annotation>
+		<annotation cp="💾">computer | disk | floppy</annotation>
+		<annotation cp="💾" type="tts">floppy disk</annotation>
+		<annotation cp="💿">cd | computer | disk | optical</annotation>
+		<annotation cp="💿" type="tts">optical disk</annotation>
+		<annotation cp="📀">blu-ray | computer | disk | dvd | optical</annotation>
+		<annotation cp="📀" type="tts">dvd</annotation>
+		<annotation cp="🧮">abacus | calculation</annotation>
+		<annotation cp="🧮" type="tts">abacus</annotation>
+		<annotation cp="🎥">camera | cinema | movie</annotation>
+		<annotation cp="🎥" type="tts">movie camera</annotation>
+		<annotation cp="🎞">cinema | film | frames | movie</annotation>
+		<annotation cp="🎞" type="tts">film frames</annotation>
+		<annotation cp="📽">cinema | film | movie | projector | video</annotation>
+		<annotation cp="📽" type="tts">film projector</annotation>
+		<annotation cp="🎬">clapper | clapper board | movie</annotation>
+		<annotation cp="🎬" type="tts">clapper board</annotation>
+		<annotation cp="📺">television | tv | video</annotation>
+		<annotation cp="📺" type="tts">television</annotation>
+		<annotation cp="📷">camera | video</annotation>
+		<annotation cp="📷" type="tts">camera</annotation>
+		<annotation cp="📸">camera | camera with flash | flash | video</annotation>
+		<annotation cp="📸" type="tts">camera with flash</annotation>
+		<annotation cp="📹">camera | video</annotation>
+		<annotation cp="📹" type="tts">video camera</annotation>
+		<annotation cp="📼">tape | vhs | video | videocassette</annotation>
+		<annotation cp="📼" type="tts">videocassette</annotation>
+		<annotation cp="🔍">glass | magnifying | magnifying glass tilted left | search | tool</annotation>
+		<annotation cp="🔍" type="tts">magnifying glass tilted left</annotation>
+		<annotation cp="🔎">glass | magnifying | magnifying glass tilted right | search | tool</annotation>
+		<annotation cp="🔎" type="tts">magnifying glass tilted right</annotation>
+		<annotation cp="🕯">candle | light</annotation>
+		<annotation cp="🕯" type="tts">candle</annotation>
+		<annotation cp="💡">bulb | comic | electric | idea | light</annotation>
+		<annotation cp="💡" type="tts">light bulb</annotation>
+		<annotation cp="🔦">electric | flashlight | light | tool | torch</annotation>
+		<annotation cp="🔦" type="tts">flashlight</annotation>
+		<annotation cp="🏮">bar | lantern | light | red | red paper lantern</annotation>
+		<annotation cp="🏮" type="tts">red paper lantern</annotation>
+		<annotation cp="🪔">diya | lamp | oil</annotation>
+		<annotation cp="🪔" type="tts">diya lamp</annotation>
+		<annotation cp="📔">book | cover | decorated | notebook | notebook with decorative cover</annotation>
+		<annotation cp="📔" type="tts">notebook with decorative cover</annotation>
+		<annotation cp="📕">book | closed</annotation>
+		<annotation cp="📕" type="tts">closed book</annotation>
+		<annotation cp="📖">book | open</annotation>
+		<annotation cp="📖" type="tts">open book</annotation>
+		<annotation cp="📗">book | green</annotation>
+		<annotation cp="📗" type="tts">green book</annotation>
+		<annotation cp="📘">blue | book</annotation>
+		<annotation cp="📘" type="tts">blue book</annotation>
+		<annotation cp="📙">book | orange</annotation>
+		<annotation cp="📙" type="tts">orange book</annotation>
+		<annotation cp="📚">book | books</annotation>
+		<annotation cp="📚" type="tts">books</annotation>
+		<annotation cp="📓">notebook</annotation>
+		<annotation cp="📓" type="tts">notebook</annotation>
+		<annotation cp="📒">ledger | notebook</annotation>
+		<annotation cp="📒" type="tts">ledger</annotation>
+		<annotation cp="📃">curl | document | page | page with curl</annotation>
+		<annotation cp="📃" type="tts">page with curl</annotation>
+		<annotation cp="📜">paper | scroll</annotation>
+		<annotation cp="📜" type="tts">scroll</annotation>
+		<annotation cp="📄">document | page | page facing up</annotation>
+		<annotation cp="📄" type="tts">page facing up</annotation>
+		<annotation cp="📰">news | newspaper | paper</annotation>
+		<annotation cp="📰" type="tts">newspaper</annotation>
+		<annotation cp="🗞">news | newspaper | paper | rolled | rolled-up newspaper</annotation>
+		<annotation cp="🗞" type="tts">rolled-up newspaper</annotation>
+		<annotation cp="📑">bookmark | mark | marker | tabs</annotation>
+		<annotation cp="📑" type="tts">bookmark tabs</annotation>
+		<annotation cp="🔖">bookmark | mark</annotation>
+		<annotation cp="🔖" type="tts">bookmark</annotation>
+		<annotation cp="🏷">label</annotation>
+		<annotation cp="🏷" type="tts">label</annotation>
+		<annotation cp="💰">bag | dollar | money | moneybag</annotation>
+		<annotation cp="💰" type="tts">money bag</annotation>
+		<annotation cp="🪙">coin | gold | metal | money | silver | treasure</annotation>
+		<annotation cp="🪙" type="tts">coin</annotation>
+		<annotation cp="💴">banknote | bill | currency | money | note | yen</annotation>
+		<annotation cp="💴" type="tts">yen banknote</annotation>
+		<annotation cp="💵">banknote | bill | currency | dollar | money | note</annotation>
+		<annotation cp="💵" type="tts">dollar banknote</annotation>
+		<annotation cp="💶">banknote | bill | currency | euro | money | note</annotation>
+		<annotation cp="💶" type="tts">euro banknote</annotation>
+		<annotation cp="💷">banknote | bill | currency | money | note | pound</annotation>
+		<annotation cp="💷" type="tts">pound banknote</annotation>
+		<annotation cp="💸">banknote | bill | fly | money | money with wings | wings</annotation>
+		<annotation cp="💸" type="tts">money with wings</annotation>
+		<annotation cp="💳">card | credit | money</annotation>
+		<annotation cp="💳" type="tts">credit card</annotation>
+		<annotation cp="🧾">accounting | bookkeeping | evidence | proof | receipt</annotation>
+		<annotation cp="🧾" type="tts">receipt</annotation>
+		<annotation cp="💹">chart | chart increasing with yen | graph | growth | money | yen</annotation>
+		<annotation cp="💹" type="tts">chart increasing with yen</annotation>
+		<annotation cp="✉">email | envelope | letter</annotation>
+		<annotation cp="✉" type="tts">envelope</annotation>
+		<annotation cp="📧">e-mail | email | letter | mail</annotation>
+		<annotation cp="📧" type="tts">e-mail</annotation>
+		<annotation cp="📨">e-mail | email | envelope | incoming | letter | receive</annotation>
+		<annotation cp="📨" type="tts">incoming envelope</annotation>
+		<annotation cp="📩">arrow | e-mail | email | envelope | envelope with arrow | outgoing</annotation>
+		<annotation cp="📩" type="tts">envelope with arrow</annotation>
+		<annotation cp="📤">box | letter | mail | outbox | sent | tray</annotation>
+		<annotation cp="📤" type="tts">outbox tray</annotation>
+		<annotation cp="📥">box | inbox | letter | mail | receive | tray</annotation>
+		<annotation cp="📥" type="tts">inbox tray</annotation>
+		<annotation cp="📦">box | package | parcel</annotation>
+		<annotation cp="📦" type="tts">package</annotation>
+		<annotation cp="📫">closed | closed mailbox with raised flag | mail | mailbox | postbox</annotation>
+		<annotation cp="📫" type="tts">closed mailbox with raised flag</annotation>
+		<annotation cp="📪">closed | closed mailbox with lowered flag | lowered | mail | mailbox | postbox</annotation>
+		<annotation cp="📪" type="tts">closed mailbox with lowered flag</annotation>
+		<annotation cp="📬">mail | mailbox | open | open mailbox with raised flag | postbox</annotation>
+		<annotation cp="📬" type="tts">open mailbox with raised flag</annotation>
+		<annotation cp="📭">lowered | mail | mailbox | open | open mailbox with lowered flag | postbox</annotation>
+		<annotation cp="📭" type="tts">open mailbox with lowered flag</annotation>
+		<annotation cp="📮">mail | mailbox | postbox</annotation>
+		<annotation cp="📮" type="tts">postbox</annotation>
+		<annotation cp="🗳">ballot | ballot box with ballot | box</annotation>
+		<annotation cp="🗳" type="tts">ballot box with ballot</annotation>
+		<annotation cp="✏">pencil</annotation>
+		<annotation cp="✏" type="tts">pencil</annotation>
+		<annotation cp="✒">black nib | nib | pen</annotation>
+		<annotation cp="✒" type="tts">black nib</annotation>
+		<annotation cp="🖋">fountain | pen</annotation>
+		<annotation cp="🖋" type="tts">fountain pen</annotation>
+		<annotation cp="🖊">ballpoint | pen</annotation>
+		<annotation cp="🖊" type="tts">pen</annotation>
+		<annotation cp="🖌">paintbrush | painting</annotation>
+		<annotation cp="🖌" type="tts">paintbrush</annotation>
+		<annotation cp="🖍">crayon</annotation>
+		<annotation cp="🖍" type="tts">crayon</annotation>
+		<annotation cp="📝">memo | pencil</annotation>
+		<annotation cp="📝" type="tts">memo</annotation>
+		<annotation cp="💼">briefcase</annotation>
+		<annotation cp="💼" type="tts">briefcase</annotation>
+		<annotation cp="📁">file | folder</annotation>
+		<annotation cp="📁" type="tts">file folder</annotation>
+		<annotation cp="📂">file | folder | open</annotation>
+		<annotation cp="📂" type="tts">open file folder</annotation>
+		<annotation cp="🗂">card | dividers | index</annotation>
+		<annotation cp="🗂" type="tts">card index dividers</annotation>
+		<annotation cp="📅">calendar | date</annotation>
+		<annotation cp="📅" type="tts">calendar</annotation>
+		<annotation cp="📆">calendar | tear-off calendar</annotation>
+		<annotation cp="📆" type="tts">tear-off calendar</annotation>
+		<annotation cp="🗒">note | pad | spiral | spiral notepad</annotation>
+		<annotation cp="🗒" type="tts">spiral notepad</annotation>
+		<annotation cp="🗓">calendar | pad | spiral</annotation>
+		<annotation cp="🗓" type="tts">spiral calendar</annotation>
+		<annotation cp="📇">card | index | rolodex</annotation>
+		<annotation cp="📇" type="tts">card index</annotation>
+		<annotation cp="📈">chart | chart increasing | graph | growth | trend | upward</annotation>
+		<annotation cp="📈" type="tts">chart increasing</annotation>
+		<annotation cp="📉">chart | chart decreasing | down | graph | trend</annotation>
+		<annotation cp="📉" type="tts">chart decreasing</annotation>
+		<annotation cp="📊">bar | chart | graph</annotation>
+		<annotation cp="📊" type="tts">bar chart</annotation>
+		<annotation cp="📋">clipboard</annotation>
+		<annotation cp="📋" type="tts">clipboard</annotation>
+		<annotation cp="📌">pin | pushpin</annotation>
+		<annotation cp="📌" type="tts">pushpin</annotation>
+		<annotation cp="📍">pin | pushpin | round pushpin</annotation>
+		<annotation cp="📍" type="tts">round pushpin</annotation>
+		<annotation cp="📎">paperclip</annotation>
+		<annotation cp="📎" type="tts">paperclip</annotation>
+		<annotation cp="🖇">link | linked paperclips | paperclip</annotation>
+		<annotation cp="🖇" type="tts">linked paperclips</annotation>
+		<annotation cp="📏">ruler | straight edge | straight ruler</annotation>
+		<annotation cp="📏" type="tts">straight ruler</annotation>
+		<annotation cp="📐">ruler | set | triangle | triangular ruler</annotation>
+		<annotation cp="📐" type="tts">triangular ruler</annotation>
+		<annotation cp="✂">cutting | scissors | tool</annotation>
+		<annotation cp="✂" type="tts">scissors</annotation>
+		<annotation cp="🗃">box | card | file</annotation>
+		<annotation cp="🗃" type="tts">card file box</annotation>
+		<annotation cp="🗄">cabinet | file | filing</annotation>
+		<annotation cp="🗄" type="tts">file cabinet</annotation>
+		<annotation cp="🗑">wastebasket</annotation>
+		<annotation cp="🗑" type="tts">wastebasket</annotation>
+		<annotation cp="🔒">closed | locked</annotation>
+		<annotation cp="🔒" type="tts">locked</annotation>
+		<annotation cp="🔓">lock | open | unlock | unlocked</annotation>
+		<annotation cp="🔓" type="tts">unlocked</annotation>
+		<annotation cp="🔏">ink | lock | locked with pen | nib | pen | privacy</annotation>
+		<annotation cp="🔏" type="tts">locked with pen</annotation>
+		<annotation cp="🔐">closed | key | lock | locked with key | secure</annotation>
+		<annotation cp="🔐" type="tts">locked with key</annotation>
+		<annotation cp="🔑">key | lock | password</annotation>
+		<annotation cp="🔑" type="tts">key</annotation>
+		<annotation cp="🗝">clue | key | lock | old</annotation>
+		<annotation cp="🗝" type="tts">old key</annotation>
+		<annotation cp="🔨">hammer | tool</annotation>
+		<annotation cp="🔨" type="tts">hammer</annotation>
+		<annotation cp="🪓">axe | chop | hatchet | split | wood</annotation>
+		<annotation cp="🪓" type="tts">axe</annotation>
+		<annotation cp="⛏">mining | pick | tool</annotation>
+		<annotation cp="⛏" type="tts">pick</annotation>
+		<annotation cp="⚒">hammer | hammer and pick | pick | tool</annotation>
+		<annotation cp="⚒" type="tts">hammer and pick</annotation>
+		<annotation cp="🛠">hammer | hammer and wrench | spanner | tool | wrench</annotation>
+		<annotation cp="🛠" type="tts">hammer and wrench</annotation>
+		<annotation cp="🗡">dagger | knife | weapon</annotation>
+		<annotation cp="🗡" type="tts">dagger</annotation>
+		<annotation cp="⚔">crossed | swords | weapon</annotation>
+		<annotation cp="⚔" type="tts">crossed swords</annotation>
+		<annotation cp="🔫">gun | handgun | pistol | revolver | tool | water | weapon</annotation>
+		<annotation cp="🔫" type="tts">water pistol</annotation>
+		<annotation cp="🪃">australia | boomerang | rebound | repercussion</annotation>
+		<annotation cp="🪃" type="tts">boomerang</annotation>
+		<annotation cp="🏹">archer | arrow | bow | bow and arrow | Sagittarius | zodiac</annotation>
+		<annotation cp="🏹" type="tts">bow and arrow</annotation>
+		<annotation cp="🛡">shield | weapon</annotation>
+		<annotation cp="🛡" type="tts">shield</annotation>
+		<annotation cp="🪚">carpenter | carpentry saw | lumber | saw | tool</annotation>
+		<annotation cp="🪚" type="tts">carpentry saw</annotation>
+		<annotation cp="🔧">spanner | tool | wrench</annotation>
+		<annotation cp="🔧" type="tts">wrench</annotation>
+		<annotation cp="🪛">screw | screwdriver | tool</annotation>
+		<annotation cp="🪛" type="tts">screwdriver</annotation>
+		<annotation cp="🔩">bolt | nut | nut and bolt | tool</annotation>
+		<annotation cp="🔩" type="tts">nut and bolt</annotation>
+		<annotation cp="⚙">cog | cogwheel | gear | tool</annotation>
+		<annotation cp="⚙" type="tts">gear</annotation>
+		<annotation cp="🗜">clamp | compress | tool | vice</annotation>
+		<annotation cp="🗜" type="tts">clamp</annotation>
+		<annotation cp="⚖">balance | justice | Libra | scale | zodiac</annotation>
+		<annotation cp="⚖" type="tts">balance scale</annotation>
+		<annotation cp="🦯">accessibility | blind | white cane</annotation>
+		<annotation cp="🦯" type="tts">white cane</annotation>
+		<annotation cp="🔗">link</annotation>
+		<annotation cp="🔗" type="tts">link</annotation>
+		<annotation cp="⛓">chain | chains</annotation>
+		<annotation cp="⛓" type="tts">chains</annotation>
+		<annotation cp="🪝">catch | crook | curve | ensnare | hook | selling point</annotation>
+		<annotation cp="🪝" type="tts">hook</annotation>
+		<annotation cp="🧰">chest | mechanic | tool | toolbox</annotation>
+		<annotation cp="🧰" type="tts">toolbox</annotation>
+		<annotation cp="🧲">attraction | horseshoe | magnet | magnetic</annotation>
+		<annotation cp="🧲" type="tts">magnet</annotation>
+		<annotation cp="🪜">climb | ladder | rung | step</annotation>
+		<annotation cp="🪜" type="tts">ladder</annotation>
+		<annotation cp="⚗">alembic | chemistry | tool</annotation>
+		<annotation cp="⚗" type="tts">alembic</annotation>
+		<annotation cp="🧪">chemist | chemistry | experiment | lab | science | test tube</annotation>
+		<annotation cp="🧪" type="tts">test tube</annotation>
+		<annotation cp="🧫">bacteria | biologist | biology | culture | lab | petri dish</annotation>
+		<annotation cp="🧫" type="tts">petri dish</annotation>
+		<annotation cp="🧬">biologist | dna | evolution | gene | genetics | life</annotation>
+		<annotation cp="🧬" type="tts">dna</annotation>
+		<annotation cp="🔬">microscope | science | tool</annotation>
+		<annotation cp="🔬" type="tts">microscope</annotation>
+		<annotation cp="🔭">science | telescope | tool</annotation>
+		<annotation cp="🔭" type="tts">telescope</annotation>
+		<annotation cp="📡">antenna | dish | satellite</annotation>
+		<annotation cp="📡" type="tts">satellite antenna</annotation>
+		<annotation cp="💉">medicine | needle | shot | sick | syringe</annotation>
+		<annotation cp="💉" type="tts">syringe</annotation>
+		<annotation cp="🩸">bleed | blood donation | drop of blood | injury | medicine | menstruation</annotation>
+		<annotation cp="🩸" type="tts">drop of blood</annotation>
+		<annotation cp="💊">doctor | medicine | pill | sick</annotation>
+		<annotation cp="💊" type="tts">pill</annotation>
+		<annotation cp="🩹">adhesive bandage | bandage</annotation>
+		<annotation cp="🩹" type="tts">adhesive bandage</annotation>
+		<annotation cp="🩺">doctor | heart | medicine | stethoscope</annotation>
+		<annotation cp="🩺" type="tts">stethoscope</annotation>
+		<annotation cp="🚪">door</annotation>
+		<annotation cp="🚪" type="tts">door</annotation>
+		<annotation cp="🛗">accessibility | elevator | hoist | lift</annotation>
+		<annotation cp="🛗" type="tts">elevator</annotation>
+		<annotation cp="🪞">mirror | reflection | reflector | speculum</annotation>
+		<annotation cp="🪞" type="tts">mirror</annotation>
+		<annotation cp="🪟">frame | fresh air | opening | transparent | view | window</annotation>
+		<annotation cp="🪟" type="tts">window</annotation>
+		<annotation cp="🛏">bed | hotel | sleep</annotation>
+		<annotation cp="🛏" type="tts">bed</annotation>
+		<annotation cp="🛋">couch | couch and lamp | hotel | lamp</annotation>
+		<annotation cp="🛋" type="tts">couch and lamp</annotation>
+		<annotation cp="🪑">chair | seat | sit</annotation>
+		<annotation cp="🪑" type="tts">chair</annotation>
+		<annotation cp="🚽">toilet</annotation>
+		<annotation cp="🚽" type="tts">toilet</annotation>
+		<annotation cp="🪠">force cup | plumber | plunger | suction | toilet</annotation>
+		<annotation cp="🪠" type="tts">plunger</annotation>
+		<annotation cp="🚿">shower | water</annotation>
+		<annotation cp="🚿" type="tts">shower</annotation>
+		<annotation cp="🛁">bath | bathtub</annotation>
+		<annotation cp="🛁" type="tts">bathtub</annotation>
+		<annotation cp="🪤">bait | mouse trap | mousetrap | snare | trap</annotation>
+		<annotation cp="🪤" type="tts">mouse trap</annotation>
+		<annotation cp="🪒">razor | sharp | shave</annotation>
+		<annotation cp="🪒" type="tts">razor</annotation>
+		<annotation cp="🧴">lotion | lotion bottle | moisturizer | shampoo | sunscreen</annotation>
+		<annotation cp="🧴" type="tts">lotion bottle</annotation>
+		<annotation cp="🧷">diaper | punk rock | safety pin</annotation>
+		<annotation cp="🧷" type="tts">safety pin</annotation>
+		<annotation cp="🧹">broom | cleaning | sweeping | witch</annotation>
+		<annotation cp="🧹" type="tts">broom</annotation>
+		<annotation cp="🧺">basket | farming | laundry | picnic</annotation>
+		<annotation cp="🧺" type="tts">basket</annotation>
+		<annotation cp="🧻">paper towels | roll of paper | toilet paper</annotation>
+		<annotation cp="🧻" type="tts">roll of paper</annotation>
+		<annotation cp="🪣">bucket | cask | pail | vat</annotation>
+		<annotation cp="🪣" type="tts">bucket</annotation>
+		<annotation cp="🧼">bar | bathing | cleaning | lather | soap | soapdish</annotation>
+		<annotation cp="🧼" type="tts">soap</annotation>
+		<annotation cp="🪥">bathroom | brush | clean | dental | hygiene | teeth | toothbrush</annotation>
+		<annotation cp="🪥" type="tts">toothbrush</annotation>
+		<annotation cp="🧽">absorbing | cleaning | porous | sponge</annotation>
+		<annotation cp="🧽" type="tts">sponge</annotation>
+		<annotation cp="🧯">extinguish | fire | fire extinguisher | quench</annotation>
+		<annotation cp="🧯" type="tts">fire extinguisher</annotation>
+		<annotation cp="🛒">cart | shopping | trolley</annotation>
+		<annotation cp="🛒" type="tts">shopping cart</annotation>
+		<annotation cp="🚬">cigarette | smoking</annotation>
+		<annotation cp="🚬" type="tts">cigarette</annotation>
+		<annotation cp="⚰">coffin | death</annotation>
+		<annotation cp="⚰" type="tts">coffin</annotation>
+		<annotation cp="🪦">cemetery | grave | graveyard | headstone | tombstone</annotation>
+		<annotation cp="🪦" type="tts">headstone</annotation>
+		<annotation cp="⚱">ashes | death | funeral | urn</annotation>
+		<annotation cp="⚱" type="tts">funeral urn</annotation>
+		<annotation cp="🗿">face | moai | moyai | statue</annotation>
+		<annotation cp="🗿" type="tts">moai</annotation>
+		<annotation cp="🪧">demonstration | picket | placard | protest | sign</annotation>
+		<annotation cp="🪧" type="tts">placard</annotation>
+		<annotation cp="🏧">atm | ATM sign | automated | bank | teller</annotation>
+		<annotation cp="🏧" type="tts">ATM sign</annotation>
+		<annotation cp="🚮">litter | litter bin | litter in bin sign</annotation>
+		<annotation cp="🚮" type="tts">litter in bin sign</annotation>
+		<annotation cp="🚰">drinking | potable | water</annotation>
+		<annotation cp="🚰" type="tts">potable water</annotation>
+		<annotation cp="♿">access | wheelchair symbol</annotation>
+		<annotation cp="♿" type="tts">wheelchair symbol</annotation>
+		<annotation cp="🚹">lavatory | man | men’s room | restroom | wc</annotation>
+		<annotation cp="🚹" type="tts">men’s room</annotation>
+		<annotation cp="🚺">lavatory | restroom | wc | woman | women’s room</annotation>
+		<annotation cp="🚺" type="tts">women’s room</annotation>
+		<annotation cp="🚻">lavatory | restroom | WC</annotation>
+		<annotation cp="🚻" type="tts">restroom</annotation>
+		<annotation cp="🚼">baby | baby symbol | changing</annotation>
+		<annotation cp="🚼" type="tts">baby symbol</annotation>
+		<annotation cp="🚾">closet | lavatory | restroom | water | wc</annotation>
+		<annotation cp="🚾" type="tts">water closet</annotation>
+		<annotation cp="🛂">control | passport</annotation>
+		<annotation cp="🛂" type="tts">passport control</annotation>
+		<annotation cp="🛃">customs</annotation>
+		<annotation cp="🛃" type="tts">customs</annotation>
+		<annotation cp="🛄">baggage | claim</annotation>
+		<annotation cp="🛄" type="tts">baggage claim</annotation>
+		<annotation cp="🛅">baggage | left luggage | locker | luggage</annotation>
+		<annotation cp="🛅" type="tts">left luggage</annotation>
+		<annotation cp="⚠">warning</annotation>
+		<annotation cp="⚠" type="tts">warning</annotation>
+		<annotation cp="🚸">child | children crossing | crossing | pedestrian | traffic</annotation>
+		<annotation cp="🚸" type="tts">children crossing</annotation>
+		<annotation cp="⛔">entry | forbidden | no | not | prohibited | traffic</annotation>
+		<annotation cp="⛔" type="tts">no entry</annotation>
+		<annotation cp="🚫">entry | forbidden | no | not | prohibited</annotation>
+		<annotation cp="🚫" type="tts">prohibited</annotation>
+		<annotation cp="🚳">bicycle | bike | forbidden | no | no bicycles | prohibited</annotation>
+		<annotation cp="🚳" type="tts">no bicycles</annotation>
+		<annotation cp="🚭">forbidden | no | not | prohibited | smoking</annotation>
+		<annotation cp="🚭" type="tts">no smoking</annotation>
+		<annotation cp="🚯">forbidden | litter | no | no littering | not | prohibited</annotation>
+		<annotation cp="🚯" type="tts">no littering</annotation>
+		<annotation cp="🚱">non-drinking | non-potable | water</annotation>
+		<annotation cp="🚱" type="tts">non-potable water</annotation>
+		<annotation cp="🚷">forbidden | no | no pedestrians | not | pedestrian | prohibited</annotation>
+		<annotation cp="🚷" type="tts">no pedestrians</annotation>
+		<annotation cp="📵">cell | forbidden | mobile | no | no mobile phones | phone</annotation>
+		<annotation cp="📵" type="tts">no mobile phones</annotation>
+		<annotation cp="🔞">18 | age restriction | eighteen | no one under eighteen | prohibited | underage</annotation>
+		<annotation cp="🔞" type="tts">no one under eighteen</annotation>
+		<annotation cp="☢">radioactive | sign</annotation>
+		<annotation cp="☢" type="tts">radioactive</annotation>
+		<annotation cp="☣">biohazard | sign</annotation>
+		<annotation cp="☣" type="tts">biohazard</annotation>
+		<annotation cp="⬆">arrow | cardinal | direction | north | up arrow</annotation>
+		<annotation cp="⬆" type="tts">up arrow</annotation>
+		<annotation cp="↗">arrow | direction | intercardinal | northeast | up-right arrow</annotation>
+		<annotation cp="↗" type="tts">up-right arrow</annotation>
+		<annotation cp="➡">arrow | cardinal | direction | east | right arrow</annotation>
+		<annotation cp="➡" type="tts">right arrow</annotation>
+		<annotation cp="↘">arrow | direction | down-right arrow | intercardinal | southeast</annotation>
+		<annotation cp="↘" type="tts">down-right arrow</annotation>
+		<annotation cp="⬇">arrow | cardinal | direction | down | south</annotation>
+		<annotation cp="⬇" type="tts">down arrow</annotation>
+		<annotation cp="↙">arrow | direction | down-left arrow | intercardinal | southwest</annotation>
+		<annotation cp="↙" type="tts">down-left arrow</annotation>
+		<annotation cp="⬅">arrow | cardinal | direction | left arrow | west</annotation>
+		<annotation cp="⬅" type="tts">left arrow</annotation>
+		<annotation cp="↖">arrow | direction | intercardinal | northwest | up-left arrow</annotation>
+		<annotation cp="↖" type="tts">up-left arrow</annotation>
+		<annotation cp="↕">arrow | up-down arrow</annotation>
+		<annotation cp="↕" type="tts">up-down arrow</annotation>
+		<annotation cp="↔">arrow | left-right arrow</annotation>
+		<annotation cp="↔" type="tts">left-right arrow</annotation>
+		<annotation cp="↮">left right arrow stroke</annotation>
+		<annotation cp="↮" type="tts">left right arrow stroke</annotation>
+		<annotation cp="↩">arrow | right arrow curving left</annotation>
+		<annotation cp="↩" type="tts">right arrow curving left</annotation>
+		<annotation cp="↪">arrow | left arrow curving right</annotation>
+		<annotation cp="↪" type="tts">left arrow curving right</annotation>
+		<annotation cp="⤴">arrow | right arrow curving up</annotation>
+		<annotation cp="⤴" type="tts">right arrow curving up</annotation>
+		<annotation cp="⤵">arrow | down | right arrow curving down</annotation>
+		<annotation cp="⤵" type="tts">right arrow curving down</annotation>
+		<annotation cp="🔃">arrow | clockwise | clockwise vertical arrows | reload</annotation>
+		<annotation cp="🔃" type="tts">clockwise vertical arrows</annotation>
+		<annotation cp="🔄">anticlockwise | arrow | counterclockwise | counterclockwise arrows button | withershins</annotation>
+		<annotation cp="🔄" type="tts">counterclockwise arrows button</annotation>
+		<annotation cp="🔙">arrow | back | BACK arrow</annotation>
+		<annotation cp="🔙" type="tts">BACK arrow</annotation>
+		<annotation cp="🔚">arrow | end | END arrow</annotation>
+		<annotation cp="🔚" type="tts">END arrow</annotation>
+		<annotation cp="🔛">arrow | mark | on | ON! arrow</annotation>
+		<annotation cp="🔛" type="tts">ON! arrow</annotation>
+		<annotation cp="🔜">arrow | soon | SOON arrow</annotation>
+		<annotation cp="🔜" type="tts">SOON arrow</annotation>
+		<annotation cp="🔝">arrow | top | TOP arrow | up</annotation>
+		<annotation cp="🔝" type="tts">TOP arrow</annotation>
+		<annotation cp="🛐">place of worship | religion | worship</annotation>
+		<annotation cp="🛐" type="tts">place of worship</annotation>
+		<annotation cp="⚛">atheist | atom | atom symbol</annotation>
+		<annotation cp="⚛" type="tts">atom symbol</annotation>
+		<annotation cp="🕉">Hindu | om | religion</annotation>
+		<annotation cp="🕉" type="tts">om</annotation>
+		<annotation cp="✡">David | Jew | Jewish | religion | star | star of David</annotation>
+		<annotation cp="✡" type="tts">star of David</annotation>
+		<annotation cp="☸">Buddhist | dharma | religion | wheel | wheel of dharma</annotation>
+		<annotation cp="☸" type="tts">wheel of dharma</annotation>
+		<annotation cp="☯">religion | tao | taoist | yang | yin</annotation>
+		<annotation cp="☯" type="tts">yin yang</annotation>
+		<annotation cp="✝">Christian | cross | latin cross | religion</annotation>
+		<annotation cp="✝" type="tts">latin cross</annotation>
+		<annotation cp="☦">Christian | cross | orthodox cross | religion</annotation>
+		<annotation cp="☦" type="tts">orthodox cross</annotation>
+		<annotation cp="☪">islam | Muslim | religion | star and crescent</annotation>
+		<annotation cp="☪" type="tts">star and crescent</annotation>
+		<annotation cp="☮">peace | peace symbol</annotation>
+		<annotation cp="☮" type="tts">peace symbol</annotation>
+		<annotation cp="🕎">candelabrum | candlestick | menorah | religion</annotation>
+		<annotation cp="🕎" type="tts">menorah</annotation>
+		<annotation cp="🔯">dotted six-pointed star | fortune | star</annotation>
+		<annotation cp="🔯" type="tts">dotted six-pointed star</annotation>
+		<annotation cp="♈">Aries | ram | zodiac</annotation>
+		<annotation cp="♈" type="tts">Aries</annotation>
+		<annotation cp="♉">bull | ox | Taurus | zodiac</annotation>
+		<annotation cp="♉" type="tts">Taurus</annotation>
+		<annotation cp="♊">Gemini | twins | zodiac</annotation>
+		<annotation cp="♊" type="tts">Gemini</annotation>
+		<annotation cp="♋">Cancer | crab | zodiac</annotation>
+		<annotation cp="♋" type="tts">Cancer</annotation>
+		<annotation cp="♌">Leo | lion | zodiac</annotation>
+		<annotation cp="♌" type="tts">Leo</annotation>
+		<annotation cp="♍">Virgo | zodiac</annotation>
+		<annotation cp="♍" type="tts">Virgo</annotation>
+		<annotation cp="♎">balance | justice | Libra | scales | zodiac</annotation>
+		<annotation cp="♎" type="tts">Libra</annotation>
+		<annotation cp="♏">Scorpio | scorpion | scorpius | zodiac</annotation>
+		<annotation cp="♏" type="tts">Scorpio</annotation>
+		<annotation cp="♐">archer | Sagittarius | zodiac</annotation>
+		<annotation cp="♐" type="tts">Sagittarius</annotation>
+		<annotation cp="♑">Capricorn | goat | zodiac</annotation>
+		<annotation cp="♑" type="tts">Capricorn</annotation>
+		<annotation cp="♒">Aquarius | bearer | water | zodiac</annotation>
+		<annotation cp="♒" type="tts">Aquarius</annotation>
+		<annotation cp="♓">fish | Pisces | zodiac</annotation>
+		<annotation cp="♓" type="tts">Pisces</annotation>
+		<annotation cp="⛎">bearer | Ophiuchus | serpent | snake | zodiac</annotation>
+		<annotation cp="⛎" type="tts">Ophiuchus</annotation>
+		<annotation cp="🔀">arrow | crossed | shuffle tracks button</annotation>
+		<annotation cp="🔀" type="tts">shuffle tracks button</annotation>
+		<annotation cp="🔁">arrow | clockwise | repeat | repeat button</annotation>
+		<annotation cp="🔁" type="tts">repeat button</annotation>
+		<annotation cp="🔂">arrow | clockwise | once | repeat single button</annotation>
+		<annotation cp="🔂" type="tts">repeat single button</annotation>
+		<annotation cp="▶">arrow | play | play button | right | triangle</annotation>
+		<annotation cp="▶" type="tts">play button</annotation>
+		<annotation cp="⏩">arrow | double | fast | fast-forward button | forward</annotation>
+		<annotation cp="⏩" type="tts">fast-forward button</annotation>
+		<annotation cp="⏭">arrow | next scene | next track | next track button | triangle</annotation>
+		<annotation cp="⏭" type="tts">next track button</annotation>
+		<annotation cp="⏯">arrow | pause | play | play or pause button | right | triangle</annotation>
+		<annotation cp="⏯" type="tts">play or pause button</annotation>
+		<annotation cp="◀">arrow | left | reverse | reverse button | triangle</annotation>
+		<annotation cp="◀" type="tts">reverse button</annotation>
+		<annotation cp="⏪">arrow | double | fast reverse button | rewind</annotation>
+		<annotation cp="⏪" type="tts">fast reverse button</annotation>
+		<annotation cp="⏮">arrow | last track button | previous scene | previous track | triangle</annotation>
+		<annotation cp="⏮" type="tts">last track button</annotation>
+		<annotation cp="🔼">arrow | button | red | upwards button</annotation>
+		<annotation cp="🔼" type="tts">upwards button</annotation>
+		<annotation cp="⏫">arrow | double | fast up button</annotation>
+		<annotation cp="⏫" type="tts">fast up button</annotation>
+		<annotation cp="🔽">arrow | button | down | downwards button | red</annotation>
+		<annotation cp="🔽" type="tts">downwards button</annotation>
+		<annotation cp="⏬">arrow | double | down | fast down button</annotation>
+		<annotation cp="⏬" type="tts">fast down button</annotation>
+		<annotation cp="⏸">bar | double | pause | pause button | vertical</annotation>
+		<annotation cp="⏸" type="tts">pause button</annotation>
+		<annotation cp="⏹">square | stop | stop button</annotation>
+		<annotation cp="⏹" type="tts">stop button</annotation>
+		<annotation cp="⏺">circle | record | record button</annotation>
+		<annotation cp="⏺" type="tts">record button</annotation>
+		<annotation cp="⏏">eject | eject button</annotation>
+		<annotation cp="⏏" type="tts">eject button</annotation>
+		<annotation cp="🎦">camera | cinema | film | movie</annotation>
+		<annotation cp="🎦" type="tts">cinema</annotation>
+		<annotation cp="🔅">brightness | dim | dim button | low</annotation>
+		<annotation cp="🔅" type="tts">dim button</annotation>
+		<annotation cp="🔆">bright | bright button | brightness</annotation>
+		<annotation cp="🔆" type="tts">bright button</annotation>
+		<annotation cp="📶">antenna | antenna bars | bar | cell | mobile | phone</annotation>
+		<annotation cp="📶" type="tts">antenna bars</annotation>
+		<annotation cp="📳">cell | mobile | mode | phone | telephone | vibration</annotation>
+		<annotation cp="📳" type="tts">vibration mode</annotation>
+		<annotation cp="📴">cell | mobile | off | phone | telephone</annotation>
+		<annotation cp="📴" type="tts">mobile phone off</annotation>
+		<annotation cp="♀">female sign | woman</annotation>
+		<annotation cp="♀" type="tts">female sign</annotation>
+		<annotation cp="♂">male sign | man</annotation>
+		<annotation cp="♂" type="tts">male sign</annotation>
+		<annotation cp="⚧">transgender | transgender symbol</annotation>
+		<annotation cp="⚧" type="tts">transgender symbol</annotation>
+		<annotation cp="✖">× | cancel | multiplication | multiply | sign | x</annotation>
+		<annotation cp="✖" type="tts">multiply</annotation>
+		<annotation cp="➕">+ | math | plus | sign</annotation>
+		<annotation cp="➕" type="tts">plus</annotation>
+		<annotation cp="➖">- | − | math | minus | sign</annotation>
+		<annotation cp="➖" type="tts">minus</annotation>
+		<annotation cp="➗">÷ | divide | division | math | sign</annotation>
+		<annotation cp="➗" type="tts">divide</annotation>
+		<annotation cp="♾">forever | infinity | unbounded | universal</annotation>
+		<annotation cp="♾" type="tts">infinity</annotation>
+		<annotation cp="‼">! | !! | bangbang | double exclamation mark | exclamation | mark</annotation>
+		<annotation cp="‼" type="tts">double exclamation mark</annotation>
+		<annotation cp="⁉">! | !? | ? | exclamation | interrobang | mark | punctuation | question</annotation>
+		<annotation cp="⁉" type="tts">exclamation question mark</annotation>
+		<annotation cp="❓">? | mark | punctuation | question | red question mark</annotation>
+		<annotation cp="❓" type="tts">red question mark</annotation>
+		<annotation cp="❔">? | mark | outlined | punctuation | question | white question mark</annotation>
+		<annotation cp="❔" type="tts">white question mark</annotation>
+		<annotation cp="❕">! | exclamation | mark | outlined | punctuation | white exclamation mark</annotation>
+		<annotation cp="❕" type="tts">white exclamation mark</annotation>
+		<annotation cp="❗">! | exclamation | mark | punctuation | red exclamation mark</annotation>
+		<annotation cp="❗" type="tts">red exclamation mark</annotation>
+		<annotation cp="〰">dash | punctuation | wavy</annotation>
+		<annotation cp="〰" type="tts">wavy dash</annotation>
+		<annotation cp="💱">bank | currency | exchange | money</annotation>
+		<annotation cp="💱" type="tts">currency exchange</annotation>
+		<annotation cp="💲">currency | dollar | heavy dollar sign | money</annotation>
+		<annotation cp="💲" type="tts">heavy dollar sign</annotation>
+		<annotation cp="⚕">aesculapius | medical symbol | medicine | staff</annotation>
+		<annotation cp="⚕" type="tts">medical symbol</annotation>
+		<annotation cp="♻">recycle | recycling symbol</annotation>
+		<annotation cp="♻" type="tts">recycling symbol</annotation>
+		<annotation cp="⚜">fleur-de-lis</annotation>
+		<annotation cp="⚜" type="tts">fleur-de-lis</annotation>
+		<annotation cp="🔱">anchor | emblem | ship | tool | trident</annotation>
+		<annotation cp="🔱" type="tts">trident emblem</annotation>
+		<annotation cp="📛">badge | name</annotation>
+		<annotation cp="📛" type="tts">name badge</annotation>
+		<annotation cp="🔰">beginner | chevron | Japanese | Japanese symbol for beginner | leaf</annotation>
+		<annotation cp="🔰" type="tts">Japanese symbol for beginner</annotation>
+		<annotation cp="⭕">circle | hollow red circle | large | o | red</annotation>
+		<annotation cp="⭕" type="tts">hollow red circle</annotation>
+		<annotation cp="✅">✓ | button | check | mark</annotation>
+		<annotation cp="✅" type="tts">check mark button</annotation>
+		<annotation cp="☑">✓ | box | check | check box with check</annotation>
+		<annotation cp="☑" type="tts">check box with check</annotation>
+		<annotation cp="✔">✓ | check | mark</annotation>
+		<annotation cp="✔" type="tts">check mark</annotation>
+		<annotation cp="❌">× | cancel | cross | mark | multiplication | multiply | x</annotation>
+		<annotation cp="❌" type="tts">cross mark</annotation>
+		<annotation cp="❎">× | cross mark button | mark | square | x</annotation>
+		<annotation cp="❎" type="tts">cross mark button</annotation>
+		<annotation cp="➰">curl | curly loop | loop</annotation>
+		<annotation cp="➰" type="tts">curly loop</annotation>
+		<annotation cp="➿">curl | double | double curly loop | loop</annotation>
+		<annotation cp="➿" type="tts">double curly loop</annotation>
+		<annotation cp="〽">mark | part | part alternation mark</annotation>
+		<annotation cp="〽" type="tts">part alternation mark</annotation>
+		<annotation cp="✳">* | asterisk | eight-spoked asterisk</annotation>
+		<annotation cp="✳" type="tts">eight-spoked asterisk</annotation>
+		<annotation cp="✴">* | eight-pointed star | star</annotation>
+		<annotation cp="✴" type="tts">eight-pointed star</annotation>
+		<annotation cp="❇">* | sparkle</annotation>
+		<annotation cp="❇" type="tts">sparkle</annotation>
+		<annotation cp="©">c | copyright</annotation>
+		<annotation cp="©" type="tts">copyright</annotation>
+		<annotation cp="®">r | registered</annotation>
+		<annotation cp="®" type="tts">registered</annotation>
+		<annotation cp="™">mark | tm | trade mark | trademark</annotation>
+		<annotation cp="™" type="tts">trade mark</annotation>
+		<annotation cp="🔠">ABCD | input | latin | letters | uppercase</annotation>
+		<annotation cp="🔠" type="tts">input latin uppercase</annotation>
+		<annotation cp="🔡">abcd | input | latin | letters | lowercase</annotation>
+		<annotation cp="🔡" type="tts">input latin lowercase</annotation>
+		<annotation cp="🔢">1234 | input | numbers</annotation>
+		<annotation cp="🔢" type="tts">input numbers</annotation>
+		<annotation cp="🔣">〒♪&amp;% | input | input symbols</annotation>
+		<annotation cp="🔣" type="tts">input symbols</annotation>
+		<annotation cp="🔤">abc | alphabet | input | latin | letters</annotation>
+		<annotation cp="🔤" type="tts">input latin letters</annotation>
+		<annotation cp="🅰">a | A button (blood type) | blood type</annotation>
+		<annotation cp="🅰" type="tts">A button (blood type)</annotation>
+		<annotation cp="🆎">ab | AB button (blood type) | blood type</annotation>
+		<annotation cp="🆎" type="tts">AB button (blood type)</annotation>
+		<annotation cp="🅱">b | B button (blood type) | blood type</annotation>
+		<annotation cp="🅱" type="tts">B button (blood type)</annotation>
+		<annotation cp="🆑">cl | CL button</annotation>
+		<annotation cp="🆑" type="tts">CL button</annotation>
+		<annotation cp="🆒">cool | COOL button</annotation>
+		<annotation cp="🆒" type="tts">COOL button</annotation>
+		<annotation cp="🆓">free | FREE button</annotation>
+		<annotation cp="🆓" type="tts">FREE button</annotation>
+		<annotation cp="ℹ">i | information</annotation>
+		<annotation cp="ℹ" type="tts">information</annotation>
+		<annotation cp="🆔">id | ID button | identity</annotation>
+		<annotation cp="🆔" type="tts">ID button</annotation>
+		<annotation cp="Ⓜ">circle | circled M | m</annotation>
+		<annotation cp="Ⓜ" type="tts">circled M</annotation>
+		<annotation cp="🆕">new | NEW button</annotation>
+		<annotation cp="🆕" type="tts">NEW button</annotation>
+		<annotation cp="🆖">ng | NG button</annotation>
+		<annotation cp="🆖" type="tts">NG button</annotation>
+		<annotation cp="🅾">blood type | o | O button (blood type)</annotation>
+		<annotation cp="🅾" type="tts">O button (blood type)</annotation>
+		<annotation cp="🆗">OK | OK button</annotation>
+		<annotation cp="🆗" type="tts">OK button</annotation>
+		<annotation cp="🅿">P button | parking</annotation>
+		<annotation cp="🅿" type="tts">P button</annotation>
+		<annotation cp="🆘">help | sos | SOS button</annotation>
+		<annotation cp="🆘" type="tts">SOS button</annotation>
+		<annotation cp="🆙">mark | up | UP! button</annotation>
+		<annotation cp="🆙" type="tts">UP! button</annotation>
+		<annotation cp="🆚">versus | vs | VS button</annotation>
+		<annotation cp="🆚" type="tts">VS button</annotation>
+		<annotation cp="🈁">“here” | Japanese | Japanese “here” button | katakana | ココ</annotation>
+		<annotation cp="🈁" type="tts">Japanese “here” button</annotation>
+		<annotation cp="🈂">“service charge” | Japanese | Japanese “service charge” button | katakana | サ</annotation>
+		<annotation cp="🈂" type="tts">Japanese “service charge” button</annotation>
+		<annotation cp="🈷">“monthly amount” | ideograph | Japanese | Japanese “monthly amount” button | 月</annotation>
+		<annotation cp="🈷" type="tts">Japanese “monthly amount” button</annotation>
+		<annotation cp="🈶">“not free of charge” | ideograph | Japanese | Japanese “not free of charge” button | 有</annotation>
+		<annotation cp="🈶" type="tts">Japanese “not free of charge” button</annotation>
+		<annotation cp="🈯">“reserved” | ideograph | Japanese | Japanese “reserved” button | 指</annotation>
+		<annotation cp="🈯" type="tts">Japanese “reserved” button</annotation>
+		<annotation cp="🉐">“bargain” | ideograph | Japanese | Japanese “bargain” button | 得</annotation>
+		<annotation cp="🉐" type="tts">Japanese “bargain” button</annotation>
+		<annotation cp="🈹">“discount” | ideograph | Japanese | Japanese “discount” button | 割</annotation>
+		<annotation cp="🈹" type="tts">Japanese “discount” button</annotation>
+		<annotation cp="🈚">“free of charge” | ideograph | Japanese | Japanese “free of charge” button | 無</annotation>
+		<annotation cp="🈚" type="tts">Japanese “free of charge” button</annotation>
+		<annotation cp="🈲">“prohibited” | ideograph | Japanese | Japanese “prohibited” button | 禁</annotation>
+		<annotation cp="🈲" type="tts">Japanese “prohibited” button</annotation>
+		<annotation cp="🉑">“acceptable” | ideograph | Japanese | Japanese “acceptable” button | 可</annotation>
+		<annotation cp="🉑" type="tts">Japanese “acceptable” button</annotation>
+		<annotation cp="🈸">“application” | ideograph | Japanese | Japanese “application” button | 申</annotation>
+		<annotation cp="🈸" type="tts">Japanese “application” button</annotation>
+		<annotation cp="🈴">“passing grade” | ideograph | Japanese | Japanese “passing grade” button | 合</annotation>
+		<annotation cp="🈴" type="tts">Japanese “passing grade” button</annotation>
+		<annotation cp="🈳">“vacancy” | ideograph | Japanese | Japanese “vacancy” button | 空</annotation>
+		<annotation cp="🈳" type="tts">Japanese “vacancy” button</annotation>
+		<annotation cp="㊗">“congratulations” | ideograph | Japanese | Japanese “congratulations” button | 祝</annotation>
+		<annotation cp="㊗" type="tts">Japanese “congratulations” button</annotation>
+		<annotation cp="㊙">“secret” | ideograph | Japanese | Japanese “secret” button | 秘</annotation>
+		<annotation cp="㊙" type="tts">Japanese “secret” button</annotation>
+		<annotation cp="🈺">“open for business” | ideograph | Japanese | Japanese “open for business” button | 営</annotation>
+		<annotation cp="🈺" type="tts">Japanese “open for business” button</annotation>
+		<annotation cp="🈵">“no vacancy” | ideograph | Japanese | Japanese “no vacancy” button | 満</annotation>
+		<annotation cp="🈵" type="tts">Japanese “no vacancy” button</annotation>
+		<annotation cp="🔴">circle | geometric | red</annotation>
+		<annotation cp="🔴" type="tts">red circle</annotation>
+		<annotation cp="🟠">circle | orange</annotation>
+		<annotation cp="🟠" type="tts">orange circle</annotation>
+		<annotation cp="🟡">circle | yellow</annotation>
+		<annotation cp="🟡" type="tts">yellow circle</annotation>
+		<annotation cp="🟢">circle | green</annotation>
+		<annotation cp="🟢" type="tts">green circle</annotation>
+		<annotation cp="🔵">blue | circle | geometric</annotation>
+		<annotation cp="🔵" type="tts">blue circle</annotation>
+		<annotation cp="🟣">circle | purple</annotation>
+		<annotation cp="🟣" type="tts">purple circle</annotation>
+		<annotation cp="🟤">brown | circle</annotation>
+		<annotation cp="🟤" type="tts">brown circle</annotation>
+		<annotation cp="⚫">black circle | circle | geometric</annotation>
+		<annotation cp="⚫" type="tts">black circle</annotation>
+		<annotation cp="⚪">circle | geometric | white circle</annotation>
+		<annotation cp="⚪" type="tts">white circle</annotation>
+		<annotation cp="🟥">red | square</annotation>
+		<annotation cp="🟥" type="tts">red square</annotation>
+		<annotation cp="🟧">orange | square</annotation>
+		<annotation cp="🟧" type="tts">orange square</annotation>
+		<annotation cp="🟨">square | yellow</annotation>
+		<annotation cp="🟨" type="tts">yellow square</annotation>
+		<annotation cp="🟩">green | square</annotation>
+		<annotation cp="🟩" type="tts">green square</annotation>
+		<annotation cp="🟦">blue | square</annotation>
+		<annotation cp="🟦" type="tts">blue square</annotation>
+		<annotation cp="🟪">purple | square</annotation>
+		<annotation cp="🟪" type="tts">purple square</annotation>
+		<annotation cp="🟫">brown | square</annotation>
+		<annotation cp="🟫" type="tts">brown square</annotation>
+		<annotation cp="⬛">black large square | geometric | square</annotation>
+		<annotation cp="⬛" type="tts">black large square</annotation>
+		<annotation cp="⬜">geometric | square | white large square</annotation>
+		<annotation cp="⬜" type="tts">white large square</annotation>
+		<annotation cp="◼">black medium square | geometric | square</annotation>
+		<annotation cp="◼" type="tts">black medium square</annotation>
+		<annotation cp="◻">geometric | square | white medium square</annotation>
+		<annotation cp="◻" type="tts">white medium square</annotation>
+		<annotation cp="◾">black medium-small square | geometric | square</annotation>
+		<annotation cp="◾" type="tts">black medium-small square</annotation>
+		<annotation cp="◽">geometric | square | white medium-small square</annotation>
+		<annotation cp="◽" type="tts">white medium-small square</annotation>
+		<annotation cp="▪">black small square | geometric | square</annotation>
+		<annotation cp="▪" type="tts">black small square</annotation>
+		<annotation cp="▫">geometric | square | white small square</annotation>
+		<annotation cp="▫" type="tts">white small square</annotation>
+		<annotation cp="🔶">diamond | geometric | large orange diamond | orange</annotation>
+		<annotation cp="🔶" type="tts">large orange diamond</annotation>
+		<annotation cp="🔷">blue | diamond | geometric | large blue diamond</annotation>
+		<annotation cp="🔷" type="tts">large blue diamond</annotation>
+		<annotation cp="🔸">diamond | geometric | orange | small orange diamond</annotation>
+		<annotation cp="🔸" type="tts">small orange diamond</annotation>
+		<annotation cp="🔹">blue | diamond | geometric | small blue diamond</annotation>
+		<annotation cp="🔹" type="tts">small blue diamond</annotation>
+		<annotation cp="🔺">geometric | red | red triangle pointed up</annotation>
+		<annotation cp="🔺" type="tts">red triangle pointed up</annotation>
+		<annotation cp="🔻">down | geometric | red | red triangle pointed down</annotation>
+		<annotation cp="🔻" type="tts">red triangle pointed down</annotation>
+		<annotation cp="💠">comic | diamond | diamond with a dot | geometric | inside</annotation>
+		<annotation cp="💠" type="tts">diamond with a dot</annotation>
+		<annotation cp="🔘">button | geometric | radio</annotation>
+		<annotation cp="🔘" type="tts">radio button</annotation>
+		<annotation cp="🔳">button | geometric | outlined | square | white square button</annotation>
+		<annotation cp="🔳" type="tts">white square button</annotation>
+		<annotation cp="🔲">black square button | button | geometric | square</annotation>
+		<annotation cp="🔲" type="tts">black square button</annotation>
+		<annotation cp="🏁">checkered | chequered | chequered flag | racing</annotation>
+		<annotation cp="🏁" type="tts">chequered flag</annotation>
+		<annotation cp="🚩">post | triangular flag</annotation>
+		<annotation cp="🚩" type="tts">triangular flag</annotation>
+		<annotation cp="🎌">celebration | cross | crossed | crossed flags | Japanese</annotation>
+		<annotation cp="🎌" type="tts">crossed flags</annotation>
+		<annotation cp="🏴">black flag | waving</annotation>
+		<annotation cp="🏴" type="tts">black flag</annotation>
+		<annotation cp="🏳">waving | white flag</annotation>
+		<annotation cp="🏳" type="tts">white flag</annotation>
+		<annotation cp="🏳‍🌈">pride | rainbow | rainbow flag</annotation>
+		<annotation cp="🏳‍🌈" type="tts">rainbow flag</annotation>
+		<annotation cp="🏳‍⚧">flag | light blue | pink | transgender | white</annotation>
+		<annotation cp="🏳‍⚧" type="tts">transgender flag</annotation>
+		<annotation cp="🏴‍☠">Jolly Roger | pirate | pirate flag | plunder | treasure</annotation>
+		<annotation cp="🏴‍☠" type="tts">pirate flag</annotation>
+		<annotation cp="¢">cent</annotation>
+		<annotation cp="¢" type="tts">cent</annotation>
+		<annotation cp="$">dollar | money | peso | USD</annotation>
+		<annotation cp="$" type="tts">dollar</annotation>
+		<annotation cp="£">currency | EGP | GBP | pound</annotation>
+		<annotation cp="£" type="tts">pound</annotation>
+		<annotation cp="¥">CNY | currency | JPY | yen | yuan</annotation>
+		<annotation cp="¥" type="tts">yen</annotation>
+		<annotation cp="₢">BRB | cruzeiro | currency</annotation>
+		<annotation cp="₢" type="tts">cruzeiro</annotation>
+		<annotation cp="₣">currency | franc | french franc</annotation>
+		<annotation cp="₣" type="tts">french franc</annotation>
+		<annotation cp="₤">currency | lira</annotation>
+		<annotation cp="₤" type="tts">lira</annotation>
+		<annotation cp="₥">mil | mill</annotation>
+		<annotation cp="₥" type="tts">mill</annotation>
+		<annotation cp="₩">KPW | KRW | won</annotation>
+		<annotation cp="₩" type="tts">won</annotation>
+		<annotation cp="€">currency | EUR | euro</annotation>
+		<annotation cp="€" type="tts">euro</annotation>
+		<annotation cp="₰">currency | german penny | pfennig</annotation>
+		<annotation cp="₰" type="tts">german penny</annotation>
+		<annotation cp="₱">peso</annotation>
+		<annotation cp="₱" type="tts">peso</annotation>
+		<annotation cp="₳">ARA | austral | currency</annotation>
+		<annotation cp="₳" type="tts">austral</annotation>
+		<annotation cp="₶">currency | livre tournois</annotation>
+		<annotation cp="₶" type="tts">livre tournois</annotation>
+		<annotation cp="₷">currency | spesmilo</annotation>
+		<annotation cp="₷" type="tts">spesmilo</annotation>
+		<annotation cp="₹">currency | indian rupee | rupee</annotation>
+		<annotation cp="₹" type="tts">indian rupee</annotation>
+		<annotation cp="₽">currency | ruble</annotation>
+		<annotation cp="₽" type="tts">ruble</annotation>
+		<annotation cp="₿">bitcoin | BTC</annotation>
+		<annotation cp="₿" type="tts">bitcoin</annotation>
+		<annotation cp="₨">currency | rupee</annotation>
+		<annotation cp="₨" type="tts">rupee</annotation>
+		<annotation cp="﷼">currency | rial</annotation>
+		<annotation cp="﷼" type="tts">rial</annotation>
+		<annotation cp="¹">one | superscript</annotation>
+		<annotation cp="¹" type="tts">superscript one</annotation>
+		<annotation cp="²">squared | superscript | two</annotation>
+		<annotation cp="²" type="tts">superscript two</annotation>
+		<annotation cp="³">cubed | superscript | three</annotation>
+		<annotation cp="³" type="tts">superscript three</annotation>
+		<annotation cp="µ">measure | micro sign</annotation>
+		<annotation cp="µ" type="tts">micro sign</annotation>
+	</annotations>
+</ldml>
diff --git a/third_party/cldr/src/common/annotations/en_001.xml b/third_party/cldr/src/common/annotations/en_001.xml
new file mode 100644
index 0000000..95da972
--- /dev/null
+++ b/third_party/cldr/src/common/annotations/en_001.xml
@@ -0,0 +1,533 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE ldml SYSTEM "../../common/dtd/ldml.dtd">
+<!-- Copyright © 1991-2020 Unicode, Inc.
+For terms of use, see http://www.unicode.org/copyright.html
+Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in the U.S. and other countries.
+CLDR data files are interpreted according to the LDML specification (http://unicode.org/reports/tr35/)
+
+Warnings: All cp values have U+FE0F characters removed. See /annotationsDerived/ for derived annotations.
+-->
+<ldml>
+	<identity>
+		<version number="$Revision$"/>
+		<language type="en"/>
+		<territory type="001"/>
+	</identity>
+	<annotations>
+		<annotation cp="_">dash | line | low dash | underdash | underline | underscore</annotation>
+		<annotation cp="_" type="tts">underscore</annotation>
+		<annotation cp="." type="tts">full stop</annotation>
+		<annotation cp="。">full stop | ideographic | ideographic full stop | period</annotation>
+		<annotation cp="。" type="tts">ideographic full stop</annotation>
+		<annotation cp="'">apostrophe | quote | single quote | straight apostrophe | typewriter apostrophe</annotation>
+		<annotation cp="'" type="tts">straight apostrophe</annotation>
+		<annotation cp="‘">apostrophe | curly quote | quote | single quote | smart quote</annotation>
+		<annotation cp="’">apostrophe | curly quote | quote | single quote | smart quote</annotation>
+		<annotation cp="“">curly quotes | double quote | quotation | quote | smart quotation</annotation>
+		<annotation cp="”">curly quotes | double quote | quotation | quote | smart quotation</annotation>
+		<annotation cp=")">bracket | parens | parenthesis | round bracket</annotation>
+		<annotation cp="【">bracket | lens bracket | lenticular bracket | open black lenticular bracket</annotation>
+		<annotation cp="【" type="tts">open black lenticular bracket</annotation>
+		<annotation cp="】">bracket | close black lenticular bracket | lens bracket | lenticular bracket</annotation>
+		<annotation cp="】" type="tts">close black lenticular bracket</annotation>
+		<annotation cp="〖">bracket | hollow lens bracket | hollow lenticular bracket | open hollow lenticular bracket</annotation>
+		<annotation cp="〖" type="tts">open hollow lenticular bracket</annotation>
+		<annotation cp="〗">bracket | close hollow lenticular bracket | hollow lens bracket | hollow lenticular bracket</annotation>
+		<annotation cp="〗" type="tts">close hollow lenticular bracket</annotation>
+		<annotation cp="@">ampersat | arobase | arroba | at mark | at sign | commercial at | strudel</annotation>
+		<annotation cp="@" type="tts">at sign</annotation>
+		<annotation cp="%" type="tts">per cent</annotation>
+		<annotation cp="‰">per mil | per mill | per mille | permil | permille</annotation>
+		<annotation cp="†">dagger | dagger sign | dagger symbol | obelisk | obelus</annotation>
+		<annotation cp="†" type="tts">dagger symbol</annotation>
+		<annotation cp="‡">dagger | double | double dagger sign | double dagger symbol | obelisk | obelus</annotation>
+		<annotation cp="‡" type="tts">double dagger symbol</annotation>
+		<annotation cp="^">accent | caret | chevron | circumflex | hat | power | wedge</annotation>
+		<annotation cp="↚" draft="contributed">leftwards arrow with stroke</annotation>
+		<annotation cp="↚" type="tts" draft="contributed">leftwards arrow with stroke</annotation>
+		<annotation cp="↛" draft="contributed">rightwards arrow with stroke</annotation>
+		<annotation cp="↛" type="tts" draft="contributed">rightwards arrow with stroke</annotation>
+		<annotation cp="↜" draft="contributed">arrow | left | wave arrow</annotation>
+		<annotation cp="↝" draft="contributed">arrow | right | wave arrow</annotation>
+		<annotation cp="↞" draft="contributed">leftwards two-headed arrow</annotation>
+		<annotation cp="↞" type="tts" draft="contributed">leftwards two-headed arrow</annotation>
+		<annotation cp="↟" draft="contributed">upwards two-headed arrow</annotation>
+		<annotation cp="↟" type="tts" draft="contributed">upwards two-headed arrow</annotation>
+		<annotation cp="↠" draft="contributed">rightwards two-headed arrow</annotation>
+		<annotation cp="↠" type="tts" draft="contributed">rightwards two-headed arrow</annotation>
+		<annotation cp="↡" draft="contributed">downwards two-headed arrow</annotation>
+		<annotation cp="↡" type="tts" draft="contributed">downwards two-headed arrow</annotation>
+		<annotation cp="↢">arrow | arrow with tail | left | leftwards arrow with tail</annotation>
+		<annotation cp="↢" type="tts">leftwards arrow with tail</annotation>
+		<annotation cp="↣" draft="contributed">rightwards arrow with tail</annotation>
+		<annotation cp="↣" type="tts" draft="contributed">rightwards arrow with tail</annotation>
+		<annotation cp="↤" draft="contributed">arrow | arrow from bar | left</annotation>
+		<annotation cp="↥" draft="contributed">arrow | arrow from bar | up</annotation>
+		<annotation cp="↦" draft="contributed">arrow | arrow from bar | right</annotation>
+		<annotation cp="↧" draft="contributed">arrow | arrow from bar | down</annotation>
+		<annotation cp="↨" draft="contributed">up-down arrow with base</annotation>
+		<annotation cp="↨" type="tts" draft="contributed">up-down arrow with base</annotation>
+		<annotation cp="↫" draft="contributed">leftwards arrow with loop</annotation>
+		<annotation cp="↫" type="tts" draft="contributed">leftwards arrow with loop</annotation>
+		<annotation cp="↬" draft="contributed">rightwards arrow with loop</annotation>
+		<annotation cp="↬" type="tts" draft="contributed">rightwards arrow with loop</annotation>
+		<annotation cp="↭" draft="contributed">left-right wave arrow</annotation>
+		<annotation cp="↭" type="tts" draft="contributed">left-right wave arrow</annotation>
+		<annotation cp="↰" draft="contributed">upwards arrow with leftwards tip</annotation>
+		<annotation cp="↰" type="tts" draft="contributed">upwards arrow with leftwards tip</annotation>
+		<annotation cp="↱" draft="contributed">upwards arrow with rightwards tip</annotation>
+		<annotation cp="↱" type="tts" draft="contributed">upwards arrow with rightwards tip</annotation>
+		<annotation cp="↲" draft="contributed">downwards arrow with leftwards tip</annotation>
+		<annotation cp="↲" type="tts" draft="contributed">downwards arrow with leftwards tip</annotation>
+		<annotation cp="↳" draft="contributed">downwards arrow with rightwards tip</annotation>
+		<annotation cp="↳" type="tts" draft="contributed">downwards arrow with rightwards tip</annotation>
+		<annotation cp="↴" draft="contributed">rightwards arrow with corner downwards</annotation>
+		<annotation cp="↴" type="tts" draft="contributed">rightwards arrow with corner downwards</annotation>
+		<annotation cp="↵" draft="contributed">downwards arrow with corner leftwards</annotation>
+		<annotation cp="↵" type="tts" draft="contributed">downwards arrow with corner leftwards</annotation>
+		<annotation cp="↶" draft="contributed">anticlockwise top-semicircle arrow</annotation>
+		<annotation cp="↶" type="tts" draft="contributed">anticlockwise top-semicircle arrow</annotation>
+		<annotation cp="↷" draft="contributed">clockwise top-semicircle arrow</annotation>
+		<annotation cp="↷" type="tts" draft="contributed">clockwise top-semicircle arrow</annotation>
+		<annotation cp="↸" draft="contributed">north-west arrow to long bar</annotation>
+		<annotation cp="↸" type="tts" draft="contributed">north-west arrow to long bar</annotation>
+		<annotation cp="↹" draft="contributed">arrow | arrow to bar | left | left arrow over right arrow | right</annotation>
+		<annotation cp="↺" draft="contributed">anticlockwise open-circle arrow</annotation>
+		<annotation cp="↺" type="tts" draft="contributed">anticlockwise open-circle arrow</annotation>
+		<annotation cp="↻" draft="contributed">clockwise open-circle arrow</annotation>
+		<annotation cp="↻" type="tts" draft="contributed">clockwise open-circle arrow</annotation>
+		<annotation cp="↼" draft="contributed">leftwards harpoon with upwards barb</annotation>
+		<annotation cp="↼" type="tts" draft="contributed">leftwards harpoon with upwards barb</annotation>
+		<annotation cp="↽" draft="contributed">leftwards harpoon with downwards barb</annotation>
+		<annotation cp="↽" type="tts" draft="contributed">leftwards harpoon with downwards barb</annotation>
+		<annotation cp="↾" draft="contributed">upwards harpoon with rightwards barb</annotation>
+		<annotation cp="↾" type="tts" draft="contributed">upwards harpoon with rightwards barb</annotation>
+		<annotation cp="↿" draft="contributed">upwards harpoon with leftwards barb</annotation>
+		<annotation cp="↿" type="tts" draft="contributed">upwards harpoon with leftwards barb</annotation>
+		<annotation cp="⇀" draft="contributed">rightwards harpoon with upwards barb</annotation>
+		<annotation cp="⇀" type="tts" draft="contributed">rightwards harpoon with upwards barb</annotation>
+		<annotation cp="⇁" draft="contributed">rightwards harpoon with downwards barb</annotation>
+		<annotation cp="⇁" type="tts" draft="contributed">rightwards harpoon with downwards barb</annotation>
+		<annotation cp="⇂" draft="contributed">downwards harpoon with rightwards barb</annotation>
+		<annotation cp="⇂" type="tts" draft="contributed">downwards harpoon with rightwards barb</annotation>
+		<annotation cp="⇃" draft="contributed">downwards harpoon with leftwards barb</annotation>
+		<annotation cp="⇃" type="tts" draft="contributed">downwards harpoon with leftwards barb</annotation>
+		<annotation cp="⇄" draft="contributed">arrow | left | right | rightwards arrow over leftwards arrow</annotation>
+		<annotation cp="⇇" draft="contributed">arrow | left | paired arrows</annotation>
+		<annotation cp="⇈" draft="contributed">arrow | paired arrows | up</annotation>
+		<annotation cp="⇉" draft="contributed">arrow | paired arrows | right</annotation>
+		<annotation cp="⇊" draft="contributed">arrow | down | paired arrows</annotation>
+		<annotation cp="⇋" draft="contributed">arrow | harpoon | left | leftwards harpoon over rightwards harpoon | right</annotation>
+		<annotation cp="⇌" draft="contributed">arrow | harpoon | left | right | rightwards harpoon over leftwards harpoon</annotation>
+		<annotation cp="⇐" draft="contributed">arrow | double arrow | left</annotation>
+		<annotation cp="⇍" draft="contributed">leftwards double arrow with stroke</annotation>
+		<annotation cp="⇍" type="tts" draft="contributed">leftwards double arrow with stroke</annotation>
+		<annotation cp="⇑" draft="contributed">arrow | double arrow | up</annotation>
+		<annotation cp="⇒" draft="contributed">arrow | double arrow | right</annotation>
+		<annotation cp="⇏" draft="contributed">rightwards double arrow with stroke</annotation>
+		<annotation cp="⇏" type="tts" draft="contributed">rightwards double arrow with stroke</annotation>
+		<annotation cp="⇓" draft="contributed">arrow | double arrow | down</annotation>
+		<annotation cp="⇔" draft="contributed">left-right double arrow</annotation>
+		<annotation cp="⇔" type="tts" draft="contributed">left-right double arrow</annotation>
+		<annotation cp="⇎" draft="contributed">left-right double arrow with stroke</annotation>
+		<annotation cp="⇎" type="tts" draft="contributed">left-right double arrow with stroke</annotation>
+		<annotation cp="⇖" draft="contributed">north-west double arrow</annotation>
+		<annotation cp="⇖" type="tts" draft="contributed">north-west double arrow</annotation>
+		<annotation cp="⇗" draft="contributed">north-east double arrow</annotation>
+		<annotation cp="⇗" type="tts" draft="contributed">north-east double arrow</annotation>
+		<annotation cp="⇘" draft="contributed">south-east double arrow</annotation>
+		<annotation cp="⇘" type="tts" draft="contributed">south-east double arrow</annotation>
+		<annotation cp="⇙" draft="contributed">south-west double arrow</annotation>
+		<annotation cp="⇙" type="tts" draft="contributed">south-west double arrow</annotation>
+		<annotation cp="⇚" draft="contributed">arrow | left | triple arrow</annotation>
+		<annotation cp="⇛" draft="contributed">arrow | right | triple arrow</annotation>
+		<annotation cp="⇜" draft="contributed">arrow | left | squiggle arrow</annotation>
+		<annotation cp="⇝" draft="contributed">arrow | right | squiggle arrow</annotation>
+		<annotation cp="⇞" draft="contributed">upwards arrow with double stroke</annotation>
+		<annotation cp="⇞" type="tts" draft="contributed">upwards arrow with double stroke</annotation>
+		<annotation cp="⇟" draft="contributed">downwards arrow with double stroke</annotation>
+		<annotation cp="⇟" type="tts" draft="contributed">downwards arrow with double stroke</annotation>
+		<annotation cp="⇠" draft="contributed">arrow | dashed arrow | left</annotation>
+		<annotation cp="⇡" draft="contributed">arrow | dashed arrow | up</annotation>
+		<annotation cp="⇢" draft="contributed">arrow | dashed arrow | right</annotation>
+		<annotation cp="⇣" draft="contributed">arrow | dashed arrow | down</annotation>
+		<annotation cp="⇤" draft="contributed">leftwards arrow to bar</annotation>
+		<annotation cp="⇤" type="tts" draft="contributed">leftwards arrow to bar</annotation>
+		<annotation cp="⇥" draft="contributed">rightwards arrow to bar</annotation>
+		<annotation cp="⇥" type="tts" draft="contributed">rightwards arrow to bar</annotation>
+		<annotation cp="⇦" draft="contributed">arrow | hollow arrow | left</annotation>
+		<annotation cp="⇧" draft="contributed">arrow | hollow arrow | up</annotation>
+		<annotation cp="⇨" draft="contributed">arrow | hollow arrow | right</annotation>
+		<annotation cp="⇩" draft="contributed">arrow | down | hollow arrow</annotation>
+		<annotation cp="⇪" draft="contributed">arrow | arrow from bar | hollow arrow | up</annotation>
+		<annotation cp="⇵" draft="contributed">downwards and upwards arrows</annotation>
+		<annotation cp="⇵" type="tts" draft="contributed">downwards and upwards arrows</annotation>
+		<annotation cp="|">bar | line | pipe | sheffer stroke | vertical bar</annotation>
+		<annotation cp="◐" draft="contributed">circle with left half filled</annotation>
+		<annotation cp="◐" type="tts" draft="contributed">circle with left half filled</annotation>
+		<annotation cp="◑" draft="contributed">circle with right half filled</annotation>
+		<annotation cp="◑" type="tts" draft="contributed">circle with right half filled</annotation>
+		<annotation cp="◒" draft="contributed">circle with lower half filled</annotation>
+		<annotation cp="◒" type="tts" draft="contributed">circle with lower half filled</annotation>
+		<annotation cp="◓" draft="contributed">circle with upper half filled</annotation>
+		<annotation cp="◓" type="tts" draft="contributed">circle with upper half filled</annotation>
+		<annotation cp="♪">eighth | music | note | quaver</annotation>
+		<annotation cp="♪" type="tts">quaver</annotation>
+		<annotation cp="😋">delicious | face | face savouring food | savouring | smile | yum</annotation>
+		<annotation cp="😋" type="tts">face savouring food</annotation>
+		<annotation cp="🤭">embarrassed | face with hand over mouth | oops | whoops</annotation>
+		<annotation cp="🤐">face | mouth | zip | zipper</annotation>
+		<annotation cp="🤨">distrust | face with raised eyebrow | sceptic | skeptic</annotation>
+		<annotation cp="😶‍🌫">absent-minded | face in clouds | face in the fog | head in clouds</annotation>
+		<annotation cp="😷">cold | doctor | face | face with medical mask | ill | mask | medicine | poorly | sick</annotation>
+		<annotation cp="🤒">face | face with thermometer | ill | poorly | sick | thermometer</annotation>
+		<annotation cp="🤕">bandage | face | face with head bandage | hurt | injury</annotation>
+		<annotation cp="🤕" type="tts">face with head bandage</annotation>
+		<annotation cp="🤧">bless you | face | gesundheit | sneeze</annotation>
+		<annotation cp="😵‍💫">dizzy | hypnotised | spiral | trouble | whoa</annotation>
+		<annotation cp="🤬">cursing | expletive | face with symbols on mouth | swearing</annotation>
+		<annotation cp="👻">creature | face | fairy tale | fantasy | ghost | monster | spectre</annotation>
+		<annotation cp="😼">cat | cat face with wry smile | face | ironic | smile | smirk | wry</annotation>
+		<annotation cp="💫">comic | dizzy | spinning | spinning stars | star</annotation>
+		<annotation cp="💬">balloon | bubble | comic | dialogue | speech</annotation>
+		<annotation cp="🗨">dialogue | speech</annotation>
+		<annotation cp="💭" type="tts">thought bubble</annotation>
+		<annotation cp="💤">comic | sleep | sleeping | sleepy | zzz</annotation>
+		<annotation cp="✌">hand | peace hand | peace sign | v | v sign | victory</annotation>
+		<annotation cp="🤘">finger | hand | horns | rock on</annotation>
+		<annotation cp="🤙">call | call-me hand | hand</annotation>
+		<annotation cp="🤙" type="tts">call-me hand</annotation>
+		<annotation cp="🙌">celebration | gesture | hand | hooray | raised | raising hands | woo hoo | yay</annotation>
+		<annotation cp="🫀">anatomical heart | cardiology | heart | organ | pulse</annotation>
+		<annotation cp="🧒">child | gender-neutral | toddler | young</annotation>
+		<annotation cp="🙇‍♂">apology | bowing | favour | gesture | man | sorry</annotation>
+		<annotation cp="🙇‍♀">apology | bowing | favour | gesture | sorry | woman</annotation>
+		<annotation cp="🧑‍🏫">instructor | lecturer | professor | teacher</annotation>
+		<annotation cp="🧑‍⚖">judge | law</annotation>
+		<annotation cp="👨‍🌾">farmer | gardener | man</annotation>
+		<annotation cp="👩‍🌾">farmer | gardener | woman</annotation>
+		<annotation cp="👨‍🔧">electrician | man | mechanic | plumber | tradesman | tradesperson</annotation>
+		<annotation cp="👩‍🔧">electrician | mechanic | plumber | tradesperson | tradeswoman | woman</annotation>
+		<annotation cp="🧑‍🚒">fire engine | fire truck | firefighter</annotation>
+		<annotation cp="👨‍🚒">fire engine | firefighter | fireman | man</annotation>
+		<annotation cp="👩‍🚒">fire engine | firefighter | firewoman | woman</annotation>
+		<annotation cp="👮‍♂">cop | man | officer | police | policeman</annotation>
+		<annotation cp="👮‍♀">cop | officer | police | policewoman | woman</annotation>
+		<annotation cp="💂‍♂">guard | guardsman | man</annotation>
+		<annotation cp="💂‍♀">guard | guardswoman | woman</annotation>
+		<annotation cp="👷">builder | construction | hat | worker</annotation>
+		<annotation cp="👷‍♂">builder | construction | man | worker</annotation>
+		<annotation cp="👷‍♀">builder | construction | woman | worker</annotation>
+		<annotation cp="👲">gua pi mao | hat | man | man with Chinese cap | skullcap</annotation>
+		<annotation cp="🤵">groom | person | person in tux | person in tuxedo | tuxedo</annotation>
+		<annotation cp="🤵‍♂">man | man in tux | man in tuxedo | tuxedo</annotation>
+		<annotation cp="🤵‍♀">tuxedo | woman | woman in tux | woman in tuxedo</annotation>
+		<annotation cp="🤱">baby | breast | breastfeeding | nursing</annotation>
+		<annotation cp="🤱" type="tts">breastfeeding</annotation>
+		<annotation cp="🎅">celebration | Christmas | claus | father | Father Christmas | santa | Santa Claus</annotation>
+		<annotation cp="🤶">celebration | Christmas | claus | mother | Mrs | Mrs Claus</annotation>
+		<annotation cp="🤶" type="tts">Mrs Claus</annotation>
+		<annotation cp="💇">barber | beauty | haircut | hairdresser | parlour</annotation>
+		<annotation cp="🧑‍🦯">accessibility | blind | person with guide cane</annotation>
+		<annotation cp="🧑‍🦯" type="tts">person with guide cane</annotation>
+		<annotation cp="👨‍🦯">accessibility | blind | man | man with guide cane</annotation>
+		<annotation cp="👨‍🦯" type="tts">man with guide cane</annotation>
+		<annotation cp="👩‍🦯">accessibility | blind | woman | woman with guide cane</annotation>
+		<annotation cp="👩‍🦯" type="tts">woman with guide cane</annotation>
+		<annotation cp="🧑‍🦼">accessibility | person in powered wheelchair | wheelchair</annotation>
+		<annotation cp="🧑‍🦼" type="tts">person in powered wheelchair</annotation>
+		<annotation cp="👨‍🦼">accessibility | man | man in powered wheelchair | wheelchair</annotation>
+		<annotation cp="👨‍🦼" type="tts">man in powered wheelchair</annotation>
+		<annotation cp="👩‍🦼">accessibility | wheelchair | woman | woman in powered wheelchair</annotation>
+		<annotation cp="👩‍🦼" type="tts">woman in powered wheelchair</annotation>
+		<annotation cp="🚣">boat | rowboat | rowing boat</annotation>
+		<annotation cp="🚣‍♂">boat | man | rowboat | rowing boat</annotation>
+		<annotation cp="🚣‍♀">boat | rowboat | rowing boat | woman</annotation>
+		<annotation cp="🏋">person lifting weights | weight | weightlifter</annotation>
+		<annotation cp="🏋‍♂">man | weightlifter</annotation>
+		<annotation cp="🏋‍♀">weightlifter | woman</annotation>
+		<annotation cp="🚴">bicycle | biking | cyclist | person cycling</annotation>
+		<annotation cp="🚴‍♂">bicycle | biking | cyclist | man cycling</annotation>
+		<annotation cp="🚴‍♀">bicycle | biking | cyclist | woman cycling</annotation>
+		<annotation cp="🗣">face | head | silhouette | speak | speaking | talk | talking</annotation>
+		<annotation cp="🦳">grey | hair | old | white</annotation>
+		<annotation cp="🐽">face | nose | pig | snout</annotation>
+		<annotation cp="🐪">camel | dromedary | hump | one hump | single hump</annotation>
+		<annotation cp="🐫">bactrian | camel | hump | two humps | two-hump camel</annotation>
+		<annotation cp="🐓">bird | cockerel | rooster</annotation>
+		<annotation cp="🐓" type="tts">cockerel</annotation>
+		<annotation cp="🐊">alligator | croc | crocodile</annotation>
+		<annotation cp="🦖">T-Rex | T. rex | T. Rex | Tyrannosaurus Rex</annotation>
+		<annotation cp="🦭">sea lion | seal</annotation>
+		<annotation cp="🐞">beetle | insect | ladybeetle | ladybird | ladybug</annotation>
+		<annotation cp="🐞" type="tts">ladybird</annotation>
+		<annotation cp="🕸">spider | spider’s web | web</annotation>
+		<annotation cp="🕸" type="tts">spider’s web</annotation>
+		<annotation cp="🌼">blossom | daisy | flower</annotation>
+		<annotation cp="🪴">grow | house | nurturing | plant | potted plant</annotation>
+		<annotation cp="🌾">ear of rice | grain | rice | sheaf</annotation>
+		<annotation cp="🌾" type="tts">ear of rice</annotation>
+		<annotation cp="🍀" type="tts">four-leaf clover</annotation>
+		<annotation cp="🍆" type="tts">aubergine</annotation>
+		<annotation cp="🌶">chilli | hot | pepper</annotation>
+		<annotation cp="🌶" type="tts">chilli</annotation>
+		<annotation cp="🫑">bell pepper | capsicum | pepper | sweet pepper | vegetable</annotation>
+		<annotation cp="🫑" type="tts">pepper</annotation>
+		<annotation cp="🥬">bok choy | cabbage | kale | leafy green | lettuce | pak choi</annotation>
+		<annotation cp="🧄">flavouring | garlic</annotation>
+		<annotation cp="🧅">flavouring | onion</annotation>
+		<annotation cp="🥜">food | monkey nut | nut | nuts | peanut | peanuts</annotation>
+		<annotation cp="🥖">baguette | bread | food | french | french stick</annotation>
+		<annotation cp="🥖" type="tts">baguette</annotation>
+		<annotation cp="🫓">arepa | lavash | naan | nan | pita | pitta</annotation>
+		<annotation cp="🍔">beefburger | burger | hamburger</annotation>
+		<annotation cp="🍔" type="tts">beefburger</annotation>
+		<annotation cp="🍟">chips | french fries | fries</annotation>
+		<annotation cp="🍟" type="tts">chips</annotation>
+		<annotation cp="🥫">can | canned food | tin | tinned food</annotation>
+		<annotation cp="🥫" type="tts">tinned food</annotation>
+		<annotation cp="🍤" type="tts">fried prawn</annotation>
+		<annotation cp="🥡">oyster pail | takeaway box | takeout box</annotation>
+		<annotation cp="🦐">food | prawn | shellfish | shrimp | small</annotation>
+		<annotation cp="🦑">food | mollusc | squid</annotation>
+		<annotation cp="🍪">biscuit | cookie | dessert | sweet</annotation>
+		<annotation cp="🍪" type="tts">biscuit</annotation>
+		<annotation cp="🍰">cake | dessert | pastry | slice | sweet</annotation>
+		<annotation cp="🍰" type="tts">cake</annotation>
+		<annotation cp="🥧">filling | pastry | pie | slice | tart</annotation>
+		<annotation cp="🍬">dessert | sweet | sweets</annotation>
+		<annotation cp="🍬" type="tts">sweets</annotation>
+		<annotation cp="🍭">dessert | lollipop | lolly | sweet</annotation>
+		<annotation cp="🍮">crème caramel | custard | dessert | egg custard | pudding | sweet</annotation>
+		<annotation cp="🍮" type="tts">egg custard</annotation>
+		<annotation cp="🍼">baby | baby’s bottle | bottle | drink | milk</annotation>
+		<annotation cp="🍶">bar | beverage | bottle | cup | drink | saké</annotation>
+		<annotation cp="🍾">bar | bottle | bottle with popping cork | champagne | cork | drink | popping</annotation>
+		<annotation cp="🍺">bar | beer | drink | mug | stein</annotation>
+		<annotation cp="🍻">bar | beer | clink | clinking beer mugs | drink | mug | steins</annotation>
+		<annotation cp="🥤">cup with straw | fizzy drink | juice | soft drink</annotation>
+		<annotation cp="🧉">drink | maté</annotation>
+		<annotation cp="🧉" type="tts">maté</annotation>
+		<annotation cp="🍽">cooking | fork | knife | knife and fork with plate | plate</annotation>
+		<annotation cp="🍽" type="tts">knife and fork with plate</annotation>
+		<annotation cp="🍴">cooking | cutlery | fork | knife | knife and fork</annotation>
+		<annotation cp="🍴" type="tts">knife and fork</annotation>
+		<annotation cp="🥄">cutlery | spoon | tableware</annotation>
+		<annotation cp="🔪">cooking | cutlery | hocho | kitchen knife | knife | tool | weapon</annotation>
+		<annotation cp="🏺">amphora | Aquarius | drink | jar | jug | zodiac</annotation>
+		<annotation cp="🏕">camping | tent</annotation>
+		<annotation cp="🏖">beach | beach with umbrella | parasol | umbrella</annotation>
+		<annotation cp="🏚">derelict | dilapidated | house</annotation>
+		<annotation cp="🏪">convenience | shop | store</annotation>
+		<annotation cp="🎠">carousel | horse | merry-go-round</annotation>
+		<annotation cp="🎡">amusement park | ferris | theme park | wheel</annotation>
+		<annotation cp="🎢">amusement park | coaster | roller | rollercoaster | theme park</annotation>
+		<annotation cp="🎢" type="tts">rollercoaster</annotation>
+		<annotation cp="💈">barber | barber’s pole | haircut | pole</annotation>
+		<annotation cp="💈" type="tts">barber’s pole</annotation>
+		<annotation cp="🚃">car | electric | railway | railway carriage | train | tram | trolleybus</annotation>
+		<annotation cp="🚃" type="tts">railway carriage</annotation>
+		<annotation cp="🚄">high-speed train | railway | shinkansen | speed | TGV | train</annotation>
+		<annotation cp="🚇">metro | subway | tube | underground</annotation>
+		<annotation cp="🚕">cab | taxi | vehicle</annotation>
+		<annotation cp="🚖">cab | oncoming | taxi</annotation>
+		<annotation cp="🚗" type="tts">car</annotation>
+		<annotation cp="🚘" type="tts">oncoming car</annotation>
+		<annotation cp="🚙">4x4 | off-road vehicle | sport utility</annotation>
+		<annotation cp="🚚">delivery | delivery van | truck</annotation>
+		<annotation cp="🚚" type="tts">delivery van</annotation>
+		<annotation cp="🏎">car | motor racing | racing</annotation>
+		<annotation cp="🏍">motorbike | racing</annotation>
+		<annotation cp="🦼">accessibility | powered wheelchair</annotation>
+		<annotation cp="🦼" type="tts">powered wheelchair</annotation>
+		<annotation cp="🛺">auto rickshaw | tuk tuk | tuk-tuk</annotation>
+		<annotation cp="🚲">bicycle | bike | cycle</annotation>
+		<annotation cp="⛽">diesel | fuel | fuelpump | gas | petrol | pump | station</annotation>
+		<annotation cp="⛵">boat | resort | sailboat | sailing | sea | yacht</annotation>
+		<annotation cp="⛵" type="tts">sailing boat</annotation>
+		<annotation cp="🛳">cruise | liner | passenger | ship</annotation>
+		<annotation cp="✈" type="tts">aeroplane</annotation>
+		<annotation cp="🛩">aeroplane | airplane | small aeroplane | small airplane</annotation>
+		<annotation cp="🛩" type="tts">small aeroplane</annotation>
+		<annotation cp="🛫">aeroplane | airplane | check-in | departure | take-off</annotation>
+		<annotation cp="🛫" type="tts">aeroplane departure</annotation>
+		<annotation cp="🛬">aeroplane | aeroplane arrival | airplane | airplane arrival | arrivals | arriving | landing</annotation>
+		<annotation cp="🛬" type="tts">aeroplane arrival</annotation>
+		<annotation cp="🪂">hang-glide | parachute | parasail | parascend | skydive</annotation>
+		<annotation cp="🚁">chopper | helicopter | vehicle</annotation>
+		<annotation cp="🚠">cable | gondola | mountain | mountain cablecar</annotation>
+		<annotation cp="🕰">clock | mantel clock</annotation>
+		<annotation cp="🕧">12:30 | 12.30 | clock | half past twelve | twelve-thirty</annotation>
+		<annotation cp="🕧" type="tts">half past twelve</annotation>
+		<annotation cp="🕜">1:30 | 1.30 | clock | half past one | one-thirty</annotation>
+		<annotation cp="🕜" type="tts">half past one</annotation>
+		<annotation cp="🕝">2:30 | 2.30 | clock | half past two | two-thirty</annotation>
+		<annotation cp="🕝" type="tts">half past two</annotation>
+		<annotation cp="🕞">3:30 | 3.30 | clock | half past three | three-thirty</annotation>
+		<annotation cp="🕞" type="tts">half past three</annotation>
+		<annotation cp="🕟">4:30 | 4.30 | clock | four-thirty | half past four</annotation>
+		<annotation cp="🕟" type="tts">half past four</annotation>
+		<annotation cp="🕠">5:30 | 5.30 | clock | five-thirty | half past five</annotation>
+		<annotation cp="🕠" type="tts">half past five</annotation>
+		<annotation cp="🕡">6:30 | 6.30 | clock | half past six | six-thirty</annotation>
+		<annotation cp="🕡" type="tts">half past six</annotation>
+		<annotation cp="🕢">7:30 | 7.30 | clock | half past seven | seven-thirty</annotation>
+		<annotation cp="🕢" type="tts">half past seven</annotation>
+		<annotation cp="🕣">8:30 | 8.30 | clock | eight-thirty | half past eight</annotation>
+		<annotation cp="🕣" type="tts">half past eight</annotation>
+		<annotation cp="🕤">9:30 | 9.30 | clock | half past nine | nine-thirty</annotation>
+		<annotation cp="🕤" type="tts">half past nine</annotation>
+		<annotation cp="🕥">10:30 | 10.30 | clock | half past ten | ten-thirty</annotation>
+		<annotation cp="🕥" type="tts">half past ten</annotation>
+		<annotation cp="🕦">11:30 | 11.30 | clock | eleven-thirty | half past eleven</annotation>
+		<annotation cp="🕦" type="tts">half past eleven</annotation>
+		<annotation cp="🌓" draft="contributed">first-quarter moon</annotation>
+		<annotation cp="🌓" type="tts" draft="contributed">first-quarter moon</annotation>
+		<annotation cp="🌗" draft="contributed">last-quarter moon</annotation>
+		<annotation cp="🌗" type="tts" draft="contributed">last-quarter moon</annotation>
+		<annotation cp="🌛" draft="contributed">first-quarter moon face</annotation>
+		<annotation cp="🌛" type="tts" draft="contributed">first-quarter moon face</annotation>
+		<annotation cp="🌜" draft="contributed">last-quarter moon face</annotation>
+		<annotation cp="🌜" type="tts" draft="contributed">last-quarter moon face</annotation>
+		<annotation cp="⭐">star | yellow medium star</annotation>
+		<annotation cp="🌌">galaxy | milky way | space</annotation>
+		<annotation cp="🌪">cloud | tornado | twister | whirlwind</annotation>
+		<annotation cp="🌀">cyclone | dizzy | hurricane | typhoon</annotation>
+		<annotation cp="🌈">pride | rain | rainbow</annotation>
+		<annotation cp="💧">cold | drop | droplet | sweat</annotation>
+		<annotation cp="🌊">ocean | sea | swell | water | wave</annotation>
+		<annotation cp="🎃">celebration | halloween | jack | jack-o’-lantern | lantern | pumpkin</annotation>
+		<annotation cp="🎃" type="tts">jack-o’-lantern</annotation>
+		<annotation cp="🎋">banner | celebration | Japanese | star festival | tanabata | tree</annotation>
+		<annotation cp="🎏">carp | carp wind sock | celebration | Japanese wind socks | koinobori | streamer</annotation>
+		<annotation cp="🎑">celebration | ceremony | jugoya | moon | moon-viewing ceremony | otsukimi | tsukimi</annotation>
+		<annotation cp="🎗">awareness ribbon | celebration | reminder | ribbon</annotation>
+		<annotation cp="⚽" draft="contributed">football</annotation>
+		<annotation cp="⚽" type="tts" draft="contributed">football</annotation>
+		<annotation cp="🎾">ball | racket | racquet | tennis</annotation>
+		<annotation cp="🎳">ball | bowling | game | pins</annotation>
+		<annotation cp="🏏">ball | bat | cricket ball | cricket bat | cricket game | cricket match | game</annotation>
+		<annotation cp="🏏" type="tts">cricket match</annotation>
+		<annotation cp="🏑" draft="contributed">hockey</annotation>
+		<annotation cp="🏑" type="tts" draft="contributed">hockey</annotation>
+		<annotation cp="🏓" draft="contributed">table tennis</annotation>
+		<annotation cp="🏓" type="tts" draft="contributed">table tennis</annotation>
+		<annotation cp="🎣">angling | fish | fishing pole | pole</annotation>
+		<annotation cp="🤿" draft="contributed">diving | scuba | snorkelling</annotation>
+		<annotation cp="🛷" type="tts">sledge</annotation>
+		<annotation cp="🎯">bull | bullseye | dart | eye | game | hit | target</annotation>
+		<annotation cp="🪄">magic | wand | witch | wizard</annotation>
+		<annotation cp="🎰">fruit machine | game | one-armed bandit | slot | slot machine</annotation>
+		<annotation cp="🎲" type="tts">game dice</annotation>
+		<annotation cp="🪆">babushka | doll | matryoshka | nesting dolls | russia | Russian dolls</annotation>
+		<annotation cp="🪆" type="tts">Russian dolls</annotation>
+		<annotation cp="🎴">card | flower | flower playing cards | game | hanafuda | Japanese | playing</annotation>
+		<annotation cp="👓">clothing | eye | eyeglasses | eyewear | specs | spectacles</annotation>
+		<annotation cp="🦺">emergency | hi-vis | high-vis | jacket | life jacket | safety | vest</annotation>
+		<annotation cp="👔" type="tts">tie</annotation>
+		<annotation cp="👕">clothing | shirt | T-shirt | tshirt</annotation>
+		<annotation cp="👕" type="tts">T-shirt</annotation>
+		<annotation cp="👖">clothing | trousers</annotation>
+		<annotation cp="🩲" draft="contributed">bathing suit | briefs | one-piece | pants | swimsuit | underwear</annotation>
+		<annotation cp="🩳" draft="contributed">bathing suit | boardshorts | shorts | swim shorts | underwear</annotation>
+		<annotation cp="🎒" type="tts">school bag</annotation>
+		<annotation cp="🩴">beach sandals | flip-flop | flipflop | sandals | thong sandals | thongs | zori</annotation>
+		<annotation cp="🩴" type="tts">flip-flop</annotation>
+		<annotation cp="👟">athletic | clothing | shoe | trainer</annotation>
+		<annotation cp="👠">clothing | heel | shoe | stiletto | woman</annotation>
+		<annotation cp="🎓">cap | celebration | clothing | graduation | hat | mortarboard</annotation>
+		<annotation cp="📢">loud | loudhailer | public address</annotation>
+		<annotation cp="📢" type="tts">loudhailer</annotation>
+		<annotation cp="🎧">headphone | headphones</annotation>
+		<annotation cp="🎧" type="tts">headphones</annotation>
+		<annotation cp="🪘" type="tts">conga drum</annotation>
+		<annotation cp="📠">facsimile | fax | fax machine</annotation>
+		<annotation cp="🎥">camera | cinema | film | movie</annotation>
+		<annotation cp="🎥" type="tts">film camera</annotation>
+		<annotation cp="🎬">clapper | clapperboard | movie</annotation>
+		<annotation cp="🎬" type="tts">clapperboard</annotation>
+		<annotation cp="📺">tele | television | telly | tv | video</annotation>
+		<annotation cp="📹">camcorder | camera | video</annotation>
+		<annotation cp="🔦">electric | light | tool | torch</annotation>
+		<annotation cp="🔦" type="tts">torch</annotation>
+		<annotation cp="🏷">label | tag</annotation>
+		<annotation cp="💷">bank | banknote | bill | currency | money | note | pound | sterling</annotation>
+		<annotation cp="📧">email | letter | mail</annotation>
+		<annotation cp="📧" type="tts">email</annotation>
+		<annotation cp="📫">closed | closed mailbox with raised flag | closed postbox with raised flag | letterbox | mail | mailbox | post | post box | postbox</annotation>
+		<annotation cp="📪">closed | closed mailbox with lowered flag | closed postbox with lowered flag | letterbox | lowered | mail | mailbox | post | post box | postbox</annotation>
+		<annotation cp="📬">mail | mailbox | open | open mailbox with raised flag | open postbox with raised flag | post | post box | postbox</annotation>
+		<annotation cp="📭">lowered | mail | mailbox | open | open mailbox with lowered flag | open postbox with lowered flag | post | post box | postbox</annotation>
+		<annotation cp="📌">map pin | pin | pushpin</annotation>
+		<annotation cp="📐">ruler | set | set square | triangle</annotation>
+		<annotation cp="📐" type="tts">set square</annotation>
+		<annotation cp="🗑">paper bin | wastebasket | wastepaper basket</annotation>
+		<annotation cp="🗑" type="tts">wastepaper basket</annotation>
+		<annotation cp="⛏">mining | pick | pickaxe | tool</annotation>
+		<annotation cp="⚒">hammer | hammer and pick | hammer and pickaxe | pick | tool</annotation>
+		<annotation cp="🛠" draft="contributed">hammer and spanner</annotation>
+		<annotation cp="🛠" type="tts" draft="contributed">hammer and spanner</annotation>
+		<annotation cp="🔫" draft="contributed">water pistol</annotation>
+		<annotation cp="🪚">carpenter | lumber | saw | timber | tool</annotation>
+		<annotation cp="🔧" draft="contributed">spanner</annotation>
+		<annotation cp="🔧" type="tts" draft="contributed">spanner</annotation>
+		<annotation cp="🦯" draft="contributed">guide cane</annotation>
+		<annotation cp="🦯" type="tts" draft="contributed">guide cane</annotation>
+		<annotation cp="🪜">climb | ladder | rung | stair | step</annotation>
+		<annotation cp="💉">ill | injection | medicine | needle | syringe</annotation>
+		<annotation cp="💊">capsule | doctor | medicine | pill | sick | tablet</annotation>
+		<annotation cp="🩹" draft="contributed">plaster</annotation>
+		<annotation cp="🩹" type="tts" draft="contributed">plaster</annotation>
+		<annotation cp="🛗" type="tts">lift</annotation>
+		<annotation cp="🪞">looking glass | reflection | reflector</annotation>
+		<annotation cp="🛋">couch | hotel | lamp | sofa | sofa and lamp</annotation>
+		<annotation cp="🛋" type="tts">sofa and lamp</annotation>
+		<annotation cp="🚽">lavatory | loo | toilet</annotation>
+		<annotation cp="🛁">bath | bathtub | tub</annotation>
+		<annotation cp="🛁" type="tts">bath</annotation>
+		<annotation cp="🪤" type="tts">mousetrap</annotation>
+		<annotation cp="🪒" draft="contributed">razor</annotation>
+		<annotation cp="🧴">lotion | lotion bottle | moisturiser | shampoo | sunscreen</annotation>
+		<annotation cp="🧷">nappy | punk rock | safety pin</annotation>
+		<annotation cp="🧻">paper towels | roll of paper | toilet paper | toilet roll</annotation>
+		<annotation cp="🛒" draft="contributed">shopping trolley</annotation>
+		<annotation cp="🛒" type="tts" draft="contributed">shopping trolley</annotation>
+		<annotation cp="🏧">atm | ATM sign | automated | bank | cashpoint | teller</annotation>
+		<annotation cp="🚮">litter | litter bin | litter in bin sign | rubbish</annotation>
+		<annotation cp="🚰" draft="contributed">drinking water</annotation>
+		<annotation cp="🚰" type="tts" draft="contributed">drinking water</annotation>
+		<annotation cp="🚹">lavatory | man | men’s toilet | restroom | wc</annotation>
+		<annotation cp="🚹" type="tts">men’s toilet</annotation>
+		<annotation cp="🚺">lavatory | restroom | wc | woman | women’s toilet</annotation>
+		<annotation cp="🚺" type="tts">women’s toilet</annotation>
+		<annotation cp="🚻">lavatory | restroom | toilets | WC</annotation>
+		<annotation cp="🚻" type="tts">toilets</annotation>
+		<annotation cp="🚱" draft="contributed">non-drinking water</annotation>
+		<annotation cp="🚱" type="tts" draft="contributed">non-drinking water</annotation>
+		<annotation cp="↮" draft="contributed">left-right arrow with stroke</annotation>
+		<annotation cp="↮" type="tts" draft="contributed">left-right arrow with stroke</annotation>
+		<annotation cp="🔄" draft="contributed">anticlockwise arrows button</annotation>
+		<annotation cp="🔄" type="tts" draft="contributed">anticlockwise arrows button</annotation>
+		<annotation cp="🕉">aum | Hindu | om | religion</annotation>
+		<annotation cp="☸">Buddhist | dharma | dharmachakra | religion | wheel | wheel of dharma</annotation>
+		<annotation cp="⏪" draft="contributed">fast-reverse button</annotation>
+		<annotation cp="⏪" type="tts" draft="contributed">fast-reverse button</annotation>
+		<annotation cp="⚧">trans | transgender | transgender symbol</annotation>
+		<annotation cp="➕">+ | math | maths | plus | sign</annotation>
+		<annotation cp="➖">- | − | math | maths | minus | sign</annotation>
+		<annotation cp="➗">÷ | divide | division | heavy division sign | math | maths</annotation>
+		<annotation cp="✅">✓ | button | mark | tick</annotation>
+		<annotation cp="✅" type="tts">tick button</annotation>
+		<annotation cp="☑">✓ | box | tick | tick box with tick</annotation>
+		<annotation cp="☑" type="tts">tick box with tick</annotation>
+		<annotation cp="✔">✓ | mark | tick</annotation>
+		<annotation cp="✔" type="tts">tick</annotation>
+		<annotation cp="™">mark | tm | trademark</annotation>
+		<annotation cp="™" type="tts">trademark</annotation>
+		<annotation cp="🏳‍⚧">flag | light blue | pink | trans | transgender | white</annotation>
+		<annotation cp="£">currency | EGP | GBP | pound | quid | sterling</annotation>
+		<annotation cp="₽">currency | rouble | ruble</annotation>
+		<annotation cp="₽" type="tts">rouble</annotation>
+		<annotation cp="µ">measure | micro sign | mu</annotation>
+	</annotations>
+</ldml>
diff --git a/third_party/cldr/src/common/annotationsDerived/en.xml b/third_party/cldr/src/common/annotationsDerived/en.xml
new file mode 100644
index 0000000..f9cdaa9
--- /dev/null
+++ b/third_party/cldr/src/common/annotationsDerived/en.xml
@@ -0,0 +1,4071 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE ldml SYSTEM "../../common/dtd/ldml.dtd">
+<!-- Copyright © 1991-2020 Unicode, Inc.
+For terms of use, see http://www.unicode.org/copyright.html
+Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in the U.S. and other countries.
+CLDR data files are interpreted according to the LDML specification (http://unicode.org/reports/tr35/)
+
+Derived short names and annotations, using GenerateDerivedAnnotations.java. See warnings in /annotations/ file.
+-->
+<ldml>
+	<identity>
+		<version number="$Revision$"/>
+		<language type="en"/>
+	</identity>
+	<annotations>
+		<annotation cp="👋🏻">hand | light skin tone | wave | waving</annotation>
+		<annotation cp="👋🏻" type="tts">waving hand: light skin tone</annotation>
+		<annotation cp="👋🏼">hand | medium-light skin tone | wave | waving</annotation>
+		<annotation cp="👋🏼" type="tts">waving hand: medium-light skin tone</annotation>
+		<annotation cp="👋🏽">hand | medium skin tone | wave | waving</annotation>
+		<annotation cp="👋🏽" type="tts">waving hand: medium skin tone</annotation>
+		<annotation cp="👋🏾">hand | medium-dark skin tone | wave | waving</annotation>
+		<annotation cp="👋🏾" type="tts">waving hand: medium-dark skin tone</annotation>
+		<annotation cp="👋🏿">dark skin tone | hand | wave | waving</annotation>
+		<annotation cp="👋🏿" type="tts">waving hand: dark skin tone</annotation>
+		<annotation cp="🤚🏻">backhand | light skin tone | raised | raised back of hand</annotation>
+		<annotation cp="🤚🏻" type="tts">raised back of hand: light skin tone</annotation>
+		<annotation cp="🤚🏼">backhand | medium-light skin tone | raised | raised back of hand</annotation>
+		<annotation cp="🤚🏼" type="tts">raised back of hand: medium-light skin tone</annotation>
+		<annotation cp="🤚🏽">backhand | medium skin tone | raised | raised back of hand</annotation>
+		<annotation cp="🤚🏽" type="tts">raised back of hand: medium skin tone</annotation>
+		<annotation cp="🤚🏾">backhand | medium-dark skin tone | raised | raised back of hand</annotation>
+		<annotation cp="🤚🏾" type="tts">raised back of hand: medium-dark skin tone</annotation>
+		<annotation cp="🤚🏿">backhand | dark skin tone | raised | raised back of hand</annotation>
+		<annotation cp="🤚🏿" type="tts">raised back of hand: dark skin tone</annotation>
+		<annotation cp="🖐🏻">finger | hand | hand with fingers splayed | light skin tone | splayed</annotation>
+		<annotation cp="🖐🏻" type="tts">hand with fingers splayed: light skin tone</annotation>
+		<annotation cp="🖐🏼">finger | hand | hand with fingers splayed | medium-light skin tone | splayed</annotation>
+		<annotation cp="🖐🏼" type="tts">hand with fingers splayed: medium-light skin tone</annotation>
+		<annotation cp="🖐🏽">finger | hand | hand with fingers splayed | medium skin tone | splayed</annotation>
+		<annotation cp="🖐🏽" type="tts">hand with fingers splayed: medium skin tone</annotation>
+		<annotation cp="🖐🏾">finger | hand | hand with fingers splayed | medium-dark skin tone | splayed</annotation>
+		<annotation cp="🖐🏾" type="tts">hand with fingers splayed: medium-dark skin tone</annotation>
+		<annotation cp="🖐🏿">dark skin tone | finger | hand | hand with fingers splayed | splayed</annotation>
+		<annotation cp="🖐🏿" type="tts">hand with fingers splayed: dark skin tone</annotation>
+		<annotation cp="✋🏻">hand | high 5 | high five | light skin tone | raised hand</annotation>
+		<annotation cp="✋🏻" type="tts">raised hand: light skin tone</annotation>
+		<annotation cp="✋🏼">hand | high 5 | high five | medium-light skin tone | raised hand</annotation>
+		<annotation cp="✋🏼" type="tts">raised hand: medium-light skin tone</annotation>
+		<annotation cp="✋🏽">hand | high 5 | high five | medium skin tone | raised hand</annotation>
+		<annotation cp="✋🏽" type="tts">raised hand: medium skin tone</annotation>
+		<annotation cp="✋🏾">hand | high 5 | high five | medium-dark skin tone | raised hand</annotation>
+		<annotation cp="✋🏾" type="tts">raised hand: medium-dark skin tone</annotation>
+		<annotation cp="✋🏿">dark skin tone | hand | high 5 | high five | raised hand</annotation>
+		<annotation cp="✋🏿" type="tts">raised hand: dark skin tone</annotation>
+		<annotation cp="🖖🏻">finger | hand | light skin tone | spock | vulcan | vulcan salute</annotation>
+		<annotation cp="🖖🏻" type="tts">vulcan salute: light skin tone</annotation>
+		<annotation cp="🖖🏼">finger | hand | medium-light skin tone | spock | vulcan | vulcan salute</annotation>
+		<annotation cp="🖖🏼" type="tts">vulcan salute: medium-light skin tone</annotation>
+		<annotation cp="🖖🏽">finger | hand | medium skin tone | spock | vulcan | vulcan salute</annotation>
+		<annotation cp="🖖🏽" type="tts">vulcan salute: medium skin tone</annotation>
+		<annotation cp="🖖🏾">finger | hand | medium-dark skin tone | spock | vulcan | vulcan salute</annotation>
+		<annotation cp="🖖🏾" type="tts">vulcan salute: medium-dark skin tone</annotation>
+		<annotation cp="🖖🏿">dark skin tone | finger | hand | spock | vulcan | vulcan salute</annotation>
+		<annotation cp="🖖🏿" type="tts">vulcan salute: dark skin tone</annotation>
+		<annotation cp="👌🏻">hand | light skin tone | OK</annotation>
+		<annotation cp="👌🏻" type="tts">OK hand: light skin tone</annotation>
+		<annotation cp="👌🏼">hand | medium-light skin tone | OK</annotation>
+		<annotation cp="👌🏼" type="tts">OK hand: medium-light skin tone</annotation>
+		<annotation cp="👌🏽">hand | medium skin tone | OK</annotation>
+		<annotation cp="👌🏽" type="tts">OK hand: medium skin tone</annotation>
+		<annotation cp="👌🏾">hand | medium-dark skin tone | OK</annotation>
+		<annotation cp="👌🏾" type="tts">OK hand: medium-dark skin tone</annotation>
+		<annotation cp="👌🏿">dark skin tone | hand | OK</annotation>
+		<annotation cp="👌🏿" type="tts">OK hand: dark skin tone</annotation>
+		<annotation cp="🤌🏻">fingers | hand gesture | interrogation | light skin tone | pinched | sarcastic</annotation>
+		<annotation cp="🤌🏻" type="tts">pinched fingers: light skin tone</annotation>
+		<annotation cp="🤌🏼">fingers | hand gesture | interrogation | medium-light skin tone | pinched | sarcastic</annotation>
+		<annotation cp="🤌🏼" type="tts">pinched fingers: medium-light skin tone</annotation>
+		<annotation cp="🤌🏽">fingers | hand gesture | interrogation | medium skin tone | pinched | sarcastic</annotation>
+		<annotation cp="🤌🏽" type="tts">pinched fingers: medium skin tone</annotation>
+		<annotation cp="🤌🏾">fingers | hand gesture | interrogation | medium-dark skin tone | pinched | sarcastic</annotation>
+		<annotation cp="🤌🏾" type="tts">pinched fingers: medium-dark skin tone</annotation>
+		<annotation cp="🤌🏿">dark skin tone | fingers | hand gesture | interrogation | pinched | sarcastic</annotation>
+		<annotation cp="🤌🏿" type="tts">pinched fingers: dark skin tone</annotation>
+		<annotation cp="🤏🏻">light skin tone | pinching hand | small amount</annotation>
+		<annotation cp="🤏🏻" type="tts">pinching hand: light skin tone</annotation>
+		<annotation cp="🤏🏼">medium-light skin tone | pinching hand | small amount</annotation>
+		<annotation cp="🤏🏼" type="tts">pinching hand: medium-light skin tone</annotation>
+		<annotation cp="🤏🏽">medium skin tone | pinching hand | small amount</annotation>
+		<annotation cp="🤏🏽" type="tts">pinching hand: medium skin tone</annotation>
+		<annotation cp="🤏🏾">medium-dark skin tone | pinching hand | small amount</annotation>
+		<annotation cp="🤏🏾" type="tts">pinching hand: medium-dark skin tone</annotation>
+		<annotation cp="🤏🏿">dark skin tone | pinching hand | small amount</annotation>
+		<annotation cp="🤏🏿" type="tts">pinching hand: dark skin tone</annotation>
+		<annotation cp="✌🏻">hand | light skin tone | v | victory</annotation>
+		<annotation cp="✌🏻" type="tts">victory hand: light skin tone</annotation>
+		<annotation cp="✌🏼">hand | medium-light skin tone | v | victory</annotation>
+		<annotation cp="✌🏼" type="tts">victory hand: medium-light skin tone</annotation>
+		<annotation cp="✌🏽">hand | medium skin tone | v | victory</annotation>
+		<annotation cp="✌🏽" type="tts">victory hand: medium skin tone</annotation>
+		<annotation cp="✌🏾">hand | medium-dark skin tone | v | victory</annotation>
+		<annotation cp="✌🏾" type="tts">victory hand: medium-dark skin tone</annotation>
+		<annotation cp="✌🏿">dark skin tone | hand | v | victory</annotation>
+		<annotation cp="✌🏿" type="tts">victory hand: dark skin tone</annotation>
+		<annotation cp="🤞🏻">cross | crossed fingers | finger | hand | light skin tone | luck</annotation>
+		<annotation cp="🤞🏻" type="tts">crossed fingers: light skin tone</annotation>
+		<annotation cp="🤞🏼">cross | crossed fingers | finger | hand | luck | medium-light skin tone</annotation>
+		<annotation cp="🤞🏼" type="tts">crossed fingers: medium-light skin tone</annotation>
+		<annotation cp="🤞🏽">cross | crossed fingers | finger | hand | luck | medium skin tone</annotation>
+		<annotation cp="🤞🏽" type="tts">crossed fingers: medium skin tone</annotation>
+		<annotation cp="🤞🏾">cross | crossed fingers | finger | hand | luck | medium-dark skin tone</annotation>
+		<annotation cp="🤞🏾" type="tts">crossed fingers: medium-dark skin tone</annotation>
+		<annotation cp="🤞🏿">cross | crossed fingers | dark skin tone | finger | hand | luck</annotation>
+		<annotation cp="🤞🏿" type="tts">crossed fingers: dark skin tone</annotation>
+		<annotation cp="🤟🏻">hand | ILY | light skin tone | love-you gesture</annotation>
+		<annotation cp="🤟🏻" type="tts">love-you gesture: light skin tone</annotation>
+		<annotation cp="🤟🏼">hand | ILY | love-you gesture | medium-light skin tone</annotation>
+		<annotation cp="🤟🏼" type="tts">love-you gesture: medium-light skin tone</annotation>
+		<annotation cp="🤟🏽">hand | ILY | love-you gesture | medium skin tone</annotation>
+		<annotation cp="🤟🏽" type="tts">love-you gesture: medium skin tone</annotation>
+		<annotation cp="🤟🏾">hand | ILY | love-you gesture | medium-dark skin tone</annotation>
+		<annotation cp="🤟🏾" type="tts">love-you gesture: medium-dark skin tone</annotation>
+		<annotation cp="🤟🏿">dark skin tone | hand | ILY | love-you gesture</annotation>
+		<annotation cp="🤟🏿" type="tts">love-you gesture: dark skin tone</annotation>
+		<annotation cp="🤘🏻">finger | hand | horns | light skin tone | rock-on | sign of the horns</annotation>
+		<annotation cp="🤘🏻" type="tts">sign of the horns: light skin tone</annotation>
+		<annotation cp="🤘🏼">finger | hand | horns | medium-light skin tone | rock-on | sign of the horns</annotation>
+		<annotation cp="🤘🏼" type="tts">sign of the horns: medium-light skin tone</annotation>
+		<annotation cp="🤘🏽">finger | hand | horns | medium skin tone | rock-on | sign of the horns</annotation>
+		<annotation cp="🤘🏽" type="tts">sign of the horns: medium skin tone</annotation>
+		<annotation cp="🤘🏾">finger | hand | horns | medium-dark skin tone | rock-on | sign of the horns</annotation>
+		<annotation cp="🤘🏾" type="tts">sign of the horns: medium-dark skin tone</annotation>
+		<annotation cp="🤘🏿">dark skin tone | finger | hand | horns | rock-on | sign of the horns</annotation>
+		<annotation cp="🤘🏿" type="tts">sign of the horns: dark skin tone</annotation>
+		<annotation cp="🤙🏻">call | call me hand | hand | light skin tone</annotation>
+		<annotation cp="🤙🏻" type="tts">call me hand: light skin tone</annotation>
+		<annotation cp="🤙🏼">call | call me hand | hand | medium-light skin tone</annotation>
+		<annotation cp="🤙🏼" type="tts">call me hand: medium-light skin tone</annotation>
+		<annotation cp="🤙🏽">call | call me hand | hand | medium skin tone</annotation>
+		<annotation cp="🤙🏽" type="tts">call me hand: medium skin tone</annotation>
+		<annotation cp="🤙🏾">call | call me hand | hand | medium-dark skin tone</annotation>
+		<annotation cp="🤙🏾" type="tts">call me hand: medium-dark skin tone</annotation>
+		<annotation cp="🤙🏿">call | call me hand | dark skin tone | hand</annotation>
+		<annotation cp="🤙🏿" type="tts">call me hand: dark skin tone</annotation>
+		<annotation cp="👈🏻">backhand | backhand index pointing left | finger | hand | index | light skin tone | point</annotation>
+		<annotation cp="👈🏻" type="tts">backhand index pointing left: light skin tone</annotation>
+		<annotation cp="👈🏼">backhand | backhand index pointing left | finger | hand | index | medium-light skin tone | point</annotation>
+		<annotation cp="👈🏼" type="tts">backhand index pointing left: medium-light skin tone</annotation>
+		<annotation cp="👈🏽">backhand | backhand index pointing left | finger | hand | index | medium skin tone | point</annotation>
+		<annotation cp="👈🏽" type="tts">backhand index pointing left: medium skin tone</annotation>
+		<annotation cp="👈🏾">backhand | backhand index pointing left | finger | hand | index | medium-dark skin tone | point</annotation>
+		<annotation cp="👈🏾" type="tts">backhand index pointing left: medium-dark skin tone</annotation>
+		<annotation cp="👈🏿">backhand | backhand index pointing left | dark skin tone | finger | hand | index | point</annotation>
+		<annotation cp="👈🏿" type="tts">backhand index pointing left: dark skin tone</annotation>
+		<annotation cp="👉🏻">backhand | backhand index pointing right | finger | hand | index | light skin tone | point</annotation>
+		<annotation cp="👉🏻" type="tts">backhand index pointing right: light skin tone</annotation>
+		<annotation cp="👉🏼">backhand | backhand index pointing right | finger | hand | index | medium-light skin tone | point</annotation>
+		<annotation cp="👉🏼" type="tts">backhand index pointing right: medium-light skin tone</annotation>
+		<annotation cp="👉🏽">backhand | backhand index pointing right | finger | hand | index | medium skin tone | point</annotation>
+		<annotation cp="👉🏽" type="tts">backhand index pointing right: medium skin tone</annotation>
+		<annotation cp="👉🏾">backhand | backhand index pointing right | finger | hand | index | medium-dark skin tone | point</annotation>
+		<annotation cp="👉🏾" type="tts">backhand index pointing right: medium-dark skin tone</annotation>
+		<annotation cp="👉🏿">backhand | backhand index pointing right | dark skin tone | finger | hand | index | point</annotation>
+		<annotation cp="👉🏿" type="tts">backhand index pointing right: dark skin tone</annotation>
+		<annotation cp="👆🏻">backhand | backhand index pointing up | finger | hand | light skin tone | point | up</annotation>
+		<annotation cp="👆🏻" type="tts">backhand index pointing up: light skin tone</annotation>
+		<annotation cp="👆🏼">backhand | backhand index pointing up | finger | hand | medium-light skin tone | point | up</annotation>
+		<annotation cp="👆🏼" type="tts">backhand index pointing up: medium-light skin tone</annotation>
+		<annotation cp="👆🏽">backhand | backhand index pointing up | finger | hand | medium skin tone | point | up</annotation>
+		<annotation cp="👆🏽" type="tts">backhand index pointing up: medium skin tone</annotation>
+		<annotation cp="👆🏾">backhand | backhand index pointing up | finger | hand | medium-dark skin tone | point | up</annotation>
+		<annotation cp="👆🏾" type="tts">backhand index pointing up: medium-dark skin tone</annotation>
+		<annotation cp="👆🏿">backhand | backhand index pointing up | dark skin tone | finger | hand | point | up</annotation>
+		<annotation cp="👆🏿" type="tts">backhand index pointing up: dark skin tone</annotation>
+		<annotation cp="🖕🏻">finger | hand | light skin tone | middle finger</annotation>
+		<annotation cp="🖕🏻" type="tts">middle finger: light skin tone</annotation>
+		<annotation cp="🖕🏼">finger | hand | medium-light skin tone | middle finger</annotation>
+		<annotation cp="🖕🏼" type="tts">middle finger: medium-light skin tone</annotation>
+		<annotation cp="🖕🏽">finger | hand | medium skin tone | middle finger</annotation>
+		<annotation cp="🖕🏽" type="tts">middle finger: medium skin tone</annotation>
+		<annotation cp="🖕🏾">finger | hand | medium-dark skin tone | middle finger</annotation>
+		<annotation cp="🖕🏾" type="tts">middle finger: medium-dark skin tone</annotation>
+		<annotation cp="🖕🏿">dark skin tone | finger | hand | middle finger</annotation>
+		<annotation cp="🖕🏿" type="tts">middle finger: dark skin tone</annotation>
+		<annotation cp="👇🏻">backhand | backhand index pointing down | down | finger | hand | light skin tone | point</annotation>
+		<annotation cp="👇🏻" type="tts">backhand index pointing down: light skin tone</annotation>
+		<annotation cp="👇🏼">backhand | backhand index pointing down | down | finger | hand | medium-light skin tone | point</annotation>
+		<annotation cp="👇🏼" type="tts">backhand index pointing down: medium-light skin tone</annotation>
+		<annotation cp="👇🏽">backhand | backhand index pointing down | down | finger | hand | medium skin tone | point</annotation>
+		<annotation cp="👇🏽" type="tts">backhand index pointing down: medium skin tone</annotation>
+		<annotation cp="👇🏾">backhand | backhand index pointing down | down | finger | hand | medium-dark skin tone | point</annotation>
+		<annotation cp="👇🏾" type="tts">backhand index pointing down: medium-dark skin tone</annotation>
+		<annotation cp="👇🏿">backhand | backhand index pointing down | dark skin tone | down | finger | hand | point</annotation>
+		<annotation cp="👇🏿" type="tts">backhand index pointing down: dark skin tone</annotation>
+		<annotation cp="☝🏻">finger | hand | index | index pointing up | light skin tone | point | up</annotation>
+		<annotation cp="☝🏻" type="tts">index pointing up: light skin tone</annotation>
+		<annotation cp="☝🏼">finger | hand | index | index pointing up | medium-light skin tone | point | up</annotation>
+		<annotation cp="☝🏼" type="tts">index pointing up: medium-light skin tone</annotation>
+		<annotation cp="☝🏽">finger | hand | index | index pointing up | medium skin tone | point | up</annotation>
+		<annotation cp="☝🏽" type="tts">index pointing up: medium skin tone</annotation>
+		<annotation cp="☝🏾">finger | hand | index | index pointing up | medium-dark skin tone | point | up</annotation>
+		<annotation cp="☝🏾" type="tts">index pointing up: medium-dark skin tone</annotation>
+		<annotation cp="☝🏿">dark skin tone | finger | hand | index | index pointing up | point | up</annotation>
+		<annotation cp="☝🏿" type="tts">index pointing up: dark skin tone</annotation>
+		<annotation cp="👍🏻">+1 | hand | light skin tone | thumb | thumbs up | up</annotation>
+		<annotation cp="👍🏻" type="tts">thumbs up: light skin tone</annotation>
+		<annotation cp="👍🏼">+1 | hand | medium-light skin tone | thumb | thumbs up | up</annotation>
+		<annotation cp="👍🏼" type="tts">thumbs up: medium-light skin tone</annotation>
+		<annotation cp="👍🏽">+1 | hand | medium skin tone | thumb | thumbs up | up</annotation>
+		<annotation cp="👍🏽" type="tts">thumbs up: medium skin tone</annotation>
+		<annotation cp="👍🏾">+1 | hand | medium-dark skin tone | thumb | thumbs up | up</annotation>
+		<annotation cp="👍🏾" type="tts">thumbs up: medium-dark skin tone</annotation>
+		<annotation cp="👍🏿">+1 | dark skin tone | hand | thumb | thumbs up | up</annotation>
+		<annotation cp="👍🏿" type="tts">thumbs up: dark skin tone</annotation>
+		<annotation cp="👎🏻">-1 | down | hand | light skin tone | thumb | thumbs down</annotation>
+		<annotation cp="👎🏻" type="tts">thumbs down: light skin tone</annotation>
+		<annotation cp="👎🏼">-1 | down | hand | medium-light skin tone | thumb | thumbs down</annotation>
+		<annotation cp="👎🏼" type="tts">thumbs down: medium-light skin tone</annotation>
+		<annotation cp="👎🏽">-1 | down | hand | medium skin tone | thumb | thumbs down</annotation>
+		<annotation cp="👎🏽" type="tts">thumbs down: medium skin tone</annotation>
+		<annotation cp="👎🏾">-1 | down | hand | medium-dark skin tone | thumb | thumbs down</annotation>
+		<annotation cp="👎🏾" type="tts">thumbs down: medium-dark skin tone</annotation>
+		<annotation cp="👎🏿">-1 | dark skin tone | down | hand | thumb | thumbs down</annotation>
+		<annotation cp="👎🏿" type="tts">thumbs down: dark skin tone</annotation>
+		<annotation cp="✊🏻">clenched | fist | hand | light skin tone | punch | raised fist</annotation>
+		<annotation cp="✊🏻" type="tts">raised fist: light skin tone</annotation>
+		<annotation cp="✊🏼">clenched | fist | hand | medium-light skin tone | punch | raised fist</annotation>
+		<annotation cp="✊🏼" type="tts">raised fist: medium-light skin tone</annotation>
+		<annotation cp="✊🏽">clenched | fist | hand | medium skin tone | punch | raised fist</annotation>
+		<annotation cp="✊🏽" type="tts">raised fist: medium skin tone</annotation>
+		<annotation cp="✊🏾">clenched | fist | hand | medium-dark skin tone | punch | raised fist</annotation>
+		<annotation cp="✊🏾" type="tts">raised fist: medium-dark skin tone</annotation>
+		<annotation cp="✊🏿">clenched | dark skin tone | fist | hand | punch | raised fist</annotation>
+		<annotation cp="✊🏿" type="tts">raised fist: dark skin tone</annotation>
+		<annotation cp="👊🏻">clenched | fist | hand | light skin tone | oncoming fist | punch</annotation>
+		<annotation cp="👊🏻" type="tts">oncoming fist: light skin tone</annotation>
+		<annotation cp="👊🏼">clenched | fist | hand | medium-light skin tone | oncoming fist | punch</annotation>
+		<annotation cp="👊🏼" type="tts">oncoming fist: medium-light skin tone</annotation>
+		<annotation cp="👊🏽">clenched | fist | hand | medium skin tone | oncoming fist | punch</annotation>
+		<annotation cp="👊🏽" type="tts">oncoming fist: medium skin tone</annotation>
+		<annotation cp="👊🏾">clenched | fist | hand | medium-dark skin tone | oncoming fist | punch</annotation>
+		<annotation cp="👊🏾" type="tts">oncoming fist: medium-dark skin tone</annotation>
+		<annotation cp="👊🏿">clenched | dark skin tone | fist | hand | oncoming fist | punch</annotation>
+		<annotation cp="👊🏿" type="tts">oncoming fist: dark skin tone</annotation>
+		<annotation cp="🤛🏻">fist | left-facing fist | leftwards | light skin tone</annotation>
+		<annotation cp="🤛🏻" type="tts">left-facing fist: light skin tone</annotation>
+		<annotation cp="🤛🏼">fist | left-facing fist | leftwards | medium-light skin tone</annotation>
+		<annotation cp="🤛🏼" type="tts">left-facing fist: medium-light skin tone</annotation>
+		<annotation cp="🤛🏽">fist | left-facing fist | leftwards | medium skin tone</annotation>
+		<annotation cp="🤛🏽" type="tts">left-facing fist: medium skin tone</annotation>
+		<annotation cp="🤛🏾">fist | left-facing fist | leftwards | medium-dark skin tone</annotation>
+		<annotation cp="🤛🏾" type="tts">left-facing fist: medium-dark skin tone</annotation>
+		<annotation cp="🤛🏿">dark skin tone | fist | left-facing fist | leftwards</annotation>
+		<annotation cp="🤛🏿" type="tts">left-facing fist: dark skin tone</annotation>
+		<annotation cp="🤜🏻">fist | light skin tone | right-facing fist | rightwards</annotation>
+		<annotation cp="🤜🏻" type="tts">right-facing fist: light skin tone</annotation>
+		<annotation cp="🤜🏼">fist | medium-light skin tone | right-facing fist | rightwards</annotation>
+		<annotation cp="🤜🏼" type="tts">right-facing fist: medium-light skin tone</annotation>
+		<annotation cp="🤜🏽">fist | medium skin tone | right-facing fist | rightwards</annotation>
+		<annotation cp="🤜🏽" type="tts">right-facing fist: medium skin tone</annotation>
+		<annotation cp="🤜🏾">fist | medium-dark skin tone | right-facing fist | rightwards</annotation>
+		<annotation cp="🤜🏾" type="tts">right-facing fist: medium-dark skin tone</annotation>
+		<annotation cp="🤜🏿">dark skin tone | fist | right-facing fist | rightwards</annotation>
+		<annotation cp="🤜🏿" type="tts">right-facing fist: dark skin tone</annotation>
+		<annotation cp="👏🏻">clap | clapping hands | hand | light skin tone</annotation>
+		<annotation cp="👏🏻" type="tts">clapping hands: light skin tone</annotation>
+		<annotation cp="👏🏼">clap | clapping hands | hand | medium-light skin tone</annotation>
+		<annotation cp="👏🏼" type="tts">clapping hands: medium-light skin tone</annotation>
+		<annotation cp="👏🏽">clap | clapping hands | hand | medium skin tone</annotation>
+		<annotation cp="👏🏽" type="tts">clapping hands: medium skin tone</annotation>
+		<annotation cp="👏🏾">clap | clapping hands | hand | medium-dark skin tone</annotation>
+		<annotation cp="👏🏾" type="tts">clapping hands: medium-dark skin tone</annotation>
+		<annotation cp="👏🏿">clap | clapping hands | dark skin tone | hand</annotation>
+		<annotation cp="👏🏿" type="tts">clapping hands: dark skin tone</annotation>
+		<annotation cp="🙌🏻">celebration | gesture | hand | hooray | light skin tone | raised | raising hands</annotation>
+		<annotation cp="🙌🏻" type="tts">raising hands: light skin tone</annotation>
+		<annotation cp="🙌🏼">celebration | gesture | hand | hooray | medium-light skin tone | raised | raising hands</annotation>
+		<annotation cp="🙌🏼" type="tts">raising hands: medium-light skin tone</annotation>
+		<annotation cp="🙌🏽">celebration | gesture | hand | hooray | medium skin tone | raised | raising hands</annotation>
+		<annotation cp="🙌🏽" type="tts">raising hands: medium skin tone</annotation>
+		<annotation cp="🙌🏾">celebration | gesture | hand | hooray | medium-dark skin tone | raised | raising hands</annotation>
+		<annotation cp="🙌🏾" type="tts">raising hands: medium-dark skin tone</annotation>
+		<annotation cp="🙌🏿">celebration | dark skin tone | gesture | hand | hooray | raised | raising hands</annotation>
+		<annotation cp="🙌🏿" type="tts">raising hands: dark skin tone</annotation>
+		<annotation cp="👐🏻">hand | light skin tone | open | open hands</annotation>
+		<annotation cp="👐🏻" type="tts">open hands: light skin tone</annotation>
+		<annotation cp="👐🏼">hand | medium-light skin tone | open | open hands</annotation>
+		<annotation cp="👐🏼" type="tts">open hands: medium-light skin tone</annotation>
+		<annotation cp="👐🏽">hand | medium skin tone | open | open hands</annotation>
+		<annotation cp="👐🏽" type="tts">open hands: medium skin tone</annotation>
+		<annotation cp="👐🏾">hand | medium-dark skin tone | open | open hands</annotation>
+		<annotation cp="👐🏾" type="tts">open hands: medium-dark skin tone</annotation>
+		<annotation cp="👐🏿">dark skin tone | hand | open | open hands</annotation>
+		<annotation cp="👐🏿" type="tts">open hands: dark skin tone</annotation>
+		<annotation cp="🤲🏻">light skin tone | palms up together | prayer</annotation>
+		<annotation cp="🤲🏻" type="tts">palms up together: light skin tone</annotation>
+		<annotation cp="🤲🏼">medium-light skin tone | palms up together | prayer</annotation>
+		<annotation cp="🤲🏼" type="tts">palms up together: medium-light skin tone</annotation>
+		<annotation cp="🤲🏽">medium skin tone | palms up together | prayer</annotation>
+		<annotation cp="🤲🏽" type="tts">palms up together: medium skin tone</annotation>
+		<annotation cp="🤲🏾">medium-dark skin tone | palms up together | prayer</annotation>
+		<annotation cp="🤲🏾" type="tts">palms up together: medium-dark skin tone</annotation>
+		<annotation cp="🤲🏿">dark skin tone | palms up together | prayer</annotation>
+		<annotation cp="🤲🏿" type="tts">palms up together: dark skin tone</annotation>
+		<annotation cp="🙏🏻">ask | folded hands | hand | high 5 | high five | light skin tone | please | pray | thanks</annotation>
+		<annotation cp="🙏🏻" type="tts">folded hands: light skin tone</annotation>
+		<annotation cp="🙏🏼">ask | folded hands | hand | high 5 | high five | medium-light skin tone | please | pray | thanks</annotation>
+		<annotation cp="🙏🏼" type="tts">folded hands: medium-light skin tone</annotation>
+		<annotation cp="🙏🏽">ask | folded hands | hand | high 5 | high five | medium skin tone | please | pray | thanks</annotation>
+		<annotation cp="🙏🏽" type="tts">folded hands: medium skin tone</annotation>
+		<annotation cp="🙏🏾">ask | folded hands | hand | high 5 | high five | medium-dark skin tone | please | pray | thanks</annotation>
+		<annotation cp="🙏🏾" type="tts">folded hands: medium-dark skin tone</annotation>
+		<annotation cp="🙏🏿">ask | dark skin tone | folded hands | hand | high 5 | high five | please | pray | thanks</annotation>
+		<annotation cp="🙏🏿" type="tts">folded hands: dark skin tone</annotation>
+		<annotation cp="✍🏻">hand | light skin tone | write | writing hand</annotation>
+		<annotation cp="✍🏻" type="tts">writing hand: light skin tone</annotation>
+		<annotation cp="✍🏼">hand | medium-light skin tone | write | writing hand</annotation>
+		<annotation cp="✍🏼" type="tts">writing hand: medium-light skin tone</annotation>
+		<annotation cp="✍🏽">hand | medium skin tone | write | writing hand</annotation>
+		<annotation cp="✍🏽" type="tts">writing hand: medium skin tone</annotation>
+		<annotation cp="✍🏾">hand | medium-dark skin tone | write | writing hand</annotation>
+		<annotation cp="✍🏾" type="tts">writing hand: medium-dark skin tone</annotation>
+		<annotation cp="✍🏿">dark skin tone | hand | write | writing hand</annotation>
+		<annotation cp="✍🏿" type="tts">writing hand: dark skin tone</annotation>
+		<annotation cp="💅🏻">care | cosmetics | light skin tone | manicure | nail | polish</annotation>
+		<annotation cp="💅🏻" type="tts">nail polish: light skin tone</annotation>
+		<annotation cp="💅🏼">care | cosmetics | manicure | medium-light skin tone | nail | polish</annotation>
+		<annotation cp="💅🏼" type="tts">nail polish: medium-light skin tone</annotation>
+		<annotation cp="💅🏽">care | cosmetics | manicure | medium skin tone | nail | polish</annotation>
+		<annotation cp="💅🏽" type="tts">nail polish: medium skin tone</annotation>
+		<annotation cp="💅🏾">care | cosmetics | manicure | medium-dark skin tone | nail | polish</annotation>
+		<annotation cp="💅🏾" type="tts">nail polish: medium-dark skin tone</annotation>
+		<annotation cp="💅🏿">care | cosmetics | dark skin tone | manicure | nail | polish</annotation>
+		<annotation cp="💅🏿" type="tts">nail polish: dark skin tone</annotation>
+		<annotation cp="🤳🏻">camera | light skin tone | phone | selfie</annotation>
+		<annotation cp="🤳🏻" type="tts">selfie: light skin tone</annotation>
+		<annotation cp="🤳🏼">camera | medium-light skin tone | phone | selfie</annotation>
+		<annotation cp="🤳🏼" type="tts">selfie: medium-light skin tone</annotation>
+		<annotation cp="🤳🏽">camera | medium skin tone | phone | selfie</annotation>
+		<annotation cp="🤳🏽" type="tts">selfie: medium skin tone</annotation>
+		<annotation cp="🤳🏾">camera | medium-dark skin tone | phone | selfie</annotation>
+		<annotation cp="🤳🏾" type="tts">selfie: medium-dark skin tone</annotation>
+		<annotation cp="🤳🏿">camera | dark skin tone | phone | selfie</annotation>
+		<annotation cp="🤳🏿" type="tts">selfie: dark skin tone</annotation>
+		<annotation cp="💪🏻">biceps | comic | flex | flexed biceps | light skin tone | muscle</annotation>
+		<annotation cp="💪🏻" type="tts">flexed biceps: light skin tone</annotation>
+		<annotation cp="💪🏼">biceps | comic | flex | flexed biceps | medium-light skin tone | muscle</annotation>
+		<annotation cp="💪🏼" type="tts">flexed biceps: medium-light skin tone</annotation>
+		<annotation cp="💪🏽">biceps | comic | flex | flexed biceps | medium skin tone | muscle</annotation>
+		<annotation cp="💪🏽" type="tts">flexed biceps: medium skin tone</annotation>
+		<annotation cp="💪🏾">biceps | comic | flex | flexed biceps | medium-dark skin tone | muscle</annotation>
+		<annotation cp="💪🏾" type="tts">flexed biceps: medium-dark skin tone</annotation>
+		<annotation cp="💪🏿">biceps | comic | dark skin tone | flex | flexed biceps | muscle</annotation>
+		<annotation cp="💪🏿" type="tts">flexed biceps: dark skin tone</annotation>
+		<annotation cp="🦵🏻">kick | leg | light skin tone | limb</annotation>
+		<annotation cp="🦵🏻" type="tts">leg: light skin tone</annotation>
+		<annotation cp="🦵🏼">kick | leg | limb | medium-light skin tone</annotation>
+		<annotation cp="🦵🏼" type="tts">leg: medium-light skin tone</annotation>
+		<annotation cp="🦵🏽">kick | leg | limb | medium skin tone</annotation>
+		<annotation cp="🦵🏽" type="tts">leg: medium skin tone</annotation>
+		<annotation cp="🦵🏾">kick | leg | limb | medium-dark skin tone</annotation>
+		<annotation cp="🦵🏾" type="tts">leg: medium-dark skin tone</annotation>
+		<annotation cp="🦵🏿">dark skin tone | kick | leg | limb</annotation>
+		<annotation cp="🦵🏿" type="tts">leg: dark skin tone</annotation>
+		<annotation cp="🦶🏻">foot | kick | light skin tone | stomp</annotation>
+		<annotation cp="🦶🏻" type="tts">foot: light skin tone</annotation>
+		<annotation cp="🦶🏼">foot | kick | medium-light skin tone | stomp</annotation>
+		<annotation cp="🦶🏼" type="tts">foot: medium-light skin tone</annotation>
+		<annotation cp="🦶🏽">foot | kick | medium skin tone | stomp</annotation>
+		<annotation cp="🦶🏽" type="tts">foot: medium skin tone</annotation>
+		<annotation cp="🦶🏾">foot | kick | medium-dark skin tone | stomp</annotation>
+		<annotation cp="🦶🏾" type="tts">foot: medium-dark skin tone</annotation>
+		<annotation cp="🦶🏿">dark skin tone | foot | kick | stomp</annotation>
+		<annotation cp="🦶🏿" type="tts">foot: dark skin tone</annotation>
+		<annotation cp="👂🏻">body | ear | light skin tone</annotation>
+		<annotation cp="👂🏻" type="tts">ear: light skin tone</annotation>
+		<annotation cp="👂🏼">body | ear | medium-light skin tone</annotation>
+		<annotation cp="👂🏼" type="tts">ear: medium-light skin tone</annotation>
+		<annotation cp="👂🏽">body | ear | medium skin tone</annotation>
+		<annotation cp="👂🏽" type="tts">ear: medium skin tone</annotation>
+		<annotation cp="👂🏾">body | ear | medium-dark skin tone</annotation>
+		<annotation cp="👂🏾" type="tts">ear: medium-dark skin tone</annotation>
+		<annotation cp="👂🏿">body | dark skin tone | ear</annotation>
+		<annotation cp="👂🏿" type="tts">ear: dark skin tone</annotation>
+		<annotation cp="🦻🏻">accessibility | ear with hearing aid | hard of hearing | light skin tone</annotation>
+		<annotation cp="🦻🏻" type="tts">ear with hearing aid: light skin tone</annotation>
+		<annotation cp="🦻🏼">accessibility | ear with hearing aid | hard of hearing | medium-light skin tone</annotation>
+		<annotation cp="🦻🏼" type="tts">ear with hearing aid: medium-light skin tone</annotation>
+		<annotation cp="🦻🏽">accessibility | ear with hearing aid | hard of hearing | medium skin tone</annotation>
+		<annotation cp="🦻🏽" type="tts">ear with hearing aid: medium skin tone</annotation>
+		<annotation cp="🦻🏾">accessibility | ear with hearing aid | hard of hearing | medium-dark skin tone</annotation>
+		<annotation cp="🦻🏾" type="tts">ear with hearing aid: medium-dark skin tone</annotation>
+		<annotation cp="🦻🏿">accessibility | dark skin tone | ear with hearing aid | hard of hearing</annotation>
+		<annotation cp="🦻🏿" type="tts">ear with hearing aid: dark skin tone</annotation>
+		<annotation cp="👃🏻">body | light skin tone | nose</annotation>
+		<annotation cp="👃🏻" type="tts">nose: light skin tone</annotation>
+		<annotation cp="👃🏼">body | medium-light skin tone | nose</annotation>
+		<annotation cp="👃🏼" type="tts">nose: medium-light skin tone</annotation>
+		<annotation cp="👃🏽">body | medium skin tone | nose</annotation>
+		<annotation cp="👃🏽" type="tts">nose: medium skin tone</annotation>
+		<annotation cp="👃🏾">body | medium-dark skin tone | nose</annotation>
+		<annotation cp="👃🏾" type="tts">nose: medium-dark skin tone</annotation>
+		<annotation cp="👃🏿">body | dark skin tone | nose</annotation>
+		<annotation cp="👃🏿" type="tts">nose: dark skin tone</annotation>
+		<annotation cp="👶🏻">baby | light skin tone | young</annotation>
+		<annotation cp="👶🏻" type="tts">baby: light skin tone</annotation>
+		<annotation cp="👶🏼">baby | medium-light skin tone | young</annotation>
+		<annotation cp="👶🏼" type="tts">baby: medium-light skin tone</annotation>
+		<annotation cp="👶🏽">baby | medium skin tone | young</annotation>
+		<annotation cp="👶🏽" type="tts">baby: medium skin tone</annotation>
+		<annotation cp="👶🏾">baby | medium-dark skin tone | young</annotation>
+		<annotation cp="👶🏾" type="tts">baby: medium-dark skin tone</annotation>
+		<annotation cp="👶🏿">baby | dark skin tone | young</annotation>
+		<annotation cp="👶🏿" type="tts">baby: dark skin tone</annotation>
+		<annotation cp="🧒🏻">child | gender-neutral | light skin tone | unspecified gender | young</annotation>
+		<annotation cp="🧒🏻" type="tts">child: light skin tone</annotation>
+		<annotation cp="🧒🏼">child | gender-neutral | medium-light skin tone | unspecified gender | young</annotation>
+		<annotation cp="🧒🏼" type="tts">child: medium-light skin tone</annotation>
+		<annotation cp="🧒🏽">child | gender-neutral | medium skin tone | unspecified gender | young</annotation>
+		<annotation cp="🧒🏽" type="tts">child: medium skin tone</annotation>
+		<annotation cp="🧒🏾">child | gender-neutral | medium-dark skin tone | unspecified gender | young</annotation>
+		<annotation cp="🧒🏾" type="tts">child: medium-dark skin tone</annotation>
+		<annotation cp="🧒🏿">child | dark skin tone | gender-neutral | unspecified gender | young</annotation>
+		<annotation cp="🧒🏿" type="tts">child: dark skin tone</annotation>
+		<annotation cp="👦🏻">boy | light skin tone | young</annotation>
+		<annotation cp="👦🏻" type="tts">boy: light skin tone</annotation>
+		<annotation cp="👦🏼">boy | medium-light skin tone | young</annotation>
+		<annotation cp="👦🏼" type="tts">boy: medium-light skin tone</annotation>
+		<annotation cp="👦🏽">boy | medium skin tone | young</annotation>
+		<annotation cp="👦🏽" type="tts">boy: medium skin tone</annotation>
+		<annotation cp="👦🏾">boy | medium-dark skin tone | young</annotation>
+		<annotation cp="👦🏾" type="tts">boy: medium-dark skin tone</annotation>
+		<annotation cp="👦🏿">boy | dark skin tone | young</annotation>
+		<annotation cp="👦🏿" type="tts">boy: dark skin tone</annotation>
+		<annotation cp="👧🏻">girl | light skin tone | Virgo | young | zodiac</annotation>
+		<annotation cp="👧🏻" type="tts">girl: light skin tone</annotation>
+		<annotation cp="👧🏼">girl | medium-light skin tone | Virgo | young | zodiac</annotation>
+		<annotation cp="👧🏼" type="tts">girl: medium-light skin tone</annotation>
+		<annotation cp="👧🏽">girl | medium skin tone | Virgo | young | zodiac</annotation>
+		<annotation cp="👧🏽" type="tts">girl: medium skin tone</annotation>
+		<annotation cp="👧🏾">girl | medium-dark skin tone | Virgo | young | zodiac</annotation>
+		<annotation cp="👧🏾" type="tts">girl: medium-dark skin tone</annotation>
+		<annotation cp="👧🏿">dark skin tone | girl | Virgo | young | zodiac</annotation>
+		<annotation cp="👧🏿" type="tts">girl: dark skin tone</annotation>
+		<annotation cp="🧑🏻">adult | gender-neutral | light skin tone | person | unspecified gender</annotation>
+		<annotation cp="🧑🏻" type="tts">person: light skin tone</annotation>
+		<annotation cp="🧑🏼">adult | gender-neutral | medium-light skin tone | person | unspecified gender</annotation>
+		<annotation cp="🧑🏼" type="tts">person: medium-light skin tone</annotation>
+		<annotation cp="🧑🏽">adult | gender-neutral | medium skin tone | person | unspecified gender</annotation>
+		<annotation cp="🧑🏽" type="tts">person: medium skin tone</annotation>
+		<annotation cp="🧑🏾">adult | gender-neutral | medium-dark skin tone | person | unspecified gender</annotation>
+		<annotation cp="🧑🏾" type="tts">person: medium-dark skin tone</annotation>
+		<annotation cp="🧑🏿">adult | dark skin tone | gender-neutral | person | unspecified gender</annotation>
+		<annotation cp="🧑🏿" type="tts">person: dark skin tone</annotation>
+		<annotation cp="👱🏻">blond | blond-haired person | hair | light skin tone | person: blond hair</annotation>
+		<annotation cp="👱🏻" type="tts">person: light skin tone, blond hair</annotation>
+		<annotation cp="👱🏼">blond | blond-haired person | hair | medium-light skin tone | person: blond hair</annotation>
+		<annotation cp="👱🏼" type="tts">person: medium-light skin tone, blond hair</annotation>
+		<annotation cp="👱🏽">blond | blond-haired person | hair | medium skin tone | person: blond hair</annotation>
+		<annotation cp="👱🏽" type="tts">person: medium skin tone, blond hair</annotation>
+		<annotation cp="👱🏾">blond | blond-haired person | hair | medium-dark skin tone | person: blond hair</annotation>
+		<annotation cp="👱🏾" type="tts">person: medium-dark skin tone, blond hair</annotation>
+		<annotation cp="👱🏿">blond | blond-haired person | dark skin tone | hair | person: blond hair</annotation>
+		<annotation cp="👱🏿" type="tts">person: dark skin tone, blond hair</annotation>
+		<annotation cp="🧑🏻‍❤‍💋‍🧑🏼">couple | kiss | light skin tone | medium-light skin tone | person</annotation>
+		<annotation cp="🧑🏻‍❤‍💋‍🧑🏼" type="tts">kiss: person, person, light skin tone, medium-light skin tone</annotation>
+		<annotation cp="🧑🏻‍❤‍💋‍🧑🏽">couple | kiss | light skin tone | medium skin tone | person</annotation>
+		<annotation cp="🧑🏻‍❤‍💋‍🧑🏽" type="tts">kiss: person, person, light skin tone, medium skin tone</annotation>
+		<annotation cp="🧑🏻‍❤‍💋‍🧑🏾">couple | kiss | light skin tone | medium-dark skin tone | person</annotation>
+		<annotation cp="🧑🏻‍❤‍💋‍🧑🏾" type="tts">kiss: person, person, light skin tone, medium-dark skin tone</annotation>
+		<annotation cp="🧑🏻‍❤‍💋‍🧑🏿">couple | dark skin tone | kiss | light skin tone | person</annotation>
+		<annotation cp="🧑🏻‍❤‍💋‍🧑🏿" type="tts">kiss: person, person, light skin tone, dark skin tone</annotation>
+		<annotation cp="🧑🏼‍❤‍💋‍🧑🏻">couple | kiss | light skin tone | medium-light skin tone | person</annotation>
+		<annotation cp="🧑🏼‍❤‍💋‍🧑🏻" type="tts">kiss: person, person, medium-light skin tone, light skin tone</annotation>
+		<annotation cp="🧑🏼‍❤‍💋‍🧑🏽">couple | kiss | medium skin tone | medium-light skin tone | person</annotation>
+		<annotation cp="🧑🏼‍❤‍💋‍🧑🏽" type="tts">kiss: person, person, medium-light skin tone, medium skin tone</annotation>
+		<annotation cp="🧑🏼‍❤‍💋‍🧑🏾">couple | kiss | medium-dark skin tone | medium-light skin tone | person</annotation>
+		<annotation cp="🧑🏼‍❤‍💋‍🧑🏾" type="tts">kiss: person, person, medium-light skin tone, medium-dark skin tone</annotation>
+		<annotation cp="🧑🏼‍❤‍💋‍🧑🏿">couple | dark skin tone | kiss | medium-light skin tone | person</annotation>
+		<annotation cp="🧑🏼‍❤‍💋‍🧑🏿" type="tts">kiss: person, person, medium-light skin tone, dark skin tone</annotation>
+		<annotation cp="🧑🏽‍❤‍💋‍🧑🏻">couple | kiss | light skin tone | medium skin tone | person</annotation>
+		<annotation cp="🧑🏽‍❤‍💋‍🧑🏻" type="tts">kiss: person, person, medium skin tone, light skin tone</annotation>
+		<annotation cp="🧑🏽‍❤‍💋‍🧑🏼">couple | kiss | medium skin tone | medium-light skin tone | person</annotation>
+		<annotation cp="🧑🏽‍❤‍💋‍🧑🏼" type="tts">kiss: person, person, medium skin tone, medium-light skin tone</annotation>
+		<annotation cp="🧑🏽‍❤‍💋‍🧑🏾">couple | kiss | medium skin tone | medium-dark skin tone | person</annotation>
+		<annotation cp="🧑🏽‍❤‍💋‍🧑🏾" type="tts">kiss: person, person, medium skin tone, medium-dark skin tone</annotation>
+		<annotation cp="🧑🏽‍❤‍💋‍🧑🏿">couple | dark skin tone | kiss | medium skin tone | person</annotation>
+		<annotation cp="🧑🏽‍❤‍💋‍🧑🏿" type="tts">kiss: person, person, medium skin tone, dark skin tone</annotation>
+		<annotation cp="🧑🏾‍❤‍💋‍🧑🏻">couple | kiss | light skin tone | medium-dark skin tone | person</annotation>
+		<annotation cp="🧑🏾‍❤‍💋‍🧑🏻" type="tts">kiss: person, person, medium-dark skin tone, light skin tone</annotation>
+		<annotation cp="🧑🏾‍❤‍💋‍🧑🏼">couple | kiss | medium-dark skin tone | medium-light skin tone | person</annotation>
+		<annotation cp="🧑🏾‍❤‍💋‍🧑🏼" type="tts">kiss: person, person, medium-dark skin tone, medium-light skin tone</annotation>
+		<annotation cp="🧑🏾‍❤‍💋‍🧑🏽">couple | kiss | medium skin tone | medium-dark skin tone | person</annotation>
+		<annotation cp="🧑🏾‍❤‍💋‍🧑🏽" type="tts">kiss: person, person, medium-dark skin tone, medium skin tone</annotation>
+		<annotation cp="🧑🏾‍❤‍💋‍🧑🏿">couple | dark skin tone | kiss | medium-dark skin tone | person</annotation>
+		<annotation cp="🧑🏾‍❤‍💋‍🧑🏿" type="tts">kiss: person, person, medium-dark skin tone, dark skin tone</annotation>
+		<annotation cp="🧑🏿‍❤‍💋‍🧑🏻">couple | dark skin tone | kiss | light skin tone | person</annotation>
+		<annotation cp="🧑🏿‍❤‍💋‍🧑🏻" type="tts">kiss: person, person, dark skin tone, light skin tone</annotation>
+		<annotation cp="🧑🏿‍❤‍💋‍🧑🏼">couple | dark skin tone | kiss | medium-light skin tone | person</annotation>
+		<annotation cp="🧑🏿‍❤‍💋‍🧑🏼" type="tts">kiss: person, person, dark skin tone, medium-light skin tone</annotation>
+		<annotation cp="🧑🏿‍❤‍💋‍🧑🏽">couple | dark skin tone | kiss | medium skin tone | person</annotation>
+		<annotation cp="🧑🏿‍❤‍💋‍🧑🏽" type="tts">kiss: person, person, dark skin tone, medium skin tone</annotation>
+		<annotation cp="🧑🏿‍❤‍💋‍🧑🏾">couple | dark skin tone | kiss | medium-dark skin tone | person</annotation>
+		<annotation cp="🧑🏿‍❤‍💋‍🧑🏾" type="tts">kiss: person, person, dark skin tone, medium-dark skin tone</annotation>
+		<annotation cp="🧑🏻‍❤‍🧑🏼">couple | couple with heart | light skin tone | love | medium-light skin tone | person</annotation>
+		<annotation cp="🧑🏻‍❤‍🧑🏼" type="tts">couple with heart: person, person, light skin tone, medium-light skin tone</annotation>
+		<annotation cp="🧑🏻‍❤‍🧑🏽">couple | couple with heart | light skin tone | love | medium skin tone | person</annotation>
+		<annotation cp="🧑🏻‍❤‍🧑🏽" type="tts">couple with heart: person, person, light skin tone, medium skin tone</annotation>
+		<annotation cp="🧑🏻‍❤‍🧑🏾">couple | couple with heart | light skin tone | love | medium-dark skin tone | person</annotation>
+		<annotation cp="🧑🏻‍❤‍🧑🏾" type="tts">couple with heart: person, person, light skin tone, medium-dark skin tone</annotation>
+		<annotation cp="🧑🏻‍❤‍🧑🏿">couple | couple with heart | dark skin tone | light skin tone | love | person</annotation>
+		<annotation cp="🧑🏻‍❤‍🧑🏿" type="tts">couple with heart: person, person, light skin tone, dark skin tone</annotation>
+		<annotation cp="🧑🏼‍❤‍🧑🏻">couple | couple with heart | light skin tone | love | medium-light skin tone | person</annotation>
+		<annotation cp="🧑🏼‍❤‍🧑🏻" type="tts">couple with heart: person, person, medium-light skin tone, light skin tone</annotation>
+		<annotation cp="🧑🏼‍❤‍🧑🏽">couple | couple with heart | love | medium skin tone | medium-light skin tone | person</annotation>
+		<annotation cp="🧑🏼‍❤‍🧑🏽" type="tts">couple with heart: person, person, medium-light skin tone, medium skin tone</annotation>
+		<annotation cp="🧑🏼‍❤‍🧑🏾">couple | couple with heart | love | medium-dark skin tone | medium-light skin tone | person</annotation>
+		<annotation cp="🧑🏼‍❤‍🧑🏾" type="tts">couple with heart: person, person, medium-light skin tone, medium-dark skin tone</annotation>
+		<annotation cp="🧑🏼‍❤‍🧑🏿">couple | couple with heart | dark skin tone | love | medium-light skin tone | person</annotation>
+		<annotation cp="🧑🏼‍❤‍🧑🏿" type="tts">couple with heart: person, person, medium-light skin tone, dark skin tone</annotation>
+		<annotation cp="🧑🏽‍❤‍🧑🏻">couple | couple with heart | light skin tone | love | medium skin tone | person</annotation>
+		<annotation cp="🧑🏽‍❤‍🧑🏻" type="tts">couple with heart: person, person, medium skin tone, light skin tone</annotation>
+		<annotation cp="🧑🏽‍❤‍🧑🏼">couple | couple with heart | love | medium skin tone | medium-light skin tone | person</annotation>
+		<annotation cp="🧑🏽‍❤‍🧑🏼" type="tts">couple with heart: person, person, medium skin tone, medium-light skin tone</annotation>
+		<annotation cp="🧑🏽‍❤‍🧑🏾">couple | couple with heart | love | medium skin tone | medium-dark skin tone | person</annotation>
+		<annotation cp="🧑🏽‍❤‍🧑🏾" type="tts">couple with heart: person, person, medium skin tone, medium-dark skin tone</annotation>
+		<annotation cp="🧑🏽‍❤‍🧑🏿">couple | couple with heart | dark skin tone | love | medium skin tone | person</annotation>
+		<annotation cp="🧑🏽‍❤‍🧑🏿" type="tts">couple with heart: person, person, medium skin tone, dark skin tone</annotation>
+		<annotation cp="🧑🏾‍❤‍🧑🏻">couple | couple with heart | light skin tone | love | medium-dark skin tone | person</annotation>
+		<annotation cp="🧑🏾‍❤‍🧑🏻" type="tts">couple with heart: person, person, medium-dark skin tone, light skin tone</annotation>
+		<annotation cp="🧑🏾‍❤‍🧑🏼">couple | couple with heart | love | medium-dark skin tone | medium-light skin tone | person</annotation>
+		<annotation cp="🧑🏾‍❤‍🧑🏼" type="tts">couple with heart: person, person, medium-dark skin tone, medium-light skin tone</annotation>
+		<annotation cp="🧑🏾‍❤‍🧑🏽">couple | couple with heart | love | medium skin tone | medium-dark skin tone | person</annotation>
+		<annotation cp="🧑🏾‍❤‍🧑🏽" type="tts">couple with heart: person, person, medium-dark skin tone, medium skin tone</annotation>
+		<annotation cp="🧑🏾‍❤‍🧑🏿">couple | couple with heart | dark skin tone | love | medium-dark skin tone | person</annotation>
+		<annotation cp="🧑🏾‍❤‍🧑🏿" type="tts">couple with heart: person, person, medium-dark skin tone, dark skin tone</annotation>
+		<annotation cp="🧑🏿‍❤‍🧑🏻">couple | couple with heart | dark skin tone | light skin tone | love | person</annotation>
+		<annotation cp="🧑🏿‍❤‍🧑🏻" type="tts">couple with heart: person, person, dark skin tone, light skin tone</annotation>
+		<annotation cp="🧑🏿‍❤‍🧑🏼">couple | couple with heart | dark skin tone | love | medium-light skin tone | person</annotation>
+		<annotation cp="🧑🏿‍❤‍🧑🏼" type="tts">couple with heart: person, person, dark skin tone, medium-light skin tone</annotation>
+		<annotation cp="🧑🏿‍❤‍🧑🏽">couple | couple with heart | dark skin tone | love | medium skin tone | person</annotation>
+		<annotation cp="🧑🏿‍❤‍🧑🏽" type="tts">couple with heart: person, person, dark skin tone, medium skin tone</annotation>
+		<annotation cp="🧑🏿‍❤‍🧑🏾">couple | couple with heart | dark skin tone | love | medium-dark skin tone | person</annotation>
+		<annotation cp="🧑🏿‍❤‍🧑🏾" type="tts">couple with heart: person, person, dark skin tone, medium-dark skin tone</annotation>
+		<annotation cp="🧑‍🦰">adult | gender-neutral | person | red hair | unspecified gender</annotation>
+		<annotation cp="🧑‍🦰" type="tts">person: red hair</annotation>
+		<annotation cp="🧑🏻‍🦰">adult | gender-neutral | light skin tone | person | red hair | unspecified gender</annotation>
+		<annotation cp="🧑🏻‍🦰" type="tts">person: light skin tone, red hair</annotation>
+		<annotation cp="🧑🏼‍🦰">adult | gender-neutral | medium-light skin tone | person | red hair | unspecified gender</annotation>
+		<annotation cp="🧑🏼‍🦰" type="tts">person: medium-light skin tone, red hair</annotation>
+		<annotation cp="🧑🏽‍🦰">adult | gender-neutral | medium skin tone | person | red hair | unspecified gender</annotation>
+		<annotation cp="🧑🏽‍🦰" type="tts">person: medium skin tone, red hair</annotation>
+		<annotation cp="🧑🏾‍🦰">adult | gender-neutral | medium-dark skin tone | person | red hair | unspecified gender</annotation>
+		<annotation cp="🧑🏾‍🦰" type="tts">person: medium-dark skin tone, red hair</annotation>
+		<annotation cp="🧑🏿‍🦰">adult | dark skin tone | gender-neutral | person | red hair | unspecified gender</annotation>
+		<annotation cp="🧑🏿‍🦰" type="tts">person: dark skin tone, red hair</annotation>
+		<annotation cp="🧑‍🦱">adult | curly hair | gender-neutral | person | unspecified gender</annotation>
+		<annotation cp="🧑‍🦱" type="tts">person: curly hair</annotation>
+		<annotation cp="🧑🏻‍🦱">adult | curly hair | gender-neutral | light skin tone | person | unspecified gender</annotation>
+		<annotation cp="🧑🏻‍🦱" type="tts">person: light skin tone, curly hair</annotation>
+		<annotation cp="🧑🏼‍🦱">adult | curly hair | gender-neutral | medium-light skin tone | person | unspecified gender</annotation>
+		<annotation cp="🧑🏼‍🦱" type="tts">person: medium-light skin tone, curly hair</annotation>
+		<annotation cp="🧑🏽‍🦱">adult | curly hair | gender-neutral | medium skin tone | person | unspecified gender</annotation>
+		<annotation cp="🧑🏽‍🦱" type="tts">person: medium skin tone, curly hair</annotation>
+		<annotation cp="🧑🏾‍🦱">adult | curly hair | gender-neutral | medium-dark skin tone | person | unspecified gender</annotation>
+		<annotation cp="🧑🏾‍🦱" type="tts">person: medium-dark skin tone, curly hair</annotation>
+		<annotation cp="🧑🏿‍🦱">adult | curly hair | dark skin tone | gender-neutral | person | unspecified gender</annotation>
+		<annotation cp="🧑🏿‍🦱" type="tts">person: dark skin tone, curly hair</annotation>
+		<annotation cp="🧑‍🦳">adult | gender-neutral | person | unspecified gender | white hair</annotation>
+		<annotation cp="🧑‍🦳" type="tts">person: white hair</annotation>
+		<annotation cp="🧑🏻‍🦳">adult | gender-neutral | light skin tone | person | unspecified gender | white hair</annotation>
+		<annotation cp="🧑🏻‍🦳" type="tts">person: light skin tone, white hair</annotation>
+		<annotation cp="🧑🏼‍🦳">adult | gender-neutral | medium-light skin tone | person | unspecified gender | white hair</annotation>
+		<annotation cp="🧑🏼‍🦳" type="tts">person: medium-light skin tone, white hair</annotation>
+		<annotation cp="🧑🏽‍🦳">adult | gender-neutral | medium skin tone | person | unspecified gender | white hair</annotation>
+		<annotation cp="🧑🏽‍🦳" type="tts">person: medium skin tone, white hair</annotation>
+		<annotation cp="🧑🏾‍🦳">adult | gender-neutral | medium-dark skin tone | person | unspecified gender | white hair</annotation>
+		<annotation cp="🧑🏾‍🦳" type="tts">person: medium-dark skin tone, white hair</annotation>
+		<annotation cp="🧑🏿‍🦳">adult | dark skin tone | gender-neutral | person | unspecified gender | white hair</annotation>
+		<annotation cp="🧑🏿‍🦳" type="tts">person: dark skin tone, white hair</annotation>
+		<annotation cp="🧑‍🦲">adult | bald | gender-neutral | person | unspecified gender</annotation>
+		<annotation cp="🧑‍🦲" type="tts">person: bald</annotation>
+		<annotation cp="🧑🏻‍🦲">adult | bald | gender-neutral | light skin tone | person | unspecified gender</annotation>
+		<annotation cp="🧑🏻‍🦲" type="tts">person: light skin tone, bald</annotation>
+		<annotation cp="🧑🏼‍🦲">adult | bald | gender-neutral | medium-light skin tone | person | unspecified gender</annotation>
+		<annotation cp="🧑🏼‍🦲" type="tts">person: medium-light skin tone, bald</annotation>
+		<annotation cp="🧑🏽‍🦲">adult | bald | gender-neutral | medium skin tone | person | unspecified gender</annotation>
+		<annotation cp="🧑🏽‍🦲" type="tts">person: medium skin tone, bald</annotation>
+		<annotation cp="🧑🏾‍🦲">adult | bald | gender-neutral | medium-dark skin tone | person | unspecified gender</annotation>
+		<annotation cp="🧑🏾‍🦲" type="tts">person: medium-dark skin tone, bald</annotation>
+		<annotation cp="🧑🏿‍🦲">adult | bald | dark skin tone | gender-neutral | person | unspecified gender</annotation>
+		<annotation cp="🧑🏿‍🦲" type="tts">person: dark skin tone, bald</annotation>
+		<annotation cp="👨🏻">adult | light skin tone | man</annotation>
+		<annotation cp="👨🏻" type="tts">man: light skin tone</annotation>
+		<annotation cp="👨🏼">adult | man | medium-light skin tone</annotation>
+		<annotation cp="👨🏼" type="tts">man: medium-light skin tone</annotation>
+		<annotation cp="👨🏽">adult | man | medium skin tone</annotation>
+		<annotation cp="👨🏽" type="tts">man: medium skin tone</annotation>
+		<annotation cp="👨🏾">adult | man | medium-dark skin tone</annotation>
+		<annotation cp="👨🏾" type="tts">man: medium-dark skin tone</annotation>
+		<annotation cp="👨🏿">adult | dark skin tone | man</annotation>
+		<annotation cp="👨🏿" type="tts">man: dark skin tone</annotation>
+		<annotation cp="🧔🏻">beard | light skin tone | person | person: beard</annotation>
+		<annotation cp="🧔🏻" type="tts">person: light skin tone, beard</annotation>
+		<annotation cp="🧔🏼">beard | medium-light skin tone | person | person: beard</annotation>
+		<annotation cp="🧔🏼" type="tts">person: medium-light skin tone, beard</annotation>
+		<annotation cp="🧔🏽">beard | medium skin tone | person | person: beard</annotation>
+		<annotation cp="🧔🏽" type="tts">person: medium skin tone, beard</annotation>
+		<annotation cp="🧔🏾">beard | medium-dark skin tone | person | person: beard</annotation>
+		<annotation cp="🧔🏾" type="tts">person: medium-dark skin tone, beard</annotation>
+		<annotation cp="🧔🏿">beard | dark skin tone | person | person: beard</annotation>
+		<annotation cp="🧔🏿" type="tts">person: dark skin tone, beard</annotation>
+		<annotation cp="🧔🏻‍♂">beard | light skin tone | man | man: beard</annotation>
+		<annotation cp="🧔🏻‍♂" type="tts">man: light skin tone, beard</annotation>
+		<annotation cp="🧔🏼‍♂">beard | man | man: beard | medium-light skin tone</annotation>
+		<annotation cp="🧔🏼‍♂" type="tts">man: medium-light skin tone, beard</annotation>
+		<annotation cp="🧔🏽‍♂">beard | man | man: beard | medium skin tone</annotation>
+		<annotation cp="🧔🏽‍♂" type="tts">man: medium skin tone, beard</annotation>
+		<annotation cp="🧔🏾‍♂">beard | man | man: beard | medium-dark skin tone</annotation>
+		<annotation cp="🧔🏾‍♂" type="tts">man: medium-dark skin tone, beard</annotation>
+		<annotation cp="🧔🏿‍♂">beard | dark skin tone | man | man: beard</annotation>
+		<annotation cp="🧔🏿‍♂" type="tts">man: dark skin tone, beard</annotation>
+		<annotation cp="👱🏻‍♂">blond | blond-haired man | hair | light skin tone | man | man: blond hair</annotation>
+		<annotation cp="👱🏻‍♂" type="tts">man: light skin tone, blond hair</annotation>
+		<annotation cp="👱🏼‍♂">blond | blond-haired man | hair | man | man: blond hair | medium-light skin tone</annotation>
+		<annotation cp="👱🏼‍♂" type="tts">man: medium-light skin tone, blond hair</annotation>
+		<annotation cp="👱🏽‍♂">blond | blond-haired man | hair | man | man: blond hair | medium skin tone</annotation>
+		<annotation cp="👱🏽‍♂" type="tts">man: medium skin tone, blond hair</annotation>
+		<annotation cp="👱🏾‍♂">blond | blond-haired man | hair | man | man: blond hair | medium-dark skin tone</annotation>
+		<annotation cp="👱🏾‍♂" type="tts">man: medium-dark skin tone, blond hair</annotation>
+		<annotation cp="👱🏿‍♂">blond | blond-haired man | dark skin tone | hair | man | man: blond hair</annotation>
+		<annotation cp="👱🏿‍♂" type="tts">man: dark skin tone, blond hair</annotation>
+		<annotation cp="👨🏻‍❤‍💋‍👨🏻">couple | kiss | light skin tone | man</annotation>
+		<annotation cp="👨🏻‍❤‍💋‍👨🏻" type="tts">kiss: man, man, light skin tone</annotation>
+		<annotation cp="👨🏻‍❤‍💋‍👨🏼">couple | kiss | light skin tone | man | medium-light skin tone</annotation>
+		<annotation cp="👨🏻‍❤‍💋‍👨🏼" type="tts">kiss: man, man, light skin tone, medium-light skin tone</annotation>
+		<annotation cp="👨🏻‍❤‍💋‍👨🏽">couple | kiss | light skin tone | man | medium skin tone</annotation>
+		<annotation cp="👨🏻‍❤‍💋‍👨🏽" type="tts">kiss: man, man, light skin tone, medium skin tone</annotation>
+		<annotation cp="👨🏻‍❤‍💋‍👨🏾">couple | kiss | light skin tone | man | medium-dark skin tone</annotation>
+		<annotation cp="👨🏻‍❤‍💋‍👨🏾" type="tts">kiss: man, man, light skin tone, medium-dark skin tone</annotation>
+		<annotation cp="👨🏻‍❤‍💋‍👨🏿">couple | dark skin tone | kiss | light skin tone | man</annotation>
+		<annotation cp="👨🏻‍❤‍💋‍👨🏿" type="tts">kiss: man, man, light skin tone, dark skin tone</annotation>
+		<annotation cp="👨🏼‍❤‍💋‍👨🏻">couple | kiss | light skin tone | man | medium-light skin tone</annotation>
+		<annotation cp="👨🏼‍❤‍💋‍👨🏻" type="tts">kiss: man, man, medium-light skin tone, light skin tone</annotation>
+		<annotation cp="👨🏼‍❤‍💋‍👨🏼">couple | kiss | man | medium-light skin tone</annotation>
+		<annotation cp="👨🏼‍❤‍💋‍👨🏼" type="tts">kiss: man, man, medium-light skin tone</annotation>
+		<annotation cp="👨🏼‍❤‍💋‍👨🏽">couple | kiss | man | medium skin tone | medium-light skin tone</annotation>
+		<annotation cp="👨🏼‍❤‍💋‍👨🏽" type="tts">kiss: man, man, medium-light skin tone, medium skin tone</annotation>
+		<annotation cp="👨🏼‍❤‍💋‍👨🏾">couple | kiss | man | medium-dark skin tone | medium-light skin tone</annotation>
+		<annotation cp="👨🏼‍❤‍💋‍👨🏾" type="tts">kiss: man, man, medium-light skin tone, medium-dark skin tone</annotation>
+		<annotation cp="👨🏼‍❤‍💋‍👨🏿">couple | dark skin tone | kiss | man | medium-light skin tone</annotation>
+		<annotation cp="👨🏼‍❤‍💋‍👨🏿" type="tts">kiss: man, man, medium-light skin tone, dark skin tone</annotation>
+		<annotation cp="👨🏽‍❤‍💋‍👨🏻">couple | kiss | light skin tone | man | medium skin tone</annotation>
+		<annotation cp="👨🏽‍❤‍💋‍👨🏻" type="tts">kiss: man, man, medium skin tone, light skin tone</annotation>
+		<annotation cp="👨🏽‍❤‍💋‍👨🏼">couple | kiss | man | medium skin tone | medium-light skin tone</annotation>
+		<annotation cp="👨🏽‍❤‍💋‍👨🏼" type="tts">kiss: man, man, medium skin tone, medium-light skin tone</annotation>
+		<annotation cp="👨🏽‍❤‍💋‍👨🏽">couple | kiss | man | medium skin tone</annotation>
+		<annotation cp="👨🏽‍❤‍💋‍👨🏽" type="tts">kiss: man, man, medium skin tone</annotation>
+		<annotation cp="👨🏽‍❤‍💋‍👨🏾">couple | kiss | man | medium skin tone | medium-dark skin tone</annotation>
+		<annotation cp="👨🏽‍❤‍💋‍👨🏾" type="tts">kiss: man, man, medium skin tone, medium-dark skin tone</annotation>
+		<annotation cp="👨🏽‍❤‍💋‍👨🏿">couple | dark skin tone | kiss | man | medium skin tone</annotation>
+		<annotation cp="👨🏽‍❤‍💋‍👨🏿" type="tts">kiss: man, man, medium skin tone, dark skin tone</annotation>
+		<annotation cp="👨🏾‍❤‍💋‍👨🏻">couple | kiss | light skin tone | man | medium-dark skin tone</annotation>
+		<annotation cp="👨🏾‍❤‍💋‍👨🏻" type="tts">kiss: man, man, medium-dark skin tone, light skin tone</annotation>
+		<annotation cp="👨🏾‍❤‍💋‍👨🏼">couple | kiss | man | medium-dark skin tone | medium-light skin tone</annotation>
+		<annotation cp="👨🏾‍❤‍💋‍👨🏼" type="tts">kiss: man, man, medium-dark skin tone, medium-light skin tone</annotation>
+		<annotation cp="👨🏾‍❤‍💋‍👨🏽">couple | kiss | man | medium skin tone | medium-dark skin tone</annotation>
+		<annotation cp="👨🏾‍❤‍💋‍👨🏽" type="tts">kiss: man, man, medium-dark skin tone, medium skin tone</annotation>
+		<annotation cp="👨🏾‍❤‍💋‍👨🏾">couple | kiss | man | medium-dark skin tone</annotation>
+		<annotation cp="👨🏾‍❤‍💋‍👨🏾" type="tts">kiss: man, man, medium-dark skin tone</annotation>
+		<annotation cp="👨🏾‍❤‍💋‍👨🏿">couple | dark skin tone | kiss | man | medium-dark skin tone</annotation>
+		<annotation cp="👨🏾‍❤‍💋‍👨🏿" type="tts">kiss: man, man, medium-dark skin tone, dark skin tone</annotation>
+		<annotation cp="👨🏿‍❤‍💋‍👨🏻">couple | dark skin tone | kiss | light skin tone | man</annotation>
+		<annotation cp="👨🏿‍❤‍💋‍👨🏻" type="tts">kiss: man, man, dark skin tone, light skin tone</annotation>
+		<annotation cp="👨🏿‍❤‍💋‍👨🏼">couple | dark skin tone | kiss | man | medium-light skin tone</annotation>
+		<annotation cp="👨🏿‍❤‍💋‍👨🏼" type="tts">kiss: man, man, dark skin tone, medium-light skin tone</annotation>
+		<annotation cp="👨🏿‍❤‍💋‍👨🏽">couple | dark skin tone | kiss | man | medium skin tone</annotation>
+		<annotation cp="👨🏿‍❤‍💋‍👨🏽" type="tts">kiss: man, man, dark skin tone, medium skin tone</annotation>
+		<annotation cp="👨🏿‍❤‍💋‍👨🏾">couple | dark skin tone | kiss | man | medium-dark skin tone</annotation>
+		<annotation cp="👨🏿‍❤‍💋‍👨🏾" type="tts">kiss: man, man, dark skin tone, medium-dark skin tone</annotation>
+		<annotation cp="👨🏿‍❤‍💋‍👨🏿">couple | dark skin tone | kiss | man</annotation>
+		<annotation cp="👨🏿‍❤‍💋‍👨🏿" type="tts">kiss: man, man, dark skin tone</annotation>
+		<annotation cp="👨🏻‍❤‍👨🏻">couple | couple with heart | light skin tone | love | man</annotation>
+		<annotation cp="👨🏻‍❤‍👨🏻" type="tts">couple with heart: man, man, light skin tone</annotation>
+		<annotation cp="👨🏻‍❤‍👨🏼">couple | couple with heart | light skin tone | love | man | medium-light skin tone</annotation>
+		<annotation cp="👨🏻‍❤‍👨🏼" type="tts">couple with heart: man, man, light skin tone, medium-light skin tone</annotation>
+		<annotation cp="👨🏻‍❤‍👨🏽">couple | couple with heart | light skin tone | love | man | medium skin tone</annotation>
+		<annotation cp="👨🏻‍❤‍👨🏽" type="tts">couple with heart: man, man, light skin tone, medium skin tone</annotation>
+		<annotation cp="👨🏻‍❤‍👨🏾">couple | couple with heart | light skin tone | love | man | medium-dark skin tone</annotation>
+		<annotation cp="👨🏻‍❤‍👨🏾" type="tts">couple with heart: man, man, light skin tone, medium-dark skin tone</annotation>
+		<annotation cp="👨🏻‍❤‍👨🏿">couple | couple with heart | dark skin tone | light skin tone | love | man</annotation>
+		<annotation cp="👨🏻‍❤‍👨🏿" type="tts">couple with heart: man, man, light skin tone, dark skin tone</annotation>
+		<annotation cp="👨🏼‍❤‍👨🏻">couple | couple with heart | light skin tone | love | man | medium-light skin tone</annotation>
+		<annotation cp="👨🏼‍❤‍👨🏻" type="tts">couple with heart: man, man, medium-light skin tone, light skin tone</annotation>
+		<annotation cp="👨🏼‍❤‍👨🏼">couple | couple with heart | love | man | medium-light skin tone</annotation>
+		<annotation cp="👨🏼‍❤‍👨🏼" type="tts">couple with heart: man, man, medium-light skin tone</annotation>
+		<annotation cp="👨🏼‍❤‍👨🏽">couple | couple with heart | love | man | medium skin tone | medium-light skin tone</annotation>
+		<annotation cp="👨🏼‍❤‍👨🏽" type="tts">couple with heart: man, man, medium-light skin tone, medium skin tone</annotation>
+		<annotation cp="👨🏼‍❤‍👨🏾">couple | couple with heart | love | man | medium-dark skin tone | medium-light skin tone</annotation>
+		<annotation cp="👨🏼‍❤‍👨🏾" type="tts">couple with heart: man, man, medium-light skin tone, medium-dark skin tone</annotation>
+		<annotation cp="👨🏼‍❤‍👨🏿">couple | couple with heart | dark skin tone | love | man | medium-light skin tone</annotation>
+		<annotation cp="👨🏼‍❤‍👨🏿" type="tts">couple with heart: man, man, medium-light skin tone, dark skin tone</annotation>
+		<annotation cp="👨🏽‍❤‍👨🏻">couple | couple with heart | light skin tone | love | man | medium skin tone</annotation>
+		<annotation cp="👨🏽‍❤‍👨🏻" type="tts">couple with heart: man, man, medium skin tone, light skin tone</annotation>
+		<annotation cp="👨🏽‍❤‍👨🏼">couple | couple with heart | love | man | medium skin tone | medium-light skin tone</annotation>
+		<annotation cp="👨🏽‍❤‍👨🏼" type="tts">couple with heart: man, man, medium skin tone, medium-light skin tone</annotation>
+		<annotation cp="👨🏽‍❤‍👨🏽">couple | couple with heart | love | man | medium skin tone</annotation>
+		<annotation cp="👨🏽‍❤‍👨🏽" type="tts">couple with heart: man, man, medium skin tone</annotation>
+		<annotation cp="👨🏽‍❤‍👨🏾">couple | couple with heart | love | man | medium skin tone | medium-dark skin tone</annotation>
+		<annotation cp="👨🏽‍❤‍👨🏾" type="tts">couple with heart: man, man, medium skin tone, medium-dark skin tone</annotation>
+		<annotation cp="👨🏽‍❤‍👨🏿">couple | couple with heart | dark skin tone | love | man | medium skin tone</annotation>
+		<annotation cp="👨🏽‍❤‍👨🏿" type="tts">couple with heart: man, man, medium skin tone, dark skin tone</annotation>
+		<annotation cp="👨🏾‍❤‍👨🏻">couple | couple with heart | light skin tone | love | man | medium-dark skin tone</annotation>
+		<annotation cp="👨🏾‍❤‍👨🏻" type="tts">couple with heart: man, man, medium-dark skin tone, light skin tone</annotation>
+		<annotation cp="👨🏾‍❤‍👨🏼">couple | couple with heart | love | man | medium-dark skin tone | medium-light skin tone</annotation>
+		<annotation cp="👨🏾‍❤‍👨🏼" type="tts">couple with heart: man, man, medium-dark skin tone, medium-light skin tone</annotation>
+		<annotation cp="👨🏾‍❤‍👨🏽">couple | couple with heart | love | man | medium skin tone | medium-dark skin tone</annotation>
+		<annotation cp="👨🏾‍❤‍👨🏽" type="tts">couple with heart: man, man, medium-dark skin tone, medium skin tone</annotation>
+		<annotation cp="👨🏾‍❤‍👨🏾">couple | couple with heart | love | man | medium-dark skin tone</annotation>
+		<annotation cp="👨🏾‍❤‍👨🏾" type="tts">couple with heart: man, man, medium-dark skin tone</annotation>
+		<annotation cp="👨🏾‍❤‍👨🏿">couple | couple with heart | dark skin tone | love | man | medium-dark skin tone</annotation>
+		<annotation cp="👨🏾‍❤‍👨🏿" type="tts">couple with heart: man, man, medium-dark skin tone, dark skin tone</annotation>
+		<annotation cp="👨🏿‍❤‍👨🏻">couple | couple with heart | dark skin tone | light skin tone | love | man</annotation>
+		<annotation cp="👨🏿‍❤‍👨🏻" type="tts">couple with heart: man, man, dark skin tone, light skin tone</annotation>
+		<annotation cp="👨🏿‍❤‍👨🏼">couple | couple with heart | dark skin tone | love | man | medium-light skin tone</annotation>
+		<annotation cp="👨🏿‍❤‍👨🏼" type="tts">couple with heart: man, man, dark skin tone, medium-light skin tone</annotation>
+		<annotation cp="👨🏿‍❤‍👨🏽">couple | couple with heart | dark skin tone | love | man | medium skin tone</annotation>
+		<annotation cp="👨🏿‍❤‍👨🏽" type="tts">couple with heart: man, man, dark skin tone, medium skin tone</annotation>
+		<annotation cp="👨🏿‍❤‍👨🏾">couple | couple with heart | dark skin tone | love | man | medium-dark skin tone</annotation>
+		<annotation cp="👨🏿‍❤‍👨🏾" type="tts">couple with heart: man, man, dark skin tone, medium-dark skin tone</annotation>
+		<annotation cp="👨🏿‍❤‍👨🏿">couple | couple with heart | dark skin tone | love | man</annotation>
+		<annotation cp="👨🏿‍❤‍👨🏿" type="tts">couple with heart: man, man, dark skin tone</annotation>
+		<annotation cp="👨‍🦰">adult | man | red hair</annotation>
+		<annotation cp="👨‍🦰" type="tts">man: red hair</annotation>
+		<annotation cp="👨🏻‍🦰">adult | light skin tone | man | red hair</annotation>
+		<annotation cp="👨🏻‍🦰" type="tts">man: light skin tone, red hair</annotation>
+		<annotation cp="👨🏼‍🦰">adult | man | medium-light skin tone | red hair</annotation>
+		<annotation cp="👨🏼‍🦰" type="tts">man: medium-light skin tone, red hair</annotation>
+		<annotation cp="👨🏽‍🦰">adult | man | medium skin tone | red hair</annotation>
+		<annotation cp="👨🏽‍🦰" type="tts">man: medium skin tone, red hair</annotation>
+		<annotation cp="👨🏾‍🦰">adult | man | medium-dark skin tone | red hair</annotation>
+		<annotation cp="👨🏾‍🦰" type="tts">man: medium-dark skin tone, red hair</annotation>
+		<annotation cp="👨🏿‍🦰">adult | dark skin tone | man | red hair</annotation>
+		<annotation cp="👨🏿‍🦰" type="tts">man: dark skin tone, red hair</annotation>
+		<annotation cp="👨‍🦱">adult | curly hair | man</annotation>
+		<annotation cp="👨‍🦱" type="tts">man: curly hair</annotation>
+		<annotation cp="👨🏻‍🦱">adult | curly hair | light skin tone | man</annotation>
+		<annotation cp="👨🏻‍🦱" type="tts">man: light skin tone, curly hair</annotation>
+		<annotation cp="👨🏼‍🦱">adult | curly hair | man | medium-light skin tone</annotation>
+		<annotation cp="👨🏼‍🦱" type="tts">man: medium-light skin tone, curly hair</annotation>
+		<annotation cp="👨🏽‍🦱">adult | curly hair | man | medium skin tone</annotation>
+		<annotation cp="👨🏽‍🦱" type="tts">man: medium skin tone, curly hair</annotation>
+		<annotation cp="👨🏾‍🦱">adult | curly hair | man | medium-dark skin tone</annotation>
+		<annotation cp="👨🏾‍🦱" type="tts">man: medium-dark skin tone, curly hair</annotation>
+		<annotation cp="👨🏿‍🦱">adult | curly hair | dark skin tone | man</annotation>
+		<annotation cp="👨🏿‍🦱" type="tts">man: dark skin tone, curly hair</annotation>
+		<annotation cp="👨‍🦳">adult | man | white hair</annotation>
+		<annotation cp="👨‍🦳" type="tts">man: white hair</annotation>
+		<annotation cp="👨🏻‍🦳">adult | light skin tone | man | white hair</annotation>
+		<annotation cp="👨🏻‍🦳" type="tts">man: light skin tone, white hair</annotation>
+		<annotation cp="👨🏼‍🦳">adult | man | medium-light skin tone | white hair</annotation>
+		<annotation cp="👨🏼‍🦳" type="tts">man: medium-light skin tone, white hair</annotation>
+		<annotation cp="👨🏽‍🦳">adult | man | medium skin tone | white hair</annotation>
+		<annotation cp="👨🏽‍🦳" type="tts">man: medium skin tone, white hair</annotation>
+		<annotation cp="👨🏾‍🦳">adult | man | medium-dark skin tone | white hair</annotation>
+		<annotation cp="👨🏾‍🦳" type="tts">man: medium-dark skin tone, white hair</annotation>
+		<annotation cp="👨🏿‍🦳">adult | dark skin tone | man | white hair</annotation>
+		<annotation cp="👨🏿‍🦳" type="tts">man: dark skin tone, white hair</annotation>
+		<annotation cp="👨‍🦲">adult | bald | man</annotation>
+		<annotation cp="👨‍🦲" type="tts">man: bald</annotation>
+		<annotation cp="👨🏻‍🦲">adult | bald | light skin tone | man</annotation>
+		<annotation cp="👨🏻‍🦲" type="tts">man: light skin tone, bald</annotation>
+		<annotation cp="👨🏼‍🦲">adult | bald | man | medium-light skin tone</annotation>
+		<annotation cp="👨🏼‍🦲" type="tts">man: medium-light skin tone, bald</annotation>
+		<annotation cp="👨🏽‍🦲">adult | bald | man | medium skin tone</annotation>
+		<annotation cp="👨🏽‍🦲" type="tts">man: medium skin tone, bald</annotation>
+		<annotation cp="👨🏾‍🦲">adult | bald | man | medium-dark skin tone</annotation>
+		<annotation cp="👨🏾‍🦲" type="tts">man: medium-dark skin tone, bald</annotation>
+		<annotation cp="👨🏿‍🦲">adult | bald | dark skin tone | man</annotation>
+		<annotation cp="👨🏿‍🦲" type="tts">man: dark skin tone, bald</annotation>
+		<annotation cp="👩🏻">adult | light skin tone | woman</annotation>
+		<annotation cp="👩🏻" type="tts">woman: light skin tone</annotation>
+		<annotation cp="👩🏼">adult | medium-light skin tone | woman</annotation>
+		<annotation cp="👩🏼" type="tts">woman: medium-light skin tone</annotation>
+		<annotation cp="👩🏽">adult | medium skin tone | woman</annotation>
+		<annotation cp="👩🏽" type="tts">woman: medium skin tone</annotation>
+		<annotation cp="👩🏾">adult | medium-dark skin tone | woman</annotation>
+		<annotation cp="👩🏾" type="tts">woman: medium-dark skin tone</annotation>
+		<annotation cp="👩🏿">adult | dark skin tone | woman</annotation>
+		<annotation cp="👩🏿" type="tts">woman: dark skin tone</annotation>
+		<annotation cp="🧔🏻‍♀">beard | light skin tone | woman | woman: beard</annotation>
+		<annotation cp="🧔🏻‍♀" type="tts">woman: light skin tone, beard</annotation>
+		<annotation cp="🧔🏼‍♀">beard | medium-light skin tone | woman | woman: beard</annotation>
+		<annotation cp="🧔🏼‍♀" type="tts">woman: medium-light skin tone, beard</annotation>
+		<annotation cp="🧔🏽‍♀">beard | medium skin tone | woman | woman: beard</annotation>
+		<annotation cp="🧔🏽‍♀" type="tts">woman: medium skin tone, beard</annotation>
+		<annotation cp="🧔🏾‍♀">beard | medium-dark skin tone | woman | woman: beard</annotation>
+		<annotation cp="🧔🏾‍♀" type="tts">woman: medium-dark skin tone, beard</annotation>
+		<annotation cp="🧔🏿‍♀">beard | dark skin tone | woman | woman: beard</annotation>
+		<annotation cp="🧔🏿‍♀" type="tts">woman: dark skin tone, beard</annotation>
+		<annotation cp="👱🏻‍♀">blond hair | blond-haired woman | blonde | hair | light skin tone | woman | woman: blond hair</annotation>
+		<annotation cp="👱🏻‍♀" type="tts">woman: light skin tone, blond hair</annotation>
+		<annotation cp="👱🏼‍♀">blond hair | blond-haired woman | blonde | hair | medium-light skin tone | woman | woman: blond hair</annotation>
+		<annotation cp="👱🏼‍♀" type="tts">woman: medium-light skin tone, blond hair</annotation>
+		<annotation cp="👱🏽‍♀">blond hair | blond-haired woman | blonde | hair | medium skin tone | woman | woman: blond hair</annotation>
+		<annotation cp="👱🏽‍♀" type="tts">woman: medium skin tone, blond hair</annotation>
+		<annotation cp="👱🏾‍♀">blond hair | blond-haired woman | blonde | hair | medium-dark skin tone | woman | woman: blond hair</annotation>
+		<annotation cp="👱🏾‍♀" type="tts">woman: medium-dark skin tone, blond hair</annotation>
+		<annotation cp="👱🏿‍♀">blond hair | blond-haired woman | blonde | dark skin tone | hair | woman | woman: blond hair</annotation>
+		<annotation cp="👱🏿‍♀" type="tts">woman: dark skin tone, blond hair</annotation>
+		<annotation cp="👩🏻‍❤‍💋‍👨🏻">couple | kiss | light skin tone | man | woman</annotation>
+		<annotation cp="👩🏻‍❤‍💋‍👨🏻" type="tts">kiss: woman, man, light skin tone</annotation>
+		<annotation cp="👩🏻‍❤‍💋‍👨🏼">couple | kiss | light skin tone | man | medium-light skin tone | woman</annotation>
+		<annotation cp="👩🏻‍❤‍💋‍👨🏼" type="tts">kiss: woman, man, light skin tone, medium-light skin tone</annotation>
+		<annotation cp="👩🏻‍❤‍💋‍👨🏽">couple | kiss | light skin tone | man | medium skin tone | woman</annotation>
+		<annotation cp="👩🏻‍❤‍💋‍👨🏽" type="tts">kiss: woman, man, light skin tone, medium skin tone</annotation>
+		<annotation cp="👩🏻‍❤‍💋‍👨🏾">couple | kiss | light skin tone | man | medium-dark skin tone | woman</annotation>
+		<annotation cp="👩🏻‍❤‍💋‍👨🏾" type="tts">kiss: woman, man, light skin tone, medium-dark skin tone</annotation>
+		<annotation cp="👩🏻‍❤‍💋‍👨🏿">couple | dark skin tone | kiss | light skin tone | man | woman</annotation>
+		<annotation cp="👩🏻‍❤‍💋‍👨🏿" type="tts">kiss: woman, man, light skin tone, dark skin tone</annotation>
+		<annotation cp="👩🏼‍❤‍💋‍👨🏻">couple | kiss | light skin tone | man | medium-light skin tone | woman</annotation>
+		<annotation cp="👩🏼‍❤‍💋‍👨🏻" type="tts">kiss: woman, man, medium-light skin tone, light skin tone</annotation>
+		<annotation cp="👩🏼‍❤‍💋‍👨🏼">couple | kiss | man | medium-light skin tone | woman</annotation>
+		<annotation cp="👩🏼‍❤‍💋‍👨🏼" type="tts">kiss: woman, man, medium-light skin tone</annotation>
+		<annotation cp="👩🏼‍❤‍💋‍👨🏽">couple | kiss | man | medium skin tone | medium-light skin tone | woman</annotation>
+		<annotation cp="👩🏼‍❤‍💋‍👨🏽" type="tts">kiss: woman, man, medium-light skin tone, medium skin tone</annotation>
+		<annotation cp="👩🏼‍❤‍💋‍👨🏾">couple | kiss | man | medium-dark skin tone | medium-light skin tone | woman</annotation>
+		<annotation cp="👩🏼‍❤‍💋‍👨🏾" type="tts">kiss: woman, man, medium-light skin tone, medium-dark skin tone</annotation>
+		<annotation cp="👩🏼‍❤‍💋‍👨🏿">couple | dark skin tone | kiss | man | medium-light skin tone | woman</annotation>
+		<annotation cp="👩🏼‍❤‍💋‍👨🏿" type="tts">kiss: woman, man, medium-light skin tone, dark skin tone</annotation>
+		<annotation cp="👩🏽‍❤‍💋‍👨🏻">couple | kiss | light skin tone | man | medium skin tone | woman</annotation>
+		<annotation cp="👩🏽‍❤‍💋‍👨🏻" type="tts">kiss: woman, man, medium skin tone, light skin tone</annotation>
+		<annotation cp="👩🏽‍❤‍💋‍👨🏼">couple | kiss | man | medium skin tone | medium-light skin tone | woman</annotation>
+		<annotation cp="👩🏽‍❤‍💋‍👨🏼" type="tts">kiss: woman, man, medium skin tone, medium-light skin tone</annotation>
+		<annotation cp="👩🏽‍❤‍💋‍👨🏽">couple | kiss | man | medium skin tone | woman</annotation>
+		<annotation cp="👩🏽‍❤‍💋‍👨🏽" type="tts">kiss: woman, man, medium skin tone</annotation>
+		<annotation cp="👩🏽‍❤‍💋‍👨🏾">couple | kiss | man | medium skin tone | medium-dark skin tone | woman</annotation>
+		<annotation cp="👩🏽‍❤‍💋‍👨🏾" type="tts">kiss: woman, man, medium skin tone, medium-dark skin tone</annotation>
+		<annotation cp="👩🏽‍❤‍💋‍👨🏿">couple | dark skin tone | kiss | man | medium skin tone | woman</annotation>
+		<annotation cp="👩🏽‍❤‍💋‍👨🏿" type="tts">kiss: woman, man, medium skin tone, dark skin tone</annotation>
+		<annotation cp="👩🏾‍❤‍💋‍👨🏻">couple | kiss | light skin tone | man | medium-dark skin tone | woman</annotation>
+		<annotation cp="👩🏾‍❤‍💋‍👨🏻" type="tts">kiss: woman, man, medium-dark skin tone, light skin tone</annotation>
+		<annotation cp="👩🏾‍❤‍💋‍👨🏼">couple | kiss | man | medium-dark skin tone | medium-light skin tone | woman</annotation>
+		<annotation cp="👩🏾‍❤‍💋‍👨🏼" type="tts">kiss: woman, man, medium-dark skin tone, medium-light skin tone</annotation>
+		<annotation cp="👩🏾‍❤‍💋‍👨🏽">couple | kiss | man | medium skin tone | medium-dark skin tone | woman</annotation>
+		<annotation cp="👩🏾‍❤‍💋‍👨🏽" type="tts">kiss: woman, man, medium-dark skin tone, medium skin tone</annotation>
+		<annotation cp="👩🏾‍❤‍💋‍👨🏾">couple | kiss | man | medium-dark skin tone | woman</annotation>
+		<annotation cp="👩🏾‍❤‍💋‍👨🏾" type="tts">kiss: woman, man, medium-dark skin tone</annotation>
+		<annotation cp="👩🏾‍❤‍💋‍👨🏿">couple | dark skin tone | kiss | man | medium-dark skin tone | woman</annotation>
+		<annotation cp="👩🏾‍❤‍💋‍👨🏿" type="tts">kiss: woman, man, medium-dark skin tone, dark skin tone</annotation>
+		<annotation cp="👩🏿‍❤‍💋‍👨🏻">couple | dark skin tone | kiss | light skin tone | man | woman</annotation>
+		<annotation cp="👩🏿‍❤‍💋‍👨🏻" type="tts">kiss: woman, man, dark skin tone, light skin tone</annotation>
+		<annotation cp="👩🏿‍❤‍💋‍👨🏼">couple | dark skin tone | kiss | man | medium-light skin tone | woman</annotation>
+		<annotation cp="👩🏿‍❤‍💋‍👨🏼" type="tts">kiss: woman, man, dark skin tone, medium-light skin tone</annotation>
+		<annotation cp="👩🏿‍❤‍💋‍👨🏽">couple | dark skin tone | kiss | man | medium skin tone | woman</annotation>
+		<annotation cp="👩🏿‍❤‍💋‍👨🏽" type="tts">kiss: woman, man, dark skin tone, medium skin tone</annotation>
+		<annotation cp="👩🏿‍❤‍💋‍👨🏾">couple | dark skin tone | kiss | man | medium-dark skin tone | woman</annotation>
+		<annotation cp="👩🏿‍❤‍💋‍👨🏾" type="tts">kiss: woman, man, dark skin tone, medium-dark skin tone</annotation>
+		<annotation cp="👩🏿‍❤‍💋‍👨🏿">couple | dark skin tone | kiss | man | woman</annotation>
+		<annotation cp="👩🏿‍❤‍💋‍👨🏿" type="tts">kiss: woman, man, dark skin tone</annotation>
+		<annotation cp="👩🏻‍❤‍💋‍👩🏻">couple | kiss | light skin tone | woman</annotation>
+		<annotation cp="👩🏻‍❤‍💋‍👩🏻" type="tts">kiss: woman, woman, light skin tone</annotation>
+		<annotation cp="👩🏻‍❤‍💋‍👩🏼">couple | kiss | light skin tone | medium-light skin tone | woman</annotation>
+		<annotation cp="👩🏻‍❤‍💋‍👩🏼" type="tts">kiss: woman, woman, light skin tone, medium-light skin tone</annotation>
+		<annotation cp="👩🏻‍❤‍💋‍👩🏽">couple | kiss | light skin tone | medium skin tone | woman</annotation>
+		<annotation cp="👩🏻‍❤‍💋‍👩🏽" type="tts">kiss: woman, woman, light skin tone, medium skin tone</annotation>
+		<annotation cp="👩🏻‍❤‍💋‍👩🏾">couple | kiss | light skin tone | medium-dark skin tone | woman</annotation>
+		<annotation cp="👩🏻‍❤‍💋‍👩🏾" type="tts">kiss: woman, woman, light skin tone, medium-dark skin tone</annotation>
+		<annotation cp="👩🏻‍❤‍💋‍👩🏿">couple | dark skin tone | kiss | light skin tone | woman</annotation>
+		<annotation cp="👩🏻‍❤‍💋‍👩🏿" type="tts">kiss: woman, woman, light skin tone, dark skin tone</annotation>
+		<annotation cp="👩🏼‍❤‍💋‍👩🏻">couple | kiss | light skin tone | medium-light skin tone | woman</annotation>
+		<annotation cp="👩🏼‍❤‍💋‍👩🏻" type="tts">kiss: woman, woman, medium-light skin tone, light skin tone</annotation>
+		<annotation cp="👩🏼‍❤‍💋‍👩🏼">couple | kiss | medium-light skin tone | woman</annotation>
+		<annotation cp="👩🏼‍❤‍💋‍👩🏼" type="tts">kiss: woman, woman, medium-light skin tone</annotation>
+		<annotation cp="👩🏼‍❤‍💋‍👩🏽">couple | kiss | medium skin tone | medium-light skin tone | woman</annotation>
+		<annotation cp="👩🏼‍❤‍💋‍👩🏽" type="tts">kiss: woman, woman, medium-light skin tone, medium skin tone</annotation>
+		<annotation cp="👩🏼‍❤‍💋‍👩🏾">couple | kiss | medium-dark skin tone | medium-light skin tone | woman</annotation>
+		<annotation cp="👩🏼‍❤‍💋‍👩🏾" type="tts">kiss: woman, woman, medium-light skin tone, medium-dark skin tone</annotation>
+		<annotation cp="👩🏼‍❤‍💋‍👩🏿">couple | dark skin tone | kiss | medium-light skin tone | woman</annotation>
+		<annotation cp="👩🏼‍❤‍💋‍👩🏿" type="tts">kiss: woman, woman, medium-light skin tone, dark skin tone</annotation>
+		<annotation cp="👩🏽‍❤‍💋‍👩🏻">couple | kiss | light skin tone | medium skin tone | woman</annotation>
+		<annotation cp="👩🏽‍❤‍💋‍👩🏻" type="tts">kiss: woman, woman, medium skin tone, light skin tone</annotation>
+		<annotation cp="👩🏽‍❤‍💋‍👩🏼">couple | kiss | medium skin tone | medium-light skin tone | woman</annotation>
+		<annotation cp="👩🏽‍❤‍💋‍👩🏼" type="tts">kiss: woman, woman, medium skin tone, medium-light skin tone</annotation>
+		<annotation cp="👩🏽‍❤‍💋‍👩🏽">couple | kiss | medium skin tone | woman</annotation>
+		<annotation cp="👩🏽‍❤‍💋‍👩🏽" type="tts">kiss: woman, woman, medium skin tone</annotation>
+		<annotation cp="👩🏽‍❤‍💋‍👩🏾">couple | kiss | medium skin tone | medium-dark skin tone | woman</annotation>
+		<annotation cp="👩🏽‍❤‍💋‍👩🏾" type="tts">kiss: woman, woman, medium skin tone, medium-dark skin tone</annotation>
+		<annotation cp="👩🏽‍❤‍💋‍👩🏿">couple | dark skin tone | kiss | medium skin tone | woman</annotation>
+		<annotation cp="👩🏽‍❤‍💋‍👩🏿" type="tts">kiss: woman, woman, medium skin tone, dark skin tone</annotation>
+		<annotation cp="👩🏾‍❤‍💋‍👩🏻">couple | kiss | light skin tone | medium-dark skin tone | woman</annotation>
+		<annotation cp="👩🏾‍❤‍💋‍👩🏻" type="tts">kiss: woman, woman, medium-dark skin tone, light skin tone</annotation>
+		<annotation cp="👩🏾‍❤‍💋‍👩🏼">couple | kiss | medium-dark skin tone | medium-light skin tone | woman</annotation>
+		<annotation cp="👩🏾‍❤‍💋‍👩🏼" type="tts">kiss: woman, woman, medium-dark skin tone, medium-light skin tone</annotation>
+		<annotation cp="👩🏾‍❤‍💋‍👩🏽">couple | kiss | medium skin tone | medium-dark skin tone | woman</annotation>
+		<annotation cp="👩🏾‍❤‍💋‍👩🏽" type="tts">kiss: woman, woman, medium-dark skin tone, medium skin tone</annotation>
+		<annotation cp="👩🏾‍❤‍💋‍👩🏾">couple | kiss | medium-dark skin tone | woman</annotation>
+		<annotation cp="👩🏾‍❤‍💋‍👩🏾" type="tts">kiss: woman, woman, medium-dark skin tone</annotation>
+		<annotation cp="👩🏾‍❤‍💋‍👩🏿">couple | dark skin tone | kiss | medium-dark skin tone | woman</annotation>
+		<annotation cp="👩🏾‍❤‍💋‍👩🏿" type="tts">kiss: woman, woman, medium-dark skin tone, dark skin tone</annotation>
+		<annotation cp="👩🏿‍❤‍💋‍👩🏻">couple | dark skin tone | kiss | light skin tone | woman</annotation>
+		<annotation cp="👩🏿‍❤‍💋‍👩🏻" type="tts">kiss: woman, woman, dark skin tone, light skin tone</annotation>
+		<annotation cp="👩🏿‍❤‍💋‍👩🏼">couple | dark skin tone | kiss | medium-light skin tone | woman</annotation>
+		<annotation cp="👩🏿‍❤‍💋‍👩🏼" type="tts">kiss: woman, woman, dark skin tone, medium-light skin tone</annotation>
+		<annotation cp="👩🏿‍❤‍💋‍👩🏽">couple | dark skin tone | kiss | medium skin tone | woman</annotation>
+		<annotation cp="👩🏿‍❤‍💋‍👩🏽" type="tts">kiss: woman, woman, dark skin tone, medium skin tone</annotation>
+		<annotation cp="👩🏿‍❤‍💋‍👩🏾">couple | dark skin tone | kiss | medium-dark skin tone | woman</annotation>
+		<annotation cp="👩🏿‍❤‍💋‍👩🏾" type="tts">kiss: woman, woman, dark skin tone, medium-dark skin tone</annotation>
+		<annotation cp="👩🏿‍❤‍💋‍👩🏿">couple | dark skin tone | kiss | woman</annotation>
+		<annotation cp="👩🏿‍❤‍💋‍👩🏿" type="tts">kiss: woman, woman, dark skin tone</annotation>
+		<annotation cp="👩🏻‍❤‍👨🏻">couple | couple with heart | light skin tone | love | man | woman</annotation>
+		<annotation cp="👩🏻‍❤‍👨🏻" type="tts">couple with heart: woman, man, light skin tone</annotation>
+		<annotation cp="👩🏻‍❤‍👨🏼">couple | couple with heart | light skin tone | love | man | medium-light skin tone | woman</annotation>
+		<annotation cp="👩🏻‍❤‍👨🏼" type="tts">couple with heart: woman, man, light skin tone, medium-light skin tone</annotation>
+		<annotation cp="👩🏻‍❤‍👨🏽">couple | couple with heart | light skin tone | love | man | medium skin tone | woman</annotation>
+		<annotation cp="👩🏻‍❤‍👨🏽" type="tts">couple with heart: woman, man, light skin tone, medium skin tone</annotation>
+		<annotation cp="👩🏻‍❤‍👨🏾">couple | couple with heart | light skin tone | love | man | medium-dark skin tone | woman</annotation>
+		<annotation cp="👩🏻‍❤‍👨🏾" type="tts">couple with heart: woman, man, light skin tone, medium-dark skin tone</annotation>
+		<annotation cp="👩🏻‍❤‍👨🏿">couple | couple with heart | dark skin tone | light skin tone | love | man | woman</annotation>
+		<annotation cp="👩🏻‍❤‍👨🏿" type="tts">couple with heart: woman, man, light skin tone, dark skin tone</annotation>
+		<annotation cp="👩🏼‍❤‍👨🏻">couple | couple with heart | light skin tone | love | man | medium-light skin tone | woman</annotation>
+		<annotation cp="👩🏼‍❤‍👨🏻" type="tts">couple with heart: woman, man, medium-light skin tone, light skin tone</annotation>
+		<annotation cp="👩🏼‍❤‍👨🏼">couple | couple with heart | love | man | medium-light skin tone | woman</annotation>
+		<annotation cp="👩🏼‍❤‍👨🏼" type="tts">couple with heart: woman, man, medium-light skin tone</annotation>
+		<annotation cp="👩🏼‍❤‍👨🏽">couple | couple with heart | love | man | medium skin tone | medium-light skin tone | woman</annotation>
+		<annotation cp="👩🏼‍❤‍👨🏽" type="tts">couple with heart: woman, man, medium-light skin tone, medium skin tone</annotation>
+		<annotation cp="👩🏼‍❤‍👨🏾">couple | couple with heart | love | man | medium-dark skin tone | medium-light skin tone | woman</annotation>
+		<annotation cp="👩🏼‍❤‍👨🏾" type="tts">couple with heart: woman, man, medium-light skin tone, medium-dark skin tone</annotation>
+		<annotation cp="👩🏼‍❤‍👨🏿">couple | couple with heart | dark skin tone | love | man | medium-light skin tone | woman</annotation>
+		<annotation cp="👩🏼‍❤‍👨🏿" type="tts">couple with heart: woman, man, medium-light skin tone, dark skin tone</annotation>
+		<annotation cp="👩🏽‍❤‍👨🏻">couple | couple with heart | light skin tone | love | man | medium skin tone | woman</annotation>
+		<annotation cp="👩🏽‍❤‍👨🏻" type="tts">couple with heart: woman, man, medium skin tone, light skin tone</annotation>
+		<annotation cp="👩🏽‍❤‍👨🏼">couple | couple with heart | love | man | medium skin tone | medium-light skin tone | woman</annotation>
+		<annotation cp="👩🏽‍❤‍👨🏼" type="tts">couple with heart: woman, man, medium skin tone, medium-light skin tone</annotation>
+		<annotation cp="👩🏽‍❤‍👨🏽">couple | couple with heart | love | man | medium skin tone | woman</annotation>
+		<annotation cp="👩🏽‍❤‍👨🏽" type="tts">couple with heart: woman, man, medium skin tone</annotation>
+		<annotation cp="👩🏽‍❤‍👨🏾">couple | couple with heart | love | man | medium skin tone | medium-dark skin tone | woman</annotation>
+		<annotation cp="👩🏽‍❤‍👨🏾" type="tts">couple with heart: woman, man, medium skin tone, medium-dark skin tone</annotation>
+		<annotation cp="👩🏽‍❤‍👨🏿">couple | couple with heart | dark skin tone | love | man | medium skin tone | woman</annotation>
+		<annotation cp="👩🏽‍❤‍👨🏿" type="tts">couple with heart: woman, man, medium skin tone, dark skin tone</annotation>
+		<annotation cp="👩🏾‍❤‍👨🏻">couple | couple with heart | light skin tone | love | man | medium-dark skin tone | woman</annotation>
+		<annotation cp="👩🏾‍❤‍👨🏻" type="tts">couple with heart: woman, man, medium-dark skin tone, light skin tone</annotation>
+		<annotation cp="👩🏾‍❤‍👨🏼">couple | couple with heart | love | man | medium-dark skin tone | medium-light skin tone | woman</annotation>
+		<annotation cp="👩🏾‍❤‍👨🏼" type="tts">couple with heart: woman, man, medium-dark skin tone, medium-light skin tone</annotation>
+		<annotation cp="👩🏾‍❤‍👨🏽">couple | couple with heart | love | man | medium skin tone | medium-dark skin tone | woman</annotation>
+		<annotation cp="👩🏾‍❤‍👨🏽" type="tts">couple with heart: woman, man, medium-dark skin tone, medium skin tone</annotation>
+		<annotation cp="👩🏾‍❤‍👨🏾">couple | couple with heart | love | man | medium-dark skin tone | woman</annotation>
+		<annotation cp="👩🏾‍❤‍👨🏾" type="tts">couple with heart: woman, man, medium-dark skin tone</annotation>
+		<annotation cp="👩🏾‍❤‍👨🏿">couple | couple with heart | dark skin tone | love | man | medium-dark skin tone | woman</annotation>
+		<annotation cp="👩🏾‍❤‍👨🏿" type="tts">couple with heart: woman, man, medium-dark skin tone, dark skin tone</annotation>
+		<annotation cp="👩🏿‍❤‍👨🏻">couple | couple with heart | dark skin tone | light skin tone | love | man | woman</annotation>
+		<annotation cp="👩🏿‍❤‍👨🏻" type="tts">couple with heart: woman, man, dark skin tone, light skin tone</annotation>
+		<annotation cp="👩🏿‍❤‍👨🏼">couple | couple with heart | dark skin tone | love | man | medium-light skin tone | woman</annotation>
+		<annotation cp="👩🏿‍❤‍👨🏼" type="tts">couple with heart: woman, man, dark skin tone, medium-light skin tone</annotation>
+		<annotation cp="👩🏿‍❤‍👨🏽">couple | couple with heart | dark skin tone | love | man | medium skin tone | woman</annotation>
+		<annotation cp="👩🏿‍❤‍👨🏽" type="tts">couple with heart: woman, man, dark skin tone, medium skin tone</annotation>
+		<annotation cp="👩🏿‍❤‍👨🏾">couple | couple with heart | dark skin tone | love | man | medium-dark skin tone | woman</annotation>
+		<annotation cp="👩🏿‍❤‍👨🏾" type="tts">couple with heart: woman, man, dark skin tone, medium-dark skin tone</annotation>
+		<annotation cp="👩🏿‍❤‍👨🏿">couple | couple with heart | dark skin tone | love | man | woman</annotation>
+		<annotation cp="👩🏿‍❤‍👨🏿" type="tts">couple with heart: woman, man, dark skin tone</annotation>
+		<annotation cp="👩🏻‍❤‍👩🏻">couple | couple with heart | light skin tone | love | woman</annotation>
+		<annotation cp="👩🏻‍❤‍👩🏻" type="tts">couple with heart: woman, woman, light skin tone</annotation>
+		<annotation cp="👩🏻‍❤‍👩🏼">couple | couple with heart | light skin tone | love | medium-light skin tone | woman</annotation>
+		<annotation cp="👩🏻‍❤‍👩🏼" type="tts">couple with heart: woman, woman, light skin tone, medium-light skin tone</annotation>
+		<annotation cp="👩🏻‍❤‍👩🏽">couple | couple with heart | light skin tone | love | medium skin tone | woman</annotation>
+		<annotation cp="👩🏻‍❤‍👩🏽" type="tts">couple with heart: woman, woman, light skin tone, medium skin tone</annotation>
+		<annotation cp="👩🏻‍❤‍👩🏾">couple | couple with heart | light skin tone | love | medium-dark skin tone | woman</annotation>
+		<annotation cp="👩🏻‍❤‍👩🏾" type="tts">couple with heart: woman, woman, light skin tone, medium-dark skin tone</annotation>
+		<annotation cp="👩🏻‍❤‍👩🏿">couple | couple with heart | dark skin tone | light skin tone | love | woman</annotation>
+		<annotation cp="👩🏻‍❤‍👩🏿" type="tts">couple with heart: woman, woman, light skin tone, dark skin tone</annotation>
+		<annotation cp="👩🏼‍❤‍👩🏻">couple | couple with heart | light skin tone | love | medium-light skin tone | woman</annotation>
+		<annotation cp="👩🏼‍❤‍👩🏻" type="tts">couple with heart: woman, woman, medium-light skin tone, light skin tone</annotation>
+		<annotation cp="👩🏼‍❤‍👩🏼">couple | couple with heart | love | medium-light skin tone | woman</annotation>
+		<annotation cp="👩🏼‍❤‍👩🏼" type="tts">couple with heart: woman, woman, medium-light skin tone</annotation>
+		<annotation cp="👩🏼‍❤‍👩🏽">couple | couple with heart | love | medium skin tone | medium-light skin tone | woman</annotation>
+		<annotation cp="👩🏼‍❤‍👩🏽" type="tts">couple with heart: woman, woman, medium-light skin tone, medium skin tone</annotation>
+		<annotation cp="👩🏼‍❤‍👩🏾">couple | couple with heart | love | medium-dark skin tone | medium-light skin tone | woman</annotation>
+		<annotation cp="👩🏼‍❤‍👩🏾" type="tts">couple with heart: woman, woman, medium-light skin tone, medium-dark skin tone</annotation>
+		<annotation cp="👩🏼‍❤‍👩🏿">couple | couple with heart | dark skin tone | love | medium-light skin tone | woman</annotation>
+		<annotation cp="👩🏼‍❤‍👩🏿" type="tts">couple with heart: woman, woman, medium-light skin tone, dark skin tone</annotation>
+		<annotation cp="👩🏽‍❤‍👩🏻">couple | couple with heart | light skin tone | love | medium skin tone | woman</annotation>
+		<annotation cp="👩🏽‍❤‍👩🏻" type="tts">couple with heart: woman, woman, medium skin tone, light skin tone</annotation>
+		<annotation cp="👩🏽‍❤‍👩🏼">couple | couple with heart | love | medium skin tone | medium-light skin tone | woman</annotation>
+		<annotation cp="👩🏽‍❤‍👩🏼" type="tts">couple with heart: woman, woman, medium skin tone, medium-light skin tone</annotation>
+		<annotation cp="👩🏽‍❤‍👩🏽">couple | couple with heart | love | medium skin tone | woman</annotation>
+		<annotation cp="👩🏽‍❤‍👩🏽" type="tts">couple with heart: woman, woman, medium skin tone</annotation>
+		<annotation cp="👩🏽‍❤‍👩🏾">couple | couple with heart | love | medium skin tone | medium-dark skin tone | woman</annotation>
+		<annotation cp="👩🏽‍❤‍👩🏾" type="tts">couple with heart: woman, woman, medium skin tone, medium-dark skin tone</annotation>
+		<annotation cp="👩🏽‍❤‍👩🏿">couple | couple with heart | dark skin tone | love | medium skin tone | woman</annotation>
+		<annotation cp="👩🏽‍❤‍👩🏿" type="tts">couple with heart: woman, woman, medium skin tone, dark skin tone</annotation>
+		<annotation cp="👩🏾‍❤‍👩🏻">couple | couple with heart | light skin tone | love | medium-dark skin tone | woman</annotation>
+		<annotation cp="👩🏾‍❤‍👩🏻" type="tts">couple with heart: woman, woman, medium-dark skin tone, light skin tone</annotation>
+		<annotation cp="👩🏾‍❤‍👩🏼">couple | couple with heart | love | medium-dark skin tone | medium-light skin tone | woman</annotation>
+		<annotation cp="👩🏾‍❤‍👩🏼" type="tts">couple with heart: woman, woman, medium-dark skin tone, medium-light skin tone</annotation>
+		<annotation cp="👩🏾‍❤‍👩🏽">couple | couple with heart | love | medium skin tone | medium-dark skin tone | woman</annotation>
+		<annotation cp="👩🏾‍❤‍👩🏽" type="tts">couple with heart: woman, woman, medium-dark skin tone, medium skin tone</annotation>
+		<annotation cp="👩🏾‍❤‍👩🏾">couple | couple with heart | love | medium-dark skin tone | woman</annotation>
+		<annotation cp="👩🏾‍❤‍👩🏾" type="tts">couple with heart: woman, woman, medium-dark skin tone</annotation>
+		<annotation cp="👩🏾‍❤‍👩🏿">couple | couple with heart | dark skin tone | love | medium-dark skin tone | woman</annotation>
+		<annotation cp="👩🏾‍❤‍👩🏿" type="tts">couple with heart: woman, woman, medium-dark skin tone, dark skin tone</annotation>
+		<annotation cp="👩🏿‍❤‍👩🏻">couple | couple with heart | dark skin tone | light skin tone | love | woman</annotation>
+		<annotation cp="👩🏿‍❤‍👩🏻" type="tts">couple with heart: woman, woman, dark skin tone, light skin tone</annotation>
+		<annotation cp="👩🏿‍❤‍👩🏼">couple | couple with heart | dark skin tone | love | medium-light skin tone | woman</annotation>
+		<annotation cp="👩🏿‍❤‍👩🏼" type="tts">couple with heart: woman, woman, dark skin tone, medium-light skin tone</annotation>
+		<annotation cp="👩🏿‍❤‍👩🏽">couple | couple with heart | dark skin tone | love | medium skin tone | woman</annotation>
+		<annotation cp="👩🏿‍❤‍👩🏽" type="tts">couple with heart: woman, woman, dark skin tone, medium skin tone</annotation>
+		<annotation cp="👩🏿‍❤‍👩🏾">couple | couple with heart | dark skin tone | love | medium-dark skin tone | woman</annotation>
+		<annotation cp="👩🏿‍❤‍👩🏾" type="tts">couple with heart: woman, woman, dark skin tone, medium-dark skin tone</annotation>
+		<annotation cp="👩🏿‍❤‍👩🏿">couple | couple with heart | dark skin tone | love | woman</annotation>
+		<annotation cp="👩🏿‍❤‍👩🏿" type="tts">couple with heart: woman, woman, dark skin tone</annotation>
+		<annotation cp="👩‍🦰">adult | red hair | woman</annotation>
+		<annotation cp="👩‍🦰" type="tts">woman: red hair</annotation>
+		<annotation cp="👩🏻‍🦰">adult | light skin tone | red hair | woman</annotation>
+		<annotation cp="👩🏻‍🦰" type="tts">woman: light skin tone, red hair</annotation>
+		<annotation cp="👩🏼‍🦰">adult | medium-light skin tone | red hair | woman</annotation>
+		<annotation cp="👩🏼‍🦰" type="tts">woman: medium-light skin tone, red hair</annotation>
+		<annotation cp="👩🏽‍🦰">adult | medium skin tone | red hair | woman</annotation>
+		<annotation cp="👩🏽‍🦰" type="tts">woman: medium skin tone, red hair</annotation>
+		<annotation cp="👩🏾‍🦰">adult | medium-dark skin tone | red hair | woman</annotation>
+		<annotation cp="👩🏾‍🦰" type="tts">woman: medium-dark skin tone, red hair</annotation>
+		<annotation cp="👩🏿‍🦰">adult | dark skin tone | red hair | woman</annotation>
+		<annotation cp="👩🏿‍🦰" type="tts">woman: dark skin tone, red hair</annotation>
+		<annotation cp="👩‍🦱">adult | curly hair | woman</annotation>
+		<annotation cp="👩‍🦱" type="tts">woman: curly hair</annotation>
+		<annotation cp="👩🏻‍🦱">adult | curly hair | light skin tone | woman</annotation>
+		<annotation cp="👩🏻‍🦱" type="tts">woman: light skin tone, curly hair</annotation>
+		<annotation cp="👩🏼‍🦱">adult | curly hair | medium-light skin tone | woman</annotation>
+		<annotation cp="👩🏼‍🦱" type="tts">woman: medium-light skin tone, curly hair</annotation>
+		<annotation cp="👩🏽‍🦱">adult | curly hair | medium skin tone | woman</annotation>
+		<annotation cp="👩🏽‍🦱" type="tts">woman: medium skin tone, curly hair</annotation>
+		<annotation cp="👩🏾‍🦱">adult | curly hair | medium-dark skin tone | woman</annotation>
+		<annotation cp="👩🏾‍🦱" type="tts">woman: medium-dark skin tone, curly hair</annotation>
+		<annotation cp="👩🏿‍🦱">adult | curly hair | dark skin tone | woman</annotation>
+		<annotation cp="👩🏿‍🦱" type="tts">woman: dark skin tone, curly hair</annotation>
+		<annotation cp="👩‍🦳">adult | white hair | woman</annotation>
+		<annotation cp="👩‍🦳" type="tts">woman: white hair</annotation>
+		<annotation cp="👩🏻‍🦳">adult | light skin tone | white hair | woman</annotation>
+		<annotation cp="👩🏻‍🦳" type="tts">woman: light skin tone, white hair</annotation>
+		<annotation cp="👩🏼‍🦳">adult | medium-light skin tone | white hair | woman</annotation>
+		<annotation cp="👩🏼‍🦳" type="tts">woman: medium-light skin tone, white hair</annotation>
+		<annotation cp="👩🏽‍🦳">adult | medium skin tone | white hair | woman</annotation>
+		<annotation cp="👩🏽‍🦳" type="tts">woman: medium skin tone, white hair</annotation>
+		<annotation cp="👩🏾‍🦳">adult | medium-dark skin tone | white hair | woman</annotation>
+		<annotation cp="👩🏾‍🦳" type="tts">woman: medium-dark skin tone, white hair</annotation>
+		<annotation cp="👩🏿‍🦳">adult | dark skin tone | white hair | woman</annotation>
+		<annotation cp="👩🏿‍🦳" type="tts">woman: dark skin tone, white hair</annotation>
+		<annotation cp="👩‍🦲">adult | bald | woman</annotation>
+		<annotation cp="👩‍🦲" type="tts">woman: bald</annotation>
+		<annotation cp="👩🏻‍🦲">adult | bald | light skin tone | woman</annotation>
+		<annotation cp="👩🏻‍🦲" type="tts">woman: light skin tone, bald</annotation>
+		<annotation cp="👩🏼‍🦲">adult | bald | medium-light skin tone | woman</annotation>
+		<annotation cp="👩🏼‍🦲" type="tts">woman: medium-light skin tone, bald</annotation>
+		<annotation cp="👩🏽‍🦲">adult | bald | medium skin tone | woman</annotation>
+		<annotation cp="👩🏽‍🦲" type="tts">woman: medium skin tone, bald</annotation>
+		<annotation cp="👩🏾‍🦲">adult | bald | medium-dark skin tone | woman</annotation>
+		<annotation cp="👩🏾‍🦲" type="tts">woman: medium-dark skin tone, bald</annotation>
+		<annotation cp="👩🏿‍🦲">adult | bald | dark skin tone | woman</annotation>
+		<annotation cp="👩🏿‍🦲" type="tts">woman: dark skin tone, bald</annotation>
+		<annotation cp="🧓🏻">adult | gender-neutral | light skin tone | old | older person | unspecified gender</annotation>
+		<annotation cp="🧓🏻" type="tts">older person: light skin tone</annotation>
+		<annotation cp="🧓🏼">adult | gender-neutral | medium-light skin tone | old | older person | unspecified gender</annotation>
+		<annotation cp="🧓🏼" type="tts">older person: medium-light skin tone</annotation>
+		<annotation cp="🧓🏽">adult | gender-neutral | medium skin tone | old | older person | unspecified gender</annotation>
+		<annotation cp="🧓🏽" type="tts">older person: medium skin tone</annotation>
+		<annotation cp="🧓🏾">adult | gender-neutral | medium-dark skin tone | old | older person | unspecified gender</annotation>
+		<annotation cp="🧓🏾" type="tts">older person: medium-dark skin tone</annotation>
+		<annotation cp="🧓🏿">adult | dark skin tone | gender-neutral | old | older person | unspecified gender</annotation>
+		<annotation cp="🧓🏿" type="tts">older person: dark skin tone</annotation>
+		<annotation cp="👴🏻">adult | light skin tone | man | old</annotation>
+		<annotation cp="👴🏻" type="tts">old man: light skin tone</annotation>
+		<annotation cp="👴🏼">adult | man | medium-light skin tone | old</annotation>
+		<annotation cp="👴🏼" type="tts">old man: medium-light skin tone</annotation>
+		<annotation cp="👴🏽">adult | man | medium skin tone | old</annotation>
+		<annotation cp="👴🏽" type="tts">old man: medium skin tone</annotation>
+		<annotation cp="👴🏾">adult | man | medium-dark skin tone | old</annotation>
+		<annotation cp="👴🏾" type="tts">old man: medium-dark skin tone</annotation>
+		<annotation cp="👴🏿">adult | dark skin tone | man | old</annotation>
+		<annotation cp="👴🏿" type="tts">old man: dark skin tone</annotation>
+		<annotation cp="👵🏻">adult | light skin tone | old | woman</annotation>
+		<annotation cp="👵🏻" type="tts">old woman: light skin tone</annotation>
+		<annotation cp="👵🏼">adult | medium-light skin tone | old | woman</annotation>
+		<annotation cp="👵🏼" type="tts">old woman: medium-light skin tone</annotation>
+		<annotation cp="👵🏽">adult | medium skin tone | old | woman</annotation>
+		<annotation cp="👵🏽" type="tts">old woman: medium skin tone</annotation>
+		<annotation cp="👵🏾">adult | medium-dark skin tone | old | woman</annotation>
+		<annotation cp="👵🏾" type="tts">old woman: medium-dark skin tone</annotation>
+		<annotation cp="👵🏿">adult | dark skin tone | old | woman</annotation>
+		<annotation cp="👵🏿" type="tts">old woman: dark skin tone</annotation>
+		<annotation cp="🙍🏻">frown | gesture | light skin tone | person frowning</annotation>
+		<annotation cp="🙍🏻" type="tts">person frowning: light skin tone</annotation>
+		<annotation cp="🙍🏼">frown | gesture | medium-light skin tone | person frowning</annotation>
+		<annotation cp="🙍🏼" type="tts">person frowning: medium-light skin tone</annotation>
+		<annotation cp="🙍🏽">frown | gesture | medium skin tone | person frowning</annotation>
+		<annotation cp="🙍🏽" type="tts">person frowning: medium skin tone</annotation>
+		<annotation cp="🙍🏾">frown | gesture | medium-dark skin tone | person frowning</annotation>
+		<annotation cp="🙍🏾" type="tts">person frowning: medium-dark skin tone</annotation>
+		<annotation cp="🙍🏿">dark skin tone | frown | gesture | person frowning</annotation>
+		<annotation cp="🙍🏿" type="tts">person frowning: dark skin tone</annotation>
+		<annotation cp="🙍🏻‍♂">frowning | gesture | light skin tone | man</annotation>
+		<annotation cp="🙍🏻‍♂" type="tts">man frowning: light skin tone</annotation>
+		<annotation cp="🙍🏼‍♂">frowning | gesture | man | medium-light skin tone</annotation>
+		<annotation cp="🙍🏼‍♂" type="tts">man frowning: medium-light skin tone</annotation>
+		<annotation cp="🙍🏽‍♂">frowning | gesture | man | medium skin tone</annotation>
+		<annotation cp="🙍🏽‍♂" type="tts">man frowning: medium skin tone</annotation>
+		<annotation cp="🙍🏾‍♂">frowning | gesture | man | medium-dark skin tone</annotation>
+		<annotation cp="🙍🏾‍♂" type="tts">man frowning: medium-dark skin tone</annotation>
+		<annotation cp="🙍🏿‍♂">dark skin tone | frowning | gesture | man</annotation>
+		<annotation cp="🙍🏿‍♂" type="tts">man frowning: dark skin tone</annotation>
+		<annotation cp="🙍🏻‍♀">frowning | gesture | light skin tone | woman</annotation>
+		<annotation cp="🙍🏻‍♀" type="tts">woman frowning: light skin tone</annotation>
+		<annotation cp="🙍🏼‍♀">frowning | gesture | medium-light skin tone | woman</annotation>
+		<annotation cp="🙍🏼‍♀" type="tts">woman frowning: medium-light skin tone</annotation>
+		<annotation cp="🙍🏽‍♀">frowning | gesture | medium skin tone | woman</annotation>
+		<annotation cp="🙍🏽‍♀" type="tts">woman frowning: medium skin tone</annotation>
+		<annotation cp="🙍🏾‍♀">frowning | gesture | medium-dark skin tone | woman</annotation>
+		<annotation cp="🙍🏾‍♀" type="tts">woman frowning: medium-dark skin tone</annotation>
+		<annotation cp="🙍🏿‍♀">dark skin tone | frowning | gesture | woman</annotation>
+		<annotation cp="🙍🏿‍♀" type="tts">woman frowning: dark skin tone</annotation>
+		<annotation cp="🙎🏻">gesture | light skin tone | person pouting | pouting</annotation>
+		<annotation cp="🙎🏻" type="tts">person pouting: light skin tone</annotation>
+		<annotation cp="🙎🏼">gesture | medium-light skin tone | person pouting | pouting</annotation>
+		<annotation cp="🙎🏼" type="tts">person pouting: medium-light skin tone</annotation>
+		<annotation cp="🙎🏽">gesture | medium skin tone | person pouting | pouting</annotation>
+		<annotation cp="🙎🏽" type="tts">person pouting: medium skin tone</annotation>
+		<annotation cp="🙎🏾">gesture | medium-dark skin tone | person pouting | pouting</annotation>
+		<annotation cp="🙎🏾" type="tts">person pouting: medium-dark skin tone</annotation>
+		<annotation cp="🙎🏿">dark skin tone | gesture | person pouting | pouting</annotation>
+		<annotation cp="🙎🏿" type="tts">person pouting: dark skin tone</annotation>
+		<annotation cp="🙎🏻‍♂">gesture | light skin tone | man | pouting</annotation>
+		<annotation cp="🙎🏻‍♂" type="tts">man pouting: light skin tone</annotation>
+		<annotation cp="🙎🏼‍♂">gesture | man | medium-light skin tone | pouting</annotation>
+		<annotation cp="🙎🏼‍♂" type="tts">man pouting: medium-light skin tone</annotation>
+		<annotation cp="🙎🏽‍♂">gesture | man | medium skin tone | pouting</annotation>
+		<annotation cp="🙎🏽‍♂" type="tts">man pouting: medium skin tone</annotation>
+		<annotation cp="🙎🏾‍♂">gesture | man | medium-dark skin tone | pouting</annotation>
+		<annotation cp="🙎🏾‍♂" type="tts">man pouting: medium-dark skin tone</annotation>
+		<annotation cp="🙎🏿‍♂">dark skin tone | gesture | man | pouting</annotation>
+		<annotation cp="🙎🏿‍♂" type="tts">man pouting: dark skin tone</annotation>
+		<annotation cp="🙎🏻‍♀">gesture | light skin tone | pouting | woman</annotation>
+		<annotation cp="🙎🏻‍♀" type="tts">woman pouting: light skin tone</annotation>
+		<annotation cp="🙎🏼‍♀">gesture | medium-light skin tone | pouting | woman</annotation>
+		<annotation cp="🙎🏼‍♀" type="tts">woman pouting: medium-light skin tone</annotation>
+		<annotation cp="🙎🏽‍♀">gesture | medium skin tone | pouting | woman</annotation>
+		<annotation cp="🙎🏽‍♀" type="tts">woman pouting: medium skin tone</annotation>
+		<annotation cp="🙎🏾‍♀">gesture | medium-dark skin tone | pouting | woman</annotation>
+		<annotation cp="🙎🏾‍♀" type="tts">woman pouting: medium-dark skin tone</annotation>
+		<annotation cp="🙎🏿‍♀">dark skin tone | gesture | pouting | woman</annotation>
+		<annotation cp="🙎🏿‍♀" type="tts">woman pouting: dark skin tone</annotation>
+		<annotation cp="🙅🏻">forbidden | gesture | hand | light skin tone | person gesturing NO | prohibited</annotation>
+		<annotation cp="🙅🏻" type="tts">person gesturing NO: light skin tone</annotation>
+		<annotation cp="🙅🏼">forbidden | gesture | hand | medium-light skin tone | person gesturing NO | prohibited</annotation>
+		<annotation cp="🙅🏼" type="tts">person gesturing NO: medium-light skin tone</annotation>
+		<annotation cp="🙅🏽">forbidden | gesture | hand | medium skin tone | person gesturing NO | prohibited</annotation>
+		<annotation cp="🙅🏽" type="tts">person gesturing NO: medium skin tone</annotation>
+		<annotation cp="🙅🏾">forbidden | gesture | hand | medium-dark skin tone | person gesturing NO | prohibited</annotation>
+		<annotation cp="🙅🏾" type="tts">person gesturing NO: medium-dark skin tone</annotation>
+		<annotation cp="🙅🏿">dark skin tone | forbidden | gesture | hand | person gesturing NO | prohibited</annotation>
+		<annotation cp="🙅🏿" type="tts">person gesturing NO: dark skin tone</annotation>
+		<annotation cp="🙅🏻‍♂">forbidden | gesture | hand | light skin tone | man | man gesturing NO | prohibited</annotation>
+		<annotation cp="🙅🏻‍♂" type="tts">man gesturing NO: light skin tone</annotation>
+		<annotation cp="🙅🏼‍♂">forbidden | gesture | hand | man | man gesturing NO | medium-light skin tone | prohibited</annotation>
+		<annotation cp="🙅🏼‍♂" type="tts">man gesturing NO: medium-light skin tone</annotation>
+		<annotation cp="🙅🏽‍♂">forbidden | gesture | hand | man | man gesturing NO | medium skin tone | prohibited</annotation>
+		<annotation cp="🙅🏽‍♂" type="tts">man gesturing NO: medium skin tone</annotation>
+		<annotation cp="🙅🏾‍♂">forbidden | gesture | hand | man | man gesturing NO | medium-dark skin tone | prohibited</annotation>
+		<annotation cp="🙅🏾‍♂" type="tts">man gesturing NO: medium-dark skin tone</annotation>
+		<annotation cp="🙅🏿‍♂">dark skin tone | forbidden | gesture | hand | man | man gesturing NO | prohibited</annotation>
+		<annotation cp="🙅🏿‍♂" type="tts">man gesturing NO: dark skin tone</annotation>
+		<annotation cp="🙅🏻‍♀">forbidden | gesture | hand | light skin tone | prohibited | woman | woman gesturing NO</annotation>
+		<annotation cp="🙅🏻‍♀" type="tts">woman gesturing NO: light skin tone</annotation>
+		<annotation cp="🙅🏼‍♀">forbidden | gesture | hand | medium-light skin tone | prohibited | woman | woman gesturing NO</annotation>
+		<annotation cp="🙅🏼‍♀" type="tts">woman gesturing NO: medium-light skin tone</annotation>
+		<annotation cp="🙅🏽‍♀">forbidden | gesture | hand | medium skin tone | prohibited | woman | woman gesturing NO</annotation>
+		<annotation cp="🙅🏽‍♀" type="tts">woman gesturing NO: medium skin tone</annotation>
+		<annotation cp="🙅🏾‍♀">forbidden | gesture | hand | medium-dark skin tone | prohibited | woman | woman gesturing NO</annotation>
+		<annotation cp="🙅🏾‍♀" type="tts">woman gesturing NO: medium-dark skin tone</annotation>
+		<annotation cp="🙅🏿‍♀">dark skin tone | forbidden | gesture | hand | prohibited | woman | woman gesturing NO</annotation>
+		<annotation cp="🙅🏿‍♀" type="tts">woman gesturing NO: dark skin tone</annotation>
+		<annotation cp="🙆🏻">gesture | hand | light skin tone | OK | person gesturing OK</annotation>
+		<annotation cp="🙆🏻" type="tts">person gesturing OK: light skin tone</annotation>
+		<annotation cp="🙆🏼">gesture | hand | medium-light skin tone | OK | person gesturing OK</annotation>
+		<annotation cp="🙆🏼" type="tts">person gesturing OK: medium-light skin tone</annotation>
+		<annotation cp="🙆🏽">gesture | hand | medium skin tone | OK | person gesturing OK</annotation>
+		<annotation cp="🙆🏽" type="tts">person gesturing OK: medium skin tone</annotation>
+		<annotation cp="🙆🏾">gesture | hand | medium-dark skin tone | OK | person gesturing OK</annotation>
+		<annotation cp="🙆🏾" type="tts">person gesturing OK: medium-dark skin tone</annotation>
+		<annotation cp="🙆🏿">dark skin tone | gesture | hand | OK | person gesturing OK</annotation>
+		<annotation cp="🙆🏿" type="tts">person gesturing OK: dark skin tone</annotation>
+		<annotation cp="🙆🏻‍♂">gesture | hand | light skin tone | man | man gesturing OK | OK</annotation>
+		<annotation cp="🙆🏻‍♂" type="tts">man gesturing OK: light skin tone</annotation>
+		<annotation cp="🙆🏼‍♂">gesture | hand | man | man gesturing OK | medium-light skin tone | OK</annotation>
+		<annotation cp="🙆🏼‍♂" type="tts">man gesturing OK: medium-light skin tone</annotation>
+		<annotation cp="🙆🏽‍♂">gesture | hand | man | man gesturing OK | medium skin tone | OK</annotation>
+		<annotation cp="🙆🏽‍♂" type="tts">man gesturing OK: medium skin tone</annotation>
+		<annotation cp="🙆🏾‍♂">gesture | hand | man | man gesturing OK | medium-dark skin tone | OK</annotation>
+		<annotation cp="🙆🏾‍♂" type="tts">man gesturing OK: medium-dark skin tone</annotation>
+		<annotation cp="🙆🏿‍♂">dark skin tone | gesture | hand | man | man gesturing OK | OK</annotation>
+		<annotation cp="🙆🏿‍♂" type="tts">man gesturing OK: dark skin tone</annotation>
+		<annotation cp="🙆🏻‍♀">gesture | hand | light skin tone | OK | woman | woman gesturing OK</annotation>
+		<annotation cp="🙆🏻‍♀" type="tts">woman gesturing OK: light skin tone</annotation>
+		<annotation cp="🙆🏼‍♀">gesture | hand | medium-light skin tone | OK | woman | woman gesturing OK</annotation>
+		<annotation cp="🙆🏼‍♀" type="tts">woman gesturing OK: medium-light skin tone</annotation>
+		<annotation cp="🙆🏽‍♀">gesture | hand | medium skin tone | OK | woman | woman gesturing OK</annotation>
+		<annotation cp="🙆🏽‍♀" type="tts">woman gesturing OK: medium skin tone</annotation>
+		<annotation cp="🙆🏾‍♀">gesture | hand | medium-dark skin tone | OK | woman | woman gesturing OK</annotation>
+		<annotation cp="🙆🏾‍♀" type="tts">woman gesturing OK: medium-dark skin tone</annotation>
+		<annotation cp="🙆🏿‍♀">dark skin tone | gesture | hand | OK | woman | woman gesturing OK</annotation>
+		<annotation cp="🙆🏿‍♀" type="tts">woman gesturing OK: dark skin tone</annotation>
+		<annotation cp="💁🏻">hand | help | information | light skin tone | person tipping hand | sassy | tipping</annotation>
+		<annotation cp="💁🏻" type="tts">person tipping hand: light skin tone</annotation>
+		<annotation cp="💁🏼">hand | help | information | medium-light skin tone | person tipping hand | sassy | tipping</annotation>
+		<annotation cp="💁🏼" type="tts">person tipping hand: medium-light skin tone</annotation>
+		<annotation cp="💁🏽">hand | help | information | medium skin tone | person tipping hand | sassy | tipping</annotation>
+		<annotation cp="💁🏽" type="tts">person tipping hand: medium skin tone</annotation>
+		<annotation cp="💁🏾">hand | help | information | medium-dark skin tone | person tipping hand | sassy | tipping</annotation>
+		<annotation cp="💁🏾" type="tts">person tipping hand: medium-dark skin tone</annotation>
+		<annotation cp="💁🏿">dark skin tone | hand | help | information | person tipping hand | sassy | tipping</annotation>
+		<annotation cp="💁🏿" type="tts">person tipping hand: dark skin tone</annotation>
+		<annotation cp="💁🏻‍♂">light skin tone | man | man tipping hand | sassy | tipping hand</annotation>
+		<annotation cp="💁🏻‍♂" type="tts">man tipping hand: light skin tone</annotation>
+		<annotation cp="💁🏼‍♂">man | man tipping hand | medium-light skin tone | sassy | tipping hand</annotation>
+		<annotation cp="💁🏼‍♂" type="tts">man tipping hand: medium-light skin tone</annotation>
+		<annotation cp="💁🏽‍♂">man | man tipping hand | medium skin tone | sassy | tipping hand</annotation>
+		<annotation cp="💁🏽‍♂" type="tts">man tipping hand: medium skin tone</annotation>
+		<annotation cp="💁🏾‍♂">man | man tipping hand | medium-dark skin tone | sassy | tipping hand</annotation>
+		<annotation cp="💁🏾‍♂" type="tts">man tipping hand: medium-dark skin tone</annotation>
+		<annotation cp="💁🏿‍♂">dark skin tone | man | man tipping hand | sassy | tipping hand</annotation>
+		<annotation cp="💁🏿‍♂" type="tts">man tipping hand: dark skin tone</annotation>
+		<annotation cp="💁🏻‍♀">light skin tone | sassy | tipping hand | woman | woman tipping hand</annotation>
+		<annotation cp="💁🏻‍♀" type="tts">woman tipping hand: light skin tone</annotation>
+		<annotation cp="💁🏼‍♀">medium-light skin tone | sassy | tipping hand | woman | woman tipping hand</annotation>
+		<annotation cp="💁🏼‍♀" type="tts">woman tipping hand: medium-light skin tone</annotation>
+		<annotation cp="💁🏽‍♀">medium skin tone | sassy | tipping hand | woman | woman tipping hand</annotation>
+		<annotation cp="💁🏽‍♀" type="tts">woman tipping hand: medium skin tone</annotation>
+		<annotation cp="💁🏾‍♀">medium-dark skin tone | sassy | tipping hand | woman | woman tipping hand</annotation>
+		<annotation cp="💁🏾‍♀" type="tts">woman tipping hand: medium-dark skin tone</annotation>
+		<annotation cp="💁🏿‍♀">dark skin tone | sassy | tipping hand | woman | woman tipping hand</annotation>
+		<annotation cp="💁🏿‍♀" type="tts">woman tipping hand: dark skin tone</annotation>
+		<annotation cp="🙋🏻">gesture | hand | happy | light skin tone | person raising hand | raised</annotation>
+		<annotation cp="🙋🏻" type="tts">person raising hand: light skin tone</annotation>
+		<annotation cp="🙋🏼">gesture | hand | happy | medium-light skin tone | person raising hand | raised</annotation>
+		<annotation cp="🙋🏼" type="tts">person raising hand: medium-light skin tone</annotation>
+		<annotation cp="🙋🏽">gesture | hand | happy | medium skin tone | person raising hand | raised</annotation>
+		<annotation cp="🙋🏽" type="tts">person raising hand: medium skin tone</annotation>
+		<annotation cp="🙋🏾">gesture | hand | happy | medium-dark skin tone | person raising hand | raised</annotation>
+		<annotation cp="🙋🏾" type="tts">person raising hand: medium-dark skin tone</annotation>
+		<annotation cp="🙋🏿">dark skin tone | gesture | hand | happy | person raising hand | raised</annotation>
+		<annotation cp="🙋🏿" type="tts">person raising hand: dark skin tone</annotation>
+		<annotation cp="🙋🏻‍♂">gesture | light skin tone | man | man raising hand | raising hand</annotation>
+		<annotation cp="🙋🏻‍♂" type="tts">man raising hand: light skin tone</annotation>
+		<annotation cp="🙋🏼‍♂">gesture | man | man raising hand | medium-light skin tone | raising hand</annotation>
+		<annotation cp="🙋🏼‍♂" type="tts">man raising hand: medium-light skin tone</annotation>
+		<annotation cp="🙋🏽‍♂">gesture | man | man raising hand | medium skin tone | raising hand</annotation>
+		<annotation cp="🙋🏽‍♂" type="tts">man raising hand: medium skin tone</annotation>
+		<annotation cp="🙋🏾‍♂">gesture | man | man raising hand | medium-dark skin tone | raising hand</annotation>
+		<annotation cp="🙋🏾‍♂" type="tts">man raising hand: medium-dark skin tone</annotation>
+		<annotation cp="🙋🏿‍♂">dark skin tone | gesture | man | man raising hand | raising hand</annotation>
+		<annotation cp="🙋🏿‍♂" type="tts">man raising hand: dark skin tone</annotation>
+		<annotation cp="🙋🏻‍♀">gesture | light skin tone | raising hand | woman | woman raising hand</annotation>
+		<annotation cp="🙋🏻‍♀" type="tts">woman raising hand: light skin tone</annotation>
+		<annotation cp="🙋🏼‍♀">gesture | medium-light skin tone | raising hand | woman | woman raising hand</annotation>
+		<annotation cp="🙋🏼‍♀" type="tts">woman raising hand: medium-light skin tone</annotation>
+		<annotation cp="🙋🏽‍♀">gesture | medium skin tone | raising hand | woman | woman raising hand</annotation>
+		<annotation cp="🙋🏽‍♀" type="tts">woman raising hand: medium skin tone</annotation>
+		<annotation cp="🙋🏾‍♀">gesture | medium-dark skin tone | raising hand | woman | woman raising hand</annotation>
+		<annotation cp="🙋🏾‍♀" type="tts">woman raising hand: medium-dark skin tone</annotation>
+		<annotation cp="🙋🏿‍♀">dark skin tone | gesture | raising hand | woman | woman raising hand</annotation>
+		<annotation cp="🙋🏿‍♀" type="tts">woman raising hand: dark skin tone</annotation>
+		<annotation cp="🧏🏻">accessibility | deaf | deaf person | ear | hear | light skin tone</annotation>
+		<annotation cp="🧏🏻" type="tts">deaf person: light skin tone</annotation>
+		<annotation cp="🧏🏼">accessibility | deaf | deaf person | ear | hear | medium-light skin tone</annotation>
+		<annotation cp="🧏🏼" type="tts">deaf person: medium-light skin tone</annotation>
+		<annotation cp="🧏🏽">accessibility | deaf | deaf person | ear | hear | medium skin tone</annotation>
+		<annotation cp="🧏🏽" type="tts">deaf person: medium skin tone</annotation>
+		<annotation cp="🧏🏾">accessibility | deaf | deaf person | ear | hear | medium-dark skin tone</annotation>
+		<annotation cp="🧏🏾" type="tts">deaf person: medium-dark skin tone</annotation>
+		<annotation cp="🧏🏿">accessibility | dark skin tone | deaf | deaf person | ear | hear</annotation>
+		<annotation cp="🧏🏿" type="tts">deaf person: dark skin tone</annotation>
+		<annotation cp="🧏🏻‍♂">deaf | light skin tone | man</annotation>
+		<annotation cp="🧏🏻‍♂" type="tts">deaf man: light skin tone</annotation>
+		<annotation cp="🧏🏼‍♂">deaf | man | medium-light skin tone</annotation>
+		<annotation cp="🧏🏼‍♂" type="tts">deaf man: medium-light skin tone</annotation>
+		<annotation cp="🧏🏽‍♂">deaf | man | medium skin tone</annotation>
+		<annotation cp="🧏🏽‍♂" type="tts">deaf man: medium skin tone</annotation>
+		<annotation cp="🧏🏾‍♂">deaf | man | medium-dark skin tone</annotation>
+		<annotation cp="🧏🏾‍♂" type="tts">deaf man: medium-dark skin tone</annotation>
+		<annotation cp="🧏🏿‍♂">dark skin tone | deaf | man</annotation>
+		<annotation cp="🧏🏿‍♂" type="tts">deaf man: dark skin tone</annotation>
+		<annotation cp="🧏🏻‍♀">deaf | light skin tone | woman</annotation>
+		<annotation cp="🧏🏻‍♀" type="tts">deaf woman: light skin tone</annotation>
+		<annotation cp="🧏🏼‍♀">deaf | medium-light skin tone | woman</annotation>
+		<annotation cp="🧏🏼‍♀" type="tts">deaf woman: medium-light skin tone</annotation>
+		<annotation cp="🧏🏽‍♀">deaf | medium skin tone | woman</annotation>
+		<annotation cp="🧏🏽‍♀" type="tts">deaf woman: medium skin tone</annotation>
+		<annotation cp="🧏🏾‍♀">deaf | medium-dark skin tone | woman</annotation>
+		<annotation cp="🧏🏾‍♀" type="tts">deaf woman: medium-dark skin tone</annotation>
+		<annotation cp="🧏🏿‍♀">dark skin tone | deaf | woman</annotation>
+		<annotation cp="🧏🏿‍♀" type="tts">deaf woman: dark skin tone</annotation>
+		<annotation cp="🙇🏻">apology | bow | gesture | light skin tone | person bowing | sorry</annotation>
+		<annotation cp="🙇🏻" type="tts">person bowing: light skin tone</annotation>
+		<annotation cp="🙇🏼">apology | bow | gesture | medium-light skin tone | person bowing | sorry</annotation>
+		<annotation cp="🙇🏼" type="tts">person bowing: medium-light skin tone</annotation>
+		<annotation cp="🙇🏽">apology | bow | gesture | medium skin tone | person bowing | sorry</annotation>
+		<annotation cp="🙇🏽" type="tts">person bowing: medium skin tone</annotation>
+		<annotation cp="🙇🏾">apology | bow | gesture | medium-dark skin tone | person bowing | sorry</annotation>
+		<annotation cp="🙇🏾" type="tts">person bowing: medium-dark skin tone</annotation>
+		<annotation cp="🙇🏿">apology | bow | dark skin tone | gesture | person bowing | sorry</annotation>
+		<annotation cp="🙇🏿" type="tts">person bowing: dark skin tone</annotation>
+		<annotation cp="🙇🏻‍♂">apology | bowing | favor | gesture | light skin tone | man | sorry</annotation>
+		<annotation cp="🙇🏻‍♂" type="tts">man bowing: light skin tone</annotation>
+		<annotation cp="🙇🏼‍♂">apology | bowing | favor | gesture | man | medium-light skin tone | sorry</annotation>
+		<annotation cp="🙇🏼‍♂" type="tts">man bowing: medium-light skin tone</annotation>
+		<annotation cp="🙇🏽‍♂">apology | bowing | favor | gesture | man | medium skin tone | sorry</annotation>
+		<annotation cp="🙇🏽‍♂" type="tts">man bowing: medium skin tone</annotation>
+		<annotation cp="🙇🏾‍♂">apology | bowing | favor | gesture | man | medium-dark skin tone | sorry</annotation>
+		<annotation cp="🙇🏾‍♂" type="tts">man bowing: medium-dark skin tone</annotation>
+		<annotation cp="🙇🏿‍♂">apology | bowing | dark skin tone | favor | gesture | man | sorry</annotation>
+		<annotation cp="🙇🏿‍♂" type="tts">man bowing: dark skin tone</annotation>
+		<annotation cp="🙇🏻‍♀">apology | bowing | favor | gesture | light skin tone | sorry | woman</annotation>
+		<annotation cp="🙇🏻‍♀" type="tts">woman bowing: light skin tone</annotation>
+		<annotation cp="🙇🏼‍♀">apology | bowing | favor | gesture | medium-light skin tone | sorry | woman</annotation>
+		<annotation cp="🙇🏼‍♀" type="tts">woman bowing: medium-light skin tone</annotation>
+		<annotation cp="🙇🏽‍♀">apology | bowing | favor | gesture | medium skin tone | sorry | woman</annotation>
+		<annotation cp="🙇🏽‍♀" type="tts">woman bowing: medium skin tone</annotation>
+		<annotation cp="🙇🏾‍♀">apology | bowing | favor | gesture | medium-dark skin tone | sorry | woman</annotation>
+		<annotation cp="🙇🏾‍♀" type="tts">woman bowing: medium-dark skin tone</annotation>
+		<annotation cp="🙇🏿‍♀">apology | bowing | dark skin tone | favor | gesture | sorry | woman</annotation>
+		<annotation cp="🙇🏿‍♀" type="tts">woman bowing: dark skin tone</annotation>
+		<annotation cp="🤦🏻">disbelief | exasperation | face | light skin tone | palm | person facepalming</annotation>
+		<annotation cp="🤦🏻" type="tts">person facepalming: light skin tone</annotation>
+		<annotation cp="🤦🏼">disbelief | exasperation | face | medium-light skin tone | palm | person facepalming</annotation>
+		<annotation cp="🤦🏼" type="tts">person facepalming: medium-light skin tone</annotation>
+		<annotation cp="🤦🏽">disbelief | exasperation | face | medium skin tone | palm | person facepalming</annotation>
+		<annotation cp="🤦🏽" type="tts">person facepalming: medium skin tone</annotation>
+		<annotation cp="🤦🏾">disbelief | exasperation | face | medium-dark skin tone | palm | person facepalming</annotation>
+		<annotation cp="🤦🏾" type="tts">person facepalming: medium-dark skin tone</annotation>
+		<annotation cp="🤦🏿">dark skin tone | disbelief | exasperation | face | palm | person facepalming</annotation>
+		<annotation cp="🤦🏿" type="tts">person facepalming: dark skin tone</annotation>
+		<annotation cp="🤦🏻‍♂">disbelief | exasperation | facepalm | light skin tone | man | man facepalming</annotation>
+		<annotation cp="🤦🏻‍♂" type="tts">man facepalming: light skin tone</annotation>
+		<annotation cp="🤦🏼‍♂">disbelief | exasperation | facepalm | man | man facepalming | medium-light skin tone</annotation>
+		<annotation cp="🤦🏼‍♂" type="tts">man facepalming: medium-light skin tone</annotation>
+		<annotation cp="🤦🏽‍♂">disbelief | exasperation | facepalm | man | man facepalming | medium skin tone</annotation>
+		<annotation cp="🤦🏽‍♂" type="tts">man facepalming: medium skin tone</annotation>
+		<annotation cp="🤦🏾‍♂">disbelief | exasperation | facepalm | man | man facepalming | medium-dark skin tone</annotation>
+		<annotation cp="🤦🏾‍♂" type="tts">man facepalming: medium-dark skin tone</annotation>
+		<annotation cp="🤦🏿‍♂">dark skin tone | disbelief | exasperation | facepalm | man | man facepalming</annotation>
+		<annotation cp="🤦🏿‍♂" type="tts">man facepalming: dark skin tone</annotation>
+		<annotation cp="🤦🏻‍♀">disbelief | exasperation | facepalm | light skin tone | woman | woman facepalming</annotation>
+		<annotation cp="🤦🏻‍♀" type="tts">woman facepalming: light skin tone</annotation>
+		<annotation cp="🤦🏼‍♀">disbelief | exasperation | facepalm | medium-light skin tone | woman | woman facepalming</annotation>
+		<annotation cp="🤦🏼‍♀" type="tts">woman facepalming: medium-light skin tone</annotation>
+		<annotation cp="🤦🏽‍♀">disbelief | exasperation | facepalm | medium skin tone | woman | woman facepalming</annotation>
+		<annotation cp="🤦🏽‍♀" type="tts">woman facepalming: medium skin tone</annotation>
+		<annotation cp="🤦🏾‍♀">disbelief | exasperation | facepalm | medium-dark skin tone | woman | woman facepalming</annotation>
+		<annotation cp="🤦🏾‍♀" type="tts">woman facepalming: medium-dark skin tone</annotation>
+		<annotation cp="🤦🏿‍♀">dark skin tone | disbelief | exasperation | facepalm | woman | woman facepalming</annotation>
+		<annotation cp="🤦🏿‍♀" type="tts">woman facepalming: dark skin tone</annotation>
+		<annotation cp="🤷🏻">doubt | ignorance | indifference | light skin tone | person shrugging | shrug</annotation>
+		<annotation cp="🤷🏻" type="tts">person shrugging: light skin tone</annotation>
+		<annotation cp="🤷🏼">doubt | ignorance | indifference | medium-light skin tone | person shrugging | shrug</annotation>
+		<annotation cp="🤷🏼" type="tts">person shrugging: medium-light skin tone</annotation>
+		<annotation cp="🤷🏽">doubt | ignorance | indifference | medium skin tone | person shrugging | shrug</annotation>
+		<annotation cp="🤷🏽" type="tts">person shrugging: medium skin tone</annotation>
+		<annotation cp="🤷🏾">doubt | ignorance | indifference | medium-dark skin tone | person shrugging | shrug</annotation>
+		<annotation cp="🤷🏾" type="tts">person shrugging: medium-dark skin tone</annotation>
+		<annotation cp="🤷🏿">dark skin tone | doubt | ignorance | indifference | person shrugging | shrug</annotation>
+		<annotation cp="🤷🏿" type="tts">person shrugging: dark skin tone</annotation>
+		<annotation cp="🤷🏻‍♂">doubt | ignorance | indifference | light skin tone | man | man shrugging | shrug</annotation>
+		<annotation cp="🤷🏻‍♂" type="tts">man shrugging: light skin tone</annotation>
+		<annotation cp="🤷🏼‍♂">doubt | ignorance | indifference | man | man shrugging | medium-light skin tone | shrug</annotation>
+		<annotation cp="🤷🏼‍♂" type="tts">man shrugging: medium-light skin tone</annotation>
+		<annotation cp="🤷🏽‍♂">doubt | ignorance | indifference | man | man shrugging | medium skin tone | shrug</annotation>
+		<annotation cp="🤷🏽‍♂" type="tts">man shrugging: medium skin tone</annotation>
+		<annotation cp="🤷🏾‍♂">doubt | ignorance | indifference | man | man shrugging | medium-dark skin tone | shrug</annotation>
+		<annotation cp="🤷🏾‍♂" type="tts">man shrugging: medium-dark skin tone</annotation>
+		<annotation cp="🤷🏿‍♂">dark skin tone | doubt | ignorance | indifference | man | man shrugging | shrug</annotation>
+		<annotation cp="🤷🏿‍♂" type="tts">man shrugging: dark skin tone</annotation>
+		<annotation cp="🤷🏻‍♀">doubt | ignorance | indifference | light skin tone | shrug | woman | woman shrugging</annotation>
+		<annotation cp="🤷🏻‍♀" type="tts">woman shrugging: light skin tone</annotation>
+		<annotation cp="🤷🏼‍♀">doubt | ignorance | indifference | medium-light skin tone | shrug | woman | woman shrugging</annotation>
+		<annotation cp="🤷🏼‍♀" type="tts">woman shrugging: medium-light skin tone</annotation>
+		<annotation cp="🤷🏽‍♀">doubt | ignorance | indifference | medium skin tone | shrug | woman | woman shrugging</annotation>
+		<annotation cp="🤷🏽‍♀" type="tts">woman shrugging: medium skin tone</annotation>
+		<annotation cp="🤷🏾‍♀">doubt | ignorance | indifference | medium-dark skin tone | shrug | woman | woman shrugging</annotation>
+		<annotation cp="🤷🏾‍♀" type="tts">woman shrugging: medium-dark skin tone</annotation>
+		<annotation cp="🤷🏿‍♀">dark skin tone | doubt | ignorance | indifference | shrug | woman | woman shrugging</annotation>
+		<annotation cp="🤷🏿‍♀" type="tts">woman shrugging: dark skin tone</annotation>
+		<annotation cp="🧑🏻‍⚕">doctor | health worker | healthcare | light skin tone | nurse | therapist</annotation>
+		<annotation cp="🧑🏻‍⚕" type="tts">health worker: light skin tone</annotation>
+		<annotation cp="🧑🏼‍⚕">doctor | health worker | healthcare | medium-light skin tone | nurse | therapist</annotation>
+		<annotation cp="🧑🏼‍⚕" type="tts">health worker: medium-light skin tone</annotation>
+		<annotation cp="🧑🏽‍⚕">doctor | health worker | healthcare | medium skin tone | nurse | therapist</annotation>
+		<annotation cp="🧑🏽‍⚕" type="tts">health worker: medium skin tone</annotation>
+		<annotation cp="🧑🏾‍⚕">doctor | health worker | healthcare | medium-dark skin tone | nurse | therapist</annotation>
+		<annotation cp="🧑🏾‍⚕" type="tts">health worker: medium-dark skin tone</annotation>
+		<annotation cp="🧑🏿‍⚕">dark skin tone | doctor | health worker | healthcare | nurse | therapist</annotation>
+		<annotation cp="🧑🏿‍⚕" type="tts">health worker: dark skin tone</annotation>
+		<annotation cp="👨🏻‍⚕">doctor | healthcare | light skin tone | man | man health worker | nurse | therapist</annotation>
+		<annotation cp="👨🏻‍⚕" type="tts">man health worker: light skin tone</annotation>
+		<annotation cp="👨🏼‍⚕">doctor | healthcare | man | man health worker | medium-light skin tone | nurse | therapist</annotation>
+		<annotation cp="👨🏼‍⚕" type="tts">man health worker: medium-light skin tone</annotation>
+		<annotation cp="👨🏽‍⚕">doctor | healthcare | man | man health worker | medium skin tone | nurse | therapist</annotation>
+		<annotation cp="👨🏽‍⚕" type="tts">man health worker: medium skin tone</annotation>
+		<annotation cp="👨🏾‍⚕">doctor | healthcare | man | man health worker | medium-dark skin tone | nurse | therapist</annotation>
+		<annotation cp="👨🏾‍⚕" type="tts">man health worker: medium-dark skin tone</annotation>
+		<annotation cp="👨🏿‍⚕">dark skin tone | doctor | healthcare | man | man health worker | nurse | therapist</annotation>
+		<annotation cp="👨🏿‍⚕" type="tts">man health worker: dark skin tone</annotation>
+		<annotation cp="👩🏻‍⚕">doctor | healthcare | light skin tone | nurse | therapist | woman | woman health worker</annotation>
+		<annotation cp="👩🏻‍⚕" type="tts">woman health worker: light skin tone</annotation>
+		<annotation cp="👩🏼‍⚕">doctor | healthcare | medium-light skin tone | nurse | therapist | woman | woman health worker</annotation>
+		<annotation cp="👩🏼‍⚕" type="tts">woman health worker: medium-light skin tone</annotation>
+		<annotation cp="👩🏽‍⚕">doctor | healthcare | medium skin tone | nurse | therapist | woman | woman health worker</annotation>
+		<annotation cp="👩🏽‍⚕" type="tts">woman health worker: medium skin tone</annotation>
+		<annotation cp="👩🏾‍⚕">doctor | healthcare | medium-dark skin tone | nurse | therapist | woman | woman health worker</annotation>
+		<annotation cp="👩🏾‍⚕" type="tts">woman health worker: medium-dark skin tone</annotation>
+		<annotation cp="👩🏿‍⚕">dark skin tone | doctor | healthcare | nurse | therapist | woman | woman health worker</annotation>
+		<annotation cp="👩🏿‍⚕" type="tts">woman health worker: dark skin tone</annotation>
+		<annotation cp="🧑🏻‍🎓">graduate | light skin tone | student</annotation>
+		<annotation cp="🧑🏻‍🎓" type="tts">student: light skin tone</annotation>
+		<annotation cp="🧑🏼‍🎓">graduate | medium-light skin tone | student</annotation>
+		<annotation cp="🧑🏼‍🎓" type="tts">student: medium-light skin tone</annotation>
+		<annotation cp="🧑🏽‍🎓">graduate | medium skin tone | student</annotation>
+		<annotation cp="🧑🏽‍🎓" type="tts">student: medium skin tone</annotation>
+		<annotation cp="🧑🏾‍🎓">graduate | medium-dark skin tone | student</annotation>
+		<annotation cp="🧑🏾‍🎓" type="tts">student: medium-dark skin tone</annotation>
+		<annotation cp="🧑🏿‍🎓">dark skin tone | graduate | student</annotation>
+		<annotation cp="🧑🏿‍🎓" type="tts">student: dark skin tone</annotation>
+		<annotation cp="👨🏻‍🎓">graduate | light skin tone | man | student</annotation>
+		<annotation cp="👨🏻‍🎓" type="tts">man student: light skin tone</annotation>
+		<annotation cp="👨🏼‍🎓">graduate | man | medium-light skin tone | student</annotation>
+		<annotation cp="👨🏼‍🎓" type="tts">man student: medium-light skin tone</annotation>
+		<annotation cp="👨🏽‍🎓">graduate | man | medium skin tone | student</annotation>
+		<annotation cp="👨🏽‍🎓" type="tts">man student: medium skin tone</annotation>
+		<annotation cp="👨🏾‍🎓">graduate | man | medium-dark skin tone | student</annotation>
+		<annotation cp="👨🏾‍🎓" type="tts">man student: medium-dark skin tone</annotation>
+		<annotation cp="👨🏿‍🎓">dark skin tone | graduate | man | student</annotation>
+		<annotation cp="👨🏿‍🎓" type="tts">man student: dark skin tone</annotation>
+		<annotation cp="👩🏻‍🎓">graduate | light skin tone | student | woman</annotation>
+		<annotation cp="👩🏻‍🎓" type="tts">woman student: light skin tone</annotation>
+		<annotation cp="👩🏼‍🎓">graduate | medium-light skin tone | student | woman</annotation>
+		<annotation cp="👩🏼‍🎓" type="tts">woman student: medium-light skin tone</annotation>
+		<annotation cp="👩🏽‍🎓">graduate | medium skin tone | student | woman</annotation>
+		<annotation cp="👩🏽‍🎓" type="tts">woman student: medium skin tone</annotation>
+		<annotation cp="👩🏾‍🎓">graduate | medium-dark skin tone | student | woman</annotation>
+		<annotation cp="👩🏾‍🎓" type="tts">woman student: medium-dark skin tone</annotation>
+		<annotation cp="👩🏿‍🎓">dark skin tone | graduate | student | woman</annotation>
+		<annotation cp="👩🏿‍🎓" type="tts">woman student: dark skin tone</annotation>
+		<annotation cp="🧑🏻‍🏫">instructor | light skin tone | professor | teacher</annotation>
+		<annotation cp="🧑🏻‍🏫" type="tts">teacher: light skin tone</annotation>
+		<annotation cp="🧑🏼‍🏫">instructor | medium-light skin tone | professor | teacher</annotation>
+		<annotation cp="🧑🏼‍🏫" type="tts">teacher: medium-light skin tone</annotation>
+		<annotation cp="🧑🏽‍🏫">instructor | medium skin tone | professor | teacher</annotation>
+		<annotation cp="🧑🏽‍🏫" type="tts">teacher: medium skin tone</annotation>
+		<annotation cp="🧑🏾‍🏫">instructor | medium-dark skin tone | professor | teacher</annotation>
+		<annotation cp="🧑🏾‍🏫" type="tts">teacher: medium-dark skin tone</annotation>
+		<annotation cp="🧑🏿‍🏫">dark skin tone | instructor | professor | teacher</annotation>
+		<annotation cp="🧑🏿‍🏫" type="tts">teacher: dark skin tone</annotation>
+		<annotation cp="👨🏻‍🏫">instructor | light skin tone | man | professor | teacher</annotation>
+		<annotation cp="👨🏻‍🏫" type="tts">man teacher: light skin tone</annotation>
+		<annotation cp="👨🏼‍🏫">instructor | man | medium-light skin tone | professor | teacher</annotation>
+		<annotation cp="👨🏼‍🏫" type="tts">man teacher: medium-light skin tone</annotation>
+		<annotation cp="👨🏽‍🏫">instructor | man | medium skin tone | professor | teacher</annotation>
+		<annotation cp="👨🏽‍🏫" type="tts">man teacher: medium skin tone</annotation>
+		<annotation cp="👨🏾‍🏫">instructor | man | medium-dark skin tone | professor | teacher</annotation>
+		<annotation cp="👨🏾‍🏫" type="tts">man teacher: medium-dark skin tone</annotation>
+		<annotation cp="👨🏿‍🏫">dark skin tone | instructor | man | professor | teacher</annotation>
+		<annotation cp="👨🏿‍🏫" type="tts">man teacher: dark skin tone</annotation>
+		<annotation cp="👩🏻‍🏫">instructor | light skin tone | professor | teacher | woman</annotation>
+		<annotation cp="👩🏻‍🏫" type="tts">woman teacher: light skin tone</annotation>
+		<annotation cp="👩🏼‍🏫">instructor | medium-light skin tone | professor | teacher | woman</annotation>
+		<annotation cp="👩🏼‍🏫" type="tts">woman teacher: medium-light skin tone</annotation>
+		<annotation cp="👩🏽‍🏫">instructor | medium skin tone | professor | teacher | woman</annotation>
+		<annotation cp="👩🏽‍🏫" type="tts">woman teacher: medium skin tone</annotation>
+		<annotation cp="👩🏾‍🏫">instructor | medium-dark skin tone | professor | teacher | woman</annotation>
+		<annotation cp="👩🏾‍🏫" type="tts">woman teacher: medium-dark skin tone</annotation>
+		<annotation cp="👩🏿‍🏫">dark skin tone | instructor | professor | teacher | woman</annotation>
+		<annotation cp="👩🏿‍🏫" type="tts">woman teacher: dark skin tone</annotation>
+		<annotation cp="🧑🏻‍⚖">judge | justice | light skin tone | scales</annotation>
+		<annotation cp="🧑🏻‍⚖" type="tts">judge: light skin tone</annotation>
+		<annotation cp="🧑🏼‍⚖">judge | justice | medium-light skin tone | scales</annotation>
+		<annotation cp="🧑🏼‍⚖" type="tts">judge: medium-light skin tone</annotation>
+		<annotation cp="🧑🏽‍⚖">judge | justice | medium skin tone | scales</annotation>
+		<annotation cp="🧑🏽‍⚖" type="tts">judge: medium skin tone</annotation>
+		<annotation cp="🧑🏾‍⚖">judge | justice | medium-dark skin tone | scales</annotation>
+		<annotation cp="🧑🏾‍⚖" type="tts">judge: medium-dark skin tone</annotation>
+		<annotation cp="🧑🏿‍⚖">dark skin tone | judge | justice | scales</annotation>
+		<annotation cp="🧑🏿‍⚖" type="tts">judge: dark skin tone</annotation>
+		<annotation cp="👨🏻‍⚖">judge | justice | light skin tone | man | scales</annotation>
+		<annotation cp="👨🏻‍⚖" type="tts">man judge: light skin tone</annotation>
+		<annotation cp="👨🏼‍⚖">judge | justice | man | medium-light skin tone | scales</annotation>
+		<annotation cp="👨🏼‍⚖" type="tts">man judge: medium-light skin tone</annotation>
+		<annotation cp="👨🏽‍⚖">judge | justice | man | medium skin tone | scales</annotation>
+		<annotation cp="👨🏽‍⚖" type="tts">man judge: medium skin tone</annotation>
+		<annotation cp="👨🏾‍⚖">judge | justice | man | medium-dark skin tone | scales</annotation>
+		<annotation cp="👨🏾‍⚖" type="tts">man judge: medium-dark skin tone</annotation>
+		<annotation cp="👨🏿‍⚖">dark skin tone | judge | justice | man | scales</annotation>
+		<annotation cp="👨🏿‍⚖" type="tts">man judge: dark skin tone</annotation>
+		<annotation cp="👩🏻‍⚖">judge | justice | light skin tone | scales | woman</annotation>
+		<annotation cp="👩🏻‍⚖" type="tts">woman judge: light skin tone</annotation>
+		<annotation cp="👩🏼‍⚖">judge | justice | medium-light skin tone | scales | woman</annotation>
+		<annotation cp="👩🏼‍⚖" type="tts">woman judge: medium-light skin tone</annotation>
+		<annotation cp="👩🏽‍⚖">judge | justice | medium skin tone | scales | woman</annotation>
+		<annotation cp="👩🏽‍⚖" type="tts">woman judge: medium skin tone</annotation>
+		<annotation cp="👩🏾‍⚖">judge | justice | medium-dark skin tone | scales | woman</annotation>
+		<annotation cp="👩🏾‍⚖" type="tts">woman judge: medium-dark skin tone</annotation>
+		<annotation cp="👩🏿‍⚖">dark skin tone | judge | justice | scales | woman</annotation>
+		<annotation cp="👩🏿‍⚖" type="tts">woman judge: dark skin tone</annotation>
+		<annotation cp="🧑🏻‍🌾">farmer | gardener | light skin tone | rancher</annotation>
+		<annotation cp="🧑🏻‍🌾" type="tts">farmer: light skin tone</annotation>
+		<annotation cp="🧑🏼‍🌾">farmer | gardener | medium-light skin tone | rancher</annotation>
+		<annotation cp="🧑🏼‍🌾" type="tts">farmer: medium-light skin tone</annotation>
+		<annotation cp="🧑🏽‍🌾">farmer | gardener | medium skin tone | rancher</annotation>
+		<annotation cp="🧑🏽‍🌾" type="tts">farmer: medium skin tone</annotation>
+		<annotation cp="🧑🏾‍🌾">farmer | gardener | medium-dark skin tone | rancher</annotation>
+		<annotation cp="🧑🏾‍🌾" type="tts">farmer: medium-dark skin tone</annotation>
+		<annotation cp="🧑🏿‍🌾">dark skin tone | farmer | gardener | rancher</annotation>
+		<annotation cp="🧑🏿‍🌾" type="tts">farmer: dark skin tone</annotation>
+		<annotation cp="👨🏻‍🌾">farmer | gardener | light skin tone | man | rancher</annotation>
+		<annotation cp="👨🏻‍🌾" type="tts">man farmer: light skin tone</annotation>
+		<annotation cp="👨🏼‍🌾">farmer | gardener | man | medium-light skin tone | rancher</annotation>
+		<annotation cp="👨🏼‍🌾" type="tts">man farmer: medium-light skin tone</annotation>
+		<annotation cp="👨🏽‍🌾">farmer | gardener | man | medium skin tone | rancher</annotation>
+		<annotation cp="👨🏽‍🌾" type="tts">man farmer: medium skin tone</annotation>
+		<annotation cp="👨🏾‍🌾">farmer | gardener | man | medium-dark skin tone | rancher</annotation>
+		<annotation cp="👨🏾‍🌾" type="tts">man farmer: medium-dark skin tone</annotation>
+		<annotation cp="👨🏿‍🌾">dark skin tone | farmer | gardener | man | rancher</annotation>
+		<annotation cp="👨🏿‍🌾" type="tts">man farmer: dark skin tone</annotation>
+		<annotation cp="👩🏻‍🌾">farmer | gardener | light skin tone | rancher | woman</annotation>
+		<annotation cp="👩🏻‍🌾" type="tts">woman farmer: light skin tone</annotation>
+		<annotation cp="👩🏼‍🌾">farmer | gardener | medium-light skin tone | rancher | woman</annotation>
+		<annotation cp="👩🏼‍🌾" type="tts">woman farmer: medium-light skin tone</annotation>
+		<annotation cp="👩🏽‍🌾">farmer | gardener | medium skin tone | rancher | woman</annotation>
+		<annotation cp="👩🏽‍🌾" type="tts">woman farmer: medium skin tone</annotation>
+		<annotation cp="👩🏾‍🌾">farmer | gardener | medium-dark skin tone | rancher | woman</annotation>
+		<annotation cp="👩🏾‍🌾" type="tts">woman farmer: medium-dark skin tone</annotation>
+		<annotation cp="👩🏿‍🌾">dark skin tone | farmer | gardener | rancher | woman</annotation>
+		<annotation cp="👩🏿‍🌾" type="tts">woman farmer: dark skin tone</annotation>
+		<annotation cp="🧑🏻‍🍳">chef | cook | light skin tone</annotation>
+		<annotation cp="🧑🏻‍🍳" type="tts">cook: light skin tone</annotation>
+		<annotation cp="🧑🏼‍🍳">chef | cook | medium-light skin tone</annotation>
+		<annotation cp="🧑🏼‍🍳" type="tts">cook: medium-light skin tone</annotation>
+		<annotation cp="🧑🏽‍🍳">chef | cook | medium skin tone</annotation>
+		<annotation cp="🧑🏽‍🍳" type="tts">cook: medium skin tone</annotation>
+		<annotation cp="🧑🏾‍🍳">chef | cook | medium-dark skin tone</annotation>
+		<annotation cp="🧑🏾‍🍳" type="tts">cook: medium-dark skin tone</annotation>
+		<annotation cp="🧑🏿‍🍳">chef | cook | dark skin tone</annotation>
+		<annotation cp="🧑🏿‍🍳" type="tts">cook: dark skin tone</annotation>
+		<annotation cp="👨🏻‍🍳">chef | cook | light skin tone | man</annotation>
+		<annotation cp="👨🏻‍🍳" type="tts">man cook: light skin tone</annotation>
+		<annotation cp="👨🏼‍🍳">chef | cook | man | medium-light skin tone</annotation>
+		<annotation cp="👨🏼‍🍳" type="tts">man cook: medium-light skin tone</annotation>
+		<annotation cp="👨🏽‍🍳">chef | cook | man | medium skin tone</annotation>
+		<annotation cp="👨🏽‍🍳" type="tts">man cook: medium skin tone</annotation>
+		<annotation cp="👨🏾‍🍳">chef | cook | man | medium-dark skin tone</annotation>
+		<annotation cp="👨🏾‍🍳" type="tts">man cook: medium-dark skin tone</annotation>
+		<annotation cp="👨🏿‍🍳">chef | cook | dark skin tone | man</annotation>
+		<annotation cp="👨🏿‍🍳" type="tts">man cook: dark skin tone</annotation>
+		<annotation cp="👩🏻‍🍳">chef | cook | light skin tone | woman</annotation>
+		<annotation cp="👩🏻‍🍳" type="tts">woman cook: light skin tone</annotation>
+		<annotation cp="👩🏼‍🍳">chef | cook | medium-light skin tone | woman</annotation>
+		<annotation cp="👩🏼‍🍳" type="tts">woman cook: medium-light skin tone</annotation>
+		<annotation cp="👩🏽‍🍳">chef | cook | medium skin tone | woman</annotation>
+		<annotation cp="👩🏽‍🍳" type="tts">woman cook: medium skin tone</annotation>
+		<annotation cp="👩🏾‍🍳">chef | cook | medium-dark skin tone | woman</annotation>
+		<annotation cp="👩🏾‍🍳" type="tts">woman cook: medium-dark skin tone</annotation>
+		<annotation cp="👩🏿‍🍳">chef | cook | dark skin tone | woman</annotation>
+		<annotation cp="👩🏿‍🍳" type="tts">woman cook: dark skin tone</annotation>
+		<annotation cp="🧑🏻‍🔧">electrician | light skin tone | mechanic | plumber | tradesperson</annotation>
+		<annotation cp="🧑🏻‍🔧" type="tts">mechanic: light skin tone</annotation>
+		<annotation cp="🧑🏼‍🔧">electrician | mechanic | medium-light skin tone | plumber | tradesperson</annotation>
+		<annotation cp="🧑🏼‍🔧" type="tts">mechanic: medium-light skin tone</annotation>
+		<annotation cp="🧑🏽‍🔧">electrician | mechanic | medium skin tone | plumber | tradesperson</annotation>
+		<annotation cp="🧑🏽‍🔧" type="tts">mechanic: medium skin tone</annotation>
+		<annotation cp="🧑🏾‍🔧">electrician | mechanic | medium-dark skin tone | plumber | tradesperson</annotation>
+		<annotation cp="🧑🏾‍🔧" type="tts">mechanic: medium-dark skin tone</annotation>
+		<annotation cp="🧑🏿‍🔧">dark skin tone | electrician | mechanic | plumber | tradesperson</annotation>
+		<annotation cp="🧑🏿‍🔧" type="tts">mechanic: dark skin tone</annotation>
+		<annotation cp="👨🏻‍🔧">electrician | light skin tone | man | mechanic | plumber | tradesperson</annotation>
+		<annotation cp="👨🏻‍🔧" type="tts">man mechanic: light skin tone</annotation>
+		<annotation cp="👨🏼‍🔧">electrician | man | mechanic | medium-light skin tone | plumber | tradesperson</annotation>
+		<annotation cp="👨🏼‍🔧" type="tts">man mechanic: medium-light skin tone</annotation>
+		<annotation cp="👨🏽‍🔧">electrician | man | mechanic | medium skin tone | plumber | tradesperson</annotation>
+		<annotation cp="👨🏽‍🔧" type="tts">man mechanic: medium skin tone</annotation>
+		<annotation cp="👨🏾‍🔧">electrician | man | mechanic | medium-dark skin tone | plumber | tradesperson</annotation>
+		<annotation cp="👨🏾‍🔧" type="tts">man mechanic: medium-dark skin tone</annotation>
+		<annotation cp="👨🏿‍🔧">dark skin tone | electrician | man | mechanic | plumber | tradesperson</annotation>
+		<annotation cp="👨🏿‍🔧" type="tts">man mechanic: dark skin tone</annotation>
+		<annotation cp="👩🏻‍🔧">electrician | light skin tone | mechanic | plumber | tradesperson | woman</annotation>
+		<annotation cp="👩🏻‍🔧" type="tts">woman mechanic: light skin tone</annotation>
+		<annotation cp="👩🏼‍🔧">electrician | mechanic | medium-light skin tone | plumber | tradesperson | woman</annotation>
+		<annotation cp="👩🏼‍🔧" type="tts">woman mechanic: medium-light skin tone</annotation>
+		<annotation cp="👩🏽‍🔧">electrician | mechanic | medium skin tone | plumber | tradesperson | woman</annotation>
+		<annotation cp="👩🏽‍🔧" type="tts">woman mechanic: medium skin tone</annotation>
+		<annotation cp="👩🏾‍🔧">electrician | mechanic | medium-dark skin tone | plumber | tradesperson | woman</annotation>
+		<annotation cp="👩🏾‍🔧" type="tts">woman mechanic: medium-dark skin tone</annotation>
+		<annotation cp="👩🏿‍🔧">dark skin tone | electrician | mechanic | plumber | tradesperson | woman</annotation>
+		<annotation cp="👩🏿‍🔧" type="tts">woman mechanic: dark skin tone</annotation>
+		<annotation cp="🧑🏻‍🏭">assembly | factory | industrial | light skin tone | worker</annotation>
+		<annotation cp="🧑🏻‍🏭" type="tts">factory worker: light skin tone</annotation>
+		<annotation cp="🧑🏼‍🏭">assembly | factory | industrial | medium-light skin tone | worker</annotation>
+		<annotation cp="🧑🏼‍🏭" type="tts">factory worker: medium-light skin tone</annotation>
+		<annotation cp="🧑🏽‍🏭">assembly | factory | industrial | medium skin tone | worker</annotation>
+		<annotation cp="🧑🏽‍🏭" type="tts">factory worker: medium skin tone</annotation>
+		<annotation cp="🧑🏾‍🏭">assembly | factory | industrial | medium-dark skin tone | worker</annotation>
+		<annotation cp="🧑🏾‍🏭" type="tts">factory worker: medium-dark skin tone</annotation>
+		<annotation cp="🧑🏿‍🏭">assembly | dark skin tone | factory | industrial | worker</annotation>
+		<annotation cp="🧑🏿‍🏭" type="tts">factory worker: dark skin tone</annotation>
+		<annotation cp="👨🏻‍🏭">assembly | factory | industrial | light skin tone | man | worker</annotation>
+		<annotation cp="👨🏻‍🏭" type="tts">man factory worker: light skin tone</annotation>
+		<annotation cp="👨🏼‍🏭">assembly | factory | industrial | man | medium-light skin tone | worker</annotation>
+		<annotation cp="👨🏼‍🏭" type="tts">man factory worker: medium-light skin tone</annotation>
+		<annotation cp="👨🏽‍🏭">assembly | factory | industrial | man | medium skin tone | worker</annotation>
+		<annotation cp="👨🏽‍🏭" type="tts">man factory worker: medium skin tone</annotation>
+		<annotation cp="👨🏾‍🏭">assembly | factory | industrial | man | medium-dark skin tone | worker</annotation>
+		<annotation cp="👨🏾‍🏭" type="tts">man factory worker: medium-dark skin tone</annotation>
+		<annotation cp="👨🏿‍🏭">assembly | dark skin tone | factory | industrial | man | worker</annotation>
+		<annotation cp="👨🏿‍🏭" type="tts">man factory worker: dark skin tone</annotation>
+		<annotation cp="👩🏻‍🏭">assembly | factory | industrial | light skin tone | woman | worker</annotation>
+		<annotation cp="👩🏻‍🏭" type="tts">woman factory worker: light skin tone</annotation>
+		<annotation cp="👩🏼‍🏭">assembly | factory | industrial | medium-light skin tone | woman | worker</annotation>
+		<annotation cp="👩🏼‍🏭" type="tts">woman factory worker: medium-light skin tone</annotation>
+		<annotation cp="👩🏽‍🏭">assembly | factory | industrial | medium skin tone | woman | worker</annotation>
+		<annotation cp="👩🏽‍🏭" type="tts">woman factory worker: medium skin tone</annotation>
+		<annotation cp="👩🏾‍🏭">assembly | factory | industrial | medium-dark skin tone | woman | worker</annotation>
+		<annotation cp="👩🏾‍🏭" type="tts">woman factory worker: medium-dark skin tone</annotation>
+		<annotation cp="👩🏿‍🏭">assembly | dark skin tone | factory | industrial | woman | worker</annotation>
+		<annotation cp="👩🏿‍🏭" type="tts">woman factory worker: dark skin tone</annotation>
+		<annotation cp="🧑🏻‍💼">architect | business | light skin tone | manager | office worker | white-collar</annotation>
+		<annotation cp="🧑🏻‍💼" type="tts">office worker: light skin tone</annotation>
+		<annotation cp="🧑🏼‍💼">architect | business | manager | medium-light skin tone | office worker | white-collar</annotation>
+		<annotation cp="🧑🏼‍💼" type="tts">office worker: medium-light skin tone</annotation>
+		<annotation cp="🧑🏽‍💼">architect | business | manager | medium skin tone | office worker | white-collar</annotation>
+		<annotation cp="🧑🏽‍💼" type="tts">office worker: medium skin tone</annotation>
+		<annotation cp="🧑🏾‍💼">architect | business | manager | medium-dark skin tone | office worker | white-collar</annotation>
+		<annotation cp="🧑🏾‍💼" type="tts">office worker: medium-dark skin tone</annotation>
+		<annotation cp="🧑🏿‍💼">architect | business | dark skin tone | manager | office worker | white-collar</annotation>
+		<annotation cp="🧑🏿‍💼" type="tts">office worker: dark skin tone</annotation>
+		<annotation cp="👨🏻‍💼">architect | business | light skin tone | man | man office worker | manager | white-collar</annotation>
+		<annotation cp="👨🏻‍💼" type="tts">man office worker: light skin tone</annotation>
+		<annotation cp="👨🏼‍💼">architect | business | man | man office worker | manager | medium-light skin tone | white-collar</annotation>
+		<annotation cp="👨🏼‍💼" type="tts">man office worker: medium-light skin tone</annotation>
+		<annotation cp="👨🏽‍💼">architect | business | man | man office worker | manager | medium skin tone | white-collar</annotation>
+		<annotation cp="👨🏽‍💼" type="tts">man office worker: medium skin tone</annotation>
+		<annotation cp="👨🏾‍💼">architect | business | man | man office worker | manager | medium-dark skin tone | white-collar</annotation>
+		<annotation cp="👨🏾‍💼" type="tts">man office worker: medium-dark skin tone</annotation>
+		<annotation cp="👨🏿‍💼">architect | business | dark skin tone | man | man office worker | manager | white-collar</annotation>
+		<annotation cp="👨🏿‍💼" type="tts">man office worker: dark skin tone</annotation>
+		<annotation cp="👩🏻‍💼">architect | business | light skin tone | manager | white-collar | woman | woman office worker</annotation>
+		<annotation cp="👩🏻‍💼" type="tts">woman office worker: light skin tone</annotation>
+		<annotation cp="👩🏼‍💼">architect | business | manager | medium-light skin tone | white-collar | woman | woman office worker</annotation>
+		<annotation cp="👩🏼‍💼" type="tts">woman office worker: medium-light skin tone</annotation>
+		<annotation cp="👩🏽‍💼">architect | business | manager | medium skin tone | white-collar | woman | woman office worker</annotation>
+		<annotation cp="👩🏽‍💼" type="tts">woman office worker: medium skin tone</annotation>
+		<annotation cp="👩🏾‍💼">architect | business | manager | medium-dark skin tone | white-collar | woman | woman office worker</annotation>
+		<annotation cp="👩🏾‍💼" type="tts">woman office worker: medium-dark skin tone</annotation>
+		<annotation cp="👩🏿‍💼">architect | business | dark skin tone | manager | white-collar | woman | woman office worker</annotation>
+		<annotation cp="👩🏿‍💼" type="tts">woman office worker: dark skin tone</annotation>
+		<annotation cp="🧑🏻‍🔬">biologist | chemist | engineer | light skin tone | physicist | scientist</annotation>
+		<annotation cp="🧑🏻‍🔬" type="tts">scientist: light skin tone</annotation>
+		<annotation cp="🧑🏼‍🔬">biologist | chemist | engineer | medium-light skin tone | physicist | scientist</annotation>
+		<annotation cp="🧑🏼‍🔬" type="tts">scientist: medium-light skin tone</annotation>
+		<annotation cp="🧑🏽‍🔬">biologist | chemist | engineer | medium skin tone | physicist | scientist</annotation>
+		<annotation cp="🧑🏽‍🔬" type="tts">scientist: medium skin tone</annotation>
+		<annotation cp="🧑🏾‍🔬">biologist | chemist | engineer | medium-dark skin tone | physicist | scientist</annotation>
+		<annotation cp="🧑🏾‍🔬" type="tts">scientist: medium-dark skin tone</annotation>
+		<annotation cp="🧑🏿‍🔬">biologist | chemist | dark skin tone | engineer | physicist | scientist</annotation>
+		<annotation cp="🧑🏿‍🔬" type="tts">scientist: dark skin tone</annotation>
+		<annotation cp="👨🏻‍🔬">biologist | chemist | engineer | light skin tone | man | physicist | scientist</annotation>
+		<annotation cp="👨🏻‍🔬" type="tts">man scientist: light skin tone</annotation>
+		<annotation cp="👨🏼‍🔬">biologist | chemist | engineer | man | medium-light skin tone | physicist | scientist</annotation>
+		<annotation cp="👨🏼‍🔬" type="tts">man scientist: medium-light skin tone</annotation>
+		<annotation cp="👨🏽‍🔬">biologist | chemist | engineer | man | medium skin tone | physicist | scientist</annotation>
+		<annotation cp="👨🏽‍🔬" type="tts">man scientist: medium skin tone</annotation>
+		<annotation cp="👨🏾‍🔬">biologist | chemist | engineer | man | medium-dark skin tone | physicist | scientist</annotation>
+		<annotation cp="👨🏾‍🔬" type="tts">man scientist: medium-dark skin tone</annotation>
+		<annotation cp="👨🏿‍🔬">biologist | chemist | dark skin tone | engineer | man | physicist | scientist</annotation>
+		<annotation cp="👨🏿‍🔬" type="tts">man scientist: dark skin tone</annotation>
+		<annotation cp="👩🏻‍🔬">biologist | chemist | engineer | light skin tone | physicist | scientist | woman</annotation>
+		<annotation cp="👩🏻‍🔬" type="tts">woman scientist: light skin tone</annotation>
+		<annotation cp="👩🏼‍🔬">biologist | chemist | engineer | medium-light skin tone | physicist | scientist | woman</annotation>
+		<annotation cp="👩🏼‍🔬" type="tts">woman scientist: medium-light skin tone</annotation>
+		<annotation cp="👩🏽‍🔬">biologist | chemist | engineer | medium skin tone | physicist | scientist | woman</annotation>
+		<annotation cp="👩🏽‍🔬" type="tts">woman scientist: medium skin tone</annotation>
+		<annotation cp="👩🏾‍🔬">biologist | chemist | engineer | medium-dark skin tone | physicist | scientist | woman</annotation>
+		<annotation cp="👩🏾‍🔬" type="tts">woman scientist: medium-dark skin tone</annotation>
+		<annotation cp="👩🏿‍🔬">biologist | chemist | dark skin tone | engineer | physicist | scientist | woman</annotation>
+		<annotation cp="👩🏿‍🔬" type="tts">woman scientist: dark skin tone</annotation>
+		<annotation cp="🧑🏻‍💻">coder | developer | inventor | light skin tone | software | technologist</annotation>
+		<annotation cp="🧑🏻‍💻" type="tts">technologist: light skin tone</annotation>
+		<annotation cp="🧑🏼‍💻">coder | developer | inventor | medium-light skin tone | software | technologist</annotation>
+		<annotation cp="🧑🏼‍💻" type="tts">technologist: medium-light skin tone</annotation>
+		<annotation cp="🧑🏽‍💻">coder | developer | inventor | medium skin tone | software | technologist</annotation>
+		<annotation cp="🧑🏽‍💻" type="tts">technologist: medium skin tone</annotation>
+		<annotation cp="🧑🏾‍💻">coder | developer | inventor | medium-dark skin tone | software | technologist</annotation>
+		<annotation cp="🧑🏾‍💻" type="tts">technologist: medium-dark skin tone</annotation>
+		<annotation cp="🧑🏿‍💻">coder | dark skin tone | developer | inventor | software | technologist</annotation>
+		<annotation cp="🧑🏿‍💻" type="tts">technologist: dark skin tone</annotation>
+		<annotation cp="👨🏻‍💻">coder | developer | inventor | light skin tone | man | software | technologist</annotation>
+		<annotation cp="👨🏻‍💻" type="tts">man technologist: light skin tone</annotation>
+		<annotation cp="👨🏼‍💻">coder | developer | inventor | man | medium-light skin tone | software | technologist</annotation>
+		<annotation cp="👨🏼‍💻" type="tts">man technologist: medium-light skin tone</annotation>
+		<annotation cp="👨🏽‍💻">coder | developer | inventor | man | medium skin tone | software | technologist</annotation>
+		<annotation cp="👨🏽‍💻" type="tts">man technologist: medium skin tone</annotation>
+		<annotation cp="👨🏾‍💻">coder | developer | inventor | man | medium-dark skin tone | software | technologist</annotation>
+		<annotation cp="👨🏾‍💻" type="tts">man technologist: medium-dark skin tone</annotation>
+		<annotation cp="👨🏿‍💻">coder | dark skin tone | developer | inventor | man | software | technologist</annotation>
+		<annotation cp="👨🏿‍💻" type="tts">man technologist: dark skin tone</annotation>
+		<annotation cp="👩🏻‍💻">coder | developer | inventor | light skin tone | software | technologist | woman</annotation>
+		<annotation cp="👩🏻‍💻" type="tts">woman technologist: light skin tone</annotation>
+		<annotation cp="👩🏼‍💻">coder | developer | inventor | medium-light skin tone | software | technologist | woman</annotation>
+		<annotation cp="👩🏼‍💻" type="tts">woman technologist: medium-light skin tone</annotation>
+		<annotation cp="👩🏽‍💻">coder | developer | inventor | medium skin tone | software | technologist | woman</annotation>
+		<annotation cp="👩🏽‍💻" type="tts">woman technologist: medium skin tone</annotation>
+		<annotation cp="👩🏾‍💻">coder | developer | inventor | medium-dark skin tone | software | technologist | woman</annotation>
+		<annotation cp="👩🏾‍💻" type="tts">woman technologist: medium-dark skin tone</annotation>
+		<annotation cp="👩🏿‍💻">coder | dark skin tone | developer | inventor | software | technologist | woman</annotation>
+		<annotation cp="👩🏿‍💻" type="tts">woman technologist: dark skin tone</annotation>
+		<annotation cp="🧑🏻‍🎤">actor | entertainer | light skin tone | rock | singer | star</annotation>
+		<annotation cp="🧑🏻‍🎤" type="tts">singer: light skin tone</annotation>
+		<annotation cp="🧑🏼‍🎤">actor | entertainer | medium-light skin tone | rock | singer | star</annotation>
+		<annotation cp="🧑🏼‍🎤" type="tts">singer: medium-light skin tone</annotation>
+		<annotation cp="🧑🏽‍🎤">actor | entertainer | medium skin tone | rock | singer | star</annotation>
+		<annotation cp="🧑🏽‍🎤" type="tts">singer: medium skin tone</annotation>
+		<annotation cp="🧑🏾‍🎤">actor | entertainer | medium-dark skin tone | rock | singer | star</annotation>
+		<annotation cp="🧑🏾‍🎤" type="tts">singer: medium-dark skin tone</annotation>
+		<annotation cp="🧑🏿‍🎤">actor | dark skin tone | entertainer | rock | singer | star</annotation>
+		<annotation cp="🧑🏿‍🎤" type="tts">singer: dark skin tone</annotation>
+		<annotation cp="👨🏻‍🎤">actor | entertainer | light skin tone | man | rock | singer | star</annotation>
+		<annotation cp="👨🏻‍🎤" type="tts">man singer: light skin tone</annotation>
+		<annotation cp="👨🏼‍🎤">actor | entertainer | man | medium-light skin tone | rock | singer | star</annotation>
+		<annotation cp="👨🏼‍🎤" type="tts">man singer: medium-light skin tone</annotation>
+		<annotation cp="👨🏽‍🎤">actor | entertainer | man | medium skin tone | rock | singer | star</annotation>
+		<annotation cp="👨🏽‍🎤" type="tts">man singer: medium skin tone</annotation>
+		<annotation cp="👨🏾‍🎤">actor | entertainer | man | medium-dark skin tone | rock | singer | star</annotation>
+		<annotation cp="👨🏾‍🎤" type="tts">man singer: medium-dark skin tone</annotation>
+		<annotation cp="👨🏿‍🎤">actor | dark skin tone | entertainer | man | rock | singer | star</annotation>
+		<annotation cp="👨🏿‍🎤" type="tts">man singer: dark skin tone</annotation>
+		<annotation cp="👩🏻‍🎤">actor | entertainer | light skin tone | rock | singer | star | woman</annotation>
+		<annotation cp="👩🏻‍🎤" type="tts">woman singer: light skin tone</annotation>
+		<annotation cp="👩🏼‍🎤">actor | entertainer | medium-light skin tone | rock | singer | star | woman</annotation>
+		<annotation cp="👩🏼‍🎤" type="tts">woman singer: medium-light skin tone</annotation>
+		<annotation cp="👩🏽‍🎤">actor | entertainer | medium skin tone | rock | singer | star | woman</annotation>
+		<annotation cp="👩🏽‍🎤" type="tts">woman singer: medium skin tone</annotation>
+		<annotation cp="👩🏾‍🎤">actor | entertainer | medium-dark skin tone | rock | singer | star | woman</annotation>
+		<annotation cp="👩🏾‍🎤" type="tts">woman singer: medium-dark skin tone</annotation>
+		<annotation cp="👩🏿‍🎤">actor | dark skin tone | entertainer | rock | singer | star | woman</annotation>
+		<annotation cp="👩🏿‍🎤" type="tts">woman singer: dark skin tone</annotation>
+		<annotation cp="🧑🏻‍🎨">artist | light skin tone | palette</annotation>
+		<annotation cp="🧑🏻‍🎨" type="tts">artist: light skin tone</annotation>
+		<annotation cp="🧑🏼‍🎨">artist | medium-light skin tone | palette</annotation>
+		<annotation cp="🧑🏼‍🎨" type="tts">artist: medium-light skin tone</annotation>
+		<annotation cp="🧑🏽‍🎨">artist | medium skin tone | palette</annotation>
+		<annotation cp="🧑🏽‍🎨" type="tts">artist: medium skin tone</annotation>
+		<annotation cp="🧑🏾‍🎨">artist | medium-dark skin tone | palette</annotation>
+		<annotation cp="🧑🏾‍🎨" type="tts">artist: medium-dark skin tone</annotation>
+		<annotation cp="🧑🏿‍🎨">artist | dark skin tone | palette</annotation>
+		<annotation cp="🧑🏿‍🎨" type="tts">artist: dark skin tone</annotation>
+		<annotation cp="👨🏻‍🎨">artist | light skin tone | man | palette</annotation>
+		<annotation cp="👨🏻‍🎨" type="tts">man artist: light skin tone</annotation>
+		<annotation cp="👨🏼‍🎨">artist | man | medium-light skin tone | palette</annotation>
+		<annotation cp="👨🏼‍🎨" type="tts">man artist: medium-light skin tone</annotation>
+		<annotation cp="👨🏽‍🎨">artist | man | medium skin tone | palette</annotation>
+		<annotation cp="👨🏽‍🎨" type="tts">man artist: medium skin tone</annotation>
+		<annotation cp="👨🏾‍🎨">artist | man | medium-dark skin tone | palette</annotation>
+		<annotation cp="👨🏾‍🎨" type="tts">man artist: medium-dark skin tone</annotation>
+		<annotation cp="👨🏿‍🎨">artist | dark skin tone | man | palette</annotation>
+		<annotation cp="👨🏿‍🎨" type="tts">man artist: dark skin tone</annotation>
+		<annotation cp="👩🏻‍🎨">artist | light skin tone | palette | woman</annotation>
+		<annotation cp="👩🏻‍🎨" type="tts">woman artist: light skin tone</annotation>
+		<annotation cp="👩🏼‍🎨">artist | medium-light skin tone | palette | woman</annotation>
+		<annotation cp="👩🏼‍🎨" type="tts">woman artist: medium-light skin tone</annotation>
+		<annotation cp="👩🏽‍🎨">artist | medium skin tone | palette | woman</annotation>
+		<annotation cp="👩🏽‍🎨" type="tts">woman artist: medium skin tone</annotation>
+		<annotation cp="👩🏾‍🎨">artist | medium-dark skin tone | palette | woman</annotation>
+		<annotation cp="👩🏾‍🎨" type="tts">woman artist: medium-dark skin tone</annotation>
+		<annotation cp="👩🏿‍🎨">artist | dark skin tone | palette | woman</annotation>
+		<annotation cp="👩🏿‍🎨" type="tts">woman artist: dark skin tone</annotation>
+		<annotation cp="🧑🏻‍✈">light skin tone | pilot | plane</annotation>
+		<annotation cp="🧑🏻‍✈" type="tts">pilot: light skin tone</annotation>
+		<annotation cp="🧑🏼‍✈">medium-light skin tone | pilot | plane</annotation>
+		<annotation cp="🧑🏼‍✈" type="tts">pilot: medium-light skin tone</annotation>
+		<annotation cp="🧑🏽‍✈">medium skin tone | pilot | plane</annotation>
+		<annotation cp="🧑🏽‍✈" type="tts">pilot: medium skin tone</annotation>
+		<annotation cp="🧑🏾‍✈">medium-dark skin tone | pilot | plane</annotation>
+		<annotation cp="🧑🏾‍✈" type="tts">pilot: medium-dark skin tone</annotation>
+		<annotation cp="🧑🏿‍✈">dark skin tone | pilot | plane</annotation>
+		<annotation cp="🧑🏿‍✈" type="tts">pilot: dark skin tone</annotation>
+		<annotation cp="👨🏻‍✈">light skin tone | man | pilot | plane</annotation>
+		<annotation cp="👨🏻‍✈" type="tts">man pilot: light skin tone</annotation>
+		<annotation cp="👨🏼‍✈">man | medium-light skin tone | pilot | plane</annotation>
+		<annotation cp="👨🏼‍✈" type="tts">man pilot: medium-light skin tone</annotation>
+		<annotation cp="👨🏽‍✈">man | medium skin tone | pilot | plane</annotation>
+		<annotation cp="👨🏽‍✈" type="tts">man pilot: medium skin tone</annotation>
+		<annotation cp="👨🏾‍✈">man | medium-dark skin tone | pilot | plane</annotation>
+		<annotation cp="👨🏾‍✈" type="tts">man pilot: medium-dark skin tone</annotation>
+		<annotation cp="👨🏿‍✈">dark skin tone | man | pilot | plane</annotation>
+		<annotation cp="👨🏿‍✈" type="tts">man pilot: dark skin tone</annotation>
+		<annotation cp="👩🏻‍✈">light skin tone | pilot | plane | woman</annotation>
+		<annotation cp="👩🏻‍✈" type="tts">woman pilot: light skin tone</annotation>
+		<annotation cp="👩🏼‍✈">medium-light skin tone | pilot | plane | woman</annotation>
+		<annotation cp="👩🏼‍✈" type="tts">woman pilot: medium-light skin tone</annotation>
+		<annotation cp="👩🏽‍✈">medium skin tone | pilot | plane | woman</annotation>
+		<annotation cp="👩🏽‍✈" type="tts">woman pilot: medium skin tone</annotation>
+		<annotation cp="👩🏾‍✈">medium-dark skin tone | pilot | plane | woman</annotation>
+		<annotation cp="👩🏾‍✈" type="tts">woman pilot: medium-dark skin tone</annotation>
+		<annotation cp="👩🏿‍✈">dark skin tone | pilot | plane | woman</annotation>
+		<annotation cp="👩🏿‍✈" type="tts">woman pilot: dark skin tone</annotation>
+		<annotation cp="🧑🏻‍🚀">astronaut | light skin tone | rocket</annotation>
+		<annotation cp="🧑🏻‍🚀" type="tts">astronaut: light skin tone</annotation>
+		<annotation cp="🧑🏼‍🚀">astronaut | medium-light skin tone | rocket</annotation>
+		<annotation cp="🧑🏼‍🚀" type="tts">astronaut: medium-light skin tone</annotation>
+		<annotation cp="🧑🏽‍🚀">astronaut | medium skin tone | rocket</annotation>
+		<annotation cp="🧑🏽‍🚀" type="tts">astronaut: medium skin tone</annotation>
+		<annotation cp="🧑🏾‍🚀">astronaut | medium-dark skin tone | rocket</annotation>
+		<annotation cp="🧑🏾‍🚀" type="tts">astronaut: medium-dark skin tone</annotation>
+		<annotation cp="🧑🏿‍🚀">astronaut | dark skin tone | rocket</annotation>
+		<annotation cp="🧑🏿‍🚀" type="tts">astronaut: dark skin tone</annotation>
+		<annotation cp="👨🏻‍🚀">astronaut | light skin tone | man | rocket</annotation>
+		<annotation cp="👨🏻‍🚀" type="tts">man astronaut: light skin tone</annotation>
+		<annotation cp="👨🏼‍🚀">astronaut | man | medium-light skin tone | rocket</annotation>
+		<annotation cp="👨🏼‍🚀" type="tts">man astronaut: medium-light skin tone</annotation>
+		<annotation cp="👨🏽‍🚀">astronaut | man | medium skin tone | rocket</annotation>
+		<annotation cp="👨🏽‍🚀" type="tts">man astronaut: medium skin tone</annotation>
+		<annotation cp="👨🏾‍🚀">astronaut | man | medium-dark skin tone | rocket</annotation>
+		<annotation cp="👨🏾‍🚀" type="tts">man astronaut: medium-dark skin tone</annotation>
+		<annotation cp="👨🏿‍🚀">astronaut | dark skin tone | man | rocket</annotation>
+		<annotation cp="👨🏿‍🚀" type="tts">man astronaut: dark skin tone</annotation>
+		<annotation cp="👩🏻‍🚀">astronaut | light skin tone | rocket | woman</annotation>
+		<annotation cp="👩🏻‍🚀" type="tts">woman astronaut: light skin tone</annotation>
+		<annotation cp="👩🏼‍🚀">astronaut | medium-light skin tone | rocket | woman</annotation>
+		<annotation cp="👩🏼‍🚀" type="tts">woman astronaut: medium-light skin tone</annotation>
+		<annotation cp="👩🏽‍🚀">astronaut | medium skin tone | rocket | woman</annotation>
+		<annotation cp="👩🏽‍🚀" type="tts">woman astronaut: medium skin tone</annotation>
+		<annotation cp="👩🏾‍🚀">astronaut | medium-dark skin tone | rocket | woman</annotation>
+		<annotation cp="👩🏾‍🚀" type="tts">woman astronaut: medium-dark skin tone</annotation>
+		<annotation cp="👩🏿‍🚀">astronaut | dark skin tone | rocket | woman</annotation>
+		<annotation cp="👩🏿‍🚀" type="tts">woman astronaut: dark skin tone</annotation>
+		<annotation cp="🧑🏻‍🚒">firefighter | firetruck | light skin tone</annotation>
+		<annotation cp="🧑🏻‍🚒" type="tts">firefighter: light skin tone</annotation>
+		<annotation cp="🧑🏼‍🚒">firefighter | firetruck | medium-light skin tone</annotation>
+		<annotation cp="🧑🏼‍🚒" type="tts">firefighter: medium-light skin tone</annotation>
+		<annotation cp="🧑🏽‍🚒">firefighter | firetruck | medium skin tone</annotation>
+		<annotation cp="🧑🏽‍🚒" type="tts">firefighter: medium skin tone</annotation>
+		<annotation cp="🧑🏾‍🚒">firefighter | firetruck | medium-dark skin tone</annotation>
+		<annotation cp="🧑🏾‍🚒" type="tts">firefighter: medium-dark skin tone</annotation>
+		<annotation cp="🧑🏿‍🚒">dark skin tone | firefighter | firetruck</annotation>
+		<annotation cp="🧑🏿‍🚒" type="tts">firefighter: dark skin tone</annotation>
+		<annotation cp="👨🏻‍🚒">firefighter | firetruck | light skin tone | man</annotation>
+		<annotation cp="👨🏻‍🚒" type="tts">man firefighter: light skin tone</annotation>
+		<annotation cp="👨🏼‍🚒">firefighter | firetruck | man | medium-light skin tone</annotation>
+		<annotation cp="👨🏼‍🚒" type="tts">man firefighter: medium-light skin tone</annotation>
+		<annotation cp="👨🏽‍🚒">firefighter | firetruck | man | medium skin tone</annotation>
+		<annotation cp="👨🏽‍🚒" type="tts">man firefighter: medium skin tone</annotation>
+		<annotation cp="👨🏾‍🚒">firefighter | firetruck | man | medium-dark skin tone</annotation>
+		<annotation cp="👨🏾‍🚒" type="tts">man firefighter: medium-dark skin tone</annotation>
+		<annotation cp="👨🏿‍🚒">dark skin tone | firefighter | firetruck | man</annotation>
+		<annotation cp="👨🏿‍🚒" type="tts">man firefighter: dark skin tone</annotation>
+		<annotation cp="👩🏻‍🚒">firefighter | firetruck | light skin tone | woman</annotation>
+		<annotation cp="👩🏻‍🚒" type="tts">woman firefighter: light skin tone</annotation>
+		<annotation cp="👩🏼‍🚒">firefighter | firetruck | medium-light skin tone | woman</annotation>
+		<annotation cp="👩🏼‍🚒" type="tts">woman firefighter: medium-light skin tone</annotation>
+		<annotation cp="👩🏽‍🚒">firefighter | firetruck | medium skin tone | woman</annotation>
+		<annotation cp="👩🏽‍🚒" type="tts">woman firefighter: medium skin tone</annotation>
+		<annotation cp="👩🏾‍🚒">firefighter | firetruck | medium-dark skin tone | woman</annotation>
+		<annotation cp="👩🏾‍🚒" type="tts">woman firefighter: medium-dark skin tone</annotation>
+		<annotation cp="👩🏿‍🚒">dark skin tone | firefighter | firetruck | woman</annotation>
+		<annotation cp="👩🏿‍🚒" type="tts">woman firefighter: dark skin tone</annotation>
+		<annotation cp="👮🏻">cop | light skin tone | officer | police</annotation>
+		<annotation cp="👮🏻" type="tts">police officer: light skin tone</annotation>
+		<annotation cp="👮🏼">cop | medium-light skin tone | officer | police</annotation>
+		<annotation cp="👮🏼" type="tts">police officer: medium-light skin tone</annotation>
+		<annotation cp="👮🏽">cop | medium skin tone | officer | police</annotation>
+		<annotation cp="👮🏽" type="tts">police officer: medium skin tone</annotation>
+		<annotation cp="👮🏾">cop | medium-dark skin tone | officer | police</annotation>
+		<annotation cp="👮🏾" type="tts">police officer: medium-dark skin tone</annotation>
+		<annotation cp="👮🏿">cop | dark skin tone | officer | police</annotation>
+		<annotation cp="👮🏿" type="tts">police officer: dark skin tone</annotation>
+		<annotation cp="👮🏻‍♂">cop | light skin tone | man | officer | police</annotation>
+		<annotation cp="👮🏻‍♂" type="tts">man police officer: light skin tone</annotation>
+		<annotation cp="👮🏼‍♂">cop | man | medium-light skin tone | officer | police</annotation>
+		<annotation cp="👮🏼‍♂" type="tts">man police officer: medium-light skin tone</annotation>
+		<annotation cp="👮🏽‍♂">cop | man | medium skin tone | officer | police</annotation>
+		<annotation cp="👮🏽‍♂" type="tts">man police officer: medium skin tone</annotation>
+		<annotation cp="👮🏾‍♂">cop | man | medium-dark skin tone | officer | police</annotation>
+		<annotation cp="👮🏾‍♂" type="tts">man police officer: medium-dark skin tone</annotation>
+		<annotation cp="👮🏿‍♂">cop | dark skin tone | man | officer | police</annotation>
+		<annotation cp="👮🏿‍♂" type="tts">man police officer: dark skin tone</annotation>
+		<annotation cp="👮🏻‍♀">cop | light skin tone | officer | police | woman</annotation>
+		<annotation cp="👮🏻‍♀" type="tts">woman police officer: light skin tone</annotation>
+		<annotation cp="👮🏼‍♀">cop | medium-light skin tone | officer | police | woman</annotation>
+		<annotation cp="👮🏼‍♀" type="tts">woman police officer: medium-light skin tone</annotation>
+		<annotation cp="👮🏽‍♀">cop | medium skin tone | officer | police | woman</annotation>
+		<annotation cp="👮🏽‍♀" type="tts">woman police officer: medium skin tone</annotation>
+		<annotation cp="👮🏾‍♀">cop | medium-dark skin tone | officer | police | woman</annotation>
+		<annotation cp="👮🏾‍♀" type="tts">woman police officer: medium-dark skin tone</annotation>
+		<annotation cp="👮🏿‍♀">cop | dark skin tone | officer | police | woman</annotation>
+		<annotation cp="👮🏿‍♀" type="tts">woman police officer: dark skin tone</annotation>
+		<annotation cp="🕵🏻">detective | light skin tone | sleuth | spy</annotation>
+		<annotation cp="🕵🏻" type="tts">detective: light skin tone</annotation>
+		<annotation cp="🕵🏼">detective | medium-light skin tone | sleuth | spy</annotation>
+		<annotation cp="🕵🏼" type="tts">detective: medium-light skin tone</annotation>
+		<annotation cp="🕵🏽">detective | medium skin tone | sleuth | spy</annotation>
+		<annotation cp="🕵🏽" type="tts">detective: medium skin tone</annotation>
+		<annotation cp="🕵🏾">detective | medium-dark skin tone | sleuth | spy</annotation>
+		<annotation cp="🕵🏾" type="tts">detective: medium-dark skin tone</annotation>
+		<annotation cp="🕵🏿">dark skin tone | detective | sleuth | spy</annotation>
+		<annotation cp="🕵🏿" type="tts">detective: dark skin tone</annotation>
+		<annotation cp="🕵🏻‍♂">detective | light skin tone | man | sleuth | spy</annotation>
+		<annotation cp="🕵🏻‍♂" type="tts">man detective: light skin tone</annotation>
+		<annotation cp="🕵🏼‍♂">detective | man | medium-light skin tone | sleuth | spy</annotation>
+		<annotation cp="🕵🏼‍♂" type="tts">man detective: medium-light skin tone</annotation>
+		<annotation cp="🕵🏽‍♂">detective | man | medium skin tone | sleuth | spy</annotation>
+		<annotation cp="🕵🏽‍♂" type="tts">man detective: medium skin tone</annotation>
+		<annotation cp="🕵🏾‍♂">detective | man | medium-dark skin tone | sleuth | spy</annotation>
+		<annotation cp="🕵🏾‍♂" type="tts">man detective: medium-dark skin tone</annotation>
+		<annotation cp="🕵🏿‍♂">dark skin tone | detective | man | sleuth | spy</annotation>
+		<annotation cp="🕵🏿‍♂" type="tts">man detective: dark skin tone</annotation>
+		<annotation cp="🕵🏻‍♀">detective | light skin tone | sleuth | spy | woman</annotation>
+		<annotation cp="🕵🏻‍♀" type="tts">woman detective: light skin tone</annotation>
+		<annotation cp="🕵🏼‍♀">detective | medium-light skin tone | sleuth | spy | woman</annotation>
+		<annotation cp="🕵🏼‍♀" type="tts">woman detective: medium-light skin tone</annotation>
+		<annotation cp="🕵🏽‍♀">detective | medium skin tone | sleuth | spy | woman</annotation>
+		<annotation cp="🕵🏽‍♀" type="tts">woman detective: medium skin tone</annotation>
+		<annotation cp="🕵🏾‍♀">detective | medium-dark skin tone | sleuth | spy | woman</annotation>
+		<annotation cp="🕵🏾‍♀" type="tts">woman detective: medium-dark skin tone</annotation>
+		<annotation cp="🕵🏿‍♀">dark skin tone | detective | sleuth | spy | woman</annotation>
+		<annotation cp="🕵🏿‍♀" type="tts">woman detective: dark skin tone</annotation>
+		<annotation cp="💂🏻">guard | light skin tone</annotation>
+		<annotation cp="💂🏻" type="tts">guard: light skin tone</annotation>
+		<annotation cp="💂🏼">guard | medium-light skin tone</annotation>
+		<annotation cp="💂🏼" type="tts">guard: medium-light skin tone</annotation>
+		<annotation cp="💂🏽">guard | medium skin tone</annotation>
+		<annotation cp="💂🏽" type="tts">guard: medium skin tone</annotation>
+		<annotation cp="💂🏾">guard | medium-dark skin tone</annotation>
+		<annotation cp="💂🏾" type="tts">guard: medium-dark skin tone</annotation>
+		<annotation cp="💂🏿">dark skin tone | guard</annotation>
+		<annotation cp="💂🏿" type="tts">guard: dark skin tone</annotation>
+		<annotation cp="💂🏻‍♂">guard | light skin tone | man</annotation>
+		<annotation cp="💂🏻‍♂" type="tts">man guard: light skin tone</annotation>
+		<annotation cp="💂🏼‍♂">guard | man | medium-light skin tone</annotation>
+		<annotation cp="💂🏼‍♂" type="tts">man guard: medium-light skin tone</annotation>
+		<annotation cp="💂🏽‍♂">guard | man | medium skin tone</annotation>
+		<annotation cp="💂🏽‍♂" type="tts">man guard: medium skin tone</annotation>
+		<annotation cp="💂🏾‍♂">guard | man | medium-dark skin tone</annotation>
+		<annotation cp="💂🏾‍♂" type="tts">man guard: medium-dark skin tone</annotation>
+		<annotation cp="💂🏿‍♂">dark skin tone | guard | man</annotation>
+		<annotation cp="💂🏿‍♂" type="tts">man guard: dark skin tone</annotation>
+		<annotation cp="💂🏻‍♀">guard | light skin tone | woman</annotation>
+		<annotation cp="💂🏻‍♀" type="tts">woman guard: light skin tone</annotation>
+		<annotation cp="💂🏼‍♀">guard | medium-light skin tone | woman</annotation>
+		<annotation cp="💂🏼‍♀" type="tts">woman guard: medium-light skin tone</annotation>
+		<annotation cp="💂🏽‍♀">guard | medium skin tone | woman</annotation>
+		<annotation cp="💂🏽‍♀" type="tts">woman guard: medium skin tone</annotation>
+		<annotation cp="💂🏾‍♀">guard | medium-dark skin tone | woman</annotation>
+		<annotation cp="💂🏾‍♀" type="tts">woman guard: medium-dark skin tone</annotation>
+		<annotation cp="💂🏿‍♀">dark skin tone | guard | woman</annotation>
+		<annotation cp="💂🏿‍♀" type="tts">woman guard: dark skin tone</annotation>
+		<annotation cp="🥷🏻">fighter | hidden | light skin tone | ninja | stealth</annotation>
+		<annotation cp="🥷🏻" type="tts">ninja: light skin tone</annotation>
+		<annotation cp="🥷🏼">fighter | hidden | medium-light skin tone | ninja | stealth</annotation>
+		<annotation cp="🥷🏼" type="tts">ninja: medium-light skin tone</annotation>
+		<annotation cp="🥷🏽">fighter | hidden | medium skin tone | ninja | stealth</annotation>
+		<annotation cp="🥷🏽" type="tts">ninja: medium skin tone</annotation>
+		<annotation cp="🥷🏾">fighter | hidden | medium-dark skin tone | ninja | stealth</annotation>
+		<annotation cp="🥷🏾" type="tts">ninja: medium-dark skin tone</annotation>
+		<annotation cp="🥷🏿">dark skin tone | fighter | hidden | ninja | stealth</annotation>
+		<annotation cp="🥷🏿" type="tts">ninja: dark skin tone</annotation>
+		<annotation cp="👷🏻">construction | hat | light skin tone | worker</annotation>
+		<annotation cp="👷🏻" type="tts">construction worker: light skin tone</annotation>
+		<annotation cp="👷🏼">construction | hat | medium-light skin tone | worker</annotation>
+		<annotation cp="👷🏼" type="tts">construction worker: medium-light skin tone</annotation>
+		<annotation cp="👷🏽">construction | hat | medium skin tone | worker</annotation>
+		<annotation cp="👷🏽" type="tts">construction worker: medium skin tone</annotation>
+		<annotation cp="👷🏾">construction | hat | medium-dark skin tone | worker</annotation>
+		<annotation cp="👷🏾" type="tts">construction worker: medium-dark skin tone</annotation>
+		<annotation cp="👷🏿">construction | dark skin tone | hat | worker</annotation>
+		<annotation cp="👷🏿" type="tts">construction worker: dark skin tone</annotation>
+		<annotation cp="👷🏻‍♂">construction | light skin tone | man | worker</annotation>
+		<annotation cp="👷🏻‍♂" type="tts">man construction worker: light skin tone</annotation>
+		<annotation cp="👷🏼‍♂">construction | man | medium-light skin tone | worker</annotation>
+		<annotation cp="👷🏼‍♂" type="tts">man construction worker: medium-light skin tone</annotation>
+		<annotation cp="👷🏽‍♂">construction | man | medium skin tone | worker</annotation>
+		<annotation cp="👷🏽‍♂" type="tts">man construction worker: medium skin tone</annotation>
+		<annotation cp="👷🏾‍♂">construction | man | medium-dark skin tone | worker</annotation>
+		<annotation cp="👷🏾‍♂" type="tts">man construction worker: medium-dark skin tone</annotation>
+		<annotation cp="👷🏿‍♂">construction | dark skin tone | man | worker</annotation>
+		<annotation cp="👷🏿‍♂" type="tts">man construction worker: dark skin tone</annotation>
+		<annotation cp="👷🏻‍♀">construction | light skin tone | woman | worker</annotation>
+		<annotation cp="👷🏻‍♀" type="tts">woman construction worker: light skin tone</annotation>
+		<annotation cp="👷🏼‍♀">construction | medium-light skin tone | woman | worker</annotation>
+		<annotation cp="👷🏼‍♀" type="tts">woman construction worker: medium-light skin tone</annotation>
+		<annotation cp="👷🏽‍♀">construction | medium skin tone | woman | worker</annotation>
+		<annotation cp="👷🏽‍♀" type="tts">woman construction worker: medium skin tone</annotation>
+		<annotation cp="👷🏾‍♀">construction | medium-dark skin tone | woman | worker</annotation>
+		<annotation cp="👷🏾‍♀" type="tts">woman construction worker: medium-dark skin tone</annotation>
+		<annotation cp="👷🏿‍♀">construction | dark skin tone | woman | worker</annotation>
+		<annotation cp="👷🏿‍♀" type="tts">woman construction worker: dark skin tone</annotation>
+		<annotation cp="🤴🏻">light skin tone | prince</annotation>
+		<annotation cp="🤴🏻" type="tts">prince: light skin tone</annotation>
+		<annotation cp="🤴🏼">medium-light skin tone | prince</annotation>
+		<annotation cp="🤴🏼" type="tts">prince: medium-light skin tone</annotation>
+		<annotation cp="🤴🏽">medium skin tone | prince</annotation>
+		<annotation cp="🤴🏽" type="tts">prince: medium skin tone</annotation>
+		<annotation cp="🤴🏾">medium-dark skin tone | prince</annotation>
+		<annotation cp="🤴🏾" type="tts">prince: medium-dark skin tone</annotation>
+		<annotation cp="🤴🏿">dark skin tone | prince</annotation>
+		<annotation cp="🤴🏿" type="tts">prince: dark skin tone</annotation>
+		<annotation cp="👸🏻">fairy tale | fantasy | light skin tone | princess</annotation>
+		<annotation cp="👸🏻" type="tts">princess: light skin tone</annotation>
+		<annotation cp="👸🏼">fairy tale | fantasy | medium-light skin tone | princess</annotation>
+		<annotation cp="👸🏼" type="tts">princess: medium-light skin tone</annotation>
+		<annotation cp="👸🏽">fairy tale | fantasy | medium skin tone | princess</annotation>
+		<annotation cp="👸🏽" type="tts">princess: medium skin tone</annotation>
+		<annotation cp="👸🏾">fairy tale | fantasy | medium-dark skin tone | princess</annotation>
+		<annotation cp="👸🏾" type="tts">princess: medium-dark skin tone</annotation>
+		<annotation cp="👸🏿">dark skin tone | fairy tale | fantasy | princess</annotation>
+		<annotation cp="👸🏿" type="tts">princess: dark skin tone</annotation>
+		<annotation cp="👳🏻">light skin tone | person wearing turban | turban</annotation>
+		<annotation cp="👳🏻" type="tts">person wearing turban: light skin tone</annotation>
+		<annotation cp="👳🏼">medium-light skin tone | person wearing turban | turban</annotation>
+		<annotation cp="👳🏼" type="tts">person wearing turban: medium-light skin tone</annotation>
+		<annotation cp="👳🏽">medium skin tone | person wearing turban | turban</annotation>
+		<annotation cp="👳🏽" type="tts">person wearing turban: medium skin tone</annotation>
+		<annotation cp="👳🏾">medium-dark skin tone | person wearing turban | turban</annotation>
+		<annotation cp="👳🏾" type="tts">person wearing turban: medium-dark skin tone</annotation>
+		<annotation cp="👳🏿">dark skin tone | person wearing turban | turban</annotation>
+		<annotation cp="👳🏿" type="tts">person wearing turban: dark skin tone</annotation>
+		<annotation cp="👳🏻‍♂">light skin tone | man | man wearing turban | turban</annotation>
+		<annotation cp="👳🏻‍♂" type="tts">man wearing turban: light skin tone</annotation>
+		<annotation cp="👳🏼‍♂">man | man wearing turban | medium-light skin tone | turban</annotation>
+		<annotation cp="👳🏼‍♂" type="tts">man wearing turban: medium-light skin tone</annotation>
+		<annotation cp="👳🏽‍♂">man | man wearing turban | medium skin tone | turban</annotation>
+		<annotation cp="👳🏽‍♂" type="tts">man wearing turban: medium skin tone</annotation>
+		<annotation cp="👳🏾‍♂">man | man wearing turban | medium-dark skin tone | turban</annotation>
+		<annotation cp="👳🏾‍♂" type="tts">man wearing turban: medium-dark skin tone</annotation>
+		<annotation cp="👳🏿‍♂">dark skin tone | man | man wearing turban | turban</annotation>
+		<annotation cp="👳🏿‍♂" type="tts">man wearing turban: dark skin tone</annotation>
+		<annotation cp="👳🏻‍♀">light skin tone | turban | woman | woman wearing turban</annotation>
+		<annotation cp="👳🏻‍♀" type="tts">woman wearing turban: light skin tone</annotation>
+		<annotation cp="👳🏼‍♀">medium-light skin tone | turban | woman | woman wearing turban</annotation>
+		<annotation cp="👳🏼‍♀" type="tts">woman wearing turban: medium-light skin tone</annotation>
+		<annotation cp="👳🏽‍♀">medium skin tone | turban | woman | woman wearing turban</annotation>
+		<annotation cp="👳🏽‍♀" type="tts">woman wearing turban: medium skin tone</annotation>
+		<annotation cp="👳🏾‍♀">medium-dark skin tone | turban | woman | woman wearing turban</annotation>
+		<annotation cp="👳🏾‍♀" type="tts">woman wearing turban: medium-dark skin tone</annotation>
+		<annotation cp="👳🏿‍♀">dark skin tone | turban | woman | woman wearing turban</annotation>
+		<annotation cp="👳🏿‍♀" type="tts">woman wearing turban: dark skin tone</annotation>
+		<annotation cp="👲🏻">cap | gua pi mao | hat | light skin tone | person | person with skullcap | skullcap</annotation>
+		<annotation cp="👲🏻" type="tts">person with skullcap: light skin tone</annotation>
+		<annotation cp="👲🏼">cap | gua pi mao | hat | medium-light skin tone | person | person with skullcap | skullcap</annotation>
+		<annotation cp="👲🏼" type="tts">person with skullcap: medium-light skin tone</annotation>
+		<annotation cp="👲🏽">cap | gua pi mao | hat | medium skin tone | person | person with skullcap | skullcap</annotation>
+		<annotation cp="👲🏽" type="tts">person with skullcap: medium skin tone</annotation>
+		<annotation cp="👲🏾">cap | gua pi mao | hat | medium-dark skin tone | person | person with skullcap | skullcap</annotation>
+		<annotation cp="👲🏾" type="tts">person with skullcap: medium-dark skin tone</annotation>
+		<annotation cp="👲🏿">cap | dark skin tone | gua pi mao | hat | person | person with skullcap | skullcap</annotation>
+		<annotation cp="👲🏿" type="tts">person with skullcap: dark skin tone</annotation>
+		<annotation cp="🧕🏻">headscarf | hijab | light skin tone | mantilla | tichel | woman with headscarf</annotation>
+		<annotation cp="🧕🏻" type="tts">woman with headscarf: light skin tone</annotation>
+		<annotation cp="🧕🏼">headscarf | hijab | mantilla | medium-light skin tone | tichel | woman with headscarf</annotation>
+		<annotation cp="🧕🏼" type="tts">woman with headscarf: medium-light skin tone</annotation>
+		<annotation cp="🧕🏽">headscarf | hijab | mantilla | medium skin tone | tichel | woman with headscarf</annotation>
+		<annotation cp="🧕🏽" type="tts">woman with headscarf: medium skin tone</annotation>
+		<annotation cp="🧕🏾">headscarf | hijab | mantilla | medium-dark skin tone | tichel | woman with headscarf</annotation>
+		<annotation cp="🧕🏾" type="tts">woman with headscarf: medium-dark skin tone</annotation>
+		<annotation cp="🧕🏿">dark skin tone | headscarf | hijab | mantilla | tichel | woman with headscarf</annotation>
+		<annotation cp="🧕🏿" type="tts">woman with headscarf: dark skin tone</annotation>
+		<annotation cp="🤵🏻">groom | light skin tone | person | person in tuxedo | tuxedo</annotation>
+		<annotation cp="🤵🏻" type="tts">person in tuxedo: light skin tone</annotation>
+		<annotation cp="🤵🏼">groom | medium-light skin tone | person | person in tuxedo | tuxedo</annotation>
+		<annotation cp="🤵🏼" type="tts">person in tuxedo: medium-light skin tone</annotation>
+		<annotation cp="🤵🏽">groom | medium skin tone | person | person in tuxedo | tuxedo</annotation>
+		<annotation cp="🤵🏽" type="tts">person in tuxedo: medium skin tone</annotation>
+		<annotation cp="🤵🏾">groom | medium-dark skin tone | person | person in tuxedo | tuxedo</annotation>
+		<annotation cp="🤵🏾" type="tts">person in tuxedo: medium-dark skin tone</annotation>
+		<annotation cp="🤵🏿">dark skin tone | groom | person | person in tuxedo | tuxedo</annotation>
+		<annotation cp="🤵🏿" type="tts">person in tuxedo: dark skin tone</annotation>
+		<annotation cp="🤵🏻‍♂">light skin tone | man | man in tuxedo | tuxedo</annotation>
+		<annotation cp="🤵🏻‍♂" type="tts">man in tuxedo: light skin tone</annotation>
+		<annotation cp="🤵🏼‍♂">man | man in tuxedo | medium-light skin tone | tuxedo</annotation>
+		<annotation cp="🤵🏼‍♂" type="tts">man in tuxedo: medium-light skin tone</annotation>
+		<annotation cp="🤵🏽‍♂">man | man in tuxedo | medium skin tone | tuxedo</annotation>
+		<annotation cp="🤵🏽‍♂" type="tts">man in tuxedo: medium skin tone</annotation>
+		<annotation cp="🤵🏾‍♂">man | man in tuxedo | medium-dark skin tone | tuxedo</annotation>
+		<annotation cp="🤵🏾‍♂" type="tts">man in tuxedo: medium-dark skin tone</annotation>
+		<annotation cp="🤵🏿‍♂">dark skin tone | man | man in tuxedo | tuxedo</annotation>
+		<annotation cp="🤵🏿‍♂" type="tts">man in tuxedo: dark skin tone</annotation>
+		<annotation cp="🤵🏻‍♀">light skin tone | tuxedo | woman | woman in tuxedo</annotation>
+		<annotation cp="🤵🏻‍♀" type="tts">woman in tuxedo: light skin tone</annotation>
+		<annotation cp="🤵🏼‍♀">medium-light skin tone | tuxedo | woman | woman in tuxedo</annotation>
+		<annotation cp="🤵🏼‍♀" type="tts">woman in tuxedo: medium-light skin tone</annotation>
+		<annotation cp="🤵🏽‍♀">medium skin tone | tuxedo | woman | woman in tuxedo</annotation>
+		<annotation cp="🤵🏽‍♀" type="tts">woman in tuxedo: medium skin tone</annotation>
+		<annotation cp="🤵🏾‍♀">medium-dark skin tone | tuxedo | woman | woman in tuxedo</annotation>
+		<annotation cp="🤵🏾‍♀" type="tts">woman in tuxedo: medium-dark skin tone</annotation>
+		<annotation cp="🤵🏿‍♀">dark skin tone | tuxedo | woman | woman in tuxedo</annotation>
+		<annotation cp="🤵🏿‍♀" type="tts">woman in tuxedo: dark skin tone</annotation>
+		<annotation cp="👰🏻">bride | light skin tone | person | person with veil | veil | wedding</annotation>
+		<annotation cp="👰🏻" type="tts">person with veil: light skin tone</annotation>
+		<annotation cp="👰🏼">bride | medium-light skin tone | person | person with veil | veil | wedding</annotation>
+		<annotation cp="👰🏼" type="tts">person with veil: medium-light skin tone</annotation>
+		<annotation cp="👰🏽">bride | medium skin tone | person | person with veil | veil | wedding</annotation>
+		<annotation cp="👰🏽" type="tts">person with veil: medium skin tone</annotation>
+		<annotation cp="👰🏾">bride | medium-dark skin tone | person | person with veil | veil | wedding</annotation>
+		<annotation cp="👰🏾" type="tts">person with veil: medium-dark skin tone</annotation>
+		<annotation cp="👰🏿">bride | dark skin tone | person | person with veil | veil | wedding</annotation>
+		<annotation cp="👰🏿" type="tts">person with veil: dark skin tone</annotation>
+		<annotation cp="👰🏻‍♂">light skin tone | man | man with veil | veil</annotation>
+		<annotation cp="👰🏻‍♂" type="tts">man with veil: light skin tone</annotation>
+		<annotation cp="👰🏼‍♂">man | man with veil | medium-light skin tone | veil</annotation>
+		<annotation cp="👰🏼‍♂" type="tts">man with veil: medium-light skin tone</annotation>
+		<annotation cp="👰🏽‍♂">man | man with veil | medium skin tone | veil</annotation>
+		<annotation cp="👰🏽‍♂" type="tts">man with veil: medium skin tone</annotation>
+		<annotation cp="👰🏾‍♂">man | man with veil | medium-dark skin tone | veil</annotation>
+		<annotation cp="👰🏾‍♂" type="tts">man with veil: medium-dark skin tone</annotation>
+		<annotation cp="👰🏿‍♂">dark skin tone | man | man with veil | veil</annotation>
+		<annotation cp="👰🏿‍♂" type="tts">man with veil: dark skin tone</annotation>
+		<annotation cp="👰🏻‍♀">light skin tone | veil | woman | woman with veil</annotation>
+		<annotation cp="👰🏻‍♀" type="tts">woman with veil: light skin tone</annotation>
+		<annotation cp="👰🏼‍♀">medium-light skin tone | veil | woman | woman with veil</annotation>
+		<annotation cp="👰🏼‍♀" type="tts">woman with veil: medium-light skin tone</annotation>
+		<annotation cp="👰🏽‍♀">medium skin tone | veil | woman | woman with veil</annotation>
+		<annotation cp="👰🏽‍♀" type="tts">woman with veil: medium skin tone</annotation>
+		<annotation cp="👰🏾‍♀">medium-dark skin tone | veil | woman | woman with veil</annotation>
+		<annotation cp="👰🏾‍♀" type="tts">woman with veil: medium-dark skin tone</annotation>
+		<annotation cp="👰🏿‍♀">dark skin tone | veil | woman | woman with veil</annotation>
+		<annotation cp="👰🏿‍♀" type="tts">woman with veil: dark skin tone</annotation>
+		<annotation cp="🤰🏻">light skin tone | pregnant | woman</annotation>
+		<annotation cp="🤰🏻" type="tts">pregnant woman: light skin tone</annotation>
+		<annotation cp="🤰🏼">medium-light skin tone | pregnant | woman</annotation>
+		<annotation cp="🤰🏼" type="tts">pregnant woman: medium-light skin tone</annotation>
+		<annotation cp="🤰🏽">medium skin tone | pregnant | woman</annotation>
+		<annotation cp="🤰🏽" type="tts">pregnant woman: medium skin tone</annotation>
+		<annotation cp="🤰🏾">medium-dark skin tone | pregnant | woman</annotation>
+		<annotation cp="🤰🏾" type="tts">pregnant woman: medium-dark skin tone</annotation>
+		<annotation cp="🤰🏿">dark skin tone | pregnant | woman</annotation>
+		<annotation cp="🤰🏿" type="tts">pregnant woman: dark skin tone</annotation>
+		<annotation cp="🤱🏻">baby | breast | breast-feeding | light skin tone | nursing</annotation>
+		<annotation cp="🤱🏻" type="tts">breast-feeding: light skin tone</annotation>
+		<annotation cp="🤱🏼">baby | breast | breast-feeding | medium-light skin tone | nursing</annotation>
+		<annotation cp="🤱🏼" type="tts">breast-feeding: medium-light skin tone</annotation>
+		<annotation cp="🤱🏽">baby | breast | breast-feeding | medium skin tone | nursing</annotation>
+		<annotation cp="🤱🏽" type="tts">breast-feeding: medium skin tone</annotation>
+		<annotation cp="🤱🏾">baby | breast | breast-feeding | medium-dark skin tone | nursing</annotation>
+		<annotation cp="🤱🏾" type="tts">breast-feeding: medium-dark skin tone</annotation>
+		<annotation cp="🤱🏿">baby | breast | breast-feeding | dark skin tone | nursing</annotation>
+		<annotation cp="🤱🏿" type="tts">breast-feeding: dark skin tone</annotation>
+		<annotation cp="👩🏻‍🍼">baby | feeding | light skin tone | nursing | woman</annotation>
+		<annotation cp="👩🏻‍🍼" type="tts">woman feeding baby: light skin tone</annotation>
+		<annotation cp="👩🏼‍🍼">baby | feeding | medium-light skin tone | nursing | woman</annotation>
+		<annotation cp="👩🏼‍🍼" type="tts">woman feeding baby: medium-light skin tone</annotation>
+		<annotation cp="👩🏽‍🍼">baby | feeding | medium skin tone | nursing | woman</annotation>
+		<annotation cp="👩🏽‍🍼" type="tts">woman feeding baby: medium skin tone</annotation>
+		<annotation cp="👩🏾‍🍼">baby | feeding | medium-dark skin tone | nursing | woman</annotation>
+		<annotation cp="👩🏾‍🍼" type="tts">woman feeding baby: medium-dark skin tone</annotation>
+		<annotation cp="👩🏿‍🍼">baby | dark skin tone | feeding | nursing | woman</annotation>
+		<annotation cp="👩🏿‍🍼" type="tts">woman feeding baby: dark skin tone</annotation>
+		<annotation cp="👨🏻‍🍼">baby | feeding | light skin tone | man | nursing</annotation>
+		<annotation cp="👨🏻‍🍼" type="tts">man feeding baby: light skin tone</annotation>
+		<annotation cp="👨🏼‍🍼">baby | feeding | man | medium-light skin tone | nursing</annotation>
+		<annotation cp="👨🏼‍🍼" type="tts">man feeding baby: medium-light skin tone</annotation>
+		<annotation cp="👨🏽‍🍼">baby | feeding | man | medium skin tone | nursing</annotation>
+		<annotation cp="👨🏽‍🍼" type="tts">man feeding baby: medium skin tone</annotation>
+		<annotation cp="👨🏾‍🍼">baby | feeding | man | medium-dark skin tone | nursing</annotation>
+		<annotation cp="👨🏾‍🍼" type="tts">man feeding baby: medium-dark skin tone</annotation>
+		<annotation cp="👨🏿‍🍼">baby | dark skin tone | feeding | man | nursing</annotation>
+		<annotation cp="👨🏿‍🍼" type="tts">man feeding baby: dark skin tone</annotation>
+		<annotation cp="🧑🏻‍🍼">baby | feeding | light skin tone | nursing | person</annotation>
+		<annotation cp="🧑🏻‍🍼" type="tts">person feeding baby: light skin tone</annotation>
+		<annotation cp="🧑🏼‍🍼">baby | feeding | medium-light skin tone | nursing | person</annotation>
+		<annotation cp="🧑🏼‍🍼" type="tts">person feeding baby: medium-light skin tone</annotation>
+		<annotation cp="🧑🏽‍🍼">baby | feeding | medium skin tone | nursing | person</annotation>
+		<annotation cp="🧑🏽‍🍼" type="tts">person feeding baby: medium skin tone</annotation>
+		<annotation cp="🧑🏾‍🍼">baby | feeding | medium-dark skin tone | nursing | person</annotation>
+		<annotation cp="🧑🏾‍🍼" type="tts">person feeding baby: medium-dark skin tone</annotation>
+		<annotation cp="🧑🏿‍🍼">baby | dark skin tone | feeding | nursing | person</annotation>
+		<annotation cp="🧑🏿‍🍼" type="tts">person feeding baby: dark skin tone</annotation>
+		<annotation cp="👼🏻">angel | baby | face | fairy tale | fantasy | light skin tone</annotation>
+		<annotation cp="👼🏻" type="tts">baby angel: light skin tone</annotation>
+		<annotation cp="👼🏼">angel | baby | face | fairy tale | fantasy | medium-light skin tone</annotation>
+		<annotation cp="👼🏼" type="tts">baby angel: medium-light skin tone</annotation>
+		<annotation cp="👼🏽">angel | baby | face | fairy tale | fantasy | medium skin tone</annotation>
+		<annotation cp="👼🏽" type="tts">baby angel: medium skin tone</annotation>
+		<annotation cp="👼🏾">angel | baby | face | fairy tale | fantasy | medium-dark skin tone</annotation>
+		<annotation cp="👼🏾" type="tts">baby angel: medium-dark skin tone</annotation>
+		<annotation cp="👼🏿">angel | baby | dark skin tone | face | fairy tale | fantasy</annotation>
+		<annotation cp="👼🏿" type="tts">baby angel: dark skin tone</annotation>
+		<annotation cp="🎅🏻">celebration | Christmas | claus | father | light skin tone | santa | Santa Claus</annotation>
+		<annotation cp="🎅🏻" type="tts">Santa Claus: light skin tone</annotation>
+		<annotation cp="🎅🏼">celebration | Christmas | claus | father | medium-light skin tone | santa | Santa Claus</annotation>
+		<annotation cp="🎅🏼" type="tts">Santa Claus: medium-light skin tone</annotation>
+		<annotation cp="🎅🏽">celebration | Christmas | claus | father | medium skin tone | santa | Santa Claus</annotation>
+		<annotation cp="🎅🏽" type="tts">Santa Claus: medium skin tone</annotation>
+		<annotation cp="🎅🏾">celebration | Christmas | claus | father | medium-dark skin tone | santa | Santa Claus</annotation>
+		<annotation cp="🎅🏾" type="tts">Santa Claus: medium-dark skin tone</annotation>
+		<annotation cp="🎅🏿">celebration | Christmas | claus | dark skin tone | father | santa | Santa Claus</annotation>
+		<annotation cp="🎅🏿" type="tts">Santa Claus: dark skin tone</annotation>
+		<annotation cp="🤶🏻">celebration | Christmas | claus | light skin tone | mother | Mrs. | Mrs. Claus</annotation>
+		<annotation cp="🤶🏻" type="tts">Mrs. Claus: light skin tone</annotation>
+		<annotation cp="🤶🏼">celebration | Christmas | claus | medium-light skin tone | mother | Mrs. | Mrs. Claus</annotation>
+		<annotation cp="🤶🏼" type="tts">Mrs. Claus: medium-light skin tone</annotation>
+		<annotation cp="🤶🏽">celebration | Christmas | claus | medium skin tone | mother | Mrs. | Mrs. Claus</annotation>
+		<annotation cp="🤶🏽" type="tts">Mrs. Claus: medium skin tone</annotation>
+		<annotation cp="🤶🏾">celebration | Christmas | claus | medium-dark skin tone | mother | Mrs. | Mrs. Claus</annotation>
+		<annotation cp="🤶🏾" type="tts">Mrs. Claus: medium-dark skin tone</annotation>
+		<annotation cp="🤶🏿">celebration | Christmas | claus | dark skin tone | mother | Mrs. | Mrs. Claus</annotation>
+		<annotation cp="🤶🏿" type="tts">Mrs. Claus: dark skin tone</annotation>
+		<annotation cp="🧑🏻‍🎄">Claus, christmas | light skin tone | mx claus</annotation>
+		<annotation cp="🧑🏻‍🎄" type="tts">mx claus: light skin tone</annotation>
+		<annotation cp="🧑🏼‍🎄">Claus, christmas | medium-light skin tone | mx claus</annotation>
+		<annotation cp="🧑🏼‍🎄" type="tts">mx claus: medium-light skin tone</annotation>
+		<annotation cp="🧑🏽‍🎄">Claus, christmas | medium skin tone | mx claus</annotation>
+		<annotation cp="🧑🏽‍🎄" type="tts">mx claus: medium skin tone</annotation>
+		<annotation cp="🧑🏾‍🎄">Claus, christmas | medium-dark skin tone | mx claus</annotation>
+		<annotation cp="🧑🏾‍🎄" type="tts">mx claus: medium-dark skin tone</annotation>
+		<annotation cp="🧑🏿‍🎄">Claus, christmas | dark skin tone | mx claus</annotation>
+		<annotation cp="🧑🏿‍🎄" type="tts">mx claus: dark skin tone</annotation>
+		<annotation cp="🦸🏻">good | hero | heroine | light skin tone | superhero | superpower</annotation>
+		<annotation cp="🦸🏻" type="tts">superhero: light skin tone</annotation>
+		<annotation cp="🦸🏼">good | hero | heroine | medium-light skin tone | superhero | superpower</annotation>
+		<annotation cp="🦸🏼" type="tts">superhero: medium-light skin tone</annotation>
+		<annotation cp="🦸🏽">good | hero | heroine | medium skin tone | superhero | superpower</annotation>
+		<annotation cp="🦸🏽" type="tts">superhero: medium skin tone</annotation>
+		<annotation cp="🦸🏾">good | hero | heroine | medium-dark skin tone | superhero | superpower</annotation>
+		<annotation cp="🦸🏾" type="tts">superhero: medium-dark skin tone</annotation>
+		<annotation cp="🦸🏿">dark skin tone | good | hero | heroine | superhero | superpower</annotation>
+		<annotation cp="🦸🏿" type="tts">superhero: dark skin tone</annotation>
+		<annotation cp="🦸🏻‍♂">good | hero | light skin tone | man | man superhero | superpower</annotation>
+		<annotation cp="🦸🏻‍♂" type="tts">man superhero: light skin tone</annotation>
+		<annotation cp="🦸🏼‍♂">good | hero | man | man superhero | medium-light skin tone | superpower</annotation>
+		<annotation cp="🦸🏼‍♂" type="tts">man superhero: medium-light skin tone</annotation>
+		<annotation cp="🦸🏽‍♂">good | hero | man | man superhero | medium skin tone | superpower</annotation>
+		<annotation cp="🦸🏽‍♂" type="tts">man superhero: medium skin tone</annotation>
+		<annotation cp="🦸🏾‍♂">good | hero | man | man superhero | medium-dark skin tone | superpower</annotation>
+		<annotation cp="🦸🏾‍♂" type="tts">man superhero: medium-dark skin tone</annotation>
+		<annotation cp="🦸🏿‍♂">dark skin tone | good | hero | man | man superhero | superpower</annotation>
+		<annotation cp="🦸🏿‍♂" type="tts">man superhero: dark skin tone</annotation>
+		<annotation cp="🦸🏻‍♀">good | hero | heroine | light skin tone | superpower | woman | woman superhero</annotation>
+		<annotation cp="🦸🏻‍♀" type="tts">woman superhero: light skin tone</annotation>
+		<annotation cp="🦸🏼‍♀">good | hero | heroine | medium-light skin tone | superpower | woman | woman superhero</annotation>
+		<annotation cp="🦸🏼‍♀" type="tts">woman superhero: medium-light skin tone</annotation>
+		<annotation cp="🦸🏽‍♀">good | hero | heroine | medium skin tone | superpower | woman | woman superhero</annotation>
+		<annotation cp="🦸🏽‍♀" type="tts">woman superhero: medium skin tone</annotation>
+		<annotation cp="🦸🏾‍♀">good | hero | heroine | medium-dark skin tone | superpower | woman | woman superhero</annotation>
+		<annotation cp="🦸🏾‍♀" type="tts">woman superhero: medium-dark skin tone</annotation>
+		<annotation cp="🦸🏿‍♀">dark skin tone | good | hero | heroine | superpower | woman | woman superhero</annotation>
+		<annotation cp="🦸🏿‍♀" type="tts">woman superhero: dark skin tone</annotation>
+		<annotation cp="🦹🏻">criminal | evil | light skin tone | superpower | supervillain | villain</annotation>
+		<annotation cp="🦹🏻" type="tts">supervillain: light skin tone</annotation>
+		<annotation cp="🦹🏼">criminal | evil | medium-light skin tone | superpower | supervillain | villain</annotation>
+		<annotation cp="🦹🏼" type="tts">supervillain: medium-light skin tone</annotation>
+		<annotation cp="🦹🏽">criminal | evil | medium skin tone | superpower | supervillain | villain</annotation>
+		<annotation cp="🦹🏽" type="tts">supervillain: medium skin tone</annotation>
+		<annotation cp="🦹🏾">criminal | evil | medium-dark skin tone | superpower | supervillain | villain</annotation>
+		<annotation cp="🦹🏾" type="tts">supervillain: medium-dark skin tone</annotation>
+		<annotation cp="🦹🏿">criminal | dark skin tone | evil | superpower | supervillain | villain</annotation>
+		<annotation cp="🦹🏿" type="tts">supervillain: dark skin tone</annotation>
+		<annotation cp="🦹🏻‍♂">criminal | evil | light skin tone | man | man supervillain | superpower | villain</annotation>
+		<annotation cp="🦹🏻‍♂" type="tts">man supervillain: light skin tone</annotation>
+		<annotation cp="🦹🏼‍♂">criminal | evil | man | man supervillain | medium-light skin tone | superpower | villain</annotation>
+		<annotation cp="🦹🏼‍♂" type="tts">man supervillain: medium-light skin tone</annotation>
+		<annotation cp="🦹🏽‍♂">criminal | evil | man | man supervillain | medium skin tone | superpower | villain</annotation>
+		<annotation cp="🦹🏽‍♂" type="tts">man supervillain: medium skin tone</annotation>
+		<annotation cp="🦹🏾‍♂">criminal | evil | man | man supervillain | medium-dark skin tone | superpower | villain</annotation>
+		<annotation cp="🦹🏾‍♂" type="tts">man supervillain: medium-dark skin tone</annotation>
+		<annotation cp="🦹🏿‍♂">criminal | dark skin tone | evil | man | man supervillain | superpower | villain</annotation>
+		<annotation cp="🦹🏿‍♂" type="tts">man supervillain: dark skin tone</annotation>
+		<annotation cp="🦹🏻‍♀">criminal | evil | light skin tone | superpower | villain | woman | woman supervillain</annotation>
+		<annotation cp="🦹🏻‍♀" type="tts">woman supervillain: light skin tone</annotation>
+		<annotation cp="🦹🏼‍♀">criminal | evil | medium-light skin tone | superpower | villain | woman | woman supervillain</annotation>
+		<annotation cp="🦹🏼‍♀" type="tts">woman supervillain: medium-light skin tone</annotation>
+		<annotation cp="🦹🏽‍♀">criminal | evil | medium skin tone | superpower | villain | woman | woman supervillain</annotation>
+		<annotation cp="🦹🏽‍♀" type="tts">woman supervillain: medium skin tone</annotation>
+		<annotation cp="🦹🏾‍♀">criminal | evil | medium-dark skin tone | superpower | villain | woman | woman supervillain</annotation>
+		<annotation cp="🦹🏾‍♀" type="tts">woman supervillain: medium-dark skin tone</annotation>
+		<annotation cp="🦹🏿‍♀">criminal | dark skin tone | evil | superpower | villain | woman | woman supervillain</annotation>
+		<annotation cp="🦹🏿‍♀" type="tts">woman supervillain: dark skin tone</annotation>
+		<annotation cp="🧙🏻">light skin tone | mage | sorcerer | sorceress | witch | wizard</annotation>
+		<annotation cp="🧙🏻" type="tts">mage: light skin tone</annotation>
+		<annotation cp="🧙🏼">mage | medium-light skin tone | sorcerer | sorceress | witch | wizard</annotation>
+		<annotation cp="🧙🏼" type="tts">mage: medium-light skin tone</annotation>
+		<annotation cp="🧙🏽">mage | medium skin tone | sorcerer | sorceress | witch | wizard</annotation>
+		<annotation cp="🧙🏽" type="tts">mage: medium skin tone</annotation>
+		<annotation cp="🧙🏾">mage | medium-dark skin tone | sorcerer | sorceress | witch | wizard</annotation>
+		<annotation cp="🧙🏾" type="tts">mage: medium-dark skin tone</annotation>
+		<annotation cp="🧙🏿">dark skin tone | mage | sorcerer | sorceress | witch | wizard</annotation>
+		<annotation cp="🧙🏿" type="tts">mage: dark skin tone</annotation>
+		<annotation cp="🧙🏻‍♂">light skin tone | man mage | sorcerer | wizard</annotation>
+		<annotation cp="🧙🏻‍♂" type="tts">man mage: light skin tone</annotation>
+		<annotation cp="🧙🏼‍♂">man mage | medium-light skin tone | sorcerer | wizard</annotation>
+		<annotation cp="🧙🏼‍♂" type="tts">man mage: medium-light skin tone</annotation>
+		<annotation cp="🧙🏽‍♂">man mage | medium skin tone | sorcerer | wizard</annotation>
+		<annotation cp="🧙🏽‍♂" type="tts">man mage: medium skin tone</annotation>
+		<annotation cp="🧙🏾‍♂">man mage | medium-dark skin tone | sorcerer | wizard</annotation>
+		<annotation cp="🧙🏾‍♂" type="tts">man mage: medium-dark skin tone</annotation>
+		<annotation cp="🧙🏿‍♂">dark skin tone | man mage | sorcerer | wizard</annotation>
+		<annotation cp="🧙🏿‍♂" type="tts">man mage: dark skin tone</annotation>
+		<annotation cp="🧙🏻‍♀">light skin tone | sorceress | witch | woman mage</annotation>
+		<annotation cp="🧙🏻‍♀" type="tts">woman mage: light skin tone</annotation>
+		<annotation cp="🧙🏼‍♀">medium-light skin tone | sorceress | witch | woman mage</annotation>
+		<annotation cp="🧙🏼‍♀" type="tts">woman mage: medium-light skin tone</annotation>
+		<annotation cp="🧙🏽‍♀">medium skin tone | sorceress | witch | woman mage</annotation>
+		<annotation cp="🧙🏽‍♀" type="tts">woman mage: medium skin tone</annotation>
+		<annotation cp="🧙🏾‍♀">medium-dark skin tone | sorceress | witch | woman mage</annotation>
+		<annotation cp="🧙🏾‍♀" type="tts">woman mage: medium-dark skin tone</annotation>
+		<annotation cp="🧙🏿‍♀">dark skin tone | sorceress | witch | woman mage</annotation>
+		<annotation cp="🧙🏿‍♀" type="tts">woman mage: dark skin tone</annotation>
+		<annotation cp="🧚🏻">fairy | light skin tone | Oberon | Puck | Titania</annotation>
+		<annotation cp="🧚🏻" type="tts">fairy: light skin tone</annotation>
+		<annotation cp="🧚🏼">fairy | medium-light skin tone | Oberon | Puck | Titania</annotation>
+		<annotation cp="🧚🏼" type="tts">fairy: medium-light skin tone</annotation>
+		<annotation cp="🧚🏽">fairy | medium skin tone | Oberon | Puck | Titania</annotation>
+		<annotation cp="🧚🏽" type="tts">fairy: medium skin tone</annotation>
+		<annotation cp="🧚🏾">fairy | medium-dark skin tone | Oberon | Puck | Titania</annotation>
+		<annotation cp="🧚🏾" type="tts">fairy: medium-dark skin tone</annotation>
+		<annotation cp="🧚🏿">dark skin tone | fairy | Oberon | Puck | Titania</annotation>
+		<annotation cp="🧚🏿" type="tts">fairy: dark skin tone</annotation>
+		<annotation cp="🧚🏻‍♂">light skin tone | man fairy | Oberon | Puck</annotation>
+		<annotation cp="🧚🏻‍♂" type="tts">man fairy: light skin tone</annotation>
+		<annotation cp="🧚🏼‍♂">man fairy | medium-light skin tone | Oberon | Puck</annotation>
+		<annotation cp="🧚🏼‍♂" type="tts">man fairy: medium-light skin tone</annotation>
+		<annotation cp="🧚🏽‍♂">man fairy | medium skin tone | Oberon | Puck</annotation>
+		<annotation cp="🧚🏽‍♂" type="tts">man fairy: medium skin tone</annotation>
+		<annotation cp="🧚🏾‍♂">man fairy | medium-dark skin tone | Oberon | Puck</annotation>
+		<annotation cp="🧚🏾‍♂" type="tts">man fairy: medium-dark skin tone</annotation>
+		<annotation cp="🧚🏿‍♂">dark skin tone | man fairy | Oberon | Puck</annotation>
+		<annotation cp="🧚🏿‍♂" type="tts">man fairy: dark skin tone</annotation>
+		<annotation cp="🧚🏻‍♀">light skin tone | Titania | woman fairy</annotation>
+		<annotation cp="🧚🏻‍♀" type="tts">woman fairy: light skin tone</annotation>
+		<annotation cp="🧚🏼‍♀">medium-light skin tone | Titania | woman fairy</annotation>
+		<annotation cp="🧚🏼‍♀" type="tts">woman fairy: medium-light skin tone</annotation>
+		<annotation cp="🧚🏽‍♀">medium skin tone | Titania | woman fairy</annotation>
+		<annotation cp="🧚🏽‍♀" type="tts">woman fairy: medium skin tone</annotation>
+		<annotation cp="🧚🏾‍♀">medium-dark skin tone | Titania | woman fairy</annotation>
+		<annotation cp="🧚🏾‍♀" type="tts">woman fairy: medium-dark skin tone</annotation>
+		<annotation cp="🧚🏿‍♀">dark skin tone | Titania | woman fairy</annotation>
+		<annotation cp="🧚🏿‍♀" type="tts">woman fairy: dark skin tone</annotation>
+		<annotation cp="🧛🏻">Dracula | light skin tone | undead | vampire</annotation>
+		<annotation cp="🧛🏻" type="tts">vampire: light skin tone</annotation>
+		<annotation cp="🧛🏼">Dracula | medium-light skin tone | undead | vampire</annotation>
+		<annotation cp="🧛🏼" type="tts">vampire: medium-light skin tone</annotation>
+		<annotation cp="🧛🏽">Dracula | medium skin tone | undead | vampire</annotation>
+		<annotation cp="🧛🏽" type="tts">vampire: medium skin tone</annotation>
+		<annotation cp="🧛🏾">Dracula | medium-dark skin tone | undead | vampire</annotation>
+		<annotation cp="🧛🏾" type="tts">vampire: medium-dark skin tone</annotation>
+		<annotation cp="🧛🏿">dark skin tone | Dracula | undead | vampire</annotation>
+		<annotation cp="🧛🏿" type="tts">vampire: dark skin tone</annotation>
+		<annotation cp="🧛🏻‍♂">Dracula | light skin tone | man vampire | undead</annotation>
+		<annotation cp="🧛🏻‍♂" type="tts">man vampire: light skin tone</annotation>
+		<annotation cp="🧛🏼‍♂">Dracula | man vampire | medium-light skin tone | undead</annotation>
+		<annotation cp="🧛🏼‍♂" type="tts">man vampire: medium-light skin tone</annotation>
+		<annotation cp="🧛🏽‍♂">Dracula | man vampire | medium skin tone | undead</annotation>
+		<annotation cp="🧛🏽‍♂" type="tts">man vampire: medium skin tone</annotation>
+		<annotation cp="🧛🏾‍♂">Dracula | man vampire | medium-dark skin tone | undead</annotation>
+		<annotation cp="🧛🏾‍♂" type="tts">man vampire: medium-dark skin tone</annotation>
+		<annotation cp="🧛🏿‍♂">dark skin tone | Dracula | man vampire | undead</annotation>
+		<annotation cp="🧛🏿‍♂" type="tts">man vampire: dark skin tone</annotation>
+		<annotation cp="🧛🏻‍♀">light skin tone | undead | woman vampire</annotation>
+		<annotation cp="🧛🏻‍♀" type="tts">woman vampire: light skin tone</annotation>
+		<annotation cp="🧛🏼‍♀">medium-light skin tone | undead | woman vampire</annotation>
+		<annotation cp="🧛🏼‍♀" type="tts">woman vampire: medium-light skin tone</annotation>
+		<annotation cp="🧛🏽‍♀">medium skin tone | undead | woman vampire</annotation>
+		<annotation cp="🧛🏽‍♀" type="tts">woman vampire: medium skin tone</annotation>
+		<annotation cp="🧛🏾‍♀">medium-dark skin tone | undead | woman vampire</annotation>
+		<annotation cp="🧛🏾‍♀" type="tts">woman vampire: medium-dark skin tone</annotation>
+		<annotation cp="🧛🏿‍♀">dark skin tone | undead | woman vampire</annotation>
+		<annotation cp="🧛🏿‍♀" type="tts">woman vampire: dark skin tone</annotation>
+		<annotation cp="🧜🏻">light skin tone | mermaid | merman | merperson | merwoman</annotation>
+		<annotation cp="🧜🏻" type="tts">merperson: light skin tone</annotation>
+		<annotation cp="🧜🏼">medium-light skin tone | mermaid | merman | merperson | merwoman</annotation>
+		<annotation cp="🧜🏼" type="tts">merperson: medium-light skin tone</annotation>
+		<annotation cp="🧜🏽">medium skin tone | mermaid | merman | merperson | merwoman</annotation>
+		<annotation cp="🧜🏽" type="tts">merperson: medium skin tone</annotation>
+		<annotation cp="🧜🏾">medium-dark skin tone | mermaid | merman | merperson | merwoman</annotation>
+		<annotation cp="🧜🏾" type="tts">merperson: medium-dark skin tone</annotation>
+		<annotation cp="🧜🏿">dark skin tone | mermaid | merman | merperson | merwoman</annotation>
+		<annotation cp="🧜🏿" type="tts">merperson: dark skin tone</annotation>
+		<annotation cp="🧜🏻‍♂">light skin tone | merman | Triton</annotation>
+		<annotation cp="🧜🏻‍♂" type="tts">merman: light skin tone</annotation>
+		<annotation cp="🧜🏼‍♂">medium-light skin tone | merman | Triton</annotation>
+		<annotation cp="🧜🏼‍♂" type="tts">merman: medium-light skin tone</annotation>
+		<annotation cp="🧜🏽‍♂">medium skin tone | merman | Triton</annotation>
+		<annotation cp="🧜🏽‍♂" type="tts">merman: medium skin tone</annotation>
+		<annotation cp="🧜🏾‍♂">medium-dark skin tone | merman | Triton</annotation>
+		<annotation cp="🧜🏾‍♂" type="tts">merman: medium-dark skin tone</annotation>
+		<annotation cp="🧜🏿‍♂">dark skin tone | merman | Triton</annotation>
+		<annotation cp="🧜🏿‍♂" type="tts">merman: dark skin tone</annotation>
+		<annotation cp="🧜🏻‍♀">light skin tone | mermaid | merwoman</annotation>
+		<annotation cp="🧜🏻‍♀" type="tts">mermaid: light skin tone</annotation>
+		<annotation cp="🧜🏼‍♀">medium-light skin tone | mermaid | merwoman</annotation>
+		<annotation cp="🧜🏼‍♀" type="tts">mermaid: medium-light skin tone</annotation>
+		<annotation cp="🧜🏽‍♀">medium skin tone | mermaid | merwoman</annotation>
+		<annotation cp="🧜🏽‍♀" type="tts">mermaid: medium skin tone</annotation>
+		<annotation cp="🧜🏾‍♀">medium-dark skin tone | mermaid | merwoman</annotation>
+		<annotation cp="🧜🏾‍♀" type="tts">mermaid: medium-dark skin tone</annotation>
+		<annotation cp="🧜🏿‍♀">dark skin tone | mermaid | merwoman</annotation>
+		<annotation cp="🧜🏿‍♀" type="tts">mermaid: dark skin tone</annotation>
+		<annotation cp="🧝🏻">elf | light skin tone | magical</annotation>
+		<annotation cp="🧝🏻" type="tts">elf: light skin tone</annotation>
+		<annotation cp="🧝🏼">elf | magical | medium-light skin tone</annotation>
+		<annotation cp="🧝🏼" type="tts">elf: medium-light skin tone</annotation>
+		<annotation cp="🧝🏽">elf | magical | medium skin tone</annotation>
+		<annotation cp="🧝🏽" type="tts">elf: medium skin tone</annotation>
+		<annotation cp="🧝🏾">elf | magical | medium-dark skin tone</annotation>
+		<annotation cp="🧝🏾" type="tts">elf: medium-dark skin tone</annotation>
+		<annotation cp="🧝🏿">dark skin tone | elf | magical</annotation>
+		<annotation cp="🧝🏿" type="tts">elf: dark skin tone</annotation>
+		<annotation cp="🧝🏻‍♂">light skin tone | magical | man elf</annotation>
+		<annotation cp="🧝🏻‍♂" type="tts">man elf: light skin tone</annotation>
+		<annotation cp="🧝🏼‍♂">magical | man elf | medium-light skin tone</annotation>
+		<annotation cp="🧝🏼‍♂" type="tts">man elf: medium-light skin tone</annotation>
+		<annotation cp="🧝🏽‍♂">magical | man elf | medium skin tone</annotation>
+		<annotation cp="🧝🏽‍♂" type="tts">man elf: medium skin tone</annotation>
+		<annotation cp="🧝🏾‍♂">magical | man elf | medium-dark skin tone</annotation>
+		<annotation cp="🧝🏾‍♂" type="tts">man elf: medium-dark skin tone</annotation>
+		<annotation cp="🧝🏿‍♂">dark skin tone | magical | man elf</annotation>
+		<annotation cp="🧝🏿‍♂" type="tts">man elf: dark skin tone</annotation>
+		<annotation cp="🧝🏻‍♀">light skin tone | magical | woman elf</annotation>
+		<annotation cp="🧝🏻‍♀" type="tts">woman elf: light skin tone</annotation>
+		<annotation cp="🧝🏼‍♀">magical | medium-light skin tone | woman elf</annotation>
+		<annotation cp="🧝🏼‍♀" type="tts">woman elf: medium-light skin tone</annotation>
+		<annotation cp="🧝🏽‍♀">magical | medium skin tone | woman elf</annotation>
+		<annotation cp="🧝🏽‍♀" type="tts">woman elf: medium skin tone</annotation>
+		<annotation cp="🧝🏾‍♀">magical | medium-dark skin tone | woman elf</annotation>
+		<annotation cp="🧝🏾‍♀" type="tts">woman elf: medium-dark skin tone</annotation>
+		<annotation cp="🧝🏿‍♀">dark skin tone | magical | woman elf</annotation>
+		<annotation cp="🧝🏿‍♀" type="tts">woman elf: dark skin tone</annotation>
+		<annotation cp="💆🏻">face | light skin tone | massage | person getting massage | salon</annotation>
+		<annotation cp="💆🏻" type="tts">person getting massage: light skin tone</annotation>
+		<annotation cp="💆🏼">face | massage | medium-light skin tone | person getting massage | salon</annotation>
+		<annotation cp="💆🏼" type="tts">person getting massage: medium-light skin tone</annotation>
+		<annotation cp="💆🏽">face | massage | medium skin tone | person getting massage | salon</annotation>
+		<annotation cp="💆🏽" type="tts">person getting massage: medium skin tone</annotation>
+		<annotation cp="💆🏾">face | massage | medium-dark skin tone | person getting massage | salon</annotation>
+		<annotation cp="💆🏾" type="tts">person getting massage: medium-dark skin tone</annotation>
+		<annotation cp="💆🏿">dark skin tone | face | massage | person getting massage | salon</annotation>
+		<annotation cp="💆🏿" type="tts">person getting massage: dark skin tone</annotation>
+		<annotation cp="💆🏻‍♂">face | light skin tone | man | man getting massage | massage</annotation>
+		<annotation cp="💆🏻‍♂" type="tts">man getting massage: light skin tone</annotation>
+		<annotation cp="💆🏼‍♂">face | man | man getting massage | massage | medium-light skin tone</annotation>
+		<annotation cp="💆🏼‍♂" type="tts">man getting massage: medium-light skin tone</annotation>
+		<annotation cp="💆🏽‍♂">face | man | man getting massage | massage | medium skin tone</annotation>
+		<annotation cp="💆🏽‍♂" type="tts">man getting massage: medium skin tone</annotation>
+		<annotation cp="💆🏾‍♂">face | man | man getting massage | massage | medium-dark skin tone</annotation>
+		<annotation cp="💆🏾‍♂" type="tts">man getting massage: medium-dark skin tone</annotation>
+		<annotation cp="💆🏿‍♂">dark skin tone | face | man | man getting massage | massage</annotation>
+		<annotation cp="💆🏿‍♂" type="tts">man getting massage: dark skin tone</annotation>
+		<annotation cp="💆🏻‍♀">face | light skin tone | massage | woman | woman getting massage</annotation>
+		<annotation cp="💆🏻‍♀" type="tts">woman getting massage: light skin tone</annotation>
+		<annotation cp="💆🏼‍♀">face | massage | medium-light skin tone | woman | woman getting massage</annotation>
+		<annotation cp="💆🏼‍♀" type="tts">woman getting massage: medium-light skin tone</annotation>
+		<annotation cp="💆🏽‍♀">face | massage | medium skin tone | woman | woman getting massage</annotation>
+		<annotation cp="💆🏽‍♀" type="tts">woman getting massage: medium skin tone</annotation>
+		<annotation cp="💆🏾‍♀">face | massage | medium-dark skin tone | woman | woman getting massage</annotation>
+		<annotation cp="💆🏾‍♀" type="tts">woman getting massage: medium-dark skin tone</annotation>
+		<annotation cp="💆🏿‍♀">dark skin tone | face | massage | woman | woman getting massage</annotation>
+		<annotation cp="💆🏿‍♀" type="tts">woman getting massage: dark skin tone</annotation>
+		<annotation cp="💇🏻">barber | beauty | haircut | light skin tone | parlor | person getting haircut</annotation>
+		<annotation cp="💇🏻" type="tts">person getting haircut: light skin tone</annotation>
+		<annotation cp="💇🏼">barber | beauty | haircut | medium-light skin tone | parlor | person getting haircut</annotation>
+		<annotation cp="💇🏼" type="tts">person getting haircut: medium-light skin tone</annotation>
+		<annotation cp="💇🏽">barber | beauty | haircut | medium skin tone | parlor | person getting haircut</annotation>
+		<annotation cp="💇🏽" type="tts">person getting haircut: medium skin tone</annotation>
+		<annotation cp="💇🏾">barber | beauty | haircut | medium-dark skin tone | parlor | person getting haircut</annotation>
+		<annotation cp="💇🏾" type="tts">person getting haircut: medium-dark skin tone</annotation>
+		<annotation cp="💇🏿">barber | beauty | dark skin tone | haircut | parlor | person getting haircut</annotation>
+		<annotation cp="💇🏿" type="tts">person getting haircut: dark skin tone</annotation>
+		<annotation cp="💇🏻‍♂">haircut | light skin tone | man | man getting haircut</annotation>
+		<annotation cp="💇🏻‍♂" type="tts">man getting haircut: light skin tone</annotation>
+		<annotation cp="💇🏼‍♂">haircut | man | man getting haircut | medium-light skin tone</annotation>
+		<annotation cp="💇🏼‍♂" type="tts">man getting haircut: medium-light skin tone</annotation>
+		<annotation cp="💇🏽‍♂">haircut | man | man getting haircut | medium skin tone</annotation>
+		<annotation cp="💇🏽‍♂" type="tts">man getting haircut: medium skin tone</annotation>
+		<annotation cp="💇🏾‍♂">haircut | man | man getting haircut | medium-dark skin tone</annotation>
+		<annotation cp="💇🏾‍♂" type="tts">man getting haircut: medium-dark skin tone</annotation>
+		<annotation cp="💇🏿‍♂">dark skin tone | haircut | man | man getting haircut</annotation>
+		<annotation cp="💇🏿‍♂" type="tts">man getting haircut: dark skin tone</annotation>
+		<annotation cp="💇🏻‍♀">haircut | light skin tone | woman | woman getting haircut</annotation>
+		<annotation cp="💇🏻‍♀" type="tts">woman getting haircut: light skin tone</annotation>
+		<annotation cp="💇🏼‍♀">haircut | medium-light skin tone | woman | woman getting haircut</annotation>
+		<annotation cp="💇🏼‍♀" type="tts">woman getting haircut: medium-light skin tone</annotation>
+		<annotation cp="💇🏽‍♀">haircut | medium skin tone | woman | woman getting haircut</annotation>
+		<annotation cp="💇🏽‍♀" type="tts">woman getting haircut: medium skin tone</annotation>
+		<annotation cp="💇🏾‍♀">haircut | medium-dark skin tone | woman | woman getting haircut</annotation>
+		<annotation cp="💇🏾‍♀" type="tts">woman getting haircut: medium-dark skin tone</annotation>
+		<annotation cp="💇🏿‍♀">dark skin tone | haircut | woman | woman getting haircut</annotation>
+		<annotation cp="💇🏿‍♀" type="tts">woman getting haircut: dark skin tone</annotation>
+		<annotation cp="🚶🏻">hike | light skin tone | person walking | walk | walking</annotation>
+		<annotation cp="🚶🏻" type="tts">person walking: light skin tone</annotation>
+		<annotation cp="🚶🏼">hike | medium-light skin tone | person walking | walk | walking</annotation>
+		<annotation cp="🚶🏼" type="tts">person walking: medium-light skin tone</annotation>
+		<annotation cp="🚶🏽">hike | medium skin tone | person walking | walk | walking</annotation>
+		<annotation cp="🚶🏽" type="tts">person walking: medium skin tone</annotation>
+		<annotation cp="🚶🏾">hike | medium-dark skin tone | person walking | walk | walking</annotation>
+		<annotation cp="🚶🏾" type="tts">person walking: medium-dark skin tone</annotation>
+		<annotation cp="🚶🏿">dark skin tone | hike | person walking | walk | walking</annotation>
+		<annotation cp="🚶🏿" type="tts">person walking: dark skin tone</annotation>
+		<annotation cp="🚶🏻‍♂">hike | light skin tone | man | man walking | walk</annotation>
+		<annotation cp="🚶🏻‍♂" type="tts">man walking: light skin tone</annotation>
+		<annotation cp="🚶🏼‍♂">hike | man | man walking | medium-light skin tone | walk</annotation>
+		<annotation cp="🚶🏼‍♂" type="tts">man walking: medium-light skin tone</annotation>
+		<annotation cp="🚶🏽‍♂">hike | man | man walking | medium skin tone | walk</annotation>
+		<annotation cp="🚶🏽‍♂" type="tts">man walking: medium skin tone</annotation>
+		<annotation cp="🚶🏾‍♂">hike | man | man walking | medium-dark skin tone | walk</annotation>
+		<annotation cp="🚶🏾‍♂" type="tts">man walking: medium-dark skin tone</annotation>
+		<annotation cp="🚶🏿‍♂">dark skin tone | hike | man | man walking | walk</annotation>
+		<annotation cp="🚶🏿‍♂" type="tts">man walking: dark skin tone</annotation>
+		<annotation cp="🚶🏻‍♀">hike | light skin tone | walk | woman | woman walking</annotation>
+		<annotation cp="🚶🏻‍♀" type="tts">woman walking: light skin tone</annotation>
+		<annotation cp="🚶🏼‍♀">hike | medium-light skin tone | walk | woman | woman walking</annotation>
+		<annotation cp="🚶🏼‍♀" type="tts">woman walking: medium-light skin tone</annotation>
+		<annotation cp="🚶🏽‍♀">hike | medium skin tone | walk | woman | woman walking</annotation>
+		<annotation cp="🚶🏽‍♀" type="tts">woman walking: medium skin tone</annotation>
+		<annotation cp="🚶🏾‍♀">hike | medium-dark skin tone | walk | woman | woman walking</annotation>
+		<annotation cp="🚶🏾‍♀" type="tts">woman walking: medium-dark skin tone</annotation>
+		<annotation cp="🚶🏿‍♀">dark skin tone | hike | walk | woman | woman walking</annotation>
+		<annotation cp="🚶🏿‍♀" type="tts">woman walking: dark skin tone</annotation>
+		<annotation cp="🧍🏻">light skin tone | person standing | stand | standing</annotation>
+		<annotation cp="🧍🏻" type="tts">person standing: light skin tone</annotation>
+		<annotation cp="🧍🏼">medium-light skin tone | person standing | stand | standing</annotation>
+		<annotation cp="🧍🏼" type="tts">person standing: medium-light skin tone</annotation>
+		<annotation cp="🧍🏽">medium skin tone | person standing | stand | standing</annotation>
+		<annotation cp="🧍🏽" type="tts">person standing: medium skin tone</annotation>
+		<annotation cp="🧍🏾">medium-dark skin tone | person standing | stand | standing</annotation>
+		<annotation cp="🧍🏾" type="tts">person standing: medium-dark skin tone</annotation>
+		<annotation cp="🧍🏿">dark skin tone | person standing | stand | standing</annotation>
+		<annotation cp="🧍🏿" type="tts">person standing: dark skin tone</annotation>
+		<annotation cp="🧍🏻‍♂">light skin tone | man | standing</annotation>
+		<annotation cp="🧍🏻‍♂" type="tts">man standing: light skin tone</annotation>
+		<annotation cp="🧍🏼‍♂">man | medium-light skin tone | standing</annotation>
+		<annotation cp="🧍🏼‍♂" type="tts">man standing: medium-light skin tone</annotation>
+		<annotation cp="🧍🏽‍♂">man | medium skin tone | standing</annotation>
+		<annotation cp="🧍🏽‍♂" type="tts">man standing: medium skin tone</annotation>
+		<annotation cp="🧍🏾‍♂">man | medium-dark skin tone | standing</annotation>
+		<annotation cp="🧍🏾‍♂" type="tts">man standing: medium-dark skin tone</annotation>
+		<annotation cp="🧍🏿‍♂">dark skin tone | man | standing</annotation>
+		<annotation cp="🧍🏿‍♂" type="tts">man standing: dark skin tone</annotation>
+		<annotation cp="🧍🏻‍♀">light skin tone | standing | woman</annotation>
+		<annotation cp="🧍🏻‍♀" type="tts">woman standing: light skin tone</annotation>
+		<annotation cp="🧍🏼‍♀">medium-light skin tone | standing | woman</annotation>
+		<annotation cp="🧍🏼‍♀" type="tts">woman standing: medium-light skin tone</annotation>
+		<annotation cp="🧍🏽‍♀">medium skin tone | standing | woman</annotation>
+		<annotation cp="🧍🏽‍♀" type="tts">woman standing: medium skin tone</annotation>
+		<annotation cp="🧍🏾‍♀">medium-dark skin tone | standing | woman</annotation>
+		<annotation cp="🧍🏾‍♀" type="tts">woman standing: medium-dark skin tone</annotation>
+		<annotation cp="🧍🏿‍♀">dark skin tone | standing | woman</annotation>
+		<annotation cp="🧍🏿‍♀" type="tts">woman standing: dark skin tone</annotation>
+		<annotation cp="🧎🏻">kneel | kneeling | light skin tone | person kneeling</annotation>
+		<annotation cp="🧎🏻" type="tts">person kneeling: light skin tone</annotation>
+		<annotation cp="🧎🏼">kneel | kneeling | medium-light skin tone | person kneeling</annotation>
+		<annotation cp="🧎🏼" type="tts">person kneeling: medium-light skin tone</annotation>
+		<annotation cp="🧎🏽">kneel | kneeling | medium skin tone | person kneeling</annotation>
+		<annotation cp="🧎🏽" type="tts">person kneeling: medium skin tone</annotation>
+		<annotation cp="🧎🏾">kneel | kneeling | medium-dark skin tone | person kneeling</annotation>
+		<annotation cp="🧎🏾" type="tts">person kneeling: medium-dark skin tone</annotation>
+		<annotation cp="🧎🏿">dark skin tone | kneel | kneeling | person kneeling</annotation>
+		<annotation cp="🧎🏿" type="tts">person kneeling: dark skin tone</annotation>
+		<annotation cp="🧎🏻‍♂">kneeling | light skin tone | man</annotation>
+		<annotation cp="🧎🏻‍♂" type="tts">man kneeling: light skin tone</annotation>
+		<annotation cp="🧎🏼‍♂">kneeling | man | medium-light skin tone</annotation>
+		<annotation cp="🧎🏼‍♂" type="tts">man kneeling: medium-light skin tone</annotation>
+		<annotation cp="🧎🏽‍♂">kneeling | man | medium skin tone</annotation>
+		<annotation cp="🧎🏽‍♂" type="tts">man kneeling: medium skin tone</annotation>
+		<annotation cp="🧎🏾‍♂">kneeling | man | medium-dark skin tone</annotation>
+		<annotation cp="🧎🏾‍♂" type="tts">man kneeling: medium-dark skin tone</annotation>
+		<annotation cp="🧎🏿‍♂">dark skin tone | kneeling | man</annotation>
+		<annotation cp="🧎🏿‍♂" type="tts">man kneeling: dark skin tone</annotation>
+		<annotation cp="🧎🏻‍♀">kneeling | light skin tone | woman</annotation>
+		<annotation cp="🧎🏻‍♀" type="tts">woman kneeling: light skin tone</annotation>
+		<annotation cp="🧎🏼‍♀">kneeling | medium-light skin tone | woman</annotation>
+		<annotation cp="🧎🏼‍♀" type="tts">woman kneeling: medium-light skin tone</annotation>
+		<annotation cp="🧎🏽‍♀">kneeling | medium skin tone | woman</annotation>
+		<annotation cp="🧎🏽‍♀" type="tts">woman kneeling: medium skin tone</annotation>
+		<annotation cp="🧎🏾‍♀">kneeling | medium-dark skin tone | woman</annotation>
+		<annotation cp="🧎🏾‍♀" type="tts">woman kneeling: medium-dark skin tone</annotation>
+		<annotation cp="🧎🏿‍♀">dark skin tone | kneeling | woman</annotation>
+		<annotation cp="🧎🏿‍♀" type="tts">woman kneeling: dark skin tone</annotation>
+		<annotation cp="🧑🏻‍🦯">accessibility | blind | light skin tone | person with white cane</annotation>
+		<annotation cp="🧑🏻‍🦯" type="tts">person with white cane: light skin tone</annotation>
+		<annotation cp="🧑🏼‍🦯">accessibility | blind | medium-light skin tone | person with white cane</annotation>
+		<annotation cp="🧑🏼‍🦯" type="tts">person with white cane: medium-light skin tone</annotation>
+		<annotation cp="🧑🏽‍🦯">accessibility | blind | medium skin tone | person with white cane</annotation>
+		<annotation cp="🧑🏽‍🦯" type="tts">person with white cane: medium skin tone</annotation>
+		<annotation cp="🧑🏾‍🦯">accessibility | blind | medium-dark skin tone | person with white cane</annotation>
+		<annotation cp="🧑🏾‍🦯" type="tts">person with white cane: medium-dark skin tone</annotation>
+		<annotation cp="🧑🏿‍🦯">accessibility | blind | dark skin tone | person with white cane</annotation>
+		<annotation cp="🧑🏿‍🦯" type="tts">person with white cane: dark skin tone</annotation>
+		<annotation cp="👨🏻‍🦯">accessibility | blind | light skin tone | man | man with white cane</annotation>
+		<annotation cp="👨🏻‍🦯" type="tts">man with white cane: light skin tone</annotation>
+		<annotation cp="👨🏼‍🦯">accessibility | blind | man | man with white cane | medium-light skin tone</annotation>
+		<annotation cp="👨🏼‍🦯" type="tts">man with white cane: medium-light skin tone</annotation>
+		<annotation cp="👨🏽‍🦯">accessibility | blind | man | man with white cane | medium skin tone</annotation>
+		<annotation cp="👨🏽‍🦯" type="tts">man with white cane: medium skin tone</annotation>
+		<annotation cp="👨🏾‍🦯">accessibility | blind | man | man with white cane | medium-dark skin tone</annotation>
+		<annotation cp="👨🏾‍🦯" type="tts">man with white cane: medium-dark skin tone</annotation>
+		<annotation cp="👨🏿‍🦯">accessibility | blind | dark skin tone | man | man with white cane</annotation>
+		<annotation cp="👨🏿‍🦯" type="tts">man with white cane: dark skin tone</annotation>
+		<annotation cp="👩🏻‍🦯">accessibility | blind | light skin tone | woman | woman with white cane</annotation>
+		<annotation cp="👩🏻‍🦯" type="tts">woman with white cane: light skin tone</annotation>
+		<annotation cp="👩🏼‍🦯">accessibility | blind | medium-light skin tone | woman | woman with white cane</annotation>
+		<annotation cp="👩🏼‍🦯" type="tts">woman with white cane: medium-light skin tone</annotation>
+		<annotation cp="👩🏽‍🦯">accessibility | blind | medium skin tone | woman | woman with white cane</annotation>
+		<annotation cp="👩🏽‍🦯" type="tts">woman with white cane: medium skin tone</annotation>
+		<annotation cp="👩🏾‍🦯">accessibility | blind | medium-dark skin tone | woman | woman with white cane</annotation>
+		<annotation cp="👩🏾‍🦯" type="tts">woman with white cane: medium-dark skin tone</annotation>
+		<annotation cp="👩🏿‍🦯">accessibility | blind | dark skin tone | woman | woman with white cane</annotation>
+		<annotation cp="👩🏿‍🦯" type="tts">woman with white cane: dark skin tone</annotation>
+		<annotation cp="🧑🏻‍🦼">accessibility | light skin tone | person in motorized wheelchair | wheelchair</annotation>
+		<annotation cp="🧑🏻‍🦼" type="tts">person in motorized wheelchair: light skin tone</annotation>
+		<annotation cp="🧑🏼‍🦼">accessibility | medium-light skin tone | person in motorized wheelchair | wheelchair</annotation>
+		<annotation cp="🧑🏼‍🦼" type="tts">person in motorized wheelchair: medium-light skin tone</annotation>
+		<annotation cp="🧑🏽‍🦼">accessibility | medium skin tone | person in motorized wheelchair | wheelchair</annotation>
+		<annotation cp="🧑🏽‍🦼" type="tts">person in motorized wheelchair: medium skin tone</annotation>
+		<annotation cp="🧑🏾‍🦼">accessibility | medium-dark skin tone | person in motorized wheelchair | wheelchair</annotation>
+		<annotation cp="🧑🏾‍🦼" type="tts">person in motorized wheelchair: medium-dark skin tone</annotation>
+		<annotation cp="🧑🏿‍🦼">accessibility | dark skin tone | person in motorized wheelchair | wheelchair</annotation>
+		<annotation cp="🧑🏿‍🦼" type="tts">person in motorized wheelchair: dark skin tone</annotation>
+		<annotation cp="👨🏻‍🦼">accessibility | light skin tone | man | man in motorized wheelchair | wheelchair</annotation>
+		<annotation cp="👨🏻‍🦼" type="tts">man in motorized wheelchair: light skin tone</annotation>
+		<annotation cp="👨🏼‍🦼">accessibility | man | man in motorized wheelchair | medium-light skin tone | wheelchair</annotation>
+		<annotation cp="👨🏼‍🦼" type="tts">man in motorized wheelchair: medium-light skin tone</annotation>
+		<annotation cp="👨🏽‍🦼">accessibility | man | man in motorized wheelchair | medium skin tone | wheelchair</annotation>
+		<annotation cp="👨🏽‍🦼" type="tts">man in motorized wheelchair: medium skin tone</annotation>
+		<annotation cp="👨🏾‍🦼">accessibility | man | man in motorized wheelchair | medium-dark skin tone | wheelchair</annotation>
+		<annotation cp="👨🏾‍🦼" type="tts">man in motorized wheelchair: medium-dark skin tone</annotation>
+		<annotation cp="👨🏿‍🦼">accessibility | dark skin tone | man | man in motorized wheelchair | wheelchair</annotation>
+		<annotation cp="👨🏿‍🦼" type="tts">man in motorized wheelchair: dark skin tone</annotation>
+		<annotation cp="👩🏻‍🦼">accessibility | light skin tone | wheelchair | woman | woman in motorized wheelchair</annotation>
+		<annotation cp="👩🏻‍🦼" type="tts">woman in motorized wheelchair: light skin tone</annotation>
+		<annotation cp="👩🏼‍🦼">accessibility | medium-light skin tone | wheelchair | woman | woman in motorized wheelchair</annotation>
+		<annotation cp="👩🏼‍🦼" type="tts">woman in motorized wheelchair: medium-light skin tone</annotation>
+		<annotation cp="👩🏽‍🦼">accessibility | medium skin tone | wheelchair | woman | woman in motorized wheelchair</annotation>
+		<annotation cp="👩🏽‍🦼" type="tts">woman in motorized wheelchair: medium skin tone</annotation>
+		<annotation cp="👩🏾‍🦼">accessibility | medium-dark skin tone | wheelchair | woman | woman in motorized wheelchair</annotation>
+		<annotation cp="👩🏾‍🦼" type="tts">woman in motorized wheelchair: medium-dark skin tone</annotation>
+		<annotation cp="👩🏿‍🦼">accessibility | dark skin tone | wheelchair | woman | woman in motorized wheelchair</annotation>
+		<annotation cp="👩🏿‍🦼" type="tts">woman in motorized wheelchair: dark skin tone</annotation>
+		<annotation cp="🧑🏻‍🦽">accessibility | light skin tone | person in manual wheelchair | wheelchair</annotation>
+		<annotation cp="🧑🏻‍🦽" type="tts">person in manual wheelchair: light skin tone</annotation>
+		<annotation cp="🧑🏼‍🦽">accessibility | medium-light skin tone | person in manual wheelchair | wheelchair</annotation>
+		<annotation cp="🧑🏼‍🦽" type="tts">person in manual wheelchair: medium-light skin tone</annotation>
+		<annotation cp="🧑🏽‍🦽">accessibility | medium skin tone | person in manual wheelchair | wheelchair</annotation>
+		<annotation cp="🧑🏽‍🦽" type="tts">person in manual wheelchair: medium skin tone</annotation>
+		<annotation cp="🧑🏾‍🦽">accessibility | medium-dark skin tone | person in manual wheelchair | wheelchair</annotation>
+		<annotation cp="🧑🏾‍🦽" type="tts">person in manual wheelchair: medium-dark skin tone</annotation>
+		<annotation cp="🧑🏿‍🦽">accessibility | dark skin tone | person in manual wheelchair | wheelchair</annotation>
+		<annotation cp="🧑🏿‍🦽" type="tts">person in manual wheelchair: dark skin tone</annotation>
+		<annotation cp="👨🏻‍🦽">accessibility | light skin tone | man | man in manual wheelchair | wheelchair</annotation>
+		<annotation cp="👨🏻‍🦽" type="tts">man in manual wheelchair: light skin tone</annotation>
+		<annotation cp="👨🏼‍🦽">accessibility | man | man in manual wheelchair | medium-light skin tone | wheelchair</annotation>
+		<annotation cp="👨🏼‍🦽" type="tts">man in manual wheelchair: medium-light skin tone</annotation>
+		<annotation cp="👨🏽‍🦽">accessibility | man | man in manual wheelchair | medium skin tone | wheelchair</annotation>
+		<annotation cp="👨🏽‍🦽" type="tts">man in manual wheelchair: medium skin tone</annotation>
+		<annotation cp="👨🏾‍🦽">accessibility | man | man in manual wheelchair | medium-dark skin tone | wheelchair</annotation>
+		<annotation cp="👨🏾‍🦽" type="tts">man in manual wheelchair: medium-dark skin tone</annotation>
+		<annotation cp="👨🏿‍🦽">accessibility | dark skin tone | man | man in manual wheelchair | wheelchair</annotation>
+		<annotation cp="👨🏿‍🦽" type="tts">man in manual wheelchair: dark skin tone</annotation>
+		<annotation cp="👩🏻‍🦽">accessibility | light skin tone | wheelchair | woman | woman in manual wheelchair</annotation>
+		<annotation cp="👩🏻‍🦽" type="tts">woman in manual wheelchair: light skin tone</annotation>
+		<annotation cp="👩🏼‍🦽">accessibility | medium-light skin tone | wheelchair | woman | woman in manual wheelchair</annotation>
+		<annotation cp="👩🏼‍🦽" type="tts">woman in manual wheelchair: medium-light skin tone</annotation>
+		<annotation cp="👩🏽‍🦽">accessibility | medium skin tone | wheelchair | woman | woman in manual wheelchair</annotation>
+		<annotation cp="👩🏽‍🦽" type="tts">woman in manual wheelchair: medium skin tone</annotation>
+		<annotation cp="👩🏾‍🦽">accessibility | medium-dark skin tone | wheelchair | woman | woman in manual wheelchair</annotation>
+		<annotation cp="👩🏾‍🦽" type="tts">woman in manual wheelchair: medium-dark skin tone</annotation>
+		<annotation cp="👩🏿‍🦽">accessibility | dark skin tone | wheelchair | woman | woman in manual wheelchair</annotation>
+		<annotation cp="👩🏿‍🦽" type="tts">woman in manual wheelchair: dark skin tone</annotation>
+		<annotation cp="🏃🏻">light skin tone | marathon | person running | running</annotation>
+		<annotation cp="🏃🏻" type="tts">person running: light skin tone</annotation>
+		<annotation cp="🏃🏼">marathon | medium-light skin tone | person running | running</annotation>
+		<annotation cp="🏃🏼" type="tts">person running: medium-light skin tone</annotation>
+		<annotation cp="🏃🏽">marathon | medium skin tone | person running | running</annotation>
+		<annotation cp="🏃🏽" type="tts">person running: medium skin tone</annotation>
+		<annotation cp="🏃🏾">marathon | medium-dark skin tone | person running | running</annotation>
+		<annotation cp="🏃🏾" type="tts">person running: medium-dark skin tone</annotation>
+		<annotation cp="🏃🏿">dark skin tone | marathon | person running | running</annotation>
+		<annotation cp="🏃🏿" type="tts">person running: dark skin tone</annotation>
+		<annotation cp="🏃🏻‍♂">light skin tone | man | marathon | racing | running</annotation>
+		<annotation cp="🏃🏻‍♂" type="tts">man running: light skin tone</annotation>
+		<annotation cp="🏃🏼‍♂">man | marathon | medium-light skin tone | racing | running</annotation>
+		<annotation cp="🏃🏼‍♂" type="tts">man running: medium-light skin tone</annotation>
+		<annotation cp="🏃🏽‍♂">man | marathon | medium skin tone | racing | running</annotation>
+		<annotation cp="🏃🏽‍♂" type="tts">man running: medium skin tone</annotation>
+		<annotation cp="🏃🏾‍♂">man | marathon | medium-dark skin tone | racing | running</annotation>
+		<annotation cp="🏃🏾‍♂" type="tts">man running: medium-dark skin tone</annotation>
+		<annotation cp="🏃🏿‍♂">dark skin tone | man | marathon | racing | running</annotation>
+		<annotation cp="🏃🏿‍♂" type="tts">man running: dark skin tone</annotation>
+		<annotation cp="🏃🏻‍♀">light skin tone | marathon | racing | running | woman</annotation>
+		<annotation cp="🏃🏻‍♀" type="tts">woman running: light skin tone</annotation>
+		<annotation cp="🏃🏼‍♀">marathon | medium-light skin tone | racing | running | woman</annotation>
+		<annotation cp="🏃🏼‍♀" type="tts">woman running: medium-light skin tone</annotation>
+		<annotation cp="🏃🏽‍♀">marathon | medium skin tone | racing | running | woman</annotation>
+		<annotation cp="🏃🏽‍♀" type="tts">woman running: medium skin tone</annotation>
+		<annotation cp="🏃🏾‍♀">marathon | medium-dark skin tone | racing | running | woman</annotation>
+		<annotation cp="🏃🏾‍♀" type="tts">woman running: medium-dark skin tone</annotation>
+		<annotation cp="🏃🏿‍♀">dark skin tone | marathon | racing | running | woman</annotation>
+		<annotation cp="🏃🏿‍♀" type="tts">woman running: dark skin tone</annotation>
+		<annotation cp="💃🏻">dance | dancing | light skin tone | woman</annotation>
+		<annotation cp="💃🏻" type="tts">woman dancing: light skin tone</annotation>
+		<annotation cp="💃🏼">dance | dancing | medium-light skin tone | woman</annotation>
+		<annotation cp="💃🏼" type="tts">woman dancing: medium-light skin tone</annotation>
+		<annotation cp="💃🏽">dance | dancing | medium skin tone | woman</annotation>
+		<annotation cp="💃🏽" type="tts">woman dancing: medium skin tone</annotation>
+		<annotation cp="💃🏾">dance | dancing | medium-dark skin tone | woman</annotation>
+		<annotation cp="💃🏾" type="tts">woman dancing: medium-dark skin tone</annotation>
+		<annotation cp="💃🏿">dance | dancing | dark skin tone | woman</annotation>
+		<annotation cp="💃🏿" type="tts">woman dancing: dark skin tone</annotation>
+		<annotation cp="🕺🏻">dance | dancing | light skin tone | man</annotation>
+		<annotation cp="🕺🏻" type="tts">man dancing: light skin tone</annotation>
+		<annotation cp="🕺🏼">dance | dancing | man | medium-light skin tone</annotation>
+		<annotation cp="🕺🏼" type="tts">man dancing: medium-light skin tone</annotation>
+		<annotation cp="🕺🏽">dance | dancing | man | medium skin tone</annotation>
+		<annotation cp="🕺🏽" type="tts">man dancing: medium skin tone</annotation>
+		<annotation cp="🕺🏾">dance | dancing | man | medium-dark skin tone</annotation>
+		<annotation cp="🕺🏾" type="tts">man dancing: medium-dark skin tone</annotation>
+		<annotation cp="🕺🏿">dance | dancing | dark skin tone | man</annotation>
+		<annotation cp="🕺🏿" type="tts">man dancing: dark skin tone</annotation>
+		<annotation cp="🕴🏻">business | light skin tone | person | person in suit levitating | suit</annotation>
+		<annotation cp="🕴🏻" type="tts">person in suit levitating: light skin tone</annotation>
+		<annotation cp="🕴🏼">business | medium-light skin tone | person | person in suit levitating | suit</annotation>
+		<annotation cp="🕴🏼" type="tts">person in suit levitating: medium-light skin tone</annotation>
+		<annotation cp="🕴🏽">business | medium skin tone | person | person in suit levitating | suit</annotation>
+		<annotation cp="🕴🏽" type="tts">person in suit levitating: medium skin tone</annotation>
+		<annotation cp="🕴🏾">business | medium-dark skin tone | person | person in suit levitating | suit</annotation>
+		<annotation cp="🕴🏾" type="tts">person in suit levitating: medium-dark skin tone</annotation>
+		<annotation cp="🕴🏿">business | dark skin tone | person | person in suit levitating | suit</annotation>
+		<annotation cp="🕴🏿" type="tts">person in suit levitating: dark skin tone</annotation>
+		<annotation cp="🧖🏻">light skin tone | person in steamy room | sauna | steam room</annotation>
+		<annotation cp="🧖🏻" type="tts">person in steamy room: light skin tone</annotation>
+		<annotation cp="🧖🏼">medium-light skin tone | person in steamy room | sauna | steam room</annotation>
+		<annotation cp="🧖🏼" type="tts">person in steamy room: medium-light skin tone</annotation>
+		<annotation cp="🧖🏽">medium skin tone | person in steamy room | sauna | steam room</annotation>
+		<annotation cp="🧖🏽" type="tts">person in steamy room: medium skin tone</annotation>
+		<annotation cp="🧖🏾">medium-dark skin tone | person in steamy room | sauna | steam room</annotation>
+		<annotation cp="🧖🏾" type="tts">person in steamy room: medium-dark skin tone</annotation>
+		<annotation cp="🧖🏿">dark skin tone | person in steamy room | sauna | steam room</annotation>
+		<annotation cp="🧖🏿" type="tts">person in steamy room: dark skin tone</annotation>
+		<annotation cp="🧖🏻‍♂">light skin tone | man in steamy room | sauna | steam room</annotation>
+		<annotation cp="🧖🏻‍♂" type="tts">man in steamy room: light skin tone</annotation>
+		<annotation cp="🧖🏼‍♂">man in steamy room | medium-light skin tone | sauna | steam room</annotation>
+		<annotation cp="🧖🏼‍♂" type="tts">man in steamy room: medium-light skin tone</annotation>
+		<annotation cp="🧖🏽‍♂">man in steamy room | medium skin tone | sauna | steam room</annotation>
+		<annotation cp="🧖🏽‍♂" type="tts">man in steamy room: medium skin tone</annotation>
+		<annotation cp="🧖🏾‍♂">man in steamy room | medium-dark skin tone | sauna | steam room</annotation>
+		<annotation cp="🧖🏾‍♂" type="tts">man in steamy room: medium-dark skin tone</annotation>
+		<annotation cp="🧖🏿‍♂">dark skin tone | man in steamy room | sauna | steam room</annotation>
+		<annotation cp="🧖🏿‍♂" type="tts">man in steamy room: dark skin tone</annotation>
+		<annotation cp="🧖🏻‍♀">light skin tone | sauna | steam room | woman in steamy room</annotation>
+		<annotation cp="🧖🏻‍♀" type="tts">woman in steamy room: light skin tone</annotation>
+		<annotation cp="🧖🏼‍♀">medium-light skin tone | sauna | steam room | woman in steamy room</annotation>
+		<annotation cp="🧖🏼‍♀" type="tts">woman in steamy room: medium-light skin tone</annotation>
+		<annotation cp="🧖🏽‍♀">medium skin tone | sauna | steam room | woman in steamy room</annotation>
+		<annotation cp="🧖🏽‍♀" type="tts">woman in steamy room: medium skin tone</annotation>
+		<annotation cp="🧖🏾‍♀">medium-dark skin tone | sauna | steam room | woman in steamy room</annotation>
+		<annotation cp="🧖🏾‍♀" type="tts">woman in steamy room: medium-dark skin tone</annotation>
+		<annotation cp="🧖🏿‍♀">dark skin tone | sauna | steam room | woman in steamy room</annotation>
+		<annotation cp="🧖🏿‍♀" type="tts">woman in steamy room: dark skin tone</annotation>
+		<annotation cp="🧗🏻">climber | light skin tone | person climbing</annotation>
+		<annotation cp="🧗🏻" type="tts">person climbing: light skin tone</annotation>
+		<annotation cp="🧗🏼">climber | medium-light skin tone | person climbing</annotation>
+		<annotation cp="🧗🏼" type="tts">person climbing: medium-light skin tone</annotation>
+		<annotation cp="🧗🏽">climber | medium skin tone | person climbing</annotation>
+		<annotation cp="🧗🏽" type="tts">person climbing: medium skin tone</annotation>
+		<annotation cp="🧗🏾">climber | medium-dark skin tone | person climbing</annotation>
+		<annotation cp="🧗🏾" type="tts">person climbing: medium-dark skin tone</annotation>
+		<annotation cp="🧗🏿">climber | dark skin tone | person climbing</annotation>
+		<annotation cp="🧗🏿" type="tts">person climbing: dark skin tone</annotation>
+		<annotation cp="🧗🏻‍♂">climber | light skin tone | man climbing</annotation>
+		<annotation cp="🧗🏻‍♂" type="tts">man climbing: light skin tone</annotation>
+		<annotation cp="🧗🏼‍♂">climber | man climbing | medium-light skin tone</annotation>
+		<annotation cp="🧗🏼‍♂" type="tts">man climbing: medium-light skin tone</annotation>
+		<annotation cp="🧗🏽‍♂">climber | man climbing | medium skin tone</annotation>
+		<annotation cp="🧗🏽‍♂" type="tts">man climbing: medium skin tone</annotation>
+		<annotation cp="🧗🏾‍♂">climber | man climbing | medium-dark skin tone</annotation>
+		<annotation cp="🧗🏾‍♂" type="tts">man climbing: medium-dark skin tone</annotation>
+		<annotation cp="🧗🏿‍♂">climber | dark skin tone | man climbing</annotation>
+		<annotation cp="🧗🏿‍♂" type="tts">man climbing: dark skin tone</annotation>
+		<annotation cp="🧗🏻‍♀">climber | light skin tone | woman climbing</annotation>
+		<annotation cp="🧗🏻‍♀" type="tts">woman climbing: light skin tone</annotation>
+		<annotation cp="🧗🏼‍♀">climber | medium-light skin tone | woman climbing</annotation>
+		<annotation cp="🧗🏼‍♀" type="tts">woman climbing: medium-light skin tone</annotation>
+		<annotation cp="🧗🏽‍♀">climber | medium skin tone | woman climbing</annotation>
+		<annotation cp="🧗🏽‍♀" type="tts">woman climbing: medium skin tone</annotation>
+		<annotation cp="🧗🏾‍♀">climber | medium-dark skin tone | woman climbing</annotation>
+		<annotation cp="🧗🏾‍♀" type="tts">woman climbing: medium-dark skin tone</annotation>
+		<annotation cp="🧗🏿‍♀">climber | dark skin tone | woman climbing</annotation>
+		<annotation cp="🧗🏿‍♀" type="tts">woman climbing: dark skin tone</annotation>
+		<annotation cp="🏇🏻">horse | jockey | light skin tone | racehorse | racing</annotation>
+		<annotation cp="🏇🏻" type="tts">horse racing: light skin tone</annotation>
+		<annotation cp="🏇🏼">horse | jockey | medium-light skin tone | racehorse | racing</annotation>
+		<annotation cp="🏇🏼" type="tts">horse racing: medium-light skin tone</annotation>
+		<annotation cp="🏇🏽">horse | jockey | medium skin tone | racehorse | racing</annotation>
+		<annotation cp="🏇🏽" type="tts">horse racing: medium skin tone</annotation>
+		<annotation cp="🏇🏾">horse | jockey | medium-dark skin tone | racehorse | racing</annotation>
+		<annotation cp="🏇🏾" type="tts">horse racing: medium-dark skin tone</annotation>
+		<annotation cp="🏇🏿">dark skin tone | horse | jockey | racehorse | racing</annotation>
+		<annotation cp="🏇🏿" type="tts">horse racing: dark skin tone</annotation>
+		<annotation cp="🏂🏻">light skin tone | ski | snow | snowboard | snowboarder</annotation>
+		<annotation cp="🏂🏻" type="tts">snowboarder: light skin tone</annotation>
+		<annotation cp="🏂🏼">medium-light skin tone | ski | snow | snowboard | snowboarder</annotation>
+		<annotation cp="🏂🏼" type="tts">snowboarder: medium-light skin tone</annotation>
+		<annotation cp="🏂🏽">medium skin tone | ski | snow | snowboard | snowboarder</annotation>
+		<annotation cp="🏂🏽" type="tts">snowboarder: medium skin tone</annotation>
+		<annotation cp="🏂🏾">medium-dark skin tone | ski | snow | snowboard | snowboarder</annotation>
+		<annotation cp="🏂🏾" type="tts">snowboarder: medium-dark skin tone</annotation>
+		<annotation cp="🏂🏿">dark skin tone | ski | snow | snowboard | snowboarder</annotation>
+		<annotation cp="🏂🏿" type="tts">snowboarder: dark skin tone</annotation>
+		<annotation cp="🏌🏻">ball | golf | light skin tone | person golfing</annotation>
+		<annotation cp="🏌🏻" type="tts">person golfing: light skin tone</annotation>
+		<annotation cp="🏌🏼">ball | golf | medium-light skin tone | person golfing</annotation>
+		<annotation cp="🏌🏼" type="tts">person golfing: medium-light skin tone</annotation>
+		<annotation cp="🏌🏽">ball | golf | medium skin tone | person golfing</annotation>
+		<annotation cp="🏌🏽" type="tts">person golfing: medium skin tone</annotation>
+		<annotation cp="🏌🏾">ball | golf | medium-dark skin tone | person golfing</annotation>
+		<annotation cp="🏌🏾" type="tts">person golfing: medium-dark skin tone</annotation>
+		<annotation cp="🏌🏿">ball | dark skin tone | golf | person golfing</annotation>
+		<annotation cp="🏌🏿" type="tts">person golfing: dark skin tone</annotation>
+		<annotation cp="🏌🏻‍♂">golf | light skin tone | man | man golfing</annotation>
+		<annotation cp="🏌🏻‍♂" type="tts">man golfing: light skin tone</annotation>
+		<annotation cp="🏌🏼‍♂">golf | man | man golfing | medium-light skin tone</annotation>
+		<annotation cp="🏌🏼‍♂" type="tts">man golfing: medium-light skin tone</annotation>
+		<annotation cp="🏌🏽‍♂">golf | man | man golfing | medium skin tone</annotation>
+		<annotation cp="🏌🏽‍♂" type="tts">man golfing: medium skin tone</annotation>
+		<annotation cp="🏌🏾‍♂">golf | man | man golfing | medium-dark skin tone</annotation>
+		<annotation cp="🏌🏾‍♂" type="tts">man golfing: medium-dark skin tone</annotation>
+		<annotation cp="🏌🏿‍♂">dark skin tone | golf | man | man golfing</annotation>
+		<annotation cp="🏌🏿‍♂" type="tts">man golfing: dark skin tone</annotation>
+		<annotation cp="🏌🏻‍♀">golf | light skin tone | woman | woman golfing</annotation>
+		<annotation cp="🏌🏻‍♀" type="tts">woman golfing: light skin tone</annotation>
+		<annotation cp="🏌🏼‍♀">golf | medium-light skin tone | woman | woman golfing</annotation>
+		<annotation cp="🏌🏼‍♀" type="tts">woman golfing: medium-light skin tone</annotation>
+		<annotation cp="🏌🏽‍♀">golf | medium skin tone | woman | woman golfing</annotation>
+		<annotation cp="🏌🏽‍♀" type="tts">woman golfing: medium skin tone</annotation>
+		<annotation cp="🏌🏾‍♀">golf | medium-dark skin tone | woman | woman golfing</annotation>
+		<annotation cp="🏌🏾‍♀" type="tts">woman golfing: medium-dark skin tone</annotation>
+		<annotation cp="🏌🏿‍♀">dark skin tone | golf | woman | woman golfing</annotation>
+		<annotation cp="🏌🏿‍♀" type="tts">woman golfing: dark skin tone</annotation>
+		<annotation cp="🏄🏻">light skin tone | person surfing | surfing</annotation>
+		<annotation cp="🏄🏻" type="tts">person surfing: light skin tone</annotation>
+		<annotation cp="🏄🏼">medium-light skin tone | person surfing | surfing</annotation>
+		<annotation cp="🏄🏼" type="tts">person surfing: medium-light skin tone</annotation>
+		<annotation cp="🏄🏽">medium skin tone | person surfing | surfing</annotation>
+		<annotation cp="🏄🏽" type="tts">person surfing: medium skin tone</annotation>
+		<annotation cp="🏄🏾">medium-dark skin tone | person surfing | surfing</annotation>
+		<annotation cp="🏄🏾" type="tts">person surfing: medium-dark skin tone</annotation>
+		<annotation cp="🏄🏿">dark skin tone | person surfing | surfing</annotation>
+		<annotation cp="🏄🏿" type="tts">person surfing: dark skin tone</annotation>
+		<annotation cp="🏄🏻‍♂">light skin tone | man | surfing</annotation>
+		<annotation cp="🏄🏻‍♂" type="tts">man surfing: light skin tone</annotation>
+		<annotation cp="🏄🏼‍♂">man | medium-light skin tone | surfing</annotation>
+		<annotation cp="🏄🏼‍♂" type="tts">man surfing: medium-light skin tone</annotation>
+		<annotation cp="🏄🏽‍♂">man | medium skin tone | surfing</annotation>
+		<annotation cp="🏄🏽‍♂" type="tts">man surfing: medium skin tone</annotation>
+		<annotation cp="🏄🏾‍♂">man | medium-dark skin tone | surfing</annotation>
+		<annotation cp="🏄🏾‍♂" type="tts">man surfing: medium-dark skin tone</annotation>
+		<annotation cp="🏄🏿‍♂">dark skin tone | man | surfing</annotation>
+		<annotation cp="🏄🏿‍♂" type="tts">man surfing: dark skin tone</annotation>
+		<annotation cp="🏄🏻‍♀">light skin tone | surfing | woman</annotation>
+		<annotation cp="🏄🏻‍♀" type="tts">woman surfing: light skin tone</annotation>
+		<annotation cp="🏄🏼‍♀">medium-light skin tone | surfing | woman</annotation>
+		<annotation cp="🏄🏼‍♀" type="tts">woman surfing: medium-light skin tone</annotation>
+		<annotation cp="🏄🏽‍♀">medium skin tone | surfing | woman</annotation>
+		<annotation cp="🏄🏽‍♀" type="tts">woman surfing: medium skin tone</annotation>
+		<annotation cp="🏄🏾‍♀">medium-dark skin tone | surfing | woman</annotation>
+		<annotation cp="🏄🏾‍♀" type="tts">woman surfing: medium-dark skin tone</annotation>
+		<annotation cp="🏄🏿‍♀">dark skin tone | surfing | woman</annotation>
+		<annotation cp="🏄🏿‍♀" type="tts">woman surfing: dark skin tone</annotation>
+		<annotation cp="🚣🏻">boat | light skin tone | person rowing boat | rowboat</annotation>
+		<annotation cp="🚣🏻" type="tts">person rowing boat: light skin tone</annotation>
+		<annotation cp="🚣🏼">boat | medium-light skin tone | person rowing boat | rowboat</annotation>
+		<annotation cp="🚣🏼" type="tts">person rowing boat: medium-light skin tone</annotation>
+		<annotation cp="🚣🏽">boat | medium skin tone | person rowing boat | rowboat</annotation>
+		<annotation cp="🚣🏽" type="tts">person rowing boat: medium skin tone</annotation>
+		<annotation cp="🚣🏾">boat | medium-dark skin tone | person rowing boat | rowboat</annotation>
+		<annotation cp="🚣🏾" type="tts">person rowing boat: medium-dark skin tone</annotation>
+		<annotation cp="🚣🏿">boat | dark skin tone | person rowing boat | rowboat</annotation>
+		<annotation cp="🚣🏿" type="tts">person rowing boat: dark skin tone</annotation>
+		<annotation cp="🚣🏻‍♂">boat | light skin tone | man | man rowing boat | rowboat</annotation>
+		<annotation cp="🚣🏻‍♂" type="tts">man rowing boat: light skin tone</annotation>
+		<annotation cp="🚣🏼‍♂">boat | man | man rowing boat | medium-light skin tone | rowboat</annotation>
+		<annotation cp="🚣🏼‍♂" type="tts">man rowing boat: medium-light skin tone</annotation>
+		<annotation cp="🚣🏽‍♂">boat | man | man rowing boat | medium skin tone | rowboat</annotation>
+		<annotation cp="🚣🏽‍♂" type="tts">man rowing boat: medium skin tone</annotation>
+		<annotation cp="🚣🏾‍♂">boat | man | man rowing boat | medium-dark skin tone | rowboat</annotation>
+		<annotation cp="🚣🏾‍♂" type="tts">man rowing boat: medium-dark skin tone</annotation>
+		<annotation cp="🚣🏿‍♂">boat | dark skin tone | man | man rowing boat | rowboat</annotation>
+		<annotation cp="🚣🏿‍♂" type="tts">man rowing boat: dark skin tone</annotation>
+		<annotation cp="🚣🏻‍♀">boat | light skin tone | rowboat | woman | woman rowing boat</annotation>
+		<annotation cp="🚣🏻‍♀" type="tts">woman rowing boat: light skin tone</annotation>
+		<annotation cp="🚣🏼‍♀">boat | medium-light skin tone | rowboat | woman | woman rowing boat</annotation>
+		<annotation cp="🚣🏼‍♀" type="tts">woman rowing boat: medium-light skin tone</annotation>
+		<annotation cp="🚣🏽‍♀">boat | medium skin tone | rowboat | woman | woman rowing boat</annotation>
+		<annotation cp="🚣🏽‍♀" type="tts">woman rowing boat: medium skin tone</annotation>
+		<annotation cp="🚣🏾‍♀">boat | medium-dark skin tone | rowboat | woman | woman rowing boat</annotation>
+		<annotation cp="🚣🏾‍♀" type="tts">woman rowing boat: medium-dark skin tone</annotation>
+		<annotation cp="🚣🏿‍♀">boat | dark skin tone | rowboat | woman | woman rowing boat</annotation>
+		<annotation cp="🚣🏿‍♀" type="tts">woman rowing boat: dark skin tone</annotation>
+		<annotation cp="🏊🏻">light skin tone | person swimming | swim</annotation>
+		<annotation cp="🏊🏻" type="tts">person swimming: light skin tone</annotation>
+		<annotation cp="🏊🏼">medium-light skin tone | person swimming | swim</annotation>
+		<annotation cp="🏊🏼" type="tts">person swimming: medium-light skin tone</annotation>
+		<annotation cp="🏊🏽">medium skin tone | person swimming | swim</annotation>
+		<annotation cp="🏊🏽" type="tts">person swimming: medium skin tone</annotation>
+		<annotation cp="🏊🏾">medium-dark skin tone | person swimming | swim</annotation>
+		<annotation cp="🏊🏾" type="tts">person swimming: medium-dark skin tone</annotation>
+		<annotation cp="🏊🏿">dark skin tone | person swimming | swim</annotation>
+		<annotation cp="🏊🏿" type="tts">person swimming: dark skin tone</annotation>
+		<annotation cp="🏊🏻‍♂">light skin tone | man | man swimming | swim</annotation>
+		<annotation cp="🏊🏻‍♂" type="tts">man swimming: light skin tone</annotation>
+		<annotation cp="🏊🏼‍♂">man | man swimming | medium-light skin tone | swim</annotation>
+		<annotation cp="🏊🏼‍♂" type="tts">man swimming: medium-light skin tone</annotation>
+		<annotation cp="🏊🏽‍♂">man | man swimming | medium skin tone | swim</annotation>
+		<annotation cp="🏊🏽‍♂" type="tts">man swimming: medium skin tone</annotation>
+		<annotation cp="🏊🏾‍♂">man | man swimming | medium-dark skin tone | swim</annotation>
+		<annotation cp="🏊🏾‍♂" type="tts">man swimming: medium-dark skin tone</annotation>
+		<annotation cp="🏊🏿‍♂">dark skin tone | man | man swimming | swim</annotation>
+		<annotation cp="🏊🏿‍♂" type="tts">man swimming: dark skin tone</annotation>
+		<annotation cp="🏊🏻‍♀">light skin tone | swim | woman | woman swimming</annotation>
+		<annotation cp="🏊🏻‍♀" type="tts">woman swimming: light skin tone</annotation>
+		<annotation cp="🏊🏼‍♀">medium-light skin tone | swim | woman | woman swimming</annotation>
+		<annotation cp="🏊🏼‍♀" type="tts">woman swimming: medium-light skin tone</annotation>
+		<annotation cp="🏊🏽‍♀">medium skin tone | swim | woman | woman swimming</annotation>
+		<annotation cp="🏊🏽‍♀" type="tts">woman swimming: medium skin tone</annotation>
+		<annotation cp="🏊🏾‍♀">medium-dark skin tone | swim | woman | woman swimming</annotation>
+		<annotation cp="🏊🏾‍♀" type="tts">woman swimming: medium-dark skin tone</annotation>
+		<annotation cp="🏊🏿‍♀">dark skin tone | swim | woman | woman swimming</annotation>
+		<annotation cp="🏊🏿‍♀" type="tts">woman swimming: dark skin tone</annotation>
+		<annotation cp="⛹🏻">ball | light skin tone | person bouncing ball</annotation>
+		<annotation cp="⛹🏻" type="tts">person bouncing ball: light skin tone</annotation>
+		<annotation cp="⛹🏼">ball | medium-light skin tone | person bouncing ball</annotation>
+		<annotation cp="⛹🏼" type="tts">person bouncing ball: medium-light skin tone</annotation>
+		<annotation cp="⛹🏽">ball | medium skin tone | person bouncing ball</annotation>
+		<annotation cp="⛹🏽" type="tts">person bouncing ball: medium skin tone</annotation>
+		<annotation cp="⛹🏾">ball | medium-dark skin tone | person bouncing ball</annotation>
+		<annotation cp="⛹🏾" type="tts">person bouncing ball: medium-dark skin tone</annotation>
+		<annotation cp="⛹🏿">ball | dark skin tone | person bouncing ball</annotation>
+		<annotation cp="⛹🏿" type="tts">person bouncing ball: dark skin tone</annotation>
+		<annotation cp="⛹🏻‍♂">ball | light skin tone | man | man bouncing ball</annotation>
+		<annotation cp="⛹🏻‍♂" type="tts">man bouncing ball: light skin tone</annotation>
+		<annotation cp="⛹🏼‍♂">ball | man | man bouncing ball | medium-light skin tone</annotation>
+		<annotation cp="⛹🏼‍♂" type="tts">man bouncing ball: medium-light skin tone</annotation>
+		<annotation cp="⛹🏽‍♂">ball | man | man bouncing ball | medium skin tone</annotation>
+		<annotation cp="⛹🏽‍♂" type="tts">man bouncing ball: medium skin tone</annotation>
+		<annotation cp="⛹🏾‍♂">ball | man | man bouncing ball | medium-dark skin tone</annotation>
+		<annotation cp="⛹🏾‍♂" type="tts">man bouncing ball: medium-dark skin tone</annotation>
+		<annotation cp="⛹🏿‍♂">ball | dark skin tone | man | man bouncing ball</annotation>
+		<annotation cp="⛹🏿‍♂" type="tts">man bouncing ball: dark skin tone</annotation>
+		<annotation cp="⛹🏻‍♀">ball | light skin tone | woman | woman bouncing ball</annotation>
+		<annotation cp="⛹🏻‍♀" type="tts">woman bouncing ball: light skin tone</annotation>
+		<annotation cp="⛹🏼‍♀">ball | medium-light skin tone | woman | woman bouncing ball</annotation>
+		<annotation cp="⛹🏼‍♀" type="tts">woman bouncing ball: medium-light skin tone</annotation>
+		<annotation cp="⛹🏽‍♀">ball | medium skin tone | woman | woman bouncing ball</annotation>
+		<annotation cp="⛹🏽‍♀" type="tts">woman bouncing ball: medium skin tone</annotation>
+		<annotation cp="⛹🏾‍♀">ball | medium-dark skin tone | woman | woman bouncing ball</annotation>
+		<annotation cp="⛹🏾‍♀" type="tts">woman bouncing ball: medium-dark skin tone</annotation>
+		<annotation cp="⛹🏿‍♀">ball | dark skin tone | woman | woman bouncing ball</annotation>
+		<annotation cp="⛹🏿‍♀" type="tts">woman bouncing ball: dark skin tone</annotation>
+		<annotation cp="🏋🏻">lifter | light skin tone | person lifting weights | weight</annotation>
+		<annotation cp="🏋🏻" type="tts">person lifting weights: light skin tone</annotation>
+		<annotation cp="🏋🏼">lifter | medium-light skin tone | person lifting weights | weight</annotation>
+		<annotation cp="🏋🏼" type="tts">person lifting weights: medium-light skin tone</annotation>
+		<annotation cp="🏋🏽">lifter | medium skin tone | person lifting weights | weight</annotation>
+		<annotation cp="🏋🏽" type="tts">person lifting weights: medium skin tone</annotation>
+		<annotation cp="🏋🏾">lifter | medium-dark skin tone | person lifting weights | weight</annotation>
+		<annotation cp="🏋🏾" type="tts">person lifting weights: medium-dark skin tone</annotation>
+		<annotation cp="🏋🏿">dark skin tone | lifter | person lifting weights | weight</annotation>
+		<annotation cp="🏋🏿" type="tts">person lifting weights: dark skin tone</annotation>
+		<annotation cp="🏋🏻‍♂">light skin tone | man | man lifting weights | weight lifter</annotation>
+		<annotation cp="🏋🏻‍♂" type="tts">man lifting weights: light skin tone</annotation>
+		<annotation cp="🏋🏼‍♂">man | man lifting weights | medium-light skin tone | weight lifter</annotation>
+		<annotation cp="🏋🏼‍♂" type="tts">man lifting weights: medium-light skin tone</annotation>
+		<annotation cp="🏋🏽‍♂">man | man lifting weights | medium skin tone | weight lifter</annotation>
+		<annotation cp="🏋🏽‍♂" type="tts">man lifting weights: medium skin tone</annotation>
+		<annotation cp="🏋🏾‍♂">man | man lifting weights | medium-dark skin tone | weight lifter</annotation>
+		<annotation cp="🏋🏾‍♂" type="tts">man lifting weights: medium-dark skin tone</annotation>
+		<annotation cp="🏋🏿‍♂">dark skin tone | man | man lifting weights | weight lifter</annotation>
+		<annotation cp="🏋🏿‍♂" type="tts">man lifting weights: dark skin tone</annotation>
+		<annotation cp="🏋🏻‍♀">light skin tone | weight lifter | woman | woman lifting weights</annotation>
+		<annotation cp="🏋🏻‍♀" type="tts">woman lifting weights: light skin tone</annotation>
+		<annotation cp="🏋🏼‍♀">medium-light skin tone | weight lifter | woman | woman lifting weights</annotation>
+		<annotation cp="🏋🏼‍♀" type="tts">woman lifting weights: medium-light skin tone</annotation>
+		<annotation cp="🏋🏽‍♀">medium skin tone | weight lifter | woman | woman lifting weights</annotation>
+		<annotation cp="🏋🏽‍♀" type="tts">woman lifting weights: medium skin tone</annotation>
+		<annotation cp="🏋🏾‍♀">medium-dark skin tone | weight lifter | woman | woman lifting weights</annotation>
+		<annotation cp="🏋🏾‍♀" type="tts">woman lifting weights: medium-dark skin tone</annotation>
+		<annotation cp="🏋🏿‍♀">dark skin tone | weight lifter | woman | woman lifting weights</annotation>
+		<annotation cp="🏋🏿‍♀" type="tts">woman lifting weights: dark skin tone</annotation>
+		<annotation cp="🚴🏻">bicycle | biking | cyclist | light skin tone | person biking</annotation>
+		<annotation cp="🚴🏻" type="tts">person biking: light skin tone</annotation>
+		<annotation cp="🚴🏼">bicycle | biking | cyclist | medium-light skin tone | person biking</annotation>
+		<annotation cp="🚴🏼" type="tts">person biking: medium-light skin tone</annotation>
+		<annotation cp="🚴🏽">bicycle | biking | cyclist | medium skin tone | person biking</annotation>
+		<annotation cp="🚴🏽" type="tts">person biking: medium skin tone</annotation>
+		<annotation cp="🚴🏾">bicycle | biking | cyclist | medium-dark skin tone | person biking</annotation>
+		<annotation cp="🚴🏾" type="tts">person biking: medium-dark skin tone</annotation>
+		<annotation cp="🚴🏿">bicycle | biking | cyclist | dark skin tone | person biking</annotation>
+		<annotation cp="🚴🏿" type="tts">person biking: dark skin tone</annotation>
+		<annotation cp="🚴🏻‍♂">bicycle | biking | cyclist | light skin tone | man</annotation>
+		<annotation cp="🚴🏻‍♂" type="tts">man biking: light skin tone</annotation>
+		<annotation cp="🚴🏼‍♂">bicycle | biking | cyclist | man | medium-light skin tone</annotation>
+		<annotation cp="🚴🏼‍♂" type="tts">man biking: medium-light skin tone</annotation>
+		<annotation cp="🚴🏽‍♂">bicycle | biking | cyclist | man | medium skin tone</annotation>
+		<annotation cp="🚴🏽‍♂" type="tts">man biking: medium skin tone</annotation>
+		<annotation cp="🚴🏾‍♂">bicycle | biking | cyclist | man | medium-dark skin tone</annotation>
+		<annotation cp="🚴🏾‍♂" type="tts">man biking: medium-dark skin tone</annotation>
+		<annotation cp="🚴🏿‍♂">bicycle | biking | cyclist | dark skin tone | man</annotation>
+		<annotation cp="🚴🏿‍♂" type="tts">man biking: dark skin tone</annotation>
+		<annotation cp="🚴🏻‍♀">bicycle | biking | cyclist | light skin tone | woman</annotation>
+		<annotation cp="🚴🏻‍♀" type="tts">woman biking: light skin tone</annotation>
+		<annotation cp="🚴🏼‍♀">bicycle | biking | cyclist | medium-light skin tone | woman</annotation>
+		<annotation cp="🚴🏼‍♀" type="tts">woman biking: medium-light skin tone</annotation>
+		<annotation cp="🚴🏽‍♀">bicycle | biking | cyclist | medium skin tone | woman</annotation>
+		<annotation cp="🚴🏽‍♀" type="tts">woman biking: medium skin tone</annotation>
+		<annotation cp="🚴🏾‍♀">bicycle | biking | cyclist | medium-dark skin tone | woman</annotation>
+		<annotation cp="🚴🏾‍♀" type="tts">woman biking: medium-dark skin tone</annotation>
+		<annotation cp="🚴🏿‍♀">bicycle | biking | cyclist | dark skin tone | woman</annotation>
+		<annotation cp="🚴🏿‍♀" type="tts">woman biking: dark skin tone</annotation>
+		<annotation cp="🚵🏻">bicycle | bicyclist | bike | cyclist | light skin tone | mountain | person mountain biking</annotation>
+		<annotation cp="🚵🏻" type="tts">person mountain biking: light skin tone</annotation>
+		<annotation cp="🚵🏼">bicycle | bicyclist | bike | cyclist | medium-light skin tone | mountain | person mountain biking</annotation>
+		<annotation cp="🚵🏼" type="tts">person mountain biking: medium-light skin tone</annotation>
+		<annotation cp="🚵🏽">bicycle | bicyclist | bike | cyclist | medium skin tone | mountain | person mountain biking</annotation>
+		<annotation cp="🚵🏽" type="tts">person mountain biking: medium skin tone</annotation>
+		<annotation cp="🚵🏾">bicycle | bicyclist | bike | cyclist | medium-dark skin tone | mountain | person mountain biking</annotation>
+		<annotation cp="🚵🏾" type="tts">person mountain biking: medium-dark skin tone</annotation>
+		<annotation cp="🚵🏿">bicycle | bicyclist | bike | cyclist | dark skin tone | mountain | person mountain biking</annotation>
+		<annotation cp="🚵🏿" type="tts">person mountain biking: dark skin tone</annotation>
+		<annotation cp="🚵🏻‍♂">bicycle | bike | cyclist | light skin tone | man | man mountain biking | mountain</annotation>
+		<annotation cp="🚵🏻‍♂" type="tts">man mountain biking: light skin tone</annotation>
+		<annotation cp="🚵🏼‍♂">bicycle | bike | cyclist | man | man mountain biking | medium-light skin tone | mountain</annotation>
+		<annotation cp="🚵🏼‍♂" type="tts">man mountain biking: medium-light skin tone</annotation>
+		<annotation cp="🚵🏽‍♂">bicycle | bike | cyclist | man | man mountain biking | medium skin tone | mountain</annotation>
+		<annotation cp="🚵🏽‍♂" type="tts">man mountain biking: medium skin tone</annotation>
+		<annotation cp="🚵🏾‍♂">bicycle | bike | cyclist | man | man mountain biking | medium-dark skin tone | mountain</annotation>
+		<annotation cp="🚵🏾‍♂" type="tts">man mountain biking: medium-dark skin tone</annotation>
+		<annotation cp="🚵🏿‍♂">bicycle | bike | cyclist | dark skin tone | man | man mountain biking | mountain</annotation>
+		<annotation cp="🚵🏿‍♂" type="tts">man mountain biking: dark skin tone</annotation>
+		<annotation cp="🚵🏻‍♀">bicycle | bike | biking | cyclist | light skin tone | mountain | woman</annotation>
+		<annotation cp="🚵🏻‍♀" type="tts">woman mountain biking: light skin tone</annotation>
+		<annotation cp="🚵🏼‍♀">bicycle | bike | biking | cyclist | medium-light skin tone | mountain | woman</annotation>
+		<annotation cp="🚵🏼‍♀" type="tts">woman mountain biking: medium-light skin tone</annotation>
+		<annotation cp="🚵🏽‍♀">bicycle | bike | biking | cyclist | medium skin tone | mountain | woman</annotation>
+		<annotation cp="🚵🏽‍♀" type="tts">woman mountain biking: medium skin tone</annotation>
+		<annotation cp="🚵🏾‍♀">bicycle | bike | biking | cyclist | medium-dark skin tone | mountain | woman</annotation>
+		<annotation cp="🚵🏾‍♀" type="tts">woman mountain biking: medium-dark skin tone</annotation>
+		<annotation cp="🚵🏿‍♀">bicycle | bike | biking | cyclist | dark skin tone | mountain | woman</annotation>
+		<annotation cp="🚵🏿‍♀" type="tts">woman mountain biking: dark skin tone</annotation>
+		<annotation cp="🤸🏻">cartwheel | gymnastics | light skin tone | person cartwheeling</annotation>
+		<annotation cp="🤸🏻" type="tts">person cartwheeling: light skin tone</annotation>
+		<annotation cp="🤸🏼">cartwheel | gymnastics | medium-light skin tone | person cartwheeling</annotation>
+		<annotation cp="🤸🏼" type="tts">person cartwheeling: medium-light skin tone</annotation>
+		<annotation cp="🤸🏽">cartwheel | gymnastics | medium skin tone | person cartwheeling</annotation>
+		<annotation cp="🤸🏽" type="tts">person cartwheeling: medium skin tone</annotation>
+		<annotation cp="🤸🏾">cartwheel | gymnastics | medium-dark skin tone | person cartwheeling</annotation>
+		<annotation cp="🤸🏾" type="tts">person cartwheeling: medium-dark skin tone</annotation>
+		<annotation cp="🤸🏿">cartwheel | dark skin tone | gymnastics | person cartwheeling</annotation>
+		<annotation cp="🤸🏿" type="tts">person cartwheeling: dark skin tone</annotation>
+		<annotation cp="🤸🏻‍♂">cartwheel | gymnastics | light skin tone | man | man cartwheeling</annotation>
+		<annotation cp="🤸🏻‍♂" type="tts">man cartwheeling: light skin tone</annotation>
+		<annotation cp="🤸🏼‍♂">cartwheel | gymnastics | man | man cartwheeling | medium-light skin tone</annotation>
+		<annotation cp="🤸🏼‍♂" type="tts">man cartwheeling: medium-light skin tone</annotation>
+		<annotation cp="🤸🏽‍♂">cartwheel | gymnastics | man | man cartwheeling | medium skin tone</annotation>
+		<annotation cp="🤸🏽‍♂" type="tts">man cartwheeling: medium skin tone</annotation>
+		<annotation cp="🤸🏾‍♂">cartwheel | gymnastics | man | man cartwheeling | medium-dark skin tone</annotation>
+		<annotation cp="🤸🏾‍♂" type="tts">man cartwheeling: medium-dark skin tone</annotation>
+		<annotation cp="🤸🏿‍♂">cartwheel | dark skin tone | gymnastics | man | man cartwheeling</annotation>
+		<annotation cp="🤸🏿‍♂" type="tts">man cartwheeling: dark skin tone</annotation>
+		<annotation cp="🤸🏻‍♀">cartwheel | gymnastics | light skin tone | woman | woman cartwheeling</annotation>
+		<annotation cp="🤸🏻‍♀" type="tts">woman cartwheeling: light skin tone</annotation>
+		<annotation cp="🤸🏼‍♀">cartwheel | gymnastics | medium-light skin tone | woman | woman cartwheeling</annotation>
+		<annotation cp="🤸🏼‍♀" type="tts">woman cartwheeling: medium-light skin tone</annotation>
+		<annotation cp="🤸🏽‍♀">cartwheel | gymnastics | medium skin tone | woman | woman cartwheeling</annotation>
+		<annotation cp="🤸🏽‍♀" type="tts">woman cartwheeling: medium skin tone</annotation>
+		<annotation cp="🤸🏾‍♀">cartwheel | gymnastics | medium-dark skin tone | woman | woman cartwheeling</annotation>
+		<annotation cp="🤸🏾‍♀" type="tts">woman cartwheeling: medium-dark skin tone</annotation>
+		<annotation cp="🤸🏿‍♀">cartwheel | dark skin tone | gymnastics | woman | woman cartwheeling</annotation>
+		<annotation cp="🤸🏿‍♀" type="tts">woman cartwheeling: dark skin tone</annotation>
+		<annotation cp="🤽🏻">light skin tone | person playing water polo | polo | water</annotation>
+		<annotation cp="🤽🏻" type="tts">person playing water polo: light skin tone</annotation>
+		<annotation cp="🤽🏼">medium-light skin tone | person playing water polo | polo | water</annotation>
+		<annotation cp="🤽🏼" type="tts">person playing water polo: medium-light skin tone</annotation>
+		<annotation cp="🤽🏽">medium skin tone | person playing water polo | polo | water</annotation>
+		<annotation cp="🤽🏽" type="tts">person playing water polo: medium skin tone</annotation>
+		<annotation cp="🤽🏾">medium-dark skin tone | person playing water polo | polo | water</annotation>
+		<annotation cp="🤽🏾" type="tts">person playing water polo: medium-dark skin tone</annotation>
+		<annotation cp="🤽🏿">dark skin tone | person playing water polo | polo | water</annotation>
+		<annotation cp="🤽🏿" type="tts">person playing water polo: dark skin tone</annotation>
+		<annotation cp="🤽🏻‍♂">light skin tone | man | man playing water polo | water polo</annotation>
+		<annotation cp="🤽🏻‍♂" type="tts">man playing water polo: light skin tone</annotation>
+		<annotation cp="🤽🏼‍♂">man | man playing water polo | medium-light skin tone | water polo</annotation>
+		<annotation cp="🤽🏼‍♂" type="tts">man playing water polo: medium-light skin tone</annotation>
+		<annotation cp="🤽🏽‍♂">man | man playing water polo | medium skin tone | water polo</annotation>
+		<annotation cp="🤽🏽‍♂" type="tts">man playing water polo: medium skin tone</annotation>
+		<annotation cp="🤽🏾‍♂">man | man playing water polo | medium-dark skin tone | water polo</annotation>
+		<annotation cp="🤽🏾‍♂" type="tts">man playing water polo: medium-dark skin tone</annotation>
+		<annotation cp="🤽🏿‍♂">dark skin tone | man | man playing water polo | water polo</annotation>
+		<annotation cp="🤽🏿‍♂" type="tts">man playing water polo: dark skin tone</annotation>
+		<annotation cp="🤽🏻‍♀">light skin tone | water polo | woman | woman playing water polo</annotation>
+		<annotation cp="🤽🏻‍♀" type="tts">woman playing water polo: light skin tone</annotation>
+		<annotation cp="🤽🏼‍♀">medium-light skin tone | water polo | woman | woman playing water polo</annotation>
+		<annotation cp="🤽🏼‍♀" type="tts">woman playing water polo: medium-light skin tone</annotation>
+		<annotation cp="🤽🏽‍♀">medium skin tone | water polo | woman | woman playing water polo</annotation>
+		<annotation cp="🤽🏽‍♀" type="tts">woman playing water polo: medium skin tone</annotation>
+		<annotation cp="🤽🏾‍♀">medium-dark skin tone | water polo | woman | woman playing water polo</annotation>
+		<annotation cp="🤽🏾‍♀" type="tts">woman playing water polo: medium-dark skin tone</annotation>
+		<annotation cp="🤽🏿‍♀">dark skin tone | water polo | woman | woman playing water polo</annotation>
+		<annotation cp="🤽🏿‍♀" type="tts">woman playing water polo: dark skin tone</annotation>
+		<annotation cp="🤾🏻">ball | handball | light skin tone | person playing handball</annotation>
+		<annotation cp="🤾🏻" type="tts">person playing handball: light skin tone</annotation>
+		<annotation cp="🤾🏼">ball | handball | medium-light skin tone | person playing handball</annotation>
+		<annotation cp="🤾🏼" type="tts">person playing handball: medium-light skin tone</annotation>
+		<annotation cp="🤾🏽">ball | handball | medium skin tone | person playing handball</annotation>
+		<annotation cp="🤾🏽" type="tts">person playing handball: medium skin tone</annotation>
+		<annotation cp="🤾🏾">ball | handball | medium-dark skin tone | person playing handball</annotation>
+		<annotation cp="🤾🏾" type="tts">person playing handball: medium-dark skin tone</annotation>
+		<annotation cp="🤾🏿">ball | dark skin tone | handball | person playing handball</annotation>
+		<annotation cp="🤾🏿" type="tts">person playing handball: dark skin tone</annotation>
+		<annotation cp="🤾🏻‍♂">handball | light skin tone | man | man playing handball</annotation>
+		<annotation cp="🤾🏻‍♂" type="tts">man playing handball: light skin tone</annotation>
+		<annotation cp="🤾🏼‍♂">handball | man | man playing handball | medium-light skin tone</annotation>
+		<annotation cp="🤾🏼‍♂" type="tts">man playing handball: medium-light skin tone</annotation>
+		<annotation cp="🤾🏽‍♂">handball | man | man playing handball | medium skin tone</annotation>
+		<annotation cp="🤾🏽‍♂" type="tts">man playing handball: medium skin tone</annotation>
+		<annotation cp="🤾🏾‍♂">handball | man | man playing handball | medium-dark skin tone</annotation>
+		<annotation cp="🤾🏾‍♂" type="tts">man playing handball: medium-dark skin tone</annotation>
+		<annotation cp="🤾🏿‍♂">dark skin tone | handball | man | man playing handball</annotation>
+		<annotation cp="🤾🏿‍♂" type="tts">man playing handball: dark skin tone</annotation>
+		<annotation cp="🤾🏻‍♀">handball | light skin tone | woman | woman playing handball</annotation>
+		<annotation cp="🤾🏻‍♀" type="tts">woman playing handball: light skin tone</annotation>
+		<annotation cp="🤾🏼‍♀">handball | medium-light skin tone | woman | woman playing handball</annotation>
+		<annotation cp="🤾🏼‍♀" type="tts">woman playing handball: medium-light skin tone</annotation>
+		<annotation cp="🤾🏽‍♀">handball | medium skin tone | woman | woman playing handball</annotation>
+		<annotation cp="🤾🏽‍♀" type="tts">woman playing handball: medium skin tone</annotation>
+		<annotation cp="🤾🏾‍♀">handball | medium-dark skin tone | woman | woman playing handball</annotation>
+		<annotation cp="🤾🏾‍♀" type="tts">woman playing handball: medium-dark skin tone</annotation>
+		<annotation cp="🤾🏿‍♀">dark skin tone | handball | woman | woman playing handball</annotation>
+		<annotation cp="🤾🏿‍♀" type="tts">woman playing handball: dark skin tone</annotation>
+		<annotation cp="🤹🏻">balance | juggle | light skin tone | multitask | person juggling | skill</annotation>
+		<annotation cp="🤹🏻" type="tts">person juggling: light skin tone</annotation>
+		<annotation cp="🤹🏼">balance | juggle | medium-light skin tone | multitask | person juggling | skill</annotation>
+		<annotation cp="🤹🏼" type="tts">person juggling: medium-light skin tone</annotation>
+		<annotation cp="🤹🏽">balance | juggle | medium skin tone | multitask | person juggling | skill</annotation>
+		<annotation cp="🤹🏽" type="tts">person juggling: medium skin tone</annotation>
+		<annotation cp="🤹🏾">balance | juggle | medium-dark skin tone | multitask | person juggling | skill</annotation>
+		<annotation cp="🤹🏾" type="tts">person juggling: medium-dark skin tone</annotation>
+		<annotation cp="🤹🏿">balance | dark skin tone | juggle | multitask | person juggling | skill</annotation>
+		<annotation cp="🤹🏿" type="tts">person juggling: dark skin tone</annotation>
+		<annotation cp="🤹🏻‍♂">juggling | light skin tone | man | multitask</annotation>
+		<annotation cp="🤹🏻‍♂" type="tts">man juggling: light skin tone</annotation>
+		<annotation cp="🤹🏼‍♂">juggling | man | medium-light skin tone | multitask</annotation>
+		<annotation cp="🤹🏼‍♂" type="tts">man juggling: medium-light skin tone</annotation>
+		<annotation cp="🤹🏽‍♂">juggling | man | medium skin tone | multitask</annotation>
+		<annotation cp="🤹🏽‍♂" type="tts">man juggling: medium skin tone</annotation>
+		<annotation cp="🤹🏾‍♂">juggling | man | medium-dark skin tone | multitask</annotation>
+		<annotation cp="🤹🏾‍♂" type="tts">man juggling: medium-dark skin tone</annotation>
+		<annotation cp="🤹🏿‍♂">dark skin tone | juggling | man | multitask</annotation>
+		<annotation cp="🤹🏿‍♂" type="tts">man juggling: dark skin tone</annotation>
+		<annotation cp="🤹🏻‍♀">juggling | light skin tone | multitask | woman</annotation>
+		<annotation cp="🤹🏻‍♀" type="tts">woman juggling: light skin tone</annotation>
+		<annotation cp="🤹🏼‍♀">juggling | medium-light skin tone | multitask | woman</annotation>
+		<annotation cp="🤹🏼‍♀" type="tts">woman juggling: medium-light skin tone</annotation>
+		<annotation cp="🤹🏽‍♀">juggling | medium skin tone | multitask | woman</annotation>
+		<annotation cp="🤹🏽‍♀" type="tts">woman juggling: medium skin tone</annotation>
+		<annotation cp="🤹🏾‍♀">juggling | medium-dark skin tone | multitask | woman</annotation>
+		<annotation cp="🤹🏾‍♀" type="tts">woman juggling: medium-dark skin tone</annotation>
+		<annotation cp="🤹🏿‍♀">dark skin tone | juggling | multitask | woman</annotation>
+		<annotation cp="🤹🏿‍♀" type="tts">woman juggling: dark skin tone</annotation>
+		<annotation cp="🧘🏻">light skin tone | meditation | person in lotus position | yoga</annotation>
+		<annotation cp="🧘🏻" type="tts">person in lotus position: light skin tone</annotation>
+		<annotation cp="🧘🏼">meditation | medium-light skin tone | person in lotus position | yoga</annotation>
+		<annotation cp="🧘🏼" type="tts">person in lotus position: medium-light skin tone</annotation>
+		<annotation cp="🧘🏽">meditation | medium skin tone | person in lotus position | yoga</annotation>
+		<annotation cp="🧘🏽" type="tts">person in lotus position: medium skin tone</annotation>
+		<annotation cp="🧘🏾">meditation | medium-dark skin tone | person in lotus position | yoga</annotation>
+		<annotation cp="🧘🏾" type="tts">person in lotus position: medium-dark skin tone</annotation>
+		<annotation cp="🧘🏿">dark skin tone | meditation | person in lotus position | yoga</annotation>
+		<annotation cp="🧘🏿" type="tts">person in lotus position: dark skin tone</annotation>
+		<annotation cp="🧘🏻‍♂">light skin tone | man in lotus position | meditation | yoga</annotation>
+		<annotation cp="🧘🏻‍♂" type="tts">man in lotus position: light skin tone</annotation>
+		<annotation cp="🧘🏼‍♂">man in lotus position | meditation | medium-light skin tone | yoga</annotation>
+		<annotation cp="🧘🏼‍♂" type="tts">man in lotus position: medium-light skin tone</annotation>
+		<annotation cp="🧘🏽‍♂">man in lotus position | meditation | medium skin tone | yoga</annotation>
+		<annotation cp="🧘🏽‍♂" type="tts">man in lotus position: medium skin tone</annotation>
+		<annotation cp="🧘🏾‍♂">man in lotus position | meditation | medium-dark skin tone | yoga</annotation>
+		<annotation cp="🧘🏾‍♂" type="tts">man in lotus position: medium-dark skin tone</annotation>
+		<annotation cp="🧘🏿‍♂">dark skin tone | man in lotus position | meditation | yoga</annotation>
+		<annotation cp="🧘🏿‍♂" type="tts">man in lotus position: dark skin tone</annotation>
+		<annotation cp="🧘🏻‍♀">light skin tone | meditation | woman in lotus position | yoga</annotation>
+		<annotation cp="🧘🏻‍♀" type="tts">woman in lotus position: light skin tone</annotation>
+		<annotation cp="🧘🏼‍♀">meditation | medium-light skin tone | woman in lotus position | yoga</annotation>
+		<annotation cp="🧘🏼‍♀" type="tts">woman in lotus position: medium-light skin tone</annotation>
+		<annotation cp="🧘🏽‍♀">meditation | medium skin tone | woman in lotus position | yoga</annotation>
+		<annotation cp="🧘🏽‍♀" type="tts">woman in lotus position: medium skin tone</annotation>
+		<annotation cp="🧘🏾‍♀">meditation | medium-dark skin tone | woman in lotus position | yoga</annotation>
+		<annotation cp="🧘🏾‍♀" type="tts">woman in lotus position: medium-dark skin tone</annotation>
+		<annotation cp="🧘🏿‍♀">dark skin tone | meditation | woman in lotus position | yoga</annotation>
+		<annotation cp="🧘🏿‍♀" type="tts">woman in lotus position: dark skin tone</annotation>
+		<annotation cp="🛀🏻">bath | bathtub | light skin tone | person taking bath</annotation>
+		<annotation cp="🛀🏻" type="tts">person taking bath: light skin tone</annotation>
+		<annotation cp="🛀🏼">bath | bathtub | medium-light skin tone | person taking bath</annotation>
+		<annotation cp="🛀🏼" type="tts">person taking bath: medium-light skin tone</annotation>
+		<annotation cp="🛀🏽">bath | bathtub | medium skin tone | person taking bath</annotation>
+		<annotation cp="🛀🏽" type="tts">person taking bath: medium skin tone</annotation>
+		<annotation cp="🛀🏾">bath | bathtub | medium-dark skin tone | person taking bath</annotation>
+		<annotation cp="🛀🏾" type="tts">person taking bath: medium-dark skin tone</annotation>
+		<annotation cp="🛀🏿">bath | bathtub | dark skin tone | person taking bath</annotation>
+		<annotation cp="🛀🏿" type="tts">person taking bath: dark skin tone</annotation>
+		<annotation cp="🛌🏻">hotel | light skin tone | person in bed | sleep</annotation>
+		<annotation cp="🛌🏻" type="tts">person in bed: light skin tone</annotation>
+		<annotation cp="🛌🏼">hotel | medium-light skin tone | person in bed | sleep</annotation>
+		<annotation cp="🛌🏼" type="tts">person in bed: medium-light skin tone</annotation>
+		<annotation cp="🛌🏽">hotel | medium skin tone | person in bed | sleep</annotation>
+		<annotation cp="🛌🏽" type="tts">person in bed: medium skin tone</annotation>
+		<annotation cp="🛌🏾">hotel | medium-dark skin tone | person in bed | sleep</annotation>
+		<annotation cp="🛌🏾" type="tts">person in bed: medium-dark skin tone</annotation>
+		<annotation cp="🛌🏿">dark skin tone | hotel | person in bed | sleep</annotation>
+		<annotation cp="🛌🏿" type="tts">person in bed: dark skin tone</annotation>
+		<annotation cp="🧑🏻‍🤝‍🧑🏻">couple | hand | hold | holding hands | light skin tone | people holding hands | person</annotation>
+		<annotation cp="🧑🏻‍🤝‍🧑🏻" type="tts">people holding hands: light skin tone</annotation>
+		<annotation cp="🧑🏻‍🤝‍🧑🏼">couple | hand | hold | holding hands | light skin tone | medium-light skin tone | people holding hands | person</annotation>
+		<annotation cp="🧑🏻‍🤝‍🧑🏼" type="tts">people holding hands: light skin tone, medium-light skin tone</annotation>
+		<annotation cp="🧑🏻‍🤝‍🧑🏽">couple | hand | hold | holding hands | light skin tone | medium skin tone | people holding hands | person</annotation>
+		<annotation cp="🧑🏻‍🤝‍🧑🏽" type="tts">people holding hands: light skin tone, medium skin tone</annotation>
+		<annotation cp="🧑🏻‍🤝‍🧑🏾">couple | hand | hold | holding hands | light skin tone | medium-dark skin tone | people holding hands | person</annotation>
+		<annotation cp="🧑🏻‍🤝‍🧑🏾" type="tts">people holding hands: light skin tone, medium-dark skin tone</annotation>
+		<annotation cp="🧑🏻‍🤝‍🧑🏿">couple | dark skin tone | hand | hold | holding hands | light skin tone | people holding hands | person</annotation>
+		<annotation cp="🧑🏻‍🤝‍🧑🏿" type="tts">people holding hands: light skin tone, dark skin tone</annotation>
+		<annotation cp="🧑🏼‍🤝‍🧑🏻">couple | hand | hold | holding hands | light skin tone | medium-light skin tone | people holding hands | person</annotation>
+		<annotation cp="🧑🏼‍🤝‍🧑🏻" type="tts">people holding hands: medium-light skin tone, light skin tone</annotation>
+		<annotation cp="🧑🏼‍🤝‍🧑🏼">couple | hand | hold | holding hands | medium-light skin tone | people holding hands | person</annotation>
+		<annotation cp="🧑🏼‍🤝‍🧑🏼" type="tts">people holding hands: medium-light skin tone</annotation>
+		<annotation cp="🧑🏼‍🤝‍🧑🏽">couple | hand | hold | holding hands | medium skin tone | medium-light skin tone | people holding hands | person</annotation>
+		<annotation cp="🧑🏼‍🤝‍🧑🏽" type="tts">people holding hands: medium-light skin tone, medium skin tone</annotation>
+		<annotation cp="🧑🏼‍🤝‍🧑🏾">couple | hand | hold | holding hands | medium-dark skin tone | medium-light skin tone | people holding hands | person</annotation>
+		<annotation cp="🧑🏼‍🤝‍🧑🏾" type="tts">people holding hands: medium-light skin tone, medium-dark skin tone</annotation>
+		<annotation cp="🧑🏼‍🤝‍🧑🏿">couple | dark skin tone | hand | hold | holding hands | medium-light skin tone | people holding hands | person</annotation>
+		<annotation cp="🧑🏼‍🤝‍🧑🏿" type="tts">people holding hands: medium-light skin tone, dark skin tone</annotation>
+		<annotation cp="🧑🏽‍🤝‍🧑🏻">couple | hand | hold | holding hands | light skin tone | medium skin tone | people holding hands | person</annotation>
+		<annotation cp="🧑🏽‍🤝‍🧑🏻" type="tts">people holding hands: medium skin tone, light skin tone</annotation>
+		<annotation cp="🧑🏽‍🤝‍🧑🏼">couple | hand | hold | holding hands | medium skin tone | medium-light skin tone | people holding hands | person</annotation>
+		<annotation cp="🧑🏽‍🤝‍🧑🏼" type="tts">people holding hands: medium skin tone, medium-light skin tone</annotation>
+		<annotation cp="🧑🏽‍🤝‍🧑🏽">couple | hand | hold | holding hands | medium skin tone | people holding hands | person</annotation>
+		<annotation cp="🧑🏽‍🤝‍🧑🏽" type="tts">people holding hands: medium skin tone</annotation>
+		<annotation cp="🧑🏽‍🤝‍🧑🏾">couple | hand | hold | holding hands | medium skin tone | medium-dark skin tone | people holding hands | person</annotation>
+		<annotation cp="🧑🏽‍🤝‍🧑🏾" type="tts">people holding hands: medium skin tone, medium-dark skin tone</annotation>
+		<annotation cp="🧑🏽‍🤝‍🧑🏿">couple | dark skin tone | hand | hold | holding hands | medium skin tone | people holding hands | person</annotation>
+		<annotation cp="🧑🏽‍🤝‍🧑🏿" type="tts">people holding hands: medium skin tone, dark skin tone</annotation>
+		<annotation cp="🧑🏾‍🤝‍🧑🏻">couple | hand | hold | holding hands | light skin tone | medium-dark skin tone | people holding hands | person</annotation>
+		<annotation cp="🧑🏾‍🤝‍🧑🏻" type="tts">people holding hands: medium-dark skin tone, light skin tone</annotation>
+		<annotation cp="🧑🏾‍🤝‍🧑🏼">couple | hand | hold | holding hands | medium-dark skin tone | medium-light skin tone | people holding hands | person</annotation>
+		<annotation cp="🧑🏾‍🤝‍🧑🏼" type="tts">people holding hands: medium-dark skin tone, medium-light skin tone</annotation>
+		<annotation cp="🧑🏾‍🤝‍🧑🏽">couple | hand | hold | holding hands | medium skin tone | medium-dark skin tone | people holding hands | person</annotation>
+		<annotation cp="🧑🏾‍🤝‍🧑🏽" type="tts">people holding hands: medium-dark skin tone, medium skin tone</annotation>
+		<annotation cp="🧑🏾‍🤝‍🧑🏾">couple | hand | hold | holding hands | medium-dark skin tone | people holding hands | person</annotation>
+		<annotation cp="🧑🏾‍🤝‍🧑🏾" type="tts">people holding hands: medium-dark skin tone</annotation>
+		<annotation cp="🧑🏾‍🤝‍🧑🏿">couple | dark skin tone | hand | hold | holding hands | medium-dark skin tone | people holding hands | person</annotation>
+		<annotation cp="🧑🏾‍🤝‍🧑🏿" type="tts">people holding hands: medium-dark skin tone, dark skin tone</annotation>
+		<annotation cp="🧑🏿‍🤝‍🧑🏻">couple | dark skin tone | hand | hold | holding hands | light skin tone | people holding hands | person</annotation>
+		<annotation cp="🧑🏿‍🤝‍🧑🏻" type="tts">people holding hands: dark skin tone, light skin tone</annotation>
+		<annotation cp="🧑🏿‍🤝‍🧑🏼">couple | dark skin tone | hand | hold | holding hands | medium-light skin tone | people holding hands | person</annotation>
+		<annotation cp="🧑🏿‍🤝‍🧑🏼" type="tts">people holding hands: dark skin tone, medium-light skin tone</annotation>
+		<annotation cp="🧑🏿‍🤝‍🧑🏽">couple | dark skin tone | hand | hold | holding hands | medium skin tone | people holding hands | person</annotation>
+		<annotation cp="🧑🏿‍🤝‍🧑🏽" type="tts">people holding hands: dark skin tone, medium skin tone</annotation>
+		<annotation cp="🧑🏿‍🤝‍🧑🏾">couple | dark skin tone | hand | hold | holding hands | medium-dark skin tone | people holding hands | person</annotation>
+		<annotation cp="🧑🏿‍🤝‍🧑🏾" type="tts">people holding hands: dark skin tone, medium-dark skin tone</annotation>
+		<annotation cp="🧑🏿‍🤝‍🧑🏿">couple | dark skin tone | hand | hold | holding hands | people holding hands | person</annotation>
+		<annotation cp="🧑🏿‍🤝‍🧑🏿" type="tts">people holding hands: dark skin tone</annotation>
+		<annotation cp="👭🏻">couple | hand | holding hands | light skin tone | women | women holding hands</annotation>
+		<annotation cp="👭🏻" type="tts">women holding hands: light skin tone</annotation>
+		<annotation cp="👩🏻‍🤝‍👩🏼">couple | hand | holding hands | light skin tone | medium-light skin tone | women | women holding hands</annotation>
+		<annotation cp="👩🏻‍🤝‍👩🏼" type="tts">women holding hands: light skin tone, medium-light skin tone</annotation>
+		<annotation cp="👩🏻‍🤝‍👩🏽">couple | hand | holding hands | light skin tone | medium skin tone | women | women holding hands</annotation>
+		<annotation cp="👩🏻‍🤝‍👩🏽" type="tts">women holding hands: light skin tone, medium skin tone</annotation>
+		<annotation cp="👩🏻‍🤝‍👩🏾">couple | hand | holding hands | light skin tone | medium-dark skin tone | women | women holding hands</annotation>
+		<annotation cp="👩🏻‍🤝‍👩🏾" type="tts">women holding hands: light skin tone, medium-dark skin tone</annotation>
+		<annotation cp="👩🏻‍🤝‍👩🏿">couple | dark skin tone | hand | holding hands | light skin tone | women | women holding hands</annotation>
+		<annotation cp="👩🏻‍🤝‍👩🏿" type="tts">women holding hands: light skin tone, dark skin tone</annotation>
+		<annotation cp="👩🏼‍🤝‍👩🏻">couple | hand | holding hands | light skin tone | medium-light skin tone | women | women holding hands</annotation>
+		<annotation cp="👩🏼‍🤝‍👩🏻" type="tts">women holding hands: medium-light skin tone, light skin tone</annotation>
+		<annotation cp="👭🏼">couple | hand | holding hands | medium-light skin tone | women | women holding hands</annotation>
+		<annotation cp="👭🏼" type="tts">women holding hands: medium-light skin tone</annotation>
+		<annotation cp="👩🏼‍🤝‍👩🏽">couple | hand | holding hands | medium skin tone | medium-light skin tone | women | women holding hands</annotation>
+		<annotation cp="👩🏼‍🤝‍👩🏽" type="tts">women holding hands: medium-light skin tone, medium skin tone</annotation>
+		<annotation cp="👩🏼‍🤝‍👩🏾">couple | hand | holding hands | medium-dark skin tone | medium-light skin tone | women | women holding hands</annotation>
+		<annotation cp="👩🏼‍🤝‍👩🏾" type="tts">women holding hands: medium-light skin tone, medium-dark skin tone</annotation>
+		<annotation cp="👩🏼‍🤝‍👩🏿">couple | dark skin tone | hand | holding hands | medium-light skin tone | women | women holding hands</annotation>
+		<annotation cp="👩🏼‍🤝‍👩🏿" type="tts">women holding hands: medium-light skin tone, dark skin tone</annotation>
+		<annotation cp="👩🏽‍🤝‍👩🏻">couple | hand | holding hands | light skin tone | medium skin tone | women | women holding hands</annotation>
+		<annotation cp="👩🏽‍🤝‍👩🏻" type="tts">women holding hands: medium skin tone, light skin tone</annotation>
+		<annotation cp="👩🏽‍🤝‍👩🏼">couple | hand | holding hands | medium skin tone | medium-light skin tone | women | women holding hands</annotation>
+		<annotation cp="👩🏽‍🤝‍👩🏼" type="tts">women holding hands: medium skin tone, medium-light skin tone</annotation>
+		<annotation cp="👭🏽">couple | hand | holding hands | medium skin tone | women | women holding hands</annotation>
+		<annotation cp="👭🏽" type="tts">women holding hands: medium skin tone</annotation>
+		<annotation cp="👩🏽‍🤝‍👩🏾">couple | hand | holding hands | medium skin tone | medium-dark skin tone | women | women holding hands</annotation>
+		<annotation cp="👩🏽‍🤝‍👩🏾" type="tts">women holding hands: medium skin tone, medium-dark skin tone</annotation>
+		<annotation cp="👩🏽‍🤝‍👩🏿">couple | dark skin tone | hand | holding hands | medium skin tone | women | women holding hands</annotation>
+		<annotation cp="👩🏽‍🤝‍👩🏿" type="tts">women holding hands: medium skin tone, dark skin tone</annotation>
+		<annotation cp="👩🏾‍🤝‍👩🏻">couple | hand | holding hands | light skin tone | medium-dark skin tone | women | women holding hands</annotation>
+		<annotation cp="👩🏾‍🤝‍👩🏻" type="tts">women holding hands: medium-dark skin tone, light skin tone</annotation>
+		<annotation cp="👩🏾‍🤝‍👩🏼">couple | hand | holding hands | medium-dark skin tone | medium-light skin tone | women | women holding hands</annotation>
+		<annotation cp="👩🏾‍🤝‍👩🏼" type="tts">women holding hands: medium-dark skin tone, medium-light skin tone</annotation>
+		<annotation cp="👩🏾‍🤝‍👩🏽">couple | hand | holding hands | medium skin tone | medium-dark skin tone | women | women holding hands</annotation>
+		<annotation cp="👩🏾‍🤝‍👩🏽" type="tts">women holding hands: medium-dark skin tone, medium skin tone</annotation>
+		<annotation cp="👭🏾">couple | hand | holding hands | medium-dark skin tone | women | women holding hands</annotation>
+		<annotation cp="👭🏾" type="tts">women holding hands: medium-dark skin tone</annotation>
+		<annotation cp="👩🏾‍🤝‍👩🏿">couple | dark skin tone | hand | holding hands | medium-dark skin tone | women | women holding hands</annotation>
+		<annotation cp="👩🏾‍🤝‍👩🏿" type="tts">women holding hands: medium-dark skin tone, dark skin tone</annotation>
+		<annotation cp="👩🏿‍🤝‍👩🏻">couple | dark skin tone | hand | holding hands | light skin tone | women | women holding hands</annotation>
+		<annotation cp="👩🏿‍🤝‍👩🏻" type="tts">women holding hands: dark skin tone, light skin tone</annotation>
+		<annotation cp="👩🏿‍🤝‍👩🏼">couple | dark skin tone | hand | holding hands | medium-light skin tone | women | women holding hands</annotation>
+		<annotation cp="👩🏿‍🤝‍👩🏼" type="tts">women holding hands: dark skin tone, medium-light skin tone</annotation>
+		<annotation cp="👩🏿‍🤝‍👩🏽">couple | dark skin tone | hand | holding hands | medium skin tone | women | women holding hands</annotation>
+		<annotation cp="👩🏿‍🤝‍👩🏽" type="tts">women holding hands: dark skin tone, medium skin tone</annotation>
+		<annotation cp="👩🏿‍🤝‍👩🏾">couple | dark skin tone | hand | holding hands | medium-dark skin tone | women | women holding hands</annotation>
+		<annotation cp="👩🏿‍🤝‍👩🏾" type="tts">women holding hands: dark skin tone, medium-dark skin tone</annotation>
+		<annotation cp="👭🏿">couple | dark skin tone | hand | holding hands | women | women holding hands</annotation>
+		<annotation cp="👭🏿" type="tts">women holding hands: dark skin tone</annotation>
+		<annotation cp="👫🏻">couple | hand | hold | holding hands | light skin tone | man | woman | woman and man holding hands</annotation>
+		<annotation cp="👫🏻" type="tts">woman and man holding hands: light skin tone</annotation>
+		<annotation cp="👩🏻‍🤝‍👨🏼">couple | hand | hold | holding hands | light skin tone | man | medium-light skin tone | woman | woman and man holding hands</annotation>
+		<annotation cp="👩🏻‍🤝‍👨🏼" type="tts">woman and man holding hands: light skin tone, medium-light skin tone</annotation>
+		<annotation cp="👩🏻‍🤝‍👨🏽">couple | hand | hold | holding hands | light skin tone | man | medium skin tone | woman | woman and man holding hands</annotation>
+		<annotation cp="👩🏻‍🤝‍👨🏽" type="tts">woman and man holding hands: light skin tone, medium skin tone</annotation>
+		<annotation cp="👩🏻‍🤝‍👨🏾">couple | hand | hold | holding hands | light skin tone | man | medium-dark skin tone | woman | woman and man holding hands</annotation>
+		<annotation cp="👩🏻‍🤝‍👨🏾" type="tts">woman and man holding hands: light skin tone, medium-dark skin tone</annotation>
+		<annotation cp="👩🏻‍🤝‍👨🏿">couple | dark skin tone | hand | hold | holding hands | light skin tone | man | woman | woman and man holding hands</annotation>
+		<annotation cp="👩🏻‍🤝‍👨🏿" type="tts">woman and man holding hands: light skin tone, dark skin tone</annotation>
+		<annotation cp="👩🏼‍🤝‍👨🏻">couple | hand | hold | holding hands | light skin tone | man | medium-light skin tone | woman | woman and man holding hands</annotation>
+		<annotation cp="👩🏼‍🤝‍👨🏻" type="tts">woman and man holding hands: medium-light skin tone, light skin tone</annotation>
+		<annotation cp="👫🏼">couple | hand | hold | holding hands | man | medium-light skin tone | woman | woman and man holding hands</annotation>
+		<annotation cp="👫🏼" type="tts">woman and man holding hands: medium-light skin tone</annotation>
+		<annotation cp="👩🏼‍🤝‍👨🏽">couple | hand | hold | holding hands | man | medium skin tone | medium-light skin tone | woman | woman and man holding hands</annotation>
+		<annotation cp="👩🏼‍🤝‍👨🏽" type="tts">woman and man holding hands: medium-light skin tone, medium skin tone</annotation>
+		<annotation cp="👩🏼‍🤝‍👨🏾">couple | hand | hold | holding hands | man | medium-dark skin tone | medium-light skin tone | woman | woman and man holding hands</annotation>
+		<annotation cp="👩🏼‍🤝‍👨🏾" type="tts">woman and man holding hands: medium-light skin tone, medium-dark skin tone</annotation>
+		<annotation cp="👩🏼‍🤝‍👨🏿">couple | dark skin tone | hand | hold | holding hands | man | medium-light skin tone | woman | woman and man holding hands</annotation>
+		<annotation cp="👩🏼‍🤝‍👨🏿" type="tts">woman and man holding hands: medium-light skin tone, dark skin tone</annotation>
+		<annotation cp="👩🏽‍🤝‍👨🏻">couple | hand | hold | holding hands | light skin tone | man | medium skin tone | woman | woman and man holding hands</annotation>
+		<annotation cp="👩🏽‍🤝‍👨🏻" type="tts">woman and man holding hands: medium skin tone, light skin tone</annotation>
+		<annotation cp="👩🏽‍🤝‍👨🏼">couple | hand | hold | holding hands | man | medium skin tone | medium-light skin tone | woman | woman and man holding hands</annotation>
+		<annotation cp="👩🏽‍🤝‍👨🏼" type="tts">woman and man holding hands: medium skin tone, medium-light skin tone</annotation>
+		<annotation cp="👫🏽">couple | hand | hold | holding hands | man | medium skin tone | woman | woman and man holding hands</annotation>
+		<annotation cp="👫🏽" type="tts">woman and man holding hands: medium skin tone</annotation>
+		<annotation cp="👩🏽‍🤝‍👨🏾">couple | hand | hold | holding hands | man | medium skin tone | medium-dark skin tone | woman | woman and man holding hands</annotation>
+		<annotation cp="👩🏽‍🤝‍👨🏾" type="tts">woman and man holding hands: medium skin tone, medium-dark skin tone</annotation>
+		<annotation cp="👩🏽‍🤝‍👨🏿">couple | dark skin tone | hand | hold | holding hands | man | medium skin tone | woman | woman and man holding hands</annotation>
+		<annotation cp="👩🏽‍🤝‍👨🏿" type="tts">woman and man holding hands: medium skin tone, dark skin tone</annotation>
+		<annotation cp="👩🏾‍🤝‍👨🏻">couple | hand | hold | holding hands | light skin tone | man | medium-dark skin tone | woman | woman and man holding hands</annotation>
+		<annotation cp="👩🏾‍🤝‍👨🏻" type="tts">woman and man holding hands: medium-dark skin tone, light skin tone</annotation>
+		<annotation cp="👩🏾‍🤝‍👨🏼">couple | hand | hold | holding hands | man | medium-dark skin tone | medium-light skin tone | woman | woman and man holding hands</annotation>
+		<annotation cp="👩🏾‍🤝‍👨🏼" type="tts">woman and man holding hands: medium-dark skin tone, medium-light skin tone</annotation>
+		<annotation cp="👩🏾‍🤝‍👨🏽">couple | hand | hold | holding hands | man | medium skin tone | medium-dark skin tone | woman | woman and man holding hands</annotation>
+		<annotation cp="👩🏾‍🤝‍👨🏽" type="tts">woman and man holding hands: medium-dark skin tone, medium skin tone</annotation>
+		<annotation cp="👫🏾">couple | hand | hold | holding hands | man | medium-dark skin tone | woman | woman and man holding hands</annotation>
+		<annotation cp="👫🏾" type="tts">woman and man holding hands: medium-dark skin tone</annotation>
+		<annotation cp="👩🏾‍🤝‍👨🏿">couple | dark skin tone | hand | hold | holding hands | man | medium-dark skin tone | woman | woman and man holding hands</annotation>
+		<annotation cp="👩🏾‍🤝‍👨🏿" type="tts">woman and man holding hands: medium-dark skin tone, dark skin tone</annotation>
+		<annotation cp="👩🏿‍🤝‍👨🏻">couple | dark skin tone | hand | hold | holding hands | light skin tone | man | woman | woman and man holding hands</annotation>
+		<annotation cp="👩🏿‍🤝‍👨🏻" type="tts">woman and man holding hands: dark skin tone, light skin tone</annotation>
+		<annotation cp="👩🏿‍🤝‍👨🏼">couple | dark skin tone | hand | hold | holding hands | man | medium-light skin tone | woman | woman and man holding hands</annotation>
+		<annotation cp="👩🏿‍🤝‍👨🏼" type="tts">woman and man holding hands: dark skin tone, medium-light skin tone</annotation>
+		<annotation cp="👩🏿‍🤝‍👨🏽">couple | dark skin tone | hand | hold | holding hands | man | medium skin tone | woman | woman and man holding hands</annotation>
+		<annotation cp="👩🏿‍🤝‍👨🏽" type="tts">woman and man holding hands: dark skin tone, medium skin tone</annotation>
+		<annotation cp="👩🏿‍🤝‍👨🏾">couple | dark skin tone | hand | hold | holding hands | man | medium-dark skin tone | woman | woman and man holding hands</annotation>
+		<annotation cp="👩🏿‍🤝‍👨🏾" type="tts">woman and man holding hands: dark skin tone, medium-dark skin tone</annotation>
+		<annotation cp="👫🏿">couple | dark skin tone | hand | hold | holding hands | man | woman | woman and man holding hands</annotation>
+		<annotation cp="👫🏿" type="tts">woman and man holding hands: dark skin tone</annotation>
+		<annotation cp="👬🏻">couple | Gemini | holding hands | light skin tone | man | men | men holding hands | twins | zodiac</annotation>
+		<annotation cp="👬🏻" type="tts">men holding hands: light skin tone</annotation>
+		<annotation cp="👨🏻‍🤝‍👨🏼">couple | Gemini | holding hands | light skin tone | man | medium-light skin tone | men | men holding hands | twins | zodiac</annotation>
+		<annotation cp="👨🏻‍🤝‍👨🏼" type="tts">men holding hands: light skin tone, medium-light skin tone</annotation>
+		<annotation cp="👨🏻‍🤝‍👨🏽">couple | Gemini | holding hands | light skin tone | man | medium skin tone | men | men holding hands | twins | zodiac</annotation>
+		<annotation cp="👨🏻‍🤝‍👨🏽" type="tts">men holding hands: light skin tone, medium skin tone</annotation>
+		<annotation cp="👨🏻‍🤝‍👨🏾">couple | Gemini | holding hands | light skin tone | man | medium-dark skin tone | men | men holding hands | twins | zodiac</annotation>
+		<annotation cp="👨🏻‍🤝‍👨🏾" type="tts">men holding hands: light skin tone, medium-dark skin tone</annotation>
+		<annotation cp="👨🏻‍🤝‍👨🏿">couple | dark skin tone | Gemini | holding hands | light skin tone | man | men | men holding hands | twins | zodiac</annotation>
+		<annotation cp="👨🏻‍🤝‍👨🏿" type="tts">men holding hands: light skin tone, dark skin tone</annotation>
+		<annotation cp="👨🏼‍🤝‍👨🏻">couple | Gemini | holding hands | light skin tone | man | medium-light skin tone | men | men holding hands | twins | zodiac</annotation>
+		<annotation cp="👨🏼‍🤝‍👨🏻" type="tts">men holding hands: medium-light skin tone, light skin tone</annotation>
+		<annotation cp="👬🏼">couple | Gemini | holding hands | man | medium-light skin tone | men | men holding hands | twins | zodiac</annotation>
+		<annotation cp="👬🏼" type="tts">men holding hands: medium-light skin tone</annotation>
+		<annotation cp="👨🏼‍🤝‍👨🏽">couple | Gemini | holding hands | man | medium skin tone | medium-light skin tone | men | men holding hands | twins | zodiac</annotation>
+		<annotation cp="👨🏼‍🤝‍👨🏽" type="tts">men holding hands: medium-light skin tone, medium skin tone</annotation>
+		<annotation cp="👨🏼‍🤝‍👨🏾">couple | Gemini | holding hands | man | medium-dark skin tone | medium-light skin tone | men | men holding hands | twins | zodiac</annotation>
+		<annotation cp="👨🏼‍🤝‍👨🏾" type="tts">men holding hands: medium-light skin tone, medium-dark skin tone</annotation>
+		<annotation cp="👨🏼‍🤝‍👨🏿">couple | dark skin tone | Gemini | holding hands | man | medium-light skin tone | men | men holding hands | twins | zodiac</annotation>
+		<annotation cp="👨🏼‍🤝‍👨🏿" type="tts">men holding hands: medium-light skin tone, dark skin tone</annotation>
+		<annotation cp="👨🏽‍🤝‍👨🏻">couple | Gemini | holding hands | light skin tone | man | medium skin tone | men | men holding hands | twins | zodiac</annotation>
+		<annotation cp="👨🏽‍🤝‍👨🏻" type="tts">men holding hands: medium skin tone, light skin tone</annotation>
+		<annotation cp="👨🏽‍🤝‍👨🏼">couple | Gemini | holding hands | man | medium skin tone | medium-light skin tone | men | men holding hands | twins | zodiac</annotation>
+		<annotation cp="👨🏽‍🤝‍👨🏼" type="tts">men holding hands: medium skin tone, medium-light skin tone</annotation>
+		<annotation cp="👬🏽">couple | Gemini | holding hands | man | medium skin tone | men | men holding hands | twins | zodiac</annotation>
+		<annotation cp="👬🏽" type="tts">men holding hands: medium skin tone</annotation>
+		<annotation cp="👨🏽‍🤝‍👨🏾">couple | Gemini | holding hands | man | medium skin tone | medium-dark skin tone | men | men holding hands | twins | zodiac</annotation>
+		<annotation cp="👨🏽‍🤝‍👨🏾" type="tts">men holding hands: medium skin tone, medium-dark skin tone</annotation>
+		<annotation cp="👨🏽‍🤝‍👨🏿">couple | dark skin tone | Gemini | holding hands | man | medium skin tone | men | men holding hands | twins | zodiac</annotation>
+		<annotation cp="👨🏽‍🤝‍👨🏿" type="tts">men holding hands: medium skin tone, dark skin tone</annotation>
+		<annotation cp="👨🏾‍🤝‍👨🏻">couple | Gemini | holding hands | light skin tone | man | medium-dark skin tone | men | men holding hands | twins | zodiac</annotation>
+		<annotation cp="👨🏾‍🤝‍👨🏻" type="tts">men holding hands: medium-dark skin tone, light skin tone</annotation>
+		<annotation cp="👨🏾‍🤝‍👨🏼">couple | Gemini | holding hands | man | medium-dark skin tone | medium-light skin tone | men | men holding hands | twins | zodiac</annotation>
+		<annotation cp="👨🏾‍🤝‍👨🏼" type="tts">men holding hands: medium-dark skin tone, medium-light skin tone</annotation>
+		<annotation cp="👨🏾‍🤝‍👨🏽">couple | Gemini | holding hands | man | medium skin tone | medium-dark skin tone | men | men holding hands | twins | zodiac</annotation>
+		<annotation cp="👨🏾‍🤝‍👨🏽" type="tts">men holding hands: medium-dark skin tone, medium skin tone</annotation>
+		<annotation cp="👬🏾">couple | Gemini | holding hands | man | medium-dark skin tone | men | men holding hands | twins | zodiac</annotation>
+		<annotation cp="👬🏾" type="tts">men holding hands: medium-dark skin tone</annotation>
+		<annotation cp="👨🏾‍🤝‍👨🏿">couple | dark skin tone | Gemini | holding hands | man | medium-dark skin tone | men | men holding hands | twins | zodiac</annotation>
+		<annotation cp="👨🏾‍🤝‍👨🏿" type="tts">men holding hands: medium-dark skin tone, dark skin tone</annotation>
+		<annotation cp="👨🏿‍🤝‍👨🏻">couple | dark skin tone | Gemini | holding hands | light skin tone | man | men | men holding hands | twins | zodiac</annotation>
+		<annotation cp="👨🏿‍🤝‍👨🏻" type="tts">men holding hands: dark skin tone, light skin tone</annotation>
+		<annotation cp="👨🏿‍🤝‍👨🏼">couple | dark skin tone | Gemini | holding hands | man | medium-light skin tone | men | men holding hands | twins | zodiac</annotation>
+		<annotation cp="👨🏿‍🤝‍👨🏼" type="tts">men holding hands: dark skin tone, medium-light skin tone</annotation>
+		<annotation cp="👨🏿‍🤝‍👨🏽">couple | dark skin tone | Gemini | holding hands | man | medium skin tone | men | men holding hands | twins | zodiac</annotation>
+		<annotation cp="👨🏿‍🤝‍👨🏽" type="tts">men holding hands: dark skin tone, medium skin tone</annotation>
+		<annotation cp="👨🏿‍🤝‍👨🏾">couple | dark skin tone | Gemini | holding hands | man | medium-dark skin tone | men | men holding hands | twins | zodiac</annotation>
+		<annotation cp="👨🏿‍🤝‍👨🏾" type="tts">men holding hands: dark skin tone, medium-dark skin tone</annotation>
+		<annotation cp="👬🏿">couple | dark skin tone | Gemini | holding hands | man | men | men holding hands | twins | zodiac</annotation>
+		<annotation cp="👬🏿" type="tts">men holding hands: dark skin tone</annotation>
+		<annotation cp="💏🏻">couple | kiss | light skin tone</annotation>
+		<annotation cp="💏🏻" type="tts">kiss: light skin tone</annotation>
+		<annotation cp="💏🏼">couple | kiss | medium-light skin tone</annotation>
+		<annotation cp="💏🏼" type="tts">kiss: medium-light skin tone</annotation>
+		<annotation cp="💏🏽">couple | kiss | medium skin tone</annotation>
+		<annotation cp="💏🏽" type="tts">kiss: medium skin tone</annotation>
+		<annotation cp="💏🏾">couple | kiss | medium-dark skin tone</annotation>
+		<annotation cp="💏🏾" type="tts">kiss: medium-dark skin tone</annotation>
+		<annotation cp="💏🏿">couple | dark skin tone | kiss</annotation>
+		<annotation cp="💏🏿" type="tts">kiss: dark skin tone</annotation>
+		<annotation cp="👩‍❤‍💋‍👨">couple | kiss | man | woman</annotation>
+		<annotation cp="👩‍❤‍💋‍👨" type="tts">kiss: woman, man</annotation>
+		<annotation cp="👨‍❤‍💋‍👨">couple | kiss | man</annotation>
+		<annotation cp="👨‍❤‍💋‍👨" type="tts">kiss: man, man</annotation>
+		<annotation cp="👩‍❤‍💋‍👩">couple | kiss | woman</annotation>
+		<annotation cp="👩‍❤‍💋‍👩" type="tts">kiss: woman, woman</annotation>
+		<annotation cp="💑🏻">couple | couple with heart | light skin tone | love</annotation>
+		<annotation cp="💑🏻" type="tts">couple with heart: light skin tone</annotation>
+		<annotation cp="💑🏼">couple | couple with heart | love | medium-light skin tone</annotation>
+		<annotation cp="💑🏼" type="tts">couple with heart: medium-light skin tone</annotation>
+		<annotation cp="💑🏽">couple | couple with heart | love | medium skin tone</annotation>
+		<annotation cp="💑🏽" type="tts">couple with heart: medium skin tone</annotation>
+		<annotation cp="💑🏾">couple | couple with heart | love | medium-dark skin tone</annotation>
+		<annotation cp="💑🏾" type="tts">couple with heart: medium-dark skin tone</annotation>
+		<annotation cp="💑🏿">couple | couple with heart | dark skin tone | love</annotation>
+		<annotation cp="💑🏿" type="tts">couple with heart: dark skin tone</annotation>
+		<annotation cp="👩‍❤‍👨">couple | couple with heart | love | man | woman</annotation>
+		<annotation cp="👩‍❤‍👨" type="tts">couple with heart: woman, man</annotation>
+		<annotation cp="👨‍❤‍👨">couple | couple with heart | love | man</annotation>
+		<annotation cp="👨‍❤‍👨" type="tts">couple with heart: man, man</annotation>
+		<annotation cp="👩‍❤‍👩">couple | couple with heart | love | woman</annotation>
+		<annotation cp="👩‍❤‍👩" type="tts">couple with heart: woman, woman</annotation>
+		<annotation cp="👨‍👩‍👦">boy | family | man | woman</annotation>
+		<annotation cp="👨‍👩‍👦" type="tts">family: man, woman, boy</annotation>
+		<annotation cp="👨‍👩‍👧">family | girl | man | woman</annotation>
+		<annotation cp="👨‍👩‍👧" type="tts">family: man, woman, girl</annotation>
+		<annotation cp="👨‍👩‍👧‍👦">boy | family | girl | man | woman</annotation>
+		<annotation cp="👨‍👩‍👧‍👦" type="tts">family: man, woman, girl, boy</annotation>
+		<annotation cp="👨‍👩‍👦‍👦">boy | family | man | woman</annotation>
+		<annotation cp="👨‍👩‍👦‍👦" type="tts">family: man, woman, boy, boy</annotation>
+		<annotation cp="👨‍👩‍👧‍👧">family | girl | man | woman</annotation>
+		<annotation cp="👨‍👩‍👧‍👧" type="tts">family: man, woman, girl, girl</annotation>
+		<annotation cp="👨‍👨‍👦">boy | family | man</annotation>
+		<annotation cp="👨‍👨‍👦" type="tts">family: man, man, boy</annotation>
+		<annotation cp="👨‍👨‍👧">family | girl | man</annotation>
+		<annotation cp="👨‍👨‍👧" type="tts">family: man, man, girl</annotation>
+		<annotation cp="👨‍👨‍👧‍👦">boy | family | girl | man</annotation>
+		<annotation cp="👨‍👨‍👧‍👦" type="tts">family: man, man, girl, boy</annotation>
+		<annotation cp="👨‍👨‍👦‍👦">boy | family | man</annotation>
+		<annotation cp="👨‍👨‍👦‍👦" type="tts">family: man, man, boy, boy</annotation>
+		<annotation cp="👨‍👨‍👧‍👧">family | girl | man</annotation>
+		<annotation cp="👨‍👨‍👧‍👧" type="tts">family: man, man, girl, girl</annotation>
+		<annotation cp="👩‍👩‍👦">boy | family | woman</annotation>
+		<annotation cp="👩‍👩‍👦" type="tts">family: woman, woman, boy</annotation>
+		<annotation cp="👩‍👩‍👧">family | girl | woman</annotation>
+		<annotation cp="👩‍👩‍👧" type="tts">family: woman, woman, girl</annotation>
+		<annotation cp="👩‍👩‍👧‍👦">boy | family | girl | woman</annotation>
+		<annotation cp="👩‍👩‍👧‍👦" type="tts">family: woman, woman, girl, boy</annotation>
+		<annotation cp="👩‍👩‍👦‍👦">boy | family | woman</annotation>
+		<annotation cp="👩‍👩‍👦‍👦" type="tts">family: woman, woman, boy, boy</annotation>
+		<annotation cp="👩‍👩‍👧‍👧">family | girl | woman</annotation>
+		<annotation cp="👩‍👩‍👧‍👧" type="tts">family: woman, woman, girl, girl</annotation>
+		<annotation cp="👨‍👦">boy | family | man</annotation>
+		<annotation cp="👨‍👦" type="tts">family: man, boy</annotation>
+		<annotation cp="👨‍👦‍👦">boy | family | man</annotation>
+		<annotation cp="👨‍👦‍👦" type="tts">family: man, boy, boy</annotation>
+		<annotation cp="👨‍👧">family | girl | man</annotation>
+		<annotation cp="👨‍👧" type="tts">family: man, girl</annotation>
+		<annotation cp="👨‍👧‍👦">boy | family | girl | man</annotation>
+		<annotation cp="👨‍👧‍👦" type="tts">family: man, girl, boy</annotation>
+		<annotation cp="👨‍👧‍👧">family | girl | man</annotation>
+		<annotation cp="👨‍👧‍👧" type="tts">family: man, girl, girl</annotation>
+		<annotation cp="👩‍👦">boy | family | woman</annotation>
+		<annotation cp="👩‍👦" type="tts">family: woman, boy</annotation>
+		<annotation cp="👩‍👦‍👦">boy | family | woman</annotation>
+		<annotation cp="👩‍👦‍👦" type="tts">family: woman, boy, boy</annotation>
+		<annotation cp="👩‍👧">family | girl | woman</annotation>
+		<annotation cp="👩‍👧" type="tts">family: woman, girl</annotation>
+		<annotation cp="👩‍👧‍👦">boy | family | girl | woman</annotation>
+		<annotation cp="👩‍👧‍👦" type="tts">family: woman, girl, boy</annotation>
+		<annotation cp="👩‍👧‍👧">family | girl | woman</annotation>
+		<annotation cp="👩‍👧‍👧" type="tts">family: woman, girl, girl</annotation>
+		<annotation cp="#⃣">keycap</annotation>
+		<annotation cp="#⃣" type="tts">keycap: #</annotation>
+		<annotation cp="*⃣">keycap</annotation>
+		<annotation cp="*⃣" type="tts">keycap: *</annotation>
+		<annotation cp="🔟">keycap</annotation>
+		<annotation cp="🔟" type="tts">keycap: 10</annotation>
+		<annotation cp="🇦🇨">flag</annotation>
+		<annotation cp="🇦🇨" type="tts">flag: Ascension Island</annotation>
+		<annotation cp="🇦🇩">flag</annotation>
+		<annotation cp="🇦🇩" type="tts">flag: Andorra</annotation>
+		<annotation cp="🇦🇪">flag</annotation>
+		<annotation cp="🇦🇪" type="tts">flag: United Arab Emirates</annotation>
+		<annotation cp="🇦🇫">flag</annotation>
+		<annotation cp="🇦🇫" type="tts">flag: Afghanistan</annotation>
+		<annotation cp="🇦🇬">flag</annotation>
+		<annotation cp="🇦🇬" type="tts">flag: Antigua &amp; Barbuda</annotation>
+		<annotation cp="🇦🇮">flag</annotation>
+		<annotation cp="🇦🇮" type="tts">flag: Anguilla</annotation>
+		<annotation cp="🇦🇱">flag</annotation>
+		<annotation cp="🇦🇱" type="tts">flag: Albania</annotation>
+		<annotation cp="🇦🇲">flag</annotation>
+		<annotation cp="🇦🇲" type="tts">flag: Armenia</annotation>
+		<annotation cp="🇦🇴">flag</annotation>
+		<annotation cp="🇦🇴" type="tts">flag: Angola</annotation>
+		<annotation cp="🇦🇶">flag</annotation>
+		<annotation cp="🇦🇶" type="tts">flag: Antarctica</annotation>
+		<annotation cp="🇦🇷">flag</annotation>
+		<annotation cp="🇦🇷" type="tts">flag: Argentina</annotation>
+		<annotation cp="🇦🇸">flag</annotation>
+		<annotation cp="🇦🇸" type="tts">flag: American Samoa</annotation>
+		<annotation cp="🇦🇹">flag</annotation>
+		<annotation cp="🇦🇹" type="tts">flag: Austria</annotation>
+		<annotation cp="🇦🇺">flag</annotation>
+		<annotation cp="🇦🇺" type="tts">flag: Australia</annotation>
+		<annotation cp="🇦🇼">flag</annotation>
+		<annotation cp="🇦🇼" type="tts">flag: Aruba</annotation>
+		<annotation cp="🇦🇽">flag</annotation>
+		<annotation cp="🇦🇽" type="tts">flag: Åland Islands</annotation>
+		<annotation cp="🇦🇿">flag</annotation>
+		<annotation cp="🇦🇿" type="tts">flag: Azerbaijan</annotation>
+		<annotation cp="🇧🇦">flag</annotation>
+		<annotation cp="🇧🇦" type="tts">flag: Bosnia &amp; Herzegovina</annotation>
+		<annotation cp="🇧🇧">flag</annotation>
+		<annotation cp="🇧🇧" type="tts">flag: Barbados</annotation>
+		<annotation cp="🇧🇩">flag</annotation>
+		<annotation cp="🇧🇩" type="tts">flag: Bangladesh</annotation>
+		<annotation cp="🇧🇪">flag</annotation>
+		<annotation cp="🇧🇪" type="tts">flag: Belgium</annotation>
+		<annotation cp="🇧🇫">flag</annotation>
+		<annotation cp="🇧🇫" type="tts">flag: Burkina Faso</annotation>
+		<annotation cp="🇧🇬">flag</annotation>
+		<annotation cp="🇧🇬" type="tts">flag: Bulgaria</annotation>
+		<annotation cp="🇧🇭">flag</annotation>
+		<annotation cp="🇧🇭" type="tts">flag: Bahrain</annotation>
+		<annotation cp="🇧🇮">flag</annotation>
+		<annotation cp="🇧🇮" type="tts">flag: Burundi</annotation>
+		<annotation cp="🇧🇯">flag</annotation>
+		<annotation cp="🇧🇯" type="tts">flag: Benin</annotation>
+		<annotation cp="🇧🇱">flag</annotation>
+		<annotation cp="🇧🇱" type="tts">flag: St. Barthélemy</annotation>
+		<annotation cp="🇧🇲">flag</annotation>
+		<annotation cp="🇧🇲" type="tts">flag: Bermuda</annotation>
+		<annotation cp="🇧🇳">flag</annotation>
+		<annotation cp="🇧🇳" type="tts">flag: Brunei</annotation>
+		<annotation cp="🇧🇴">flag</annotation>
+		<annotation cp="🇧🇴" type="tts">flag: Bolivia</annotation>
+		<annotation cp="🇧🇶">flag</annotation>
+		<annotation cp="🇧🇶" type="tts">flag: Caribbean Netherlands</annotation>
+		<annotation cp="🇧🇷">flag</annotation>
+		<annotation cp="🇧🇷" type="tts">flag: Brazil</annotation>
+		<annotation cp="🇧🇸">flag</annotation>
+		<annotation cp="🇧🇸" type="tts">flag: Bahamas</annotation>
+		<annotation cp="🇧🇹">flag</annotation>
+		<annotation cp="🇧🇹" type="tts">flag: Bhutan</annotation>
+		<annotation cp="🇧🇻">flag</annotation>
+		<annotation cp="🇧🇻" type="tts">flag: Bouvet Island</annotation>
+		<annotation cp="🇧🇼">flag</annotation>
+		<annotation cp="🇧🇼" type="tts">flag: Botswana</annotation>
+		<annotation cp="🇧🇾">flag</annotation>
+		<annotation cp="🇧🇾" type="tts">flag: Belarus</annotation>
+		<annotation cp="🇧🇿">flag</annotation>
+		<annotation cp="🇧🇿" type="tts">flag: Belize</annotation>
+		<annotation cp="🇨🇦">flag</annotation>
+		<annotation cp="🇨🇦" type="tts">flag: Canada</annotation>
+		<annotation cp="🇨🇨">flag</annotation>
+		<annotation cp="🇨🇨" type="tts">flag: Cocos (Keeling) Islands</annotation>
+		<annotation cp="🇨🇩">flag</annotation>
+		<annotation cp="🇨🇩" type="tts">flag: Congo - Kinshasa</annotation>
+		<annotation cp="🇨🇫">flag</annotation>
+		<annotation cp="🇨🇫" type="tts">flag: Central African Republic</annotation>
+		<annotation cp="🇨🇬">flag</annotation>
+		<annotation cp="🇨🇬" type="tts">flag: Congo - Brazzaville</annotation>
+		<annotation cp="🇨🇭">flag</annotation>
+		<annotation cp="🇨🇭" type="tts">flag: Switzerland</annotation>
+		<annotation cp="🇨🇮">flag</annotation>
+		<annotation cp="🇨🇮" type="tts">flag: Côte d’Ivoire</annotation>
+		<annotation cp="🇨🇰">flag</annotation>
+		<annotation cp="🇨🇰" type="tts">flag: Cook Islands</annotation>
+		<annotation cp="🇨🇱">flag</annotation>
+		<annotation cp="🇨🇱" type="tts">flag: Chile</annotation>
+		<annotation cp="🇨🇲">flag</annotation>
+		<annotation cp="🇨🇲" type="tts">flag: Cameroon</annotation>
+		<annotation cp="🇨🇳">flag</annotation>
+		<annotation cp="🇨🇳" type="tts">flag: China</annotation>
+		<annotation cp="🇨🇴">flag</annotation>
+		<annotation cp="🇨🇴" type="tts">flag: Colombia</annotation>
+		<annotation cp="🇨🇵">flag</annotation>
+		<annotation cp="🇨🇵" type="tts">flag: Clipperton Island</annotation>
+		<annotation cp="🇨🇷">flag</annotation>
+		<annotation cp="🇨🇷" type="tts">flag: Costa Rica</annotation>
+		<annotation cp="🇨🇺">flag</annotation>
+		<annotation cp="🇨🇺" type="tts">flag: Cuba</annotation>
+		<annotation cp="🇨🇻">flag</annotation>
+		<annotation cp="🇨🇻" type="tts">flag: Cape Verde</annotation>
+		<annotation cp="🇨🇼">flag</annotation>
+		<annotation cp="🇨🇼" type="tts">flag: Curaçao</annotation>
+		<annotation cp="🇨🇽">flag</annotation>
+		<annotation cp="🇨🇽" type="tts">flag: Christmas Island</annotation>
+		<annotation cp="🇨🇾">flag</annotation>
+		<annotation cp="🇨🇾" type="tts">flag: Cyprus</annotation>
+		<annotation cp="🇨🇿">flag</annotation>
+		<annotation cp="🇨🇿" type="tts">flag: Czechia</annotation>
+		<annotation cp="🇩🇪">flag</annotation>
+		<annotation cp="🇩🇪" type="tts">flag: Germany</annotation>
+		<annotation cp="🇩🇬">flag</annotation>
+		<annotation cp="🇩🇬" type="tts">flag: Diego Garcia</annotation>
+		<annotation cp="🇩🇯">flag</annotation>
+		<annotation cp="🇩🇯" type="tts">flag: Djibouti</annotation>
+		<annotation cp="🇩🇰">flag</annotation>
+		<annotation cp="🇩🇰" type="tts">flag: Denmark</annotation>
+		<annotation cp="🇩🇲">flag</annotation>
+		<annotation cp="🇩🇲" type="tts">flag: Dominica</annotation>
+		<annotation cp="🇩🇴">flag</annotation>
+		<annotation cp="🇩🇴" type="tts">flag: Dominican Republic</annotation>
+		<annotation cp="🇩🇿">flag</annotation>
+		<annotation cp="🇩🇿" type="tts">flag: Algeria</annotation>
+		<annotation cp="🇪🇦">flag</annotation>
+		<annotation cp="🇪🇦" type="tts">flag: Ceuta &amp; Melilla</annotation>
+		<annotation cp="🇪🇨">flag</annotation>
+		<annotation cp="🇪🇨" type="tts">flag: Ecuador</annotation>
+		<annotation cp="🇪🇪">flag</annotation>
+		<annotation cp="🇪🇪" type="tts">flag: Estonia</annotation>
+		<annotation cp="🇪🇬">flag</annotation>
+		<annotation cp="🇪🇬" type="tts">flag: Egypt</annotation>
+		<annotation cp="🇪🇭">flag</annotation>
+		<annotation cp="🇪🇭" type="tts">flag: Western Sahara</annotation>
+		<annotation cp="🇪🇷">flag</annotation>
+		<annotation cp="🇪🇷" type="tts">flag: Eritrea</annotation>
+		<annotation cp="🇪🇸">flag</annotation>
+		<annotation cp="🇪🇸" type="tts">flag: Spain</annotation>
+		<annotation cp="🇪🇹">flag</annotation>
+		<annotation cp="🇪🇹" type="tts">flag: Ethiopia</annotation>
+		<annotation cp="🇪🇺">flag</annotation>
+		<annotation cp="🇪🇺" type="tts">flag: European Union</annotation>
+		<annotation cp="🇫🇮">flag</annotation>
+		<annotation cp="🇫🇮" type="tts">flag: Finland</annotation>
+		<annotation cp="🇫🇯">flag</annotation>
+		<annotation cp="🇫🇯" type="tts">flag: Fiji</annotation>
+		<annotation cp="🇫🇰">flag</annotation>
+		<annotation cp="🇫🇰" type="tts">flag: Falkland Islands</annotation>
+		<annotation cp="🇫🇲">flag</annotation>
+		<annotation cp="🇫🇲" type="tts">flag: Micronesia</annotation>
+		<annotation cp="🇫🇴">flag</annotation>
+		<annotation cp="🇫🇴" type="tts">flag: Faroe Islands</annotation>
+		<annotation cp="🇫🇷">flag</annotation>
+		<annotation cp="🇫🇷" type="tts">flag: France</annotation>
+		<annotation cp="🇬🇦">flag</annotation>
+		<annotation cp="🇬🇦" type="tts">flag: Gabon</annotation>
+		<annotation cp="🇬🇧">flag</annotation>
+		<annotation cp="🇬🇧" type="tts">flag: United Kingdom</annotation>
+		<annotation cp="🇬🇩">flag</annotation>
+		<annotation cp="🇬🇩" type="tts">flag: Grenada</annotation>
+		<annotation cp="🇬🇪">flag</annotation>
+		<annotation cp="🇬🇪" type="tts">flag: Georgia</annotation>
+		<annotation cp="🇬🇫">flag</annotation>
+		<annotation cp="🇬🇫" type="tts">flag: French Guiana</annotation>
+		<annotation cp="🇬🇬">flag</annotation>
+		<annotation cp="🇬🇬" type="tts">flag: Guernsey</annotation>
+		<annotation cp="🇬🇭">flag</annotation>
+		<annotation cp="🇬🇭" type="tts">flag: Ghana</annotation>
+		<annotation cp="🇬🇮">flag</annotation>
+		<annotation cp="🇬🇮" type="tts">flag: Gibraltar</annotation>
+		<annotation cp="🇬🇱">flag</annotation>
+		<annotation cp="🇬🇱" type="tts">flag: Greenland</annotation>
+		<annotation cp="🇬🇲">flag</annotation>
+		<annotation cp="🇬🇲" type="tts">flag: Gambia</annotation>
+		<annotation cp="🇬🇳">flag</annotation>
+		<annotation cp="🇬🇳" type="tts">flag: Guinea</annotation>
+		<annotation cp="🇬🇵">flag</annotation>
+		<annotation cp="🇬🇵" type="tts">flag: Guadeloupe</annotation>
+		<annotation cp="🇬🇶">flag</annotation>
+		<annotation cp="🇬🇶" type="tts">flag: Equatorial Guinea</annotation>
+		<annotation cp="🇬🇷">flag</annotation>
+		<annotation cp="🇬🇷" type="tts">flag: Greece</annotation>
+		<annotation cp="🇬🇸">flag</annotation>
+		<annotation cp="🇬🇸" type="tts">flag: South Georgia &amp; South Sandwich Islands</annotation>
+		<annotation cp="🇬🇹">flag</annotation>
+		<annotation cp="🇬🇹" type="tts">flag: Guatemala</annotation>
+		<annotation cp="🇬🇺">flag</annotation>
+		<annotation cp="🇬🇺" type="tts">flag: Guam</annotation>
+		<annotation cp="🇬🇼">flag</annotation>
+		<annotation cp="🇬🇼" type="tts">flag: Guinea-Bissau</annotation>
+		<annotation cp="🇬🇾">flag</annotation>
+		<annotation cp="🇬🇾" type="tts">flag: Guyana</annotation>
+		<annotation cp="🇭🇰">flag</annotation>
+		<annotation cp="🇭🇰" type="tts">flag: Hong Kong SAR China</annotation>
+		<annotation cp="🇭🇲">flag</annotation>
+		<annotation cp="🇭🇲" type="tts">flag: Heard &amp; McDonald Islands</annotation>
+		<annotation cp="🇭🇳">flag</annotation>
+		<annotation cp="🇭🇳" type="tts">flag: Honduras</annotation>
+		<annotation cp="🇭🇷">flag</annotation>
+		<annotation cp="🇭🇷" type="tts">flag: Croatia</annotation>
+		<annotation cp="🇭🇹">flag</annotation>
+		<annotation cp="🇭🇹" type="tts">flag: Haiti</annotation>
+		<annotation cp="🇭🇺">flag</annotation>
+		<annotation cp="🇭🇺" type="tts">flag: Hungary</annotation>
+		<annotation cp="🇮🇨">flag</annotation>
+		<annotation cp="🇮🇨" type="tts">flag: Canary Islands</annotation>
+		<annotation cp="🇮🇩">flag</annotation>
+		<annotation cp="🇮🇩" type="tts">flag: Indonesia</annotation>
+		<annotation cp="🇮🇪">flag</annotation>
+		<annotation cp="🇮🇪" type="tts">flag: Ireland</annotation>
+		<annotation cp="🇮🇱">flag</annotation>
+		<annotation cp="🇮🇱" type="tts">flag: Israel</annotation>
+		<annotation cp="🇮🇲">flag</annotation>
+		<annotation cp="🇮🇲" type="tts">flag: Isle of Man</annotation>
+		<annotation cp="🇮🇳">flag</annotation>
+		<annotation cp="🇮🇳" type="tts">flag: India</annotation>
+		<annotation cp="🇮🇴">flag</annotation>
+		<annotation cp="🇮🇴" type="tts">flag: British Indian Ocean Territory</annotation>
+		<annotation cp="🇮🇶">flag</annotation>
+		<annotation cp="🇮🇶" type="tts">flag: Iraq</annotation>
+		<annotation cp="🇮🇷">flag</annotation>
+		<annotation cp="🇮🇷" type="tts">flag: Iran</annotation>
+		<annotation cp="🇮🇸">flag</annotation>
+		<annotation cp="🇮🇸" type="tts">flag: Iceland</annotation>
+		<annotation cp="🇮🇹">flag</annotation>
+		<annotation cp="🇮🇹" type="tts">flag: Italy</annotation>
+		<annotation cp="🇯🇪">flag</annotation>
+		<annotation cp="🇯🇪" type="tts">flag: Jersey</annotation>
+		<annotation cp="🇯🇲">flag</annotation>
+		<annotation cp="🇯🇲" type="tts">flag: Jamaica</annotation>
+		<annotation cp="🇯🇴">flag</annotation>
+		<annotation cp="🇯🇴" type="tts">flag: Jordan</annotation>
+		<annotation cp="🇯🇵">flag</annotation>
+		<annotation cp="🇯🇵" type="tts">flag: Japan</annotation>
+		<annotation cp="🇰🇪">flag</annotation>
+		<annotation cp="🇰🇪" type="tts">flag: Kenya</annotation>
+		<annotation cp="🇰🇬">flag</annotation>
+		<annotation cp="🇰🇬" type="tts">flag: Kyrgyzstan</annotation>
+		<annotation cp="🇰🇭">flag</annotation>
+		<annotation cp="🇰🇭" type="tts">flag: Cambodia</annotation>
+		<annotation cp="🇰🇮">flag</annotation>
+		<annotation cp="🇰🇮" type="tts">flag: Kiribati</annotation>
+		<annotation cp="🇰🇲">flag</annotation>
+		<annotation cp="🇰🇲" type="tts">flag: Comoros</annotation>
+		<annotation cp="🇰🇳">flag</annotation>
+		<annotation cp="🇰🇳" type="tts">flag: St. Kitts &amp; Nevis</annotation>
+		<annotation cp="🇰🇵">flag</annotation>
+		<annotation cp="🇰🇵" type="tts">flag: North Korea</annotation>
+		<annotation cp="🇰🇷">flag</annotation>
+		<annotation cp="🇰🇷" type="tts">flag: South Korea</annotation>
+		<annotation cp="🇰🇼">flag</annotation>
+		<annotation cp="🇰🇼" type="tts">flag: Kuwait</annotation>
+		<annotation cp="🇰🇾">flag</annotation>
+		<annotation cp="🇰🇾" type="tts">flag: Cayman Islands</annotation>
+		<annotation cp="🇰🇿">flag</annotation>
+		<annotation cp="🇰🇿" type="tts">flag: Kazakhstan</annotation>
+		<annotation cp="🇱🇦">flag</annotation>
+		<annotation cp="🇱🇦" type="tts">flag: Laos</annotation>
+		<annotation cp="🇱🇧">flag</annotation>
+		<annotation cp="🇱🇧" type="tts">flag: Lebanon</annotation>
+		<annotation cp="🇱🇨">flag</annotation>
+		<annotation cp="🇱🇨" type="tts">flag: St. Lucia</annotation>
+		<annotation cp="🇱🇮">flag</annotation>
+		<annotation cp="🇱🇮" type="tts">flag: Liechtenstein</annotation>
+		<annotation cp="🇱🇰">flag</annotation>
+		<annotation cp="🇱🇰" type="tts">flag: Sri Lanka</annotation>
+		<annotation cp="🇱🇷">flag</annotation>
+		<annotation cp="🇱🇷" type="tts">flag: Liberia</annotation>
+		<annotation cp="🇱🇸">flag</annotation>
+		<annotation cp="🇱🇸" type="tts">flag: Lesotho</annotation>
+		<annotation cp="🇱🇹">flag</annotation>
+		<annotation cp="🇱🇹" type="tts">flag: Lithuania</annotation>
+		<annotation cp="🇱🇺">flag</annotation>
+		<annotation cp="🇱🇺" type="tts">flag: Luxembourg</annotation>
+		<annotation cp="🇱🇻">flag</annotation>
+		<annotation cp="🇱🇻" type="tts">flag: Latvia</annotation>
+		<annotation cp="🇱🇾">flag</annotation>
+		<annotation cp="🇱🇾" type="tts">flag: Libya</annotation>
+		<annotation cp="🇲🇦">flag</annotation>
+		<annotation cp="🇲🇦" type="tts">flag: Morocco</annotation>
+		<annotation cp="🇲🇨">flag</annotation>
+		<annotation cp="🇲🇨" type="tts">flag: Monaco</annotation>
+		<annotation cp="🇲🇩">flag</annotation>
+		<annotation cp="🇲🇩" type="tts">flag: Moldova</annotation>
+		<annotation cp="🇲🇪">flag</annotation>
+		<annotation cp="🇲🇪" type="tts">flag: Montenegro</annotation>
+		<annotation cp="🇲🇫">flag</annotation>
+		<annotation cp="🇲🇫" type="tts">flag: St. Martin</annotation>
+		<annotation cp="🇲🇬">flag</annotation>
+		<annotation cp="🇲🇬" type="tts">flag: Madagascar</annotation>
+		<annotation cp="🇲🇭">flag</annotation>
+		<annotation cp="🇲🇭" type="tts">flag: Marshall Islands</annotation>
+		<annotation cp="🇲🇰">flag</annotation>
+		<annotation cp="🇲🇰" type="tts">flag: North Macedonia</annotation>
+		<annotation cp="🇲🇱">flag</annotation>
+		<annotation cp="🇲🇱" type="tts">flag: Mali</annotation>
+		<annotation cp="🇲🇲">flag</annotation>
+		<annotation cp="🇲🇲" type="tts">flag: Myanmar (Burma)</annotation>
+		<annotation cp="🇲🇳">flag</annotation>
+		<annotation cp="🇲🇳" type="tts">flag: Mongolia</annotation>
+		<annotation cp="🇲🇴">flag</annotation>
+		<annotation cp="🇲🇴" type="tts">flag: Macao SAR China</annotation>
+		<annotation cp="🇲🇵">flag</annotation>
+		<annotation cp="🇲🇵" type="tts">flag: Northern Mariana Islands</annotation>
+		<annotation cp="🇲🇶">flag</annotation>
+		<annotation cp="🇲🇶" type="tts">flag: Martinique</annotation>
+		<annotation cp="🇲🇷">flag</annotation>
+		<annotation cp="🇲🇷" type="tts">flag: Mauritania</annotation>
+		<annotation cp="🇲🇸">flag</annotation>
+		<annotation cp="🇲🇸" type="tts">flag: Montserrat</annotation>
+		<annotation cp="🇲🇹">flag</annotation>
+		<annotation cp="🇲🇹" type="tts">flag: Malta</annotation>
+		<annotation cp="🇲🇺">flag</annotation>
+		<annotation cp="🇲🇺" type="tts">flag: Mauritius</annotation>
+		<annotation cp="🇲🇻">flag</annotation>
+		<annotation cp="🇲🇻" type="tts">flag: Maldives</annotation>
+		<annotation cp="🇲🇼">flag</annotation>
+		<annotation cp="🇲🇼" type="tts">flag: Malawi</annotation>
+		<annotation cp="🇲🇽">flag</annotation>
+		<annotation cp="🇲🇽" type="tts">flag: Mexico</annotation>
+		<annotation cp="🇲🇾">flag</annotation>
+		<annotation cp="🇲🇾" type="tts">flag: Malaysia</annotation>
+		<annotation cp="🇲🇿">flag</annotation>
+		<annotation cp="🇲🇿" type="tts">flag: Mozambique</annotation>
+		<annotation cp="🇳🇦">flag</annotation>
+		<annotation cp="🇳🇦" type="tts">flag: Namibia</annotation>
+		<annotation cp="🇳🇨">flag</annotation>
+		<annotation cp="🇳🇨" type="tts">flag: New Caledonia</annotation>
+		<annotation cp="🇳🇪">flag</annotation>
+		<annotation cp="🇳🇪" type="tts">flag: Niger</annotation>
+		<annotation cp="🇳🇫">flag</annotation>
+		<annotation cp="🇳🇫" type="tts">flag: Norfolk Island</annotation>
+		<annotation cp="🇳🇬">flag</annotation>
+		<annotation cp="🇳🇬" type="tts">flag: Nigeria</annotation>
+		<annotation cp="🇳🇮">flag</annotation>
+		<annotation cp="🇳🇮" type="tts">flag: Nicaragua</annotation>
+		<annotation cp="🇳🇱">flag</annotation>
+		<annotation cp="🇳🇱" type="tts">flag: Netherlands</annotation>
+		<annotation cp="🇳🇴">flag</annotation>
+		<annotation cp="🇳🇴" type="tts">flag: Norway</annotation>
+		<annotation cp="🇳🇵">flag</annotation>
+		<annotation cp="🇳🇵" type="tts">flag: Nepal</annotation>
+		<annotation cp="🇳🇷">flag</annotation>
+		<annotation cp="🇳🇷" type="tts">flag: Nauru</annotation>
+		<annotation cp="🇳🇺">flag</annotation>
+		<annotation cp="🇳🇺" type="tts">flag: Niue</annotation>
+		<annotation cp="🇳🇿">flag</annotation>
+		<annotation cp="🇳🇿" type="tts">flag: New Zealand</annotation>
+		<annotation cp="🇴🇲">flag</annotation>
+		<annotation cp="🇴🇲" type="tts">flag: Oman</annotation>
+		<annotation cp="🇵🇦">flag</annotation>
+		<annotation cp="🇵🇦" type="tts">flag: Panama</annotation>
+		<annotation cp="🇵🇪">flag</annotation>
+		<annotation cp="🇵🇪" type="tts">flag: Peru</annotation>
+		<annotation cp="🇵🇫">flag</annotation>
+		<annotation cp="🇵🇫" type="tts">flag: French Polynesia</annotation>
+		<annotation cp="🇵🇬">flag</annotation>
+		<annotation cp="🇵🇬" type="tts">flag: Papua New Guinea</annotation>
+		<annotation cp="🇵🇭">flag</annotation>
+		<annotation cp="🇵🇭" type="tts">flag: Philippines</annotation>
+		<annotation cp="🇵🇰">flag</annotation>
+		<annotation cp="🇵🇰" type="tts">flag: Pakistan</annotation>
+		<annotation cp="🇵🇱">flag</annotation>
+		<annotation cp="🇵🇱" type="tts">flag: Poland</annotation>
+		<annotation cp="🇵🇲">flag</annotation>
+		<annotation cp="🇵🇲" type="tts">flag: St. Pierre &amp; Miquelon</annotation>
+		<annotation cp="🇵🇳">flag</annotation>
+		<annotation cp="🇵🇳" type="tts">flag: Pitcairn Islands</annotation>
+		<annotation cp="🇵🇷">flag</annotation>
+		<annotation cp="🇵🇷" type="tts">flag: Puerto Rico</annotation>
+		<annotation cp="🇵🇸">flag</annotation>
+		<annotation cp="🇵🇸" type="tts">flag: Palestinian Territories</annotation>
+		<annotation cp="🇵🇹">flag</annotation>
+		<annotation cp="🇵🇹" type="tts">flag: Portugal</annotation>
+		<annotation cp="🇵🇼">flag</annotation>
+		<annotation cp="🇵🇼" type="tts">flag: Palau</annotation>
+		<annotation cp="🇵🇾">flag</annotation>
+		<annotation cp="🇵🇾" type="tts">flag: Paraguay</annotation>
+		<annotation cp="🇶🇦">flag</annotation>
+		<annotation cp="🇶🇦" type="tts">flag: Qatar</annotation>
+		<annotation cp="🇷🇪">flag</annotation>
+		<annotation cp="🇷🇪" type="tts">flag: Réunion</annotation>
+		<annotation cp="🇷🇴">flag</annotation>
+		<annotation cp="🇷🇴" type="tts">flag: Romania</annotation>
+		<annotation cp="🇷🇸">flag</annotation>
+		<annotation cp="🇷🇸" type="tts">flag: Serbia</annotation>
+		<annotation cp="🇷🇺">flag</annotation>
+		<annotation cp="🇷🇺" type="tts">flag: Russia</annotation>
+		<annotation cp="🇷🇼">flag</annotation>
+		<annotation cp="🇷🇼" type="tts">flag: Rwanda</annotation>
+		<annotation cp="🇸🇦">flag</annotation>
+		<annotation cp="🇸🇦" type="tts">flag: Saudi Arabia</annotation>
+		<annotation cp="🇸🇧">flag</annotation>
+		<annotation cp="🇸🇧" type="tts">flag: Solomon Islands</annotation>
+		<annotation cp="🇸🇨">flag</annotation>
+		<annotation cp="🇸🇨" type="tts">flag: Seychelles</annotation>
+		<annotation cp="🇸🇩">flag</annotation>
+		<annotation cp="🇸🇩" type="tts">flag: Sudan</annotation>
+		<annotation cp="🇸🇪">flag</annotation>
+		<annotation cp="🇸🇪" type="tts">flag: Sweden</annotation>
+		<annotation cp="🇸🇬">flag</annotation>
+		<annotation cp="🇸🇬" type="tts">flag: Singapore</annotation>
+		<annotation cp="🇸🇭">flag</annotation>
+		<annotation cp="🇸🇭" type="tts">flag: St. Helena</annotation>
+		<annotation cp="🇸🇮">flag</annotation>
+		<annotation cp="🇸🇮" type="tts">flag: Slovenia</annotation>
+		<annotation cp="🇸🇯">flag</annotation>
+		<annotation cp="🇸🇯" type="tts">flag: Svalbard &amp; Jan Mayen</annotation>
+		<annotation cp="🇸🇰">flag</annotation>
+		<annotation cp="🇸🇰" type="tts">flag: Slovakia</annotation>
+		<annotation cp="🇸🇱">flag</annotation>
+		<annotation cp="🇸🇱" type="tts">flag: Sierra Leone</annotation>
+		<annotation cp="🇸🇲">flag</annotation>
+		<annotation cp="🇸🇲" type="tts">flag: San Marino</annotation>
+		<annotation cp="🇸🇳">flag</annotation>
+		<annotation cp="🇸🇳" type="tts">flag: Senegal</annotation>
+		<annotation cp="🇸🇴">flag</annotation>
+		<annotation cp="🇸🇴" type="tts">flag: Somalia</annotation>
+		<annotation cp="🇸🇷">flag</annotation>
+		<annotation cp="🇸🇷" type="tts">flag: Suriname</annotation>
+		<annotation cp="🇸🇸">flag</annotation>
+		<annotation cp="🇸🇸" type="tts">flag: South Sudan</annotation>
+		<annotation cp="🇸🇹">flag</annotation>
+		<annotation cp="🇸🇹" type="tts">flag: São Tomé &amp; Príncipe</annotation>
+		<annotation cp="🇸🇻">flag</annotation>
+		<annotation cp="🇸🇻" type="tts">flag: El Salvador</annotation>
+		<annotation cp="🇸🇽">flag</annotation>
+		<annotation cp="🇸🇽" type="tts">flag: Sint Maarten</annotation>
+		<annotation cp="🇸🇾">flag</annotation>
+		<annotation cp="🇸🇾" type="tts">flag: Syria</annotation>
+		<annotation cp="🇸🇿">flag</annotation>
+		<annotation cp="🇸🇿" type="tts">flag: Eswatini</annotation>
+		<annotation cp="🇹🇦">flag</annotation>
+		<annotation cp="🇹🇦" type="tts">flag: Tristan da Cunha</annotation>
+		<annotation cp="🇹🇨">flag</annotation>
+		<annotation cp="🇹🇨" type="tts">flag: Turks &amp; Caicos Islands</annotation>
+		<annotation cp="🇹🇩">flag</annotation>
+		<annotation cp="🇹🇩" type="tts">flag: Chad</annotation>
+		<annotation cp="🇹🇫">flag</annotation>
+		<annotation cp="🇹🇫" type="tts">flag: French Southern Territories</annotation>
+		<annotation cp="🇹🇬">flag</annotation>
+		<annotation cp="🇹🇬" type="tts">flag: Togo</annotation>
+		<annotation cp="🇹🇭">flag</annotation>
+		<annotation cp="🇹🇭" type="tts">flag: Thailand</annotation>
+		<annotation cp="🇹🇯">flag</annotation>
+		<annotation cp="🇹🇯" type="tts">flag: Tajikistan</annotation>
+		<annotation cp="🇹🇰">flag</annotation>
+		<annotation cp="🇹🇰" type="tts">flag: Tokelau</annotation>
+		<annotation cp="🇹🇱">flag</annotation>
+		<annotation cp="🇹🇱" type="tts">flag: Timor-Leste</annotation>
+		<annotation cp="🇹🇲">flag</annotation>
+		<annotation cp="🇹🇲" type="tts">flag: Turkmenistan</annotation>
+		<annotation cp="🇹🇳">flag</annotation>
+		<annotation cp="🇹🇳" type="tts">flag: Tunisia</annotation>
+		<annotation cp="🇹🇴">flag</annotation>
+		<annotation cp="🇹🇴" type="tts">flag: Tonga</annotation>
+		<annotation cp="🇹🇷">flag</annotation>
+		<annotation cp="🇹🇷" type="tts">flag: Turkey</annotation>
+		<annotation cp="🇹🇹">flag</annotation>
+		<annotation cp="🇹🇹" type="tts">flag: Trinidad &amp; Tobago</annotation>
+		<annotation cp="🇹🇻">flag</annotation>
+		<annotation cp="🇹🇻" type="tts">flag: Tuvalu</annotation>
+		<annotation cp="🇹🇼">flag</annotation>
+		<annotation cp="🇹🇼" type="tts">flag: Taiwan</annotation>
+		<annotation cp="🇹🇿">flag</annotation>
+		<annotation cp="🇹🇿" type="tts">flag: Tanzania</annotation>
+		<annotation cp="🇺🇦">flag</annotation>
+		<annotation cp="🇺🇦" type="tts">flag: Ukraine</annotation>
+		<annotation cp="🇺🇬">flag</annotation>
+		<annotation cp="🇺🇬" type="tts">flag: Uganda</annotation>
+		<annotation cp="🇺🇲">flag</annotation>
+		<annotation cp="🇺🇲" type="tts">flag: U.S. Outlying Islands</annotation>
+		<annotation cp="🇺🇳">flag</annotation>
+		<annotation cp="🇺🇳" type="tts">flag: United Nations</annotation>
+		<annotation cp="🇺🇸">flag</annotation>
+		<annotation cp="🇺🇸" type="tts">flag: United States</annotation>
+		<annotation cp="🇺🇾">flag</annotation>
+		<annotation cp="🇺🇾" type="tts">flag: Uruguay</annotation>
+		<annotation cp="🇺🇿">flag</annotation>
+		<annotation cp="🇺🇿" type="tts">flag: Uzbekistan</annotation>
+		<annotation cp="🇻🇦">flag</annotation>
+		<annotation cp="🇻🇦" type="tts">flag: Vatican City</annotation>
+		<annotation cp="🇻🇨">flag</annotation>
+		<annotation cp="🇻🇨" type="tts">flag: St. Vincent &amp; Grenadines</annotation>
+		<annotation cp="🇻🇪">flag</annotation>
+		<annotation cp="🇻🇪" type="tts">flag: Venezuela</annotation>
+		<annotation cp="🇻🇬">flag</annotation>
+		<annotation cp="🇻🇬" type="tts">flag: British Virgin Islands</annotation>
+		<annotation cp="🇻🇮">flag</annotation>
+		<annotation cp="🇻🇮" type="tts">flag: U.S. Virgin Islands</annotation>
+		<annotation cp="🇻🇳">flag</annotation>
+		<annotation cp="🇻🇳" type="tts">flag: Vietnam</annotation>
+		<annotation cp="🇻🇺">flag</annotation>
+		<annotation cp="🇻🇺" type="tts">flag: Vanuatu</annotation>
+		<annotation cp="🇼🇫">flag</annotation>
+		<annotation cp="🇼🇫" type="tts">flag: Wallis &amp; Futuna</annotation>
+		<annotation cp="🇼🇸">flag</annotation>
+		<annotation cp="🇼🇸" type="tts">flag: Samoa</annotation>
+		<annotation cp="🇽🇰">flag</annotation>
+		<annotation cp="🇽🇰" type="tts">flag: Kosovo</annotation>
+		<annotation cp="🇾🇪">flag</annotation>
+		<annotation cp="🇾🇪" type="tts">flag: Yemen</annotation>
+		<annotation cp="🇾🇹">flag</annotation>
+		<annotation cp="🇾🇹" type="tts">flag: Mayotte</annotation>
+		<annotation cp="🇿🇦">flag</annotation>
+		<annotation cp="🇿🇦" type="tts">flag: South Africa</annotation>
+		<annotation cp="🇿🇲">flag</annotation>
+		<annotation cp="🇿🇲" type="tts">flag: Zambia</annotation>
+		<annotation cp="🇿🇼">flag</annotation>
+		<annotation cp="🇿🇼" type="tts">flag: Zimbabwe</annotation>
+		<annotation cp="🏴󠁧󠁢󠁥󠁮󠁧󠁿">flag</annotation>
+		<annotation cp="🏴󠁧󠁢󠁥󠁮󠁧󠁿" type="tts">flag: England</annotation>
+		<annotation cp="🏴󠁧󠁢󠁳󠁣󠁴󠁿">flag</annotation>
+		<annotation cp="🏴󠁧󠁢󠁳󠁣󠁴󠁿" type="tts">flag: Scotland</annotation>
+		<annotation cp="🏴󠁧󠁢󠁷󠁬󠁳󠁿">flag</annotation>
+		<annotation cp="🏴󠁧󠁢󠁷󠁬󠁳󠁿" type="tts">flag: Wales</annotation>
+		<annotation cp="¤" type="tts">Unknown Currency</annotation>
+		<annotation cp="֏" type="tts">Armenian Dram</annotation>
+		<annotation cp="؋" type="tts">Afghan Afghani</annotation>
+		<annotation cp="৳" type="tts">Bangladeshi Taka</annotation>
+		<annotation cp="฿" type="tts">Thai Baht</annotation>
+		<annotation cp="៛" type="tts">Cambodian Riel</annotation>
+		<annotation cp="₡" type="tts">Costa Rican Colón</annotation>
+		<annotation cp="₦" type="tts">Nigerian Naira</annotation>
+		<annotation cp="₧" type="tts">Spanish Peseta</annotation>
+		<annotation cp="₪" type="tts">Israeli New Shekel</annotation>
+		<annotation cp="₫" type="tts">Vietnamese Dong</annotation>
+		<annotation cp="₭" type="tts">Laotian Kip</annotation>
+		<annotation cp="₮" type="tts">Mongolian Tugrik</annotation>
+		<annotation cp="₲" type="tts">Paraguayan Guarani</annotation>
+		<annotation cp="₴" type="tts">Ukrainian Hryvnia</annotation>
+		<annotation cp="₵" type="tts">Ghanaian Cedi</annotation>
+		<annotation cp="₸" type="tts">Kazakhstani Tenge</annotation>
+		<annotation cp="₺" type="tts">Turkish Lira</annotation>
+		<annotation cp="₼" type="tts">Azerbaijani Manat</annotation>
+		<annotation cp="₾" type="tts">Georgian Lari</annotation>
+		<annotation cp="0⃣">keycap</annotation>
+		<annotation cp="0⃣" type="tts">keycap: 0</annotation>
+		<annotation cp="1⃣">keycap</annotation>
+		<annotation cp="1⃣" type="tts">keycap: 1</annotation>
+		<annotation cp="2⃣">keycap</annotation>
+		<annotation cp="2⃣" type="tts">keycap: 2</annotation>
+		<annotation cp="3⃣">keycap</annotation>
+		<annotation cp="3⃣" type="tts">keycap: 3</annotation>
+		<annotation cp="4⃣">keycap</annotation>
+		<annotation cp="4⃣" type="tts">keycap: 4</annotation>
+		<annotation cp="5⃣">keycap</annotation>
+		<annotation cp="5⃣" type="tts">keycap: 5</annotation>
+		<annotation cp="6⃣">keycap</annotation>
+		<annotation cp="6⃣" type="tts">keycap: 6</annotation>
+		<annotation cp="7⃣">keycap</annotation>
+		<annotation cp="7⃣" type="tts">keycap: 7</annotation>
+		<annotation cp="8⃣">keycap</annotation>
+		<annotation cp="8⃣" type="tts">keycap: 8</annotation>
+		<annotation cp="9⃣">keycap</annotation>
+		<annotation cp="9⃣" type="tts">keycap: 9</annotation>
+	</annotations>
+</ldml>
diff --git a/third_party/cldr/src/common/annotationsDerived/en_001.xml b/third_party/cldr/src/common/annotationsDerived/en_001.xml
new file mode 100644
index 0000000..12d005a
--- /dev/null
+++ b/third_party/cldr/src/common/annotationsDerived/en_001.xml
@@ -0,0 +1,295 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE ldml SYSTEM "../../common/dtd/ldml.dtd">
+<!-- Copyright © 1991-2020 Unicode, Inc.
+For terms of use, see http://www.unicode.org/copyright.html
+Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in the U.S. and other countries.
+CLDR data files are interpreted according to the LDML specification (http://unicode.org/reports/tr35/)
+
+Derived short names and annotations, using GenerateDerivedAnnotations.java. See warnings in /annotations/ file.
+-->
+<ldml>
+	<identity>
+		<version number="$Revision$"/>
+		<language type="en"/>
+		<territory type="001"/>
+	</identity>
+	<annotations>
+		<annotation cp="✌🏻">hand | light skin tone | peace hand | peace sign | v | v sign | victory</annotation>
+		<annotation cp="✌🏼">hand | medium-light skin tone | peace hand | peace sign | v | v sign | victory</annotation>
+		<annotation cp="✌🏽">hand | medium skin tone | peace hand | peace sign | v | v sign | victory</annotation>
+		<annotation cp="✌🏾">hand | medium-dark skin tone | peace hand | peace sign | v | v sign | victory</annotation>
+		<annotation cp="✌🏿">dark skin tone | hand | peace hand | peace sign | v | v sign | victory</annotation>
+		<annotation cp="🤘🏻">finger | hand | horns | light skin tone | rock on</annotation>
+		<annotation cp="🤘🏼">finger | hand | horns | medium-light skin tone | rock on</annotation>
+		<annotation cp="🤘🏽">finger | hand | horns | medium skin tone | rock on</annotation>
+		<annotation cp="🤘🏾">finger | hand | horns | medium-dark skin tone | rock on</annotation>
+		<annotation cp="🤘🏿">dark skin tone | finger | hand | horns | rock on</annotation>
+		<annotation cp="🤙🏻">call | call-me hand | hand | light skin tone</annotation>
+		<annotation cp="🤙🏻" type="tts">call-me hand: light skin tone</annotation>
+		<annotation cp="🤙🏼">call | call-me hand | hand | medium-light skin tone</annotation>
+		<annotation cp="🤙🏼" type="tts">call-me hand: medium-light skin tone</annotation>
+		<annotation cp="🤙🏽">call | call-me hand | hand | medium skin tone</annotation>
+		<annotation cp="🤙🏽" type="tts">call-me hand: medium skin tone</annotation>
+		<annotation cp="🤙🏾">call | call-me hand | hand | medium-dark skin tone</annotation>
+		<annotation cp="🤙🏾" type="tts">call-me hand: medium-dark skin tone</annotation>
+		<annotation cp="🤙🏿">call | call-me hand | dark skin tone | hand</annotation>
+		<annotation cp="🤙🏿" type="tts">call-me hand: dark skin tone</annotation>
+		<annotation cp="🙌🏻">celebration | gesture | hand | hooray | light skin tone | raised | raising hands | woo hoo | yay</annotation>
+		<annotation cp="🙌🏼">celebration | gesture | hand | hooray | medium-light skin tone | raised | raising hands | woo hoo | yay</annotation>
+		<annotation cp="🙌🏽">celebration | gesture | hand | hooray | medium skin tone | raised | raising hands | woo hoo | yay</annotation>
+		<annotation cp="🙌🏾">celebration | gesture | hand | hooray | medium-dark skin tone | raised | raising hands | woo hoo | yay</annotation>
+		<annotation cp="🙌🏿">celebration | dark skin tone | gesture | hand | hooray | raised | raising hands | woo hoo | yay</annotation>
+		<annotation cp="🧒🏻">child | gender-neutral | light skin tone | toddler | young</annotation>
+		<annotation cp="🧒🏼">child | gender-neutral | medium-light skin tone | toddler | young</annotation>
+		<annotation cp="🧒🏽">child | gender-neutral | medium skin tone | toddler | young</annotation>
+		<annotation cp="🧒🏾">child | gender-neutral | medium-dark skin tone | toddler | young</annotation>
+		<annotation cp="🧒🏿">child | dark skin tone | gender-neutral | toddler | young</annotation>
+		<annotation cp="🙇🏻‍♂">apology | bowing | favour | gesture | light skin tone | man | sorry</annotation>
+		<annotation cp="🙇🏼‍♂">apology | bowing | favour | gesture | man | medium-light skin tone | sorry</annotation>
+		<annotation cp="🙇🏽‍♂">apology | bowing | favour | gesture | man | medium skin tone | sorry</annotation>
+		<annotation cp="🙇🏾‍♂">apology | bowing | favour | gesture | man | medium-dark skin tone | sorry</annotation>
+		<annotation cp="🙇🏿‍♂">apology | bowing | dark skin tone | favour | gesture | man | sorry</annotation>
+		<annotation cp="🙇🏻‍♀">apology | bowing | favour | gesture | light skin tone | sorry | woman</annotation>
+		<annotation cp="🙇🏼‍♀">apology | bowing | favour | gesture | medium-light skin tone | sorry | woman</annotation>
+		<annotation cp="🙇🏽‍♀">apology | bowing | favour | gesture | medium skin tone | sorry | woman</annotation>
+		<annotation cp="🙇🏾‍♀">apology | bowing | favour | gesture | medium-dark skin tone | sorry | woman</annotation>
+		<annotation cp="🙇🏿‍♀">apology | bowing | dark skin tone | favour | gesture | sorry | woman</annotation>
+		<annotation cp="🧑🏻‍🏫">instructor | lecturer | light skin tone | professor | teacher</annotation>
+		<annotation cp="🧑🏼‍🏫">instructor | lecturer | medium-light skin tone | professor | teacher</annotation>
+		<annotation cp="🧑🏽‍🏫">instructor | lecturer | medium skin tone | professor | teacher</annotation>
+		<annotation cp="🧑🏾‍🏫">instructor | lecturer | medium-dark skin tone | professor | teacher</annotation>
+		<annotation cp="🧑🏿‍🏫">dark skin tone | instructor | lecturer | professor | teacher</annotation>
+		<annotation cp="🧑🏻‍⚖">judge | law | light skin tone</annotation>
+		<annotation cp="🧑🏼‍⚖">judge | law | medium-light skin tone</annotation>
+		<annotation cp="🧑🏽‍⚖">judge | law | medium skin tone</annotation>
+		<annotation cp="🧑🏾‍⚖">judge | law | medium-dark skin tone</annotation>
+		<annotation cp="🧑🏿‍⚖">dark skin tone | judge | law</annotation>
+		<annotation cp="👨🏻‍🌾">farmer | gardener | light skin tone | man</annotation>
+		<annotation cp="👨🏼‍🌾">farmer | gardener | man | medium-light skin tone</annotation>
+		<annotation cp="👨🏽‍🌾">farmer | gardener | man | medium skin tone</annotation>
+		<annotation cp="👨🏾‍🌾">farmer | gardener | man | medium-dark skin tone</annotation>
+		<annotation cp="👨🏿‍🌾">dark skin tone | farmer | gardener | man</annotation>
+		<annotation cp="👩🏻‍🌾">farmer | gardener | light skin tone | woman</annotation>
+		<annotation cp="👩🏼‍🌾">farmer | gardener | medium-light skin tone | woman</annotation>
+		<annotation cp="👩🏽‍🌾">farmer | gardener | medium skin tone | woman</annotation>
+		<annotation cp="👩🏾‍🌾">farmer | gardener | medium-dark skin tone | woman</annotation>
+		<annotation cp="👩🏿‍🌾">dark skin tone | farmer | gardener | woman</annotation>
+		<annotation cp="👨🏻‍🔧">electrician | light skin tone | man | mechanic | plumber | tradesman | tradesperson</annotation>
+		<annotation cp="👨🏼‍🔧">electrician | man | mechanic | medium-light skin tone | plumber | tradesman | tradesperson</annotation>
+		<annotation cp="👨🏽‍🔧">electrician | man | mechanic | medium skin tone | plumber | tradesman | tradesperson</annotation>
+		<annotation cp="👨🏾‍🔧">electrician | man | mechanic | medium-dark skin tone | plumber | tradesman | tradesperson</annotation>
+		<annotation cp="👨🏿‍🔧">dark skin tone | electrician | man | mechanic | plumber | tradesman | tradesperson</annotation>
+		<annotation cp="👩🏻‍🔧">electrician | light skin tone | mechanic | plumber | tradesperson | tradeswoman | woman</annotation>
+		<annotation cp="👩🏼‍🔧">electrician | mechanic | medium-light skin tone | plumber | tradesperson | tradeswoman | woman</annotation>
+		<annotation cp="👩🏽‍🔧">electrician | mechanic | medium skin tone | plumber | tradesperson | tradeswoman | woman</annotation>
+		<annotation cp="👩🏾‍🔧">electrician | mechanic | medium-dark skin tone | plumber | tradesperson | tradeswoman | woman</annotation>
+		<annotation cp="👩🏿‍🔧">dark skin tone | electrician | mechanic | plumber | tradesperson | tradeswoman | woman</annotation>
+		<annotation cp="🧑🏻‍🚒">fire engine | fire truck | firefighter | light skin tone</annotation>
+		<annotation cp="🧑🏼‍🚒">fire engine | fire truck | firefighter | medium-light skin tone</annotation>
+		<annotation cp="🧑🏽‍🚒">fire engine | fire truck | firefighter | medium skin tone</annotation>
+		<annotation cp="🧑🏾‍🚒">fire engine | fire truck | firefighter | medium-dark skin tone</annotation>
+		<annotation cp="🧑🏿‍🚒">dark skin tone | fire engine | fire truck | firefighter</annotation>
+		<annotation cp="👨🏻‍🚒">fire engine | firefighter | fireman | light skin tone | man</annotation>
+		<annotation cp="👨🏼‍🚒">fire engine | firefighter | fireman | man | medium-light skin tone</annotation>
+		<annotation cp="👨🏽‍🚒">fire engine | firefighter | fireman | man | medium skin tone</annotation>
+		<annotation cp="👨🏾‍🚒">fire engine | firefighter | fireman | man | medium-dark skin tone</annotation>
+		<annotation cp="👨🏿‍🚒">dark skin tone | fire engine | firefighter | fireman | man</annotation>
+		<annotation cp="👩🏻‍🚒">fire engine | firefighter | firewoman | light skin tone | woman</annotation>
+		<annotation cp="👩🏼‍🚒">fire engine | firefighter | firewoman | medium-light skin tone | woman</annotation>
+		<annotation cp="👩🏽‍🚒">fire engine | firefighter | firewoman | medium skin tone | woman</annotation>
+		<annotation cp="👩🏾‍🚒">fire engine | firefighter | firewoman | medium-dark skin tone | woman</annotation>
+		<annotation cp="👩🏿‍🚒">dark skin tone | fire engine | firefighter | firewoman | woman</annotation>
+		<annotation cp="👮🏻‍♂">cop | light skin tone | man | officer | police | policeman</annotation>
+		<annotation cp="👮🏼‍♂">cop | man | medium-light skin tone | officer | police | policeman</annotation>
+		<annotation cp="👮🏽‍♂">cop | man | medium skin tone | officer | police | policeman</annotation>
+		<annotation cp="👮🏾‍♂">cop | man | medium-dark skin tone | officer | police | policeman</annotation>
+		<annotation cp="👮🏿‍♂">cop | dark skin tone | man | officer | police | policeman</annotation>
+		<annotation cp="👮🏻‍♀">cop | light skin tone | officer | police | policewoman | woman</annotation>
+		<annotation cp="👮🏼‍♀">cop | medium-light skin tone | officer | police | policewoman | woman</annotation>
+		<annotation cp="👮🏽‍♀">cop | medium skin tone | officer | police | policewoman | woman</annotation>
+		<annotation cp="👮🏾‍♀">cop | medium-dark skin tone | officer | police | policewoman | woman</annotation>
+		<annotation cp="👮🏿‍♀">cop | dark skin tone | officer | police | policewoman | woman</annotation>
+		<annotation cp="💂🏻‍♂">guard | guardsman | light skin tone | man</annotation>
+		<annotation cp="💂🏼‍♂">guard | guardsman | man | medium-light skin tone</annotation>
+		<annotation cp="💂🏽‍♂">guard | guardsman | man | medium skin tone</annotation>
+		<annotation cp="💂🏾‍♂">guard | guardsman | man | medium-dark skin tone</annotation>
+		<annotation cp="💂🏿‍♂">dark skin tone | guard | guardsman | man</annotation>
+		<annotation cp="💂🏻‍♀">guard | guardswoman | light skin tone | woman</annotation>
+		<annotation cp="💂🏼‍♀">guard | guardswoman | medium-light skin tone | woman</annotation>
+		<annotation cp="💂🏽‍♀">guard | guardswoman | medium skin tone | woman</annotation>
+		<annotation cp="💂🏾‍♀">guard | guardswoman | medium-dark skin tone | woman</annotation>
+		<annotation cp="💂🏿‍♀">dark skin tone | guard | guardswoman | woman</annotation>
+		<annotation cp="👷🏻">builder | construction | hat | light skin tone | worker</annotation>
+		<annotation cp="👷🏼">builder | construction | hat | medium-light skin tone | worker</annotation>
+		<annotation cp="👷🏽">builder | construction | hat | medium skin tone | worker</annotation>
+		<annotation cp="👷🏾">builder | construction | hat | medium-dark skin tone | worker</annotation>
+		<annotation cp="👷🏿">builder | construction | dark skin tone | hat | worker</annotation>
+		<annotation cp="👷🏻‍♂">builder | construction | light skin tone | man | worker</annotation>
+		<annotation cp="👷🏼‍♂">builder | construction | man | medium-light skin tone | worker</annotation>
+		<annotation cp="👷🏽‍♂">builder | construction | man | medium skin tone | worker</annotation>
+		<annotation cp="👷🏾‍♂">builder | construction | man | medium-dark skin tone | worker</annotation>
+		<annotation cp="👷🏿‍♂">builder | construction | dark skin tone | man | worker</annotation>
+		<annotation cp="👷🏻‍♀">builder | construction | light skin tone | woman | worker</annotation>
+		<annotation cp="👷🏼‍♀">builder | construction | medium-light skin tone | woman | worker</annotation>
+		<annotation cp="👷🏽‍♀">builder | construction | medium skin tone | woman | worker</annotation>
+		<annotation cp="👷🏾‍♀">builder | construction | medium-dark skin tone | woman | worker</annotation>
+		<annotation cp="👷🏿‍♀">builder | construction | dark skin tone | woman | worker</annotation>
+		<annotation cp="👲🏻">gua pi mao | hat | light skin tone | man | man with Chinese cap | skullcap</annotation>
+		<annotation cp="👲🏼">gua pi mao | hat | man | man with Chinese cap | medium-light skin tone | skullcap</annotation>
+		<annotation cp="👲🏽">gua pi mao | hat | man | man with Chinese cap | medium skin tone | skullcap</annotation>
+		<annotation cp="👲🏾">gua pi mao | hat | man | man with Chinese cap | medium-dark skin tone | skullcap</annotation>
+		<annotation cp="👲🏿">dark skin tone | gua pi mao | hat | man | man with Chinese cap | skullcap</annotation>
+		<annotation cp="🤵🏻">groom | light skin tone | person | person in tux | person in tuxedo | tuxedo</annotation>
+		<annotation cp="🤵🏼">groom | medium-light skin tone | person | person in tux | person in tuxedo | tuxedo</annotation>
+		<annotation cp="🤵🏽">groom | medium skin tone | person | person in tux | person in tuxedo | tuxedo</annotation>
+		<annotation cp="🤵🏾">groom | medium-dark skin tone | person | person in tux | person in tuxedo | tuxedo</annotation>
+		<annotation cp="🤵🏿">dark skin tone | groom | person | person in tux | person in tuxedo | tuxedo</annotation>
+		<annotation cp="🤵🏻‍♂">light skin tone | man | man in tux | man in tuxedo | tuxedo</annotation>
+		<annotation cp="🤵🏼‍♂">man | man in tux | man in tuxedo | medium-light skin tone | tuxedo</annotation>
+		<annotation cp="🤵🏽‍♂">man | man in tux | man in tuxedo | medium skin tone | tuxedo</annotation>
+		<annotation cp="🤵🏾‍♂">man | man in tux | man in tuxedo | medium-dark skin tone | tuxedo</annotation>
+		<annotation cp="🤵🏿‍♂">dark skin tone | man | man in tux | man in tuxedo | tuxedo</annotation>
+		<annotation cp="🤵🏻‍♀">light skin tone | tuxedo | woman | woman in tux | woman in tuxedo</annotation>
+		<annotation cp="🤵🏼‍♀">medium-light skin tone | tuxedo | woman | woman in tux | woman in tuxedo</annotation>
+		<annotation cp="🤵🏽‍♀">medium skin tone | tuxedo | woman | woman in tux | woman in tuxedo</annotation>
+		<annotation cp="🤵🏾‍♀">medium-dark skin tone | tuxedo | woman | woman in tux | woman in tuxedo</annotation>
+		<annotation cp="🤵🏿‍♀">dark skin tone | tuxedo | woman | woman in tux | woman in tuxedo</annotation>
+		<annotation cp="🤱🏻">baby | breast | breastfeeding | light skin tone | nursing</annotation>
+		<annotation cp="🤱🏻" type="tts">breastfeeding: light skin tone</annotation>
+		<annotation cp="🤱🏼">baby | breast | breastfeeding | medium-light skin tone | nursing</annotation>
+		<annotation cp="🤱🏼" type="tts">breastfeeding: medium-light skin tone</annotation>
+		<annotation cp="🤱🏽">baby | breast | breastfeeding | medium skin tone | nursing</annotation>
+		<annotation cp="🤱🏽" type="tts">breastfeeding: medium skin tone</annotation>
+		<annotation cp="🤱🏾">baby | breast | breastfeeding | medium-dark skin tone | nursing</annotation>
+		<annotation cp="🤱🏾" type="tts">breastfeeding: medium-dark skin tone</annotation>
+		<annotation cp="🤱🏿">baby | breast | breastfeeding | dark skin tone | nursing</annotation>
+		<annotation cp="🤱🏿" type="tts">breastfeeding: dark skin tone</annotation>
+		<annotation cp="🎅🏻">celebration | Christmas | claus | father | Father Christmas | light skin tone | santa | Santa Claus</annotation>
+		<annotation cp="🎅🏼">celebration | Christmas | claus | father | Father Christmas | medium-light skin tone | santa | Santa Claus</annotation>
+		<annotation cp="🎅🏽">celebration | Christmas | claus | father | Father Christmas | medium skin tone | santa | Santa Claus</annotation>
+		<annotation cp="🎅🏾">celebration | Christmas | claus | father | Father Christmas | medium-dark skin tone | santa | Santa Claus</annotation>
+		<annotation cp="🎅🏿">celebration | Christmas | claus | dark skin tone | father | Father Christmas | santa | Santa Claus</annotation>
+		<annotation cp="🤶🏻">celebration | Christmas | claus | light skin tone | mother | Mrs | Mrs Claus</annotation>
+		<annotation cp="🤶🏻" type="tts">Mrs Claus: light skin tone</annotation>
+		<annotation cp="🤶🏼">celebration | Christmas | claus | medium-light skin tone | mother | Mrs | Mrs Claus</annotation>
+		<annotation cp="🤶🏼" type="tts">Mrs Claus: medium-light skin tone</annotation>
+		<annotation cp="🤶🏽">celebration | Christmas | claus | medium skin tone | mother | Mrs | Mrs Claus</annotation>
+		<annotation cp="🤶🏽" type="tts">Mrs Claus: medium skin tone</annotation>
+		<annotation cp="🤶🏾">celebration | Christmas | claus | medium-dark skin tone | mother | Mrs | Mrs Claus</annotation>
+		<annotation cp="🤶🏾" type="tts">Mrs Claus: medium-dark skin tone</annotation>
+		<annotation cp="🤶🏿">celebration | Christmas | claus | dark skin tone | mother | Mrs | Mrs Claus</annotation>
+		<annotation cp="🤶🏿" type="tts">Mrs Claus: dark skin tone</annotation>
+		<annotation cp="💇🏻">barber | beauty | haircut | hairdresser | light skin tone | parlour</annotation>
+		<annotation cp="💇🏼">barber | beauty | haircut | hairdresser | medium-light skin tone | parlour</annotation>
+		<annotation cp="💇🏽">barber | beauty | haircut | hairdresser | medium skin tone | parlour</annotation>
+		<annotation cp="💇🏾">barber | beauty | haircut | hairdresser | medium-dark skin tone | parlour</annotation>
+		<annotation cp="💇🏿">barber | beauty | dark skin tone | haircut | hairdresser | parlour</annotation>
+		<annotation cp="🧑🏻‍🦯">accessibility | blind | light skin tone | person with guide cane</annotation>
+		<annotation cp="🧑🏻‍🦯" type="tts">person with guide cane: light skin tone</annotation>
+		<annotation cp="🧑🏼‍🦯">accessibility | blind | medium-light skin tone | person with guide cane</annotation>
+		<annotation cp="🧑🏼‍🦯" type="tts">person with guide cane: medium-light skin tone</annotation>
+		<annotation cp="🧑🏽‍🦯">accessibility | blind | medium skin tone | person with guide cane</annotation>
+		<annotation cp="🧑🏽‍🦯" type="tts">person with guide cane: medium skin tone</annotation>
+		<annotation cp="🧑🏾‍🦯">accessibility | blind | medium-dark skin tone | person with guide cane</annotation>
+		<annotation cp="🧑🏾‍🦯" type="tts">person with guide cane: medium-dark skin tone</annotation>
+		<annotation cp="🧑🏿‍🦯">accessibility | blind | dark skin tone | person with guide cane</annotation>
+		<annotation cp="🧑🏿‍🦯" type="tts">person with guide cane: dark skin tone</annotation>
+		<annotation cp="👨🏻‍🦯">accessibility | blind | light skin tone | man | man with guide cane</annotation>
+		<annotation cp="👨🏻‍🦯" type="tts">man with guide cane: light skin tone</annotation>
+		<annotation cp="👨🏼‍🦯">accessibility | blind | man | man with guide cane | medium-light skin tone</annotation>
+		<annotation cp="👨🏼‍🦯" type="tts">man with guide cane: medium-light skin tone</annotation>
+		<annotation cp="👨🏽‍🦯">accessibility | blind | man | man with guide cane | medium skin tone</annotation>
+		<annotation cp="👨🏽‍🦯" type="tts">man with guide cane: medium skin tone</annotation>
+		<annotation cp="👨🏾‍🦯">accessibility | blind | man | man with guide cane | medium-dark skin tone</annotation>
+		<annotation cp="👨🏾‍🦯" type="tts">man with guide cane: medium-dark skin tone</annotation>
+		<annotation cp="👨🏿‍🦯">accessibility | blind | dark skin tone | man | man with guide cane</annotation>
+		<annotation cp="👨🏿‍🦯" type="tts">man with guide cane: dark skin tone</annotation>
+		<annotation cp="👩🏻‍🦯">accessibility | blind | light skin tone | woman | woman with guide cane</annotation>
+		<annotation cp="👩🏻‍🦯" type="tts">woman with guide cane: light skin tone</annotation>
+		<annotation cp="👩🏼‍🦯">accessibility | blind | medium-light skin tone | woman | woman with guide cane</annotation>
+		<annotation cp="👩🏼‍🦯" type="tts">woman with guide cane: medium-light skin tone</annotation>
+		<annotation cp="👩🏽‍🦯">accessibility | blind | medium skin tone | woman | woman with guide cane</annotation>
+		<annotation cp="👩🏽‍🦯" type="tts">woman with guide cane: medium skin tone</annotation>
+		<annotation cp="👩🏾‍🦯">accessibility | blind | medium-dark skin tone | woman | woman with guide cane</annotation>
+		<annotation cp="👩🏾‍🦯" type="tts">woman with guide cane: medium-dark skin tone</annotation>
+		<annotation cp="👩🏿‍🦯">accessibility | blind | dark skin tone | woman | woman with guide cane</annotation>
+		<annotation cp="👩🏿‍🦯" type="tts">woman with guide cane: dark skin tone</annotation>
+		<annotation cp="🧑🏻‍🦼">accessibility | light skin tone | person in powered wheelchair | wheelchair</annotation>
+		<annotation cp="🧑🏻‍🦼" type="tts">person in powered wheelchair: light skin tone</annotation>
+		<annotation cp="🧑🏼‍🦼">accessibility | medium-light skin tone | person in powered wheelchair | wheelchair</annotation>
+		<annotation cp="🧑🏼‍🦼" type="tts">person in powered wheelchair: medium-light skin tone</annotation>
+		<annotation cp="🧑🏽‍🦼">accessibility | medium skin tone | person in powered wheelchair | wheelchair</annotation>
+		<annotation cp="🧑🏽‍🦼" type="tts">person in powered wheelchair: medium skin tone</annotation>
+		<annotation cp="🧑🏾‍🦼">accessibility | medium-dark skin tone | person in powered wheelchair | wheelchair</annotation>
+		<annotation cp="🧑🏾‍🦼" type="tts">person in powered wheelchair: medium-dark skin tone</annotation>
+		<annotation cp="🧑🏿‍🦼">accessibility | dark skin tone | person in powered wheelchair | wheelchair</annotation>
+		<annotation cp="🧑🏿‍🦼" type="tts">person in powered wheelchair: dark skin tone</annotation>
+		<annotation cp="👨🏻‍🦼">accessibility | light skin tone | man | man in powered wheelchair | wheelchair</annotation>
+		<annotation cp="👨🏻‍🦼" type="tts">man in powered wheelchair: light skin tone</annotation>
+		<annotation cp="👨🏼‍🦼">accessibility | man | man in powered wheelchair | medium-light skin tone | wheelchair</annotation>
+		<annotation cp="👨🏼‍🦼" type="tts">man in powered wheelchair: medium-light skin tone</annotation>
+		<annotation cp="👨🏽‍🦼">accessibility | man | man in powered wheelchair | medium skin tone | wheelchair</annotation>
+		<annotation cp="👨🏽‍🦼" type="tts">man in powered wheelchair: medium skin tone</annotation>
+		<annotation cp="👨🏾‍🦼">accessibility | man | man in powered wheelchair | medium-dark skin tone | wheelchair</annotation>
+		<annotation cp="👨🏾‍🦼" type="tts">man in powered wheelchair: medium-dark skin tone</annotation>
+		<annotation cp="👨🏿‍🦼">accessibility | dark skin tone | man | man in powered wheelchair | wheelchair</annotation>
+		<annotation cp="👨🏿‍🦼" type="tts">man in powered wheelchair: dark skin tone</annotation>
+		<annotation cp="👩🏻‍🦼">accessibility | light skin tone | wheelchair | woman | woman in powered wheelchair</annotation>
+		<annotation cp="👩🏻‍🦼" type="tts">woman in powered wheelchair: light skin tone</annotation>
+		<annotation cp="👩🏼‍🦼">accessibility | medium-light skin tone | wheelchair | woman | woman in powered wheelchair</annotation>
+		<annotation cp="👩🏼‍🦼" type="tts">woman in powered wheelchair: medium-light skin tone</annotation>
+		<annotation cp="👩🏽‍🦼">accessibility | medium skin tone | wheelchair | woman | woman in powered wheelchair</annotation>
+		<annotation cp="👩🏽‍🦼" type="tts">woman in powered wheelchair: medium skin tone</annotation>
+		<annotation cp="👩🏾‍🦼">accessibility | medium-dark skin tone | wheelchair | woman | woman in powered wheelchair</annotation>
+		<annotation cp="👩🏾‍🦼" type="tts">woman in powered wheelchair: medium-dark skin tone</annotation>
+		<annotation cp="👩🏿‍🦼">accessibility | dark skin tone | wheelchair | woman | woman in powered wheelchair</annotation>
+		<annotation cp="👩🏿‍🦼" type="tts">woman in powered wheelchair: dark skin tone</annotation>
+		<annotation cp="🚣🏻">boat | light skin tone | rowboat | rowing boat</annotation>
+		<annotation cp="🚣🏼">boat | medium-light skin tone | rowboat | rowing boat</annotation>
+		<annotation cp="🚣🏽">boat | medium skin tone | rowboat | rowing boat</annotation>
+		<annotation cp="🚣🏾">boat | medium-dark skin tone | rowboat | rowing boat</annotation>
+		<annotation cp="🚣🏿">boat | dark skin tone | rowboat | rowing boat</annotation>
+		<annotation cp="🚣🏻‍♂">boat | light skin tone | man | rowboat | rowing boat</annotation>
+		<annotation cp="🚣🏼‍♂">boat | man | medium-light skin tone | rowboat | rowing boat</annotation>
+		<annotation cp="🚣🏽‍♂">boat | man | medium skin tone | rowboat | rowing boat</annotation>
+		<annotation cp="🚣🏾‍♂">boat | man | medium-dark skin tone | rowboat | rowing boat</annotation>
+		<annotation cp="🚣🏿‍♂">boat | dark skin tone | man | rowboat | rowing boat</annotation>
+		<annotation cp="🚣🏻‍♀">boat | light skin tone | rowboat | rowing boat | woman</annotation>
+		<annotation cp="🚣🏼‍♀">boat | medium-light skin tone | rowboat | rowing boat | woman</annotation>
+		<annotation cp="🚣🏽‍♀">boat | medium skin tone | rowboat | rowing boat | woman</annotation>
+		<annotation cp="🚣🏾‍♀">boat | medium-dark skin tone | rowboat | rowing boat | woman</annotation>
+		<annotation cp="🚣🏿‍♀">boat | dark skin tone | rowboat | rowing boat | woman</annotation>
+		<annotation cp="🏋🏻">light skin tone | person lifting weights | weight | weightlifter</annotation>
+		<annotation cp="🏋🏼">medium-light skin tone | person lifting weights | weight | weightlifter</annotation>
+		<annotation cp="🏋🏽">medium skin tone | person lifting weights | weight | weightlifter</annotation>
+		<annotation cp="🏋🏾">medium-dark skin tone | person lifting weights | weight | weightlifter</annotation>
+		<annotation cp="🏋🏿">dark skin tone | person lifting weights | weight | weightlifter</annotation>
+		<annotation cp="🏋🏻‍♂">light skin tone | man | weightlifter</annotation>
+		<annotation cp="🏋🏼‍♂">man | medium-light skin tone | weightlifter</annotation>
+		<annotation cp="🏋🏽‍♂">man | medium skin tone | weightlifter</annotation>
+		<annotation cp="🏋🏾‍♂">man | medium-dark skin tone | weightlifter</annotation>
+		<annotation cp="🏋🏿‍♂">dark skin tone | man | weightlifter</annotation>
+		<annotation cp="🏋🏻‍♀">light skin tone | weightlifter | woman</annotation>
+		<annotation cp="🏋🏼‍♀">medium-light skin tone | weightlifter | woman</annotation>
+		<annotation cp="🏋🏽‍♀">medium skin tone | weightlifter | woman</annotation>
+		<annotation cp="🏋🏾‍♀">medium-dark skin tone | weightlifter | woman</annotation>
+		<annotation cp="🏋🏿‍♀">dark skin tone | weightlifter | woman</annotation>
+		<annotation cp="🚴🏻">bicycle | biking | cyclist | light skin tone | person cycling</annotation>
+		<annotation cp="🚴🏼">bicycle | biking | cyclist | medium-light skin tone | person cycling</annotation>
+		<annotation cp="🚴🏽">bicycle | biking | cyclist | medium skin tone | person cycling</annotation>
+		<annotation cp="🚴🏾">bicycle | biking | cyclist | medium-dark skin tone | person cycling</annotation>
+		<annotation cp="🚴🏿">bicycle | biking | cyclist | dark skin tone | person cycling</annotation>
+		<annotation cp="🚴🏻‍♂">bicycle | biking | cyclist | light skin tone | man cycling</annotation>
+		<annotation cp="🚴🏼‍♂">bicycle | biking | cyclist | man cycling | medium-light skin tone</annotation>
+		<annotation cp="🚴🏽‍♂">bicycle | biking | cyclist | man cycling | medium skin tone</annotation>
+		<annotation cp="🚴🏾‍♂">bicycle | biking | cyclist | man cycling | medium-dark skin tone</annotation>
+		<annotation cp="🚴🏿‍♂">bicycle | biking | cyclist | dark skin tone | man cycling</annotation>
+		<annotation cp="🚴🏻‍♀">bicycle | biking | cyclist | light skin tone | woman cycling</annotation>
+		<annotation cp="🚴🏼‍♀">bicycle | biking | cyclist | medium-light skin tone | woman cycling</annotation>
+		<annotation cp="🚴🏽‍♀">bicycle | biking | cyclist | medium skin tone | woman cycling</annotation>
+		<annotation cp="🚴🏾‍♀">bicycle | biking | cyclist | medium-dark skin tone | woman cycling</annotation>
+		<annotation cp="🚴🏿‍♀">bicycle | biking | cyclist | dark skin tone | woman cycling</annotation>
+		<annotation cp="🇺🇲" type="tts">flag: US Outlying Islands</annotation>
+		<annotation cp="🇻🇮" type="tts">flag: US Virgin Islands</annotation>
+	</annotations>
+</ldml>
diff --git a/third_party/cldr/update.sh b/third_party/cldr/update.sh
new file mode 100755
index 0000000..6f4dbf6
--- /dev/null
+++ b/third_party/cldr/update.sh
@@ -0,0 +1,31 @@
+#!/bin/bash
+
+# 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.
+
+# Run this script to fetch the latest CLDR files from unicode.org.
+
+# WARNING: This will remove all existing files in //third_party/cldr/src.
+
+# Currently only fetches files needed for emoji keywords in English.
+# If needed, update the unzip line as appropriate.
+
+# CLDR release to checkout. See http://cldr.unicode.org/index/downloads
+CLDR_URL='http://unicode.org/Public/cldr/38.1/cldr-common-38.1.zip'
+# To update the CLDR files, change this URL and also update the Version
+# field in README.chromium. Then run this script and commit the changes.
+
+# Set working directory and terminate on error.
+set -e
+cd "$(dirname "$0")"
+
+# Download release zip.
+curl "$CLDR_URL" -o cldr.zip
+
+# Remove existing src directory.
+rm -rf src
+
+# Unzip relevant files into src directory and clean zip.
+unzip -d src -o cldr.zip common/annotations{Derived,}/{en,en_001}.xml
+rm -v cldr.zip
\ No newline at end of file
diff --git a/third_party/webxr_test_pages/webxr-samples/proposals/phone-ar-depth-gpu.html b/third_party/webxr_test_pages/webxr-samples/proposals/phone-ar-depth-gpu.html
index b8af122a..5461c61 100644
--- a/third_party/webxr_test_pages/webxr-samples/proposals/phone-ar-depth-gpu.html
+++ b/third_party/webxr_test_pages/webxr-samples/proposals/phone-ar-depth-gpu.html
@@ -127,6 +127,10 @@
         let options = {
           requiredFeatures: ['depth-sensing', 'dom-overlay'],
           domOverlay: { root: textOverlayElement },
+          depthSensing: {
+            usagePreference: ["cpu-optimized"],
+            dataFormatPreference: ["luminance-alpha"],
+          }
         };
 
         navigator.xr.requestSession('immersive-ar', options).then((session) => {
@@ -154,6 +158,14 @@
           xrRefSpace = refSpace;
           session.requestAnimationFrame(onXRFrame);
         });
+
+        if(session.depthUsage != "cpu-optimized") {
+          throw new Error("Unsupported depth API usage!");
+        }
+
+        if(session.depthDataFormat != "luminance-alpha") {
+          throw new Error("Unsupported depth data format!");
+        }
       }
 
       function onEndSession(session) {
@@ -206,6 +218,7 @@
             uniformLocations: {
               depthTexture: gl.getUniformLocation(shaderProgram, 'uDepthTexture'),
               uvTransform: gl.getUniformLocation(shaderProgram, 'uUvTransform'),
+              rawValueToMeters: gl.getUniformLocation(shaderProgram, 'uRawValueToMeters'),
             },
         };
 
@@ -355,6 +368,9 @@
         gl.uniformMatrix4fv(programInfo.uniformLocations.uvTransform, false,
                             depthData.normTextureFromNormView.matrix);
 
+        gl.uniform1f(programInfo.uniformLocations.rawValueToMeters,
+                     depthData.rawValueToMeters);
+
         gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
       }
 
diff --git a/third_party/webxr_test_pages/webxr-samples/proposals/phone-ar-depth.html b/third_party/webxr_test_pages/webxr-samples/proposals/phone-ar-depth.html
index 0f9d3fd..a05c805 100644
--- a/third_party/webxr_test_pages/webxr-samples/proposals/phone-ar-depth.html
+++ b/third_party/webxr_test_pages/webxr-samples/proposals/phone-ar-depth.html
@@ -128,6 +128,10 @@
         let options = {
           requiredFeatures: ['depth-sensing', 'dom-overlay'],
           domOverlay: { root: textOverlayElement },
+          depthSensing: {
+            usagePreference: ["cpu-optimized"],
+            dataFormatPreference: ["luminance-alpha"],
+          }
         };
 
         navigator.xr.requestSession('immersive-ar', options).then((session) => {
@@ -155,6 +159,14 @@
           xrRefSpace = refSpace;
           session.requestAnimationFrame(onXRFrame);
         });
+
+        if(session.depthUsage != "cpu-optimized") {
+          throw new Error("Unsupported depth API usage!");
+        }
+
+        if(session.depthDataFormat != "luminance-alpha") {
+          throw new Error("Unsupported depth data format!");
+        }
       }
 
       function onEndSession(session) {
@@ -312,7 +324,7 @@
 
         for(let x = 0; x < depth_width; x = x + RESOLUTION) {
           for(let y = 0; y < depth_height; y = y + RESOLUTION) {
-            const distance = depthData.getDepth(x, y);
+            const distance = depthData.getDepthInMeters(x, y);
 
             const depth_coords = vec3.fromValues(x, y, 0);
 
diff --git a/third_party/webxr_test_pages/webxr-samples/shaders/depth-api-cpu.frag b/third_party/webxr_test_pages/webxr-samples/shaders/depth-api-cpu.frag
index 4981f778..58d1fb1 100644
--- a/third_party/webxr_test_pages/webxr-samples/shaders/depth-api-cpu.frag
+++ b/third_party/webxr_test_pages/webxr-samples/shaders/depth-api-cpu.frag
@@ -2,7 +2,7 @@
 
 varying float vDepthDistance;
 
-const highp float kMaxDepth = 8.0; // In meters.
+const highp float kMaxDepthInMeters = 8.0; // In meters.
 const float kInvalidDepthThreshold = 0.01;
 
 vec3 TurboColormap(in float x);
@@ -17,7 +17,7 @@
 }
 
 void main(void) {
-  highp float normalized_depth = clamp(vDepthDistance / 8.0, 0.0, 1.0);
+  highp float normalized_depth = clamp(vDepthDistance / kMaxDepthInMeters, 0.0, 1.0);
   gl_FragColor = vec4(DepthGetColorVisualization(normalized_depth), 0.75);
 }
 
diff --git a/third_party/webxr_test_pages/webxr-samples/shaders/depth-api-gpu.frag b/third_party/webxr_test_pages/webxr-samples/shaders/depth-api-gpu.frag
index c2d95e30..3e21fb1 100644
--- a/third_party/webxr_test_pages/webxr-samples/shaders/depth-api-gpu.frag
+++ b/third_party/webxr_test_pages/webxr-samples/shaders/depth-api-gpu.frag
@@ -2,17 +2,19 @@
 
 uniform sampler2D uDepthTexture;
 uniform mat4 uUvTransform;
+uniform float uRawValueToMeters;
 
 varying vec2 vTexCoord;
 
-float DepthGetMillimeters(in sampler2D depth_texture, in vec2 depth_uv) {
+float DepthGetMeters(in sampler2D depth_texture, in vec2 depth_uv) {
   // Depth is packed into the luminance and alpha components of its texture.
-  // The texture is a normalized format, storing millimeters.
+  // The texture is in a normalized format, storing raw values that need to be
+  // converted to meters.
   vec2 packedDepthAndVisibility = texture2D(depth_texture, depth_uv).ra;
-  return dot(packedDepthAndVisibility, vec2(255.0, 256.0 * 255.0));
+  return dot(packedDepthAndVisibility, vec2(255.0, 256.0 * 255.0)) * uRawValueToMeters;
 }
 
-const highp float kMaxDepth = 8000.0; // In millimeters.
+const highp float kMaxDepthInMeters = 8.0;
 const float kInvalidDepthThreshold = 0.01;
 
 vec3 TurboColormap(in float x);
@@ -30,7 +32,7 @@
   vec2 texCoord = (uUvTransform * vec4(vTexCoord.xy, 0, 1)).xy;
 
   highp float normalized_depth = clamp(
-    DepthGetMillimeters(uDepthTexture, texCoord) / kMaxDepth, 0.0, 1.0);
+    DepthGetMeters(uDepthTexture, texCoord) / kMaxDepthInMeters, 0.0, 1.0);
   gl_FragColor = vec4(DepthGetColorVisualization(normalized_depth), 0.75);
 }
 
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index ccf53ebc..aaf50a8 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -6799,6 +6799,7 @@
   <int value="238" label="CSDH_BAD_OWNER"/>
   <int value="239" label="SYNC_COMPOSITOR_NO_LOCAL_SURFACE_ID"/>
   <int value="240" label="WCI_INVALID_FULLSCREEN_OPTIONS"/>
+  <int value="241" label="PAYMENTS_WITHOUT_PERMISSION"/>
 </enum>
 
 <enum name="BadMessageReasonExtensions">
@@ -31008,8 +31009,8 @@
   <int value="3682" label="UndeferrableThirdPartySubresourceRequestWithCookie"/>
   <int value="3683" label="XRDepthSensing"/>
   <int value="3684" label="XRFrameGetDepthInformation"/>
-  <int value="3685" label="XRDepthInformationGetDepth"/>
-  <int value="3686" label="XRDepthInformationDataAttribute"/>
+  <int value="3685" label="XRCPUDepthInformationGetDepth"/>
+  <int value="3686" label="XRCPUDepthInformationDataAttribute"/>
   <int value="3687" label="InterestCohortAPI_interestCohort_Method"/>
   <int value="3688"
       label="OBSOLETE_AddressSpaceLocalEmbeddedInPrivateSecureContext"/>
@@ -31128,6 +31129,8 @@
   <int value="3788" label="RTCPeerConnectionUsingComplexUnifiedPlan"/>
   <int value="3789" label="WindowScreenIsExtended"/>
   <int value="3790" label="WindowScreenChange"/>
+  <int value="3791" label="XRWebGLDepthInformationTextureAttribute"/>
+  <int value="3792" label="XRWebGLBindingGetDepthInformation"/>
 </enum>
 
 <enum name="FeaturePolicyAllowlistType">
@@ -43285,7 +43288,6 @@
   <int value="-1480926949" label="MaterialDesignBookmarks:enabled"/>
   <int value="-1480866718" label="ash-disable-login-dim-and-blur"/>
   <int value="-1480606359" label="AssistantIntentPageUrl:enabled"/>
-  <int value="-1478929417" label="MojoLinuxChannelSharedMem:enabled"/>
   <int value="-1478876902" label="disable-permission-action-reporting"/>
   <int value="-1478137998" label="lite-video-default-downlink-bandwidth-kbps"/>
   <int value="-1477686864" label="OmniboxRichAutocompletion:enabled"/>
@@ -44219,6 +44221,7 @@
   <int value="-662064703" label="MediaSessionService:enabled"/>
   <int value="-661978438" label="enable-data-reduction-proxy-lo-fi"/>
   <int value="-660160292" label="enable-apps-show-on-first-paint"/>
+  <int value="-658319177" label="VaapiAV1Decoder:disabled"/>
   <int value="-657808907" label="CopyLinkToText:disabled"/>
   <int value="-654196854" label="PasswordsKeyboardAccessory:enabled"/>
   <int value="-653616608" label="MacSyscallSandbox:disabled"/>
@@ -45332,6 +45335,7 @@
   <int value="415395210" label="TrimOnMemoryPressure:enabled"/>
   <int value="416116189" label="DeprecateLowUsageCodecs:enabled"/>
   <int value="416691040" label="SendTabToSelfOmniboxSendingAnimation:disabled"/>
+  <int value="416760194" label="ExoLockNotification:disabled"/>
   <int value="416887895" label="enable-password-change-support"/>
   <int value="417709910"
       label="AutofillSendExperimentIdsInPaymentsRPCs:disabled"/>
@@ -45563,6 +45567,7 @@
   <int value="636341169" label="ExploreSites:disabled"/>
   <int value="636413416" label="OmniboxKeywordSearchButton:disabled"/>
   <int value="636425179" label="mhtml-generator-option"/>
+  <int value="636909796" label="VaapiAV1Decoder:enabled"/>
   <int value="637396292" label="AllBookmarks:enabled"/>
   <int value="637452937" label="ChromeHomeSurvey:enabled"/>
   <int value="638845342"
@@ -46753,7 +46758,6 @@
       label="OmniboxUIExperimentVerticalMarginLimitToNonTouchOnly:disabled"/>
   <int value="1760946944" label="MacViewsAutofillPopup:disabled"/>
   <int value="1762320532" label="AutofillKeyboardAccessory:enabled"/>
-  <int value="1764618580" label="MojoLinuxChannelSharedMem:disabled"/>
   <int value="1766676896" label="affiliation-based-matching:disabled"/>
   <int value="1767411597" label="DisallowUnsafeHttpDownloads:enabled"/>
   <int value="1768759000" label="AutofillProfileServerValidation:disabled"/>
@@ -46925,6 +46929,7 @@
   <int value="1919917329" label="ImplicitRootScroller:disabled"/>
   <int value="1920894670"
       label="OmniboxPreserveDefaultMatchAgainstAsyncUpdate:enabled"/>
+  <int value="1921543515" label="ExoLockNotification:enabled"/>
   <int value="1923052799" label="CrostiniUseDlc:disabled"/>
   <int value="1923496816" label="AssistantIntentTranslateInfo:disabled"/>
   <int value="1923780021" label="PrivacyReorderedAndroid:enabled"/>
diff --git a/tools/metrics/histograms/histograms_xml/offline/histograms.xml b/tools/metrics/histograms/histograms_xml/offline/histograms.xml
index 3bc80ac5..7acaae2 100644
--- a/tools/metrics/histograms/histograms_xml/offline/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/offline/histograms.xml
@@ -81,6 +81,9 @@
 
 <histogram name="OfflineIndicator.ShownDuration" units="ms"
     expires_after="2021-07-11">
+  <obsolete>
+    Removed M90. Replaced by OfflineIndicator.ShownDurationV2.
+  </obsolete>
   <owner>curranmax@chromium.org</owner>
   <owner>tbansal@chromium.org</owner>
   <owner>sinansahin@google.com</owner>
@@ -91,6 +94,23 @@
   </summary>
 </histogram>
 
+<histogram name="OfflineIndicator.ShownDurationV2" units="ms"
+    expires_after="2021-08-01">
+  <owner>curranmax@chromium.org</owner>
+  <owner>tbansal@chromium.org</owner>
+  <summary>
+    The duration the offline indicator was shown. Recorded when the offline
+    indicator stops being shown. There are two differences between this
+    histogram and OfflineIndicator.ShownDuration: 1) the maximum bucket size and
+    total number of buckets are higher in this histogram, and 2) this histogram
+    is persisted in perfs. The second point means that if the user backgrounds
+    then kills Chrome when the Offline Indicator was shown, then we will still
+    record a sample. Note that in this case, when the user opens Chrome again
+    and if the Offline Indicator is shown, then it will be treated as a
+    continuation from before Chrome was killed.
+  </summary>
+</histogram>
+
 <histogram base="true" name="OfflinePages.AccessCount" units="units"
     expires_after="M85">
   <owner>jianli@chromium.org</owner>
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json
index c8781ad0..131e28e 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": "15f9e69440eee271420ffe8cb70f04779163c246",
-            "remote_path": "perfetto_binaries/trace_processor_shell/win/4825074456917de8843b42e96abea018a7237b75/trace_processor_shell.exe"
+            "hash": "0c6e959dafcea2ff90282821b1b1afc5805d3c69",
+            "remote_path": "perfetto_binaries/trace_processor_shell/win/0f2d499389c5de52c1d42310715bf83835e44c48/trace_processor_shell.exe"
         },
         "mac": {
             "hash": "f2a7837b9050229eaba591e9407581c2295c6314",
-            "remote_path": "perfetto_binaries/trace_processor_shell/mac/91cc5be54402afc43605c15f07548e53741e0430/trace_processor_shell"
+            "remote_path": "perfetto_binaries/trace_processor_shell/mac/d8241a7a3e4d6e6a8dc6ec6e00d90ad7a93166eb/trace_processor_shell"
         },
         "linux": {
-            "hash": "23a050cd7812d362911768fbe6825a7ce145888e",
-            "remote_path": "perfetto_binaries/trace_processor_shell/linux/91cc5be54402afc43605c15f07548e53741e0430/trace_processor_shell"
+            "hash": "05d9967eb1f683f8578de30caef481b874e29fa5",
+            "remote_path": "perfetto_binaries/trace_processor_shell/linux/0f2d499389c5de52c1d42310715bf83835e44c48/trace_processor_shell"
         }
     },
     "power_profile.sql": {
diff --git a/ui/accessibility/ax_tree_serializer.h b/ui/accessibility/ax_tree_serializer.h
index 8158c45..09f0c7b 100644
--- a/ui/accessibility/ax_tree_serializer.h
+++ b/ui/accessibility/ax_tree_serializer.h
@@ -646,8 +646,7 @@
       base::debug::SetCrashKeyString(reparent_err, error.str().substr(0, 230));
       CHECK(false) << error.str();
 #endif  // defined(AX_FAIL_FAST_BUILD)
-      // TODO: re-add this, including crash keys above.
-      // base::debug::DumpWithoutCrashing();
+      base::debug::DumpWithoutCrashing();
       Reset();
       return false;
     }
diff --git a/ui/base/resource/resource_bundle.cc b/ui/base/resource/resource_bundle.cc
index 9f74540..c41afd1c 100644
--- a/ui/base/resource/resource_bundle.cc
+++ b/ui/base/resource/resource_bundle.cc
@@ -1067,11 +1067,15 @@
       // Fall back on the main data pack (shouldn't be any strings here except
       // in unittests).
       data = GetRawDataResource(resource_id);
+#if defined(OS_FUCHSIA)
+      CHECK(!data.empty());
+#else   // !defined(OS_FUCHSIA)
       if (data.empty()) {
         LOG(WARNING) << "unable to find resource: " << resource_id;
         NOTREACHED();
         return base::string16();
       }
+#endif  // !defined(OS_FUCHSIA)
     }
   }
 
diff --git a/ui/gfx/color_transform.cc b/ui/gfx/color_transform.cc
index cac57477..8a072cc 100644
--- a/ui/gfx/color_transform.cc
+++ b/ui/gfx/color_transform.cc
@@ -938,8 +938,16 @@
     int src_bit_depth,
     const ColorSpace& dst,
     int dst_bit_depth) {
-  steps_.push_back(std::make_unique<ColorTransformMatrix>(
-      GetRangeAdjustMatrix(src, src_bit_depth)));
+  // ITU-T H.273: If MatrixCoefficients is equal to 0 (Identity) or 8 (YCgCo),
+  // range adjustment is performed on R,G,B samples rather than Y,U,V samples.
+  const bool src_matrix_is_identity_or_ycgco =
+      src.GetMatrixID() == ColorSpace::MatrixID::GBR ||
+      src.GetMatrixID() == ColorSpace::MatrixID::YCOCG;
+  auto src_range_adjust_matrix = std::make_unique<ColorTransformMatrix>(
+      GetRangeAdjustMatrix(src, src_bit_depth));
+
+  if (!src_matrix_is_identity_or_ycgco)
+    steps_.push_back(std::move(src_range_adjust_matrix));
 
   if (src.GetMatrixID() == ColorSpace::MatrixID::BT2020_CL) {
     // BT2020 CL is a special case.
@@ -949,6 +957,9 @@
         std::make_unique<ColorTransformMatrix>(Invert(GetTransferMatrix(src))));
   }
 
+  if (src_matrix_is_identity_or_ycgco)
+    steps_.push_back(std::move(src_range_adjust_matrix));
+
   // If the target color space is not defined, just apply the adjust and
   // tranfer matrices. This path is used by YUV to RGB color conversion
   // when full color conversion is not enabled.
@@ -1020,6 +1031,17 @@
         std::make_unique<ColorTransformFromLinear>(dst.GetTransferID()));
   }
 
+  // ITU-T H.273: If MatrixCoefficients is equal to 0 (Identity) or 8 (YCgCo),
+  // range adjustment is performed on R,G,B samples rather than Y,U,V samples.
+  const bool dst_matrix_is_identity_or_ycgco =
+      dst.GetMatrixID() == ColorSpace::MatrixID::GBR ||
+      dst.GetMatrixID() == ColorSpace::MatrixID::YCOCG;
+  auto dst_range_adjust_matrix = std::make_unique<ColorTransformMatrix>(
+      Invert(GetRangeAdjustMatrix(dst, dst_bit_depth)));
+
+  if (dst_matrix_is_identity_or_ycgco)
+    steps_.push_back(std::move(dst_range_adjust_matrix));
+
   if (dst.GetMatrixID() == ColorSpace::MatrixID::BT2020_CL) {
     NOTREACHED();
   } else {
@@ -1027,8 +1049,8 @@
         std::make_unique<ColorTransformMatrix>(GetTransferMatrix(dst)));
   }
 
-  steps_.push_back(std::make_unique<ColorTransformMatrix>(
-      Invert(GetRangeAdjustMatrix(dst, dst_bit_depth))));
+  if (!dst_matrix_is_identity_or_ycgco)
+    steps_.push_back(std::move(dst_range_adjust_matrix));
 }
 
 ColorTransformInternal::ColorTransformInternal(const ColorSpace& src,
diff --git a/ui/gfx/color_transform_unittest.cc b/ui/gfx/color_transform_unittest.cc
index ff78354..46dfa60 100644
--- a/ui/gfx/color_transform_unittest.cc
+++ b/ui/gfx/color_transform_unittest.cc
@@ -128,6 +128,42 @@
   EXPECT_GT(tmp.z(), tmp.y());
 }
 
+TEST(SimpleColorSpace, YCOCGLimitedToSRGB) {
+  ColorSpace ycocg(ColorSpace::PrimaryID::BT709,
+                   ColorSpace::TransferID::IEC61966_2_1,
+                   ColorSpace::MatrixID::YCOCG, ColorSpace::RangeID::LIMITED);
+  ColorSpace sRGB = ColorSpace::CreateSRGB();
+  std::unique_ptr<ColorTransform> t(ColorTransform::NewColorTransform(
+      ycocg, sRGB, ColorTransform::Intent::INTENT_ABSOLUTE));
+
+  ColorTransform::TriStim tmp(16.0f / 255.0f, 0.5f, 0.5f);
+  t->Transform(&tmp, 1);
+  EXPECT_NEAR(tmp.x(), 0.0f, kMathEpsilon);
+  EXPECT_NEAR(tmp.y(), 0.0f, kMathEpsilon);
+  EXPECT_NEAR(tmp.z(), 0.0f, kMathEpsilon);
+
+  tmp = ColorTransform::TriStim(235.0f / 255.0f, 0.5f, 0.5f);
+  t->Transform(&tmp, 1);
+  EXPECT_NEAR(tmp.x(), 1.0f, kMathEpsilon);
+  EXPECT_NEAR(tmp.y(), 1.0f, kMathEpsilon);
+  EXPECT_NEAR(tmp.z(), 1.0f, kMathEpsilon);
+
+  // Test a blue color
+  // Use the equations for MatrixCoefficients 8 and VideoFullRangeFlag 0 in
+  // ITU-T H.273:
+  // Equations 11-13: E'_R = 0.0, E'_G = 0.0, E'_B = 1.0
+  // Equations 20-22: R = 16, G = 16, B = 219 + 16 = 235
+  // Equations 44-46:
+  //   Y = Round(0.5 * 16 + 0.25 * (16 + 235)) = Round(70.75) = 71
+  //   Cb = Round(0.5 * 16 - 0.25 * (16 + 235)) + 128 = Round(-54.75) + 128 = 73
+  //   Cr = Round(0.5 * (16 - 235)) + 128 = Round(-109.5) + 128 = 18
+  tmp = ColorTransform::TriStim(71.0f / 255.0f, 73.0f / 255.0f, 18.0f / 255.0f);
+  t->Transform(&tmp, 1);
+  EXPECT_NEAR(tmp.x(), 0.0f, kMathEpsilon);
+  EXPECT_NEAR(tmp.y(), 0.0f, 1.0f / 255.0f);
+  EXPECT_NEAR(tmp.z(), 1.0f, kMathEpsilon);
+}
+
 TEST(SimpleColorSpace, TransferFnCancel) {
   ColorSpace::PrimaryID primary = ColorSpace::PrimaryID::BT709;
   ColorSpace::MatrixID matrix = ColorSpace::MatrixID::RGB;
diff --git a/ui/gfx/render_text.cc b/ui/gfx/render_text.cc
index 35ef455..8b6064d1 100644
--- a/ui/gfx/render_text.cc
+++ b/ui/gfx/render_text.cc
@@ -1190,6 +1190,10 @@
 }
 
 void RenderText::SetDisplayOffset(int horizontal_offset) {
+  SetDisplayOffset({horizontal_offset, display_offset_.y()});
+}
+
+void RenderText::SetDisplayOffset(Vector2d offset) {
   const int extra_content = GetContentWidth() - display_rect_.width();
   const int cursor_width = cursor_enabled_ ? 1 : 0;
 
@@ -1215,24 +1219,30 @@
         break;
     }
   }
-  if (horizontal_offset < min_offset)
-    horizontal_offset = min_offset;
-  else if (horizontal_offset > max_offset)
-    horizontal_offset = max_offset;
+
+  const int horizontal_offset =
+      base::ClampToRange(offset.x(), min_offset, max_offset);
+
+  // y-offset is set only when the vertical alignment is ALIGN_TOP.
+  // TODO(jongkown.lee): Support other vertical alignments.
+  DCHECK(vertical_alignment_ == ALIGN_TOP || offset.y() == 0);
+  const int vertical_offset = base::ClampToRange(
+      offset.y(),
+      std::min(display_rect_.height() - GetStringSize().height(), 0), 0);
 
   cached_bounds_and_offset_valid_ = true;
-  display_offset_.set_x(horizontal_offset);
+  display_offset_ = {horizontal_offset, vertical_offset};
   cursor_bounds_ = GetCursorBounds(selection_model_, true);
 }
 
 Vector2d RenderText::GetLineOffset(size_t line_number) {
   const internal::ShapedText* shaped_text = GetShapedText();
   Vector2d offset = display_rect().OffsetFromOrigin();
-  // TODO(ckocagil): Apply the display offset for multiline scrolling.
   if (!multiline()) {
     offset.Add(GetUpdatedDisplayOffset());
   } else {
     DCHECK_LT(line_number, shaped_text->lines().size());
+    offset.Add(GetUpdatedDisplayOffset());
     offset.Add(
         Vector2d(0, shaped_text->lines()[line_number].preceding_heights));
   }
@@ -2224,9 +2234,8 @@
   if (cached_bounds_and_offset_valid_)
     return;
 
-  // TODO(ckocagil): Add support for scrolling multiline text.
-
   int delta_x = 0;
+  int delta_y = 0;
 
   if (cursor_enabled()) {
     // When cursor is enabled, ensure it is visible. For this, set the valid
@@ -2241,9 +2250,16 @@
       delta_x = display_rect_.right() - cursor_bounds_.right();
     else if (cursor_bounds_.x() < display_rect_.x())
       delta_x = display_rect_.x() - cursor_bounds_.x();
+
+    if (vertical_alignment_ == ALIGN_TOP) {
+      if (cursor_bounds_.bottom() > display_rect_.bottom())
+        delta_y = display_rect_.bottom() - cursor_bounds_.bottom();
+      else if (cursor_bounds_.y() < display_rect_.y())
+        delta_y = display_rect_.y() - cursor_bounds_.y();
+    }
   }
 
-  SetDisplayOffset(display_offset_.x() + delta_x);
+  SetDisplayOffset(display_offset_ + Vector2d(delta_x, delta_y));
 }
 
 internal::GraphemeIterator RenderText::GetGraphemeIteratorAtIndex(
diff --git a/ui/gfx/render_text.h b/ui/gfx/render_text.h
index 29c589e..bb7ce1ca82 100644
--- a/ui/gfx/render_text.h
+++ b/ui/gfx/render_text.h
@@ -582,6 +582,7 @@
 
   const Vector2d& GetUpdatedDisplayOffset();
   void SetDisplayOffset(int horizontal_offset);
+  void SetDisplayOffset(Vector2d offset);
 
   // Returns the line offset from the origin after applying the text alignment
   // and the display offset.
diff --git a/ui/gfx/render_text_test_api.h b/ui/gfx/render_text_test_api.h
index 07c48f4..1de8993 100644
--- a/ui/gfx/render_text_test_api.h
+++ b/ui/gfx/render_text_test_api.h
@@ -66,6 +66,10 @@
     return render_text_->GetShapedText()->lines();
   }
 
+  const Vector2d& display_offset() const {
+    return render_text_->display_offset_;
+  }
+
   SelectionModel EdgeSelectionModel(VisualCursorDirection direction) {
     return render_text_->EdgeSelectionModel(direction);
   }
diff --git a/ui/gfx/render_text_unittest.cc b/ui/gfx/render_text_unittest.cc
index d18be3d..1b25f80 100644
--- a/ui/gfx/render_text_unittest.cc
+++ b/ui/gfx/render_text_unittest.cc
@@ -3332,6 +3332,56 @@
   EXPECT_EQ(original_text_direction, render_text->GetDisplayTextDirection());
 }
 
+TEST_F(RenderTextTest, MoveCursor_UpDown_Scroll) {
+  RenderText* render_text = GetRenderText();
+  render_text->SetDisplayRect(Rect(100, 30));
+  render_text->SetMultiline(true);
+  render_text->SetVerticalAlignment(ALIGN_TOP);
+
+  const size_t kLineSize = 50;
+  std::string text;
+  for (size_t i = 0; i < kLineSize - 1; ++i)
+    text += "a\n";
+
+  render_text->SetText(ASCIIToUTF16(text));
+  EXPECT_EQ(kLineSize, render_text->GetNumLines());
+
+  // Move cursor down with scroll.
+  render_text->SelectRange(Range(0));
+  // |line_height| is the distance from the top.
+  float line_height =
+      render_text->GetLineSizeF(render_text->selection_model()).height();
+  for (size_t i = 1; i < kLineSize; ++i) {
+    SCOPED_TRACE(base::StringPrintf("Testing line [%" PRIuS "]", i));
+    render_text->MoveCursor(CHARACTER_BREAK, CURSOR_DOWN, SELECTION_NONE);
+    ASSERT_EQ(Range(i * 2), render_text->selection());
+    ASSERT_TRUE(render_text->display_rect().Contains(
+        render_text->GetUpdatedCursorBounds()));
+    line_height +=
+        render_text->GetLineSizeF(render_text->selection_model()).height();
+    ASSERT_FLOAT_EQ(test_api()->display_offset().y(),
+                    std::min(0.0f, 30.0f - line_height));
+  }
+
+  // Move cursor up with scroll.
+  // |line_height| is the distance from the bottom.
+  line_height =
+      render_text->GetLineSizeF(render_text->selection_model()).height();
+  int offset_y = test_api()->display_offset().y();
+  for (size_t i = kLineSize - 2; i != size_t{-1}; --i) {
+    SCOPED_TRACE(base::StringPrintf("Testing line [%" PRIuS "]", i));
+    render_text->MoveCursor(CHARACTER_BREAK, CURSOR_UP, SELECTION_NONE);
+    ASSERT_EQ(Range(i * 2), render_text->selection());
+    ASSERT_TRUE(render_text->display_rect().Contains(
+        render_text->GetUpdatedCursorBounds()));
+    line_height +=
+        render_text->GetLineSizeF(render_text->selection_model()).height();
+    ASSERT_FLOAT_EQ(test_api()->display_offset().y(),
+                    offset_y + std::max(0.0f, line_height - 30.0f));
+  }
+  EXPECT_EQ(0, test_api()->display_offset().y());
+}
+
 TEST_F(RenderTextTest, GetDisplayTextDirection) {
   struct {
     const char* text;
diff --git a/ui/views/BUILD.gn b/ui/views/BUILD.gn
index 00c34dc5..03e80401 100644
--- a/ui/views/BUILD.gn
+++ b/ui/views/BUILD.gn
@@ -172,6 +172,7 @@
     "controls/table/table_utils.h",
     "controls/table/table_view.h",
     "controls/table/table_view_observer.h",
+    "controls/textarea/textarea.h",
     "controls/textfield/textfield.h",
     "controls/textfield/textfield_controller.h",
     "controls/textfield/textfield_model.h",
@@ -379,6 +380,7 @@
     "controls/table/table_header.cc",
     "controls/table/table_utils.cc",
     "controls/table/table_view.cc",
+    "controls/textarea/textarea.cc",
     "controls/textfield/textfield.cc",
     "controls/textfield/textfield_controller.cc",
     "controls/textfield/textfield_model.cc",
@@ -1128,8 +1130,10 @@
     "controls/table/table_view_unittest.cc",
     "controls/table/test_table_model.cc",
     "controls/table/test_table_model.h",
+    "controls/textarea/textarea_unittest.cc",
     "controls/textfield/textfield_model_unittest.cc",
     "controls/textfield/textfield_unittest.cc",
+    "controls/textfield/textfield_unittest.h",
     "controls/tree/tree_view_unittest.cc",
     "event_monitor_unittest.cc",
     "focus/focus_manager_unittest.cc",
diff --git a/ui/views/controls/textarea/textarea.cc b/ui/views/controls/textarea/textarea.cc
new file mode 100644
index 0000000..de01539
--- /dev/null
+++ b/ui/views/controls/textarea/textarea.cc
@@ -0,0 +1,114 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/views/controls/textarea/textarea.h"
+
+#include "base/logging.h"
+#include "ui/base/ime/text_edit_commands.h"
+#include "ui/events/event.h"
+#include "ui/events/keycodes/keyboard_codes.h"
+#include "ui/views/metadata/metadata_impl_macros.h"
+
+namespace views {
+
+Textarea::Textarea() {
+  GetRenderText()->SetMultiline(true);
+  GetRenderText()->SetVerticalAlignment(gfx::ALIGN_TOP);
+  GetRenderText()->SetWordWrapBehavior(gfx::WRAP_LONG_WORDS);
+}
+
+size_t Textarea::GetNumLines() {
+  return GetRenderText()->GetNumLines();
+}
+
+bool Textarea::OnMouseWheel(const ui::MouseWheelEvent& event) {
+  GetRenderText()->SetDisplayOffset(GetRenderText()->GetUpdatedDisplayOffset() +
+                                    gfx::Vector2d(0, event.y_offset()));
+  UpdateCursorViewPosition();
+  SchedulePaint();
+  return true;
+}
+
+Textfield::EditCommandResult Textarea::DoExecuteTextEditCommand(
+    ui::TextEditCommand command) {
+  bool rtl = GetTextDirection() == base::i18n::RIGHT_TO_LEFT;
+  gfx::VisualCursorDirection begin = rtl ? gfx::CURSOR_RIGHT : gfx::CURSOR_LEFT;
+  gfx::VisualCursorDirection end = rtl ? gfx::CURSOR_LEFT : gfx::CURSOR_RIGHT;
+
+  switch (command) {
+    case ui::TextEditCommand::MOVE_UP:
+      textfield_model()->MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_UP,
+                                    gfx::SELECTION_NONE);
+      break;
+    case ui::TextEditCommand::MOVE_DOWN:
+      textfield_model()->MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_DOWN,
+                                    gfx::SELECTION_NONE);
+      break;
+    case ui::TextEditCommand::MOVE_UP_AND_MODIFY_SELECTION:
+      textfield_model()->MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_UP,
+                                    gfx::SELECTION_RETAIN);
+      break;
+    case ui::TextEditCommand::MOVE_DOWN_AND_MODIFY_SELECTION:
+      textfield_model()->MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_DOWN,
+                                    gfx::SELECTION_RETAIN);
+      break;
+    case ui::TextEditCommand::
+        MOVE_TO_BEGINNING_OF_DOCUMENT_AND_MODIFY_SELECTION:
+    case ui::TextEditCommand::MOVE_PAGE_UP_AND_MODIFY_SELECTION:
+      textfield_model()->MoveCursor(gfx::FIELD_BREAK, begin,
+                                    kPageSelectionBehavior);
+      break;
+    case ui::TextEditCommand::
+        MOVE_TO_BEGINNING_OF_PARAGRAPH_AND_MODIFY_SELECTION:
+      textfield_model()->MoveCursor(gfx::FIELD_BREAK, begin,
+                                    kMoveParagraphSelectionBehavior);
+      break;
+    case ui::TextEditCommand::MOVE_TO_END_OF_DOCUMENT_AND_MODIFY_SELECTION:
+    case ui::TextEditCommand::MOVE_PAGE_DOWN_AND_MODIFY_SELECTION:
+      textfield_model()->MoveCursor(gfx::FIELD_BREAK, end,
+                                    kPageSelectionBehavior);
+      break;
+    case ui::TextEditCommand::MOVE_TO_END_OF_PARAGRAPH_AND_MODIFY_SELECTION:
+      textfield_model()->MoveCursor(gfx::FIELD_BREAK, end,
+                                    kMoveParagraphSelectionBehavior);
+      break;
+    default:
+      return Textfield::DoExecuteTextEditCommand(command);
+  }
+
+  // TODO(jongkwon.lee): Return |cursor_changed| with actual value. It's okay
+  // for now because |cursor_changed| is detected afterward in
+  // |Textfield::ExecuteTextEditCommand|.
+  return {false, false};
+}
+
+bool Textarea::PreHandleKeyPressed(const ui::KeyEvent& event) {
+  if (event.key_code() == ui::VKEY_RETURN) {
+    DoInsertChar('\n');
+    return true;
+  }
+  return false;
+}
+
+ui::TextEditCommand Textarea::GetCommandForKeyEvent(const ui::KeyEvent& event) {
+  if (event.type() != ui::ET_KEY_PRESSED || event.IsUnicodeKeyCode())
+    return Textfield::GetCommandForKeyEvent(event);
+
+  const bool shift = event.IsShiftDown();
+  switch (event.key_code()) {
+    case ui::VKEY_UP:
+      return shift ? ui::TextEditCommand::MOVE_UP_AND_MODIFY_SELECTION
+                   : ui::TextEditCommand::MOVE_UP;
+    case ui::VKEY_DOWN:
+      return shift ? ui::TextEditCommand::MOVE_DOWN_AND_MODIFY_SELECTION
+                   : ui::TextEditCommand::MOVE_DOWN;
+    default:
+      return Textfield::GetCommandForKeyEvent(event);
+  }
+}
+
+BEGIN_METADATA(Textarea, Textfield)
+END_METADATA
+
+}  // namespace views
diff --git a/ui/views/controls/textarea/textarea.h b/ui/views/controls/textarea/textarea.h
new file mode 100644
index 0000000..9e0699c
--- /dev/null
+++ b/ui/views/controls/textarea/textarea.h
@@ -0,0 +1,36 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_VIEWS_CONTROLS_TEXTAREA_TEXTAREA_H_
+#define UI_VIEWS_CONTROLS_TEXTAREA_TEXTAREA_H_
+
+#include "ui/views/controls/textfield/textfield.h"
+
+namespace views {
+
+// A multiline textfield implementation.
+class VIEWS_EXPORT Textarea : public Textfield {
+ public:
+  METADATA_HEADER(Textarea);
+
+  Textarea();
+  ~Textarea() override = default;
+
+  // Returns the number of lines of the text.
+  size_t GetNumLines();
+
+  // Textfield:
+  bool OnMouseWheel(const ui::MouseWheelEvent& event) override;
+
+ protected:
+  // Textfield:
+  Textfield::EditCommandResult DoExecuteTextEditCommand(
+      ui::TextEditCommand command) override;
+  bool PreHandleKeyPressed(const ui::KeyEvent& event) override;
+  ui::TextEditCommand GetCommandForKeyEvent(const ui::KeyEvent& event) override;
+};
+
+}  // namespace views
+
+#endif  // UI_VIEWS_CONTROLS_TEXTAREA_TEXTAREA_H_
diff --git a/ui/views/controls/textarea/textarea_unittest.cc b/ui/views/controls/textarea/textarea_unittest.cc
new file mode 100644
index 0000000..a75ccf7c3
--- /dev/null
+++ b/ui/views/controls/textarea/textarea_unittest.cc
@@ -0,0 +1,299 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/views/controls/textarea/textarea.h"
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/strings/utf_string_conversions.h"
+#include "build/build_config.h"
+#include "ui/events/event.h"
+#include "ui/gfx/render_text.h"
+#include "ui/strings/grit/ui_strings.h"
+#include "ui/views/controls/textfield/textfield_test_api.h"
+#include "ui/views/controls/textfield/textfield_unittest.h"
+#include "ui/views/style/platform_style.h"
+#include "ui/views/widget/widget.h"
+
+namespace {
+
+const base::char16 kHebrewLetterSamekh = 0x05E1;
+
+}  // namespace
+
+namespace views {
+namespace {
+
+class TextareaTest : public test::TextfieldTest {
+ public:
+  TextareaTest() = default;
+  ~TextareaTest() override = default;
+
+  // TextfieldTest:
+  void SetUp() override {
+    TextfieldTest::SetUp();
+
+    ASSERT_FALSE(textarea_);
+    textarea_ = PrepareTextfields(0, std::make_unique<Textarea>(),
+                                  gfx::Rect(100, 100, 800, 100));
+  }
+
+ protected:
+  void RunMoveUpDownTest(int start_index,
+                         ui::KeyboardCode key_code,
+                         std::vector<int> expected) {
+    DCHECK(key_code == ui::VKEY_UP || key_code == ui::VKEY_DOWN);
+    textarea_->SetSelectedRange(gfx::Range(start_index));
+    for (size_t i = 0; i < expected.size(); ++i) {
+      SCOPED_TRACE(testing::Message()
+                   << (key_code == ui::VKEY_UP ? "MOVE UP " : "MOVE DOWN ")
+                   << i + 1 << " times from Range " << start_index);
+      SendKeyEvent(key_code);
+      EXPECT_EQ(gfx::Range(expected[i]), textarea_->GetSelectedRange());
+    }
+  }
+
+  size_t GetCursorLine() const {
+    return test_api_->GetRenderText()->GetLineContainingCaret(
+        textarea_->GetSelectionModel());
+  }
+
+  // TextfieldTest:
+  void SendHomeEvent(bool shift) override {
+    SendKeyEvent(ui::VKEY_HOME, shift, TestingNativeMac());
+  }
+
+  // TextfieldTest:
+  void SendEndEvent(bool shift) override {
+    SendKeyEvent(ui::VKEY_END, shift, TestingNativeMac());
+  }
+
+  Textarea* textarea_ = nullptr;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(TextareaTest);
+};
+
+}  // namespace
+
+// Disabled when using XKB for crbug.com/1171828.
+#if BUILDFLAG(USE_XKBCOMMON)
+#define MAYBE_InsertNewlineTest DISABLED_InsertNewlineTest
+#else
+#define MAYBE_InsertNewlineTest InsertNewlineTest
+#endif  // BUILDFLAG(USE_XKBCOMMON)
+TEST_F(TextareaTest, MAYBE_InsertNewlineTest) {
+  for (size_t i = 0; i < 5; i++) {
+    SendKeyEvent(static_cast<ui::KeyboardCode>(ui::VKEY_A + i));
+    SendKeyEvent(ui::VKEY_RETURN);
+  }
+  EXPECT_STR_EQ("a\nb\nc\nd\ne\n", textarea_->GetText());
+}
+
+TEST_F(TextareaTest, PasteNewlineTest) {
+  const std::string& kText = "abc\n   \n";
+  textarea_->SetText(base::ASCIIToUTF16(kText));
+  textarea_->SelectAll(false);
+  textarea_->ExecuteCommand(Textfield::kCopy, 0);
+  textarea_->SetText(base::string16());
+  textarea_->ExecuteCommand(Textfield::kPaste, 0);
+  EXPECT_STR_EQ(kText, textarea_->GetText());
+}
+
+// Re-enable when crbug.com/1163587 is fixed.
+TEST_F(TextareaTest, DISABLED_CursorMovement) {
+  textarea_->SetText(base::ASCIIToUTF16("one\n\ntwo three"));
+
+  // Move Up/Down at the front of the line.
+  RunMoveUpDownTest(0, ui::VKEY_DOWN, {4, 5, 14});
+  RunMoveUpDownTest(5, ui::VKEY_UP, {4, 0, 0});
+
+  // Move Up/Down at the end of the line.
+  RunMoveUpDownTest(3, ui::VKEY_DOWN, {4, 8, 14});
+  RunMoveUpDownTest(14, ui::VKEY_UP, {4, 3, 0});
+
+  // Move Up/Down at the middle position.
+  RunMoveUpDownTest(2, ui::VKEY_DOWN, {4, 7, 14});
+  RunMoveUpDownTest(7, ui::VKEY_UP, {4, 2, 0});
+
+  // Test Home/End key on each lines.
+  textarea_->SetSelectedRange(gfx::Range(2));  // First line.
+  SendHomeEvent(false);
+  EXPECT_EQ(gfx::Range(0), textarea_->GetSelectedRange());
+  SendEndEvent(false);
+  EXPECT_EQ(gfx::Range(3), textarea_->GetSelectedRange());
+  textarea_->SetSelectedRange(gfx::Range(4));  // 2nd line.
+  SendHomeEvent(false);
+  EXPECT_EQ(gfx::Range(4), textarea_->GetSelectedRange());
+  SendEndEvent(false);
+  EXPECT_EQ(gfx::Range(4), textarea_->GetSelectedRange());
+  textarea_->SetSelectedRange(gfx::Range(7));  // 3rd line.
+  SendHomeEvent(false);
+  EXPECT_EQ(gfx::Range(5), textarea_->GetSelectedRange());
+  SendEndEvent(false);
+  EXPECT_EQ(gfx::Range(14), textarea_->GetSelectedRange());
+}
+
+// Ensure cursor view is always inside display rect.
+TEST_F(TextareaTest, CursorViewBounds) {
+  textarea_->SetBounds(0, 0, 100, 31);
+  for (size_t i = 0; i < 10; ++i) {
+    SCOPED_TRACE(base::StringPrintf("VKEY_RETURN %" PRIuS " times", i + 1));
+    SendKeyEvent(ui::VKEY_RETURN);
+    ASSERT_TRUE(textarea_->GetVisibleBounds().Contains(GetCursorViewRect()));
+    ASSERT_FALSE(GetCursorViewRect().size().IsEmpty());
+  }
+
+  for (size_t i = 0; i < 10; ++i) {
+    SCOPED_TRACE(base::StringPrintf("VKEY_UP %" PRIuS " times", i + 1));
+    SendKeyEvent(ui::VKEY_UP);
+    ASSERT_TRUE(textarea_->GetVisibleBounds().Contains(GetCursorViewRect()));
+    ASSERT_FALSE(GetCursorViewRect().size().IsEmpty());
+  }
+}
+
+TEST_F(TextareaTest, LineSelection) {
+  textarea_->SetText(base::ASCIIToUTF16("12\n34567 89"));
+
+  // Place the cursor after "5".
+  textarea_->SetEditableSelectionRange(gfx::Range(6));
+
+  // Select line towards right.
+  SendEndEvent(true);
+  EXPECT_STR_EQ("67 89", textarea_->GetSelectedText());
+
+  // Select line towards left. On Mac, the existing selection should be extended
+  // to cover the whole line.
+  SendHomeEvent(true);
+
+  if (Textarea::kLineSelectionBehavior == gfx::SELECTION_EXTEND)
+    EXPECT_STR_EQ("34567 89", textarea_->GetSelectedText());
+  else
+    EXPECT_STR_EQ("345", textarea_->GetSelectedText());
+
+  EXPECT_TRUE(textarea_->GetSelectedRange().is_reversed());
+
+  // Select line towards right.
+  SendEndEvent(true);
+
+  if (Textarea::kLineSelectionBehavior == gfx::SELECTION_EXTEND)
+    EXPECT_STR_EQ("34567 89", textarea_->GetSelectedText());
+  else
+    EXPECT_STR_EQ("67 89", textarea_->GetSelectedText());
+
+  EXPECT_FALSE(textarea_->GetSelectedRange().is_reversed());
+}
+
+// Disabled on Mac for crbug.com/1171826.
+#if defined(OS_MAC)
+#define MAYBE_MoveUpDownAndModifySelection DISABLED_MoveUpDownAndModifySelection
+#else
+#define MAYBE_MoveUpDownAndModifySelection MoveUpDownAndModifySelection
+#endif  // defined(OS_MAC)
+TEST_F(TextareaTest, MAYBE_MoveUpDownAndModifySelection) {
+  textarea_->SetText(base::ASCIIToUTF16("12\n34567 89"));
+  textarea_->SetEditableSelectionRange(gfx::Range(6));
+  EXPECT_EQ(1U, GetCursorLine());
+
+  // Up key should place the cursor after "2" not after newline to place the
+  // cursor on the first line.
+  SendKeyEvent(ui::VKEY_UP);
+  EXPECT_EQ(0U, GetCursorLine());
+  EXPECT_EQ(gfx::Range(2), textarea_->GetSelectedRange());
+
+  // Down key after Up key should select the same range as the previous one.
+  SendKeyEvent(ui::VKEY_DOWN);
+  EXPECT_EQ(1U, GetCursorLine());
+  EXPECT_EQ(gfx::Range(6), textarea_->GetSelectedRange());
+
+  // Shift+Up should select the text to the upper line position including
+  // the newline character.
+  SendKeyEvent(ui::VKEY_UP, true /* shift */, false /* command */);
+  EXPECT_EQ(gfx::Range(6, 2), textarea_->GetSelectedRange());
+
+  // Shift+Down should collapse the selection.
+  SendKeyEvent(ui::VKEY_DOWN, true /* shift */, false /* command */);
+  EXPECT_EQ(gfx::Range(6), textarea_->GetSelectedRange());
+
+  // Shift+Down again should select the text to the end of the last line.
+  SendKeyEvent(ui::VKEY_DOWN, true /* shift */, false /* command */);
+  EXPECT_EQ(gfx::Range(6, 11), textarea_->GetSelectedRange());
+}
+
+TEST_F(TextareaTest, MovePageUpDownAndModifySelection) {
+  textarea_->SetText(base::ASCIIToUTF16("12\n34567 89"));
+  textarea_->SetEditableSelectionRange(gfx::Range(6));
+
+  EXPECT_TRUE(
+      textarea_->IsTextEditCommandEnabled(ui::TextEditCommand::MOVE_PAGE_UP));
+  EXPECT_TRUE(
+      textarea_->IsTextEditCommandEnabled(ui::TextEditCommand::MOVE_PAGE_DOWN));
+  EXPECT_TRUE(textarea_->IsTextEditCommandEnabled(
+      ui::TextEditCommand::MOVE_PAGE_UP_AND_MODIFY_SELECTION));
+  EXPECT_TRUE(textarea_->IsTextEditCommandEnabled(
+      ui::TextEditCommand::MOVE_PAGE_DOWN_AND_MODIFY_SELECTION));
+
+  test_api_->ExecuteTextEditCommand(ui::TextEditCommand::MOVE_PAGE_UP);
+  EXPECT_EQ(gfx::Range(0), textarea_->GetSelectedRange());
+
+  test_api_->ExecuteTextEditCommand(ui::TextEditCommand::MOVE_PAGE_DOWN);
+  EXPECT_EQ(gfx::Range(11), textarea_->GetSelectedRange());
+
+  textarea_->SetEditableSelectionRange(gfx::Range(6));
+  test_api_->ExecuteTextEditCommand(
+      ui::TextEditCommand::MOVE_PAGE_UP_AND_MODIFY_SELECTION);
+  EXPECT_EQ(gfx::Range(6, 0), textarea_->GetSelectedRange());
+
+  test_api_->ExecuteTextEditCommand(
+      ui::TextEditCommand::MOVE_PAGE_DOWN_AND_MODIFY_SELECTION);
+
+  if (Textarea::kLineSelectionBehavior == gfx::SELECTION_EXTEND)
+    EXPECT_EQ(gfx::Range(0, 11), textarea_->GetSelectedRange());
+  else
+    EXPECT_EQ(gfx::Range(6, 11), textarea_->GetSelectedRange());
+}
+
+// Ensure the textarea breaks the long word and scrolls on overflow.
+TEST_F(TextareaTest, OverflowTest) {
+  const size_t count = 50U;
+  textarea_->SetBounds(0, 0, 60, 40);
+
+  textarea_->SetText(base::string16(count, 'a'));
+  EXPECT_TRUE(GetDisplayRect().Contains(GetCursorBounds()));
+
+  textarea_->SetText(base::string16(count, kHebrewLetterSamekh));
+  EXPECT_TRUE(GetDisplayRect().Contains(GetCursorBounds()));
+}
+
+TEST_F(TextareaTest, OverflowInRTLTest) {
+  const size_t count = 50U;
+  textarea_->SetBounds(0, 0, 60, 40);
+  std::string locale = base::i18n::GetConfiguredLocale();
+  base::i18n::SetICUDefaultLocale("he");
+
+  textarea_->SetText(base::string16(count, 'a'));
+  EXPECT_TRUE(GetDisplayRect().Contains(GetCursorBounds()));
+
+  textarea_->SetText(base::string16(count, kHebrewLetterSamekh));
+  EXPECT_TRUE(GetDisplayRect().Contains(GetCursorBounds()));
+
+  // Reset locale.
+  base::i18n::SetICUDefaultLocale(locale);
+}
+
+TEST_F(TextareaTest, OnBlurTest) {
+  const std::string& kText = "abcdef";
+  textarea_->SetText(base::ASCIIToUTF16(kText));
+
+  SendEndEvent(false);
+  EXPECT_EQ(kText.size(), textarea_->GetCursorPosition());
+
+  // A focus loss should not change the cursor position.
+  textarea_->OnBlur();
+  EXPECT_EQ(kText.size(), textarea_->GetCursorPosition());
+}
+
+}  // namespace views
diff --git a/ui/views/controls/textfield/textfield.cc b/ui/views/controls/textfield/textfield.cc
index 76d8083..c714fec 100644
--- a/ui/views/controls/textfield/textfield.cc
+++ b/ui/views/controls/textfield/textfield.cc
@@ -118,159 +118,6 @@
   kTextfieldSelectedRange,
 };
 
-#if defined(OS_APPLE)
-constexpr gfx::SelectionBehavior kLineSelectionBehavior = gfx::SELECTION_EXTEND;
-constexpr gfx::SelectionBehavior kWordSelectionBehavior = gfx::SELECTION_CARET;
-constexpr gfx::SelectionBehavior kMoveParagraphSelectionBehavior =
-    gfx::SELECTION_CARET;
-#else
-constexpr gfx::SelectionBehavior kLineSelectionBehavior = gfx::SELECTION_RETAIN;
-constexpr gfx::SelectionBehavior kWordSelectionBehavior = gfx::SELECTION_RETAIN;
-constexpr gfx::SelectionBehavior kMoveParagraphSelectionBehavior =
-    gfx::SELECTION_RETAIN;
-#endif
-
-// Get the default command for a given key |event|.
-ui::TextEditCommand GetCommandForKeyEvent(const ui::KeyEvent& event) {
-  if (event.type() != ui::ET_KEY_PRESSED || event.IsUnicodeKeyCode())
-    return ui::TextEditCommand::INVALID_COMMAND;
-
-  const bool shift = event.IsShiftDown();
-#if defined(OS_APPLE)
-  const bool command = event.IsCommandDown();
-#endif
-  const bool control = event.IsControlDown() || event.IsCommandDown();
-  const bool alt = event.IsAltDown() || event.IsAltGrDown();
-  switch (event.key_code()) {
-    case ui::VKEY_Z:
-      if (control && !shift && !alt)
-        return ui::TextEditCommand::UNDO;
-      return (control && shift && !alt) ? ui::TextEditCommand::REDO
-                                        : ui::TextEditCommand::INVALID_COMMAND;
-    case ui::VKEY_Y:
-      return (control && !alt) ? ui::TextEditCommand::REDO
-                               : ui::TextEditCommand::INVALID_COMMAND;
-    case ui::VKEY_A:
-      return (control && !alt) ? ui::TextEditCommand::SELECT_ALL
-                               : ui::TextEditCommand::INVALID_COMMAND;
-    case ui::VKEY_X:
-      return (control && !alt) ? ui::TextEditCommand::CUT
-                               : ui::TextEditCommand::INVALID_COMMAND;
-    case ui::VKEY_C:
-      return (control && !alt) ? ui::TextEditCommand::COPY
-                               : ui::TextEditCommand::INVALID_COMMAND;
-    case ui::VKEY_V:
-      return (control && !alt) ? ui::TextEditCommand::PASTE
-                               : ui::TextEditCommand::INVALID_COMMAND;
-    case ui::VKEY_RIGHT:
-      // Ignore alt+right, which may be a browser navigation shortcut.
-      if (alt)
-        return ui::TextEditCommand::INVALID_COMMAND;
-      if (!shift) {
-        return control ? ui::TextEditCommand::MOVE_WORD_RIGHT
-                       : ui::TextEditCommand::MOVE_RIGHT;
-      }
-      return control ? ui::TextEditCommand::MOVE_WORD_RIGHT_AND_MODIFY_SELECTION
-                     : ui::TextEditCommand::MOVE_RIGHT_AND_MODIFY_SELECTION;
-    case ui::VKEY_LEFT:
-      // Ignore alt+left, which may be a browser navigation shortcut.
-      if (alt)
-        return ui::TextEditCommand::INVALID_COMMAND;
-      if (!shift) {
-        return control ? ui::TextEditCommand::MOVE_WORD_LEFT
-                       : ui::TextEditCommand::MOVE_LEFT;
-      }
-      return control ? ui::TextEditCommand::MOVE_WORD_LEFT_AND_MODIFY_SELECTION
-                     : ui::TextEditCommand::MOVE_LEFT_AND_MODIFY_SELECTION;
-    case ui::VKEY_HOME:
-      if (shift) {
-        return ui::TextEditCommand::
-            MOVE_TO_BEGINNING_OF_LINE_AND_MODIFY_SELECTION;
-      }
-#if defined(OS_APPLE)
-      return ui::TextEditCommand::SCROLL_TO_BEGINNING_OF_DOCUMENT;
-#else
-      return ui::TextEditCommand::MOVE_TO_BEGINNING_OF_LINE;
-#endif
-    case ui::VKEY_END:
-      if (shift)
-        return ui::TextEditCommand::MOVE_TO_END_OF_LINE_AND_MODIFY_SELECTION;
-#if defined(OS_APPLE)
-      return ui::TextEditCommand::SCROLL_TO_END_OF_DOCUMENT;
-#else
-      return ui::TextEditCommand::MOVE_TO_END_OF_LINE;
-#endif
-    case ui::VKEY_UP:
-#if defined(OS_APPLE)
-      if (control && shift) {
-        return ui::TextEditCommand::
-            MOVE_PARAGRAPH_BACKWARD_AND_MODIFY_SELECTION;
-      }
-      if (command)
-        return ui::TextEditCommand::MOVE_TO_BEGINNING_OF_DOCUMENT;
-      return shift ? ui::TextEditCommand::
-                         MOVE_TO_BEGINNING_OF_LINE_AND_MODIFY_SELECTION
-                   : ui::TextEditCommand::MOVE_UP;
-#else
-      return shift ? ui::TextEditCommand::
-                         MOVE_TO_BEGINNING_OF_LINE_AND_MODIFY_SELECTION
-                   : ui::TextEditCommand::INVALID_COMMAND;
-#endif
-    case ui::VKEY_DOWN:
-#if defined(OS_APPLE)
-      if (control && shift) {
-        return ui::TextEditCommand::MOVE_PARAGRAPH_FORWARD_AND_MODIFY_SELECTION;
-      }
-      if (command)
-        return ui::TextEditCommand::MOVE_TO_END_OF_DOCUMENT;
-      return shift
-                 ? ui::TextEditCommand::MOVE_TO_END_OF_LINE_AND_MODIFY_SELECTION
-                 : ui::TextEditCommand::MOVE_DOWN;
-#else
-      return shift
-                 ? ui::TextEditCommand::MOVE_TO_END_OF_LINE_AND_MODIFY_SELECTION
-                 : ui::TextEditCommand::INVALID_COMMAND;
-#endif
-    case ui::VKEY_BACK:
-      if (!control) {
-#if defined(OS_WIN)
-        if (alt)
-          return shift ? ui::TextEditCommand::REDO : ui::TextEditCommand::UNDO;
-#endif
-        return ui::TextEditCommand::DELETE_BACKWARD;
-      }
-#if defined(OS_LINUX) || defined(OS_CHROMEOS)
-      // Only erase by line break on Linux and ChromeOS.
-      if (shift)
-        return ui::TextEditCommand::DELETE_TO_BEGINNING_OF_LINE;
-#endif
-      return ui::TextEditCommand::DELETE_WORD_BACKWARD;
-    case ui::VKEY_DELETE:
-#if defined(OS_LINUX) || defined(OS_CHROMEOS)
-      // Only erase by line break on Linux and ChromeOS.
-      if (shift && control)
-        return ui::TextEditCommand::DELETE_TO_END_OF_LINE;
-#endif
-      if (control)
-        return ui::TextEditCommand::DELETE_WORD_FORWARD;
-      return shift ? ui::TextEditCommand::CUT
-                   : ui::TextEditCommand::DELETE_FORWARD;
-    case ui::VKEY_INSERT:
-      if (control && !shift)
-        return ui::TextEditCommand::COPY;
-      return (shift && !control) ? ui::TextEditCommand::PASTE
-                                 : ui::TextEditCommand::INVALID_COMMAND;
-    case ui::VKEY_PRIOR:
-      return control ? ui::TextEditCommand::SCROLL_PAGE_UP
-                     : ui::TextEditCommand::INVALID_COMMAND;
-    case ui::VKEY_NEXT:
-      return control ? ui::TextEditCommand::SCROLL_PAGE_DOWN
-                     : ui::TextEditCommand::INVALID_COMMAND;
-    default:
-      return ui::TextEditCommand::INVALID_COMMAND;
-  }
-}
-
 // Returns the ui::TextEditCommand corresponding to the |command_id| menu
 // action. |has_selection| is true if the textfield has an active selection.
 // Keep in sync with UpdateContextMenu.
@@ -749,9 +596,15 @@
   // beyond their legibility, or enlarging controls dynamically with content.
   gfx::Rect bounds = GetLocalBounds();
   const gfx::Insets insets = GetInsets();
-  // The text will draw with the correct vertical alignment if we don't apply
-  // the vertical insets.
-  bounds.Inset(insets.left(), 0, insets.right(), 0);
+
+  if (GetRenderText()->multiline()) {
+    bounds.Inset(insets);
+  } else {
+    // The text will draw with the correct vertical alignment if we don't apply
+    // the vertical insets.
+    bounds.Inset(insets.left(), 0, insets.right(), 0);
+  }
+
   bounds.set_x(GetMirroredXForRect(bounds));
   GetRenderText()->SetDisplayRect(bounds);
   UpdateAfterChange(TextChangeType::kNone, true);
@@ -853,6 +706,9 @@
 }
 
 bool Textfield::OnKeyPressed(const ui::KeyEvent& event) {
+  if (PreHandleKeyPressed(event))
+    return true;
+
   ui::TextEditCommand edit_command = scheduled_text_edit_command_;
   scheduled_text_edit_command_ = ui::TextEditCommand::INVALID_COMMAND;
 
@@ -1241,7 +1097,8 @@
   render_text->set_focused(false);
 
   // If necessary, yank the cursor to the logical start of the textfield.
-  if (PlatformStyle::kTextfieldScrollsToStartOnFocusChange)
+  if (PlatformStyle::kTextfieldScrollsToStartOnFocusChange &&
+      !render_text->multiline())
     model_->MoveCursorTo(gfx::SelectionModel(0, gfx::CURSOR_FORWARD));
 
   if (GetInputMethod()) {
@@ -1871,7 +1728,7 @@
 #if defined(OS_APPLE)
       return true;
 #else
-      return false;
+      return GetRenderText()->multiline();
 #endif
     case ui::TextEditCommand::INSERT_TEXT:
     case ui::TextEditCommand::SET_MARK:
@@ -2006,6 +1863,30 @@
 void Textfield::ExecuteTextEditCommand(ui::TextEditCommand command) {
   DestroyTouchSelection();
 
+  // We only execute the commands enabled in Textfield::IsTextEditCommandEnabled
+  // below. Hence don't do a virtual IsTextEditCommandEnabled call.
+  if (!IsTextEditCommandEnabled(command))
+    return;
+
+  OnBeforeUserAction();
+
+  gfx::SelectionModel selection_model = GetSelectionModel();
+  bool text_changed, cursor_changed;
+  std::tie(text_changed, cursor_changed) = DoExecuteTextEditCommand(command);
+
+  cursor_changed |= (GetSelectionModel() != selection_model);
+  if (cursor_changed && HasSelection())
+    UpdateSelectionClipboard();
+  UpdateAfterChange(
+      text_changed ? TextChangeType::kUserTriggered : TextChangeType::kNone,
+      cursor_changed);
+  OnAfterUserAction();
+}
+
+Textfield::EditCommandResult Textfield::DoExecuteTextEditCommand(
+    ui::TextEditCommand command) {
+  bool changed = false;
+  bool cursor_changed = false;
   bool add_to_kill_buffer = false;
 
   base::AutoReset<bool> show_rejection_ui(&show_rejection_ui_if_any_, true);
@@ -2028,48 +1909,40 @@
       break;
   }
 
-  // We only execute the commands enabled in Textfield::IsTextEditCommandEnabled
-  // below. Hence don't do a virtual IsTextEditCommandEnabled call.
-  if (!IsTextEditCommandEnabled(command))
-    return;
-
-  bool changed = false;
   bool rtl = GetTextDirection() == base::i18n::RIGHT_TO_LEFT;
   gfx::VisualCursorDirection begin = rtl ? gfx::CURSOR_RIGHT : gfx::CURSOR_LEFT;
   gfx::VisualCursorDirection end = rtl ? gfx::CURSOR_LEFT : gfx::CURSOR_RIGHT;
-  gfx::SelectionModel selection_model = GetSelectionModel();
 
-  OnBeforeUserAction();
   switch (command) {
     case ui::TextEditCommand::DELETE_BACKWARD:
-      changed = model_->Backspace(add_to_kill_buffer);
+      changed = cursor_changed = model_->Backspace(add_to_kill_buffer);
       break;
     case ui::TextEditCommand::DELETE_FORWARD:
-      changed = model_->Delete(add_to_kill_buffer);
+      changed = cursor_changed = model_->Delete(add_to_kill_buffer);
       break;
     case ui::TextEditCommand::DELETE_TO_BEGINNING_OF_LINE:
       model_->MoveCursor(gfx::LINE_BREAK, begin, gfx::SELECTION_RETAIN);
-      changed = model_->Backspace(add_to_kill_buffer);
+      changed = cursor_changed = model_->Backspace(add_to_kill_buffer);
       break;
     case ui::TextEditCommand::DELETE_TO_BEGINNING_OF_PARAGRAPH:
       model_->MoveCursor(gfx::FIELD_BREAK, begin, gfx::SELECTION_RETAIN);
-      changed = model_->Backspace(add_to_kill_buffer);
+      changed = cursor_changed = model_->Backspace(add_to_kill_buffer);
       break;
     case ui::TextEditCommand::DELETE_TO_END_OF_LINE:
       model_->MoveCursor(gfx::LINE_BREAK, end, gfx::SELECTION_RETAIN);
-      changed = model_->Delete(add_to_kill_buffer);
+      changed = cursor_changed = model_->Delete(add_to_kill_buffer);
       break;
     case ui::TextEditCommand::DELETE_TO_END_OF_PARAGRAPH:
       model_->MoveCursor(gfx::FIELD_BREAK, end, gfx::SELECTION_RETAIN);
-      changed = model_->Delete(add_to_kill_buffer);
+      changed = cursor_changed = model_->Delete(add_to_kill_buffer);
       break;
     case ui::TextEditCommand::DELETE_WORD_BACKWARD:
       model_->MoveCursor(gfx::WORD_BREAK, begin, gfx::SELECTION_RETAIN);
-      changed = model_->Backspace(add_to_kill_buffer);
+      changed = cursor_changed = model_->Backspace(add_to_kill_buffer);
       break;
     case ui::TextEditCommand::DELETE_WORD_FORWARD:
       model_->MoveCursor(gfx::WORD_BREAK, end, gfx::SELECTION_RETAIN);
-      changed = model_->Delete(add_to_kill_buffer);
+      changed = cursor_changed = model_->Delete(add_to_kill_buffer);
       break;
     case ui::TextEditCommand::MOVE_BACKWARD:
       model_->MoveCursor(gfx::CHARACTER_BREAK, begin, gfx::SELECTION_NONE);
@@ -2182,28 +2055,28 @@
                          kWordSelectionBehavior);
       break;
     case ui::TextEditCommand::UNDO:
-      changed = model_->Undo();
+      changed = cursor_changed = model_->Undo();
       break;
     case ui::TextEditCommand::REDO:
-      changed = model_->Redo();
+      changed = cursor_changed = model_->Redo();
       break;
     case ui::TextEditCommand::CUT:
-      changed = Cut();
+      changed = cursor_changed = Cut();
       break;
     case ui::TextEditCommand::COPY:
       Copy();
       break;
     case ui::TextEditCommand::PASTE:
-      changed = Paste();
+      changed = cursor_changed = Paste();
       break;
     case ui::TextEditCommand::SELECT_ALL:
       SelectAll(false);
       break;
     case ui::TextEditCommand::TRANSPOSE:
-      changed = model_->Transpose();
+      changed = cursor_changed = model_->Transpose();
       break;
     case ui::TextEditCommand::YANK:
-      changed = model_->Yank();
+      changed = cursor_changed = model_->Yank();
       break;
     case ui::TextEditCommand::INSERT_TEXT:
     case ui::TextEditCommand::SET_MARK:
@@ -2213,14 +2086,7 @@
       break;
   }
 
-  const auto text_change_type =
-      changed ? TextChangeType::kUserTriggered : TextChangeType::kNone;
-  const bool cursor_changed =
-      changed || (GetSelectionModel() != selection_model);
-  if (cursor_changed && HasSelection())
-    UpdateSelectionClipboard();
-  UpdateAfterChange(text_change_type, cursor_changed);
-  OnAfterUserAction();
+  return {changed, cursor_changed};
 }
 
 void Textfield::OffsetDoubleClickWord(int offset) {
@@ -2276,6 +2142,151 @@
                                     std::move(callback));
 }
 
+bool Textfield::PreHandleKeyPressed(const ui::KeyEvent& event) {
+  return false;
+}
+
+ui::TextEditCommand Textfield::GetCommandForKeyEvent(
+    const ui::KeyEvent& event) {
+  if (event.type() != ui::ET_KEY_PRESSED || event.IsUnicodeKeyCode())
+    return ui::TextEditCommand::INVALID_COMMAND;
+
+  const bool shift = event.IsShiftDown();
+#if defined(OS_APPLE)
+  const bool command = event.IsCommandDown();
+#endif
+  const bool control = event.IsControlDown() || event.IsCommandDown();
+  const bool alt = event.IsAltDown() || event.IsAltGrDown();
+  switch (event.key_code()) {
+    case ui::VKEY_Z:
+      if (control && !shift && !alt)
+        return ui::TextEditCommand::UNDO;
+      return (control && shift && !alt) ? ui::TextEditCommand::REDO
+                                        : ui::TextEditCommand::INVALID_COMMAND;
+    case ui::VKEY_Y:
+      return (control && !alt) ? ui::TextEditCommand::REDO
+                               : ui::TextEditCommand::INVALID_COMMAND;
+    case ui::VKEY_A:
+      return (control && !alt) ? ui::TextEditCommand::SELECT_ALL
+                               : ui::TextEditCommand::INVALID_COMMAND;
+    case ui::VKEY_X:
+      return (control && !alt) ? ui::TextEditCommand::CUT
+                               : ui::TextEditCommand::INVALID_COMMAND;
+    case ui::VKEY_C:
+      return (control && !alt) ? ui::TextEditCommand::COPY
+                               : ui::TextEditCommand::INVALID_COMMAND;
+    case ui::VKEY_V:
+      return (control && !alt) ? ui::TextEditCommand::PASTE
+                               : ui::TextEditCommand::INVALID_COMMAND;
+    case ui::VKEY_RIGHT:
+      // Ignore alt+right, which may be a browser navigation shortcut.
+      if (alt)
+        return ui::TextEditCommand::INVALID_COMMAND;
+      if (!shift) {
+        return control ? ui::TextEditCommand::MOVE_WORD_RIGHT
+                       : ui::TextEditCommand::MOVE_RIGHT;
+      }
+      return control ? ui::TextEditCommand::MOVE_WORD_RIGHT_AND_MODIFY_SELECTION
+                     : ui::TextEditCommand::MOVE_RIGHT_AND_MODIFY_SELECTION;
+    case ui::VKEY_LEFT:
+      // Ignore alt+left, which may be a browser navigation shortcut.
+      if (alt)
+        return ui::TextEditCommand::INVALID_COMMAND;
+      if (!shift) {
+        return control ? ui::TextEditCommand::MOVE_WORD_LEFT
+                       : ui::TextEditCommand::MOVE_LEFT;
+      }
+      return control ? ui::TextEditCommand::MOVE_WORD_LEFT_AND_MODIFY_SELECTION
+                     : ui::TextEditCommand::MOVE_LEFT_AND_MODIFY_SELECTION;
+    case ui::VKEY_HOME:
+      if (shift) {
+        return ui::TextEditCommand::
+            MOVE_TO_BEGINNING_OF_LINE_AND_MODIFY_SELECTION;
+      }
+#if defined(OS_APPLE)
+      return ui::TextEditCommand::SCROLL_TO_BEGINNING_OF_DOCUMENT;
+#else
+      return ui::TextEditCommand::MOVE_TO_BEGINNING_OF_LINE;
+#endif
+    case ui::VKEY_END:
+      if (shift)
+        return ui::TextEditCommand::MOVE_TO_END_OF_LINE_AND_MODIFY_SELECTION;
+#if defined(OS_APPLE)
+      return ui::TextEditCommand::SCROLL_TO_END_OF_DOCUMENT;
+#else
+      return ui::TextEditCommand::MOVE_TO_END_OF_LINE;
+#endif
+    case ui::VKEY_UP:
+#if defined(OS_APPLE)
+      if (control && shift) {
+        return ui::TextEditCommand::
+            MOVE_PARAGRAPH_BACKWARD_AND_MODIFY_SELECTION;
+      }
+      if (command)
+        return ui::TextEditCommand::MOVE_TO_BEGINNING_OF_DOCUMENT;
+      return shift ? ui::TextEditCommand::
+                         MOVE_TO_BEGINNING_OF_LINE_AND_MODIFY_SELECTION
+                   : ui::TextEditCommand::MOVE_UP;
+#else
+      return shift ? ui::TextEditCommand::
+                         MOVE_TO_BEGINNING_OF_LINE_AND_MODIFY_SELECTION
+                   : ui::TextEditCommand::INVALID_COMMAND;
+#endif
+    case ui::VKEY_DOWN:
+#if defined(OS_APPLE)
+      if (control && shift) {
+        return ui::TextEditCommand::MOVE_PARAGRAPH_FORWARD_AND_MODIFY_SELECTION;
+      }
+      if (command)
+        return ui::TextEditCommand::MOVE_TO_END_OF_DOCUMENT;
+      return shift
+                 ? ui::TextEditCommand::MOVE_TO_END_OF_LINE_AND_MODIFY_SELECTION
+                 : ui::TextEditCommand::MOVE_DOWN;
+#else
+      return shift
+                 ? ui::TextEditCommand::MOVE_TO_END_OF_LINE_AND_MODIFY_SELECTION
+                 : ui::TextEditCommand::INVALID_COMMAND;
+#endif
+    case ui::VKEY_BACK:
+      if (!control) {
+#if defined(OS_WIN)
+        if (alt)
+          return shift ? ui::TextEditCommand::REDO : ui::TextEditCommand::UNDO;
+#endif
+        return ui::TextEditCommand::DELETE_BACKWARD;
+      }
+#if defined(OS_LINUX) || defined(OS_CHROMEOS)
+      // Only erase by line break on Linux and ChromeOS.
+      if (shift)
+        return ui::TextEditCommand::DELETE_TO_BEGINNING_OF_LINE;
+#endif
+      return ui::TextEditCommand::DELETE_WORD_BACKWARD;
+    case ui::VKEY_DELETE:
+#if defined(OS_LINUX) || defined(OS_CHROMEOS)
+      // Only erase by line break on Linux and ChromeOS.
+      if (shift && control)
+        return ui::TextEditCommand::DELETE_TO_END_OF_LINE;
+#endif
+      if (control)
+        return ui::TextEditCommand::DELETE_WORD_FORWARD;
+      return shift ? ui::TextEditCommand::CUT
+                   : ui::TextEditCommand::DELETE_FORWARD;
+    case ui::VKEY_INSERT:
+      if (control && !shift)
+        return ui::TextEditCommand::COPY;
+      return (shift && !control) ? ui::TextEditCommand::PASTE
+                                 : ui::TextEditCommand::INVALID_COMMAND;
+    case ui::VKEY_PRIOR:
+      return control ? ui::TextEditCommand::SCROLL_PAGE_UP
+                     : ui::TextEditCommand::INVALID_COMMAND;
+    case ui::VKEY_NEXT:
+      return control ? ui::TextEditCommand::SCROLL_PAGE_DOWN
+                     : ui::TextEditCommand::INVALID_COMMAND;
+    default:
+      return ui::TextEditCommand::INVALID_COMMAND;
+  }
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // Textfield, private:
 
diff --git a/ui/views/controls/textfield/textfield.h b/ui/views/controls/textfield/textfield.h
index c3be0b0..ed8bc19f 100644
--- a/ui/views/controls/textfield/textfield.h
+++ b/ui/views/controls/textfield/textfield.h
@@ -11,6 +11,7 @@
 #include <memory>
 #include <set>
 #include <string>
+#include <utility>
 
 #if defined(OS_WIN)
 #include <vector>
@@ -85,6 +86,29 @@
     kLastCommandId = kSelectAll,
   };
 
+#if defined(OS_APPLE)
+  static constexpr gfx::SelectionBehavior kLineSelectionBehavior =
+      gfx::SELECTION_EXTEND;
+  static constexpr gfx::SelectionBehavior kWordSelectionBehavior =
+      gfx::SELECTION_CARET;
+  static constexpr gfx::SelectionBehavior kMoveParagraphSelectionBehavior =
+      gfx::SELECTION_CARET;
+  static constexpr gfx::SelectionBehavior kPageSelectionBehavior =
+      gfx::SELECTION_EXTEND;
+#else
+  static constexpr gfx::SelectionBehavior kLineSelectionBehavior =
+      gfx::SELECTION_RETAIN;
+  static constexpr gfx::SelectionBehavior kWordSelectionBehavior =
+      gfx::SELECTION_RETAIN;
+  static constexpr gfx::SelectionBehavior kMoveParagraphSelectionBehavior =
+      gfx::SELECTION_RETAIN;
+  static constexpr gfx::SelectionBehavior kPageSelectionBehavior =
+      gfx::SELECTION_RETAIN;
+#endif
+
+  // Pair of |text_changed|, |cursor_changed|.
+  using EditCommandResult = std::pair<bool, bool>;
+
   // Returns the text cursor blink time, or 0 for no blinking.
   static base::TimeDelta GetCaretBlinkInterval();
 
@@ -437,6 +461,8 @@
       views::PropertyChangedCallback callback) WARN_UNUSED_RESULT;
 
  protected:
+  TextfieldModel* textfield_model() { return model_.get(); }
+
   // Inserts or appends a character in response to an IME operation.
   virtual void DoInsertChar(base::char16 ch);
 
@@ -474,6 +500,20 @@
   // gesture event.
   void RequestFocusForGesture(const ui::GestureEventDetails& details);
 
+  virtual Textfield::EditCommandResult DoExecuteTextEditCommand(
+      ui::TextEditCommand command);
+
+  // Handles key press event ahead of OnKeyPressed(). This is used for Textarea
+  // to handle the return key. Use TextfieldController::HandleKeyEvent to
+  // intercept the key event in other cases.
+  virtual bool PreHandleKeyPressed(const ui::KeyEvent& event);
+
+  // Get the default command for a given key |event|.
+  virtual ui::TextEditCommand GetCommandForKeyEvent(const ui::KeyEvent& event);
+
+  // Update the cursor position in the text field.
+  void UpdateCursorViewPosition();
+
  private:
   friend class TextfieldTestApi;
 
@@ -527,9 +567,6 @@
   // A callback function to periodically update the cursor node_data.
   void UpdateCursorVisibility();
 
-  // Update the cursor position in the text field.
-  void UpdateCursorViewPosition();
-
   // Gets the style::TextStyle that should be used.
   int GetTextStyle() const;
 
diff --git a/ui/views/controls/textfield/textfield_model.cc b/ui/views/controls/textfield/textfield_model.cc
index 82e865e..ba182dd 100644
--- a/ui/views/controls/textfield/textfield_model.cc
+++ b/ui/views/controls/textfield/textfield_model.cc
@@ -626,6 +626,11 @@
   if (text.empty())
     return false;
 
+  if (render_text()->multiline()) {
+    InsertTextInternal(text, false);
+    return true;
+  }
+
   // Leading/trailing whitespace is often selected accidentally, and is rarely
   // critical to include (e.g. when pasting into a find bar).  Trim it.  By
   // contrast, whitespace in the middle of the string may need exact
diff --git a/ui/views/controls/textfield/textfield_model.h b/ui/views/controls/textfield/textfield_model.h
index 5050309..7770089 100644
--- a/ui/views/controls/textfield/textfield_model.h
+++ b/ui/views/controls/textfield/textfield_model.h
@@ -40,6 +40,7 @@
 
 namespace test {
 class BridgedNativeWidgetTest;
+class TextfieldTest;
 }  // namespace test
 
 // A model that represents text content for a views::Textfield.
@@ -270,7 +271,7 @@
   friend class internal::Edit;
   friend class test::BridgedNativeWidgetTest;
   friend class TextfieldModelTest;
-  friend class TextfieldTest;
+  friend class test::TextfieldTest;
 
   FRIEND_TEST_ALL_PREFIXES(TextfieldModelTest, UndoRedo_BasicTest);
   FRIEND_TEST_ALL_PREFIXES(TextfieldModelTest, UndoRedo_CutCopyPasteTest);
diff --git a/ui/views/controls/textfield/textfield_unittest.cc b/ui/views/controls/textfield/textfield_unittest.cc
index f8923f5..8867572 100644
--- a/ui/views/controls/textfield/textfield_unittest.cc
+++ b/ui/views/controls/textfield/textfield_unittest.cc
@@ -2,30 +2,29 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ui/views/controls/textfield/textfield.h"
+#include "ui/views/controls/textfield/textfield_unittest.h"
 
 #include <stddef.h>
 #include <stdint.h>
 
 #include <set>
 #include <string>
-#include <utility>
 #include <vector>
 
 #include "base/command_line.h"
-#include "base/feature_list.h"
 #include "base/format_macros.h"
 #include "base/i18n/rtl.h"
 #include "base/pickle.h"
 #include "base/stl_util.h"
 #include "base/strings/string16.h"
+#include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
 #include "ui/accessibility/ax_enums.mojom.h"
 #include "ui/accessibility/ax_node_data.h"
-#include "ui/base/clipboard/clipboard.h"
 #include "ui/base/clipboard/scoped_clipboard_writer.h"
+#include "ui/base/clipboard/test/test_clipboard.h"
 #include "ui/base/dragdrop/drag_drop_types.h"
 #include "ui/base/emoji/emoji_panel_helper.h"
 #include "ui/base/ime/constants.h"
@@ -41,20 +40,17 @@
 #include "ui/events/event_processor.h"
 #include "ui/events/event_utils.h"
 #include "ui/events/keycodes/dom/dom_code.h"
-#include "ui/events/keycodes/keyboard_codes.h"
 #include "ui/events/test/event_generator.h"
 #include "ui/events/test/keyboard_layout.h"
 #include "ui/gfx/render_text.h"
 #include "ui/gfx/render_text_test_api.h"
 #include "ui/strings/grit/ui_strings.h"
-#include "ui/views/controls/textfield/textfield_controller.h"
 #include "ui/views/controls/textfield/textfield_model.h"
 #include "ui/views/controls/textfield/textfield_test_api.h"
 #include "ui/views/focus/focus_manager.h"
 #include "ui/views/style/platform_style.h"
 #include "ui/views/test/ax_event_counter.h"
 #include "ui/views/test/test_views_delegate.h"
-#include "ui/views/test/views_test_base.h"
 #include "ui/views/test/widget_test.h"
 #include "ui/views/views_features.h"
 #include "ui/views/widget/widget.h"
@@ -85,18 +81,79 @@
 using base::UTF8ToUTF16;
 using base::WideToUTF16;
 
-#define EXPECT_STR_EQ(ascii, utf16) EXPECT_EQ(ASCIIToUTF16(ascii), utf16)
+namespace views {
+namespace test {
 
-namespace {
+const ui::EventType kFocusEvent =
+    base::FeatureList::IsEnabled(features::kTextfieldFocusOnTapUp)
+        ? ui::ET_GESTURE_TAP
+        : ui::ET_GESTURE_TAP_DOWN;
 
 const base::char16 kHebrewLetterSamekh = 0x05E1;
 
+// Convenience to make constructing a GestureEvent simpler.
+class GestureEventForTest : public ui::GestureEvent {
+ public:
+  GestureEventForTest(int x, int y, ui::GestureEventDetails details)
+      : GestureEvent(x, y, ui::EF_NONE, base::TimeTicks(), details) {}
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(GestureEventForTest);
+};
+
+// This controller will happily destroy the target field passed on
+// construction when a key event is triggered.
+class TextfieldDestroyerController : public TextfieldController {
+ public:
+  explicit TextfieldDestroyerController(Textfield* target) : target_(target) {
+    target_->set_controller(this);
+  }
+
+  Textfield* target() { return target_.get(); }
+
+  // TextfieldController:
+  bool HandleKeyEvent(Textfield* sender,
+                      const ui::KeyEvent& key_event) override {
+    if (target_)
+      target_->OnBlur();
+    target_.reset();
+    return false;
+  }
+
+ private:
+  std::unique_ptr<Textfield> target_;
+
+  DISALLOW_COPY_AND_ASSIGN(TextfieldDestroyerController);
+};
+
+// Class that focuses a textfield when it sees a KeyDown event.
+class TextfieldFocuser : public View {
+ public:
+  explicit TextfieldFocuser(Textfield* textfield) : textfield_(textfield) {
+    SetFocusBehavior(FocusBehavior::ALWAYS);
+  }
+
+  void set_consume(bool consume) { consume_ = consume; }
+
+  // View:
+  bool OnKeyPressed(const ui::KeyEvent& event) override {
+    textfield_->RequestFocus();
+    return consume_;
+  }
+
+ private:
+  bool consume_ = true;
+  Textfield* textfield_;
+
+  DISALLOW_COPY_AND_ASSIGN(TextfieldFocuser);
+};
+
 class MockInputMethod : public ui::InputMethodBase {
  public:
   MockInputMethod();
   ~MockInputMethod() override;
 
-  // Overridden from InputMethod:
+  // InputMethod:
   ui::EventDispatchDetails DispatchKeyEvent(ui::KeyEvent* key) override;
   void OnTextInputTypeChanged(const ui::TextInputClient* client) override;
   void OnCaretBoundsChanged(const ui::TextInputClient* client) override {}
@@ -120,7 +177,6 @@
 
   void SetCompositionTextForNextKey(const ui::CompositionText& composition);
   void SetResultTextForNextKey(const base::string16& result);
-  int count_show_virtual_keyboard_ = 0;
 
  private:
   // Overridden from InputMethodBase.
@@ -150,6 +206,8 @@
   bool text_input_type_changed_ = false;
   bool cancel_composition_called_ = false;
 
+  int count_show_virtual_keyboard_ = 0;
+
   DISALLOW_COPY_AND_ASSIGN(MockInputMethod);
 };
 
@@ -273,8 +331,9 @@
 class TestTextfield : public views::Textfield {
  public:
   TestTextfield() = default;
+  ~TestTextfield() override = default;
 
-  // ui::TextInputClient overrides:
+  // ui::TextInputClient:
   void InsertChar(const ui::KeyEvent& e) override {
     views::Textfield::InsertChar(e);
 #if defined(OS_APPLE)
@@ -303,7 +362,7 @@
   }
 
  private:
-  // views::View override:
+  // views::View:
   void OnKeyEvent(ui::KeyEvent* event) override {
     key_received_ = true;
     event_flags_ = event->flags();
@@ -333,256 +392,198 @@
   DISALLOW_COPY_AND_ASSIGN(TestTextfield);
 };
 
-// Convenience to make constructing a GestureEvent simpler.
-class GestureEventForTest : public ui::GestureEvent {
- public:
-  GestureEventForTest(int x, int y, ui::GestureEventDetails details)
-      : GestureEvent(x, y, 0, base::TimeTicks(), details) {}
+TextfieldTest::TextfieldTest() {
+  input_method_ = new MockInputMethod();
+  ui::SetUpInputMethodForTesting(input_method_);
+}
 
- private:
-  DISALLOW_COPY_AND_ASSIGN(GestureEventForTest);
-};
+TextfieldTest::~TextfieldTest() = default;
 
-// This controller will happily destroy the target textfield passed on
-// construction when a key event is triggered.
-class TextfieldDestroyerController : public views::TextfieldController {
- public:
-  explicit TextfieldDestroyerController(views::Textfield* target)
-      : target_(target) {
-    target_->set_controller(this);
-  }
+void TextfieldTest::SetUp() {
+  // OS clipboard is a global resource, which causes flakiness when unit tests
+  // run in parallel. So, use a per-instance test clipboard.
+  ui::Clipboard::SetClipboardForCurrentThread(
+      std::make_unique<ui::TestClipboard>());
+  ViewsTestBase::SetUp();
+}
 
-  views::Textfield* target() { return target_.get(); }
+void TextfieldTest::TearDown() {
+  if (widget_)
+    widget_->Close();
+  // Clear kill buffer used for "Yank" text editing command so that no state
+  // persists between tests.
+  TextfieldModel::ClearKillBuffer();
+  ViewsTestBase::TearDown();
+}
 
-  // views::TextfieldController:
-  bool HandleKeyEvent(views::Textfield* sender,
-                      const ui::KeyEvent& key_event) override {
-    if (target_)
-      target_->OnBlur();
-    target_.reset();
-    return false;
-  }
+ui::ClipboardBuffer TextfieldTest::GetAndResetCopiedToClipboard() {
+  return std::exchange(copied_to_clipboard_, ui::ClipboardBuffer::kMaxValue);
+}
 
- private:
-  std::unique_ptr<views::Textfield> target_;
-};
-
-// Class that focuses a textfield when it sees a KeyDown event.
-class TextfieldFocuser : public views::View {
- public:
-  explicit TextfieldFocuser(views::Textfield* textfield)
-      : textfield_(textfield) {
-    SetFocusBehavior(FocusBehavior::ALWAYS);
-  }
-
-  void set_consume(bool consume) { consume_ = consume; }
-
-  // View:
-  bool OnKeyPressed(const ui::KeyEvent& event) override {
-    textfield_->RequestFocus();
-    return consume_;
-  }
-
- private:
-  bool consume_ = true;
-  views::Textfield* textfield_;
-
-  DISALLOW_COPY_AND_ASSIGN(TextfieldFocuser);
-};
-
-base::string16 GetClipboardText(ui::ClipboardBuffer clipboard_buffer) {
+base::string16 TextfieldTest::GetClipboardText(
+    ui::ClipboardBuffer clipboard_buffer) {
   base::string16 text;
   ui::Clipboard::GetForCurrentThread()->ReadText(
       clipboard_buffer, /* data_dst = */ nullptr, &text);
   return text;
 }
 
-void SetClipboardText(ui::ClipboardBuffer clipboard_buffer,
-                      const std::string& text) {
+void TextfieldTest::SetClipboardText(ui::ClipboardBuffer clipboard_buffer,
+                                     const std::string& text) {
   ui::ScopedClipboardWriter(clipboard_buffer).WriteText(ASCIIToUTF16(text));
 }
 
-}  // namespace
+void TextfieldTest::ContentsChanged(Textfield* sender,
+                                    const base::string16& new_contents) {
+  // Paste calls TextfieldController::ContentsChanged() explicitly even if the
+  // paste action did not change the content. So |new_contents| may match
+  // |last_contents_|. For more info, see http://crbug.com/79002
+  last_contents_ = new_contents;
+}
 
-namespace views {
+void TextfieldTest::OnBeforeUserAction(Textfield* sender) {
+  ++on_before_user_action_;
+}
 
-class TextfieldTest : public ViewsTestBase, public TextfieldController {
- public:
-  TextfieldTest() {
-    input_method_ = new MockInputMethod();
-    ui::SetUpInputMethodForTesting(input_method_);
+void TextfieldTest::OnAfterUserAction(Textfield* sender) {
+  ++on_after_user_action_;
+}
+
+void TextfieldTest::OnAfterCutOrCopy(ui::ClipboardBuffer clipboard_type) {
+  copied_to_clipboard_ = clipboard_type;
+}
+
+void TextfieldTest::InitTextfield(int count) {
+  ASSERT_FALSE(textfield_);
+  textfield_ = PrepareTextfields(count, std::make_unique<TestTextfield>(),
+                                 gfx::Rect(100, 100, 100, 100));
+}
+
+void TextfieldTest::PrepareTextfieldsInternal(int count,
+                                              Textfield* textfield,
+                                              View* container,
+                                              gfx::Rect bounds) {
+  input_method_->SetDelegate(
+      test::WidgetTest::GetInputMethodDelegateForWidget(widget_.get()));
+
+  textfield->set_controller(this);
+  textfield->SetBoundsRect(bounds);
+  textfield->SetID(1);
+  test_api_ = std::make_unique<TextfieldTestApi>(textfield);
+
+  for (int i = 1; i < count; ++i) {
+    Textfield* textfield =
+        container->AddChildView(std::make_unique<Textfield>());
+    textfield->SetID(i + 1);
   }
 
-  // ::testing::Test:
-  void TearDown() override {
-    if (widget_)
-      widget_->Close();
-    // Clear kill buffer used for "Yank" text editing command so that no state
-    // persists between tests.
-    TextfieldModel::ClearKillBuffer();
-    ViewsTestBase::TearDown();
-  }
+  model_ = test_api_->model();
+  model_->ClearEditHistory();
 
-  ui::ClipboardBuffer GetAndResetCopiedToClipboard() {
-    ui::ClipboardBuffer clipboard_buffer = copied_to_clipboard_;
-    copied_to_clipboard_ = ui::ClipboardBuffer::kMaxValue;
-    return clipboard_buffer;
-  }
+  // Since the window type is activatable, showing the widget will also
+  // activate it. Calling Activate directly is insufficient, since that does
+  // not also _focus_ an aura::Window (i.e. using the FocusClient). Both the
+  // widget and the textfield must have focus to properly handle input.
+  widget_->Show();
+  textfield->RequestFocus();
 
-  // TextfieldController:
-  void ContentsChanged(Textfield* sender,
-                       const base::string16& new_contents) override {
-    // Paste calls TextfieldController::ContentsChanged() explicitly even if the
-    // paste action did not change the content. So |new_contents| may match
-    // |last_contents_|. For more info, see http://crbug.com/79002
-    last_contents_ = new_contents;
-  }
+  event_generator_ =
+      std::make_unique<ui::test::EventGenerator>(GetRootWindow(widget_.get()));
+  event_generator_->set_target(ui::test::EventGenerator::Target::WINDOW);
+  event_target_ = textfield;
+}
 
-  void OnBeforeUserAction(Textfield* sender) override {
-    ++on_before_user_action_;
-  }
+ui::MenuModel* TextfieldTest::GetContextMenuModel() {
+  test_api_->UpdateContextMenu();
+  return test_api_->context_menu_contents();
+}
 
-  void OnAfterUserAction(Textfield* sender) override {
-    ++on_after_user_action_;
-  }
-
-  void OnAfterCutOrCopy(ui::ClipboardBuffer clipboard_buffer) override {
-    copied_to_clipboard_ = clipboard_buffer;
-  }
-
-  void InitTextfield() { InitTextfields(1); }
-
-  void InitTextfields(int count) {
-    ASSERT_FALSE(textfield_);
-    textfield_ = new TestTextfield();
-    textfield_->set_controller(this);
-    widget_ = new Widget();
-
-    // The widget type must be an activatable type, and we don't want to worry
-    // about the non-client view, which leaves just TYPE_WINDOW_FRAMELESS.
-    Widget::InitParams params =
-        CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
-
-    params.bounds = gfx::Rect(100, 100, 100, 100);
-    widget_->Init(std::move(params));
-    input_method_->SetDelegate(
-        test::WidgetTest::GetInputMethodDelegateForWidget(widget_));
-    View* container = widget_->SetContentsView(std::make_unique<View>());
-    container->AddChildView(textfield_);
-    textfield_->SetBoundsRect(params.bounds);
-    textfield_->SetID(1);
-    test_api_ = std::make_unique<TextfieldTestApi>(textfield_);
-
-    for (int i = 1; i < count; i++) {
-      Textfield* textfield = new Textfield();
-      container->AddChildView(textfield);
-      textfield->SetID(i + 1);
-    }
-
-    model_ = test_api_->model();
-    model_->ClearEditHistory();
-
-    // Since the window type is activatable, showing the widget will also
-    // activate it. Calling Activate directly is insufficient, since that does
-    // not also _focus_ an aura::Window (i.e. using the FocusClient). Both the
-    // widget and the textfield must have focus to properly handle input.
-    widget_->Show();
-    textfield_->RequestFocus();
-
-    event_generator_ =
-        std::make_unique<ui::test::EventGenerator>(GetRootWindow(widget_));
-    event_generator_->set_target(ui::test::EventGenerator::Target::WINDOW);
-  }
-  ui::MenuModel* GetContextMenuModel() {
-    test_api_->UpdateContextMenu();
-    return test_api_->context_menu_contents();
-  }
-
-  // True if native Mac keystrokes should be used (to avoid ifdef litter).
-  bool TestingNativeMac() {
+bool TextfieldTest::TestingNativeMac() const {
 #if defined(OS_APPLE)
-    return true;
+  return true;
 #else
-    return false;
+  return false;
 #endif
-  }
+}
 
-  bool TestingNativeCrOs() const {
+bool TextfieldTest::TestingNativeCrOs() const {
 #if BUILDFLAG(IS_CHROMEOS_ASH)
-    return true;
+  return true;
 #else
-    return false;
+  return false;
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
-  }
+}
 
- protected:
-  void SendKeyPress(ui::KeyboardCode key_code, int flags) {
-    event_generator_->PressKey(key_code, flags);
-  }
+void TextfieldTest::SendKeyPress(ui::KeyboardCode key_code, int flags) {
+  event_generator_->PressKey(key_code, flags);
+}
 
-  void SendKeyEvent(ui::KeyboardCode key_code,
-                    bool alt,
-                    bool shift,
-                    bool control_or_command,
-                    bool caps_lock) {
-    bool control = control_or_command;
-    bool command = false;
+void TextfieldTest::SendKeyEvent(ui::KeyboardCode key_code,
+                                 bool alt,
+                                 bool shift,
+                                 bool control_or_command,
+                                 bool caps_lock) {
+  bool control = control_or_command;
+  bool command = false;
 
-    // By default, swap control and command for native events on Mac. This
-    // handles most cases.
-    if (TestingNativeMac())
-      std::swap(control, command);
+  // By default, swap control and command for native events on Mac. This
+  // handles most cases.
+  if (TestingNativeMac())
+    std::swap(control, command);
 
-    int flags =
-        (shift ? ui::EF_SHIFT_DOWN : 0) | (control ? ui::EF_CONTROL_DOWN : 0) |
-        (alt ? ui::EF_ALT_DOWN : 0) | (command ? ui::EF_COMMAND_DOWN : 0) |
-        (caps_lock ? ui::EF_CAPS_LOCK_ON : 0);
+  int flags =
+      (shift ? ui::EF_SHIFT_DOWN : 0) | (control ? ui::EF_CONTROL_DOWN : 0) |
+      (alt ? ui::EF_ALT_DOWN : 0) | (command ? ui::EF_COMMAND_DOWN : 0) |
+      (caps_lock ? ui::EF_CAPS_LOCK_ON : 0);
 
-    SendKeyPress(key_code, flags);
-  }
+  SendKeyPress(key_code, flags);
+}
 
-  void SendKeyEvent(ui::KeyboardCode key_code,
-                    bool shift,
-                    bool control_or_command) {
-    SendKeyEvent(key_code, false, shift, control_or_command, false);
-  }
+void TextfieldTest::SendKeyEvent(ui::KeyboardCode key_code,
+                                 bool shift,
+                                 bool control_or_command) {
+  SendKeyEvent(key_code, false, shift, control_or_command, false);
+}
 
-  void SendKeyEvent(ui::KeyboardCode key_code) {
-    SendKeyEvent(key_code, false, false);
-  }
+void TextfieldTest::SendKeyEvent(ui::KeyboardCode key_code) {
+  SendKeyEvent(key_code, false, false);
+}
 
-  void SendKeyEvent(base::char16 ch) { SendKeyEvent(ch, ui::EF_NONE, false); }
+void TextfieldTest::SendKeyEvent(base::char16 ch) {
+  SendKeyEvent(ch, ui::EF_NONE, false);
+}
 
-  void SendKeyEvent(base::char16 ch, int flags) {
-    SendKeyEvent(ch, flags, false);
-  }
+void TextfieldTest::SendKeyEvent(base::char16 ch, int flags) {
+  SendKeyEvent(ch, flags, false);
+}
 
-  void SendKeyEvent(base::char16 ch, int flags, bool from_vk) {
-    if (ch < 0x80) {
-      ui::KeyboardCode code =
-          ch == ' ' ? ui::VKEY_SPACE
-                    : static_cast<ui::KeyboardCode>(ui::VKEY_A + ch - 'a');
-      SendKeyPress(code, flags);
-    } else {
-      // For unicode characters, assume they come from IME rather than the
-      // keyboard. So they are dispatched directly to the input method. But on
-      // Mac, key events don't pass through InputMethod. Hence they are
-      // dispatched regularly.
-      ui::KeyEvent event(ch, ui::VKEY_UNKNOWN, ui::DomCode::NONE, flags);
-      if (from_vk) {
-        ui::Event::Properties properties;
-        properties[ui::kPropertyFromVK] =
-            std::vector<uint8_t>(ui::kPropertyFromVKSize);
-        event.SetProperties(properties);
-      }
-#if defined(OS_APPLE)
-      event_generator_->Dispatch(&event);
-#else
-      input_method_->DispatchKeyEvent(&event);
-#endif
+void TextfieldTest::SendKeyEvent(base::char16 ch, int flags, bool from_vk) {
+  if (ch < 0x80) {
+    ui::KeyboardCode code =
+        ch == ' ' ? ui::VKEY_SPACE
+                  : static_cast<ui::KeyboardCode>(ui::VKEY_A + ch - 'a');
+    SendKeyPress(code, flags);
+  } else {
+    // For unicode characters, assume they come from IME rather than the
+    // keyboard. So they are dispatched directly to the input method. But on
+    // Mac, key events don't pass through InputMethod. Hence they are
+    // dispatched regularly.
+    ui::KeyEvent event(ch, ui::VKEY_UNKNOWN, ui::DomCode::NONE, flags);
+    if (from_vk) {
+      ui::Event::Properties properties;
+      properties[ui::kPropertyFromVK] =
+          std::vector<uint8_t>(ui::kPropertyFromVKSize);
+      event.SetProperties(properties);
     }
+#if defined(OS_APPLE)
+    event_generator_->Dispatch(&event);
+#else
+    input_method_->DispatchKeyEvent(&event);
+#endif
   }
+}
 
+void TextfieldTest::DispatchMockInputMethodKeyEvent() {
   // Send a key to trigger MockInputMethod::DispatchKeyEvent(). Note the
   // specific VKEY isn't used (MockInputMethod will mock a ui::VKEY_PROCESSKEY
   // whenever it has a test composition). However, on Mac, it can't be a letter
@@ -590,244 +591,220 @@
   // and don't have a meaningful ui::KeyEvent that would trigger
   // DispatchKeyEvent(). It also can't be VKEY_ENTER, since those key events may
   // need to be suppressed when interacting with real system IME.
-  void DispatchMockInputMethodKeyEvent() { SendKeyEvent(ui::VKEY_INSERT); }
+  SendKeyEvent(ui::VKEY_INSERT);
+}
 
-  // Sends a platform-specific move (and select) to the logical start of line.
-  // Eg. this should move (and select) to the right end of line for RTL text.
-  void SendHomeEvent(bool shift) {
-    if (TestingNativeMac()) {
-      // [NSResponder moveToBeginningOfLine:] is the correct way to do this on
-      // Mac, but that doesn't have a default key binding. Since
-      // views::Textfield doesn't currently support multiple lines, the same
-      // effect can be achieved by Cmd+Up which maps to
-      // [NSResponder moveToBeginningOfDocument:].
-      SendKeyEvent(ui::VKEY_UP, shift /* shift */, true /* command */);
-      return;
-    }
-    SendKeyEvent(ui::VKEY_HOME, shift /* shift */, false /* control */);
+// Sends a platform-specific move (and select) to the logical start of line.
+// Eg. this should move (and select) to the right end of line for RTL text.
+void TextfieldTest::SendHomeEvent(bool shift) {
+  if (TestingNativeMac()) {
+    // [NSResponder moveToBeginningOfLine:] is the correct way to do this on
+    // Mac, but that doesn't have a default key binding. Since
+    // views::Textfield doesn't currently support multiple lines, the same
+    // effect can be achieved by Cmd+Up which maps to
+    // [NSResponder moveToBeginningOfDocument:].
+    SendKeyEvent(ui::VKEY_UP, shift /* shift */, true /* command */);
+    return;
   }
+  SendKeyEvent(ui::VKEY_HOME, shift /* shift */, false /* control */);
+}
 
-  // Sends a platform-specific move (and select) to the logical end of line.
-  void SendEndEvent(bool shift) {
-    if (TestingNativeMac()) {
-      SendKeyEvent(ui::VKEY_DOWN, shift, true);  // Cmd+Down.
-      return;
-    }
-    SendKeyEvent(ui::VKEY_END, shift, false);
+// Sends a platform-specific move (and select) to the logical end of line.
+void TextfieldTest::SendEndEvent(bool shift) {
+  if (TestingNativeMac()) {
+    SendKeyEvent(ui::VKEY_DOWN, shift, true);  // Cmd+Down.
+    return;
   }
+  SendKeyEvent(ui::VKEY_END, shift, false);
+}
 
-  // Sends {delete, move, select} word {forward, backward}.
-  void SendWordEvent(ui::KeyboardCode key, bool shift) {
-    bool alt = false;
-    bool control = true;
-    bool caps = false;
-    if (TestingNativeMac()) {
-      // Use Alt+Left/Right/Backspace on native Mac.
-      alt = true;
-      control = false;
-    }
-    SendKeyEvent(key, alt, shift, control, caps);
+// Sends {delete, move, select} word {forward, backward}.
+void TextfieldTest::SendWordEvent(ui::KeyboardCode key, bool shift) {
+  bool alt = false;
+  bool control = true;
+  bool caps = false;
+  if (TestingNativeMac()) {
+    // Use Alt+Left/Right/Backspace on native Mac.
+    alt = true;
+    control = false;
   }
+  SendKeyEvent(key, alt, shift, control, caps);
+}
 
-  // Sends Shift+Delete if supported, otherwise Cmd+X again.
-  void SendAlternateCut() {
-    if (TestingNativeMac())
-      SendKeyEvent(ui::VKEY_X, false, true);
-    else
-      SendKeyEvent(ui::VKEY_DELETE, true, false);
-  }
+// Sends Shift+Delete if supported, otherwise Cmd+X again.
+void TextfieldTest::SendAlternateCut() {
+  if (TestingNativeMac())
+    SendKeyEvent(ui::VKEY_X, false, true);
+  else
+    SendKeyEvent(ui::VKEY_DELETE, true, false);
+}
 
-  // Sends Ctrl+Insert if supported, otherwise Cmd+C again.
-  void SendAlternateCopy() {
-    if (TestingNativeMac())
-      SendKeyEvent(ui::VKEY_C, false, true);
-    else
-      SendKeyEvent(ui::VKEY_INSERT, false, true);
-  }
+// Sends Ctrl+Insert if supported, otherwise Cmd+C again.
+void TextfieldTest::SendAlternateCopy() {
+  if (TestingNativeMac())
+    SendKeyEvent(ui::VKEY_C, false, true);
+  else
+    SendKeyEvent(ui::VKEY_INSERT, false, true);
+}
 
-  // Sends Shift+Insert if supported, otherwise Cmd+V again.
-  void SendAlternatePaste() {
-    if (TestingNativeMac())
-      SendKeyEvent(ui::VKEY_V, false, true);
-    else
-      SendKeyEvent(ui::VKEY_INSERT, true, false);
-  }
+// Sends Shift+Insert if supported, otherwise Cmd+V again.
+void TextfieldTest::SendAlternatePaste() {
+  if (TestingNativeMac())
+    SendKeyEvent(ui::VKEY_V, false, true);
+  else
+    SendKeyEvent(ui::VKEY_INSERT, true, false);
+}
 
-  View* GetFocusedView() {
-    return widget_->GetFocusManager()->GetFocusedView();
-  }
+View* TextfieldTest::GetFocusedView() {
+  return widget_->GetFocusManager()->GetFocusedView();
+}
 
-  int GetCursorPositionX(int cursor_pos) {
-    return test_api_->GetRenderText()
-        ->GetCursorBounds(gfx::SelectionModel(cursor_pos, gfx::CURSOR_FORWARD),
-                          false)
-        .x();
-  }
+int TextfieldTest::GetCursorPositionX(int cursor_pos) {
+  return test_api_->GetRenderText()
+      ->GetCursorBounds(gfx::SelectionModel(cursor_pos, gfx::CURSOR_FORWARD),
+                        false)
+      .x();
+}
 
-  int GetCursorYForTesting() {
-    return test_api_->GetRenderText()->GetLineOffset(0).y() + 1;
-  }
+int TextfieldTest::GetCursorYForTesting() {
+  return test_api_->GetRenderText()->GetLineOffset(0).y() + 1;
+}
 
-  // Get the current cursor bounds.
-  gfx::Rect GetCursorBounds() {
-    return test_api_->GetRenderText()->GetUpdatedCursorBounds();
-  }
+gfx::Rect TextfieldTest::GetCursorBounds() {
+  return test_api_->GetRenderText()->GetUpdatedCursorBounds();
+}
 
-  // Get the cursor bounds of |sel|.
-  gfx::Rect GetCursorBounds(const gfx::SelectionModel& sel) {
-    return test_api_->GetRenderText()->GetCursorBounds(sel, true);
-  }
+// Gets the cursor bounds of |sel|.
+gfx::Rect TextfieldTest::GetCursorBounds(const gfx::SelectionModel& sel) {
+  return test_api_->GetRenderText()->GetCursorBounds(sel, true);
+}
 
-  gfx::Rect GetDisplayRect() {
-    return test_api_->GetRenderText()->display_rect();
-  }
+gfx::Rect TextfieldTest::GetDisplayRect() {
+  return test_api_->GetRenderText()->display_rect();
+}
 
-  // Mouse click on the point whose x-axis is |bound|'s x plus |x_offset| and
-  // y-axis is in the middle of |bound|'s vertical range.
-  void MouseClick(const gfx::Rect bound, int x_offset) {
-    gfx::Point point(bound.x() + x_offset, bound.y() + bound.height() / 2);
-    ui::MouseEvent click(ui::ET_MOUSE_PRESSED, point, point,
+gfx::Rect TextfieldTest::GetCursorViewRect() {
+  return test_api_->GetCursorViewRect();
+}
+
+// Performs a mouse click on the point whose x-axis is |bound|'s x plus
+// |x_offset| and y-axis is in the middle of |bound|'s vertical range.
+void TextfieldTest::MouseClick(const gfx::Rect bound, int x_offset) {
+  gfx::Point point(bound.x() + x_offset, bound.y() + bound.height() / 2);
+  ui::MouseEvent click(ui::ET_MOUSE_PRESSED, point, point,
+                       ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
+                       ui::EF_LEFT_MOUSE_BUTTON);
+  event_target_->OnMousePressed(click);
+  ui::MouseEvent release(ui::ET_MOUSE_RELEASED, point, point,
                          ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
                          ui::EF_LEFT_MOUSE_BUTTON);
-    textfield_->OnMousePressed(click);
-    ui::MouseEvent release(ui::ET_MOUSE_RELEASED, point, point,
-                           ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
-                           ui::EF_LEFT_MOUSE_BUTTON);
-    textfield_->OnMouseReleased(release);
-  }
+  event_target_->OnMouseReleased(release);
+}
 
-  // This is to avoid double/triple click.
-  void NonClientMouseClick() {
-    ui::MouseEvent click(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(),
+// This is to avoid double/triple click.
+void TextfieldTest::NonClientMouseClick() {
+  ui::MouseEvent click(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(),
+                       ui::EventTimeForNow(),
+                       ui::EF_LEFT_MOUSE_BUTTON | ui::EF_IS_NON_CLIENT,
+                       ui::EF_LEFT_MOUSE_BUTTON);
+  event_target_->OnMousePressed(click);
+  ui::MouseEvent release(ui::ET_MOUSE_RELEASED, gfx::Point(), gfx::Point(),
                          ui::EventTimeForNow(),
                          ui::EF_LEFT_MOUSE_BUTTON | ui::EF_IS_NON_CLIENT,
                          ui::EF_LEFT_MOUSE_BUTTON);
-    textfield_->OnMousePressed(click);
-    ui::MouseEvent release(ui::ET_MOUSE_RELEASED, gfx::Point(), gfx::Point(),
-                           ui::EventTimeForNow(),
-                           ui::EF_LEFT_MOUSE_BUTTON | ui::EF_IS_NON_CLIENT,
-                           ui::EF_LEFT_MOUSE_BUTTON);
-    textfield_->OnMouseReleased(release);
-  }
+  event_target_->OnMouseReleased(release);
+}
 
-  void VerifyTextfieldContextMenuContents(bool textfield_has_selection,
-                                          bool can_undo,
-                                          ui::MenuModel* menu) {
-    const auto& text = textfield_->GetText();
-    const bool is_all_selected =
-        !text.empty() &&
-        textfield_->GetSelectedRange().length() == text.length();
+void TextfieldTest::VerifyTextfieldContextMenuContents(
+    bool textfield_has_selection,
+    bool can_undo,
+    ui::MenuModel* menu) {
+  const auto& text = textfield_->GetText();
+  const bool is_all_selected =
+      !text.empty() && textfield_->GetSelectedRange().length() == text.length();
 
-    int menu_index = 0;
+  int menu_index = 0;
 
 #if defined(OS_APPLE)
-    if (textfield_has_selection) {
-      EXPECT_TRUE(menu->IsEnabledAt(menu_index++ /* Look Up "Selection" */));
-      EXPECT_TRUE(menu->IsEnabledAt(menu_index++ /* Separator */));
-    }
+  if (textfield_has_selection) {
+    EXPECT_TRUE(menu->IsEnabledAt(menu_index++ /* Look Up "Selection" */));
+    EXPECT_TRUE(menu->IsEnabledAt(menu_index++ /* Separator */));
+  }
 #endif
 
-    if (ui::IsEmojiPanelSupported()) {
-      EXPECT_TRUE(menu->IsEnabledAt(menu_index++ /* EMOJI */));
-      EXPECT_TRUE(menu->IsEnabledAt(menu_index++ /* Separator */));
-    }
-
-    EXPECT_EQ(can_undo, menu->IsEnabledAt(menu_index++ /* UNDO */));
+  if (ui::IsEmojiPanelSupported()) {
+    EXPECT_TRUE(menu->IsEnabledAt(menu_index++ /* EMOJI */));
     EXPECT_TRUE(menu->IsEnabledAt(menu_index++ /* Separator */));
-    EXPECT_EQ(textfield_has_selection,
-              menu->IsEnabledAt(menu_index++ /* CUT */));
-    EXPECT_EQ(textfield_has_selection,
-              menu->IsEnabledAt(menu_index++ /* COPY */));
-    EXPECT_NE(GetClipboardText(ui::ClipboardBuffer::kCopyPaste).empty(),
-              menu->IsEnabledAt(menu_index++ /* PASTE */));
-    EXPECT_EQ(textfield_has_selection,
-              menu->IsEnabledAt(menu_index++ /* DELETE */));
-    EXPECT_TRUE(menu->IsEnabledAt(menu_index++ /* Separator */));
-    EXPECT_EQ(!is_all_selected,
-              menu->IsEnabledAt(menu_index++ /* SELECT ALL */));
   }
 
-  void PressMouseButton(ui::EventFlags mouse_button_flags) {
-    ui::MouseEvent press(ui::ET_MOUSE_PRESSED, mouse_position_, mouse_position_,
-                         ui::EventTimeForNow(), mouse_button_flags,
-                         mouse_button_flags);
-    textfield_->OnMousePressed(press);
-  }
+  EXPECT_EQ(can_undo, menu->IsEnabledAt(menu_index++ /* UNDO */));
+  EXPECT_TRUE(menu->IsEnabledAt(menu_index++ /* Separator */));
+  EXPECT_EQ(textfield_has_selection, menu->IsEnabledAt(menu_index++ /* CUT */));
+  EXPECT_EQ(textfield_has_selection,
+            menu->IsEnabledAt(menu_index++ /* COPY */));
+  EXPECT_NE(GetClipboardText(ui::ClipboardBuffer::kCopyPaste).empty(),
+            menu->IsEnabledAt(menu_index++ /* PASTE */));
+  EXPECT_EQ(textfield_has_selection,
+            menu->IsEnabledAt(menu_index++ /* DELETE */));
+  EXPECT_TRUE(menu->IsEnabledAt(menu_index++ /* Separator */));
+  EXPECT_EQ(!is_all_selected, menu->IsEnabledAt(menu_index++ /* SELECT ALL */));
+}
 
-  void ReleaseMouseButton(ui::EventFlags mouse_button_flags) {
-    ui::MouseEvent release(ui::ET_MOUSE_RELEASED, mouse_position_,
-                           mouse_position_, ui::EventTimeForNow(),
-                           mouse_button_flags, mouse_button_flags);
-    textfield_->OnMouseReleased(release);
-  }
+void TextfieldTest::PressMouseButton(ui::EventFlags mouse_button_flags) {
+  ui::MouseEvent press(ui::ET_MOUSE_PRESSED, mouse_position_, mouse_position_,
+                       ui::EventTimeForNow(), mouse_button_flags,
+                       mouse_button_flags);
+  event_target_->OnMousePressed(press);
+}
 
-  void PressLeftMouseButton() { PressMouseButton(ui::EF_LEFT_MOUSE_BUTTON); }
+void TextfieldTest::ReleaseMouseButton(ui::EventFlags mouse_button_flags) {
+  ui::MouseEvent release(ui::ET_MOUSE_RELEASED, mouse_position_,
+                         mouse_position_, ui::EventTimeForNow(),
+                         mouse_button_flags, mouse_button_flags);
+  event_target_->OnMouseReleased(release);
+}
 
-  void ReleaseLeftMouseButton() {
-    ReleaseMouseButton(ui::EF_LEFT_MOUSE_BUTTON);
-  }
+void TextfieldTest::PressLeftMouseButton() {
+  PressMouseButton(ui::EF_LEFT_MOUSE_BUTTON);
+}
 
-  void ClickLeftMouseButton() {
-    PressLeftMouseButton();
-    ReleaseLeftMouseButton();
-  }
+void TextfieldTest::ReleaseLeftMouseButton() {
+  ReleaseMouseButton(ui::EF_LEFT_MOUSE_BUTTON);
+}
 
-  void ClickRightMouseButton() {
-    PressMouseButton(ui::EF_RIGHT_MOUSE_BUTTON);
-    ReleaseMouseButton(ui::EF_RIGHT_MOUSE_BUTTON);
-  }
+void TextfieldTest::ClickLeftMouseButton() {
+  PressLeftMouseButton();
+  ReleaseLeftMouseButton();
+}
 
-  void DragMouseTo(const gfx::Point& where) {
-    mouse_position_ = where;
-    ui::MouseEvent drag(ui::ET_MOUSE_DRAGGED, where, where,
-                        ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON, 0);
-    textfield_->OnMouseDragged(drag);
-  }
+void TextfieldTest::ClickRightMouseButton() {
+  PressMouseButton(ui::EF_RIGHT_MOUSE_BUTTON);
+  ReleaseMouseButton(ui::EF_RIGHT_MOUSE_BUTTON);
+}
 
-  // Textfield does not listen to OnMouseMoved, so this function does not send
-  // an event when it updates the cursor position.
-  void MoveMouseTo(const gfx::Point& where) { mouse_position_ = where; }
+void TextfieldTest::DragMouseTo(const gfx::Point& where) {
+  mouse_position_ = where;
+  ui::MouseEvent drag(ui::ET_MOUSE_DRAGGED, where, where, ui::EventTimeForNow(),
+                      ui::EF_LEFT_MOUSE_BUTTON, 0);
+  event_target_->OnMouseDragged(drag);
+}
 
-  // Tap on the textfield.
-  void TapAtCursor(ui::EventPointerType pointer_type) {
-    ui::GestureEventDetails tap_down_details(ui::ET_GESTURE_TAP_DOWN);
-    tap_down_details.set_primary_pointer_type(pointer_type);
-    GestureEventForTest tap_down(GetCursorPositionX(0), 0, tap_down_details);
-    textfield_->OnGestureEvent(&tap_down);
+void TextfieldTest::MoveMouseTo(const gfx::Point& where) {
+  mouse_position_ = where;
+}
 
-    ui::GestureEventDetails tap_up_details(ui::ET_GESTURE_TAP);
-    tap_up_details.set_primary_pointer_type(pointer_type);
-    GestureEventForTest tap_up(GetCursorPositionX(0), 0, tap_up_details);
-    textfield_->OnGestureEvent(&tap_up);
-  }
+// Taps on the textfield.
+void TextfieldTest::TapAtCursor(ui::EventPointerType pointer_type) {
+  ui::GestureEventDetails tap_down_details(ui::ET_GESTURE_TAP_DOWN);
+  tap_down_details.set_primary_pointer_type(pointer_type);
+  GestureEventForTest tap_down(GetCursorPositionX(0), 0, tap_down_details);
+  textfield_->OnGestureEvent(&tap_down);
 
-  // We need widget to populate wrapper class.
-  Widget* widget_ = nullptr;
-
-  TestTextfield* textfield_ = nullptr;
-  std::unique_ptr<TextfieldTestApi> test_api_;
-  TextfieldModel* model_ = nullptr;
-
-  // The string from Controller::ContentsChanged callback.
-  base::string16 last_contents_;
-
-  // For testing input method related behaviors.
-  MockInputMethod* input_method_ = nullptr;
-
-  // Indicates how many times OnBeforeUserAction() is called.
-  int on_before_user_action_ = 0;
-
-  // Indicates how many times OnAfterUserAction() is called.
-  int on_after_user_action_ = 0;
-
-  // Position of the mouse for synthetic mouse events.
-  gfx::Point mouse_position_;
-  ui::ClipboardBuffer copied_to_clipboard_ = ui::ClipboardBuffer::kMaxValue;
-  std::unique_ptr<ui::test::EventGenerator> event_generator_;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(TextfieldTest);
-};
+  ui::GestureEventDetails tap_up_details(ui::ET_GESTURE_TAP);
+  tap_up_details.set_primary_pointer_type(pointer_type);
+  GestureEventForTest tap_up(GetCursorPositionX(0), 0, tap_up_details);
+  textfield_->OnGestureEvent(&tap_up);
+}
 
 TEST_F(TextfieldTest, ModelChangesTest) {
   InitTextfield();
@@ -1302,16 +1279,16 @@
 TEST_F(TextfieldTest, InsertionDeletionTest) {
   // Insert a test string in a textfield.
   InitTextfield();
-  for (size_t i = 0; i < 10; i++)
+  for (size_t i = 0; i < 10; ++i)
     SendKeyEvent(static_cast<ui::KeyboardCode>(ui::VKEY_A + i));
   EXPECT_STR_EQ("abcdefghij", textfield_->GetText());
 
   // Test the delete and backspace keys.
   textfield_->SetSelectedRange(gfx::Range(5));
-  for (size_t i = 0; i < 3; i++)
+  for (size_t i = 0; i < 3; ++i)
     SendKeyEvent(ui::VKEY_BACK);
   EXPECT_STR_EQ("abfghij", textfield_->GetText());
-  for (size_t i = 0; i < 3; i++)
+  for (size_t i = 0; i < 3; ++i)
     SendKeyEvent(ui::VKEY_DELETE);
   EXPECT_STR_EQ("abij", textfield_->GetText());
 
@@ -1743,7 +1720,7 @@
 }
 
 TEST_F(TextfieldTest, FocusTraversalTest) {
-  InitTextfields(3);
+  InitTextfield(3);
   textfield_->RequestFocus();
 
   EXPECT_EQ(1, GetFocusedView()->GetID());
@@ -2462,7 +2439,7 @@
 #if defined(OS_APPLE)
 
 TEST_F(TextfieldTest, Yank) {
-  InitTextfields(2);
+  InitTextfield(2);
   textfield_->SetText(ASCIIToUTF16("abcdef"));
   textfield_->SetSelectedRange(gfx::Range(2, 4));
 
@@ -3331,7 +3308,7 @@
 // Verify that the selection clipboard is not updated for selections on a
 // password textfield.
 TEST_F(TextfieldTest, SelectionClipboard_Password) {
-  InitTextfields(2);
+  InitTextfield(2);
   textfield_->SetText(ASCIIToUTF16("abcd"));
 
   // Select-all should update the selection clipboard for a non-password
@@ -3452,7 +3429,7 @@
 TEST_F(TextfieldTest, VirtualKeyboardFocusEnsureCaretNotInRect) {
   InitTextfield();
 
-  aura::Window* root_window = GetRootWindow(widget_);
+  aura::Window* root_window = GetRootWindow(widget_.get());
   int keyboard_height = 200;
   gfx::Rect root_bounds = root_window->bounds();
   gfx::Rect orig_widget_bounds = gfx::Rect(0, 300, 400, 200);
@@ -3747,17 +3724,13 @@
 // Verify that after creating a new Textfield, the Textfield doesn't
 // automatically receive focus and the text cursor is not visible.
 TEST_F(TextfieldTest, TextfieldInitialization) {
-  TestTextfield* new_textfield = new TestTextfield();
-  new_textfield->set_controller(this);
-  Widget* widget(new Widget());
-  Widget::InitParams params =
-      CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
-  params.bounds = gfx::Rect(100, 100, 100, 100);
-  widget->Init(std::move(params));
+  std::unique_ptr<Widget> widget = CreateTestWidget();
   View* container = widget->SetContentsView(std::make_unique<View>());
-  container->AddChildView(new_textfield);
 
-  new_textfield->SetBoundsRect(params.bounds);
+  TestTextfield* new_textfield =
+      container->AddChildView(std::make_unique<TestTextfield>());
+  new_textfield->set_controller(this);
+  new_textfield->SetBoundsRect(gfx::Rect(100, 100, 100, 100));
   new_textfield->SetID(1);
   test_api_ = std::make_unique<TextfieldTestApi>(new_textfield);
   widget->Show();
@@ -4015,6 +3988,7 @@
   EXPECT_EQ(ui::TextInputClient::FOCUS_REASON_NONE,
             textfield_->GetFocusReason());
 
+  // Pen tap, followed by a touch tap.
   TapAtCursor(ui::EventPointerType::kPen);
   TapAtCursor(ui::EventPointerType::kTouch);
   EXPECT_EQ(ui::TextInputClient::FOCUS_REASON_PEN,
@@ -4173,4 +4147,5 @@
       ui::TextEditCommand::SCROLL_TO_END_OF_DOCUMENT));
 #endif
 }
+}  // namespace test
 }  // namespace views
diff --git a/ui/views/controls/textfield/textfield_unittest.h b/ui/views/controls/textfield/textfield_unittest.h
new file mode 100644
index 0000000..daea347
--- /dev/null
+++ b/ui/views/controls/textfield/textfield_unittest.h
@@ -0,0 +1,184 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_VIEWS_CONTROLS_TEXTFIELD_TEXTFIELD_UNITTEST_H_
+#define UI_VIEWS_CONTROLS_TEXTFIELD_TEXTFIELD_UNITTEST_H_
+
+#include "ui/views/controls/textfield/textfield.h"
+
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "ui/base/clipboard/clipboard.h"
+#include "ui/events/event_constants.h"
+#include "ui/views/controls/textfield/textfield_controller.h"
+#include "ui/views/test/views_test_base.h"
+
+#define EXPECT_STR_EQ(ascii, utf16) EXPECT_EQ(base::ASCIIToUTF16(ascii), utf16)
+
+namespace ui {
+namespace test {
+class EventGenerator;
+}
+}  // namespace ui
+
+namespace views {
+
+class TextfieldTestApi;
+
+namespace test {
+
+class MockInputMethod;
+class TestTextfield;
+
+class TextfieldTest : public ViewsTestBase, public TextfieldController {
+ public:
+  TextfieldTest();
+  ~TextfieldTest() override;
+
+  // ViewsTestBase:
+  void SetUp() override;
+  void TearDown() override;
+
+  ui::ClipboardBuffer GetAndResetCopiedToClipboard();
+  base::string16 GetClipboardText(ui::ClipboardBuffer type);
+  void SetClipboardText(ui::ClipboardBuffer type, const std::string& text);
+
+  // TextfieldController:
+  void ContentsChanged(Textfield* sender,
+                       const base::string16& new_contents) override;
+  void OnBeforeUserAction(Textfield* sender) override;
+  void OnAfterUserAction(Textfield* sender) override;
+  void OnAfterCutOrCopy(ui::ClipboardBuffer clipboard_type) override;
+
+  void InitTextfield(int count = 1);
+  ui::MenuModel* GetContextMenuModel();
+
+  bool TestingNativeMac() const;
+  bool TestingNativeCrOs() const;
+
+  template <typename T>
+  T* PrepareTextfields(int count,
+                       std::unique_ptr<T> textfield_owned,
+                       gfx::Rect bounds) {
+    widget_ = CreateTestWidget();
+    widget_->SetBounds(bounds);
+
+    View* container = widget_->SetContentsView(std::make_unique<View>());
+    T* textfield = container->AddChildView(std::move(textfield_owned));
+
+    PrepareTextfieldsInternal(count, textfield, container, bounds);
+
+    return textfield;
+  }
+
+ protected:
+  void PrepareTextfieldsInternal(int count,
+                                 Textfield* textfield,
+                                 View* view,
+                                 gfx::Rect bounds);
+
+  void SendKeyPress(ui::KeyboardCode key_code, int flags);
+  void SendKeyEvent(ui::KeyboardCode key_code,
+                    bool alt,
+                    bool shift,
+                    bool control_or_command,
+                    bool caps_lock);
+  void SendKeyEvent(ui::KeyboardCode key_code,
+                    bool shift,
+                    bool control_or_command);
+  void SendKeyEvent(ui::KeyboardCode key_code);
+  void SendKeyEvent(base::char16 ch);
+  void SendKeyEvent(base::char16 ch, int flags);
+  void SendKeyEvent(base::char16 ch, int flags, bool from_vk);
+  void DispatchMockInputMethodKeyEvent();
+
+  // Sends a platform-specific move (and select) to the logical start of line.
+  // Eg. this should move (and select) to the right end of line for RTL text.
+  virtual void SendHomeEvent(bool shift);
+
+  // Sends a platform-specific move (and select) to the logical end of line.
+  virtual void SendEndEvent(bool shift);
+
+  // Sends {delete, move, select} word {forward, backward}.
+  void SendWordEvent(ui::KeyboardCode key, bool shift);
+
+  // Sends Shift+Delete if supported, otherwise Cmd+X again.
+  void SendAlternateCut();
+
+  // Sends Ctrl+Insert if supported, otherwise Cmd+C again.
+  void SendAlternateCopy();
+
+  // Sends Shift+Insert if supported, otherwise Cmd+V again.
+  void SendAlternatePaste();
+
+  View* GetFocusedView();
+  int GetCursorPositionX(int cursor_pos);
+  int GetCursorYForTesting();
+
+  // Get the current cursor bounds.
+  gfx::Rect GetCursorBounds();
+
+  // Get the cursor bounds of |sel|.
+  gfx::Rect GetCursorBounds(const gfx::SelectionModel& sel);
+
+  gfx::Rect GetDisplayRect();
+  gfx::Rect GetCursorViewRect();
+
+  // Mouse click on the point whose x-axis is |bound|'s x plus |x_offset| and
+  // y-axis is in the middle of |bound|'s vertical range.
+  void MouseClick(const gfx::Rect bound, int x_offset);
+
+  // This is to avoid double/triple click.
+  void NonClientMouseClick();
+
+  void VerifyTextfieldContextMenuContents(bool textfield_has_selection,
+                                          bool can_undo,
+                                          ui::MenuModel* menu);
+  void PressMouseButton(ui::EventFlags mouse_button_flags);
+  void ReleaseMouseButton(ui::EventFlags mouse_button_flags);
+  void PressLeftMouseButton();
+  void ReleaseLeftMouseButton();
+  void ClickLeftMouseButton();
+  void ClickRightMouseButton();
+  void DragMouseTo(const gfx::Point& where);
+
+  // Textfield does not listen to OnMouseMoved, so this function does not send
+  // an event when it updates the cursor position.
+  void MoveMouseTo(const gfx::Point& where);
+  void TapAtCursor(ui::EventPointerType pointer_type);
+
+  // We need widget to populate wrapper class.
+  std::unique_ptr<Widget> widget_ = nullptr;
+
+  TestTextfield* textfield_ = nullptr;
+  std::unique_ptr<TextfieldTestApi> test_api_;
+  TextfieldModel* model_ = nullptr;
+
+  // The string from Controller::ContentsChanged callback.
+  base::string16 last_contents_;
+
+  // For testing input method related behaviors.
+  MockInputMethod* input_method_ = nullptr;
+
+  // Indicates how many times OnBeforeUserAction() is called.
+  int on_before_user_action_ = 0;
+
+  // Indicates how many times OnAfterUserAction() is called.
+  int on_after_user_action_ = 0;
+
+  // Position of the mouse for synthetic mouse events.
+  gfx::Point mouse_position_;
+
+  ui::ClipboardBuffer copied_to_clipboard_ = ui::ClipboardBuffer::kMaxValue;
+  std::unique_ptr<ui::test::EventGenerator> event_generator_;
+  View* event_target_ = nullptr;
+};
+
+}  // namespace test
+
+}  // namespace views
+
+#endif  // UI_VIEWS_CONTROLS_TEXTFIELD_TEXTFIELD_UNITTEST_H_
diff --git a/ui/views/examples/BUILD.gn b/ui/views/examples/BUILD.gn
index 0908b991..8b1471b 100644
--- a/ui/views/examples/BUILD.gn
+++ b/ui/views/examples/BUILD.gn
@@ -69,6 +69,8 @@
     "table_example.h",
     "text_example.cc",
     "text_example.h",
+    "textarea_example.cc",
+    "textarea_example.h",
     "textfield_example.cc",
     "textfield_example.h",
     "throbber_example.cc",
diff --git a/ui/views/examples/create_examples.cc b/ui/views/examples/create_examples.cc
index 1046b4a..112d0b5 100644
--- a/ui/views/examples/create_examples.cc
+++ b/ui/views/examples/create_examples.cc
@@ -30,6 +30,7 @@
 #include "ui/views/examples/tabbed_pane_example.h"
 #include "ui/views/examples/table_example.h"
 #include "ui/views/examples/text_example.h"
+#include "ui/views/examples/textarea_example.h"
 #include "ui/views/examples/textfield_example.h"
 #include "ui/views/examples/throbber_example.h"
 #include "ui/views/examples/toggle_button_example.h"
@@ -67,6 +68,7 @@
   examples.push_back(std::make_unique<TabbedPaneExample>());
   examples.push_back(std::make_unique<TableExample>());
   examples.push_back(std::make_unique<TextExample>());
+  examples.push_back(std::make_unique<TextareaExample>());
   examples.push_back(std::make_unique<TextfieldExample>());
   examples.push_back(std::make_unique<ToggleButtonExample>());
   examples.push_back(std::make_unique<ThrobberExample>());
diff --git a/ui/views/examples/textarea_example.cc b/ui/views/examples/textarea_example.cc
new file mode 100644
index 0000000..cf1ddf2
--- /dev/null
+++ b/ui/views/examples/textarea_example.cc
@@ -0,0 +1,36 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/views/examples/textarea_example.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/strings/utf_string_conversions.h"
+#include "ui/views/controls/textarea/textarea.h"
+#include "ui/views/layout/fill_layout.h"
+#include "ui/views/view.h"
+
+namespace views {
+namespace examples {
+
+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.";
+  auto textarea = std::make_unique<Textarea>();
+  textarea->SetText(base::UTF8ToUTF16(kLongText));
+  container->SetLayoutManager(std::make_unique<views::FillLayout>());
+  container->AddChildView(std::move(textarea));
+}
+
+}  // namespace examples
+}  // namespace views
diff --git a/ui/views/examples/textarea_example.h b/ui/views/examples/textarea_example.h
new file mode 100644
index 0000000..2fdf1a8
--- /dev/null
+++ b/ui/views/examples/textarea_example.h
@@ -0,0 +1,26 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_VIEWS_EXAMPLES_TEXTAREA_EXAMPLE_H_
+#define UI_VIEWS_EXAMPLES_TEXTAREA_EXAMPLE_H_
+
+#include "base/macros.h"
+#include "ui/views/examples/example_base.h"
+
+namespace views {
+namespace examples {
+
+class VIEWS_EXAMPLES_EXPORT TextareaExample : public ExampleBase {
+ public:
+  TextareaExample();
+  ~TextareaExample() override = default;
+
+  // ExampleBase:
+  void CreateExampleView(View* container) override;
+};
+
+}  // namespace examples
+}  // namespace views
+
+#endif  // UI_VIEWS_EXAMPLES_TEXTAREA_EXAMPLE_H_
diff --git a/ui/webui/resources/cr_components/customize_themes/customize_themes.html b/ui/webui/resources/cr_components/customize_themes/customize_themes.html
index e6eeeb1d..e4a252f 100644
--- a/ui/webui/resources/cr_components/customize_themes/customize_themes.html
+++ b/ui/webui/resources/cr_components/customize_themes/customize_themes.html
@@ -148,7 +148,8 @@
 </div>
 <cr-grid id="themesContainer" columns="6" role="radiogroup">
   <div aria-label="[[i18n('colorPickerLabel')]]"
-      tabindex="0" on-click="onAutogeneratedThemeClick_" role="radio"
+      tabindex$="[[getTabIndex_('autogenerated', selectedTheme)]]"
+      on-click="onAutogeneratedThemeClick_" role="radio"
       aria-checked$="[[getThemeIconCheckedStatus_('autogenerated', selectedTheme)]]">
     <div id="autogeneratedThemeContainer">
       <cr-theme-icon id="autogeneratedTheme"
@@ -163,7 +164,8 @@
     </paper-tooltip>
   </div>
   <div aria-label="[[i18n('defaultThemeLabel')]]"
-      tabindex="0" on-click="onDefaultThemeClick_" role="radio"
+      tabindex$="[[getTabIndex_('default', selectedTheme)]]"
+      on-click="onDefaultThemeClick_" role="radio"
       aria-checked$="[[getThemeIconCheckedStatus_('default', selectedTheme)]]">
     <cr-theme-icon id="defaultTheme"
         selected$="[[isThemeIconSelected_('default', selectedTheme)]]">
@@ -173,7 +175,8 @@
     </paper-tooltip>
   </div>
   <template is="dom-repeat" id="themes" items="[[chromeThemes_]]">
-    <div aria-label="[[item.label]]" tabindex="0"
+    <div aria-label="[[item.label]]"
+        tabindex$="[[getTabIndex_(item.id, selectedTheme)]]"
         on-click="onChromeThemeClick_" class="chrome-theme-wrapper" role="radio"
         aria-checked$="[[getThemeIconCheckedStatus_(item.id, selectedTheme)]]">
       <cr-theme-icon
diff --git a/ui/webui/resources/cr_components/customize_themes/customize_themes.js b/ui/webui/resources/cr_components/customize_themes/customize_themes.js
index 68acbe6..f1260fcf 100644
--- a/ui/webui/resources/cr_components/customize_themes/customize_themes.js
+++ b/ui/webui/resources/cr_components/customize_themes/customize_themes.js
@@ -40,10 +40,11 @@
     return {
       /**
        * An object describing the currently selected theme.
-       * @type {!Theme}
+       * @type {?Theme}
        */
       selectedTheme: {
         type: Object,
+        value: null,
         observer: 'onThemeChange_',
         notify: true,
       },
@@ -143,7 +144,8 @@
 
   /** @private */
   onThemeChange_() {
-    if (this.selectedTheme.type !== ThemeType.kAutogenerated) {
+    if (!this.selectedTheme ||
+        this.selectedTheme.type !== ThemeType.kAutogenerated) {
       return;
     }
     const rgbaFrameColor =
@@ -202,6 +204,18 @@
    * @return {string}
    * @private
    */
+  getTabIndex_(id) {
+    if (!this.selectedTheme && id === 'autogenerated') {
+      return '0';
+    }
+    return this.isThemeIconSelected_(id) ? '0' : '-1';
+  }
+
+  /**
+   * @param {string|number} id
+   * @return {string}
+   * @private
+   */
   getThemeIconCheckedStatus_(id) {
     return this.isThemeIconSelected_(id) ? 'true' : 'false';
   }
@@ -211,7 +225,8 @@
    * @private
    */
   isThirdPartyTheme_() {
-    return this.selectedTheme.type === ThemeType.kThirdParty;
+    return !!this.selectedTheme &&
+        this.selectedTheme.type === ThemeType.kThirdParty;
   }
 
   /**
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/payments/WebLayerPaymentRequestFactory.java b/weblayer/browser/java/org/chromium/weblayer_private/payments/WebLayerPaymentRequestFactory.java
index c78d931..6b1a565 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/payments/WebLayerPaymentRequestFactory.java
+++ b/weblayer/browser/java/org/chromium/weblayer_private/payments/WebLayerPaymentRequestFactory.java
@@ -21,8 +21,6 @@
 import org.chromium.content_public.browser.RenderFrameHost;
 import org.chromium.content_public.browser.WebContents;
 import org.chromium.content_public.browser.WebContentsStatics;
-import org.chromium.mojo.system.MojoException;
-import org.chromium.mojo.system.MojoResult;
 import org.chromium.payments.mojom.PaymentRequest;
 import org.chromium.services.service_manager.InterfaceFactory;
 import org.chromium.url.GURL;
@@ -107,8 +105,7 @@
     public PaymentRequest createImpl() {
         if (mRenderFrameHost == null) return new InvalidPaymentRequest();
         if (!mRenderFrameHost.isFeatureEnabled(FeaturePolicyFeature.PAYMENT)) {
-            mRenderFrameHost.getRemoteInterfaces().onConnectionError(
-                    new MojoException(MojoResult.PERMISSION_DENIED));
+            mRenderFrameHost.terminateRendererDueToBadMessage(241 /*PAYMENTS_WITHOUT_PERMISSION*/);
             return null;
         }
 
diff --git a/weblayer/browser/webrtc/media_stream_manager.cc b/weblayer/browser/webrtc/media_stream_manager.cc
index a025d45a..967fdc4 100644
--- a/weblayer/browser/webrtc/media_stream_manager.cc
+++ b/weblayer/browser/webrtc/media_stream_manager.cc
@@ -69,9 +69,6 @@
   }
   void OnDeviceStopped(const std::string& label,
                        const content::DesktopMediaID& media_id) override {}
-  void SetStopCallback(base::OnceClosure stop) override {
-    stop_ = std::move(stop);
-  }
 
   bool streaming_audio() const { return streaming_audio_; }