diff --git a/.gn b/.gn
index aff8517..cb36623 100644
--- a/.gn
+++ b/.gn
@@ -73,7 +73,6 @@
   "//chrome/browser/updates:*",  # 21 errors
   "//chrome/browser:*",  # 780 errors
   "//chrome/child:*",  # 3 errors
-  "//chrome/credential_provider/gaiacp:*",  # 1 error
   "//chrome/install_static:*",  # 4 errors
   "//chrome/notification_helper:*",  # 4 errors
   "//chrome/renderer:*",  # 42 errors
diff --git a/DEPS b/DEPS
index 195861f..4b7cc313 100644
--- a/DEPS
+++ b/DEPS
@@ -195,7 +195,7 @@
   # 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': '952f088d41e16c5524d68c4a00c038f24d734102',
+  'skia_revision': 'd385091edd586eac1fb4939a85472ba4eba8eefd',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
@@ -207,7 +207,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': '1e068e1dbb6250bd45e72c5ef73d5fe563d6ca95',
+  'angle_revision': 'd3eba1db0a1451029dcb34a028d8e347107cf929',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -215,7 +215,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
-  'pdfium_revision': '82b1c03b687687f98916b4f439b576d03bb99965',
+  'pdfium_revision': 'c6fa19fccc985d79622faeb9ecc9f0542bb56b5b',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling BoringSSL
   # and whatever else without interference from each other.
@@ -258,7 +258,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': 'f9ede33deef5dadb50a3b00cd1607e508c9229a4',
+  'catapult_revision': '77fb6d1812df4cff2b7c6f9916e091644bbd4fed',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -266,7 +266,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling devtools-frontend
   # and whatever else without interference from each other.
-  'devtools_frontend_revision': 'df4b3fe83ab26882e6a38a2ec709548a97b63941',
+  'devtools_frontend_revision': '6b329fd516bd9c1b4676d568118cbda149221742',
   # 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.
@@ -302,7 +302,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'spv_tools_revision': 'bceab9fab4bcea27af04567ee5f899510c84a968',
+  'spv_tools_revision': '08291a3a9e22c5c1a0561ad9f620b157bd75c957',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -346,7 +346,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling nearby
   # and whatever else without interference from each other.
-  'nearby_revision': 'ae277748ce068ef1730d5104002d4324fc4ed89e',
+  'nearby_revision': 'abc1cbf2673a38678eb7033291049590762526d7',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling securemessage
   # and whatever else without interference from each other.
@@ -896,7 +896,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '7d98e228f836926d223183bb3b95588b31782f9a',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'ffb12276aa7d5014d8358f561789568bc49faf73',
 
   'src/third_party/devtools-frontend/src':
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
@@ -1471,7 +1471,7 @@
   },
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '3cdde9771d8f289e4701048920ae828dc85e5335',
+    Var('webrtc_git') + '/src.git' + '@' + '89760badbdb09af158de5e1568ba0eff4240fa77',
 
   'src/third_party/libgifcodec':
      Var('skia_git') + '/libgifcodec' + '@'+  Var('libgifcodec_revision'),
@@ -1543,7 +1543,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@cba56d2d3c0923ff8aba635dc5e9b5794714c41d',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@47ebbfa449fdfdf1ed3c1acc3183b439259a685c',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/ash/ash_strings_grd/IDS_ASH_VIRTUAL_DESKS_ALERT_DESK_ACTIVATED.png.sha1 b/ash/ash_strings_grd/IDS_ASH_VIRTUAL_DESKS_ALERT_DESK_ACTIVATED.png.sha1
index 1e88e90..0e3ba80d 100644
--- a/ash/ash_strings_grd/IDS_ASH_VIRTUAL_DESKS_ALERT_DESK_ACTIVATED.png.sha1
+++ b/ash/ash_strings_grd/IDS_ASH_VIRTUAL_DESKS_ALERT_DESK_ACTIVATED.png.sha1
@@ -1 +1 @@
-8f947c92a5b23810ff5cd966d12aed537a18aed2
\ No newline at end of file
+5aded0d35e5f2a27b9b4884e83a86120d4f8b2b4
\ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_VIRTUAL_DESKS_ALERT_DESK_REMOVED.png.sha1 b/ash/ash_strings_grd/IDS_ASH_VIRTUAL_DESKS_ALERT_DESK_REMOVED.png.sha1
index dc28629..598a41d 100644
--- a/ash/ash_strings_grd/IDS_ASH_VIRTUAL_DESKS_ALERT_DESK_REMOVED.png.sha1
+++ b/ash/ash_strings_grd/IDS_ASH_VIRTUAL_DESKS_ALERT_DESK_REMOVED.png.sha1
@@ -1 +1 @@
-d60dbf64ba69bf06c2e14938aca74aa7d1de9f59
\ No newline at end of file
+e804df4de8e0633ce8fc5685e8bbe25e4a5490b2
\ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_VIRTUAL_DESKS_ALERT_WINDOW_MOVED_FROM_ACTIVE_DESK.png.sha1 b/ash/ash_strings_grd/IDS_ASH_VIRTUAL_DESKS_ALERT_WINDOW_MOVED_FROM_ACTIVE_DESK.png.sha1
index f69f638..c152161e1 100644
--- a/ash/ash_strings_grd/IDS_ASH_VIRTUAL_DESKS_ALERT_WINDOW_MOVED_FROM_ACTIVE_DESK.png.sha1
+++ b/ash/ash_strings_grd/IDS_ASH_VIRTUAL_DESKS_ALERT_WINDOW_MOVED_FROM_ACTIVE_DESK.png.sha1
@@ -1 +1 @@
-84e439c808c3505a80cb16d9c06aecb49074992a
\ No newline at end of file
+3976aebf166b57492bd979f58f5dcc2dce666dd4
\ No newline at end of file
diff --git a/ash/clipboard/clipboard_history_resource_manager.cc b/ash/clipboard/clipboard_history_resource_manager.cc
index 4a45b8c..7f4c21e 100644
--- a/ash/clipboard/clipboard_history_resource_manager.cc
+++ b/ash/clipboard/clipboard_history_resource_manager.cc
@@ -115,6 +115,8 @@
       return base::UTF8ToUTF16(data.text());
     case ui::ClipboardInternalFormat::kHtml:
       return base::UTF8ToUTF16(data.markup_data());
+    case ui::ClipboardInternalFormat::kSvg:
+      return base::UTF8ToUTF16(data.svg_data());
     case ui::ClipboardInternalFormat::kRtf:
       return GetLocalizedString(IDS_CLIPBOARD_MENU_RTF_CONTENT);
     case ui::ClipboardInternalFormat::kBookmark:
diff --git a/ash/clipboard/test_support/clipboard_history_item_builder.cc b/ash/clipboard/test_support/clipboard_history_item_builder.cc
index 934f614..2c5e0ba 100644
--- a/ash/clipboard/test_support/clipboard_history_item_builder.cc
+++ b/ash/clipboard/test_support/clipboard_history_item_builder.cc
@@ -58,6 +58,8 @@
       return SetText("Text");
     case ui::ClipboardInternalFormat::kHtml:
       return SetMarkup("Markup");
+    case ui::ClipboardInternalFormat::kSvg:
+      return SetMarkup("Svg");
     case ui::ClipboardInternalFormat::kRtf:
       return SetRtf("Rtf");
     case ui::ClipboardInternalFormat::kBookmark:
@@ -79,6 +81,8 @@
       return ClearText();
     case ui::ClipboardInternalFormat::kHtml:
       return ClearMarkup();
+    case ui::ClipboardInternalFormat::kSvg:
+      return ClearSvg();
     case ui::ClipboardInternalFormat::kRtf:
       return ClearRtf();
     case ui::ClipboardInternalFormat::kBookmark:
@@ -116,6 +120,17 @@
   return *this;
 }
 
+ClipboardHistoryItemBuilder& ClipboardHistoryItemBuilder::SetSvg(
+    const std::string& svg) {
+  svg_ = svg;
+  return *this;
+}
+
+ClipboardHistoryItemBuilder& ClipboardHistoryItemBuilder::ClearSvg() {
+  svg_ = base::nullopt;
+  return *this;
+}
+
 ClipboardHistoryItemBuilder& ClipboardHistoryItemBuilder::SetRtf(
     const std::string& rtf) {
   rtf_ = rtf;
diff --git a/ash/clipboard/test_support/clipboard_history_item_builder.h b/ash/clipboard/test_support/clipboard_history_item_builder.h
index 7f89281..3f9d06c3 100644
--- a/ash/clipboard/test_support/clipboard_history_item_builder.h
+++ b/ash/clipboard/test_support/clipboard_history_item_builder.h
@@ -48,6 +48,10 @@
   ClipboardHistoryItemBuilder& SetMarkup(const std::string& markup);
   ClipboardHistoryItemBuilder& ClearMarkup();
 
+  // Sets/clears `svg_` data.
+  ClipboardHistoryItemBuilder& SetSvg(const std::string& svg);
+  ClipboardHistoryItemBuilder& ClearSvg();
+
   // Sets/clears `rtf_` data.
   ClipboardHistoryItemBuilder& SetRtf(const std::string& rtf);
   ClipboardHistoryItemBuilder& ClearRtf();
@@ -79,6 +83,7 @@
   // `ui::ClipboardData` formats.
   base::Optional<std::string> text_;
   base::Optional<std::string> markup_;
+  base::Optional<std::string> svg_;
   base::Optional<std::string> rtf_;
   base::Optional<std::string> bookmark_title_;
   base::Optional<SkBitmap> bitmap_;
diff --git a/ash/clipboard/views/clipboard_history_item_view.cc b/ash/clipboard/views/clipboard_history_item_view.cc
index 9e71442..3073ddf 100644
--- a/ash/clipboard/views/clipboard_history_item_view.cc
+++ b/ash/clipboard/views/clipboard_history_item_view.cc
@@ -142,6 +142,7 @@
       return std::make_unique<ClipboardHistoryBitmapItemView>(item, container);
     case ui::ClipboardInternalFormat::kText:
     case ui::ClipboardInternalFormat::kHtml:
+    case ui::ClipboardInternalFormat::kSvg:
     case ui::ClipboardInternalFormat::kRtf:
     case ui::ClipboardInternalFormat::kBookmark:
     case ui::ClipboardInternalFormat::kWeb:
diff --git a/ash/frame/default_frame_header_unittest.cc b/ash/frame/default_frame_header_unittest.cc
index 76d6df3..4b57baa 100644
--- a/ash/frame/default_frame_header_unittest.cc
+++ b/ash/frame/default_frame_header_unittest.cc
@@ -6,7 +6,6 @@
 
 #include <memory>
 
-#include "ash/frame/non_client_frame_view_ash.h"
 #include "ash/public/cpp/caption_buttons/frame_back_button.h"
 #include "ash/public/cpp/caption_buttons/frame_caption_button_container_view.h"
 #include "ash/public/cpp/shell_window_ids.h"
@@ -14,16 +13,13 @@
 #include "ash/test/ash_test_base.h"
 #include "ash/wm/desks/desks_util.h"
 #include "base/i18n/rtl.h"
-#include "base/stl_util.h"
 #include "base/test/icu_test_util.h"
 #include "ui/aura/window.h"
-#include "ui/compositor/scoped_animation_duration_scale_mode.h"
 #include "ui/gfx/animation/animation_test_api.h"
 #include "ui/gfx/color_utils.h"
 #include "ui/views/test/test_views.h"
 #include "ui/views/widget/widget.h"
 #include "ui/views/window/non_client_view.h"
-#include "ui/wm/core/window_util.h"
 
 using views::NonClientFrameView;
 using views::Widget;
@@ -86,197 +82,70 @@
 
 // Ensure the right frame colors are used.
 TEST_F(DefaultFrameHeaderTest, FrameColors) {
-  const auto win0_bounds = gfx::Rect{1, 2, 3, 4};
-  auto win0 = CreateAppWindow(win0_bounds, AppType::BROWSER);
-  Widget* widget = Widget::GetWidgetForNativeWindow(win0.get());
-  DefaultFrameHeader* frame_header =
-      static_cast<DefaultFrameHeader*>(FrameHeader::Get(widget));
+  std::unique_ptr<Widget> widget = CreateTestWidget(
+      nullptr, desks_util::GetActiveDeskContainerId(), gfx::Rect(1, 2, 3, 4));
+  FrameCaptionButtonContainerView container(widget.get());
+  views::StaticSizedView window_icon(gfx::Size(16, 16));
+  window_icon.SetBounds(0, 0, 16, 16);
+  widget->SetBounds(gfx::Rect(0, 0, 500, 500));
+  widget->Show();
+
+  DefaultFrameHeader frame_header(
+      widget.get(), widget->non_client_view()->frame_view(), &container);
   // Check frame color is sensitive to mode.
   SkColor active = SkColorSetRGB(70, 70, 70);
   SkColor inactive = SkColorSetRGB(200, 200, 200);
-  win0->SetProperty(kFrameActiveColorKey, active);
-  win0->SetProperty(kFrameInactiveColorKey, inactive);
-  frame_header->UpdateFrameColors();
-  frame_header->mode_ = FrameHeader::MODE_ACTIVE;
-  EXPECT_EQ(active, frame_header->GetCurrentFrameColor());
-  frame_header->mode_ = FrameHeader::MODE_INACTIVE;
-  EXPECT_EQ(inactive, frame_header->GetCurrentFrameColor());
-  EXPECT_EQ(active, frame_header->GetActiveFrameColorForPaintForTest());
+  widget->GetNativeWindow()->SetProperty(kFrameActiveColorKey, active);
+  widget->GetNativeWindow()->SetProperty(kFrameInactiveColorKey, inactive);
+  frame_header.UpdateFrameColors();
+  frame_header.mode_ = FrameHeader::MODE_ACTIVE;
+  EXPECT_EQ(active, frame_header.GetCurrentFrameColor());
+  frame_header.mode_ = FrameHeader::MODE_INACTIVE;
+  EXPECT_EQ(inactive, frame_header.GetCurrentFrameColor());
+  EXPECT_EQ(active, frame_header.GetActiveFrameColorForPaintForTest());
 
   // Update to the new value which has no blue, which should animate.
-  frame_header->mode_ = FrameHeader::MODE_ACTIVE;
+  frame_header.mode_ = FrameHeader::MODE_ACTIVE;
   SkColor new_active = SkColorSetRGB(70, 70, 0);
-  win0->SetProperty(kFrameActiveColorKey, new_active);
-  frame_header->UpdateFrameColors();
+  widget->GetNativeWindow()->SetProperty(kFrameActiveColorKey, new_active);
+  frame_header.UpdateFrameColors();
+
+  gfx::SlideAnimation* animation =
+      frame_header.GetAnimationForActiveFrameColorForTest();
+  gfx::AnimationTestApi test_api(animation);
+
+  // animate half way through.
+  base::TimeTicks now = base::TimeTicks::Now();
+  test_api.SetStartTime(now);
+  test_api.Step(now + base::TimeDelta::FromMilliseconds(120));
+
+  // GetCurrentFrameColor should return the target color.
+  EXPECT_EQ(new_active, frame_header.GetCurrentFrameColor());
+
+  // The color used for paint should be somewhere between 0 and 70.
+  SkColor new_active_for_paint =
+      frame_header.GetActiveFrameColorForPaintForTest();
+  EXPECT_NE(new_active, new_active_for_paint);
+  EXPECT_EQ(53u, SkColorGetB(new_active_for_paint));
 
   // Now update to the new value which is full blue.
   SkColor new_new_active = SkColorSetRGB(70, 70, 255);
-  win0->SetProperty(kFrameActiveColorKey, new_new_active);
-  frame_header->UpdateFrameColors();
+  widget->GetNativeWindow()->SetProperty(kFrameActiveColorKey, new_new_active);
+  frame_header.UpdateFrameColors();
+
+  now = base::TimeTicks::Now();
+  test_api.SetStartTime(now);
+  test_api.Step(now + base::TimeDelta::FromMilliseconds(20));
 
   // Again, GetCurrentFrameColor should return the target color.
-  EXPECT_EQ(new_new_active, frame_header->GetCurrentFrameColor());
-}
+  EXPECT_EQ(new_new_active, frame_header.GetCurrentFrameColor());
 
-namespace {
-
-class LayerDestroyedChecker : public ui::LayerObserver {
- public:
-  explicit LayerDestroyedChecker(ui::Layer* layer) { layer->AddObserver(this); }
-  LayerDestroyedChecker(const LayerDestroyedChecker&) = delete;
-  LayerDestroyedChecker& operator=(const LayerDestroyedChecker&) = delete;
-  ~LayerDestroyedChecker() override = default;
-
-  void LayerDestroyed(ui::Layer* layer) override {
-    layer->RemoveObserver(this);
-    destroyed_ = true;
-  }
-  bool destroyed() const { return destroyed_; }
-
- private:
-  bool destroyed_ = false;
-};
-
-}  // namespace
-
-// A class to wait until hthe frame header is painted.
-class FramePaintWaiter : public ui::CompositorObserver {
- public:
-  explicit FramePaintWaiter(aura::Window* window)
-      : frame_header_(
-            FrameHeader::Get(Widget::GetWidgetForNativeWindow(window))) {
-    frame_header_->view()->GetWidget()->GetCompositor()->AddObserver(this);
-  }
-  FramePaintWaiter(const FramePaintWaiter&) = delete;
-  FramePaintWaiter& operator=(FramePaintWaiter&) = delete;
-  ~FramePaintWaiter() override {
-    frame_header_->view()->GetWidget()->GetCompositor()->RemoveObserver(this);
-  }
-
-  // ui::CompositorObserver:
-  void OnCompositingDidCommit(ui::Compositor* compositor) override {
-    if (frame_header_->painted_)
-      run_loop_.Quit();
-  }
-
-  void Wait() { run_loop_.Run(); }
-
- private:
-  base::RunLoop run_loop_;
-  FrameHeader* frame_header_ = nullptr;
-};
-
-TEST_F(DefaultFrameHeaderTest, DeleteDuringAnimation) {
-  const auto bounds = gfx::Rect(100, 100);
-  auto win0 = CreateAppWindow(bounds, AppType::BROWSER);
-  auto win1 = CreateAppWindow(bounds, AppType::BROWSER);
-
-  Widget* widget = Widget::GetWidgetForNativeWindow(win0.get());
-  EXPECT_TRUE(FrameHeader::Get(widget));
-
-  EXPECT_TRUE(wm::IsActiveWindow(win1.get()));
-
-  // A frame will not animate until it is painted first.
-  FramePaintWaiter(win0.get()).Wait();
-  FramePaintWaiter(win1.get()).Wait();
-
-  ui::ScopedAnimationDurationScaleMode non_zero_duration_mode(
-      ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
-  wm::ActivateWindow(win0.get());
-
-  auto* header_view = NonClientFrameViewAsh::Get(win0.get())->GetHeaderView();
-  ASSERT_TRUE(header_view);
-  auto* animating_layer_holding_view = header_view->children()[0];
-  EXPECT_TRUE(!std::strcmp(animating_layer_holding_view->GetClassName(),
-                           "FrameAnimatorView"));
-  ASSERT_TRUE(animating_layer_holding_view->layer());
-  ASSERT_GT(animating_layer_holding_view->layer()->parent()->children().size(),
-            2u);
-  auto* animating_layer =
-      animating_layer_holding_view->layer()->parent()->children()[0];
-  EXPECT_EQ(ui::LAYER_TEXTURED, animating_layer->type());
-  EXPECT_NE(std::string::npos, animating_layer->name().find(":Old", 0));
-  EXPECT_TRUE(animating_layer->GetAnimator()->is_animating());
-
-  LayerDestroyedChecker checker(animating_layer);
-
-  win0.reset();
-
-  EXPECT_TRUE(checker.destroyed());
-}
-
-// Make sure that the animation is canceled when resized.
-TEST_F(DefaultFrameHeaderTest, ResizeAndReorderDuringAnimation) {
-  const auto bounds = gfx::Rect(100, 100);
-  auto win_0 = CreateAppWindow(bounds, AppType::BROWSER);
-  auto win_1 = CreateAppWindow(bounds, AppType::BROWSER);
-
-  EXPECT_TRUE(wm::IsActiveWindow(win_1.get()));
-
-  // A frame will not animate until it is painted first.
-  FramePaintWaiter(win_0.get()).Wait();
-  FramePaintWaiter(win_1.get()).Wait();
-
-  ui::ScopedAnimationDurationScaleMode non_zero_duration_mode(
-      ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
-
-  auto* header_view_0 =
-      NonClientFrameViewAsh::Get(win_0.get())->GetHeaderView();
-  auto* animating_layer_holding_view_0 = header_view_0->children()[0];
-  EXPECT_TRUE(!std::strcmp(animating_layer_holding_view_0->GetClassName(),
-                           "FrameAnimatorView"));
-  size_t original_layers_count_0 =
-      animating_layer_holding_view_0->layer()->parent()->children().size();
-
-  auto* header_view_1 =
-      NonClientFrameViewAsh::Get(win_1.get())->GetHeaderView();
-  auto* animating_layer_holding_view_1 = header_view_1->children()[0];
-  EXPECT_TRUE(!std::strcmp(animating_layer_holding_view_1->GetClassName(),
-                           "FrameAnimatorView"));
-  size_t original_layers_count_1 =
-      animating_layer_holding_view_1->layer()->parent()->children().size();
-
-  wm::ActivateWindow(win_0.get());
-
-  {
-    // Resize during animation
-    EXPECT_EQ(
-        animating_layer_holding_view_0->layer()->parent()->children().size(),
-        original_layers_count_0 + 1);
-    auto* animating_layer =
-        animating_layer_holding_view_0->layer()->parent()->children()[0];
-    EXPECT_TRUE(animating_layer->GetAnimator()->is_animating());
-
-    LayerDestroyedChecker checker(animating_layer);
-
-    win_0->SetBounds(gfx::Rect(200, 200));
-
-    // Animating layer shuld have been removed.
-    EXPECT_EQ(
-        animating_layer_holding_view_0->layer()->parent()->children().size(),
-        original_layers_count_0);
-    EXPECT_TRUE(checker.destroyed());
-  }
-
-  {
-    // wind_1 should still be animating.
-    EXPECT_EQ(
-        animating_layer_holding_view_1->layer()->parent()->children().size(),
-        original_layers_count_1 + 1);
-    auto* animating_layer =
-        animating_layer_holding_view_1->layer()->parent()->children()[0];
-    EXPECT_TRUE(animating_layer->GetAnimator()->is_animating());
-    LayerDestroyedChecker checker(animating_layer);
-
-    // Change the view's stacking order should stop the animation.
-    ASSERT_EQ(3u, header_view_1->children().size());
-    header_view_1->ReorderChildView(header_view_1->children()[2], 0);
-
-    EXPECT_EQ(
-        animating_layer_holding_view_1->layer()->parent()->children().size(),
-        original_layers_count_1);
-    EXPECT_TRUE(checker.destroyed());
-  }
+  // The start value should be the previous paint color, so it should be
+  // near 53.
+  SkColor new_new_active_for_paint =
+      frame_header.GetActiveFrameColorForPaintForTest();
+  EXPECT_NE(new_active_for_paint, new_new_active_for_paint);
+  EXPECT_EQ(54u, SkColorGetB(new_new_active_for_paint));
 }
 
 }  // namespace ash
diff --git a/ash/frame/header_view.cc b/ash/frame/header_view.cc
index 053d30f..71a2cfd 100644
--- a/ash/frame/header_view.cc
+++ b/ash/frame/header_view.cc
@@ -291,7 +291,13 @@
   if (!should_paint_ || !target_widget_)
     return;
 
-  frame_header_->PaintHeader(canvas);
+  bool paint_as_active =
+      target_widget_->non_client_view()->frame_view()->ShouldPaintAsActive();
+  frame_header_->SetPaintAsActive(paint_as_active);
+
+  FrameHeader::Mode header_mode =
+      paint_as_active ? FrameHeader::MODE_ACTIVE : FrameHeader::MODE_INACTIVE;
+  frame_header_->PaintHeader(canvas, header_mode);
 }
 
 void HeaderView::UpdateBackButton() {
diff --git a/ash/frame/non_client_frame_view_ash.cc b/ash/frame/non_client_frame_view_ash.cc
index 77c8eb06..f3f7b7b 100644
--- a/ash/frame/non_client_frame_view_ash.cc
+++ b/ash/frame/non_client_frame_view_ash.cc
@@ -410,7 +410,8 @@
 }
 
 void NonClientFrameViewAsh::PaintAsActiveChanged() {
-  header_view_->GetFrameHeader()->SetPaintAsActive(ShouldPaintAsActive());
+  // The icons differ between active and inactive.
+  header_view_->SchedulePaint();
   frame_->non_client_view()->Layout();
 }
 
diff --git a/ash/public/cpp/caption_buttons/frame_caption_button_container_view.cc b/ash/public/cpp/caption_buttons/frame_caption_button_container_view.cc
index 9c87699dc..882aefb8 100644
--- a/ash/public/cpp/caption_buttons/frame_caption_button_container_view.cc
+++ b/ash/public/cpp/caption_buttons/frame_caption_button_container_view.cc
@@ -228,7 +228,6 @@
   minimize_button_->set_paint_as_active(paint_as_active);
   size_button_->set_paint_as_active(paint_as_active);
   close_button_->set_paint_as_active(paint_as_active);
-  SchedulePaint();
 }
 
 void FrameCaptionButtonContainerView::SetBackgroundColor(
diff --git a/ash/public/cpp/default_frame_header.cc b/ash/public/cpp/default_frame_header.cc
index 181707df..ab4518f 100644
--- a/ash/public/cpp/default_frame_header.cc
+++ b/ash/public/cpp/default_frame_header.cc
@@ -25,10 +25,6 @@
 
 namespace {
 
-// Duration of animation scheduled when frame color is changed.
-constexpr base::TimeDelta kFrameColorChangeAnimationDuration =
-    base::TimeDelta::FromMilliseconds(240);
-
 // Tiles an image into an area, rounding the top corners.
 void TileRoundRect(gfx::Canvas* canvas,
                    const cc::PaintFlags& flags,
@@ -57,6 +53,36 @@
 
 namespace ash {
 
+DefaultFrameHeader::ColorAnimator::ColorAnimator(
+    gfx::AnimationDelegate* delegate)
+    : animation_(delegate) {
+  animation_.SetSlideDuration(base::TimeDelta::FromMilliseconds(240));
+  animation_.SetTweenType(gfx::Tween::EASE_IN);
+  animation_.Reset(1);
+}
+
+DefaultFrameHeader::ColorAnimator::ColorAnimator::~ColorAnimator() = default;
+
+void DefaultFrameHeader::ColorAnimator::SetTargetColor(SkColor target) {
+  target_color_ = target;
+  start_color_ = current_color_;
+  if (current_color_ == kDefaultFrameColor) {
+    // Changing from default should be set immediately.
+    current_color_ = target_color_;
+    animation_.Reset(1);
+  } else {
+    animation_.Reset(0);
+  }
+  animation_.Show();
+}
+
+SkColor DefaultFrameHeader::ColorAnimator::GetCurrentColor() {
+  current_color_ =
+      color_utils::AlphaBlend(target_color_, start_color_,
+                              static_cast<float>(animation_.GetCurrentValue()));
+  return current_color_;
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 // DefaultFrameHeader, public:
 
@@ -64,7 +90,9 @@
     views::Widget* target_widget,
     views::View* header_view,
     FrameCaptionButtonContainerView* caption_button_container)
-    : FrameHeader(target_widget, header_view) {
+    : FrameHeader(target_widget, header_view),
+      active_frame_color_(this),
+      inactive_frame_color_(this) {
   DCHECK(caption_button_container);
   SetCaptionButtonContainer(caption_button_container);
 }
@@ -86,19 +114,18 @@
       target_window->GetProperty(kFrameInactiveColorKey);
 
   bool updated = false;
-  // Update the frame if the frame color for the current active state chagnes.
-  if (active_frame_color_ != active_frame_color) {
-    active_frame_color_ = active_frame_color;
-    updated = mode() == Mode::MODE_ACTIVE;
+  if (active_frame_color_.target_color() != active_frame_color) {
+    active_frame_color_.SetTargetColor(active_frame_color);
+    updated = true;
   }
-  if (inactive_frame_color_ != inactive_frame_color) {
-    inactive_frame_color_ = inactive_frame_color;
-    updated |= mode() == Mode::MODE_INACTIVE;
+  if (inactive_frame_color_.target_color() != inactive_frame_color) {
+    inactive_frame_color_.SetTargetColor(inactive_frame_color);
+    updated = true;
   }
 
   if (updated) {
     UpdateCaptionButtonColors();
-    StartTransitionAnimation(kFrameColorChangeAnimationDuration);
+    view()->SchedulePaint();
   }
 }
 
@@ -112,8 +139,10 @@
                           : 0;
 
   cc::PaintFlags flags;
-  flags.setColor(mode() == Mode::MODE_ACTIVE ? active_frame_color_
-                                             : inactive_frame_color_);
+  flags.setColor(color_utils::AlphaBlend(
+      active_frame_color_.GetCurrentColor(),
+      inactive_frame_color_.GetCurrentColor(),
+      static_cast<float>(activation_animation().GetCurrentValue())));
   flags.setAntiAlias(true);
   if (width_in_pixels_ > 0) {
     canvas->Save();
@@ -157,11 +186,17 @@
 }
 
 SkColor DefaultFrameHeader::GetCurrentFrameColor() const {
-  return mode() == MODE_ACTIVE ? active_frame_color_ : inactive_frame_color_;
+  return mode() == MODE_ACTIVE ? active_frame_color_.target_color()
+                               : inactive_frame_color_.target_color();
+}
+
+gfx::SlideAnimation*
+DefaultFrameHeader::GetAnimationForActiveFrameColorForTest() {
+  return active_frame_color_.animation();
 }
 
 SkColor DefaultFrameHeader::GetActiveFrameColorForPaintForTest() {
-  return active_frame_color_;
+  return active_frame_color_.GetCurrentColor();
 }
 
 }  // namespace ash
diff --git a/ash/public/cpp/default_frame_header.h b/ash/public/cpp/default_frame_header.h
index 67b8381e6..b528ee2 100644
--- a/ash/public/cpp/default_frame_header.h
+++ b/ash/public/cpp/default_frame_header.h
@@ -26,8 +26,12 @@
                      FrameCaptionButtonContainerView* caption_button_container);
   ~DefaultFrameHeader() override;
 
-  SkColor active_frame_color_for_testing() { return active_frame_color_; }
-  SkColor inactive_frame_color_for_testing() { return inactive_frame_color_; }
+  SkColor active_frame_color_for_testing() {
+    return active_frame_color_.target_color();
+  }
+  SkColor inactive_frame_color_for_testing() {
+    return inactive_frame_color_.target_color();
+  }
 
   void SetWidthInPixels(int width_in_pixels);
 
@@ -47,10 +51,33 @@
   // Returns the window of the target widget.
   aura::Window* GetTargetWindow();
 
+  gfx::SlideAnimation* GetAnimationForActiveFrameColorForTest();
   SkColor GetActiveFrameColorForPaintForTest();
 
-  SkColor active_frame_color_ = kDefaultFrameColor;
-  SkColor inactive_frame_color_ = kDefaultFrameColor;
+  // A utility class to animate color value.
+  class ColorAnimator {
+   public:
+    explicit ColorAnimator(gfx::AnimationDelegate* delegate);
+    ~ColorAnimator();
+
+    void SetTargetColor(SkColor target);
+    SkColor target_color() const { return target_color_; }
+    SkColor GetCurrentColor();
+    float get_value() const { return animation_.GetCurrentValue(); }
+
+    gfx::SlideAnimation* animation() { return &animation_; }
+
+   private:
+    gfx::SlideAnimation animation_;
+    SkColor start_color_ = kDefaultFrameColor;
+    SkColor target_color_ = kDefaultFrameColor;
+    SkColor current_color_ = kDefaultFrameColor;
+
+    DISALLOW_COPY_AND_ASSIGN(ColorAnimator);
+  };
+
+  ColorAnimator active_frame_color_;
+  ColorAnimator inactive_frame_color_;
 
   int width_in_pixels_ = -1;
 
diff --git a/ash/public/cpp/frame_header.cc b/ash/public/cpp/frame_header.cc
index 6b549a4..97ad1644 100644
--- a/ash/public/cpp/frame_header.cc
+++ b/ash/public/cpp/frame_header.cc
@@ -11,9 +11,6 @@
 #include "ash/public/cpp/window_properties.h"
 #include "base/logging.h"  // DCHECK
 #include "ui/base/class_property.h"
-#include "ui/compositor/layer_animation_observer.h"
-#include "ui/compositor/layer_tree_owner.h"
-#include "ui/compositor/scoped_layer_animation_settings.h"
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/color_utils.h"
 #include "ui/gfx/font_list.h"
@@ -32,9 +29,6 @@
 
 namespace {
 
-constexpr base::TimeDelta kFrameActivationAnimationDuration =
-    base::TimeDelta::FromMilliseconds(200);
-
 DEFINE_UI_CLASS_PROPERTY_KEY(FrameHeader*, kFrameHeaderKey, nullptr)
 
 // Returns the available bounds for the header's title given the views to the
@@ -67,6 +61,29 @@
   return gfx::Rect(x, y, width, title_height);
 }
 
+// Returns true if the header for |widget| can animate to new visuals when the
+// widget's activation changes. Returns false if the header should switch to
+// new visuals instantaneously.
+bool CanAnimateActivation(views::Widget* widget) {
+  // Do not animate the header if the parent (e.g. the active desk container) is
+  // already animating. All of the implementers of FrameHeader animate
+  // activation by continuously painting during the animation. This gives the
+  // parent's animation a slower frame rate.
+  // TODO(sky): Expose a better way to determine this rather than assuming the
+  // parent is a toplevel container.
+  aura::Window* window = widget->GetNativeWindow();
+  // TODO(sky): parent()->layer() is for mash until animations ported.
+  if (!window || !window->parent() || !window->parent()->layer())
+    return true;
+
+  ui::LayerAnimator* parent_layer_animator =
+      window->parent()->layer()->GetAnimator();
+  return !parent_layer_animator->IsAnimatingProperty(
+             ui::LayerAnimationElement::OPACITY) &&
+         !parent_layer_animator->IsAnimatingProperty(
+             ui::LayerAnimationElement::VISIBILITY);
+}
+
 }  // namespace
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -90,102 +107,29 @@
          caption_button_container_->GetMinimumSize().width();
 }
 
-// An invisible view that drives the frame's animation. This holds the animating
-// layer as a layer beneath this view so that it's behind all other child layers
-// of the window to avoid hiding their contents.
-class FrameHeader::FrameAnimatorView : public views::View,
-                                       public views::ViewObserver,
-                                       public ui::ImplicitAnimationObserver {
- public:
-  FrameAnimatorView(FrameHeader* frame_header, views::View* parent)
-      : frame_header_(frame_header), parent_(parent) {
-    SetPaintToLayer(ui::LAYER_NOT_DRAWN);
-    parent_->AddChildViewAt(this, 0);
-    parent_->AddObserver(this);
-  }
-  FrameAnimatorView(const FrameAnimatorView&) = delete;
-  FrameAnimatorView& operator=(const FrameAnimatorView&) = delete;
-  ~FrameAnimatorView() override {
-    StopAnimation();
-    // A child view should always be removed first.
-    parent_->RemoveObserver(this);
-  }
+void FrameHeader::PaintHeader(gfx::Canvas* canvas, Mode mode) {
+  Mode old_mode = mode_;
+  mode_ = mode;
 
-  void StartAnimation(base::TimeDelta duration) {
-    StopAnimation();
-    aura::Window* window = frame_header_->target_widget()->GetNativeWindow();
+  if (mode_ != old_mode) {
+    UpdateCaptionButtonColors();
 
-    // Make sure the this view is at the bottom of root view's children.
-    parent_->ReorderChildView(this, 0);
-
-    std::unique_ptr<ui::LayerTreeOwner> old_layer_owner =
-        std::make_unique<ui::LayerTreeOwner>(window->RecreateLayer());
-    ui::Layer* old_layer = old_layer_owner->root();
-    ui::Layer* new_layer = window->layer();
-    new_layer->SetName(old_layer->name());
-    old_layer->SetName(old_layer->name() + ":Old");
-    old_layer->SetTransform(gfx::Transform());
-
-    layer_owner_ = std::move(old_layer_owner);
-
-    AddLayerBeneathView(old_layer);
-
-    // The old layer is on top and should fade out.
-    old_layer->SetOpacity(1.f);
-    new_layer->SetOpacity(1.f);
-    {
-      ui::ScopedLayerAnimationSettings settings(old_layer->GetAnimator());
-      settings.SetPreemptionStrategy(
-          ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
-      settings.AddObserver(this);
-      settings.SetTransitionDuration(duration);
-      old_layer->SetOpacity(0.f);
-      settings.SetTweenType(gfx::Tween::EASE_OUT);
+    if (!initial_paint_ && CanAnimateActivation(target_widget_)) {
+      activation_animation_.SetSlideDuration(
+          base::TimeDelta::FromMilliseconds(200));
+      if (mode_ == MODE_ACTIVE)
+        activation_animation_.Show();
+      else
+        activation_animation_.Hide();
+    } else {
+      if (mode_ == MODE_ACTIVE)
+        activation_animation_.Reset(1);
+      else
+        activation_animation_.Reset(0);
     }
+    initial_paint_ = false;
   }
 
-  // views::Views:
-  const char* GetClassName() const override { return "FrameAnimatorView"; }
-  std::unique_ptr<ui::Layer> RecreateLayer() override {
-    // A layer may be recreated for another animation (maximize/restore).
-    // Just cancel the animation if that happens during animation.
-    StopAnimation();
-    return views::View::RecreateLayer();
-  }
-
-  // ViewObserver::
-  void OnChildViewReordered(views::View* observed_view,
-                            views::View* child) override {
-    // Stop animation if the child view order has changed during animation.
-    StopAnimation();
-  }
-  void OnViewBoundsChanged(views::View* observed_view) override {
-    // Stop animation if the frame size changed during animation.
-    StopAnimation();
-    SetBoundsRect(parent_->GetLocalBounds());
-  }
-
-  // ui::ImplicitAnimationObserver overrides:
-  void OnImplicitAnimationsCompleted() override {
-    RemoveLayerBeneathView(layer_owner_->root());
-    layer_owner_ = nullptr;
-  }
-
- private:
-  void StopAnimation() {
-    if (layer_owner_) {
-      layer_owner_->root()->GetAnimator()->StopAnimating();
-      layer_owner_ = nullptr;
-    }
-  }
-
-  FrameHeader* frame_header_;
-  views::View* parent_;
-  std::unique_ptr<ui::LayerTreeOwner> layer_owner_;
-};
-
-void FrameHeader::PaintHeader(gfx::Canvas* canvas) {
-  painted_ = true;
   DoPaintHeader(canvas);
 }
 
@@ -213,18 +157,6 @@
 }
 
 void FrameHeader::SetPaintAsActive(bool paint_as_active) {
-  // No need to animate if already active.
-  const bool already_active = (mode_ == Mode::MODE_ACTIVE);
-
-  if (already_active == paint_as_active)
-    return;
-
-  mode_ = paint_as_active ? MODE_ACTIVE : MODE_INACTIVE;
-
-  // The frame has no content yet to animatie.
-  if (painted_)
-    StartTransitionAnimation(kFrameActivationAnimationDuration);
-
   caption_button_container_->SetPaintAsActive(paint_as_active);
   if (back_button_)
     back_button_->set_paint_as_active(paint_as_active);
@@ -267,14 +199,22 @@
 }
 
 ///////////////////////////////////////////////////////////////////////////////
+// gfx::AnimationDelegate overrides:
+
+void FrameHeader::AnimationProgressed(const gfx::Animation* animation) {
+  view_->SchedulePaintInRect(GetPaintedBounds());
+}
+
+///////////////////////////////////////////////////////////////////////////////
 // FrameHeader, protected:
 
 FrameHeader::FrameHeader(views::Widget* target_widget, views::View* view)
-    : target_widget_(target_widget), view_(view) {
+    : views::AnimationDelegateViews(view),
+      target_widget_(target_widget),
+      view_(view) {
   DCHECK(target_widget);
   DCHECK(view);
   UpdateFrameHeaderKey();
-  frame_animator_ = new FrameAnimatorView(this, view);
 }
 
 void FrameHeader::UpdateFrameHeaderKey() {
@@ -329,18 +269,6 @@
   LayoutHeaderInternal();
 }
 
-void FrameHeader::StartTransitionAnimation(base::TimeDelta duration) {
-  aura::Window* window = target_widget_->GetNativeWindow();
-  // Don't start another animation if the window is already animating
-  // such as maximize/restore/unminimize.
-  if (window->layer()->GetAnimator()->is_animating())
-    return;
-
-  frame_animator_->StartAnimation(duration);
-
-  frame_animator_->SchedulePaint();
-}
-
 ///////////////////////////////////////////////////////////////////////////////
 // FrameHeader, private:
 
diff --git a/ash/public/cpp/frame_header.h b/ash/public/cpp/frame_header.h
index 89ce587..6f8165d 100644
--- a/ash/public/cpp/frame_header.h
+++ b/ash/public/cpp/frame_header.h
@@ -7,12 +7,11 @@
 
 #include "ash/public/cpp/ash_public_export.h"
 #include "ash/public/cpp/caption_buttons/frame_caption_button_container_view.h"
-#include "base/callback.h"
-#include "base/optional.h"
 #include "base/strings/string16.h"
 #include "third_party/skia/include/core/SkColor.h"
 #include "ui/base/ui_base_types.h"
-#include "ui/compositor/layer_animation_observer.h"
+#include "ui/gfx/animation/slide_animation.h"
+#include "ui/views/animation/animation_delegate_views.h"
 #include "ui/views/window/frame_caption_button.h"
 
 namespace gfx {
@@ -30,13 +29,13 @@
 class CaptionButtonModel;
 
 // Helper class for managing the window header.
-class ASH_PUBLIC_EXPORT FrameHeader {
+class ASH_PUBLIC_EXPORT FrameHeader : public views::AnimationDelegateViews {
  public:
   enum Mode { MODE_ACTIVE, MODE_INACTIVE };
 
   static FrameHeader* Get(views::Widget* widget);
 
-  virtual ~FrameHeader();
+  ~FrameHeader() override;
 
   const base::string16& frame_text_override() const {
     return frame_text_override_;
@@ -46,7 +45,7 @@
   int GetMinimumHeaderWidth() const;
 
   // Paints the header.
-  void PaintHeader(gfx::Canvas* canvas);
+  void PaintHeader(gfx::Canvas* canvas, Mode mode);
 
   // Performs layout for the header.
   void LayoutHeader();
@@ -82,6 +81,9 @@
   // regardless of what ShouldShowWindowTitle() returns.
   void SetFrameTextOverride(const base::string16& frame_text_override);
 
+  // views::AnimationDelegateViews:
+  void AnimationProgressed(const gfx::Animation* animation) override;
+
   void UpdateFrameHeaderKey();
 
   views::View* view() { return view_; }
@@ -110,20 +112,19 @@
 
   Mode mode() const { return mode_; }
 
+  const gfx::SlideAnimation& activation_animation() {
+    return activation_animation_;
+  }
+
   virtual void DoPaintHeader(gfx::Canvas* canvas) = 0;
   virtual views::CaptionButtonLayoutSize GetButtonLayoutSize() const = 0;
   virtual SkColor GetTitleColor() const = 0;
   virtual SkColor GetCurrentFrameColor() const = 0;
 
-  // Starts fade transition animation with given duration.
-  void StartTransitionAnimation(base::TimeDelta duration);
-
  private:
-  class FrameAnimatorView;
   FRIEND_TEST_ALL_PREFIXES(DefaultFrameHeaderTest, BackButtonAlignment);
   FRIEND_TEST_ALL_PREFIXES(DefaultFrameHeaderTest, TitleIconAlignment);
   FRIEND_TEST_ALL_PREFIXES(DefaultFrameHeaderTest, FrameColors);
-  friend class FramePaintWaiter;
 
   void LayoutHeaderInternal();
 
@@ -138,19 +139,20 @@
   views::FrameCaptionButton* back_button_ = nullptr;  // May remain nullptr.
   views::View* left_header_view_ = nullptr;    // May remain nullptr.
   FrameCaptionButtonContainerView* caption_button_container_ = nullptr;
-  FrameAnimatorView* frame_animator_ = nullptr;  // owned by view tree.
 
   // The height of the header to paint.
   int painted_height_ = 0;
 
-  // Used to skip animation when the frame hasn't painted yet.
-  bool painted_ = false;
-
   // Whether the header should be painted as active.
   Mode mode_ = MODE_INACTIVE;
 
+  // Whether the header is painted for the first time.
+  bool initial_paint_ = true;
+
   base::string16 frame_text_override_;
 
+  gfx::SlideAnimation activation_animation_{this};
+
   DISALLOW_COPY_AND_ASSIGN(FrameHeader);
 };
 
diff --git a/ash/public/cpp/shelf_config.h b/ash/public/cpp/shelf_config.h
index 24e430a4..298bc4dfb 100644
--- a/ash/public/cpp/shelf_config.h
+++ b/ash/public/cpp/shelf_config.h
@@ -9,6 +9,7 @@
 #include "ash/public/cpp/app_list/app_list_controller_observer.h"
 #include "ash/public/cpp/shelf_types.h"
 #include "ash/public/cpp/tablet_mode_observer.h"
+#include "ash/style/ash_color_provider.h"
 #include "ash/system/model/virtual_keyboard_model.h"
 #include "ash/wm/overview/overview_observer.h"
 #include "base/macros.h"
@@ -198,6 +199,9 @@
   // if the wallpaper can not be used to generate a themed color.
   SkColor GetThemedColorFromWallpaper(SkColor base_color) const;
 
+  // Gets the base layer type for shelf color.
+  AshColorProvider::BaseLayerType GetShelfBaseLayerType() const;
+
   // Gets the default shelf color, calculated using the wallpaper color if
   // available.
   SkColor GetDefaultShelfColor() const;
diff --git a/ash/shelf/shelf_config.cc b/ash/shelf/shelf_config.cc
index 2027767..a4f760b 100644
--- a/ash/shelf/shelf_config.cc
+++ b/ash/shelf/shelf_config.cc
@@ -486,6 +486,24 @@
   return SkColorSetA(base_color, base_alpha);
 }
 
+AshColorProvider::BaseLayerType ShelfConfig::GetShelfBaseLayerType() const {
+  if (!chromeos::switches::ShouldShowShelfHotseat()) {
+    return in_tablet_mode_ ? AshColorProvider::BaseLayerType::kTransparent60
+                           : AshColorProvider::BaseLayerType::kTransparent80;
+  }
+
+  if (in_tablet_mode_) {
+    if (is_in_app()) {
+      return AshColorProvider::Get()->color_mode() ==
+                     AshColorProvider::AshColorMode::kLight
+                 ? AshColorProvider::BaseLayerType::kOpaque
+                 : AshColorProvider::BaseLayerType::kTransparent90;
+    }
+    return AshColorProvider::BaseLayerType::kTransparent60;
+  }
+  return AshColorProvider::BaseLayerType::kTransparent80;
+}
+
 SkColor ShelfConfig::GetDefaultShelfColor() const {
   if (!features::IsBackgroundBlurEnabled()) {
     return GetThemedColorFromWallpaper(
@@ -494,17 +512,7 @@
             AshColorProvider::AshColorMode::kDark));
   }
 
-  AshColorProvider::BaseLayerType layer_type;
-  if (!chromeos::switches::ShouldShowShelfHotseat()) {
-    layer_type = in_tablet_mode_
-                     ? AshColorProvider::BaseLayerType::kTransparent60
-                     : AshColorProvider::BaseLayerType::kTransparent80;
-  } else if (in_tablet_mode_) {
-    layer_type = is_in_app() ? AshColorProvider::BaseLayerType::kTransparent90
-                             : AshColorProvider::BaseLayerType::kTransparent60;
-  } else {
-    layer_type = AshColorProvider::BaseLayerType::kTransparent80;
-  }
+  AshColorProvider::BaseLayerType layer_type = GetShelfBaseLayerType();
 
   SkColor final_color = AshColorProvider::Get()->GetBaseLayerColor(
       layer_type, AshColorProvider::AshColorMode::kDark);
diff --git a/ash/wm/desks/desks_controller.cc b/ash/wm/desks/desks_controller.cc
index 76e33fbe..1fb2f151 100644
--- a/ash/wm/desks/desks_controller.cc
+++ b/ash/wm/desks/desks_controller.cc
@@ -288,8 +288,7 @@
     Shell::Get()
         ->accessibility_controller()
         ->TriggerAccessibilityAlertWithMessage(l10n_util::GetStringFUTF8(
-            IDS_ASH_VIRTUAL_DESKS_ALERT_DESK_ACTIVATED,
-            base::NumberToString16(target_desk_index + 1)));
+            IDS_ASH_VIRTUAL_DESKS_ALERT_DESK_ACTIVATED, desk->name()));
   }
 
   if (source == DesksSwitchSource::kDeskRemoved ||
@@ -380,9 +379,7 @@
       ->accessibility_controller()
       ->TriggerAccessibilityAlertWithMessage(l10n_util::GetStringFUTF8(
           IDS_ASH_VIRTUAL_DESKS_ALERT_WINDOW_MOVED_FROM_ACTIVE_DESK,
-          window->GetTitle(),
-          base::NumberToString16(GetDeskIndex(active_desk_) + 1),
-          base::NumberToString16(GetDeskIndex(target_desk) + 1)));
+          window->GetTitle(), active_desk_->name(), target_desk->name()));
 
   UMA_HISTOGRAM_ENUMERATION(kMoveWindowFromActiveDeskHistogramName, source);
   ReportNumberOfWindowsPerDeskHistogram();
@@ -660,9 +657,8 @@
   Shell::Get()
       ->accessibility_controller()
       ->TriggerAccessibilityAlertWithMessage(l10n_util::GetStringFUTF8(
-          IDS_ASH_VIRTUAL_DESKS_ALERT_DESK_REMOVED,
-          base::NumberToString16(removed_desk_number),
-          base::NumberToString16(active_desk_number)));
+          IDS_ASH_VIRTUAL_DESKS_ALERT_DESK_REMOVED, removed_desk->name(),
+          active_desk_->name()));
 
   desks_restore_util::UpdatePrimaryUserDesksPrefs();
 
diff --git a/ash/wm/gestures/wm_gesture_handler.cc b/ash/wm/gestures/wm_gesture_handler.cc
index 206eb9e..4dbf7c3 100644
--- a/ash/wm/gestures/wm_gesture_handler.cc
+++ b/ash/wm/gestures/wm_gesture_handler.cc
@@ -4,10 +4,12 @@
 
 #include "ash/wm/gestures/wm_gesture_handler.h"
 
+#include "ash/public/cpp/ash_features.h"
 #include "ash/shell.h"
 #include "ash/wm/desks/desks_controller.h"
 #include "ash/wm/desks/desks_histogram_enums.h"
 #include "ash/wm/overview/overview_controller.h"
+#include "ash/wm/window_cycle_controller.h"
 #include "base/metrics/user_metrics.h"
 #include "ui/events/event.h"
 #include "ui/events/types/event_type.h"
@@ -90,10 +92,14 @@
 
   scroll_data_->scroll_x += event.x_offset();
   scroll_data_->scroll_y += event.y_offset();
-  // If the requirements to move the overview selector are met, reset
-  // |scroll_data_|.
-  const bool moved = MoveOverviewSelection(finger_count, scroll_data_->scroll_x,
-                                           scroll_data_->scroll_y);
+  // If the requirements to move the overview selector or the window cycle list
+  // selector are met, reset |scroll_data_|. If both are open, move the cycle
+  // list's selector.
+  const bool moved =
+      MoveWindowCycleListSelection(finger_count, scroll_data_->scroll_x,
+                                   scroll_data_->scroll_y) ||
+      MoveOverviewSelection(finger_count, scroll_data_->scroll_x,
+                            scroll_data_->scroll_y);
   if (moved)
     scroll_data_ = base::make_optional(ScrollData());
   scroll_data_->finger_count = finger_count;
@@ -130,15 +136,41 @@
 
   auto* overview_controller = Shell::Get()->overview_controller();
   const bool in_overview = overview_controller->InOverviewSession();
+  if (!ShouldHorizontallyScrollSelector(in_overview, scroll_x, scroll_y))
+    return false;
+
+  overview_controller->IncrementSelection(/*forward=*/scroll_x > 0);
+  return true;
+}
+
+bool WmGestureHandler::MoveWindowCycleListSelection(int finger_count,
+                                                    float scroll_x,
+                                                    float scroll_y) {
+  if (!features::IsInteractiveWindowCycleListEnabled() || finger_count != 3)
+    return false;
+
+  auto* window_cycle_controller = Shell::Get()->window_cycle_controller();
+  const bool is_cycling = window_cycle_controller->IsCycling();
+  if (!ShouldHorizontallyScrollSelector(is_cycling, scroll_x, scroll_y))
+    return false;
+
+  window_cycle_controller->HandleCycleWindow(
+      scroll_x > 0 ? WindowCycleController::FORWARD
+                   : WindowCycleController::BACKWARD);
+  return true;
+}
+
+bool WmGestureHandler::ShouldHorizontallyScrollSelector(bool in_session,
+                                                        float scroll_x,
+                                                        float scroll_y) {
   // Dominantly vertical scrolls and small horizontal scrolls do not move the
-  // overview selector.
-  if (!in_overview || std::fabs(scroll_x) < std::fabs(scroll_y))
+  // selector.
+  if (!in_session || std::fabs(scroll_x) < std::fabs(scroll_y))
     return false;
 
   if (std::fabs(scroll_x) < kHorizontalThresholdDp)
     return false;
 
-  overview_controller->IncrementSelection(/*forward=*/scroll_x > 0);
   return true;
 }
 
diff --git a/ash/wm/gestures/wm_gesture_handler.h b/ash/wm/gestures/wm_gesture_handler.h
index 6a11a82f..66c44c6 100644
--- a/ash/wm/gestures/wm_gesture_handler.h
+++ b/ash/wm/gestures/wm_gesture_handler.h
@@ -48,6 +48,18 @@
   // the middle of scrolls and when scrolls have ended.
   bool MoveOverviewSelection(int finger_count, float scroll_x, float scroll_y);
 
+  // Tries to move the window cycle list selector. Returns true if successful.
+  // Called in the middle of scrolls and when scrolls have ended.
+  bool MoveWindowCycleListSelection(int finger_count,
+                                    float scroll_x,
+                                    float scroll_y);
+
+  // Returns whether or not the selector for a given session of overview/window
+  // cycle list should horizontally scroll.
+  bool ShouldHorizontallyScrollSelector(bool in_session,
+                                        float scroll_x,
+                                        float scroll_y);
+
   // Contains the data during a scroll session. Empty is no scroll is underway.
   base::Optional<ScrollData> scroll_data_;
 
diff --git a/ash/wm/gestures/wm_gesture_handler_unittest.cc b/ash/wm/gestures/wm_gesture_handler_unittest.cc
index 477ac9c..4156a0dd 100644
--- a/ash/wm/gestures/wm_gesture_handler_unittest.cc
+++ b/ash/wm/gestures/wm_gesture_handler_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "ash/wm/gestures/wm_gesture_handler.h"
 
+#include "ash/public/cpp/ash_features.h"
 #include "ash/session/session_controller_impl.h"
 #include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
@@ -13,7 +14,10 @@
 #include "ash/wm/desks/desks_test_util.h"
 #include "ash/wm/overview/overview_controller.h"
 #include "ash/wm/overview/overview_test_util.h"
+#include "ash/wm/window_cycle_controller.h"
+#include "ash/wm/window_cycle_list.h"
 #include "ash/wm/window_util.h"
+#include "base/test/scoped_feature_list.h"
 #include "ui/aura/window.h"
 #include "ui/events/test/event_generator.h"
 #include "ui/views/widget/widget.h"
@@ -250,4 +254,76 @@
   EXPECT_EQ(desk_controller->desks()[0].get(), desk_controller->active_desk());
 }
 
+class InteractiveWindowCycleListGestureHandlerTest
+    : public WmGestureHandlerTest {
+ public:
+  InteractiveWindowCycleListGestureHandlerTest() = default;
+  InteractiveWindowCycleListGestureHandlerTest(
+      const InteractiveWindowCycleListGestureHandlerTest&) = delete;
+  InteractiveWindowCycleListGestureHandlerTest& operator=(
+      const InteractiveWindowCycleListGestureHandlerTest&) = delete;
+  ~InteractiveWindowCycleListGestureHandlerTest() override = default;
+
+  // AshTestBase:
+  void SetUp() override {
+    scoped_feature_list_.InitAndEnableFeature(
+        features::kInteractiveWindowCycleList);
+    AshTestBase::SetUp();
+    WindowCycleList::DisableInitialDelayForTesting();
+  }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+// Tests three finger horizontal scroll gesture to move selection left or right.
+TEST_F(InteractiveWindowCycleListGestureHandlerTest,
+       HorizontalScrollInWindowCycleList) {
+  const gfx::Rect bounds(0, 0, 400, 400);
+  std::unique_ptr<aura::Window> window1 = CreateTestWindow(bounds);
+  std::unique_ptr<aura::Window> window2 = CreateTestWindow(bounds);
+  std::unique_ptr<aura::Window> window3 = CreateTestWindow(bounds);
+  std::unique_ptr<aura::Window> window4 = CreateTestWindow(bounds);
+  std::unique_ptr<aura::Window> window5 = CreateTestWindow(bounds);
+  const float horizontal_scroll = WmGestureHandler::kHorizontalThresholdDp;
+
+  auto scroll_until_window_highlighted_and_confirm = [this](float x_offset,
+                                                            float y_offset) {
+    WindowCycleController* controller = Shell::Get()->window_cycle_controller();
+    controller->StartCycling();
+    Scroll(x_offset, y_offset, kNumFingersForHighlight);
+    controller->CompleteCycling();
+  };
+
+  // Start cycle, simulating alt key being held down. Scroll right to fourth
+  // item.
+  // Current order is [5,4,3,2,1].
+  scroll_until_window_highlighted_and_confirm(horizontal_scroll * 3, 0);
+  EXPECT_TRUE(wm::IsActiveWindow(window2.get()));
+
+  // Start cycle. Scroll left to third item.
+  // Current order is [2,5,4,3,1].
+  scroll_until_window_highlighted_and_confirm(-horizontal_scroll * 3, 0);
+  EXPECT_TRUE(wm::IsActiveWindow(window4.get()));
+
+  // Start cycle. Scroll right to second item.
+  // Current order is [4,2,5,3,1].
+  scroll_until_window_highlighted_and_confirm(horizontal_scroll, 0);
+  EXPECT_TRUE(wm::IsActiveWindow(window2.get()));
+
+  // Open an overview session and window cycle list. Scroll right to second
+  // item. Scroll should only go to the window cycle list.
+  // Current order is [2,4,5,3,1].
+  Shell::Get()->overview_controller()->StartOverview();
+  EXPECT_TRUE(InOverviewSession());
+
+  Shell::Get()->window_cycle_controller()->StartCycling();
+  Scroll(horizontal_scroll, 0, kNumFingersForHighlight);
+  EXPECT_EQ(nullptr, GetHighlightedWindow());
+
+  Shell::Get()->window_cycle_controller()->CompleteCycling();
+  EXPECT_FALSE(InOverviewSession());
+  EXPECT_TRUE(wm::IsActiveWindow(window4.get()));
+}
+
 }  // namespace ash
diff --git a/ash/wm/overview/overview_item.cc b/ash/wm/overview/overview_item.cc
index f16cb13..d4ed5f9e 100644
--- a/ash/wm/overview/overview_item.cc
+++ b/ash/wm/overview/overview_item.cc
@@ -8,6 +8,7 @@
 #include <utility>
 #include <vector>
 
+#include "ash/accessibility/accessibility_controller_impl.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/public/cpp/window_properties.h"
 #include "ash/public/cpp/window_state_type.h"
@@ -1051,6 +1052,11 @@
 
   overview_grid_->RemoveItem(this, /*item_destroying=*/true,
                              /*reposition=*/!animating_to_close_);
+
+  Shell::Get()
+      ->accessibility_controller()
+      ->TriggerAccessibilityAlertWithMessage(l10n_util::GetStringFUTF8(
+          IDS_ASH_OVERVIEW_WINDOW_CLOSING_A11Y_ALERT, window->GetTitle()));
 }
 
 void OverviewItem::OnPreWindowStateTypeChange(WindowState* window_state,
diff --git a/ash/wm/overview/overview_session.cc b/ash/wm/overview/overview_session.cc
index e530165..4474020 100644
--- a/ash/wm/overview/overview_session.cc
+++ b/ash/wm/overview/overview_session.cc
@@ -950,11 +950,6 @@
 }
 
 void OverviewSession::OnWindowDestroying(aura::Window* window) {
-  Shell::Get()
-      ->accessibility_controller()
-      ->TriggerAccessibilityAlertWithMessage(l10n_util::GetStringFUTF8(
-          IDS_ASH_OVERVIEW_WINDOW_CLOSING_A11Y_ALERT, window->GetTitle()));
-
   window->RemoveObserver(this);
   observed_windows_.erase(window);
   if (window == restore_focus_window_)
diff --git a/ash/wm/window_cycle_list.h b/ash/wm/window_cycle_list.h
index effc6ca..dc996bc 100644
--- a/ash/wm/window_cycle_list.h
+++ b/ash/wm/window_cycle_list.h
@@ -57,6 +57,7 @@
 
  private:
   friend class WindowCycleControllerTest;
+  friend class InteractiveWindowCycleListGestureHandlerTest;
 
   static void DisableInitialDelayForTesting();
 
diff --git a/base/allocator/allocator_shim_unittest.cc b/base/allocator/allocator_shim_unittest.cc
index 1ebdda1..fbb49aa8 100644
--- a/base/allocator/allocator_shim_unittest.cc
+++ b/base/allocator/allocator_shim_unittest.cc
@@ -38,15 +38,6 @@
 #include <unistd.h>
 #endif
 
-// Some new Android NDKs (64 bit) does not expose (p)valloc anymore. These
-// functions are implemented at the shim-layer level.
-#if defined(OS_ANDROID)
-extern "C" {
-void* valloc(size_t size);
-void* pvalloc(size_t size);
-}
-#endif
-
 namespace base {
 namespace allocator {
 namespace {
@@ -340,7 +331,6 @@
   ASSERT_GE(zero_allocs_intercepted_by_size[2 * 23], 1u);
 
 #if !defined(OS_WIN)
-  const size_t kPageSize = base::GetPageSize();
   void* posix_memalign_ptr = nullptr;
   int res = posix_memalign(&posix_memalign_ptr, 256, 59);
   ASSERT_EQ(0, res);
@@ -349,11 +339,17 @@
   ASSERT_GE(aligned_allocs_intercepted_by_alignment[256], 1u);
   ASSERT_GE(aligned_allocs_intercepted_by_size[59], 1u);
 
+  // (p)valloc() are not defined on Android. pvalloc() is a GNU extension,
+  // valloc() is not in POSIX.
+#if !defined(OS_ANDROID)
+  const size_t kPageSize = base::GetPageSize();
   void* valloc_ptr = valloc(61);
   ASSERT_NE(nullptr, valloc_ptr);
   ASSERT_EQ(0u, reinterpret_cast<uintptr_t>(valloc_ptr) % kPageSize);
   ASSERT_GE(aligned_allocs_intercepted_by_alignment[kPageSize], 1u);
   ASSERT_GE(aligned_allocs_intercepted_by_size[61], 1u);
+#endif  // !defined(OS_ANDROID)
+
 #endif  // !OS_WIN
 
 #if !defined(OS_WIN) && !defined(OS_APPLE)
@@ -363,12 +359,15 @@
   ASSERT_GE(aligned_allocs_intercepted_by_alignment[128], 1u);
   ASSERT_GE(aligned_allocs_intercepted_by_size[53], 1u);
 
+#if !defined(OS_ANDROID)
   void* pvalloc_ptr = pvalloc(67);
   ASSERT_NE(nullptr, pvalloc_ptr);
   ASSERT_EQ(0u, reinterpret_cast<uintptr_t>(pvalloc_ptr) % kPageSize);
   ASSERT_GE(aligned_allocs_intercepted_by_alignment[kPageSize], 1u);
   // pvalloc rounds the size up to the next page.
   ASSERT_GE(aligned_allocs_intercepted_by_size[kPageSize], 1u);
+#endif  // !defined(OS_ANDROID)
+
 #endif  // !OS_WIN && !OS_APPLE
 
   char* realloc_ptr = static_cast<char*>(malloc(10));
@@ -389,16 +388,22 @@
   free(memalign_ptr);
   ASSERT_GE(frees_intercepted_by_addr[Hash(memalign_ptr)], 1u);
 
+#if !defined(OS_ANDROID)
   free(pvalloc_ptr);
   ASSERT_GE(frees_intercepted_by_addr[Hash(pvalloc_ptr)], 1u);
+#endif  // !defined(OS_ANDROID)
+
 #endif  // !OS_WIN && !OS_APPLE
 
 #if !defined(OS_WIN)
   free(posix_memalign_ptr);
   ASSERT_GE(frees_intercepted_by_addr[Hash(posix_memalign_ptr)], 1u);
 
+#if !defined(OS_ANDROID)
   free(valloc_ptr);
   ASSERT_GE(frees_intercepted_by_addr[Hash(valloc_ptr)], 1u);
+#endif  // !defined(OS_ANDROID)
+
 #endif  // !OS_WIN
 
   free(realloc_ptr);
diff --git a/base/allocator/partition_allocator/partition_alloc_features.h b/base/allocator/partition_allocator/partition_alloc_features.h
index 42a31b6..62acad5 100644
--- a/base/allocator/partition_allocator/partition_alloc_features.h
+++ b/base/allocator/partition_allocator/partition_alloc_features.h
@@ -33,22 +33,22 @@
   // case, we enable it for all builds then.
 #if !(defined(ARCH_CPU_64_BITS) && !defined(OS_NACL))
   return false;
-#elif BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
-  // We have to check if windows version is greater than or equal to 8.1.
-  // If IsWindows8Point1OrGreater() doesn't allocate any memory,
-  // we will use the helper function here. See https://crbug.com/1101421.
-  return true;
-#else  // defined(ARCH_CPU_64_BITS) && !defined(OS_NACL)
+#else
 #if defined(OS_WIN)
   // Lots of crashes (at PartitionAddressSpace::Init) occur
   // when enabling GigaCage on Windows whose version is smaller than 8.1,
   // because PTEs for reserved memory counts against commit limit. See
   // https://crbug.com/1101421.
-  if (!IsWindows8Point1OrGreater())
+  static bool recent_enough_windows_version = IsWindows8Point1OrGreater();
+  if (!recent_enough_windows_version)
     return false;
-#endif
+#endif  // defined(OS_WIN)
+#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
+  return true;
+#else
   return FeatureList::IsEnabled(kPartitionAllocGigaCage);
-#endif
+#endif  // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
+#endif  // !(defined(ARCH_CPU_64_BITS) && !defined(OS_NACL))
 }
 
 }  // namespace base
diff --git a/build/config/android/rules.gni b/build/config/android/rules.gni
index b61dce0..232fd4e1 100644
--- a/build/config/android/rules.gni
+++ b/build/config/android/rules.gni
@@ -4197,10 +4197,8 @@
         rebase_path(invoker.aar_path, root_build_dir),
         "--output-dir",
         rebase_path(_output_path, root_build_dir),
-
-        # TODO(dewittj): Re-enable after internal roll updating the info files.
-        #"--assert-info-file",
-        #rebase_path(_info_path, root_build_dir),
+        "--assert-info-file",
+        rebase_path(_info_path, root_build_dir),
       ]
       if (_strip_resources) {
         args += [ "--ignore-resources" ]
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index f375043..00b0a6f 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-0.20200827.3.2
+0.20200828.0.1
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index f375043..00b0a6f 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-0.20200827.3.2
+0.20200828.0.1
diff --git a/chrome/VERSION b/chrome/VERSION
index a6e9e1a..4b4d9319 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=87
 MINOR=0
-BUILD=4247
+BUILD=4248
 PATCH=0
diff --git a/chrome/android/DEPS b/chrome/android/DEPS
index eb220a1..f2da4ed5 100644
--- a/chrome/android/DEPS
+++ b/chrome/android/DEPS
@@ -1,4 +1,10 @@
 include_rules = [
+  # Restricting uses of ChromeActivity in favor of direct dependency aquisition. Code that lives
+  # under java/src/.../chrome/browser has a seperate exception under that same directory.
+  # Exceptions can be found under "specific_include_rules" below. Valid ChromeActivity dependents
+  # should add an allow rule to a DEPS file in the relevant directory.
+  "-chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+
   "+chrome/browser/browser_controls/android",
   "+chrome/browser/enterprise",
   "+chrome/browser/feedback/android",
@@ -65,4 +71,105 @@
   "chrome/android/java/src/org/chromium/chrome/browser/AppHooks.java": [
     "+chrome/browser/xsurface/android",
   ],
-}
+
+  # Tests and test-oriented classes are allowed to rely on ChromeActivity for DEPS. When committing
+  # a file that doesn't conform to these patterns, add an allow rule to the DEPS file to the 
+  # testing directory.
+  ".*Test\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  ".*TestRule\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+
+  # Exceptions to the ChromeActivity dependency restriction. These will all eventually be removed
+  # new code should rely on acceptable dependency aquisition patterns. These are sorted by package,
+  # but the package to work with the filename matching done by checkdeps.
+  "AssistantCoordinator\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "AssistantRootViewContainer\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "AutofillAssistantUiController\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "FeedbackContext\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "AutofillAssistantFacade\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "AutofillKeyboardAccessoryViewBridge\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "ManualFillingComponentBridge\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "ManualFillingMediator\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "ExploreSurfaceCoordinator\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "ExploreSurfaceCoordinator\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "StartSurfaceCoordinator\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "StartSurfaceDelegate\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "MostVisitedListCoordinator\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "SingleTabSwitcherCoordinator\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "TasksSurfaceCoordinator\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "TabGroupPopupUi\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "TabGroupPopupUiCoordinator\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "TabGroupUi\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "TabGroupUiCoordinator\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "TabListCoordinator\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "TabManagementDelegate\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "TabManagementDelegateImpl\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "VrDelegate\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "VrDelegateFallback\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "VrDelegateImpl\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "VrShell\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "VrShellDelegate\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "VrWindowAndroid\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "FeedSurfaceCoordinator\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+}
\ No newline at end of file
diff --git a/chrome/android/features/autofill_assistant/javatests/DEPS b/chrome/android/features/autofill_assistant/javatests/DEPS
index 0d2fad1..1a82a7c6 100644
--- a/chrome/android/features/autofill_assistant/javatests/DEPS
+++ b/chrome/android/features/autofill_assistant/javatests/DEPS
@@ -1,5 +1,6 @@
 include_rules = [
   "+components/browser_ui/widget/android",
+  "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
   "+chrome/browser/password_manager/android_test_helpers",
   "-content/public/android",
   "+content/public/android/java/src/org/chromium/content_public/browser",
diff --git a/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/DEPS b/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/DEPS
index 73eba5c..6857a2b 100644
--- a/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/DEPS
+++ b/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/DEPS
@@ -1,4 +1,5 @@
 include_rules = [
+  "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
   # To observer web contents swaps on the tab.
   "+chrome/browser/tab/java/src/org/chromium/chrome/browser/tab",
 ]
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/DEPS b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/DEPS
index 6e28af9..a059eee 100644
--- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/DEPS
+++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/DEPS
@@ -1,4 +1,5 @@
 include_rules = [
+  "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
   "+components/browser_ui/styles/android",
   "+content/public/android/java/src/org/chromium/content_public/browser"
 ]
diff --git a/chrome/android/java/res/layout/share_sheet_item.xml b/chrome/android/java/res/layout/share_sheet_item.xml
index 5ac725af..b35bf33 100644
--- a/chrome/android/java/res/layout/share_sheet_item.xml
+++ b/chrome/android/java/res/layout/share_sheet_item.xml
@@ -10,7 +10,7 @@
     android:clickable="true"
     android:textAppearance="@style/TextAppearance.TextSmall.Primary"
     android:layout_height="fill_parent"
-    android:layout_width="62sp"
+    android:layout_width="68sp"
     android:layout_marginStart="16dp"
     android:layout_marginTop="16dp"
     android:background="?attr/selectableItemBackground"
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/DEPS b/chrome/android/java/src/org/chromium/chrome/browser/DEPS
new file mode 100644
index 0000000..4fa44991
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/DEPS
@@ -0,0 +1,442 @@
+include_rules = [
+  # Restricting uses of ChromeActivity in favor of direct dependency aquisition. Code that doesn't
+  # live under chrome/android/java/src/.../chrome/browser has a seperate exception in
+  # chrome/android/DEPS. Exceptions can be found under "specific_include_rules" below. See
+  # chrome/android/DEPS for more information.
+  "-chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+]
+
+specific_include_rules = {
+  # Exceptions to the ChromeActivity dependency restriction. These will all eventually be removed
+  # new code should rely on acceptable dependency aquisition patterns. These are sorted by package,
+  # but the package to work with the filename matching done by checkdeps.
+  "ChromeAccessorActivity\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "ChromeActivitySessionTracker\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "ChromeKeyboardVisibilityDelegate\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "ChromeTabbedActivity\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "ChromeWindow\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "KeyboardShortcuts\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "LaunchIntentDispatcher\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "NavigationPopup\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "ServiceTabLauncher\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "TabbedModeTabDelegateFactory\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "WarmupManager\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "ActivityTabWebContentsDelegateAndroid\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "AutofillExpirationDateFixFlowBridge\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "AutofillExpirationDateFixFlowPrompt\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "AutofillNameFixFlowBridge\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "AutofillNameFixFlowPrompt\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "AutofillPopupBridge\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "CardUnmaskBridge\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "CardUnmaskPrompt\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "BookmarkPage\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "BookmarkUtils\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "QualityEnforcer\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "DigitalGoodsFactory\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "TwaFinishHandler\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "ClientPackageNameProvider\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "SplashController\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "WebappSplashController\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "OverlayPanel\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "OverlayPanelBase\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "OverlayPanelContent\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "BarOverlapTapSuppression\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "ContextualSearchManagementDelegate\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "ContextualSearchManager\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "ContextualSearchSelectionController\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "ContextualSearchTabHelper\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "BaseCustomTabActivity\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "BaseCustomTabRootUiCoordinator\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "CustomTabActivityLifecycleUmaTracker\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "CustomTabBottomBarDelegate\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "CustomTabCompositorContentInitializer\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "CustomTabDelegateFactory\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "CustomTabIncognitoManager\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "CustomTabIntentDataProvider\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "CustomTabTabPersistencePolicy\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "CustomTabTaskDescriptionHelper\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "CustomTabTopBarDelegate\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "CustomTabActivityNavigationController\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "CustomTabActivityTabController\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "CustomTabActivityTabFactory\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "CustomTabToolbarColorController\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "CustomTabToolbarCoordinator\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "ChromeActivityCommonsModule\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "DisplayCutoutTabHelper\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "ReaderModeManager\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "DownloadController\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "DownloadInfoBarController\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "DownloadPage\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "ExploreSitesIPH\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "ExploreSitesPage\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "ExternalNavigationDelegateImpl\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "ScreenshotTask\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "ForcedSigninProcessor\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "BrowserControlsManager\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "ContextReporter\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "GSAHelper\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "HistoryManager\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "HistoryManagerUtils\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "HistoryPage\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "InfoBarContainer\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "TranslateCompactInfoBar\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "ProcessInitializationHandler\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "LanguageAskPrompt\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "PictureInPictureController\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "ChromeTabModalPresenter\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "TabModalLifetimeHandler\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "NativePageFactory\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "RecentTabsPage\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "OfflinePageBridge\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "OfflinePageTabObserver\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "OfflinePageUtils\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "OfflineIndicatorController\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "TopSnackbarManager\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "TopSnackbarView\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "UpdateInfoBarController\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "UpdateStatusProvider\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "FakeAppUpdateManagerWrapper\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "UpdateNotificationControllerFactory\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "UpdateNotificationControllerImpl\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "UpdateNotificationServiceBridge\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "AutocompleteMediator\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "PaintPreviewHelper\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "AutoSigninSnackbarController\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "CredentialLeakDialogBridge\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "OnboardingDialogBridge\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "PasswordChangeLauncher\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "PasswordCheckupLauncher\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "PasswordGenerationDialogBridge\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "PasswordGenerationDialogCoordinator\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "AndroidPaymentApp\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "AndroidPaymentAppFinder\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "AutofillPaymentInstrument\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "CardEditor\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "PaymentRequestFactory\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "PaymentRequestImpl\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "TwaPackageManagerDelegate\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "PaymentHandlerCoordinator\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "PaymentHandlerMediator\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "PaymentHandlerToolbarCoordinator\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "PaymentUIsManager\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "PrintShareActivity\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "SafeBrowsingPasswordReuseDialogBridge\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "EditorScreenshotTask\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "SendTabToSelfInfoBarController\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "SendTabToSelfShareActivity\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "SigninUtils\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "AccountPickerDelegate\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "InterceptNavigationDelegateClientImpl\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "TabImpl\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "TrustedCdn\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "TabbedRootUiCoordinator\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "ChromeTabCreator\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "TabDelegate\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "ReturnToChromeExperimentsUtil\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "ToolbarButtonInProductHelpController\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "ToolbarManager\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "BottomControlsCoordinator\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "CustomTabToolbar\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "ViewShiftingActionBarDelegate\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "RootUiCoordinator\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "StatusBarColorController\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "SuspendedTab\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "ArCoreJavaUtils\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "ArImmersiveOverlay\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "WebApkActivityCoordinator\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "WebApkActivityLifecycleUmaTracker\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "WebApkServiceClient\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "WebApkUpdateManager\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "WebappActivityCoordinator\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "WebappDeferredStartupWithStorageHandler\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "WebappDisclosureSnackbarController\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "AddToHomescreenCoordinator\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "Fido2CredentialRequest\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+  "ShareServiceImplementationFactory\.java": [
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+  ],
+}
\ No newline at end of file
diff --git a/chrome/android/javatests/DEPS b/chrome/android/javatests/DEPS
index 1ba5a4e..c02d868 100644
--- a/chrome/android/javatests/DEPS
+++ b/chrome/android/javatests/DEPS
@@ -1,5 +1,6 @@
 include_rules = [
   "+chrome/app",
+  "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
   "+chrome/browser/android/lifecycle",
   "+chrome/browser/image_fetcher",
   "+chrome/browser/password_check/android",
diff --git a/chrome/android/junit/DEPS b/chrome/android/junit/DEPS
index b0d0bf68..03b76af 100644
--- a/chrome/android/junit/DEPS
+++ b/chrome/android/junit/DEPS
@@ -1,4 +1,5 @@
 include_rules = [
+  "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
   "!clank/java/src/org/chromium/chrome/browser/AppHooksImpl.java",
   "+chrome/browser/android/lifecycle",
   "+chrome/browser/image_fetcher",
diff --git a/chrome/browser/DEPS b/chrome/browser/DEPS
index 25ab377..6c6d580 100644
--- a/chrome/browser/DEPS
+++ b/chrome/browser/DEPS
@@ -2,6 +2,12 @@
   "+apps",
   "+cc/paint",
   "+cc/test",
+
+  # Restricting uses of ChromeActivity in favor of direct dependency aquisition. Code that lives
+  # under java/src/.../chrome/browser has a seperate exception under that same directory.
+  # Exceptions can be found under "specific_include_rules" below.
+  "-chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+
   "+chrome/android/native_j_unittests_jni_headers",
   "+chrome/android/chrome_jni_headers",
   "+chrome/android/features/autofill_assistant/jni_headers",
@@ -457,6 +463,7 @@
   '.*Test\.java': [
     # Android instrumentation tests depend on the whole app.
     "+chrome/android",
+    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
   ],
   "platform_util_linux.cc": [
     # The following is used to call the org.freedesktop.FileManager1
diff --git a/chrome/browser/android/autofill_assistant/client_android.cc b/chrome/browser/android/autofill_assistant/client_android.cc
index 8c5bfda..e866bdb3 100644
--- a/chrome/browser/android/autofill_assistant/client_android.cc
+++ b/chrome/browser/android/autofill_assistant/client_android.cc
@@ -587,12 +587,16 @@
 }
 
 void ClientAndroid::CreateController(std::unique_ptr<Service> service) {
+  // Persist status message when hot-swapping controllers.
+  std::string status_message;
   if (controller_) {
+    status_message = controller_->GetStatusMessage();
     DestroyController();
   }
   controller_ = std::make_unique<Controller>(
       web_contents_, /* client= */ this, base::DefaultTickClock::GetInstance(),
       std::move(service));
+  controller_->SetStatusMessage(status_message);
 }
 
 void ClientAndroid::DestroyController() {
diff --git a/chrome/browser/android/vr/arcore_device/arcore_gl.cc b/chrome/browser/android/vr/arcore_device/arcore_gl.cc
index 2b9362e..233d124c 100644
--- a/chrome/browser/android/vr/arcore_device/arcore_gl.cc
+++ b/chrome/browser/android/vr/arcore_device/arcore_gl.cc
@@ -24,6 +24,7 @@
 #include "chrome/browser/android/vr/arcore_device/arcore_session_utils.h"
 #include "chrome/browser/android/vr/web_xr_presentation_state.h"
 #include "device/vr/android/arcore/arcore.h"
+#include "device/vr/android/arcore/arcore_math_utils.h"
 #include "device/vr/android/arcore/type_converters.h"
 #include "device/vr/public/mojom/pose.h"
 #include "device/vr/public/mojom/vr_service.mojom.h"
@@ -42,11 +43,6 @@
 #include "ui/gl/init/gl_factory.h"
 
 namespace {
-// Input display coordinates (range 0..1) used with ArCore's
-// transformDisplayUvCoords to calculate the output matrix.
-constexpr std::array<float, 6> kDisplayCoordinatesForTransform = {
-    0.f, 0.f, 1.f, 0.f, 0.f, 1.f};
-
 // When scheduling the next ARCore update task, aim to have that run this much
 // time ahead of when the next camera image is expected to be ready. In case
 // the overall system is running slower than ideal, i.e. if the device switches
@@ -63,40 +59,6 @@
 
 const char kInputSourceProfileName[] = "generic-touchscreen";
 
-gfx::Transform ConvertUvsToTransformMatrix(const std::vector<float>& uvs) {
-  // We're creating a matrix that transforms viewport UV coordinates (for a
-  // screen-filling quad, origin at bottom left, u=1 at right, v=1 at top) to
-  // camera texture UV coordinates. This matrix is used to compute texture
-  // coordinates for copying an appropriately cropped and rotated subsection of
-  // the camera image. The SampleData is a bit unfortunate. ArCore doesn't
-  // provide a way to get a matrix directly. There's a function to transform UV
-  // vectors individually, which obviously can't be used from a shader, so we
-  // run that on selected vectors and recreate the matrix from the result.
-
-  // Assumes that |uvs| is the result of transforming the display coordinates
-  // from kDisplayCoordinatesForTransform. This combines the solved matrix with
-  // a Y flip because ArCore's "normalized screen space" coordinates have the
-  // origin at the top left to match 2D Android APIs, so it needs a Y flip to
-  // get an origin at bottom left as used for textures.
-  DCHECK_EQ(uvs.size(), 6U);
-  float u00 = uvs[0];
-  float v00 = uvs[1];
-  float u10 = uvs[2];
-  float v10 = uvs[3];
-  float u01 = uvs[4];
-  float v01 = uvs[5];
-
-  // Transform initializes to the identity matrix and then is modified by uvs.
-  gfx::Transform result;
-  result.matrix().set(0, 0, u10 - u00);
-  result.matrix().set(0, 1, -(u01 - u00));
-  result.matrix().set(0, 3, u01);
-  result.matrix().set(1, 0, v10 - v00);
-  result.matrix().set(1, 1, -(v01 - v00));
-  result.matrix().set(1, 3, v01);
-  return result;
-}
-
 const gfx::Size kDefaultFrameSize = {1, 1};
 const display::Display::Rotation kDefaultRotation = display::Display::ROTATE_0;
 
@@ -328,9 +290,7 @@
   // resize.
   if (should_recalculate_uvs_) {
     // Get the UV transform matrix from ArCore's UV transform.
-    std::vector<float> uvs_transformed =
-        arcore_->TransformDisplayUvCoords(kDisplayCoordinatesForTransform);
-    uv_transform_ = ConvertUvsToTransformMatrix(uvs_transformed);
+    uv_transform_ = arcore_->GetCameraUvFromScreenUvTransform();
 
     // We need near/far distances to make a projection matrix. The actual
     // values don't matter, the Renderer will recalculate dependent values
diff --git a/chrome/browser/android/vr/arcore_device/fake_arcore.cc b/chrome/browser/android/vr/arcore_device/fake_arcore.cc
index ffcc943..fd426e8a 100644
--- a/chrome/browser/android/vr/arcore_device/fake_arcore.cc
+++ b/chrome/browser/android/vr/arcore_device/fake_arcore.cc
@@ -46,7 +46,7 @@
 }
 
 std::vector<float> FakeArCore::TransformDisplayUvCoords(
-    const base::span<const float> uvs) {
+    const base::span<const float> uvs) const {
   // Try to match ArCore's transfore values.
   //
   // Sample ArCore input: width=1080, height=1795, rotation=0,
@@ -77,6 +77,8 @@
   DCHECK(frame_size_.width());
   DCHECK(frame_size_.height());
 
+  DCHECK_GE(uvs.size(), 6u);
+
   // Do clipping calculations in orientation ROTATE_0. screen U is left=0,
   // right=1. Screen V is bottom=0, top=1. We'll apply screen rotation later.
 
@@ -147,6 +149,7 @@
     uvs_out.push_back(v);
     DVLOG(2) << __FUNCTION__ << ": uv[" << i << "]=(" << u << ", " << v << ")";
   }
+
   return uvs_out;
 }
 
diff --git a/chrome/browser/android/vr/arcore_device/fake_arcore.h b/chrome/browser/android/vr/arcore_device/fake_arcore.h
index b6168c9..c24eece 100644
--- a/chrome/browser/android/vr/arcore_device/fake_arcore.h
+++ b/chrome/browser/android/vr/arcore_device/fake_arcore.h
@@ -27,8 +27,7 @@
   void SetCameraTexture(uint32_t texture) override;
   void SetDisplayGeometry(const gfx::Size& frame_size,
                           display::Display::Rotation display_rotation) override;
-  std::vector<float> TransformDisplayUvCoords(
-      const base::span<const float> uvs) override;
+
   gfx::Transform GetProjectionMatrix(float near, float far) override;
   mojom::VRPosePtr Update(bool* camera_updated) override;
   base::TimeDelta GetFrameTimestamp() override;
@@ -79,6 +78,10 @@
 
   void SetCameraAspect(float aspect) { camera_aspect_ = aspect; }
 
+ protected:
+  std::vector<float> TransformDisplayUvCoords(
+      const base::span<const float> uvs) const override;
+
  private:
   bool IsOnGlThread() const;
 
diff --git a/chrome/browser/chrome_browser_interface_binders.cc b/chrome/browser/chrome_browser_interface_binders.cc
index 70d67f8c..5eaf3a3 100644
--- a/chrome/browser/chrome_browser_interface_binders.cc
+++ b/chrome/browser/chrome_browser_interface_binders.cc
@@ -139,6 +139,11 @@
 #include "chrome/browser/ui/webui/discards/site_data.mojom.h"
 #endif
 
+#if !defined(OS_CHROMEOS) && !defined(OS_ANDROID)
+#include "chrome/browser/ui/webui/signin/profile_picker_ui.h"
+#include "ui/webui/resources/cr_components/customize_themes/customize_themes.mojom.h"
+#endif  // !defined(OS_ANDROID) && !defined(OS_CHROMEOS)
+
 #if defined(OS_CHROMEOS)
 #include "chrome/browser/ui/webui/app_management/app_management.mojom.h"
 #include "chrome/browser/ui/webui/chromeos/add_supervision/add_supervision.mojom.h"
@@ -671,6 +676,14 @@
         nearby_share::NearbyShareDialogUI>(map);
   }
 #endif  // defined(OS_CHROMEOS)
+
+#if !defined(OS_ANDROID) && !defined(OS_CHROMEOS)
+  if (base::FeatureList::IsEnabled(features::kNewProfilePicker)) {
+    RegisterWebUIControllerInterfaceBinder<
+        customize_themes::mojom::CustomizeThemesHandlerFactory,
+        ProfilePickerUI>(map);
+  }
+#endif  // !defined(OS_ANDROID) && !defined(OS_CHROMEOS)
 }
 
 }  // namespace internal
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index 962ba45..ba38632 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -2715,6 +2715,8 @@
     "extensions/file_manager/private_api_drive.h",
     "extensions/file_manager/private_api_file_system.cc",
     "extensions/file_manager/private_api_file_system.h",
+    "extensions/file_manager/private_api_holding_space.cc",
+    "extensions/file_manager/private_api_holding_space.h",
     "extensions/file_manager/private_api_media_parser.cc",
     "extensions/file_manager/private_api_media_parser.h",
     "extensions/file_manager/private_api_media_parser_util.cc",
diff --git a/chrome/browser/chromeos/crosapi/browser_manager.cc b/chrome/browser/chromeos/crosapi/browser_manager.cc
index 711aed4..fcb92e72 100644
--- a/chrome/browser/chromeos/crosapi/browser_manager.cc
+++ b/chrome/browser/chromeos/crosapi/browser_manager.cc
@@ -15,6 +15,7 @@
 #include "base/command_line.h"
 #include "base/environment.h"
 #include "base/files/file_path.h"
+#include "base/files/file_util.h"
 #include "base/logging.h"
 #include "base/metrics/user_metrics.h"
 #include "base/metrics/user_metrics_action.h"
@@ -66,10 +67,21 @@
       PLOG(ERROR) << "Failed to unlink the log file " << log_path;
       return base::ScopedFD();
     }
+
+    // If log file does not exist, most likely the user directory does not exist
+    // either. So create it here.
+    base::File::Error error;
+    if (!base::CreateDirectoryAndGetError(browser_util::GetUserDataDir(),
+                                          &error)) {
+      LOG(ERROR) << "Failed to make directory "
+                 << browser_util::GetUserDataDir()
+                 << base::File::ErrorToString(error);
+      return base::ScopedFD();
+    }
   }
 
   int fd =
-      HANDLE_EINTR(open(log_path.c_str(), O_WRONLY | O_CREAT | O_EXCL, 644));
+      HANDLE_EINTR(open(log_path.c_str(), O_WRONLY | O_CREAT | O_EXCL, 0644));
 
   if (fd < 0) {
     PLOG(ERROR) << "Failed to get file descriptor for " << log_path;
@@ -281,6 +293,7 @@
           invitation.AttachMessagePipe(0), /*version=*/0));
   lacros_chrome_service_.set_disconnect_handler(base::BindOnce(
       &BrowserManager::OnMojoDisconnected, weak_factory_.GetWeakPtr()));
+  lacros_chrome_service_->Init(crosapi::mojom::LacrosInitParams::New());
   lacros_chrome_service_->RequestAshChromeServiceReceiver(
       base::BindOnce(&BrowserManager::OnAshChromeServiceReceiverReceived,
                      weak_factory_.GetWeakPtr()));
diff --git a/chrome/browser/chromeos/crostini/crostini_installer.h b/chrome/browser/chromeos/crostini/crostini_installer.h
index e9c12686..ce26ed5 100644
--- a/chrome/browser/chromeos/crostini/crostini_installer.h
+++ b/chrome/browser/chromeos/crostini/crostini_installer.h
@@ -29,7 +29,9 @@
  public:
   // These values are persisted to logs. Entries should not be renumbered and
   // numeric values should never be reused.
-  // When you add entries to this enum don't forget to update enums.xml
+  // When you add entries to this enum don't forget to update enums.xml and the
+  // plx scripts in
+  // https://plx.corp.google.com/home2/home/collections/c16e3c1474497b821
   enum class SetupResult {
     kNotStarted = 0,
     // kUserCancelled = 1,
@@ -64,6 +66,8 @@
     kErrorUnknown = 26,
 
     kMaxValue = kErrorUnknown,
+    // When adding a new value, check you've followed the steps in the comment
+    // at the top of this enum.
   };
 
   static CrostiniInstaller* GetForProfile(Profile* profile);
diff --git a/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc b/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc
index f135f13..6b0665a 100644
--- a/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc
+++ b/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc
@@ -110,6 +110,7 @@
 #include "chrome/browser/web_applications/components/app_registrar.h"
 #include "chrome/browser/web_applications/components/app_registrar_observer.h"
 #include "chrome/browser/web_applications/components/web_app_provider_base.h"
+#include "chrome/browser/web_applications/system_web_app_manager.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/extensions/api/autotest_private.h"
 #include "chrome/common/pref_names.h"
@@ -1709,6 +1710,67 @@
 }
 
 ///////////////////////////////////////////////////////////////////////////////
+// AutotestPrivateWaitForSystemWebAppsInstallFunction
+//////////////////////////////////////////////////////////////////////////////
+
+AutotestPrivateWaitForSystemWebAppsInstallFunction::
+    AutotestPrivateWaitForSystemWebAppsInstallFunction() = default;
+
+AutotestPrivateWaitForSystemWebAppsInstallFunction::
+    ~AutotestPrivateWaitForSystemWebAppsInstallFunction() = default;
+
+ExtensionFunction::ResponseAction
+AutotestPrivateWaitForSystemWebAppsInstallFunction::Run() {
+  Profile* profile = Profile::FromBrowserContext(browser_context());
+  web_app::WebAppProviderBase* provider =
+      web_app::WebAppProviderBase::GetProviderBase(profile);
+
+  if (!provider)
+    return RespondNow(Error("Web Apps are not available for profile."));
+
+  provider->system_web_app_manager().on_apps_synchronized().Post(
+      FROM_HERE,
+      base::BindOnce(
+          &AutotestPrivateWaitForSystemWebAppsInstallFunction::Respond, this,
+          NoArguments()));
+  return RespondLater();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// AutotestPrivateGetRegisteredSystemWebAppsFunction
+//////////////////////////////////////////////////////////////////////////////
+
+AutotestPrivateGetRegisteredSystemWebAppsFunction::
+    AutotestPrivateGetRegisteredSystemWebAppsFunction() = default;
+
+AutotestPrivateGetRegisteredSystemWebAppsFunction::
+    ~AutotestPrivateGetRegisteredSystemWebAppsFunction() = default;
+
+ExtensionFunction::ResponseAction
+AutotestPrivateGetRegisteredSystemWebAppsFunction::Run() {
+  Profile* profile = Profile::FromBrowserContext(browser_context());
+  web_app::WebAppProviderBase* provider =
+      web_app::WebAppProviderBase::GetProviderBase(profile);
+
+  if (!provider)
+    return RespondNow(Error("Web Apps are not available for profile."));
+
+  std::vector<api::autotest_private::SystemApp> result;
+
+  for (const auto& info :
+       provider->system_web_app_manager().GetRegisteredSystemAppsForTesting()) {
+    api::autotest_private::SystemApp system_app;
+    system_app.name_for_logging = info.name_for_logging;
+    system_app.url = info.install_url.GetOrigin().spec();
+    result.push_back(std::move(system_app));
+  }
+
+  return RespondNow(ArgumentList(
+      api::autotest_private::GetRegisteredSystemWebApps::Results::Create(
+          result)));
+}
+
+///////////////////////////////////////////////////////////////////////////////
 // AutotestPrivateLaunchArcIntentFunction
 ///////////////////////////////////////////////////////////////////////////////
 
diff --git a/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.h b/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.h
index 8e1c486..790ee46 100644
--- a/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.h
+++ b/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.h
@@ -292,6 +292,31 @@
   ResponseAction Run() override;
 };
 
+class AutotestPrivateWaitForSystemWebAppsInstallFunction
+    : public ExtensionFunction {
+ public:
+  AutotestPrivateWaitForSystemWebAppsInstallFunction();
+  DECLARE_EXTENSION_FUNCTION(
+      "autotestPrivate.waitForSystemWebAppsInstall",
+      AUTOTESTPRIVATE_WAITFORSYSTEMWEBAPPSINSTALLFUNCTION)
+
+ private:
+  ~AutotestPrivateWaitForSystemWebAppsInstallFunction() override;
+  ResponseAction Run() override;
+};
+
+class AutotestPrivateGetRegisteredSystemWebAppsFunction
+    : public ExtensionFunction {
+ public:
+  AutotestPrivateGetRegisteredSystemWebAppsFunction();
+  DECLARE_EXTENSION_FUNCTION("autotestPrivate.getRegisteredSystemWebApps",
+                             AUTOTESTPRIVATE_GETREGISTEREDSYSTEMWEBAPPSFUNCTION)
+
+ private:
+  ~AutotestPrivateGetRegisteredSystemWebAppsFunction() override;
+  ResponseAction Run() override;
+};
+
 class AutotestPrivateLaunchArcAppFunction : public ExtensionFunction {
  public:
   DECLARE_EXTENSION_FUNCTION("autotestPrivate.launchArcApp",
diff --git a/chrome/browser/chromeos/extensions/autotest_private/autotest_private_apitest.cc b/chrome/browser/chromeos/extensions/autotest_private/autotest_private_apitest.cc
index d665c09..54547be2 100644
--- a/chrome/browser/chromeos/extensions/autotest_private/autotest_private_apitest.cc
+++ b/chrome/browser/chromeos/extensions/autotest_private/autotest_private_apitest.cc
@@ -18,6 +18,7 @@
 #include "chrome/browser/ui/app_list/arc/arc_app_list_prefs.h"
 #include "chrome/browser/ui/ash/chrome_launcher_prefs.h"
 #include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/web_applications/test/test_system_web_app_installation.h"
 #include "components/arc/arc_prefs.h"
 #include "components/arc/arc_util.h"
 #include "components/arc/session/connection_holder.h"
@@ -333,4 +334,23 @@
       << message_;
 }
 
+class AutotestPrivateSystemWebAppsTest : public AutotestPrivateApiTest {
+ public:
+  AutotestPrivateSystemWebAppsTest() {
+    installation_ =
+        web_app::TestSystemWebAppInstallation::SetUpStandaloneSingleWindowApp(
+            true);
+  }
+  ~AutotestPrivateSystemWebAppsTest() override = default;
+
+ private:
+  std::unique_ptr<web_app::TestSystemWebAppInstallation> installation_;
+};
+
+IN_PROC_BROWSER_TEST_F(AutotestPrivateSystemWebAppsTest, SystemWebApps) {
+  ASSERT_TRUE(
+      RunComponentExtensionTestWithArg("autotest_private", "systemWebApps"))
+      << message_;
+}
+
 }  // namespace extensions
diff --git a/chrome/browser/chromeos/extensions/external_cache.cc b/chrome/browser/chromeos/extensions/external_cache.cc
index badc8ff..0ccd974 100644
--- a/chrome/browser/chromeos/extensions/external_cache.cc
+++ b/chrome/browser/chromeos/extensions/external_cache.cc
@@ -67,17 +67,14 @@
 }
 
 // static
-bool ExternalCache::ShouldCacheImmediately(
-    const base::Value& extension,
-    const std::string& installed_version) {
+bool ExternalCache::ShouldCacheImmediately(const base::Value& extension) {
   DCHECK(extension.is_dict());
 
   const base::Value* keep_if_present_value =
       extension.FindKeyOfType(extensions::ExternalProviderImpl::kKeepIfPresent,
                               base::Value::Type::BOOLEAN);
 
-  return !installed_version.empty() ||
-         (keep_if_present_value && keep_if_present_value->GetBool()) ||
+  return (keep_if_present_value && keep_if_present_value->GetBool()) ||
          extension.FindKey(extensions::ExternalProviderImpl::kExternalCrx);
 }
 
diff --git a/chrome/browser/chromeos/extensions/external_cache.h b/chrome/browser/chromeos/extensions/external_cache.h
index 9559868d..24f2e5f 100644
--- a/chrome/browser/chromeos/extensions/external_cache.h
+++ b/chrome/browser/chromeos/extensions/external_cache.h
@@ -43,8 +43,7 @@
   // If the external extension is not curently cached, whether the extension's
   // value should be added to the set of cached extensions (returned by
   // GetCachedExtensions()) regardles of the extension's download status.
-  static bool ShouldCacheImmediately(const base::Value& extension_value,
-                                     const std::string& installed_version);
+  static bool ShouldCacheImmediately(const base::Value& extension_value);
 
   // Returns already cached extensions.
   virtual const base::DictionaryValue* GetCachedExtensions() = 0;
diff --git a/chrome/browser/chromeos/extensions/external_cache_delegate.cc b/chrome/browser/chromeos/extensions/external_cache_delegate.cc
index 0ac759a..37e7afc 100644
--- a/chrome/browser/chromeos/extensions/external_cache_delegate.cc
+++ b/chrome/browser/chromeos/extensions/external_cache_delegate.cc
@@ -15,9 +15,4 @@
 void ExternalCacheDelegate::OnExtensionDownloadFailed(
     const extensions::ExtensionId& id) {}
 
-std::string ExternalCacheDelegate::GetInstalledExtensionVersion(
-    const extensions::ExtensionId& id) {
-  return std::string();
-}
-
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/extensions/external_cache_delegate.h b/chrome/browser/chromeos/extensions/external_cache_delegate.h
index c1d88084..ab3e37c 100644
--- a/chrome/browser/chromeos/extensions/external_cache_delegate.h
+++ b/chrome/browser/chromeos/extensions/external_cache_delegate.h
@@ -27,12 +27,6 @@
 
   // Called when extension with |id| fails to load due to a download error.
   virtual void OnExtensionDownloadFailed(const extensions::ExtensionId& id);
-
-  // Cache needs to provide already installed extensions otherwise they
-  // will be removed. Cache calls this function to get version of installed
-  // extension or empty string if not installed.
-  virtual std::string GetInstalledExtensionVersion(
-      const extensions::ExtensionId& id);
 };
 
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/extensions/external_cache_impl.cc b/chrome/browser/chromeos/extensions/external_cache_impl.cc
index 00b36d4a..9afac9e 100644
--- a/chrome/browser/chromeos/extensions/external_cache_impl.cc
+++ b/chrome/browser/chromeos/extensions/external_cache_impl.cc
@@ -98,7 +98,7 @@
 void ExternalCacheImpl::OnDamagedFileDetected(const base::FilePath& path) {
   for (base::DictionaryValue::Iterator it(*cached_extensions_.get());
        !it.IsAtEnd(); it.Advance()) {
-    const base::DictionaryValue* entry = NULL;
+    const base::DictionaryValue* entry = nullptr;
     if (!it.value().GetAsDictionary(&entry)) {
       NOTREACHED() << "ExternalCacheImpl found bad entry with type "
                    << it.value().type();
@@ -109,10 +109,10 @@
     if (entry->GetString(extensions::ExternalProviderImpl::kExternalCrx,
                          &external_crx) &&
         external_crx == path.value()) {
-      std::string id = it.key();
+      extensions::ExtensionId id = it.key();
       LOG(ERROR) << "ExternalCacheImpl extension at " << path.value()
                  << " failed to install, deleting it.";
-      cached_extensions_->Remove(id, NULL);
+      cached_extensions_->Remove(id, nullptr);
 
       local_cache_.RemoveExtension(id, std::string());
       UpdateExtensionLoader();
@@ -125,30 +125,32 @@
   DLOG(ERROR) << "ExternalCacheImpl cannot find external_crx " << path.value();
 }
 
-void ExternalCacheImpl::RemoveExtensions(const std::vector<std::string>& ids) {
+void ExternalCacheImpl::RemoveExtensions(
+    const std::vector<extensions::ExtensionId>& ids) {
   if (ids.empty())
     return;
 
   for (size_t i = 0; i < ids.size(); ++i) {
-    cached_extensions_->Remove(ids[i], NULL);
-    extensions_->Remove(ids[i], NULL);
+    cached_extensions_->Remove(ids[i], nullptr);
+    extensions_->Remove(ids[i], nullptr);
     local_cache_.RemoveExtension(ids[i], std::string());
   }
   UpdateExtensionLoader();
 }
 
-bool ExternalCacheImpl::GetExtension(const std::string& id,
+bool ExternalCacheImpl::GetExtension(const extensions::ExtensionId& id,
                                      base::FilePath* file_path,
                                      std::string* version) {
   return local_cache_.GetExtension(id, std::string(), file_path, version);
 }
 
-bool ExternalCacheImpl::ExtensionFetchPending(const std::string& id) {
+bool ExternalCacheImpl::ExtensionFetchPending(
+    const extensions::ExtensionId& id) {
   return extensions_->HasKey(id) && !cached_extensions_->HasKey(id);
 }
 
 void ExternalCacheImpl::PutExternalExtension(
-    const std::string& id,
+    const extensions::ExtensionId& id,
     const base::FilePath& crx_file_path,
     const std::string& version,
     PutExternalExtensionCallback callback) {
@@ -169,7 +171,7 @@
 }
 
 void ExternalCacheImpl::OnExtensionDownloadFailed(
-    const std::string& id,
+    const extensions::ExtensionId& id,
     Error error,
     const PingResult& ping_result,
     const std::set<int>& request_ids,
@@ -208,22 +210,17 @@
     std::move(callback).Run(true);
 }
 
-bool ExternalCacheImpl::IsExtensionPending(const std::string& id) {
+bool ExternalCacheImpl::IsExtensionPending(const extensions::ExtensionId& id) {
   return ExtensionFetchPending(id);
 }
 
-bool ExternalCacheImpl::GetExtensionExistingVersion(const std::string& id,
-                                                    std::string* version) {
-  base::DictionaryValue* extension_dictionary = NULL;
-  if (cached_extensions_->GetDictionary(id, &extension_dictionary)) {
-    if (extension_dictionary->GetString(
-            extensions::ExternalProviderImpl::kExternalVersion, version)) {
-      return true;
-    }
-    *version = delegate_->GetInstalledExtensionVersion(id);
-    return !version->empty();
-  }
-  return false;
+bool ExternalCacheImpl::GetExtensionExistingVersion(
+    const extensions::ExtensionId& id,
+    std::string* version) {
+  base::DictionaryValue* extension_dictionary = nullptr;
+  return cached_extensions_->GetDictionary(id, &extension_dictionary) &&
+         extension_dictionary->GetString(
+             extensions::ExternalProviderImpl::kExternalVersion, version);
 }
 
 void ExternalCacheImpl::UpdateExtensionLoader() {
@@ -272,15 +269,13 @@
       cached_extensions_->SetKey(
           entry.first,
           GetExtensionValueToCache(entry.second, file_path.value(), version));
-    } else if (ShouldCacheImmediately(
-                   entry.second,
-                   delegate_->GetInstalledExtensionVersion(entry.first))) {
+    } else if (ShouldCacheImmediately(entry.second)) {
       cached_extensions_->SetKey(entry.first, entry.second.Clone());
     }
   }
 
   if (downloader_)
-    downloader_->StartAllPending(NULL);
+    downloader_->StartAllPending(nullptr);
 
   VLOG(1) << "Updated ExternalCacheImpl, there are "
           << cached_extensions_->size() << " extensions cached";
@@ -288,7 +283,7 @@
   UpdateExtensionLoader();
 }
 
-void ExternalCacheImpl::OnPutExtension(const std::string& id,
+void ExternalCacheImpl::OnPutExtension(const extensions::ExtensionId& id,
                                        const base::FilePath& file_path,
                                        bool file_ownership_passed) {
   if (local_cache_.is_shutdown() || file_ownership_passed) {
@@ -310,7 +305,7 @@
 
   std::string version;
   std::string hash;
-  if (!local_cache_.GetExtension(id, hash, NULL, &version)) {
+  if (!local_cache_.GetExtension(id, hash, nullptr, &version)) {
     // Copy entry to don't modify it inside extensions_.
     LOG(ERROR) << "Can't find installed extension in cache " << id;
     return;
@@ -331,7 +326,7 @@
 }
 
 void ExternalCacheImpl::OnPutExternalExtension(
-    const std::string& id,
+    const extensions::ExtensionId& id,
     PutExternalExtensionCallback callback,
     const base::FilePath& file_path,
     bool file_ownership_passed) {
diff --git a/chrome/browser/chromeos/extensions/external_cache_impl.h b/chrome/browser/chromeos/extensions/external_cache_impl.h
index cf8534f3..f94606d5 100644
--- a/chrome/browser/chromeos/extensions/external_cache_impl.h
+++ b/chrome/browser/chromeos/extensions/external_cache_impl.h
@@ -21,6 +21,7 @@
 #include "content/public/browser/notification_observer.h"
 #include "content/public/browser/notification_registrar.h"
 #include "extensions/browser/updater/extension_downloader_delegate.h"
+#include "extensions/common/extension_id.h"
 
 namespace base {
 class DictionaryValue;
@@ -64,12 +65,13 @@
   void UpdateExtensionsList(
       std::unique_ptr<base::DictionaryValue> prefs) override;
   void OnDamagedFileDetected(const base::FilePath& path) override;
-  void RemoveExtensions(const std::vector<std::string>& ids) override;
-  bool GetExtension(const std::string& id,
+  void RemoveExtensions(
+      const std::vector<extensions::ExtensionId>& ids) override;
+  bool GetExtension(const extensions::ExtensionId& id,
                     base::FilePath* file_path,
                     std::string* version) override;
-  bool ExtensionFetchPending(const std::string& id) override;
-  void PutExternalExtension(const std::string& id,
+  bool ExtensionFetchPending(const extensions::ExtensionId& id) override;
+  void PutExternalExtension(const extensions::ExtensionId& id,
                             const base::FilePath& crx_file_path,
                             const std::string& version,
                             PutExternalExtensionCallback callback) override;
@@ -80,7 +82,7 @@
                const content::NotificationDetails& details) override;
 
   // Implementation of ExtensionDownloaderDelegate:
-  void OnExtensionDownloadFailed(const std::string& id,
+  void OnExtensionDownloadFailed(const extensions::ExtensionId& id,
                                  Error error,
                                  const PingResult& ping_result,
                                  const std::set<int>& request_ids,
@@ -91,8 +93,8 @@
                                    const PingResult& ping_result,
                                    const std::set<int>& request_ids,
                                    InstallCallback callback) override;
-  bool IsExtensionPending(const std::string& id) override;
-  bool GetExtensionExistingVersion(const std::string& id,
+  bool IsExtensionPending(const extensions::ExtensionId& id) override;
+  bool GetExtensionExistingVersion(const extensions::ExtensionId& id,
                                    std::string* version) override;
 
   void set_flush_on_put(bool flush_on_put) { flush_on_put_ = flush_on_put; }
@@ -106,13 +108,13 @@
   void CheckCache();
 
   // Invoked on the UI thread when a new entry has been installed in the cache.
-  void OnPutExtension(const std::string& id,
+  void OnPutExtension(const extensions::ExtensionId& id,
                       const base::FilePath& file_path,
                       bool file_ownership_passed);
 
   // Invoked on the UI thread when the external extension has been installed
   // in the local cache by calling PutExternalExtension.
-  void OnPutExternalExtension(const std::string& id,
+  void OnPutExternalExtension(const extensions::ExtensionId& id,
                               PutExternalExtensionCallback callback,
                               const base::FilePath& file_path,
                               bool file_ownership_passed);
diff --git a/chrome/browser/chromeos/extensions/external_cache_impl_unittest.cc b/chrome/browser/chromeos/extensions/external_cache_impl_unittest.cc
index 9def4250..87854ff 100644
--- a/chrome/browser/chromeos/extensions/external_cache_impl_unittest.cc
+++ b/chrome/browser/chromeos/extensions/external_cache_impl_unittest.cc
@@ -38,6 +38,8 @@
 const char kTestExtensionId3[] = "cccccccccccccccccccccccccccccccc";
 const char kTestExtensionId4[] = "dddddddddddddddddddddddddddddddd";
 const char kNonWebstoreUpdateUrl[] = "https://localhost/service/update2/crx";
+const char kExternalCrxPath[] = "/local/path/to/extension.crx";
+const char kExternalCrxVersion[] = "1.2.3.4";
 
 }  // namespace
 
@@ -63,12 +65,6 @@
   void OnExtensionListsUpdated(const base::DictionaryValue* prefs) override {
     prefs_.reset(prefs->DeepCopy());
   }
-  std::string GetInstalledExtensionVersion(
-      const extensions::ExtensionId& id) override {
-    std::map<std::string, std::string>::iterator it =
-        installed_extensions_.find(id);
-    return it != installed_extensions_.end() ? it->second : std::string();
-  }
 
   base::FilePath CreateCacheDir(bool initialized) {
     EXPECT_TRUE(cache_dir_.CreateUniqueTempDir());
@@ -113,9 +109,13 @@
     return entry;
   }
 
-  void AddInstalledExtension(const std::string& id,
-                             const std::string& version) {
-    installed_extensions_[id] = version;
+  std::unique_ptr<base::DictionaryValue> CreateEntryWithExternalCrx() {
+    auto entry = std::make_unique<base::DictionaryValue>();
+    entry->SetString(extensions::ExternalProviderImpl::kExternalCrx,
+                     kExternalCrxPath);
+    entry->SetString(extensions::ExternalProviderImpl::kExternalVersion,
+                     kExternalCrxVersion);
+    return entry;
   }
 
  private:
@@ -127,7 +127,6 @@
   base::ScopedTempDir cache_dir_;
   base::ScopedTempDir temp_dir_;
   std::unique_ptr<base::DictionaryValue> prefs_;
-  std::map<std::string, std::string> installed_extensions_;
 
   ScopedCrosSettingsTestHelper cros_settings_test_helper_;
 
@@ -262,7 +261,7 @@
       base::PathExists(GetExtensionFile(cache_dir, kTestExtensionId4, "4")));
 }
 
-TEST_F(ExternalCacheImplTest, PreserveInstalled) {
+TEST_F(ExternalCacheImplTest, PreserveExternalCrx) {
   base::FilePath cache_dir(CreateCacheDir(false));
   ExternalCacheImpl external_cache(
       cache_dir, url_loader_factory(),
@@ -270,24 +269,25 @@
       true, false);
 
   std::unique_ptr<base::DictionaryValue> prefs(new base::DictionaryValue);
-  prefs->Set(kTestExtensionId1, CreateEntryWithUpdateUrl(true));
+  prefs->Set(kTestExtensionId1, CreateEntryWithExternalCrx());
   prefs->Set(kTestExtensionId2, CreateEntryWithUpdateUrl(true));
 
-  AddInstalledExtension(kTestExtensionId1, "1");
-
   external_cache.UpdateExtensionsList(std::move(prefs));
   content::RunAllTasksUntilIdle();
 
   ASSERT_TRUE(provided_prefs());
   EXPECT_EQ(provided_prefs()->size(), 1ul);
 
-  // File not in cache but extension installed.
+  // Extensions downloaded from update url will only be visible in the provided
+  // prefs once the download of the .crx has finished. Extensions that are
+  // provided as external crx path directly should also be visible in the
+  // provided prefs directly.
   const base::DictionaryValue* entry1 = NULL;
   ASSERT_TRUE(provided_prefs()->GetDictionary(kTestExtensionId1, &entry1));
-  EXPECT_TRUE(
-      entry1->HasKey(extensions::ExternalProviderImpl::kExternalUpdateUrl));
-  EXPECT_FALSE(entry1->HasKey(extensions::ExternalProviderImpl::kExternalCrx));
   EXPECT_FALSE(
+      entry1->HasKey(extensions::ExternalProviderImpl::kExternalUpdateUrl));
+  EXPECT_TRUE(entry1->HasKey(extensions::ExternalProviderImpl::kExternalCrx));
+  EXPECT_TRUE(
       entry1->HasKey(extensions::ExternalProviderImpl::kExternalVersion));
 }
 
diff --git a/chrome/browser/chromeos/extensions/file_manager/file_manager_private_api_functions.h b/chrome/browser/chromeos/extensions/file_manager/file_manager_private_api_functions.h
index d0816e6..2a6d2734 100644
--- a/chrome/browser/chromeos/extensions/file_manager/file_manager_private_api_functions.h
+++ b/chrome/browser/chromeos/extensions/file_manager/file_manager_private_api_functions.h
@@ -12,6 +12,7 @@
 #include "chrome/browser/chromeos/extensions/file_manager/private_api_dialog.h"
 #include "chrome/browser/chromeos/extensions/file_manager/private_api_drive.h"
 #include "chrome/browser/chromeos/extensions/file_manager/private_api_file_system.h"
+#include "chrome/browser/chromeos/extensions/file_manager/private_api_holding_space.h"
 #include "chrome/browser/chromeos/extensions/file_manager/private_api_media_parser.h"
 #include "chrome/browser/chromeos/extensions/file_manager/private_api_misc.h"
 #include "chrome/browser/chromeos/extensions/file_manager/private_api_mount.h"
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 eba96b0d..e06270f 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
@@ -7,6 +7,7 @@
 
 #include <memory>
 
+#include "ash/public/cpp/ash_features.h"
 #include "base/base64.h"
 #include "base/bind.h"
 #include "base/path_service.h"
@@ -354,6 +355,25 @@
   file_manager::EventRouter* event_router_ = nullptr;
 };
 
+// Parameterize by whether holding space feature is enabled.
+class FileManagerPrivateHoldingSpaceApiTest
+    : public FileManagerPrivateApiTest,
+      public testing::WithParamInterface<bool> {
+ public:
+  FileManagerPrivateHoldingSpaceApiTest() {
+    scoped_feature_list_.InitWithFeatureState(
+        ash::features::kTemporaryHoldingSpace, GetParam());
+  }
+  ~FileManagerPrivateHoldingSpaceApiTest() override = default;
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+INSTANTIATE_TEST_SUITE_P(HoldingSpaceEnabled,
+                         FileManagerPrivateHoldingSpaceApiTest,
+                         testing::Bool());
+
 IN_PROC_BROWSER_TEST_F(FileManagerPrivateApiTest, Mount) {
   using chromeos::file_system_provider::IconSet;
   profile()->GetPrefs()->SetBoolean(drive::prefs::kDisableDrive, true);
@@ -590,3 +610,28 @@
   response_helper.WaitForResponse();
   EXPECT_TRUE(response_helper.GetResponse());
 }
+
+IN_PROC_BROWSER_TEST_P(FileManagerPrivateHoldingSpaceApiTest, HoldingSpace) {
+  const base::FilePath test_dir = temp_dir_.GetPath();
+  AddLocalFileSystem(browser()->profile(), test_dir);
+
+  {
+    base::ScopedAllowBlockingForTesting allow_io;
+    base::File image_file(test_dir.Append("test_image.jpg"),
+                          base::File::FLAG_CREATE | base::File::FLAG_WRITE);
+    ASSERT_TRUE(image_file.IsValid());
+    base::File audio_file(test_dir.Append("test_audio.mp3"),
+                          base::File::FLAG_CREATE | base::File::FLAG_WRITE);
+    ASSERT_TRUE(audio_file.IsValid());
+    base::File video_file(test_dir.Append("test_video.mp4"),
+                          base::File::FLAG_CREATE | base::File::FLAG_WRITE);
+    ASSERT_TRUE(video_file.IsValid());
+  }
+
+  if (GetParam()) {
+    EXPECT_TRUE(RunComponentExtensionTest("file_browser/holding_space"));
+  } else {
+    EXPECT_TRUE(
+        RunComponentExtensionTest("file_browser/holding_space_disabled"));
+  }
+}
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_holding_space.cc b/chrome/browser/chromeos/extensions/file_manager/private_api_holding_space.cc
new file mode 100644
index 0000000..a6ec731c
--- /dev/null
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_holding_space.cc
@@ -0,0 +1,97 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/extensions/file_manager/private_api_holding_space.h"
+
+#include <memory>
+#include <vector>
+
+#include "base/memory/ref_counted.h"
+#include "chrome/browser/chromeos/file_manager/fileapi_util.h"
+#include "chrome/browser/extensions/chrome_extension_function_details.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/ash/holding_space/holding_space_keyed_service.h"
+#include "chrome/browser/ui/ash/holding_space/holding_space_keyed_service_factory.h"
+#include "chrome/common/extensions/api/file_manager_private.h"
+#include "chrome/common/extensions/api/file_manager_private_internal.h"
+#include "storage/browser/file_system/file_system_context.h"
+#include "storage/browser/file_system/file_system_url.h"
+#include "url/gurl.h"
+
+namespace extensions {
+
+FileManagerPrivateInternalToggleAddedToHoldingSpaceFunction::
+    FileManagerPrivateInternalToggleAddedToHoldingSpaceFunction() = default;
+
+FileManagerPrivateInternalToggleAddedToHoldingSpaceFunction::
+    ~FileManagerPrivateInternalToggleAddedToHoldingSpaceFunction() = default;
+
+ExtensionFunction::ResponseAction
+FileManagerPrivateInternalToggleAddedToHoldingSpaceFunction::Run() {
+  using extensions::api::file_manager_private_internal::
+      ToggleAddedToHoldingSpace::Params;
+  const std::unique_ptr<Params> params(Params::Create(*args_));
+  EXTENSION_FUNCTION_VALIDATE(params);
+
+  const ChromeExtensionFunctionDetails chrome_details(this);
+
+  ash::HoldingSpaceKeyedService* const holding_space =
+      ash::HoldingSpaceKeyedServiceFactory::GetInstance()->GetService(
+          chrome_details.GetProfile());
+  if (!holding_space)
+    return RespondNow(Error("Not enabled"));
+
+  scoped_refptr<storage::FileSystemContext> file_system_context =
+      file_manager::util::GetFileSystemContextForRenderFrameHost(
+          chrome_details.GetProfile(), render_frame_host());
+
+  std::vector<storage::FileSystemURL> file_system_urls;
+  for (const auto& item_url : params->urls) {
+    const storage::FileSystemURL file_system_url =
+        file_system_context->CrackURL(GURL(item_url));
+    if (!file_system_url.is_valid())
+      return RespondNow(Error("Invalid item URL " + item_url));
+    file_system_urls.push_back(file_system_url);
+  }
+
+  for (const auto& file_system_url : file_system_urls) {
+    const bool in_holding_space =
+        holding_space->ContainsPinnedFile(file_system_url);
+    if (params->add && !in_holding_space) {
+      holding_space->AddPinnedFile(file_system_url);
+    } else if (!params->add && in_holding_space) {
+      holding_space->RemovePinnedFile(file_system_url);
+    }
+  }
+
+  return RespondNow(NoArguments());
+}
+
+FileManagerPrivateGetHoldingSpaceStateFunction::
+    FileManagerPrivateGetHoldingSpaceStateFunction() = default;
+
+FileManagerPrivateGetHoldingSpaceStateFunction::
+    ~FileManagerPrivateGetHoldingSpaceStateFunction() = default;
+
+ExtensionFunction::ResponseAction
+FileManagerPrivateGetHoldingSpaceStateFunction::Run() {
+  const ChromeExtensionFunctionDetails chrome_details(this);
+  ash::HoldingSpaceKeyedService* const holding_space =
+      ash::HoldingSpaceKeyedServiceFactory::GetInstance()->GetService(
+          chrome_details.GetProfile());
+  if (!holding_space)
+    return RespondNow(Error("Not enabled"));
+
+  std::vector<GURL> items = holding_space->GetPinnedFiles();
+
+  api::file_manager_private::HoldingSpaceState holding_space_state;
+  for (const auto& item : items)
+    holding_space_state.item_urls.push_back(item.spec());
+
+  return RespondNow(ArgumentList(
+      api::file_manager_private::GetHoldingSpaceState::Results::Create(
+          holding_space_state)));
+}
+
+}  // namespace extensions
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_holding_space.h b/chrome/browser/chromeos/extensions/file_manager/private_api_holding_space.h
new file mode 100644
index 0000000..c019d75
--- /dev/null
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_holding_space.h
@@ -0,0 +1,45 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_CHROMEOS_EXTENSIONS_FILE_MANAGER_PRIVATE_API_HOLDING_SPACE_H_
+#define CHROME_BROWSER_CHROMEOS_EXTENSIONS_FILE_MANAGER_PRIVATE_API_HOLDING_SPACE_H_
+
+#include "extensions/browser/extension_function.h"
+
+namespace extensions {
+
+class FileManagerPrivateInternalToggleAddedToHoldingSpaceFunction
+    : public ExtensionFunction {
+ public:
+  FileManagerPrivateInternalToggleAddedToHoldingSpaceFunction();
+
+  DECLARE_EXTENSION_FUNCTION(
+      "fileManagerPrivateInternal.toggleAddedToHoldingSpace",
+      FILEMANAGERPRIVATEINTERNAL_TOGGLEADDEDTOHOLDINGSPACE)
+
+ protected:
+  ~FileManagerPrivateInternalToggleAddedToHoldingSpaceFunction() override;
+
+  // ExtensionFunction overrides.
+  ResponseAction Run() override;
+};
+
+class FileManagerPrivateGetHoldingSpaceStateFunction
+    : public ExtensionFunction {
+ public:
+  FileManagerPrivateGetHoldingSpaceStateFunction();
+
+  DECLARE_EXTENSION_FUNCTION("fileManagerPrivate.getHoldingSpaceState",
+                             FILEMANAGERPRIVATEINTERNAL_GETHOLDINGSPACESTATE)
+
+ protected:
+  ~FileManagerPrivateGetHoldingSpaceStateFunction() override;
+
+  // ExtensionFunction overrides.
+  ResponseAction Run() override;
+};
+
+}  // namespace extensions
+
+#endif  // CHROME_BROWSER_CHROMEOS_EXTENSIONS_FILE_MANAGER_PRIVATE_API_HOLDING_SPACE_H_
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_strings.cc b/chrome/browser/chromeos/extensions/file_manager/private_api_strings.cc
index 4146e09f..9c404b6 100644
--- a/chrome/browser/chromeos/extensions/file_manager/private_api_strings.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_strings.cc
@@ -7,6 +7,7 @@
 #include <memory>
 #include <utility>
 
+#include "ash/public/cpp/ash_features.h"
 #include "base/feature_list.h"
 #include "base/strings/stringprintf.h"
 #include "chrome/browser/chromeos/crostini/crostini_features.h"
@@ -66,6 +67,8 @@
       "DRIVE_BIDIRECTIONAL_NATIVE_MESSAGING_ENABLED",
       base::FeatureList::IsEnabled(
           chromeos::features::kDriveFsBidirectionalNativeMessaging));
+  dict->SetBoolean("HOLDING_SPACE_ENABLED",
+                   ash::features::IsTemporaryHoldingSpaceEnabled());
 
   dict->SetString("UI_LOCALE", extension_l10n_util::CurrentLocaleOrDefault());
 
diff --git a/chrome/browser/chromeos/extensions/test_external_cache.cc b/chrome/browser/chromeos/extensions/test_external_cache.cc
index 6d3f29b..508ec9fb 100644
--- a/chrome/browser/chromeos/extensions/test_external_cache.cc
+++ b/chrome/browser/chromeos/extensions/test_external_cache.cc
@@ -124,9 +124,7 @@
           entry.first,
           GetExtensionValueToCache(entry.second, crx_cache_[entry.first].path,
                                    crx_cache_[entry.first].version));
-    } else if (ShouldCacheImmediately(
-                   entry.second,
-                   delegate_->GetInstalledExtensionVersion(entry.first))) {
+    } else if (ShouldCacheImmediately(entry.second)) {
       cached_extensions_.SetKey(entry.first, entry.second.Clone());
     }
   }
diff --git a/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc b/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
index d85f351b..b273e03 100644
--- a/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
+++ b/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
@@ -12,6 +12,8 @@
 #include "chrome/browser/chromeos/file_manager/file_manager_browsertest_base.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
+#include "chrome/browser/web_applications/system_web_app_manager.h"
+#include "chrome/browser/web_applications/web_app_provider.h"
 #include "chrome/common/pref_names.h"
 #include "chromeos/constants/chromeos_switches.h"
 #include "components/prefs/pref_service.h"
@@ -202,6 +204,26 @@
   StartTest();
 }
 
+// Files app tests that require SWA (System Web Apps).
+class SWAsFilesAppBrowserTest : public FilesAppBrowserTest {
+ public:
+  SWAsFilesAppBrowserTest() = default;
+  SWAsFilesAppBrowserTest(const SWAsFilesAppBrowserTest&) = delete;
+  SWAsFilesAppBrowserTest& operator=(const SWAsFilesAppBrowserTest&) = delete;
+
+  void SetUpOnMainThread() override {
+    web_app::WebAppProvider::Get(profile())
+        ->system_web_app_manager()
+        .InstallSystemAppsForTesting();
+
+    FilesAppBrowserTest::SetUpOnMainThread();
+  }
+};
+
+IN_PROC_BROWSER_TEST_P(SWAsFilesAppBrowserTest, Test) {
+  StartTest();
+}
+
 // A version of the FilesAppBrowserTest that supports spanning browser restart
 // to allow testing prefs and other things.
 class ExtendedFilesAppBrowserTest : public FilesAppBrowserTest {
@@ -300,6 +322,12 @@
                       TestCase("audioRepeatOneModeMultipleFileDrive")));
 
 WRAPPED_INSTANTIATE_TEST_SUITE_P(
+    OpenImageBacklight, /* open_image_backlight.js */
+    SWAsFilesAppBrowserTest,
+    ::testing::Values(TestCase("imageOpenBacklight").InGuestMode(),
+                      TestCase("imageOpenBacklight")));
+
+WRAPPED_INSTANTIATE_TEST_SUITE_P(
     OpenImageFiles, /* open_image_files.js */
     FilesAppBrowserTest,
     ::testing::Values(TestCase("imageOpenDownloads").InGuestMode(),
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 f17da69..b17246c 100644
--- a/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc
+++ b/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc
@@ -27,6 +27,8 @@
 #include "base/test/bind_test_util.h"
 #include "base/threading/thread_restrictions.h"
 #include "base/time/time.h"
+#include "chrome/browser/apps/app_service/app_service_proxy.h"
+#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chromeos/arc/fileapi/arc_documents_provider_util.h"
 #include "chrome/browser/chromeos/arc/fileapi/arc_media_view_util.h"
@@ -2306,6 +2308,24 @@
     return;
   }
 
+  if (name == "hasSwaStarted") {
+    std::string swa_app_id;
+    ASSERT_TRUE(value.GetString("swaAppId", &swa_app_id));
+
+    *output = "false";
+
+    auto* proxy = apps::AppServiceProxyFactory::GetForProfile(profile());
+    proxy->InstanceRegistry().ForEachInstance(
+        [&swa_app_id, &output](const apps::InstanceUpdate& update) {
+          if (update.AppId() == swa_app_id &&
+              update.State() & apps::InstanceState::kStarted) {
+            *output = "true";
+          }
+        });
+
+    return;
+  }
+
   if (name == "countAppWindows") {
     std::string app_id;
     ASSERT_TRUE(value.GetString("appId", &app_id));
diff --git a/chrome/browser/chromeos/web_applications/chrome_camera_app_ui_delegate.cc b/chrome/browser/chromeos/web_applications/chrome_camera_app_ui_delegate.cc
index 3b26f54..baedd6c 100644
--- a/chrome/browser/chromeos/web_applications/chrome_camera_app_ui_delegate.cc
+++ b/chrome/browser/chromeos/web_applications/chrome_camera_app_ui_delegate.cc
@@ -6,12 +6,76 @@
 
 #include "base/files/file_path.h"
 #include "base/system/sys_info.h"
+#include "chrome/browser/apps/app_service/app_service_proxy.h"
+#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
+#include "chrome/browser/apps/app_service/launch_utils.h"
 #include "chrome/browser/chromeos/file_manager/path_util.h"
+#include "chrome/browser/chromeos/web_applications/default_web_app_ids.h"
+#include "chrome/browser/media/webrtc/media_capture_devices_dispatcher.h"
 #include "chrome/browser/metrics/chrome_metrics_service_accessor.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/web_applications/web_app_tab_helper.h"
 #include "chrome/browser/web_launch/web_launch_files_helper.h"
+#include "chromeos/components/camera_app_ui/url_constants.h"
+#include "components/services/app_service/public/mojom/types.mojom.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_ui_data_source.h"
+#include "third_party/blink/public/mojom/mediastream/media_stream.mojom.h"
+#include "ui/gfx/native_widget_types.h"
+
+namespace {
+
+constexpr int kDefaultWindowWidth = 864;
+constexpr int kDefaultWindowHeight = 486;
+
+}  // namespace
+
+// static
+void ChromeCameraAppUIDelegate::CameraAppDialog::ShowIntent(
+    const std::string& queries,
+    gfx::NativeWindow parent) {
+  CameraAppDialog* dialog =
+      new CameraAppDialog(chromeos::kChromeUICameraAppMainURL + queries);
+  dialog->ShowSystemDialog(parent);
+}
+
+ChromeCameraAppUIDelegate::CameraAppDialog::CameraAppDialog(
+    const std::string& url)
+    : chromeos::SystemWebDialogDelegate(GURL(url), /*title=*/base::string16()) {
+}
+
+ChromeCameraAppUIDelegate::CameraAppDialog::~CameraAppDialog() {}
+
+ui::ModalType ChromeCameraAppUIDelegate::CameraAppDialog::GetDialogModalType()
+    const {
+  return ui::MODAL_TYPE_WINDOW;
+}
+
+bool ChromeCameraAppUIDelegate::CameraAppDialog::CanMaximizeDialog() const {
+  return true;
+}
+
+void ChromeCameraAppUIDelegate::CameraAppDialog::GetDialogSize(
+    gfx::Size* size) const {
+  size->SetSize(kDefaultWindowWidth, kDefaultWindowHeight);
+}
+
+void ChromeCameraAppUIDelegate::CameraAppDialog::RequestMediaAccessPermission(
+    content::WebContents* web_contents,
+    const content::MediaStreamRequest& request,
+    content::MediaResponseCallback callback) {
+  MediaCaptureDevicesDispatcher::GetInstance()->ProcessMediaAccessRequest(
+      web_contents, request, std::move(callback), /* extension */ nullptr);
+}
+
+bool ChromeCameraAppUIDelegate::CameraAppDialog::CheckMediaAccessPermission(
+    content::RenderFrameHost* render_frame_host,
+    const GURL& security_origin,
+    blink::mojom::MediaStreamType type) {
+  return MediaCaptureDevicesDispatcher::GetInstance()
+      ->CheckMediaAccessPermission(render_frame_host, security_origin, type);
+}
 
 ChromeCameraAppUIDelegate::ChromeCameraAppUIDelegate(content::WebUI* web_ui)
     : web_ui_(web_ui) {}
@@ -45,3 +109,29 @@
   // UMA.
   return ChromeMetricsServiceAccessor::IsMetricsAndCrashReportingEnabled();
 }
+
+void ChromeCameraAppUIDelegate::OpenFileInGallery(const std::string& name) {
+  // Check to avoid directory traversal attack.
+  base::FilePath name_component(name);
+  if (name_component.ReferencesParent())
+    return;
+
+  Profile* profile = Profile::FromWebUI(web_ui_);
+
+  base::FilePath path =
+      file_manager::util::GetDownloadsFolderForProfile(profile).Append(
+          name_component);
+  auto&& file_paths = std::vector<base::FilePath>({path});
+  apps::mojom::FilePathsPtr launch_files =
+      apps::mojom::FilePaths::New(file_paths);
+
+  apps::AppServiceProxy* proxy =
+      apps::AppServiceProxyFactory::GetForProfile(profile);
+  proxy->LaunchAppWithFiles(
+      chromeos::default_web_apps::kMediaAppId,
+      apps::mojom::LaunchContainer::kLaunchContainerWindow,
+      apps::GetEventFlags(apps::mojom::LaunchContainer::kLaunchContainerTab,
+                          WindowOpenDisposition::NEW_FOREGROUND_TAB,
+                          /* preferred_container=*/false),
+      apps::mojom::LaunchSource::kFromOtherApp, std::move(launch_files));
+}
diff --git a/chrome/browser/chromeos/web_applications/chrome_camera_app_ui_delegate.h b/chrome/browser/chromeos/web_applications/chrome_camera_app_ui_delegate.h
index 02031c7f..93d5b39 100644
--- a/chrome/browser/chromeos/web_applications/chrome_camera_app_ui_delegate.h
+++ b/chrome/browser/chromeos/web_applications/chrome_camera_app_ui_delegate.h
@@ -5,15 +5,60 @@
 #ifndef CHROME_BROWSER_CHROMEOS_WEB_APPLICATIONS_CHROME_CAMERA_APP_UI_DELEGATE_H_
 #define CHROME_BROWSER_CHROMEOS_WEB_APPLICATIONS_CHROME_CAMERA_APP_UI_DELEGATE_H_
 
+#include "chrome/browser/ui/webui/chromeos/system_web_dialog_delegate.h"
 #include "chromeos/components/camera_app_ui/camera_app_ui_delegate.h"
+#include "content/public/browser/media_stream_request.h"
 #include "content/public/browser/web_ui.h"
 
+namespace content {
+struct MediaStreamRequest;
+class RenderFrameHost;
+class WebContents;
+}  // namespace content
+
+namespace blink {
+namespace mojom {
+enum class MediaStreamType;
+}  // namespace mojom
+}  // namespace blink
+
+namespace ui {
+enum ModalType;
+}  // namespace ui
+
 /**
  * Implementation of the CameraAppUIDelegate interface. Provides the camera app
  * code in chromeos/ with functions that only exist in chrome/.
  */
 class ChromeCameraAppUIDelegate : public CameraAppUIDelegate {
  public:
+  class CameraAppDialog : public chromeos::SystemWebDialogDelegate {
+   public:
+    static void ShowIntent(const std::string& queries,
+                           gfx::NativeWindow parent);
+
+    // SystemWebDialogDelegate
+    ui::ModalType GetDialogModalType() const override;
+    bool CanMaximizeDialog() const override;
+
+    // ui::WebDialogDelegate
+    void GetDialogSize(gfx::Size* size) const override;
+    void RequestMediaAccessPermission(
+        content::WebContents* web_contents,
+        const content::MediaStreamRequest& request,
+        content::MediaResponseCallback callback) override;
+    bool CheckMediaAccessPermission(
+        content::RenderFrameHost* render_frame_host,
+        const GURL& security_origin,
+        blink::mojom::MediaStreamType type) override;
+
+   private:
+    explicit CameraAppDialog(const std::string& url);
+    ~CameraAppDialog() override;
+
+    DISALLOW_COPY_AND_ASSIGN(CameraAppDialog);
+  };
+
   explicit ChromeCameraAppUIDelegate(content::WebUI* web_ui);
 
   ChromeCameraAppUIDelegate(const ChromeCameraAppUIDelegate&) = delete;
@@ -24,6 +69,7 @@
   void SetLaunchDirectory() override;
   void PopulateLoadTimeData(content::WebUIDataSource* source) override;
   bool IsMetricsAndCrashReportingEnabled() override;
+  void OpenFileInGallery(const std::string& name) override;
 
  private:
   content::WebUI* web_ui_;  // Owns |this|.
diff --git a/chrome/browser/metrics/chrome_metrics_service_client.cc b/chrome/browser/metrics/chrome_metrics_service_client.cc
index 867b30a..9a22cf06 100644
--- a/chrome/browser/metrics/chrome_metrics_service_client.cc
+++ b/chrome/browser/metrics/chrome_metrics_service_client.cc
@@ -149,6 +149,7 @@
 #include "chrome/browser/metrics/ambient_mode_metrics_provider.h"
 #include "chrome/browser/metrics/assistant_service_metrics_provider.h"
 #include "chrome/browser/metrics/chromeos_metrics_provider.h"
+#include "chrome/browser/metrics/cros_healthd_metrics_provider.h"
 #include "chrome/browser/signin/signin_status_metrics_provider_chromeos.h"
 #include "components/metrics/structured/structured_metrics_provider.h"
 #endif
@@ -712,6 +713,9 @@
           metrics::MetricsLogUploader::UMA));
 
   metrics_service_->RegisterMetricsProvider(
+      std::make_unique<CrosHealthdMetricsProvider>());
+
+  metrics_service_->RegisterMetricsProvider(
       std::make_unique<SigninStatusMetricsProviderChromeOS>());
 
   // Record default UMA state as opt-out for all Chrome OS users, if not
diff --git a/chrome/browser/metrics/chrome_metrics_service_client_unittest.cc b/chrome/browser/metrics/chrome_metrics_service_client_unittest.cc
index df9b3c9..01351cc 100644
--- a/chrome/browser/metrics/chrome_metrics_service_client_unittest.cc
+++ b/chrome/browser/metrics/chrome_metrics_service_client_unittest.cc
@@ -177,10 +177,10 @@
 
 #if defined(OS_CHROMEOS)
   // AmbientModeMetricsProvider, AssistantServiceMetricsProvider,
-  // ChromeOSMetricsProvider,
+  // CrosHealthdMetricsProvider, ChromeOSMetricsProvider,
   // SigninStatusMetricsProviderChromeOS, PrinterMetricsProvider, and
   // HashedLoggingMetricsProvider.
-  expected_providers += 6;
+  expected_providers += 7;
 #endif  // defined(OS_CHROMEOS)
 
 #if !defined(OS_CHROMEOS)
diff --git a/chrome/browser/nearby_sharing/certificates/BUILD.gn b/chrome/browser/nearby_sharing/certificates/BUILD.gn
index 1895031..0830343 100644
--- a/chrome/browser/nearby_sharing/certificates/BUILD.gn
+++ b/chrome/browser/nearby_sharing/certificates/BUILD.gn
@@ -37,6 +37,7 @@
     "//components/leveldb_proto",
     "//components/prefs",
     "//crypto",
+    "//device/bluetooth",
   ]
 }
 
@@ -59,6 +60,7 @@
     "//components/leveldb_proto",
     "//components/prefs",
     "//crypto",
+    "//device/bluetooth/public/cpp",
   ]
 }
 
@@ -87,6 +89,8 @@
     "//components/leveldb_proto:test_support",
     "//components/prefs:test_support",
     "//crypto",
+    "//device/bluetooth",
+    "//device/bluetooth:mocks",
     "//testing/gtest",
   ]
 }
diff --git a/chrome/browser/nearby_sharing/certificates/fake_nearby_share_certificate_storage.cc b/chrome/browser/nearby_sharing/certificates/fake_nearby_share_certificate_storage.cc
index f91e9dd..a6c19f28 100644
--- a/chrome/browser/nearby_sharing/certificates/fake_nearby_share_certificate_storage.cc
+++ b/chrome/browser/nearby_sharing/certificates/fake_nearby_share_certificate_storage.cc
@@ -99,7 +99,7 @@
 
 void FakeNearbyShareCertificateStorage::ReplacePrivateCertificates(
     const std::vector<NearbySharePrivateCertificate>& private_certificates) {
-  replace_private_certificates_calls_.push_back(private_certificates);
+  private_certificates_ = private_certificates;
 }
 
 void FakeNearbyShareCertificateStorage::ReplacePublicCertificates(
diff --git a/chrome/browser/nearby_sharing/certificates/fake_nearby_share_certificate_storage.h b/chrome/browser/nearby_sharing/certificates/fake_nearby_share_certificate_storage.h
index fcc0c158..643343f 100644
--- a/chrome/browser/nearby_sharing/certificates/fake_nearby_share_certificate_storage.h
+++ b/chrome/browser/nearby_sharing/certificates/fake_nearby_share_certificate_storage.h
@@ -95,45 +95,6 @@
   FakeNearbyShareCertificateStorage();
   ~FakeNearbyShareCertificateStorage() override;
 
-  void SetPublicCertificateIds(const std::vector<std::string>& ids);
-  void SetPrivateCertificates(
-      base::Optional<std::vector<NearbySharePrivateCertificate>>
-          private_certificates);
-  void SetNextPrivateCertificateExpirationTime(base::Optional<base::Time> time);
-  void SetNextPublicCertificateExpirationTime(base::Optional<base::Time> time);
-
-  std::vector<PublicCertificateCallback>& get_public_certificates_callbacks() {
-    return get_public_certificates_callbacks_;
-  }
-
-  std::vector<std::vector<NearbySharePrivateCertificate>>&
-  replace_private_certificates_calls() {
-    return replace_private_certificates_calls_;
-  }
-
-  std::vector<ReplacePublicCertificatesCall>&
-  replace_public_certificates_calls() {
-    return replace_public_certificates_calls_;
-  }
-
-  std::vector<AddPublicCertificatesCall>& add_public_certificates_calls() {
-    return add_public_certificates_calls_;
-  }
-
-  std::vector<RemoveExpiredPublicCertificatesCall>&
-  remove_expired_public_certificates_calls() {
-    return remove_expired_public_certificates_calls_;
-  }
-
-  size_t num_clear_private_certificates_calls() {
-    return num_clear_private_certificates_calls_;
-  }
-
-  std::vector<ResultCallback>& clear_public_certificates_callbacks() {
-    return clear_public_certificates_callbacks_;
-  }
-
- private:
   // NearbyShareCertificateStorage:
   std::vector<std::string> GetPublicCertificateIds() const override;
   void GetPublicCertificates(PublicCertificateCallback callback) override;
@@ -159,6 +120,40 @@
   void ClearPrivateCertificates() override;
   void ClearPublicCertificates(ResultCallback callback) override;
 
+  void SetPublicCertificateIds(const std::vector<std::string>& ids);
+  void SetPrivateCertificates(
+      base::Optional<std::vector<NearbySharePrivateCertificate>>
+          private_certificates);
+  void SetNextPrivateCertificateExpirationTime(base::Optional<base::Time> time);
+  void SetNextPublicCertificateExpirationTime(base::Optional<base::Time> time);
+
+  std::vector<PublicCertificateCallback>& get_public_certificates_callbacks() {
+    return get_public_certificates_callbacks_;
+  }
+
+  std::vector<ReplacePublicCertificatesCall>&
+  replace_public_certificates_calls() {
+    return replace_public_certificates_calls_;
+  }
+
+  std::vector<AddPublicCertificatesCall>& add_public_certificates_calls() {
+    return add_public_certificates_calls_;
+  }
+
+  std::vector<RemoveExpiredPublicCertificatesCall>&
+  remove_expired_public_certificates_calls() {
+    return remove_expired_public_certificates_calls_;
+  }
+
+  size_t num_clear_private_certificates_calls() {
+    return num_clear_private_certificates_calls_;
+  }
+
+  std::vector<ResultCallback>& clear_public_certificates_callbacks() {
+    return clear_public_certificates_callbacks_;
+  }
+
+ private:
   size_t num_clear_private_certificates_calls_ = 0;
   base::Optional<base::Time> next_private_certificate_expiration_time_;
   base::Optional<base::Time> next_public_certificate_expiration_time_;
@@ -166,8 +161,6 @@
   base::Optional<std::vector<NearbySharePrivateCertificate>>
       private_certificates_;
   std::vector<PublicCertificateCallback> get_public_certificates_callbacks_;
-  std::vector<std::vector<NearbySharePrivateCertificate>>
-      replace_private_certificates_calls_;
   std::vector<ReplacePublicCertificatesCall> replace_public_certificates_calls_;
   std::vector<AddPublicCertificatesCall> add_public_certificates_calls_;
   std::vector<RemoveExpiredPublicCertificatesCall>
diff --git a/chrome/browser/nearby_sharing/certificates/nearby_share_certificate_manager.cc b/chrome/browser/nearby_sharing/certificates/nearby_share_certificate_manager.cc
index 7805f47d..109c3d3 100644
--- a/chrome/browser/nearby_sharing/certificates/nearby_share_certificate_manager.cc
+++ b/chrome/browser/nearby_sharing/certificates/nearby_share_certificate_manager.cc
@@ -28,10 +28,9 @@
   OnStop();
 }
 
-void NearbyShareCertificateManager::NotifyPublicCertificatesDownloaded(
-    bool new_certs_added) {
+void NearbyShareCertificateManager::NotifyPublicCertificatesDownloaded() {
   for (auto& observer : observers_)
-    observer.OnPublicCertificatesDownloaded(new_certs_added);
+    observer.OnPublicCertificatesDownloaded();
 }
 
 void NearbyShareCertificateManager::NotifyPrivateCertificatesChanged() {
diff --git a/chrome/browser/nearby_sharing/certificates/nearby_share_certificate_manager.h b/chrome/browser/nearby_sharing/certificates/nearby_share_certificate_manager.h
index 35a11757..3284c700 100644
--- a/chrome/browser/nearby_sharing/certificates/nearby_share_certificate_manager.h
+++ b/chrome/browser/nearby_sharing/certificates/nearby_share_certificate_manager.h
@@ -30,7 +30,7 @@
  public:
   class Observer : public base::CheckedObserver {
    public:
-    virtual void OnPublicCertificatesDownloaded(bool new_certs_added) = 0;
+    virtual void OnPublicCertificatesDownloaded() = 0;
     virtual void OnPrivateCertificatesChanged() = 0;
   };
 
@@ -80,7 +80,7 @@
   virtual void OnStart() = 0;
   virtual void OnStop() = 0;
 
-  void NotifyPublicCertificatesDownloaded(bool new_certs_added);
+  void NotifyPublicCertificatesDownloaded();
   void NotifyPrivateCertificatesChanged();
 
  private:
diff --git a/chrome/browser/nearby_sharing/certificates/nearby_share_certificate_manager_impl.cc b/chrome/browser/nearby_sharing/certificates/nearby_share_certificate_manager_impl.cc
index d4af064..e153052 100644
--- a/chrome/browser/nearby_sharing/certificates/nearby_share_certificate_manager_impl.cc
+++ b/chrome/browser/nearby_sharing/certificates/nearby_share_certificate_manager_impl.cc
@@ -4,8 +4,12 @@
 
 #include "chrome/browser/nearby_sharing/certificates/nearby_share_certificate_manager_impl.h"
 
+#include <array>
+#include <string>
+
 #include "base/bind.h"
 #include "base/callback.h"
+#include "base/containers/flat_map.h"
 #include "base/memory/ptr_util.h"
 #include "base/notreached.h"
 #include "base/strings/string_number_conversions.h"
@@ -23,11 +27,18 @@
 #include "chrome/browser/nearby_sharing/scheduling/nearby_share_scheduler_factory.h"
 #include "components/leveldb_proto/public/proto_database_provider.h"
 #include "components/prefs/pref_service.h"
+#include "device/bluetooth/bluetooth_adapter.h"
+#include "device/bluetooth/bluetooth_adapter_factory.h"
+#include "device/bluetooth/public/cpp/bluetooth_address.h"
 
 namespace {
 
 const char kDeviceIdPrefix[] = "users/me/devices/";
 
+constexpr std::array<NearbyShareVisibility, 2> kVisibilities = {
+    NearbyShareVisibility::kAllContacts,
+    NearbyShareVisibility::kSelectedContacts};
+
 void TryDecryptPublicCertificates(
     const NearbyShareEncryptedMetadataKey& encrypted_metadata_key,
     NearbyShareCertificateManager::CertDecryptedCallback callback,
@@ -110,6 +121,31 @@
       pref_service_(pref_service),
       client_factory_(client_factory),
       clock_(clock),
+      private_certificate_expiration_scheduler_(
+          NearbyShareSchedulerFactory::CreateExpirationScheduler(
+              base::BindRepeating(&NearbyShareCertificateManagerImpl::
+                                      NextPrivateCertificateExpirationTime,
+                                  base::Unretained(this)),
+              /*retry_failures=*/true,
+              /*require_connectivity=*/false,
+              prefs::
+                  kNearbySharingSchedulerPrivateCertificateExpirationPrefName,
+              pref_service_,
+              base::BindRepeating(&NearbyShareCertificateManagerImpl::
+                                      OnPrivateCertificateExpiration,
+                                  base::Unretained(this)),
+              clock_)),
+      upload_local_device_certificates_scheduler_(
+          NearbyShareSchedulerFactory::CreateOnDemandScheduler(
+              /*retry_failures=*/true,
+              /*require_connectivity=*/true,
+              prefs::
+                  kNearbySharingSchedulerUploadLocalDeviceCertificatesPrefName,
+              pref_service_,
+              base::BindRepeating(&NearbyShareCertificateManagerImpl::
+                                      OnLocalDeviceCertificateUploadRequest,
+                                  base::Unretained(this)),
+              clock_)),
       download_public_certificates_scheduler_(
           NearbyShareSchedulerFactory::CreatePeriodicScheduler(
               kNearbySharePublicCertificateDownloadPeriod,
@@ -126,16 +162,16 @@
           pref_service_,
           proto_database_provider,
           profile_path)) {}
+
 NearbyShareCertificateManagerImpl::~NearbyShareCertificateManagerImpl() =
     default;
 
 NearbySharePrivateCertificate
 NearbyShareCertificateManagerImpl::GetValidPrivateCertificate(
     NearbyShareVisibility visibility) {
-  base::Optional<std::vector<NearbySharePrivateCertificate>> certs =
-      cert_store_->GetPrivateCertificates();
-  DCHECK(certs);
-  for (auto& cert : *certs) {
+  std::vector<NearbySharePrivateCertificate> certs =
+      *cert_store_->GetPrivateCertificates();
+  for (auto& cert : certs) {
     if (IsNearbyShareCertificateWithinValidityPeriod(
             clock_->Now(), cert.not_before(), cert.not_after(),
             /*use_public_certificate_tolerance=*/false) &&
@@ -171,6 +207,159 @@
   download_public_certificates_scheduler_->MakeImmediateRequest();
 }
 
+void NearbyShareCertificateManagerImpl::OnStart() {
+  private_certificate_expiration_scheduler_->Start();
+  upload_local_device_certificates_scheduler_->Start();
+  download_public_certificates_scheduler_->Start();
+}
+
+void NearbyShareCertificateManagerImpl::OnStop() {
+  private_certificate_expiration_scheduler_->Stop();
+  upload_local_device_certificates_scheduler_->Stop();
+  download_public_certificates_scheduler_->Stop();
+}
+
+base::Time
+NearbyShareCertificateManagerImpl::NextPrivateCertificateExpirationTime() {
+  // If no private certificates are present, return the minimum time to trigger
+  // an immediate refresh.
+  return cert_store_->NextPrivateCertificateExpirationTime().value_or(
+      base::Time::Min());
+}
+
+void NearbyShareCertificateManagerImpl::OnPrivateCertificateExpiration() {
+  NS_LOG(VERBOSE)
+      << __func__
+      << ": Private certificate expiration detected; refreshing certificates.";
+  base::Time now = clock_->Now();
+  base::flat_map<NearbyShareVisibility, size_t> num_valid_certs;
+  base::flat_map<NearbyShareVisibility, base::Time> latest_not_after;
+  for (NearbyShareVisibility visibility : kVisibilities) {
+    num_valid_certs[visibility] = 0;
+    latest_not_after[visibility] = now;
+  }
+
+  // Remove all expired certificates.
+  std::vector<NearbySharePrivateCertificate> old_certs =
+      *cert_store_->GetPrivateCertificates();
+  std::vector<NearbySharePrivateCertificate> new_certs;
+  for (const NearbySharePrivateCertificate& cert : old_certs) {
+    if (IsNearbyShareCertificateExpired(
+            now, cert.not_after(),
+            /*use_public_certificate_tolerance=*/false)) {
+      continue;
+    }
+    ++num_valid_certs[cert.visibility()];
+    latest_not_after[cert.visibility()] =
+        std::max(latest_not_after[cert.visibility()], cert.not_after());
+    new_certs.push_back(cert);
+  }
+
+  if (!old_certs.empty() && new_certs.size() == old_certs.size()) {
+    NS_LOG(VERBOSE) << __func__
+                    << ": All private certificates are still valid.";
+    private_certificate_expiration_scheduler_->HandleResult(/*success=*/true);
+    return;
+  }
+
+  device::BluetoothAdapterFactory::Get()->GetAdapter(base::BindOnce(
+      &NearbyShareCertificateManagerImpl::FinishPrivateCertificateRefresh,
+      weak_ptr_factory_.GetWeakPtr(), std::move(new_certs),
+      std::move(num_valid_certs), std::move(latest_not_after)));
+}
+
+void NearbyShareCertificateManagerImpl::FinishPrivateCertificateRefresh(
+    std::vector<NearbySharePrivateCertificate> new_certs,
+    base::flat_map<NearbyShareVisibility, size_t> num_valid_certs,
+    base::flat_map<NearbyShareVisibility, base::Time> latest_not_after,
+    scoped_refptr<device::BluetoothAdapter> bluetooth_adapter) {
+  nearbyshare::proto::EncryptedMetadata metadata;
+
+  base::Optional<std::string> device_name =
+      local_device_data_manager_->GetDeviceName();
+  if (!device_name) {
+    NS_LOG(WARNING)
+        << __func__
+        << ": Cannot create private certificates; missing device name.";
+    private_certificate_expiration_scheduler_->HandleResult(/*success=*/false);
+    return;
+  }
+  metadata.set_device_name(*device_name);
+
+  base::Optional<std::string> full_name =
+      local_device_data_manager_->GetFullName();
+  base::Optional<std::string> icon_url =
+      local_device_data_manager_->GetIconUrl();
+  if (full_name) {
+    metadata.set_full_name(*full_name);
+  }
+  if (icon_url) {
+    metadata.set_icon_url(*icon_url);
+  }
+
+  std::array<uint8_t, 6> bytes;
+  if (bluetooth_adapter &&
+      device::ParseBluetoothAddress(bluetooth_adapter->GetAddress(), bytes)) {
+    metadata.set_bluetooth_mac_address(std::string(bytes.begin(), bytes.end()));
+  } else {
+    NS_LOG(WARNING) << __func__
+                    << ": No valid Bluetooth MAC available during private "
+                    << "certificate creation.";
+    // TODO(https://crbug.com/1122641): Decide the best way to handle
+    // missing/invalid Bluetooth MAC addresses. Also, log a metric to track how
+    // often this happens.
+  }
+
+  NS_LOG(VERBOSE)
+      << __func__ << ": Creating "
+      << kNearbyShareNumPrivateCertificates -
+             num_valid_certs[NearbyShareVisibility::kAllContacts]
+      << " all-contacts visibility and "
+      << kNearbyShareNumPrivateCertificates -
+             num_valid_certs[NearbyShareVisibility::kSelectedContacts]
+      << " selected-contacts visibility private certificates.";
+  // Add new certificates if necessary. Each visibility should have
+  // kNearbyShareNumPrivateCertificates.
+  for (NearbyShareVisibility visibility : kVisibilities) {
+    while (num_valid_certs[visibility] < kNearbyShareNumPrivateCertificates) {
+      new_certs.emplace_back(
+          visibility, /*not_before=*/latest_not_after[visibility], metadata);
+      ++num_valid_certs[visibility];
+      latest_not_after[visibility] = new_certs.back().not_after();
+    }
+  }
+
+  cert_store_->ReplacePrivateCertificates(new_certs);
+  NotifyPrivateCertificatesChanged();
+  private_certificate_expiration_scheduler_->HandleResult(/*success=*/true);
+
+  upload_local_device_certificates_scheduler_->MakeImmediateRequest();
+}
+
+void NearbyShareCertificateManagerImpl::
+    OnLocalDeviceCertificateUploadRequest() {
+  std::vector<nearbyshare::proto::PublicCertificate> public_certs;
+  std::vector<NearbySharePrivateCertificate> private_certs =
+      *cert_store_->GetPrivateCertificates();
+  for (const NearbySharePrivateCertificate& private_cert : private_certs) {
+    public_certs.push_back(*private_cert.ToPublicCertificate());
+  }
+
+  NS_LOG(VERBOSE) << __func__ << ": Uploading local device certificates.";
+  local_device_data_manager_->UploadCertificates(
+      std::move(public_certs),
+      base::BindOnce(&NearbyShareCertificateManagerImpl::
+                         OnLocalDeviceCertificateUploadFinished,
+                     weak_ptr_factory_.GetWeakPtr()));
+}
+
+void NearbyShareCertificateManagerImpl::OnLocalDeviceCertificateUploadFinished(
+    bool success) {
+  NS_LOG(VERBOSE) << __func__ << ": Upload of local device certificates "
+                  << (success ? "succeeded" : "failed.");
+  upload_local_device_certificates_scheduler_->HandleResult(success);
+}
+
 void NearbyShareCertificateManagerImpl::OnDownloadPublicCertificatesRequest(
     base::Optional<std::string> page_token) {
   DCHECK(!client_);
@@ -242,6 +431,7 @@
     NS_LOG(VERBOSE)
         << __func__
         << ": Public certificates successfully downloaded and stored.";
+    NotifyPublicCertificatesDownloaded();
   } else if (http_result == NearbyShareHttpResult::kSuccess) {
     NS_LOG(ERROR) << __func__ << ": Public certificates not stored.";
   } else {
@@ -252,11 +442,3 @@
   RecordResultMetrics(http_result);
   download_public_certificates_scheduler_->HandleResult(success);
 }
-
-void NearbyShareCertificateManagerImpl::OnStart() {
-  download_public_certificates_scheduler_->Start();
-}
-
-void NearbyShareCertificateManagerImpl::OnStop() {
-  download_public_certificates_scheduler_->Stop();
-}
diff --git a/chrome/browser/nearby_sharing/certificates/nearby_share_certificate_manager_impl.h b/chrome/browser/nearby_sharing/certificates/nearby_share_certificate_manager_impl.h
index e21c952..006f2d3 100644
--- a/chrome/browser/nearby_sharing/certificates/nearby_share_certificate_manager_impl.h
+++ b/chrome/browser/nearby_sharing/certificates/nearby_share_certificate_manager_impl.h
@@ -10,6 +10,8 @@
 
 #include "base/containers/span.h"
 #include "base/files/file_path.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/memory/weak_ptr.h"
 #include "base/optional.h"
 #include "base/time/clock.h"
 #include "base/time/default_clock.h"
@@ -27,6 +29,10 @@
 class NearbyShareScheduler;
 class PrefService;
 
+namespace device {
+class BluetoothAdapter;
+}  // namespace device
+
 namespace leveldb_proto {
 class ProtoDatabaseProvider;
 }  // namespace leveldb_proto
@@ -37,7 +43,16 @@
 }  // namespace proto
 }  // namespace nearbyshare
 
-// TODO(nohle): Add description after class is fully implemented.
+// An implementation of the NearbyShareCertificateManager that handles
+//   1) creating, storing, and uploading local device certificates, as well as
+//      removing expired/revoked local device certificates;
+//   2) downloading, storing, and decrypting public certificates from trusted
+//      contacts, as well as removing expired public certificates.
+//
+// TODO(https://crbug.com/1121443): Add the following if we remove
+// GetValidPrivateCertificate() and perform all private certificate crypto
+// operations internally: "This implementation also provides the high-level
+// interface for performing cryptographic operations related to certificates."
 class NearbyShareCertificateManagerImpl : public NearbyShareCertificateManager {
  public:
   class Factory {
@@ -89,6 +104,20 @@
   void OnStart() override;
   void OnStop() override;
 
+  // Removes expired privates certificates, ensures that at least
+  // kNearbyShareNumPrivateCertificates are present for each visibility with
+  // contiguous validity periods, and uploads any changes to the Nearby Share
+  // server.
+  base::Time NextPrivateCertificateExpirationTime();
+  void OnPrivateCertificateExpiration();
+  void FinishPrivateCertificateRefresh(
+      std::vector<NearbySharePrivateCertificate> new_certs,
+      base::flat_map<NearbyShareVisibility, size_t> num_valid_certs,
+      base::flat_map<NearbyShareVisibility, base::Time> latest_not_after,
+      scoped_refptr<device::BluetoothAdapter> bluetooth_adapter);
+  void OnLocalDeviceCertificateUploadRequest();
+  void OnLocalDeviceCertificateUploadFinished(bool success);
+
   void OnDownloadPublicCertificatesRequest(
       base::Optional<std::string> page_token);
   void OnRpcSuccess(
@@ -103,9 +132,15 @@
   PrefService* pref_service_ = nullptr;
   NearbyShareClientFactory* client_factory_ = nullptr;
   base::Clock* clock_ = nullptr;
+  std::unique_ptr<NearbyShareScheduler>
+      private_certificate_expiration_scheduler_;
+  std::unique_ptr<NearbyShareScheduler>
+      upload_local_device_certificates_scheduler_;
   std::unique_ptr<NearbyShareScheduler> download_public_certificates_scheduler_;
   std::unique_ptr<NearbyShareCertificateStorage> cert_store_;
   std::unique_ptr<NearbyShareClient> client_;
+  base::WeakPtrFactory<NearbyShareCertificateManagerImpl> weak_ptr_factory_{
+      this};
 };
 
 #endif  // CHROME_BROWSER_NEARBY_SHARING_CERTIFICATES_NEARBY_SHARE_CERTIFICATE_MANAGER_IMPL_H_
diff --git a/chrome/browser/nearby_sharing/certificates/nearby_share_certificate_manager_impl_unittest.cc b/chrome/browser/nearby_sharing/certificates/nearby_share_certificate_manager_impl_unittest.cc
index fc3ec32..2971811 100644
--- a/chrome/browser/nearby_sharing/certificates/nearby_share_certificate_manager_impl_unittest.cc
+++ b/chrome/browser/nearby_sharing/certificates/nearby_share_certificate_manager_impl_unittest.cc
@@ -8,6 +8,7 @@
 #include "base/time/time.h"
 #include "chrome/browser/nearby_sharing/certificates/constants.h"
 #include "chrome/browser/nearby_sharing/certificates/fake_nearby_share_certificate_storage.h"
+#include "chrome/browser/nearby_sharing/certificates/nearby_share_certificate_manager.h"
 #include "chrome/browser/nearby_sharing/certificates/test_util.h"
 #include "chrome/browser/nearby_sharing/client/fake_nearby_share_client.h"
 #include "chrome/browser/nearby_sharing/common/nearby_share_prefs.h"
@@ -15,6 +16,8 @@
 #include "chrome/browser/nearby_sharing/scheduling/fake_nearby_share_scheduler_factory.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/testing_pref_service.h"
+#include "device/bluetooth/bluetooth_adapter_factory.h"
+#include "device/bluetooth/test/mock_bluetooth_adapter.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace {
@@ -37,11 +40,14 @@
 
 }  // namespace
 
-class NearbyShareCertificateManagerImplTest : public ::testing::Test {
+class NearbyShareCertificateManagerImplTest
+    : public ::testing::Test,
+      public NearbyShareCertificateManager::Observer {
  public:
   NearbyShareCertificateManagerImplTest() {
     local_device_data_manager_ =
         std::make_unique<FakeNearbyShareLocalDeviceDataManager>();
+    local_device_data_manager_->SetId(kDeviceId);
 
     pref_service_ = std::make_unique<TestingPrefServiceSimple>();
     pref_service_->registry()->RegisterDictionaryPref(
@@ -55,8 +61,21 @@
         local_device_data_manager_.get(), pref_service_.get(),
         /*proto_database_provider=*/nullptr, base::FilePath(), &client_factory_,
         &clock_);
+    cert_man_->AddObserver(this);
 
-    scheduler_ =
+    private_cert_exp_scheduler_ =
+        scheduler_factory_.pref_name_to_expiration_instance()
+            .find(
+                prefs::
+                    kNearbySharingSchedulerPrivateCertificateExpirationPrefName)
+            ->second.fake_scheduler;
+    upload_scheduler_ =
+        scheduler_factory_.pref_name_to_on_demand_instance()
+            .find(
+                prefs::
+                    kNearbySharingSchedulerUploadLocalDeviceCertificatesPrefName)
+            ->second.fake_scheduler;
+    download_scheduler_ =
         scheduler_factory_.pref_name_to_periodic_instance()
             .find(prefs::
                       kNearbySharingSchedulerDownloadPublicCertificatesPrefName)
@@ -65,15 +84,35 @@
 
     PopulatePrivateCertificates();
     PopulatePublicCertificates();
+
+    mock_adapter_ =
+        base::MakeRefCounted<testing::NiceMock<device::MockBluetoothAdapter>>();
+    ON_CALL(*mock_adapter_, GetAddress()).WillByDefault([this] {
+      return bluetooth_mac_address_;
+    });
+    device::BluetoothAdapterFactory::SetAdapterForTesting(mock_adapter_);
   }
 
   ~NearbyShareCertificateManagerImplTest() override = default;
 
   void TearDown() override {
+    cert_man_->RemoveObserver(this);
     NearbyShareSchedulerFactory::SetFactoryForTesting(nullptr);
     NearbyShareCertificateStorageImpl::Factory::SetFactoryForTesting(nullptr);
   }
 
+  void SetBluetoothMacAddress(const std::string& bluetooth_mac_address) {
+    bluetooth_mac_address_ = bluetooth_mac_address;
+  }
+
+  // NearbyShareCertificateManager::Observer:
+  void OnPublicCertificatesDownloaded() override {
+    ++num_public_certs_downloaded_notifications_;
+  }
+  void OnPrivateCertificatesChanged() override {
+    ++num_private_certs_changed_notifications_;
+  }
+
  protected:
   void GetPublicCertificatesCallback(
       bool success,
@@ -87,22 +126,110 @@
     std::move(callback).Run(success, std::move(pub_certs));
   }
 
+  void HandlePrivateCertificateRefresh(bool expect_private_cert_refresh,
+                                       bool expected_success) {
+    if (expect_private_cert_refresh)
+      private_cert_exp_scheduler_->InvokeRequestCallback();
+
+    EXPECT_EQ(expect_private_cert_refresh ? 1u : 0u,
+              private_cert_exp_scheduler_->handled_results().size());
+    if (expect_private_cert_refresh) {
+      EXPECT_EQ(expected_success,
+                private_cert_exp_scheduler_->handled_results().back());
+    }
+    EXPECT_EQ(expect_private_cert_refresh && expected_success ? 1u : 0u,
+              num_private_certs_changed_notifications_);
+    EXPECT_EQ(expect_private_cert_refresh && expected_success ? 1u : 0u,
+              upload_scheduler_->num_immediate_requests());
+  }
+
+  void VerifyPrivateCertificates(
+      const nearbyshare::proto::EncryptedMetadata& expected_metadata) {
+    // Expect a full set of certificates for both all-contacts and
+    // selected-contacts
+    std::vector<NearbySharePrivateCertificate> certs =
+        *cert_store_->GetPrivateCertificates();
+    EXPECT_EQ(2 * kNearbyShareNumPrivateCertificates, certs.size());
+
+    base::Time min_not_before_all_contacts = base::Time::Max();
+    base::Time min_not_before_selected_contacts = base::Time::Max();
+    base::Time max_not_after_all_contacts = base::Time::Min();
+    base::Time max_not_after_selected_contacts = base::Time::Min();
+    for (const auto& cert : certs) {
+      EXPECT_EQ(cert.not_after() - cert.not_before(),
+                kNearbyShareCertificateValidityPeriod);
+      switch (cert.visibility()) {
+        case NearbyShareVisibility::kAllContacts:
+          min_not_before_all_contacts =
+              std::min(min_not_before_all_contacts, cert.not_before());
+          max_not_after_all_contacts =
+              std::max(max_not_after_all_contacts, cert.not_after());
+          break;
+        case NearbyShareVisibility::kSelectedContacts:
+          min_not_before_selected_contacts =
+              std::min(min_not_before_selected_contacts, cert.not_before());
+          max_not_after_selected_contacts =
+              std::max(max_not_after_selected_contacts, cert.not_after());
+          break;
+        default:
+          NOTREACHED();
+          break;
+      }
+
+      // Verify metadata.
+      EXPECT_EQ(expected_metadata.SerializeAsString(),
+                cert.unencrypted_metadata().SerializeAsString());
+    }
+
+    // Verify contiguous validity periods
+    EXPECT_EQ(kNearbyShareNumPrivateCertificates *
+                  kNearbyShareCertificateValidityPeriod,
+              max_not_after_all_contacts - min_not_before_all_contacts);
+    EXPECT_EQ(
+        kNearbyShareNumPrivateCertificates *
+            kNearbyShareCertificateValidityPeriod,
+        max_not_after_selected_contacts - min_not_before_selected_contacts);
+  }
+
+  void RunUpload(bool success) {
+    size_t initial_num_upload_calls =
+        local_device_data_manager_->upload_certificates_calls().size();
+    upload_scheduler_->InvokeRequestCallback();
+    EXPECT_EQ(initial_num_upload_calls + 1,
+              local_device_data_manager_->upload_certificates_calls().size());
+
+    EXPECT_EQ(2 * kNearbyShareNumPrivateCertificates,
+              local_device_data_manager_->upload_certificates_calls()
+                  .back()
+                  .certificates.size());
+
+    size_t initial_num_handled_results =
+        upload_scheduler_->handled_results().size();
+    std::move(
+        local_device_data_manager_->upload_certificates_calls().back().callback)
+        .Run(success);
+    EXPECT_EQ(initial_num_handled_results + 1,
+              upload_scheduler_->handled_results().size());
+    EXPECT_EQ(success, upload_scheduler_->handled_results().back());
+  }
+
   // Test downloading public certificates with or without errors. The RPC is
-  // paginated, and |num_pages| will be simulated. If |rpc_fail| is not nullopt
-  // or store_fail is true, then those respective failures will be simulated on
-  // the last page.
+  // paginated, and |num_pages| will be simulated. If |rpc_fail| is not
+  // nullopt or store_fail is true, then those respective failures will be
+  // simulated on the last page.
   void DownloadPublicCertificatesFlow(
       size_t num_pages,
       base::Optional<NearbyShareHttpError> rpc_fail,
       bool store_fail) {
-    size_t prev_num_results = scheduler_->handled_results().size();
+    size_t prev_num_results = download_scheduler_->handled_results().size();
     cert_store_->SetPublicCertificateIds(kPublicCertificateIds);
-    local_device_data_manager_->SetId(kDeviceId);
 
     cert_man_->Start();
-    scheduler_->InvokeRequestCallback();
+    download_scheduler_->InvokeRequestCallback();
     cert_man_->Stop();
 
+    size_t initial_num_notifications =
+        num_public_certs_downloaded_notifications_;
     std::string page_token;
     for (size_t page_number = 0; page_number < num_pages; ++page_number) {
       bool last_page = page_number == num_pages - 1;
@@ -125,11 +252,16 @@
 
       auto& add_cert_call = cert_store_->add_public_certificates_calls().back();
       CheckStorageAddCertificates(add_cert_call);
+
       std::move(add_cert_call.callback)
           .Run(/*success=*/!(last_page && store_fail));
     }
-    ASSERT_EQ(scheduler_->handled_results().size(), prev_num_results + 1);
-    EXPECT_EQ(scheduler_->handled_results().back(), !rpc_fail && !store_fail);
+    ASSERT_EQ(download_scheduler_->handled_results().size(),
+              prev_num_results + 1);
+    bool success = !rpc_fail && !store_fail;
+    EXPECT_EQ(download_scheduler_->handled_results().back(), success);
+    EXPECT_EQ(initial_num_notifications + (success ? 1u : 0u),
+              num_public_certs_downloaded_notifications_);
   }
 
   void CheckRpcRequest(
@@ -172,7 +304,7 @@
 
   void PopulatePrivateCertificates() {
     private_certificates_.clear();
-    auto& metadata = GetNearbyShareTestMetadata();
+    const auto& metadata = GetNearbyShareTestMetadata();
     for (auto visibility : {NearbyShareVisibility::kAllContacts,
                             NearbyShareVisibility::kSelectedContacts}) {
       private_certificates_.emplace_back(visibility, t0, metadata);
@@ -201,7 +333,13 @@
   }
 
   FakeNearbyShareCertificateStorage* cert_store_;
-  FakeNearbyShareScheduler* scheduler_;
+  FakeNearbyShareScheduler* private_cert_exp_scheduler_;
+  FakeNearbyShareScheduler* upload_scheduler_;
+  FakeNearbyShareScheduler* download_scheduler_;
+  std::string bluetooth_mac_address_ = kTestUnparsedBluetoothMacAddress;
+  scoped_refptr<testing::NiceMock<device::MockBluetoothAdapter>> mock_adapter_;
+  size_t num_public_certs_downloaded_notifications_ = 0;
+  size_t num_private_certs_changed_notifications_ = 0;
   std::vector<NearbySharePrivateCertificate> private_certificates_;
   std::vector<nearbyshare::proto::PublicCertificate> public_certificates_;
   std::vector<NearbyShareEncryptedMetadataKey> metadata_encryption_keys_;
@@ -277,9 +415,10 @@
 
 TEST_F(NearbyShareCertificateManagerImplTest,
        DownloadPublicCertificatesImmediateRequest) {
-  size_t prev_num_requests = scheduler_->num_immediate_requests();
+  size_t prev_num_requests = download_scheduler_->num_immediate_requests();
   cert_man_->DownloadPublicCertificates();
-  EXPECT_EQ(scheduler_->num_immediate_requests(), prev_num_requests + 1);
+  EXPECT_EQ(download_scheduler_->num_immediate_requests(),
+            prev_num_requests + 1);
 }
 
 TEST_F(NearbyShareCertificateManagerImplTest,
@@ -300,3 +439,156 @@
   DownloadPublicCertificatesFlow(/*num_pages=*/2, /*rpc_fail=*/base::nullopt,
                                  /*store_fail=*/true);
 }
+
+TEST_F(NearbyShareCertificateManagerImplTest,
+       RefreshPrivateCertificates_ValidCertificates) {
+  clock_.SetNow(t0);
+  cert_store_->SetPrivateCertificates(private_certificates_);
+
+  local_device_data_manager_->SetDeviceName(
+      GetNearbyShareTestMetadata().device_name());
+  local_device_data_manager_->SetFullName(
+      GetNearbyShareTestMetadata().full_name());
+  local_device_data_manager_->SetIconUrl(
+      GetNearbyShareTestMetadata().icon_url());
+  SetBluetoothMacAddress(kTestUnparsedBluetoothMacAddress);
+
+  cert_man_->Start();
+  HandlePrivateCertificateRefresh(/*expect_private_cert_refresh=*/false,
+                                  /*expected_success=*/true);
+  VerifyPrivateCertificates(/*expected_metadata=*/GetNearbyShareTestMetadata());
+}
+
+TEST_F(NearbyShareCertificateManagerImplTest,
+       RefreshPrivateCertificates_NoCertificates_UploadSuccess) {
+  clock_.SetNow(t0);
+  cert_store_->SetPrivateCertificates(
+      std::vector<NearbySharePrivateCertificate>());
+
+  local_device_data_manager_->SetDeviceName(
+      GetNearbyShareTestMetadata().device_name());
+  local_device_data_manager_->SetFullName(
+      GetNearbyShareTestMetadata().full_name());
+  local_device_data_manager_->SetIconUrl(
+      GetNearbyShareTestMetadata().icon_url());
+  SetBluetoothMacAddress(kTestUnparsedBluetoothMacAddress);
+
+  cert_man_->Start();
+  HandlePrivateCertificateRefresh(/*expect_private_cert_refresh=*/true,
+                                  /*expected_success=*/true);
+  RunUpload(/*success=*/true);
+  VerifyPrivateCertificates(/*expected_metadata=*/GetNearbyShareTestMetadata());
+}
+
+TEST_F(NearbyShareCertificateManagerImplTest,
+       RefreshPrivateCertificates_NoCertificates_UploadFailure) {
+  clock_.SetNow(t0);
+  cert_store_->SetPrivateCertificates(
+      std::vector<NearbySharePrivateCertificate>());
+
+  local_device_data_manager_->SetDeviceName(
+      GetNearbyShareTestMetadata().device_name());
+  local_device_data_manager_->SetFullName(
+      GetNearbyShareTestMetadata().full_name());
+  local_device_data_manager_->SetIconUrl(
+      GetNearbyShareTestMetadata().icon_url());
+  SetBluetoothMacAddress(kTestUnparsedBluetoothMacAddress);
+
+  cert_man_->Start();
+  HandlePrivateCertificateRefresh(/*expect_private_cert_refresh=*/true,
+                                  /*expected_success=*/true);
+  RunUpload(/*success=*/false);
+  VerifyPrivateCertificates(/*expected_metadata=*/GetNearbyShareTestMetadata());
+}
+
+TEST_F(NearbyShareCertificateManagerImplTest,
+       RefreshPrivateCertificates_ExpiredCertificate) {
+  // First certificates are expired;
+  clock_.SetNow(t0 + kNearbyShareCertificateValidityPeriod * 1.5);
+  cert_store_->SetPrivateCertificates(private_certificates_);
+
+  local_device_data_manager_->SetDeviceName(
+      GetNearbyShareTestMetadata().device_name());
+  local_device_data_manager_->SetFullName(
+      GetNearbyShareTestMetadata().full_name());
+  local_device_data_manager_->SetIconUrl(
+      GetNearbyShareTestMetadata().icon_url());
+  SetBluetoothMacAddress(kTestUnparsedBluetoothMacAddress);
+
+  cert_man_->Start();
+  HandlePrivateCertificateRefresh(/*expect_private_cert_refresh=*/true,
+                                  /*expected_success=*/true);
+  RunUpload(/*success=*/true);
+  VerifyPrivateCertificates(/*expected_metadata=*/GetNearbyShareTestMetadata());
+}
+
+TEST_F(NearbyShareCertificateManagerImplTest,
+       RefreshPrivateCertificates_InvalidDeviceName) {
+  clock_.SetNow(t0);
+  cert_store_->SetPrivateCertificates(
+      std::vector<NearbySharePrivateCertificate>());
+
+  // Device name is missing in local device data manager.
+  local_device_data_manager_->SetFullName(
+      GetNearbyShareTestMetadata().full_name());
+  local_device_data_manager_->SetIconUrl(
+      GetNearbyShareTestMetadata().icon_url());
+  SetBluetoothMacAddress(kTestUnparsedBluetoothMacAddress);
+
+  cert_man_->Start();
+
+  // Expect failure because a device name is required.
+  HandlePrivateCertificateRefresh(/*expect_private_cert_refresh=*/true,
+                                  /*expected_success=*/false);
+}
+
+TEST_F(NearbyShareCertificateManagerImplTest,
+       RefreshPrivateCertificates_InvalidBluetoothMacAddress) {
+  clock_.SetNow(t0);
+  cert_store_->SetPrivateCertificates(
+      std::vector<NearbySharePrivateCertificate>());
+
+  // The bluetooth adapter returns an invalid Bluetooth MAC address.
+  local_device_data_manager_->SetDeviceName(
+      GetNearbyShareTestMetadata().device_name());
+  local_device_data_manager_->SetFullName(
+      GetNearbyShareTestMetadata().full_name());
+  local_device_data_manager_->SetIconUrl(
+      GetNearbyShareTestMetadata().icon_url());
+  SetBluetoothMacAddress("invalid_mac_address");
+
+  cert_man_->Start();
+  HandlePrivateCertificateRefresh(/*expect_private_cert_refresh=*/true,
+                                  /*expected_success=*/true);
+  RunUpload(/*success=*/true);
+
+  // The MAC address is not set.
+  nearbyshare::proto::EncryptedMetadata metadata = GetNearbyShareTestMetadata();
+  metadata.clear_bluetooth_mac_address();
+
+  VerifyPrivateCertificates(/*expected_metadata=*/metadata);
+}
+
+TEST_F(NearbyShareCertificateManagerImplTest,
+       RefreshPrivateCertificates_MissingFullNameAndIconUrl) {
+  clock_.SetNow(t0);
+  cert_store_->SetPrivateCertificates(
+      std::vector<NearbySharePrivateCertificate>());
+
+  // Full name and icon URL are missing in local device data manager.
+  local_device_data_manager_->SetDeviceName(
+      GetNearbyShareTestMetadata().device_name());
+  SetBluetoothMacAddress(kTestUnparsedBluetoothMacAddress);
+
+  cert_man_->Start();
+  HandlePrivateCertificateRefresh(/*expect_private_cert_refresh=*/true,
+                                  /*expected_success=*/true);
+  RunUpload(/*success=*/true);
+
+  // The full name and icon URL are not set.
+  nearbyshare::proto::EncryptedMetadata metadata = GetNearbyShareTestMetadata();
+  metadata.clear_full_name();
+  metadata.clear_icon_url();
+
+  VerifyPrivateCertificates(/*expected_metadata=*/metadata);
+}
diff --git a/chrome/browser/nearby_sharing/certificates/nearby_share_private_certificate.cc b/chrome/browser/nearby_sharing/certificates/nearby_share_private_certificate.cc
index 15bfa84..6dafde4b 100644
--- a/chrome/browser/nearby_sharing/certificates/nearby_share_private_certificate.cc
+++ b/chrome/browser/nearby_sharing/certificates/nearby_share_private_certificate.cc
@@ -248,7 +248,7 @@
 }
 
 base::Optional<nearbyshare::proto::PublicCertificate>
-NearbySharePrivateCertificate::ToPublicCertificate() {
+NearbySharePrivateCertificate::ToPublicCertificate() const {
   std::vector<uint8_t> public_key;
   if (!key_pair_->ExportPublicKey(&public_key)) {
     NS_LOG(ERROR) << "Failed to export public key.";
@@ -423,7 +423,7 @@
 }
 
 base::Optional<std::vector<uint8_t>>
-NearbySharePrivateCertificate::EncryptMetadata() {
+NearbySharePrivateCertificate::EncryptMetadata() const {
   // Init() keeps a reference to the input key, so that reference must outlive
   // the lifetime of |aead|.
   std::vector<uint8_t> derived_key = DeriveNearbyShareKey(
diff --git a/chrome/browser/nearby_sharing/certificates/nearby_share_private_certificate.h b/chrome/browser/nearby_sharing/certificates/nearby_share_private_certificate.h
index 7d05a18c..9e57fbf6 100644
--- a/chrome/browser/nearby_sharing/certificates/nearby_share_private_certificate.h
+++ b/chrome/browser/nearby_sharing/certificates/nearby_share_private_certificate.h
@@ -96,7 +96,8 @@
   // Converts this private certificate to a public certificate proto that can be
   // shared with select contacts. Returns base::nullopt if the conversion was
   // unsuccessful.
-  base::Optional<nearbyshare::proto::PublicCertificate> ToPublicCertificate();
+  base::Optional<nearbyshare::proto::PublicCertificate> ToPublicCertificate()
+      const;
 
   // Converts this private certificate to a dictionary value for storage
   // in Prefs.
@@ -120,7 +121,7 @@
 
   // Encrypts |unencrypted_metadata_| with the |metadata_encryption_key_|, using
   // the |secret_key_| as salt.
-  base::Optional<std::vector<uint8_t>> EncryptMetadata();
+  base::Optional<std::vector<uint8_t>> EncryptMetadata() const;
 
   // Specifies which contacts can receive the public certificate corresponding
   // to this private certificate.
diff --git a/chrome/browser/nearby_sharing/certificates/test_util.cc b/chrome/browser/nearby_sharing/certificates/test_util.cc
index b664e6c..b417380 100644
--- a/chrome/browser/nearby_sharing/certificates/test_util.cc
+++ b/chrome/browser/nearby_sharing/certificates/test_util.cc
@@ -10,6 +10,7 @@
 #include "chrome/browser/nearby_sharing/certificates/constants.h"
 #include "chrome/browser/nearby_sharing/certificates/nearby_share_visibility.h"
 #include "chrome/browser/nearby_sharing/proto/timestamp.pb.h"
+#include "device/bluetooth/public/cpp/bluetooth_address.h"
 
 namespace {
 
@@ -80,12 +81,11 @@
 const uint8_t kTestEncryptedMetadata[] = {
     0x4d, 0x59, 0x5d, 0xb6, 0xac, 0x70, 0x00, 0x8f, 0x32, 0x9d, 0x0d, 0xcf,
     0xc3, 0x8b, 0x01, 0x19, 0x1d, 0xad, 0x2e, 0xb4, 0x62, 0xec, 0xf3, 0xa5,
-    0xe4, 0x89, 0x51, 0x37, 0x0d, 0x78, 0xad, 0x9d, 0x2e, 0xe5, 0x99, 0xc6,
-    0xdb, 0x14, 0x65, 0x50, 0xf9, 0x25, 0xf3, 0x34, 0xec, 0xea, 0x55, 0xda,
-    0x3d, 0x97, 0x08, 0xf1, 0x0b, 0x67, 0x9b, 0x2b, 0x54,
+    0xe4, 0x89, 0x51, 0x37, 0x0d, 0x78, 0xad, 0x9d, 0x2e, 0xe5, 0x99, 0xd5,
+    0xf7, 0x1d, 0x71, 0x47, 0xef, 0x33,
     // tag
-    0xbf, 0x2e, 0x98, 0x10, 0x4f, 0x0b, 0xde, 0x17, 0xd0, 0xf6, 0xcf, 0xfd,
-    0x43, 0xe5, 0x46, 0xc2};
+    0x63, 0x47, 0xae, 0xb0, 0xdf, 0x67, 0x07, 0x16, 0x70, 0x97, 0x3d, 0x8f,
+    0xc8, 0xe6, 0x61, 0xc0};
 
 // Plaintext "sample" (from RFC 6979 A.2.5).
 const uint8_t kTestPayloadToSign[] = {0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65};
@@ -122,8 +122,11 @@
 
 }  // namespace
 
+// Do not change. Values align with kTestEncryptedMetadata.
+const char kTestDeviceName[] = "device_name";
 const char kTestMetadataFullName[] = "full_name";
 const char kTestMetadataIconUrl[] = "icon_url";
+const char kTestUnparsedBluetoothMacAddress[] = "4E:65:61:72:62:79";
 
 std::unique_ptr<crypto::ECPrivateKey> GetNearbyShareTestP256KeyPair() {
   return crypto::ECPrivateKey::CreateFromPrivateKeyInfo(kTestPrivateKeyBytes);
@@ -193,11 +196,14 @@
 const nearbyshare::proto::EncryptedMetadata& GetNearbyShareTestMetadata() {
   static const base::NoDestructor<nearbyshare::proto::EncryptedMetadata>
       metadata([] {
+        std::array<uint8_t, 6> bytes;
+        device::ParseBluetoothAddress(kTestUnparsedBluetoothMacAddress, bytes);
+
         nearbyshare::proto::EncryptedMetadata metadata;
-        metadata.set_device_name("device_name");
+        metadata.set_device_name(kTestDeviceName);
         metadata.set_full_name(kTestMetadataFullName);
         metadata.set_icon_url(kTestMetadataIconUrl);
-        metadata.set_bluetooth_mac_address("bluetooth_mac_address");
+        metadata.set_bluetooth_mac_address(bytes.data(), 6u);
         return metadata;
       }());
   return *metadata;
diff --git a/chrome/browser/nearby_sharing/certificates/test_util.h b/chrome/browser/nearby_sharing/certificates/test_util.h
index 93fae80..e8a13ff 100644
--- a/chrome/browser/nearby_sharing/certificates/test_util.h
+++ b/chrome/browser/nearby_sharing/certificates/test_util.h
@@ -20,6 +20,9 @@
 extern const char kTestMetadataFullName[];
 extern const char kTestMetadataIconUrl[];
 
+// Test Bluetooth MAC address in the format "XX:XX:XX:XX:XX:XX".
+extern const char kTestUnparsedBluetoothMacAddress[];
+
 std::unique_ptr<crypto::ECPrivateKey> GetNearbyShareTestP256KeyPair();
 const std::vector<uint8_t>& GetNearbyShareTestP256PublicKey();
 
diff --git a/chrome/browser/nearby_sharing/common/nearby_share_prefs.cc b/chrome/browser/nearby_sharing/common/nearby_share_prefs.cc
index 71dc325..156391c 100644
--- a/chrome/browser/nearby_sharing/common/nearby_share_prefs.cc
+++ b/chrome/browser/nearby_sharing/common/nearby_share_prefs.cc
@@ -28,18 +28,24 @@
 const char kNearbySharingIconUrlPrefName[] = "nearby_sharing.icon_url";
 const char kNearbySharingOnboardingDismissedTimePrefName[] =
     "nearby_sharing.onboarding_dismissed_time";
+const char kNearbySharingPublicCertificateExpirationDictPrefName[] =
+    "nearbyshare.public_certificate_expiration_dict";
+const char kNearbySharingPrivateCertificateListPrefName[] =
+    "nearbyshare.private_certificate_list";
 const char kNearbySharingSchedulerContactDownloadPrefName[] =
     "nearby_sharing.scheduler.contact_download";
 const char kNearbySharingSchedulerContactUploadPrefName[] =
     "nearby_sharing.scheduler.contact_upload";
 const char kNearbySharingSchedulerDownloadDeviceDataPrefName[] =
     "nearby_sharing.scheduler.download_device_data";
-const char kNearbySharingPublicCertificateExpirationDictPrefName[] =
-    "nearbyshare.public_certificate_expiration_dict";
-const char kNearbySharingPrivateCertificateListPrefName[] =
-    "nearbyshare.private_certificate_list";
 const char kNearbySharingSchedulerDownloadPublicCertificatesPrefName[] =
     "nearby_sharing.scheduler.download_public_certificates";
+const char kNearbySharingSchedulerPrivateCertificateExpirationPrefName[] =
+    "nearby_sharing.scheduler.private_certificate_expiration";
+const char kNearbySharingSchedulerUploadDeviceNamePrefName[] =
+    "nearby_sharing.scheduler.upload_device_name";
+const char kNearbySharingSchedulerUploadLocalDeviceCertificatesPrefName[] =
+    "nearby_sharing.scheduler.upload_local_device_certificates";
 
 }  // namespace prefs
 
@@ -65,12 +71,6 @@
                                /*default_value=*/std::string());
   registry->RegisterStringPref(prefs::kNearbySharingIconUrlPrefName,
                                /*default_value=*/std::string());
-  registry->RegisterDictionaryPref(
-      prefs::kNearbySharingSchedulerContactDownloadPrefName);
-  registry->RegisterDictionaryPref(
-      prefs::kNearbySharingSchedulerContactUploadPrefName);
-  registry->RegisterDictionaryPref(
-      prefs::kNearbySharingSchedulerDownloadDeviceDataPrefName);
   registry->RegisterTimePref(
       prefs::kNearbySharingOnboardingDismissedTimePrefName,
       /*default_value=*/base::Time());
@@ -79,7 +79,19 @@
   registry->RegisterListPref(
       prefs::kNearbySharingPrivateCertificateListPrefName);
   registry->RegisterDictionaryPref(
+      prefs::kNearbySharingSchedulerContactDownloadPrefName);
+  registry->RegisterDictionaryPref(
+      prefs::kNearbySharingSchedulerContactUploadPrefName);
+  registry->RegisterDictionaryPref(
+      prefs::kNearbySharingSchedulerDownloadDeviceDataPrefName);
+  registry->RegisterDictionaryPref(
       prefs::kNearbySharingSchedulerDownloadPublicCertificatesPrefName);
+  registry->RegisterDictionaryPref(
+      prefs::kNearbySharingSchedulerPrivateCertificateExpirationPrefName);
+  registry->RegisterDictionaryPref(
+      prefs::kNearbySharingSchedulerUploadDeviceNamePrefName);
+  registry->RegisterDictionaryPref(
+      prefs::kNearbySharingSchedulerUploadLocalDeviceCertificatesPrefName);
 }
 
 void RegisterNearbySharingLocalPrefs(PrefRegistrySimple* local_state) {
diff --git a/chrome/browser/nearby_sharing/common/nearby_share_prefs.h b/chrome/browser/nearby_sharing/common/nearby_share_prefs.h
index af6af35..78454f3 100644
--- a/chrome/browser/nearby_sharing/common/nearby_share_prefs.h
+++ b/chrome/browser/nearby_sharing/common/nearby_share_prefs.h
@@ -19,12 +19,16 @@
 extern const char kNearbySharingFullNamePrefName[];
 extern const char kNearbySharingIconUrlPrefName[];
 extern const char kNearbySharingOnboardingDismissedTimePrefName[];
+extern const char kNearbySharingPrivateCertificateListPrefName[];
+extern const char kNearbySharingPublicCertificateExpirationDictPrefName[];
 extern const char kNearbySharingSchedulerContactDownloadPrefName[];
 extern const char kNearbySharingSchedulerContactUploadPrefName[];
 extern const char kNearbySharingSchedulerDownloadDeviceDataPrefName[];
-extern const char kNearbySharingPublicCertificateExpirationDictPrefName[];
-extern const char kNearbySharingPrivateCertificateListPrefName[];
 extern const char kNearbySharingSchedulerDownloadPublicCertificatesPrefName[];
+extern const char kNearbySharingSchedulerPrivateCertificateExpirationPrefName[];
+extern const char kNearbySharingSchedulerUploadDeviceNamePrefName[];
+extern const char
+    kNearbySharingSchedulerUploadLocalDeviceCertificatesPrefName[];
 
 }  // namespace prefs
 
diff --git a/chrome/browser/password_check/android/java/res/layout/password_check_edit_fragment.xml b/chrome/browser/password_check/android/java/res/layout/password_check_edit_fragment.xml
index 3594842..1890f398 100644
--- a/chrome/browser/password_check/android/java/res/layout/password_check_edit_fragment.xml
+++ b/chrome/browser/password_check/android/java/res/layout/password_check_edit_fragment.xml
@@ -77,6 +77,7 @@
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:imeOptions="flagNoExtractUi"
+            android:importantForAutofill="noExcludeDescendants"
             android:inputType="textVisiblePassword"
             android:hint="@string/password_entry_viewer_password"/>
         </com.google.android.material.textfield.TextInputLayout>
diff --git a/chrome/browser/pdf/pdf_extension_test.cc b/chrome/browser/pdf/pdf_extension_test.cc
index 07a8866..6ebf3a8b 100644
--- a/chrome/browser/pdf/pdf_extension_test.cc
+++ b/chrome/browser/pdf/pdf_extension_test.cc
@@ -974,26 +974,36 @@
 
 class PDFExtensionContentSettingJSTest
     : public PDFExtensionJSTestBase,
-      public testing::WithParamInterface<bool> {
+      public testing::WithParamInterface<std::pair<bool, bool>> {
  public:
   ~PDFExtensionContentSettingJSTest() override = default;
 
  protected:
   const std::vector<base::Feature> GetEnabledFeatures() const override {
+    std::vector<base::Feature> features;
     if (ShouldHonorJsContentSettings()) {
-      return {chrome_pdf::features::kPdfHonorJsContentSettings};
+      features.push_back(chrome_pdf::features::kPdfHonorJsContentSettings);
     }
-    return {};
+    if (ShouldEnablePDFViewerUpdate()) {
+      features.push_back(chrome_pdf::features::kPDFViewerUpdate);
+    }
+    return features;
   }
 
   const std::vector<base::Feature> GetDisabledFeatures() const override {
-    if (ShouldHonorJsContentSettings()) {
-      return {};
+    std::vector<base::Feature> features;
+    if (!ShouldHonorJsContentSettings()) {
+      features.push_back(chrome_pdf::features::kPdfHonorJsContentSettings);
     }
-    return {chrome_pdf::features::kPdfHonorJsContentSettings};
+    if (!ShouldEnablePDFViewerUpdate()) {
+      features.push_back(chrome_pdf::features::kPDFViewerUpdate);
+    }
+    return features;
   }
 
-  bool ShouldHonorJsContentSettings() const { return GetParam(); }
+  bool ShouldEnablePDFViewerUpdate() const { return GetParam().first; }
+
+  bool ShouldHonorJsContentSettings() const { return GetParam().second; }
 
   // When blocking JavaScript, block the exact query from pdf/main.js while
   // still allowing enough JavaScript to run in the extension for the test
@@ -1048,7 +1058,10 @@
 
 INSTANTIATE_TEST_SUITE_P(/* no prefix */,
                          PDFExtensionContentSettingJSTest,
-                         testing::Bool());
+                         testing::ValuesIn({std::make_pair(true, false),
+                                            std::make_pair(false, false),
+                                            std::make_pair(true, true),
+                                            std::make_pair(false, true)}));
 
 // Service worker tests are regression tests for
 // https://crbug.com/916514.
diff --git a/chrome/browser/push_messaging/push_messaging_browsertest.cc b/chrome/browser/push_messaging/push_messaging_browsertest.cc
index 6c3cabda..dab26db 100644
--- a/chrome/browser/push_messaging/push_messaging_browsertest.cc
+++ b/chrome/browser/push_messaging/push_messaging_browsertest.cc
@@ -2788,8 +2788,10 @@
 class PushSubscriptionChangeEventTest : public PushMessagingBrowserTest {
  public:
   PushSubscriptionChangeEventTest() {
-    scoped_feature_list_.InitAndEnableFeature(
-        features::kPushSubscriptionChangeEvent);
+    scoped_feature_list_.InitWithFeatures(
+        {features::kPushSubscriptionChangeEvent,
+         features::kPushSubscriptionWithExpirationTime},
+        {});
   }
 
   ~PushSubscriptionChangeEventTest() override = default;
@@ -2899,3 +2901,53 @@
       "PushMessaging.PushSubscriptionChangeStatus",
       blink::mojom::PushEventStatus::SUCCESS, 1);
 }
+
+IN_PROC_BROWSER_TEST_F(PushSubscriptionChangeEventTest, OnInvalidation) {
+  std::string script_result;
+
+  ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());
+
+  ASSERT_TRUE(RunScript("hasSubscription()", &script_result));
+  EXPECT_EQ("true - subscribed", script_result);
+
+  ASSERT_TRUE(RunScript("isControlled()", &script_result));
+  ASSERT_EQ("false - is not controlled", script_result);
+  LoadTestPage();  // Reload to become controlled.
+  ASSERT_TRUE(RunScript("isControlled()", &script_result));
+  ASSERT_EQ("true - is controlled", script_result);
+
+  PushMessagingAppIdentifier app_identifier =
+      GetAppIdentifierForServiceWorkerRegistration(0LL);
+  ASSERT_FALSE(app_identifier.is_null());
+
+  base::RunLoop run_loop;
+  push_service()->SetInvalidationCallbackForTesting(run_loop.QuitClosure());
+  push_service()->OnSubscriptionInvalidation(app_identifier.app_id());
+  run_loop.Run();
+
+  // Old subscription should be gone
+  PushMessagingAppIdentifier deleted_identifier =
+      PushMessagingAppIdentifier::FindByAppId(GetBrowser()->profile(),
+                                              app_identifier.app_id());
+  EXPECT_TRUE(deleted_identifier.is_null());
+
+  // New subscription with a different app id should exist
+  PushMessagingAppIdentifier new_identifier =
+      PushMessagingAppIdentifier::FindByServiceWorker(
+          GetBrowser()->profile(), app_identifier.origin(),
+          app_identifier.service_worker_registration_id());
+  EXPECT_FALSE(new_identifier.is_null());
+
+  base::RunLoop().RunUntilIdle();
+
+  // Expect `pushsubscriptionchange` event that is not null
+  ASSERT_TRUE(RunScript("resultQueue.pop()", &script_result));
+  EXPECT_NE("null", script_result);
+  ASSERT_TRUE(RunScript("resultQueue.pop()", &script_result));
+  EXPECT_NE("null", script_result);
+
+  // Check that we record this case in UMA.
+  histogram_tester_.ExpectUniqueSample(
+      "PushMessaging.PushSubscriptionChangeStatus",
+      blink::mojom::PushEventStatus::SUCCESS, 1);
+}
diff --git a/chrome/browser/push_messaging/push_messaging_refresher.cc b/chrome/browser/push_messaging/push_messaging_refresher.cc
index caf5fb04..7c305d87 100644
--- a/chrome/browser/push_messaging/push_messaging_refresher.cc
+++ b/chrome/browser/push_messaging/push_messaging_refresher.cc
@@ -108,6 +108,10 @@
   return app_identifier;
 }
 
+base::WeakPtr<PushMessagingRefresher> PushMessagingRefresher::GetWeakPtr() {
+  return weak_factory_.GetWeakPtr();
+}
+
 void PushMessagingRefresher::AddObserver(Observer* observer) {
   observers_.AddObserver(observer);
 }
diff --git a/chrome/browser/push_messaging/push_messaging_refresher.h b/chrome/browser/push_messaging/push_messaging_refresher.h
index b56843e..82f56f0 100644
--- a/chrome/browser/push_messaging/push_messaging_refresher.h
+++ b/chrome/browser/push_messaging/push_messaging_refresher.h
@@ -52,6 +52,8 @@
   base::Optional<PushMessagingAppIdentifier> FindActiveAppIdentifier(
       const std::string& app_id);
 
+  base::WeakPtr<PushMessagingRefresher> GetWeakPtr();
+
   // Observer for Refresh status updates
   class Observer : public base::CheckedObserver {
    public:
diff --git a/chrome/browser/push_messaging/push_messaging_service_impl.cc b/chrome/browser/push_messaging/push_messaging_service_impl.cc
index d5d6b534..93f2f3d 100644
--- a/chrome/browser/push_messaging/push_messaging_service_impl.cc
+++ b/chrome/browser/push_messaging/push_messaging_service_impl.cc
@@ -261,6 +261,7 @@
 
   registrar_.Add(this, chrome::NOTIFICATION_APP_TERMINATING,
                  content::NotificationService::AllSources());
+  refresh_observer_.Add(&refresher_);
 }
 
 PushMessagingServiceImpl::~PushMessagingServiceImpl() = default;
@@ -347,15 +348,21 @@
   base::OnceClosure message_handled_closure =
       message_callback_for_testing_.is_null() ? base::DoNothing()
                                               : message_callback_for_testing_;
+  refresher_.GotMessageFrom(app_id);
   PushMessagingAppIdentifier app_identifier =
       PushMessagingAppIdentifier::FindByAppId(profile_, app_id);
   // Drop message and unregister if app_id was unknown (maybe recently deleted).
   if (app_identifier.is_null()) {
-    DeliverMessageCallback(app_id, GURL::EmptyGURL(),
-                           -1 /* kInvalidServiceWorkerRegistrationId */,
-                           message, std::move(message_handled_closure),
-                           blink::mojom::PushEventStatus::UNKNOWN_APP_ID);
-    return;
+    base::Optional<PushMessagingAppIdentifier> refresh_identifier =
+        refresher_.FindActiveAppIdentifier(app_id);
+    if (!refresh_identifier) {
+      DeliverMessageCallback(app_id, GURL::EmptyGURL(),
+                             -1 /* kInvalidServiceWorkerRegistrationId */,
+                             message, std::move(message_handled_closure),
+                             blink::mojom::PushEventStatus::UNKNOWN_APP_ID);
+      return;
+    }
+    app_identifier = std::move(*refresh_identifier);
   }
 
   LogMessageReceivedEventToDevTools(
@@ -1272,6 +1279,9 @@
   // Ensure |completed_closure| is run after this function
   base::ScopedClosureRunner scoped_closure(std::move(completed_closure));
 
+  if (!base::FeatureList::IsEnabled(features::kPushSubscriptionChangeEvent))
+    return;
+
   if (app_identifier.is_null()) {
     FirePushSubscriptionChangeCallback(
         app_identifier, blink::mojom::PushEventStatus::UNKNOWN_APP_ID);
@@ -1336,6 +1346,156 @@
 #endif  // BUILDFLAG(ENABLE_BACKGROUND_MODE)
 }
 
+// OnSubscriptionInvalidation methods ------------------------------------------
+
+void PushMessagingServiceImpl::OnSubscriptionInvalidation(
+    const std::string& app_id) {
+  DCHECK(base::FeatureList::IsEnabled(features::kPushSubscriptionChangeEvent))
+      << "It is not allowed to call this method when "
+         "features::kPushSubscriptionChangeEvent is disabled.";
+  PushMessagingAppIdentifier old_app_identifier =
+      PushMessagingAppIdentifier::FindByAppId(profile_, app_id);
+  if (old_app_identifier.is_null())
+    return;
+
+  GetSenderId(profile_, old_app_identifier.origin(),
+              old_app_identifier.service_worker_registration_id(),
+              base::BindOnce(&PushMessagingServiceImpl::GetOldSubscription,
+                             weak_factory_.GetWeakPtr(), old_app_identifier));
+}
+
+void PushMessagingServiceImpl::GetOldSubscription(
+    PushMessagingAppIdentifier old_app_identifier,
+    const std::string& sender_id) {
+  GetPushSubscriptionFromAppIdentifier(
+      old_app_identifier,
+      base::BindOnce(&PushMessagingServiceImpl::StartRefresh,
+                     weak_factory_.GetWeakPtr(), old_app_identifier,
+                     sender_id));
+}
+
+void PushMessagingServiceImpl::StartRefresh(
+    PushMessagingAppIdentifier old_app_identifier,
+    const std::string& sender_id,
+    blink::mojom::PushSubscriptionPtr old_subscription) {
+  // Generate a new app_identifier with the same information, but a different
+  // app_id. Expiration time will be overwritten by DoSubscribe, if the flag
+  // features::kPushSubscriptionWithExpiration time is enabled
+  PushMessagingAppIdentifier new_app_identifier =
+      PushMessagingAppIdentifier::Generate(
+          old_app_identifier.origin(),
+          old_app_identifier.service_worker_registration_id(),
+          base::nullopt /* expiration_time */);
+
+  refresher_.Refresh(old_app_identifier, new_app_identifier.app_id(),
+                     sender_id);
+
+  UpdateSubscription(
+      new_app_identifier, push_messaging::MakeOptions(sender_id),
+      base::BindOnce(&PushMessagingServiceImpl::DidUpdateSubscription,
+                     weak_factory_.GetWeakPtr(), new_app_identifier.app_id(),
+                     old_app_identifier.app_id(), std::move(old_subscription),
+                     sender_id));
+}
+
+void PushMessagingServiceImpl::UpdateSubscription(
+    PushMessagingAppIdentifier app_identifier,
+    blink::mojom::PushSubscriptionOptionsPtr options,
+    RegisterCallback callback) {
+  // After getting a new GCM registration, update the |subscription_id| in SW
+  // database before running the callback
+  auto register_callback = base::BindOnce(
+      [](RegisterCallback cb, Profile* profile, PushMessagingAppIdentifier ai,
+         const std::string& registration_id, const GURL& endpoint,
+         const base::Optional<base::Time>& expiration_time,
+         const std::vector<uint8_t>& p256dh, const std::vector<uint8_t>& auth,
+         blink::mojom::PushRegistrationStatus status) {
+        base::OnceClosure closure =
+            base::BindOnce(std::move(cb), registration_id, endpoint,
+                           expiration_time, p256dh, auth, status);
+        base::ScopedClosureRunner closure_runner(std::move(closure));
+        if (status ==
+            blink::mojom::PushRegistrationStatus::SUCCESS_FROM_PUSH_SERVICE) {
+          UpdatePushSubscriptionId(profile, ai.origin(),
+                                   ai.service_worker_registration_id(),
+                                   registration_id, closure_runner.Release());
+        }
+      },
+      std::move(callback), profile_, app_identifier);
+  // Subscribe using the new subscription information, this will overwrite
+  // the expiration time of |app_identifier|
+  DoSubscribe(app_identifier, std::move(options), std::move(register_callback),
+              -1 /* render_process_id */, -1 /* render_frame_id */,
+              CONTENT_SETTING_ALLOW);
+}
+
+void PushMessagingServiceImpl::DidUpdateSubscription(
+    const std::string& new_app_id,
+    const std::string& old_app_id,
+    blink::mojom::PushSubscriptionPtr old_subscription,
+    const std::string& sender_id,
+    const std::string& registration_id,
+    const GURL& endpoint,
+    const base::Optional<base::Time>& expiration_time,
+    const std::vector<uint8_t>& p256dh,
+    const std::vector<uint8_t>& auth,
+    blink::mojom::PushRegistrationStatus status) {
+  // TODO(crbug.com/1122545): Currently, if |status| is unsuccessful, the old
+  // subscription remains in SW database and preferences and the refresh is
+  // aborted. Instead, one should abort the refresh and retry to refresh
+  // periodically.
+  if (status !=
+      blink::mojom::PushRegistrationStatus::SUCCESS_FROM_PUSH_SERVICE) {
+    return;
+  }
+
+  // Old subscription is now replaced locally by the new subscription
+  refresher_.OnSubscriptionUpdated(new_app_id);
+
+  PushMessagingAppIdentifier new_app_identifier =
+      PushMessagingAppIdentifier::FindByAppId(profile_, new_app_id);
+
+  // Callback for testing
+  base::OnceClosure callback =
+      (invalidation_callback_for_testing_)
+          ? std::move(invalidation_callback_for_testing_)
+          : base::DoNothing();
+
+  FirePushSubscriptionChange(
+      new_app_identifier, std::move(callback),
+      blink::mojom::PushSubscription::New(
+          endpoint, expiration_time, push_messaging::MakeOptions(sender_id),
+          p256dh, auth),
+      std::move(old_subscription));
+}
+
+// PushMessagingRefresher::Observer methods ------------------------------------
+
+void PushMessagingServiceImpl::OnOldSubscriptionExpired(
+    const std::string& app_id,
+    const std::string& sender_id) {
+  // Unsubscribe without clearing SW database, since values of the new
+  // subscription are already saved there.
+  // After unsubscribing, the refresher will get notified.
+  UnsubscribeInternal(
+      blink::mojom::PushUnregistrationReason::REFRESH_FINISHED,
+      GURL::EmptyGURL() /* origin */, -1 /* service_worker_registration_id */,
+      app_id, sender_id,
+      base::BindOnce(&UnregisterCallbackToClosure,
+                     base::BindOnce(&PushMessagingRefresher::OnUnsubscribed,
+                                    refresher_.GetWeakPtr(), app_id)));
+}
+
+void PushMessagingServiceImpl::OnRefreshFinished(
+    const PushMessagingAppIdentifier& app_identifier) {
+  // TODO(viviy): Log data in UMA
+}
+
+void PushMessagingServiceImpl::SetInvalidationCallbackForTesting(
+    base::OnceClosure callback) {
+  invalidation_callback_for_testing_ = std::move(callback);
+}
+
 // Helper methods --------------------------------------------------------------
 
 void PushMessagingServiceImpl::SetRemoveExpiredSubscriptionsCallbackForTesting(
diff --git a/chrome/browser/push_messaging/push_messaging_service_impl.h b/chrome/browser/push_messaging/push_messaging_service_impl.h
index f25de1b..2c54d9cb 100644
--- a/chrome/browser/push_messaging/push_messaging_service_impl.h
+++ b/chrome/browser/push_messaging/push_messaging_service_impl.h
@@ -16,8 +16,10 @@
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "base/optional.h"
+#include "base/scoped_observer.h"
 #include "base/time/time.h"
 #include "chrome/browser/push_messaging/push_messaging_notification_manager.h"
+#include "chrome/browser/push_messaging/push_messaging_refresher.h"
 #include "chrome/common/buildflags.h"
 #include "components/content_settings/core/browser/content_settings_observer.h"
 #include "components/content_settings/core/common/content_settings.h"
@@ -62,7 +64,8 @@
                                  public gcm::GCMAppHandler,
                                  public content_settings::Observer,
                                  public KeyedService,
-                                 public content::NotificationObserver {
+                                 public content::NotificationObserver,
+                                 public PushMessagingRefresher::Observer {
  public:
   // If any Service Workers are using push, starts GCM and adds an app handler.
   static void InitializeForProfile(Profile* profile);
@@ -140,13 +143,26 @@
   // KeyedService implementation.
   void Shutdown() override;
 
-  // content::NotificationObserver:
+  // content::NotificationObserver implementation
   void Observe(int type,
                const content::NotificationSource& source,
                const content::NotificationDetails& details) override;
 
+  // WARNING: Only call this function if features::kPushSubscriptionChangeEvent
+  // is enabled, will be later used by the Push Service to trigger subscription
+  // refreshes
+  void OnSubscriptionInvalidation(const std::string& app_id);
+
+  // PushMessagingRefresher::Observer implementation
+  // Initiate unsubscribe task when old subscription becomes invalid
+  void OnOldSubscriptionExpired(const std::string& app_id,
+                                const std::string& sender_id) override;
+  void OnRefreshFinished(
+      const PushMessagingAppIdentifier& app_identifier) override;
+
   void SetMessageCallbackForTesting(const base::Closure& callback);
   void SetUnsubscribeCallbackForTesting(const base::Closure& callback);
+  void SetInvalidationCallbackForTesting(base::OnceClosure callback);
   void SetContentSettingChangedCallbackForTesting(
       base::RepeatingClosure callback);
   void SetServiceWorkerUnregisteredCallbackForTesting(
@@ -275,12 +291,40 @@
       const std::vector<uint8_t>& p256dh,
       const std::vector<uint8_t>& auth);
 
+  // OnSubscriptionInvalidation methods-----------------------------------------
+
+  void GetOldSubscription(PushMessagingAppIdentifier old_app_identifier,
+                          const std::string& sender_id);
+
+  // After gathering all relavent information to start the refresh,
+  // generate a new app id and initiate refresh
+  void StartRefresh(PushMessagingAppIdentifier old_app_identifier,
+                    const std::string& sender_id,
+                    blink::mojom::PushSubscriptionPtr old_subscription);
+
+  // Makes a new susbcription and replaces the old subscription by new
+  // subscription in preferences and service worker database
+  void UpdateSubscription(PushMessagingAppIdentifier app_identifier,
+                          blink::mojom::PushSubscriptionOptionsPtr options,
+                          RegisterCallback callback);
+
+  // After the subscription is updated, fire a `pushsubscriptionchange` event
+  // and notify the |refresher_|
+  void DidUpdateSubscription(const std::string& new_app_id,
+                             const std::string& old_app_id,
+                             blink::mojom::PushSubscriptionPtr old_subscription,
+                             const std::string& sender_id,
+                             const std::string& registration_id,
+                             const GURL& endpoint,
+                             const base::Optional<base::Time>& expiration_time,
+                             const std::vector<uint8_t>& p256dh,
+                             const std::vector<uint8_t>& auth,
+                             blink::mojom::PushRegistrationStatus status);
   // Helper methods ------------------------------------------------------------
 
   // The subscription given in |identifier| will be unsubscribed (and a
   // `pushsubscriptionchange` event fires if
   // features::kPushSubscriptionChangeEvent is enabled)
-  // |completed_closure|
   void UnexpectedChange(PushMessagingAppIdentifier identifier,
                         blink::mojom::PushUnregistrationReason reason,
                         base::OnceClosure completed_closure);
@@ -341,9 +385,14 @@
   base::Closure service_worker_unregistered_callback_for_testing_;
   base::Closure service_worker_database_wiped_callback_for_testing_;
   base::OnceClosure remove_expired_subscriptions_callback_for_testing_;
+  base::OnceClosure invalidation_callback_for_testing_;
 
   PushMessagingNotificationManager notification_manager_;
 
+  PushMessagingRefresher refresher_;
+
+  ScopedObserver<PushMessagingRefresher, PushMessagingRefresher::Observer>
+      refresh_observer_{this};
   // A multiset containing one entry for each in-flight push message delivery,
   // keyed by the receiver's app id.
   std::multiset<std::string> in_flight_message_deliveries_;
diff --git a/chrome/browser/resources/chromeos/crostini_upgrader/app.html b/chrome/browser/resources/chromeos/crostini_upgrader/app.html
index 585db83..8b35dae 100644
--- a/chrome/browser/resources/chromeos/crostini_upgrader/app.html
+++ b/chrome/browser/resources/chromeos/crostini_upgrader/app.html
@@ -136,8 +136,8 @@
         hidden="[[!isState_(state_, State.CANCELING)]]">
       <paper-progress class="progress-bar" indeterminate></paper-progress>
     </div>
-    <div id="error-message" hidden="[[!isState_(state_, State.ERROR)]]">
-      <textarea id="error-log" rows="10" cols="80" readonly>[[getErrorMessage_(state_)]]</textarea>
+    <div id="upgrade-error-message" hidden="[[isErrorLogsHidden_(state_)]]">
+      <textarea id="error-log" rows="10" cols="80" readonly>[[getErrorLogs_(state_)]]</textarea>
     </div>
   </div>
 
diff --git a/chrome/browser/resources/chromeos/crostini_upgrader/app.js b/chrome/browser/resources/chromeos/crostini_upgrader/app.js
index e76c5f4..fa7e253 100644
--- a/chrome/browser/resources/chromeos/crostini_upgrader/app.js
+++ b/chrome/browser/resources/chromeos/crostini_upgrader/app.js
@@ -24,6 +24,7 @@
   BACKUP_SUCCEEDED: 'backupSucceeded',
   PRECHECKS_FAILED: 'prechecksFailed',
   UPGRADING: 'upgrading',
+  UPGRADE_ERROR: 'upgrade_error',
   OFFER_RESTORE: 'offerRestore',
   RESTORE: 'restore',
   RESTORE_SUCCEEDED: 'restoreSucceeded',
@@ -170,7 +171,7 @@
         if (this.backupCheckboxChecked_) {
           this.state_ = State.OFFER_RESTORE;
         } else {
-          this.state_ = State.ERROR;
+          this.state_ = State.UPGRADE_ERROR;
         }
       }),
       callbackRouter.onRestoreProgress.addListener((percent) => {
@@ -257,6 +258,7 @@
         BrowserProxy.getInstance().handler.cancel();
         break;
       case State.PRECHECKS_FAILED:
+      case State.UPGRADE_ERROR:
       case State.ERROR:
       case State.OFFER_RESTORE:
       case State.SUCCEEDED:
@@ -324,7 +326,14 @@
    */
   isProgressMessageHidden_(state) {
     return this.isState_(this.state_, State.PROMPT) ||
-        this.isState_(this.state_, State.ERROR);
+        this.isState_(this.state_, State.UPGRADE_ERROR) ||
+        this.isState_(this.state_, State.OFFER_RESTORE);
+  },
+
+  isErrorLogsHidden_(state) {
+    return !(
+        this.isState_(this.state_, State.UPGRADE_ERROR) ||
+        this.isState_(this.state_, State.OFFER_RESTORE));
   },
 
   /**
@@ -384,6 +393,7 @@
         titleId = 'upgradingTitle';
         break;
       case State.OFFER_RESTORE:
+      case State.UPGRADE_ERROR:
       case State.ERROR:
         titleId = 'errorTitle';
         break;
@@ -416,6 +426,7 @@
         return loadTimeData.getString('upgrade');
       case State.PRECHECKS_FAILED:
         return loadTimeData.getString('retry');
+      case State.UPGRADE_ERROR:
       case State.ERROR:
         return loadTimeData.getString('cancel');
       case State.SUCCEEDED:
@@ -499,7 +510,7 @@
    * @return {string}
    * @private
    */
-  getErrorMessage_(state) {
+  getErrorLogs_(state) {
     return this.progressMessages_.join('\n');
   },
 
@@ -514,6 +525,8 @@
       case State.RESTORE_SUCCEEDED:
       case State.PRECHECKS_FAILED:
         return 'img-square-illustration';
+      case State.OFFER_RESTORE:
+      case State.UPGRADE_ERROR:
       case State.ERROR:
         return 'img-square-error-illustration';
     }
@@ -531,6 +544,8 @@
       case State.RESTORE_SUCCEEDED:
         return 'images/success_illustration.svg';
       case State.PRECHECKS_FAILED:
+      case State.OFFER_RESTORE:
+      case State.UPGRADE_ERROR:
       case State.ERROR:
         return 'images/error_illustration.png';
     }
diff --git a/chrome/browser/resources/settings/chromeos/os_languages_page/BUILD.gn b/chrome/browser/resources/settings/chromeos/os_languages_page/BUILD.gn
index 7ad92e27..33f3643 100644
--- a/chrome/browser/resources/settings/chromeos/os_languages_page/BUILD.gn
+++ b/chrome/browser/resources/settings/chromeos/os_languages_page/BUILD.gn
@@ -26,6 +26,10 @@
 }
 
 js_library("change_device_language_dialog") {
+  deps = [
+    "../../languages_page:languages",
+    "//ui/webui/resources/cr_elements:cr_scrollable_behavior",
+  ]
 }
 
 js_library("input_method_options_page") {
@@ -159,6 +163,10 @@
 
 js_library("change_device_language_dialog.m") {
   sources = [ "$root_gen_dir/chrome/browser/resources/settings/chromeos/os_languages_page/change_device_language_dialog.m.js" ]
+  deps = [
+    "../../languages_page:languages.m",
+    "//ui/webui/resources/cr_elements:cr_scrollable_behavior.m",
+  ]
   extra_deps = [ ":change_device_language_dialog_module" ]
 }
 
diff --git a/chrome/browser/resources/settings/chromeos/os_languages_page/change_device_language_dialog.html b/chrome/browser/resources/settings/chromeos/os_languages_page/change_device_language_dialog.html
index 6b1e557..8f5342a 100644
--- a/chrome/browser/resources/settings/chromeos/os_languages_page/change_device_language_dialog.html
+++ b/chrome/browser/resources/settings/chromeos/os_languages_page/change_device_language_dialog.html
@@ -1,12 +1,79 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
 
 <link rel="import" href="chrome://resources/cr_elements/cr_button/cr_button.html">
+<link rel="import" href="chrome://resources/cr_elements/cr_scrollable_behavior.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html">
+<link rel="import" href="chrome://resources/html/assert.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/iron-flex-layout-classes.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/iron-list/iron-list.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/paper-ripple/paper-ripple.html">
+<link rel="import" href="../../languages_page/languages.html">
+<link rel="import" href="../../settings_shared_css.html">
 
 <dom-module id="os-settings-change-device-language-dialog">
   <template>
+    <style include="settings-shared iron-flex">
+      #dialogBody {
+        display: flex;
+        flex-direction: column;
+        height: 336px;
+        overflow: auto;
+        padding-inline-end: 0;
+        padding-inline-start: 0;
+      }
+
+      iron-icon[icon='settings:check-circle'] {
+        --iron-icon-fill-color: var(--cr-link-color);
+        margin-inline-end: 26px;
+      }
+
+      iron-list > div:not(.selected):hover {
+        background-color: var(--cros-menu-item-bg-color-focus);
+      }
+
+      [scrollable] iron-list > :not(.no-outline):focus {
+        background-color: var(--cros-menu-item-bg-color-focus);
+      }
+
+      [scrollable] iron-list > .selected:not(.no-outline):focus {
+        background-color: transparent;
+      }
+
+      .list-item {
+        color: var(--cros-text-color-primary);
+        min-height: 36px;
+      }
+
+      .padded {
+        padding-inline-start: 20px;
+      }
+
+      paper-ripple {
+        color: var(--cros-menu-item-ripple-color);
+      }
+    </style>
+
     <cr-dialog id="dialog" close-text="$i18n{close}" show-on-attach>
       <div slot="title">$i18n{changeDeviceLanguageDialogTitle}</div>
+      <div id="dialogBody" slot="body" scrollable>
+        <iron-list scroll-target="[[$$('[slot=body]')]]"
+            items="[[getPossibleDeviceLanguages_(
+                languages.supported, languages.enabled.*)]]"
+            selection-enabled selected-item="{{selectedLanguage_}}">
+          <template>
+            <!-- |selected| is a property of iron-list -->
+            <div class$="list-item [[getItemClass_(selected)]]"
+                tabindex$="[[tabIndex]]">
+              <paper-ripple></paper-ripple>
+              <div class="flex padded">
+                [[getDisplayText_(item)]]
+              </div>
+              <iron-icon icon="settings:check-circle" hidden="[[!selected]]">
+              </iron-icon>
+            </div>
+          </template>
+        </iron-list>
+      </div>
       <div slot="button-container">
         <cr-button class="cancel-button" on-click="onCancelButtonTap_">
           $i18n{cancel}
diff --git a/chrome/browser/resources/settings/chromeos/os_languages_page/change_device_language_dialog.js b/chrome/browser/resources/settings/chromeos/os_languages_page/change_device_language_dialog.js
index de84d993..e465caf 100644
--- a/chrome/browser/resources/settings/chromeos/os_languages_page/change_device_language_dialog.js
+++ b/chrome/browser/resources/settings/chromeos/os_languages_page/change_device_language_dialog.js
@@ -9,21 +9,86 @@
 Polymer({
   is: 'os-settings-change-device-language-dialog',
 
+  behaviors: [
+    CrScrollableBehavior,
+  ],
+
   properties: {
+    /** @type {!LanguagesModel|undefined} */
+    languages: Object,
+
+    /** @type {!LanguageHelper} */
+    languageHelper: Object,
+
+    /** @private {?chrome.languageSettingsPrivate.Language} */
+    selectedLanguage_: {
+      type: Object,
+      value: null,
+    },
+
     /** @private */
     disableActionButton_: {
       type: Boolean,
-      value: true,
+      computed: 'shouldDisableActionButton_(selectedLanguage_)',
     },
   },
 
+  /**
+   * @return {!Array<!chrome.languageSettingsPrivate.Language>} A list of
+   *     possible device language.
+   * @private
+   */
+  getPossibleDeviceLanguages_() {
+    // TODO(crbug/1113439): add search and filter based on search value.
+    return this.languages.supported.filter(language => {
+      if (!language.supportsUI || language.isProhibitedLanguage ||
+          language.code === this.languages.prospectiveUILanguage) {
+        return false;
+      }
+
+      return true;
+    });
+  },
+
+  /**
+   * @param {boolean} selected
+   * @private
+   */
+  getItemClass_(selected) {
+    return selected ? 'selected' : '';
+  },
+
+  /**
+   * @param {!chrome.languageSettingsPrivate.Language} language
+   * @return {string} The text to be displayed.
+   * @private
+   */
+  getDisplayText_(language) {
+    let displayText = language.displayName;
+    // If the native name is different, add it.
+    if (language.displayName != language.nativeDisplayName) {
+      displayText += ' - ' + language.nativeDisplayName;
+    }
+    return displayText;
+  },
+
+  /** @private */
+  shouldDisableActionButton_() {
+    return this.selectedLanguage_ === null;
+  },
+
   /** @private */
   onCancelButtonTap_() {
     this.$.dialog.close();
   },
 
-  /** @private */
+  /**
+   * Sets device language.
+   * @private
+   */
   onActionButtonTap_() {
+    assert(this.selectedLanguage_);
+    this.languageHelper.setProspectiveUILanguage(this.selectedLanguage_.code);
     this.$.dialog.close();
   },
 });
diff --git a/chrome/browser/resources/settings/chromeos/os_languages_page/os_languages_page_v2.html b/chrome/browser/resources/settings/chromeos/os_languages_page/os_languages_page_v2.html
index f043077..ed5a173 100644
--- a/chrome/browser/resources/settings/chromeos/os_languages_page/os_languages_page_v2.html
+++ b/chrome/browser/resources/settings/chromeos/os_languages_page/os_languages_page_v2.html
@@ -130,7 +130,8 @@
     </div>
 
     <template is="dom-if" if="[[showChangeDeviceLanguageDialog_]]" restamp>
-      <os-settings-change-device-language-dialog
+      <os-settings-change-device-language-dialog languages="[[languages]]"
+          language-helper="[[languageHelper]]"
           on-close="onChangeDeviceLanguageDialogClose_">
       </os-settings-change-device-language-dialog>
     </template>
diff --git a/chrome/browser/resources/signin/profile_picker/manage_profiles_browser_proxy.js b/chrome/browser/resources/signin/profile_picker/manage_profiles_browser_proxy.js
index bc026a7f..bfd7df4 100644
--- a/chrome/browser/resources/signin/profile_picker/manage_profiles_browser_proxy.js
+++ b/chrome/browser/resources/signin/profile_picker/manage_profiles_browser_proxy.js
@@ -20,6 +20,9 @@
 
 /**
  * This is the data structure sent back and forth between C++ and JS.
+ * `colorId` has the following special values:
+ *   - `-1` for the default theme.
+ *   - `0` for a manually picked color theme.
  * @typedef {{
  *   colorId: number,
  *   color: number,
@@ -31,6 +34,19 @@
  */
 export let AutogeneratedThemeColorInfo;
 
+/**
+ * This is the data structure sent back and forth between C++ and JS.
+ * `colorId` has the following special values:
+ *   - `-1` for the default theme..
+ *   - `0` for a manually picked color theme
+ * `color` is defined only for manually picked themes.
+ * @typedef {{
+ *   colorId: number,
+ *   color: (number|undefined),
+ * }}
+ */
+export let UserThemeChoice;
+
 /** @interface */
 export class ManageProfilesBrowserProxy {
   /**
@@ -69,6 +85,14 @@
   getNewProfileSuggestedThemeInfo() {}
 
   /**
+   * Retrieves all relevant theme information for the particular theme.
+   * @param {!UserThemeChoice} theme A theme which info needs to be retrieved.
+   * @return {!Promise<!AutogeneratedThemeColorInfo>} A promise firing with the
+   * theme info, once it has been retrieved.
+   */
+  getProfileThemeInfo(theme) {}
+
+  /**
    * Retrieves profile statistics to be shown in the remove profile warning.
    * @param {string} profilePath
    */
@@ -128,6 +152,11 @@
   }
 
   /** @override */
+  getProfileThemeInfo(theme) {
+    return sendWithPromise('getProfileThemeInfo', theme);
+  }
+
+  /** @override */
   removeProfile(profilePath) {
     chrome.send('removeProfile', [profilePath]);
   }
diff --git a/chrome/browser/resources/signin/profile_picker/profile_creation_flow/BUILD.gn b/chrome/browser/resources/signin/profile_picker/profile_creation_flow/BUILD.gn
index 4dc5d2b..9c3a1c9e 100644
--- a/chrome/browser/resources/signin/profile_picker/profile_creation_flow/BUILD.gn
+++ b/chrome/browser/resources/signin/profile_picker/profile_creation_flow/BUILD.gn
@@ -32,6 +32,7 @@
     "..:navigation_behavior",
     "..:policy_helper",
     "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
+    "//ui/webui/resources/cr_components/customize_themes",
     "//ui/webui/resources/cr_elements/cr_button:cr_button.m",
     "//ui/webui/resources/cr_elements/cr_checkbox:cr_checkbox.m",
     "//ui/webui/resources/cr_elements/cr_icon_button:cr_icon_button.m",
diff --git a/chrome/browser/resources/signin/profile_picker/profile_creation_flow/local_profile_customization.html b/chrome/browser/resources/signin/profile_picker/profile_creation_flow/local_profile_customization.html
index 6f5fbd30..17e3c7f 100644
--- a/chrome/browser/resources/signin/profile_picker/profile_creation_flow/local_profile_customization.html
+++ b/chrome/browser/resources/signin/profile_picker/profile_creation_flow/local_profile_customization.html
@@ -60,9 +60,23 @@
     width: 300px;
   }
 
+  #colorPickerContainer {
+    border: 1px solid var(--google-grey-refresh-300);
+    border-radius: 4px;
+    display: flex;
+    flex-direction: column;
+    padding: 15px 36px 23px;
+  }
+
+  #colorPickerHeader {
+    color: var(--cr-primary-text-color);
+    padding-bottom: 16px;
+  }
+
   #colorPicker {
-    height: 220px;
-    width: 370px;
+    --cr-customize-themes-grid-gap: 8px;
+    --cr-customize-themes-icon-size: 44px;
+    align-self: center;
   }
 
   cr-checkbox {
@@ -114,9 +128,12 @@
         auto-validate spellcheck="false">
     </cr-input>
 
-    <!-- TODO(crbug.com/1115301): Add color picker. -->
-    <div id="colorPicker"
-        style$="background-color:[[profileThemeInfo.themeFrameColor]]">
+    <div id="colorPickerContainer">
+      <div id="colorPickerHeader">
+        $i18n{localProfileCreationThemeText}
+      </div>
+      <cr-customize-themes id="colorPicker" selected-theme="{{selectedTheme_}}">
+      </cr-customize-themes>
     </div>
   </div>
 </div>
diff --git a/chrome/browser/resources/signin/profile_picker/profile_creation_flow/local_profile_customization.js b/chrome/browser/resources/signin/profile_picker/profile_creation_flow/local_profile_customization.js
index ae99a9b..98262e8 100644
--- a/chrome/browser/resources/signin/profile_picker/profile_creation_flow/local_profile_customization.js
+++ b/chrome/browser/resources/signin/profile_picker/profile_creation_flow/local_profile_customization.js
@@ -7,6 +7,8 @@
 import 'chrome://resources/cr_elements/cr_input/cr_input.m.js';
 import 'chrome://resources/cr_elements/cr_checkbox/cr_checkbox.m.js';
 import 'chrome://resources/cr_elements/shared_vars_css.m.js';
+import 'chrome://resources/cr_components/customize_themes/customize_themes.js';
+import 'chrome://resources/cr_components/customize_themes/customize_themes.mojom-lite.js';
 import './shared_css.js';
 import '../icons.js';
 
@@ -14,7 +16,7 @@
 import {WebUIListenerBehavior} from 'chrome://resources/js/web_ui_listener_behavior.m.js';
 import {html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
-import {AutogeneratedThemeColorInfo, ManageProfilesBrowserProxy, ManageProfilesBrowserProxyImpl} from '../manage_profiles_browser_proxy.js';
+import {AutogeneratedThemeColorInfo, ManageProfilesBrowserProxy, ManageProfilesBrowserProxyImpl, UserThemeChoice} from '../manage_profiles_browser_proxy.js';
 import {navigateToPreviousRoute} from '../navigation_behavior.js';
 import {isProfileCreationAllowed} from '../policy_helper.js';
 
@@ -26,9 +28,32 @@
   behaviors: [WebUIListenerBehavior],
 
   properties: {
-    /** @type {!AutogeneratedThemeColorInfo} */
+    /**
+     * The theme info used to display colored UI elements.
+     * @type {!AutogeneratedThemeColorInfo}
+     */
     profileThemeInfo: {
       type: Object,
+      observer: 'onProfileThemeInfoChange_',
+    },
+
+    /**
+     * The currently selected theme in the color picker.
+     * @private {!customizeThemes.mojom.Theme}
+     */
+    selectedTheme_: {
+      type: Object,
+      observer: 'onSelectedThemeChange_',
+    },
+
+    /**
+     * True if `selectedTheme_` doesn't need to be updated when
+     * `profileThemeInfo` changes.
+     * @private {boolean}
+     */
+    disableSelectedThemeUpdates_: {
+      type: Boolean,
+      value: false,
     },
 
     /**
@@ -72,10 +97,14 @@
   manageProfilesBrowserProxy_: null,
 
   /** @override */
-  ready() {
-    this.sanityCheck_();
+  created() {
     this.manageProfilesBrowserProxy_ =
         ManageProfilesBrowserProxyImpl.getInstance();
+  },
+
+  /** @override */
+  ready() {
+    this.sanityCheck_();
     this.addWebUIListener(
         'create-profile-finished', () => this.handleCreateProfileFinished_());
   },
@@ -138,6 +167,52 @@
   },
 
   /** @private */
+  onProfileThemeInfoChange_() {
+    if (this.disableSelectedThemeUpdates_) {
+      return;
+    }
+
+    this.selectedTheme_ = {
+      type: customizeThemes.mojom.ThemeType.kChrome,
+      info: {
+        chromeThemeId: this.profileThemeInfo.colorId,
+      },
+    };
+  },
+
+  /**
+   * @return {!Promise}
+   * @private
+   */
+  async onSelectedThemeChange_() {
+    /** @type {UserThemeChoice} */
+    let theme;
+    if (this.selectedTheme_.type ===
+        customizeThemes.mojom.ThemeType.kAutogenerated) {
+      theme = {
+        colorId: 0,
+        color: this.selectedTheme_.info.autogeneratedThemeColors.frame.value
+      };
+    } else if (
+        this.selectedTheme_.type === customizeThemes.mojom.ThemeType.kChrome) {
+      theme = {
+        colorId: /** @type {number} */ (this.selectedTheme_.info.chromeThemeId),
+      };
+    } else if (
+        this.selectedTheme_.type === customizeThemes.mojom.ThemeType.kDefault) {
+      theme = {
+        colorId: -1,
+      };
+    }
+
+    const newThemeInfo =
+        await this.manageProfilesBrowserProxy_.getProfileThemeInfo(theme);
+    this.disableSelectedThemeUpdates_ = true;
+    this.profileThemeInfo = newThemeInfo;
+    this.disableSelectedThemeUpdates_ = false;
+  },
+
+  /** @private */
   handleCreateProfileFinished_() {
     this.onClickBack_();
     this.createInProgress_ = false;
diff --git a/chrome/browser/safe_browsing/android/javatests/src/org/chromium/chrome/browser/safe_browsing/settings/SecuritySettingsFragmentTest.java b/chrome/browser/safe_browsing/android/javatests/src/org/chromium/chrome/browser/safe_browsing/settings/SecuritySettingsFragmentTest.java
index 4bf712da..5c572d9 100644
--- a/chrome/browser/safe_browsing/android/javatests/src/org/chromium/chrome/browser/safe_browsing/settings/SecuritySettingsFragmentTest.java
+++ b/chrome/browser/safe_browsing/android/javatests/src/org/chromium/chrome/browser/safe_browsing/settings/SecuritySettingsFragmentTest.java
@@ -306,6 +306,55 @@
     @Test
     @SmallTest
     @Feature({"SafeBrowsing"})
+    @Features.EnableFeatures(ChromeFeatureList.SAFE_BROWSING_ENHANCED_PROTECTION_ENABLED)
+    @Policies.Add({ @Policies.Item(key = "SafeBrowsingProtectionLevel", string = "2") })
+    public void testSafeBrowsingProtectionLevelManagedEnhanced() {
+        TestThreadUtils.runOnUiThreadBlocking(
+                () -> { ChromeBrowserInitializer.getInstance().handleSynchronousStartup(); });
+        launchSettingsActivity();
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            Assert.assertTrue(SafeBrowsingBridge.isSafeBrowsingManaged());
+            Assert.assertTrue(mManagedTextPreference.isVisible());
+            Assert.assertFalse(getEnhancedProtectionButton().isEnabled());
+            Assert.assertFalse(getStandardProtectionButton().isEnabled());
+            Assert.assertFalse(getNoProtectionButton().isEnabled());
+            Assert.assertEquals(SafeBrowsingState.ENHANCED_PROTECTION, getSafeBrowsingState());
+        });
+    }
+
+    @Test
+    @SmallTest
+    @Feature({"SafeBrowsing"})
+    @Features.EnableFeatures(ChromeFeatureList.SAFE_BROWSING_ENHANCED_PROTECTION_ENABLED)
+    @Policies.Add({ @Policies.Item(key = "SafeBrowsingProtectionLevel", string = "1") })
+    public void testSafeBrowsingProtectionLevelManagedStandard() {
+        TestThreadUtils.runOnUiThreadBlocking(
+                () -> { ChromeBrowserInitializer.getInstance().handleSynchronousStartup(); });
+        launchSettingsActivity();
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            Assert.assertTrue(SafeBrowsingBridge.isSafeBrowsingManaged());
+            Assert.assertEquals(SafeBrowsingState.STANDARD_PROTECTION, getSafeBrowsingState());
+        });
+    }
+
+    @Test
+    @SmallTest
+    @Feature({"SafeBrowsing"})
+    @Features.EnableFeatures(ChromeFeatureList.SAFE_BROWSING_ENHANCED_PROTECTION_ENABLED)
+    @Policies.Add({ @Policies.Item(key = "SafeBrowsingProtectionLevel", string = "0") })
+    public void testSafeBrowsingProtectionLevelManagedDisabled() {
+        TestThreadUtils.runOnUiThreadBlocking(
+                () -> { ChromeBrowserInitializer.getInstance().handleSynchronousStartup(); });
+        launchSettingsActivity();
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            Assert.assertTrue(SafeBrowsingBridge.isSafeBrowsingManaged());
+            Assert.assertEquals(SafeBrowsingState.NO_SAFE_BROWSING, getSafeBrowsingState());
+        });
+    }
+
+    @Test
+    @SmallTest
+    @Feature({"SafeBrowsing"})
     public void testHelpButtonClicked() {
         launchSettingsActivity();
         mSecuritySettingsFragment.setHelpAndFeedbackLauncher(mHelpAndFeedbackLauncher);
diff --git a/chrome/browser/site_isolation/site_per_process_text_input_browsertest.cc b/chrome/browser/site_isolation/site_per_process_text_input_browsertest.cc
index 28f5152..9e813d7 100644
--- a/chrome/browser/site_isolation/site_per_process_text_input_browsertest.cc
+++ b/chrome/browser/site_isolation/site_per_process_text_input_browsertest.cc
@@ -58,101 +58,9 @@
 namespace {
 using IndexVector = std::vector<size_t>;
 
-// TextInputManager Observers
-
-// A base class for observing the TextInputManager owned by the given
-// WebContents. Subclasses could observe the TextInputManager for different
-// changes. The class wraps a public tester which accepts callbacks that
-// are run after specific changes in TextInputManager. Different observers can
-// be subclassed from this by providing their specific callback methods.
-class TextInputManagerObserverBase {
- public:
-  explicit TextInputManagerObserverBase(content::WebContents* web_contents)
-      : tester_(new content::TextInputManagerTester(web_contents)),
-        success_(false) {}
-
-  virtual ~TextInputManagerObserverBase() {}
-
-  // Wait for derived class's definition of success.
-  void Wait() {
-    if (success_)
-      return;
-    message_loop_runner_ = new content::MessageLoopRunner();
-    message_loop_runner_->Run();
-  }
-
-  bool success() const { return success_; }
-
- protected:
-  content::TextInputManagerTester* tester() { return tester_.get(); }
-
-  void OnSuccess() {
-    success_ = true;
-    if (message_loop_runner_)
-      message_loop_runner_->Quit();
-
-    // By deleting |tester_| we make sure that the internal observer used in
-    // content/ is removed from the observer list of TextInputManager.
-    tester_.reset(nullptr);
-  }
-
- private:
-  std::unique_ptr<content::TextInputManagerTester> tester_;
-  bool success_;
-  scoped_refptr<content::MessageLoopRunner> message_loop_runner_;
-
-  DISALLOW_COPY_AND_ASSIGN(TextInputManagerObserverBase);
-};
-
-// This class observes TextInputManager for changes in |TextInputState.value|.
-class TextInputManagerValueObserver : public TextInputManagerObserverBase {
- public:
-  TextInputManagerValueObserver(content::WebContents* web_contents,
-                                const std::string& expected_value)
-      : TextInputManagerObserverBase(web_contents),
-        expected_value_(expected_value) {
-    tester()->SetUpdateTextInputStateCalledCallback(base::Bind(
-        &TextInputManagerValueObserver::VerifyValue, base::Unretained(this)));
-  }
-
- private:
-  void VerifyValue() {
-    std::string value;
-    if (tester()->GetTextInputValue(&value) && expected_value_ == value)
-      OnSuccess();
-  }
-
-  const std::string expected_value_;
-
-  DISALLOW_COPY_AND_ASSIGN(TextInputManagerValueObserver);
-};
-
-// This class observes TextInputManager for changes in |TextInputState.type|.
-class TextInputManagerTypeObserver : public TextInputManagerObserverBase {
- public:
-  TextInputManagerTypeObserver(content::WebContents* web_contents,
-                               ui::TextInputType expected_type)
-      : TextInputManagerObserverBase(web_contents),
-        expected_type_(expected_type) {
-    tester()->SetUpdateTextInputStateCalledCallback(base::Bind(
-        &TextInputManagerTypeObserver::VerifyType, base::Unretained(this)));
-  }
-
- private:
-  void VerifyType() {
-    ui::TextInputType type =
-        tester()->GetTextInputType(&type) ? type : ui::TEXT_INPUT_TYPE_NONE;
-    if (expected_type_ == type)
-      OnSuccess();
-  }
-
-  const ui::TextInputType expected_type_;
-
-  DISALLOW_COPY_AND_ASSIGN(TextInputManagerTypeObserver);
-};
-
 // This class observes TextInputManager for the first change in TextInputState.
-class TextInputManagerChangeObserver : public TextInputManagerObserverBase {
+class TextInputManagerChangeObserver
+    : public content::TextInputManagerObserverBase {
  public:
   explicit TextInputManagerChangeObserver(content::WebContents* web_contents)
       : TextInputManagerObserverBase(web_contents) {
@@ -170,7 +78,7 @@
 };
 
 // This class observes |TextInputState.type| for a specific RWHV.
-class ViewTextInputTypeObserver : public TextInputManagerObserverBase {
+class ViewTextInputTypeObserver : public content::TextInputManagerObserverBase {
  public:
   explicit ViewTextInputTypeObserver(content::WebContents* web_contents,
                                      content::RenderWidgetHostView* rwhv,
@@ -201,7 +109,8 @@
 
 // This class observes the |expected_view| for the first change in its
 // selection bounds.
-class ViewSelectionBoundsChangedObserver : public TextInputManagerObserverBase {
+class ViewSelectionBoundsChangedObserver
+    : public content::TextInputManagerObserverBase {
  public:
   ViewSelectionBoundsChangedObserver(
       content::WebContents* web_contents,
@@ -227,7 +136,7 @@
 // This class observes the |expected_view| for the first change in its
 // composition range information.
 class ViewCompositionRangeChangedObserver
-    : public TextInputManagerObserverBase {
+    : public content::TextInputManagerObserverBase {
  public:
   ViewCompositionRangeChangedObserver(
       content::WebContents* web_contents,
@@ -251,7 +160,7 @@
 };
 
 // This class observes the |expected_view| for a change in the text selection.
-class ViewTextSelectionObserver : public TextInputManagerObserverBase {
+class ViewTextSelectionObserver : public content::TextInputManagerObserverBase {
  public:
   ViewTextSelectionObserver(content::WebContents* web_contents,
                             content::RenderWidgetHostView* expected_view,
@@ -280,7 +189,7 @@
 };
 
 // This class observes all the text selection updates within a WebContents.
-class TextSelectionObserver : public TextInputManagerObserverBase {
+class TextSelectionObserver : public content::TextInputManagerObserverBase {
  public:
   explicit TextSelectionObserver(content::WebContents* web_contents)
       : TextInputManagerObserverBase(web_contents) {
@@ -461,7 +370,8 @@
     AddInputFieldToFrame(frames[i], "text", values[i], true);
 
   for (size_t i = 0; i < frames.size(); ++i) {
-    TextInputManagerValueObserver observer(active_contents(), values[i]);
+    content::TextInputManagerValueObserver observer(active_contents(),
+                                                    values[i]);
     SimulateKeyPress(active_contents(), ui::DomKey::TAB, ui::DomCode::TAB,
                      ui::VKEY_TAB, false, false, false, false);
     observer.Wait();
@@ -492,7 +402,8 @@
       active_contents(), frames[0]->GetView(), ui::TEXT_INPUT_TYPE_NONE);
 
   for (size_t i = 0; i < frames.size(); ++i) {
-    TextInputManagerValueObserver observer(active_contents(), values[i]);
+    content::TextInputManagerValueObserver observer(active_contents(),
+                                                    values[i]);
     SimulateKeyPress(active_contents(), ui::DomKey::TAB, ui::DomCode::TAB,
                      ui::VKEY_TAB, false, false, false, false);
     observer.Wait();
@@ -562,8 +473,8 @@
     AddInputFieldToFrame(frames[i], "text", "", true);
 
   // Press tab key to focus the <input> in the first frame.
-  TextInputManagerTypeObserver type_observer_text_a(active_contents(),
-                                                    ui::TEXT_INPUT_TYPE_TEXT);
+  content::TextInputManagerTypeObserver type_observer_text_a(
+      active_contents(), ui::TEXT_INPUT_TYPE_TEXT);
   SimulateKeyPress(active_contents(), ui::DomKey::TAB, ui::DomCode::TAB,
                    ui::VKEY_TAB, false, false, false, false);
   type_observer_text_a.Wait();
@@ -573,22 +484,22 @@
       "frame.parentNode.removeChild(frame);";
   // Detach first frame and observe |TextInputState.type| resetting to
   // ui::TEXT_INPUT_TYPE_NONE.
-  TextInputManagerTypeObserver type_observer_none_a(active_contents(),
-                                                    ui::TEXT_INPUT_TYPE_NONE);
+  content::TextInputManagerTypeObserver type_observer_none_a(
+      active_contents(), ui::TEXT_INPUT_TYPE_NONE);
   EXPECT_TRUE(ExecuteScript(active_contents(), remove_first_iframe_script));
   type_observer_none_a.Wait();
 
   // Press tab to focus the <input> in the second frame.
-  TextInputManagerTypeObserver type_observer_text_b(active_contents(),
-                                                    ui::TEXT_INPUT_TYPE_TEXT);
+  content::TextInputManagerTypeObserver type_observer_text_b(
+      active_contents(), ui::TEXT_INPUT_TYPE_TEXT);
   SimulateKeyPress(active_contents(), ui::DomKey::TAB, ui::DomCode::TAB,
                    ui::VKEY_TAB, false, false, false, false);
   type_observer_text_b.Wait();
 
   // Detach first frame and observe |TextInputState.type| resetting to
   // ui::TEXT_INPUT_TYPE_NONE.
-  TextInputManagerTypeObserver type_observer_none_b(active_contents(),
-                                                    ui::TEXT_INPUT_TYPE_NONE);
+  content::TextInputManagerTypeObserver type_observer_none_b(
+      active_contents(), ui::TEXT_INPUT_TYPE_NONE);
   EXPECT_TRUE(ExecuteScript(active_contents(), remove_first_iframe_script));
   type_observer_none_b.Wait();
 }
@@ -606,15 +517,15 @@
   AddInputFieldToFrame(child_frame, "text", "child", false);
 
   // Focus <input> in child frame and verify the |TextInputState.value|.
-  TextInputManagerValueObserver child_set_state_observer(active_contents(),
-                                                         "child");
+  content::TextInputManagerValueObserver child_set_state_observer(
+      active_contents(), "child");
   SimulateKeyPress(active_contents(), ui::DomKey::TAB, ui::DomCode::TAB,
                    ui::VKEY_TAB, false, false, false, false);
   child_set_state_observer.Wait();
 
   // Navigate the child frame to about:blank and verify that TextInputManager
   // correctly sets its |TextInputState.type| to ui::TEXT_INPUT_TYPE_NONE.
-  TextInputManagerTypeObserver child_reset_state_observer(
+  content::TextInputManagerTypeObserver child_reset_state_observer(
       active_contents(), ui::TEXT_INPUT_TYPE_NONE);
   EXPECT_TRUE(ExecuteScript(
       main_frame, "document.querySelector('iframe').src = 'about:blank'"));
@@ -631,14 +542,14 @@
   content::RenderFrameHost* main_frame = GetFrame(IndexVector{});
   AddInputFieldToFrame(main_frame, "text", "", false);
 
-  TextInputManagerTypeObserver set_state_observer(active_contents(),
-                                                  ui::TEXT_INPUT_TYPE_TEXT);
+  content::TextInputManagerTypeObserver set_state_observer(
+      active_contents(), ui::TEXT_INPUT_TYPE_TEXT);
   SimulateKeyPress(active_contents(), ui::DomKey::TAB, ui::DomCode::TAB,
                    ui::VKEY_TAB, false, false, false, false);
   set_state_observer.Wait();
 
-  TextInputManagerTypeObserver reset_state_observer(active_contents(),
-                                                    ui::TEXT_INPUT_TYPE_NONE);
+  content::TextInputManagerTypeObserver reset_state_observer(
+      active_contents(), ui::TEXT_INPUT_TYPE_NONE);
   ui_test_utils::NavigateToURL(browser(), GURL("about:blank"));
   reset_state_observer.Wait();
 }
@@ -688,8 +599,8 @@
   AddInputFieldToFrame(main_frame, "text", "", false);
 
   // Focus the input and wait for state update.
-  TextInputManagerTypeObserver observer(active_contents(),
-                                        ui::TEXT_INPUT_TYPE_TEXT);
+  content::TextInputManagerTypeObserver observer(active_contents(),
+                                                 ui::TEXT_INPUT_TYPE_TEXT);
   SimulateKeyPress(active_contents(), ui::DomKey::TAB, ui::DomCode::TAB,
                    ui::VKEY_TAB, false, false, false, false);
   observer.Wait();
@@ -719,7 +630,8 @@
 
   auto send_tab_and_wait_for_value =
       [&web_contents](const std::string& expected_value) {
-        TextInputManagerValueObserver observer(web_contents, expected_value);
+        content::TextInputManagerValueObserver observer(web_contents,
+                                                        expected_value);
         SimulateKeyPress(web_contents, ui::DomKey::TAB, ui::DomCode::TAB,
                          ui::VKEY_TAB, false, false, false, false);
         observer.Wait();
@@ -814,7 +726,7 @@
   content::WebContents* web_contents = active_contents();
 
   auto send_tab_and_wait_for_value = [&web_contents](const std::string& value) {
-    TextInputManagerValueObserver observer(web_contents, value);
+    content::TextInputManagerValueObserver observer(web_contents, value);
     SimulateKeyPress(web_contents, ui::DomKey::TAB, ui::DomCode::TAB,
                      ui::VKEY_TAB, false, false, false, false);
     observer.Wait();
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index f462bfd..bbd26f9 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -1872,6 +1872,8 @@
       "ash/holding_space/holding_space_keyed_service.h",
       "ash/holding_space/holding_space_keyed_service_factory.cc",
       "ash/holding_space/holding_space_keyed_service_factory.h",
+      "ash/holding_space/holding_space_util.cc",
+      "ash/holding_space/holding_space_util.h",
       "ash/image_downloader_impl.cc",
       "ash/image_downloader_impl.h",
       "ash/ime_controller_client.cc",
@@ -2752,6 +2754,8 @@
       "webui/settings/system_handler.h",
       "webui/signin/inline_login_handler_impl.cc",
       "webui/signin/inline_login_handler_impl.h",
+      "webui/signin/profile_creation_customize_themes_handler.cc",
+      "webui/signin/profile_creation_customize_themes_handler.h",
       "webui/signin/profile_picker_handler.cc",
       "webui/signin/profile_picker_handler.h",
       "webui/signin/profile_picker_ui.cc",
@@ -2796,7 +2800,10 @@
       "webui/welcome/welcome_ui.h",
     ]
 
-    deps += [ "//components/country_codes" ]
+    deps += [
+      "//components/country_codes",
+      "//ui/webui/resources/cr_components/customize_themes:mojom",
+    ]
 
     if (enable_dice_support) {
       sources += [
diff --git a/chrome/browser/ui/ash/chrome_new_window_client.cc b/chrome/browser/ui/ash/chrome_new_window_client.cc
index 0be06289..3e59464 100644
--- a/chrome/browser/ui/ash/chrome_new_window_client.cc
+++ b/chrome/browser/ui/ash/chrome_new_window_client.cc
@@ -25,6 +25,7 @@
 #include "chrome/browser/chromeos/arc/intent_helper/custom_tab_session_impl.h"
 #include "chrome/browser/chromeos/file_manager/app_id.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
+#include "chrome/browser/chromeos/web_applications/chrome_camera_app_ui_delegate.h"
 #include "chrome/browser/extensions/api/terminal/terminal_extension_helper.h"
 #include "chrome/browser/extensions/extension_util.h"
 #include "chrome/browser/prefs/incognito_mode_prefs.h"
@@ -49,6 +50,7 @@
 #include "chrome/browser/web_applications/components/app_registrar.h"
 #include "chrome/browser/web_applications/components/web_app_helpers.h"
 #include "chrome/browser/web_applications/components/web_app_provider_base.h"
+#include "chrome/browser/web_applications/system_web_app_manager.h"
 #include "chrome/common/extensions/extension_constants.h"
 #include "chrome/common/url_constants.h"
 #include "chrome/common/webui_url_constants.h"
@@ -608,6 +610,16 @@
 
 void ChromeNewWindowClient::LaunchCameraApp(const std::string& queries,
                                             int32_t task_id) {
+  apps::RecordAppLaunch(extension_misc::kCameraAppId,
+                        apps::mojom::LaunchSource::kFromArc);
+
+  if (web_app::SystemWebAppManager::IsAppEnabled(
+          web_app::SystemAppType::CAMERA)) {
+    ChromeCameraAppUIDelegate::CameraAppDialog::ShowIntent(
+        queries, arc::GetArcWindow(task_id));
+    return;
+  }
+
   Profile* const profile = ProfileManager::GetActiveUserProfile();
   const extensions::ExtensionRegistry* registry =
       extensions::ExtensionRegistry::Get(profile);
@@ -622,9 +634,6 @@
   apps::LaunchPlatformAppWithUrl(profile, extension,
                                  /*handler_id=*/std::string(), url,
                                  /*referrer_url=*/GURL());
-
-  apps::RecordAppLaunch(extension_misc::kCameraAppId,
-                        apps::mojom::LaunchSource::kFromArc);
 }
 
 void ChromeNewWindowClient::CloseCameraApp() {
diff --git a/chrome/browser/ui/ash/holding_space/holding_space_keyed_service.cc b/chrome/browser/ui/ash/holding_space/holding_space_keyed_service.cc
index b8ddc482..acf6b949 100644
--- a/chrome/browser/ui/ash/holding_space/holding_space_keyed_service.cc
+++ b/chrome/browser/ui/ash/holding_space/holding_space_keyed_service.cc
@@ -13,9 +13,11 @@
 #include "chrome/browser/chromeos/file_manager/app_id.h"
 #include "chrome/browser/chromeos/file_manager/fileapi_util.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/ash/holding_space/holding_space_util.h"
 #include "components/account_id/account_id.h"
 #include "components/pref_registry/pref_registry_syncable.h"
 #include "components/prefs/scoped_user_pref_update.h"
+#include "storage/browser/file_system/file_system_url.h"
 
 namespace ash {
 
@@ -27,6 +29,10 @@
   return profile->GetPrefs();
 }
 
+ProfileManager* GetProfileManager() {
+  return g_browser_process->profile_manager();
+}
+
 }  // namespace
 
 // static
@@ -36,38 +42,56 @@
     content::BrowserContext* context,
     const AccountId& account_id)
     : browser_context_(context),
+      account_id_(account_id),
       holding_space_client_(Profile::FromBrowserContext(context)) {
-  RestoreModel();
-  holding_space_model_observer_.Add(&holding_space_model_);
-  HoldingSpaceController::Get()->RegisterClientAndModelForUser(
-      account_id, &holding_space_client_, &holding_space_model_);
-
-  // Observe the profile manager to get notified when the profile creation
-  // finishes to start handling user downloads. The keyed service is created
-  // with the profile, and at this stage the download manager may not be
-  // ready to be used.
-  if (g_browser_process->profile_manager()->IsValidProfile(
-          Profile::FromBrowserContext(browser_context_))) {
-    download_manager_ =
-        content::BrowserContext::GetDownloadManager(browser_context_);
-    download_manager_->AddObserver(this);
-  } else {
-    observed_profile_manager_.Add(g_browser_process->profile_manager());
+  // If the service's associated profile is ready, we can proceed to restore the
+  // `holding_space_model_` from persistence.
+  ProfileManager* const profile_manager = GetProfileManager();
+  if (profile_manager->IsValidProfile(Profile::FromBrowserContext(context))) {
+    RestoreModelFromPersistence();
+    return;
   }
+  // Otherwise we need to wait for the profile to be added.
+  profile_manager_observer_.Add(profile_manager);
 }
 
 HoldingSpaceKeyedService::~HoldingSpaceKeyedService() = default;
 
-void HoldingSpaceKeyedService::Shutdown() {
-  RemoveDownloadManagerObservers();
-}
-
 // static
 void HoldingSpaceKeyedService::RegisterProfilePrefs(
     user_prefs::PrefRegistrySyncable* registry) {
   registry->RegisterListPref(kPersistencePath);
 }
 
+void HoldingSpaceKeyedService::AddPinnedFile(
+    const storage::FileSystemURL& file_system_url) {
+  auto item = HoldingSpaceItem::CreateFileBackedItem(
+      HoldingSpaceItem::Type::kPinnedFile, file_system_url.path(),
+      file_system_url.ToGURL(), gfx::ImageSkia());
+  holding_space_model_.AddItem(std::move(item));
+}
+
+void HoldingSpaceKeyedService::RemovePinnedFile(
+    const storage::FileSystemURL& file_system_url) {
+  holding_space_model_.RemoveItem(HoldingSpaceItem::GetFileBackedItemId(
+      HoldingSpaceItem::Type::kPinnedFile, file_system_url.path()));
+}
+
+bool HoldingSpaceKeyedService::ContainsPinnedFile(
+    const storage::FileSystemURL& file_system_url) const {
+  return holding_space_model_.GetItem(HoldingSpaceItem::GetFileBackedItemId(
+      HoldingSpaceItem::Type::kPinnedFile, file_system_url.path()));
+}
+
+std::vector<GURL> HoldingSpaceKeyedService::GetPinnedFiles() const {
+  std::vector<GURL> pinned_files;
+  for (const auto& item : holding_space_model_.items()) {
+    if (item->type() == HoldingSpaceItem::Type::kPinnedFile)
+      pinned_files.push_back(item->file_system_url());
+  }
+  return pinned_files;
+}
+
 void HoldingSpaceKeyedService::AddScreenshot(
     const base::FilePath& screenshot_file,
     const gfx::ImageSkia& image) {
@@ -93,6 +117,17 @@
   holding_space_model_.AddItem(std::move(item));
 }
 
+void HoldingSpaceKeyedService::SetDownloadManagerForTesting(
+    content::DownloadManager* manager) {
+  RemoveDownloadManagerObservers();
+  download_manager_ = manager;
+  download_manager_->AddObserver(this);
+}
+
+void HoldingSpaceKeyedService::Shutdown() {
+  RemoveDownloadManagerObservers();
+}
+
 void HoldingSpaceKeyedService::OnHoldingSpaceItemAdded(
     const HoldingSpaceItem* item) {
   // `kDownload` type holding space items have their own persistence mechanism.
@@ -118,19 +153,117 @@
   });
 }
 
+void HoldingSpaceKeyedService::OnProfileAdded(Profile* profile) {
+  if (profile == Profile::FromBrowserContext(browser_context_)) {
+    profile_manager_observer_.Remove(GetProfileManager());
+    RestoreModelFromPersistence();
+  }
+}
+
+void HoldingSpaceKeyedService::ManagerGoingDown(
+    content::DownloadManager* manager) {
+  RemoveDownloadManagerObservers();
+  download_manager_ = nullptr;
+}
+
+void HoldingSpaceKeyedService::OnDownloadCreated(
+    content::DownloadManager* manager,
+    download::DownloadItem* item) {
+  download_items_observer_.Add(item);
+}
+
+void HoldingSpaceKeyedService::OnDownloadUpdated(download::DownloadItem* item) {
+  switch (item->GetState()) {
+    case download::DownloadItem::COMPLETE:
+      AddDownload(item->GetFullPath());
+      FALLTHROUGH;
+    case download::DownloadItem::CANCELLED:
+    case download::DownloadItem::INTERRUPTED:
+      download_items_observer_.Remove(item);
+      break;
+    case download::DownloadItem::IN_PROGRESS:
+    case download::DownloadItem::MAX_DOWNLOAD_STATE:
+      break;
+  }
+}
+
+void HoldingSpaceKeyedService::RemoveDownloadManagerObservers() {
+  if (download_manager_)
+    download_manager_->RemoveObserver(this);
+  download_items_observer_.RemoveAll();
+}
+
 // TODO(dmblack): Restore download holding space items.
-void HoldingSpaceKeyedService::RestoreModel() {
+void HoldingSpaceKeyedService::RestoreModelFromPersistence() {
   DCHECK(holding_space_model_.items().empty());
-  const auto* holding_space_items =
+
+  const auto* persisted_holding_space_items =
       GetPrefService(browser_context_)->GetList(kPersistencePath);
-  for (const auto& holding_space_item : holding_space_items->GetList()) {
-    holding_space_model_.AddItem(HoldingSpaceItem::Deserialize(
-        base::Value::AsDictionaryValue(holding_space_item),
+
+  if (persisted_holding_space_items->GetList().empty()) {
+    OnModelRestored();
+    return;
+  }
+
+  std::vector<HoldingSpaceItemPtr> holding_space_items;
+  for (const auto& persisted_holding_space_item :
+       persisted_holding_space_items->GetList()) {
+    holding_space_items.push_back(HoldingSpaceItem::Deserialize(
+        base::Value::AsDictionaryValue(persisted_holding_space_item),
         base::BindOnce(&HoldingSpaceKeyedService::ResolveFileSystemUrl,
                        base::Unretained(this)),
         base::BindOnce(&HoldingSpaceKeyedService::ResolveImage,
                        base::Unretained(this))));
   }
+
+  holding_space_util::PartitionItemsByExistence(
+      Profile::FromBrowserContext(browser_context_),
+      std::move(holding_space_items),
+      base::BindOnce(&HoldingSpaceKeyedService::RestoreModelByExistence,
+                     weak_factory_.GetWeakPtr()));
+}
+
+void HoldingSpaceKeyedService::RestoreModelByExistence(
+    std::vector<HoldingSpaceItemPtr> existing_items,
+    std::vector<HoldingSpaceItemPtr> non_existing_items) {
+  DCHECK(holding_space_model_.items().empty());
+
+  // Restore existing holding space items.
+  for (auto& holding_space_item : existing_items)
+    holding_space_model_.AddItem(std::move(holding_space_item));
+
+  // Clean up non-existing holding space items from persistence.
+  if (!non_existing_items.empty()) {
+    ListPrefUpdate update(GetPrefService(browser_context_), kPersistencePath);
+    update->EraseListValueIf([&non_existing_items](
+                                 const base::Value& persisted_item) {
+      const std::string& persisted_item_id = HoldingSpaceItem::DeserializeId(
+          base::Value::AsDictionaryValue(persisted_item));
+      return std::any_of(
+          non_existing_items.begin(), non_existing_items.end(),
+          [&persisted_item_id](const HoldingSpaceItemPtr& non_existing_item) {
+            return non_existing_item->id() == persisted_item_id;
+          });
+    });
+  }
+
+  OnModelRestored();
+}
+
+void HoldingSpaceKeyedService::OnModelRestored() {
+  holding_space_model_observer_.Add(&holding_space_model_);
+  HoldingSpaceController::Get()->RegisterClientAndModelForUser(
+      account_id_, &holding_space_client_, &holding_space_model_);
+
+  // NOTE: `download_manager_` may have already been set in tests.
+  if (download_manager_)
+    return;
+
+  // Once the `holding_space_model_` has been restored from persistence, we can
+  // start to observe the `download_manager_` to track downloaded files.
+  download_manager_ =
+      content::BrowserContext::GetDownloadManager(browser_context_);
+  download_manager_->AddObserver(this);
 }
 
 GURL HoldingSpaceKeyedService::ResolveFileSystemUrl(
@@ -150,66 +283,4 @@
   return GetIconForPath(file_path);
 }
 
-void HoldingSpaceKeyedService::SetDownloadManagerForTesting(
-    content::DownloadManager* manager) {
-  RemoveDownloadManagerObservers();
-  download_manager_ = manager;
-  download_manager_->AddObserver(this);
-}
-
-void HoldingSpaceKeyedService::OnProfileAdded(Profile* profile) {
-  if (!profile->IsSameOrParent(Profile::FromBrowserContext(browser_context_)))
-    return;
-
-  observed_profile_manager_.RemoveAll();
-
-  // Download Manager may have been already set in tests.
-  if (download_manager_)
-    return;
-
-  download_manager_ =
-      content::BrowserContext::GetDownloadManager(browser_context_);
-  download_manager_->AddObserver(this);
-}
-
-void HoldingSpaceKeyedService::OnDownloadCreated(
-    content::DownloadManager* manager,
-    download::DownloadItem* item) {
-  download_items_observer_.Add(item);
-}
-
-void HoldingSpaceKeyedService::OnDownloadDropped(
-    content::DownloadManager* manager) {}
-
-void HoldingSpaceKeyedService::OnManagerInitialized() {}
-
-void HoldingSpaceKeyedService::ManagerGoingDown(
-    content::DownloadManager* manager) {
-  RemoveDownloadManagerObservers();
-  download_manager_ = nullptr;
-}
-
-void HoldingSpaceKeyedService::OnDownloadUpdated(download::DownloadItem* item) {
-  download::DownloadItem::DownloadState state = item->GetState();
-  if (state == download::DownloadItem::COMPLETE ||
-      state == download::DownloadItem::CANCELLED ||
-      state == download::DownloadItem::INTERRUPTED) {
-    // Stop observing now to ensure we only send one complete/fail notification.
-    download_items_observer_.Remove(item);
-
-    if (state == download::DownloadItem::COMPLETE) {
-      const base::FilePath download_path = item->GetFullPath();
-      AddDownload(download_path);
-    }
-  }
-}
-
-void HoldingSpaceKeyedService::RemoveDownloadManagerObservers() {
-  if (!download_manager_)
-    return;
-
-  download_manager_->RemoveObserver(this);
-  download_items_observer_.RemoveAll();
-}
-
 }  // namespace ash
diff --git a/chrome/browser/ui/ash/holding_space/holding_space_keyed_service.h b/chrome/browser/ui/ash/holding_space/holding_space_keyed_service.h
index 503eccd..35bddfe4 100644
--- a/chrome/browser/ui/ash/holding_space/holding_space_keyed_service.h
+++ b/chrome/browser/ui/ash/holding_space/holding_space_keyed_service.h
@@ -5,6 +5,9 @@
 #ifndef CHROME_BROWSER_UI_ASH_HOLDING_SPACE_HOLDING_SPACE_KEYED_SERVICE_H_
 #define CHROME_BROWSER_UI_ASH_HOLDING_SPACE_HOLDING_SPACE_KEYED_SERVICE_H_
 
+#include <memory>
+#include <vector>
+
 #include "ash/public/cpp/holding_space/holding_space_model.h"
 #include "ash/public/cpp/holding_space/holding_space_model_observer.h"
 #include "base/scoped_observer.h"
@@ -16,6 +19,7 @@
 #include "components/download/public/common/download_item.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "content/public/browser/download_manager.h"
+#include "url/gurl.h"
 
 class GURL;
 
@@ -35,8 +39,15 @@
 class ImageSkia;
 }  // namespace gfx
 
+namespace storage {
+class FileSystemURL;
+}
+
 namespace ash {
 
+class HoldingSpaceItem;
+using HoldingSpaceItemPtr = std::unique_ptr<HoldingSpaceItem>;
+
 // Browser context keyed service that:
 // *   Manages the temporary holding space per-profile data model.
 // *   Serves as an entry point to add holding space items from Chrome.
@@ -60,32 +71,30 @@
   // Registers profile preferences for holding space.
   static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
 
-  // Methods to add an item backed by the provided absolute file path.
+  // Adds a pinned file item identified by the provided file system URL.
+  void AddPinnedFile(const storage::FileSystemURL& file_system_url);
+
+  // Removes a pinned file item identified by the provided file system URL.
+  // No-op if the file is not present in the holding space.
+  void RemovePinnedFile(const storage::FileSystemURL& file_system_url);
+
+  // Returns whether the holding space contains a pinned file identified by a
+  // file system URL.
+  bool ContainsPinnedFile(const storage::FileSystemURL& file_system_url) const;
+
+  // Returns the list of pinned files in the holding space. It returns the files
+  // files system URLs as GURLs.
+  std::vector<GURL> GetPinnedFiles() const;
+
+  // Adds a screenshot item backed by the provided absolute file path.
   // The path is expected to be under a mount point path recognized by the file
   // manager app (otherwise, the item will be dropped silently).
   void AddScreenshot(const base::FilePath& screenshot_path,
                      const gfx::ImageSkia& image);
+
+  // Adds a download item backed by the provided absolute file path.
   void AddDownload(const base::FilePath& download_path);
 
-  void RemoveDownloadManagerObservers();
-
-  // KeyedService:
-  void Shutdown() override;
-
-  // ProfileManagerObserver:
-  void OnProfileAdded(Profile* profile) override;
-
-  // content::DownloadManager::Observer implementation:
-  void OnDownloadCreated(content::DownloadManager* manager,
-                         download::DownloadItem* item) override;
-  void OnDownloadDropped(content::DownloadManager* manager) override;
-  void OnManagerInitialized() override;
-  void ManagerGoingDown(content::DownloadManager* manager) override;
-
-  // download::DownloadItem::Observer implementation:
-  void OnDownloadUpdated(download::DownloadItem* item) override;
-
-
   const HoldingSpaceClient* client_for_testing() const {
     return &holding_space_client_;
   }
@@ -93,32 +102,61 @@
   const HoldingSpaceModel* model_for_testing() const {
     return &holding_space_model_;
   }
+
   void SetDownloadManagerForTesting(content::DownloadManager* manager);
 
  private:
+  // KeyedService:
+  void Shutdown() override;
+
   // HoldingSpaceModelObserver:
   void OnHoldingSpaceItemAdded(const HoldingSpaceItem* item) override;
   void OnHoldingSpaceItemRemoved(const HoldingSpaceItem* item) override;
 
-  // Restores |holding_space_model_| from persistent storage.
-  void RestoreModel();
+  // ProfileManagerObserver:
+  void OnProfileAdded(Profile* profile) override;
+
+  // content::DownloadManager::Observer:
+  void ManagerGoingDown(content::DownloadManager* manager) override;
+  void OnDownloadCreated(content::DownloadManager* manager,
+                         download::DownloadItem* item) override;
+
+  // download::DownloadItem::Observer:
+  void OnDownloadUpdated(download::DownloadItem* item) override;
+
+  // Removes all observers from:
+  // - `download_manager_`
+  // - `download_items_observer_`.
+  void RemoveDownloadManagerObservers();
+
+  // Restores `holding_space_model_` from persistent storage.
+  void RestoreModelFromPersistence();
+  void RestoreModelByExistence(
+      std::vector<HoldingSpaceItemPtr> existing_items,
+      std::vector<HoldingSpaceItemPtr> non_existing_items);
+  void OnModelRestored();
 
   // Resolves file attributes from a file path;
   GURL ResolveFileSystemUrl(const base::FilePath& file_path) const;
   gfx::ImageSkia ResolveImage(const base::FilePath& file_path) const;
 
   content::BrowserContext* const browser_context_;
+  const AccountId account_id_;
+
   HoldingSpaceClientImpl holding_space_client_;
   HoldingSpaceModel holding_space_model_;
 
   ScopedObserver<HoldingSpaceModel, HoldingSpaceModelObserver>
       holding_space_model_observer_{this};
 
-  content::DownloadManager* download_manager_ = nullptr;
   ScopedObserver<ProfileManager, ProfileManagerObserver>
-      observed_profile_manager_{this};
+      profile_manager_observer_{this};
+
+  content::DownloadManager* download_manager_ = nullptr;
   ScopedObserver<download::DownloadItem, download::DownloadItem::Observer>
       download_items_observer_{this};
+
+  base::WeakPtrFactory<HoldingSpaceKeyedService> weak_factory_{this};
 };
 
 }  // namespace ash
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 efd7d77..19cd8b8 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
@@ -9,11 +9,13 @@
 #include "ash/public/cpp/ash_features.h"
 #include "ash/public/cpp/file_icon_util.h"
 #include "ash/public/cpp/holding_space/holding_space_controller.h"
+#include "ash/public/cpp/holding_space/holding_space_controller_observer.h"
 #include "ash/public/cpp/holding_space/holding_space_item.h"
 #include "ash/public/cpp/holding_space/holding_space_model.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
 #include "base/guid.h"
+#include "base/scoped_observer.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/bind_test_util.h"
 #include "base/test/scoped_feature_list.h"
@@ -97,6 +99,46 @@
   std::string name_;
 };
 
+// Utility class which can wait until a `HoldingSpaceModel` for a given profile
+// is attached to the `HoldingSpaceController`.
+class HoldingSpaceModelAttachedWaiter : public HoldingSpaceControllerObserver {
+ public:
+  explicit HoldingSpaceModelAttachedWaiter(Profile* profile)
+      : profile_(profile) {
+    holding_space_controller_observer_.Add(HoldingSpaceController::Get());
+  }
+
+  void Wait() {
+    if (IsModelAttached())
+      return;
+
+    wait_loop_ = std::make_unique<base::RunLoop>();
+    wait_loop_->Run();
+    wait_loop_.reset();
+  }
+
+ private:
+  // HoldingSpaceControllerObserver:
+  void OnHoldingSpaceModelAttached(HoldingSpaceModel* model) override {
+    if (wait_loop_ && IsModelAttached())
+      wait_loop_->Quit();
+  }
+
+  void OnHoldingSpaceModelDetached(HoldingSpaceModel* model) override {}
+
+  bool IsModelAttached() const {
+    HoldingSpaceKeyedService* const holding_space_service =
+        HoldingSpaceKeyedServiceFactory::GetInstance()->GetService(profile_);
+    return HoldingSpaceController::Get()->model() ==
+           holding_space_service->model_for_testing();
+  }
+
+  Profile* const profile_;
+  ScopedObserver<HoldingSpaceController, HoldingSpaceControllerObserver>
+      holding_space_controller_observer_{this};
+  std::unique_ptr<base::RunLoop> wait_loop_;
+};
+
 }  // namespace
 
 class HoldingSpaceKeyedServiceTest : public BrowserWithTestWindowTest {
@@ -256,7 +298,9 @@
     return item;
   }
 
-  content::MockDownloadManager* manager() { return download_manager_.get(); }
+  content::MockDownloadManager* download_manager() {
+    return download_manager_.get();
+  }
 
  private:
   chromeos::FakeChromeUserManager* fake_user_manager_;
@@ -413,18 +457,21 @@
   }
 }
 
-// Verifies that the holding space model is restored from persistence.
+// Verifies that the holding space model is restored from persistence. Note that
+// when restoring from persistence, existence of backing files is verified and
+// any stale holding space items are removed.
 TEST_F(HoldingSpaceKeyedServiceTest, RestorePersistentStorage) {
   // Create file system mount point.
   ScopedDownloadsMountPoint downloads_mount(GetProfile());
   ASSERT_TRUE(downloads_mount.IsValid());
 
-  HoldingSpaceModel::ItemList persisted_holding_space_items;
+  HoldingSpaceModel::ItemList restored_holding_space_items;
+  base::ListValue persisted_holding_space_items_after_restoration;
 
   // Create a secondary profile w/ a pre-populated pref store.
-  TestingProfile* const second_profile = CreateSecondaryProfile(
+  TestingProfile* const secondary_profile = CreateSecondaryProfile(
       base::BindLambdaForTesting([&](TestingPrefStore* pref_store) {
-        auto serialized_holding_space_items =
+        auto persisted_holding_space_items_before_restoration =
             std::make_unique<base::ListValue>();
 
         // Persist some holding space items of each type.
@@ -436,27 +483,48 @@
           const base::FilePath file = CreateArbitraryFile(downloads_mount);
           const GURL file_system_url = GetFileSystemUrl(GetProfile(), file);
 
-          auto holding_space_item = HoldingSpaceItem::CreateFileBackedItem(
-              type, file, file_system_url, GetIconForPath(file));
+          auto fresh_holding_space_item =
+              HoldingSpaceItem::CreateFileBackedItem(
+                  type, file, file_system_url, GetIconForPath(file));
 
-          serialized_holding_space_items->Append(
-              holding_space_item->Serialize());
+          persisted_holding_space_items_before_restoration->Append(
+              fresh_holding_space_item->Serialize());
 
-          persisted_holding_space_items.push_back(
-              std::move(holding_space_item));
+          // We expect the `fresh_holding_space_item` to still be in persistence
+          // after model restoration since its backing file exists.
+          persisted_holding_space_items_after_restoration.Append(
+              fresh_holding_space_item->Serialize());
+
+          // We expect the `fresh_holding_space_item` to be restored from
+          // persistence since its backing file exists.
+          restored_holding_space_items.push_back(
+              std::move(fresh_holding_space_item));
+
+          auto stale_holding_space_item =
+              HoldingSpaceItem::CreateFileBackedItem(
+                  type,
+                  base::FilePath(base::UnguessableToken::Create().ToString()),
+                  GURL(), gfx::ImageSkia());
+
+          // NOTE: While the `stale_holding_space_item` is persisted here, we do
+          // *not* expect it to be restored or to be persisted after model
+          // restoration since its backing file does *not* exist.
+          persisted_holding_space_items_before_restoration->Append(
+              stale_holding_space_item->Serialize());
         }
 
         pref_store->SetValueSilently(
             HoldingSpaceKeyedService::kPersistencePath,
-            std::move(serialized_holding_space_items),
+            std::move(persisted_holding_space_items_before_restoration),
             PersistentPrefStore::DEFAULT_PREF_WRITE_FLAGS);
       }));
 
   ActivateSecondaryProfile();
+  HoldingSpaceModelAttachedWaiter(secondary_profile).Wait();
 
   HoldingSpaceKeyedService* const secondary_holding_space_service =
       HoldingSpaceKeyedServiceFactory::GetInstance()->GetService(
-          second_profile);
+          secondary_profile);
   HoldingSpaceModel* const secondary_holding_space_model =
       HoldingSpaceController::Get()->model();
 
@@ -464,16 +532,22 @@
             secondary_holding_space_service->model_for_testing());
 
   EXPECT_EQ(secondary_holding_space_model->items().size(),
-            persisted_holding_space_items.size());
+            restored_holding_space_items.size());
 
+  // Verify in-memory holding space items.
   for (size_t i = 0; i < secondary_holding_space_model->items().size(); ++i) {
     const auto& item = secondary_holding_space_model->items()[i];
-    const auto& persisted_item = persisted_holding_space_items[i];
-    EXPECT_EQ(*item, *persisted_item)
+    const auto& restored_item = restored_holding_space_items[i];
+    EXPECT_EQ(*item, *restored_item)
         << "Expected equality of values at index " << i << ":"
         << "\n\tActual: " << item->id()
-        << "\n\tPersisted: " << persisted_item->id();
+        << "\n\rRestored: " << restored_item->id();
   }
+
+  // Verify persisted holding space items.
+  EXPECT_EQ(*secondary_profile->GetPrefs()->GetList(
+                HoldingSpaceKeyedService::kPersistencePath),
+            persisted_holding_space_items_after_restoration);
 }
 
 TEST_F(HoldingSpaceKeyedServiceTest, AddDownloadItem) {
@@ -489,7 +563,7 @@
   const base::FilePath download_item_full_path =
       CreateFile(downloads_mount, download_item_virtual_path, "download 1");
 
-  content::MockDownloadManager* mock_download_manager = manager();
+  content::MockDownloadManager* mock_download_manager = download_manager();
   std::unique_ptr<download::MockDownloadItem> item(
       CreateMockInProgressDownload(download_item_full_path));
 
@@ -499,14 +573,15 @@
 
   download::MockDownloadItem* mock_download_item = item.get();
   EXPECT_CALL(*mock_download_manager, MockCreateDownloadItem(testing::_))
-      .WillRepeatedly(
-          testing::DoAll(testing::InvokeWithoutArgs([&holding_space_service,
-                                                     mock_download_manager,
-                                                     mock_download_item]() {
-                           holding_space_service->OnDownloadCreated(
-                               mock_download_manager, mock_download_item);
-                         }),
-                         testing::Return(item.get())));
+      .WillRepeatedly(testing::DoAll(
+          testing::InvokeWithoutArgs([&holding_space_service,
+                                      mock_download_manager,
+                                      mock_download_item]() {
+            static_cast<content::DownloadManager::Observer*>(
+                holding_space_service)
+                ->OnDownloadCreated(mock_download_manager, mock_download_item);
+          }),
+          testing::Return(item.get())));
 
   std::vector<GURL> url_chain;
   url_chain.push_back(item->GetURL());
diff --git a/chrome/browser/ui/ash/holding_space/holding_space_util.cc b/chrome/browser/ui/ash/holding_space/holding_space_util.cc
new file mode 100644
index 0000000..2a89075b
--- /dev/null
+++ b/chrome/browser/ui/ash/holding_space/holding_space_util.cc
@@ -0,0 +1,89 @@
+// 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/ash/holding_space/holding_space_util.h"
+
+#include "ash/public/cpp/holding_space/holding_space_item.h"
+#include "base/barrier_closure.h"
+#include "chrome/browser/chromeos/file_manager/app_id.h"
+#include "chrome/browser/chromeos/file_manager/fileapi_util.h"
+#include "storage/browser/file_system/file_system_context.h"
+
+namespace ash {
+namespace holding_space_util {
+
+void ItemExists(Profile* profile,
+                const HoldingSpaceItem* item,
+                ItemExistsCallback callback) {
+  if (!item) {
+    std::move(callback).Run(/*exists=*/false);
+    return;
+  }
+  file_manager::util::GetMetadataForPath(
+      file_manager::util::GetFileSystemContextForExtensionId(
+          profile, file_manager::kFileManagerAppId),
+      item->file_path(), storage::FileSystemOperation::GET_METADATA_FIELD_NONE,
+      base::BindOnce(
+          [](ItemExistsCallback callback, base::File::Error result,
+             const base::File::Info& file_info) {
+            // Absence of error is confirmation of existence.
+            bool exists = result == base::File::Error::FILE_OK;
+            std::move(callback).Run(exists);
+          },
+          std::move(callback)));
+}
+
+void PartitionItemsByExistence(Profile* profile,
+                               HoldingSpaceItemPtrList items,
+                               PartitionItemsByExistenceCallback callback) {
+  if (items.empty()) {
+    std::move(callback).Run(/*existing_items=*/{}, /*non_existing_items=*/{});
+    return;
+  }
+
+  auto existing_items = std::make_unique<HoldingSpaceItemPtrList>();
+  auto non_existing_items = std::make_unique<HoldingSpaceItemPtrList>();
+
+  auto* existing_items_ptr = existing_items.get();
+  auto* non_existing_items_ptr = non_existing_items.get();
+
+  // This `barrier_closure` will be run after verifying the existence of all
+  // holding space `items`. It is expected that both `existing_items` and
+  // `non_existing_items` will have been populated by the time of invocation.
+  base::RepeatingClosure barrier_closure = base::BarrierClosure(
+      items.size(),
+      base::BindOnce(
+          [](std::unique_ptr<HoldingSpaceItemPtrList> existing_items,
+             std::unique_ptr<HoldingSpaceItemPtrList> non_existing_items,
+             PartitionItemsByExistenceCallback callback) {
+            std::move(callback).Run(std::move(*existing_items),
+                                    std::move(*non_existing_items));
+          },
+          std::move(existing_items), std::move(non_existing_items),
+          std::move(callback)));
+
+  // Verify existence of each holding space `item`. Upon successful check of
+  // existence, each `item` should be pushed into either `existing_items` or
+  // `non_existing_items` as appropriate.
+  for (auto& item : items) {
+    HoldingSpaceItem* item_ptr = item.get();
+    ItemExists(profile, item_ptr,
+               base::BindOnce(
+                   [](HoldingSpaceItemPtr item,
+                      HoldingSpaceItemPtrList* existing_items,
+                      HoldingSpaceItemPtrList* non_existing_items,
+                      base::RepeatingClosure barrier_closure, bool exists) {
+                     if (exists)
+                       existing_items->push_back(std::move(item));
+                     else
+                       non_existing_items->push_back(std::move(item));
+                     barrier_closure.Run();
+                   },
+                   std::move(item), base::Unretained(existing_items_ptr),
+                   base::Unretained(non_existing_items_ptr), barrier_closure));
+  }
+}
+
+}  // namespace holding_space_util
+}  // namespace ash
diff --git a/chrome/browser/ui/ash/holding_space/holding_space_util.h b/chrome/browser/ui/ash/holding_space/holding_space_util.h
new file mode 100644
index 0000000..af7c7a3
--- /dev/null
+++ b/chrome/browser/ui/ash/holding_space/holding_space_util.h
@@ -0,0 +1,42 @@
+// 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_ASH_HOLDING_SPACE_HOLDING_SPACE_UTIL_H_
+#define CHROME_BROWSER_UI_ASH_HOLDING_SPACE_HOLDING_SPACE_UTIL_H_
+
+#include <memory>
+#include <vector>
+
+#include "base/callback_forward.h"
+
+class Profile;
+
+namespace ash {
+
+class HoldingSpaceItem;
+using HoldingSpaceItemPtr = std::unique_ptr<HoldingSpaceItem>;
+using HoldingSpaceItemPtrList = std::vector<HoldingSpaceItemPtr>;
+
+// A utility for holding space.
+namespace holding_space_util {
+
+// Checks `item` existence, returning the result via `callback`.
+using ItemExistsCallback = base::OnceCallback<void(bool)>;
+void ItemExists(Profile* profile,
+                const HoldingSpaceItem* item,
+                ItemExistsCallback callback);
+
+// Partitions `items` into `existing_items` and `non_existing_items`, returning
+// the result via `callback`.
+using PartitionItemsByExistenceCallback =
+    base::OnceCallback<void(HoldingSpaceItemPtrList existing_items,
+                            HoldingSpaceItemPtrList non_existing_items)>;
+void PartitionItemsByExistence(Profile* profile,
+                               HoldingSpaceItemPtrList items,
+                               PartitionItemsByExistenceCallback callback);
+
+}  // namespace holding_space_util
+}  // namespace ash
+
+#endif  // CHROME_BROWSER_UI_ASH_HOLDING_SPACE_HOLDING_SPACE_UTIL_H_
diff --git a/chrome/browser/ui/ash/launcher/app_service/app_service_app_window_launcher_controller.cc b/chrome/browser/ui/ash/launcher/app_service/app_service_app_window_launcher_controller.cc
index 7aa5bff..6b3b5897 100644
--- a/chrome/browser/ui/ash/launcher/app_service/app_service_app_window_launcher_controller.cc
+++ b/chrome/browser/ui/ash/launcher/app_service/app_service_app_window_launcher_controller.cc
@@ -515,7 +515,7 @@
       static_cast<exo::ShellSurfaceBase*>(
           views::Widget::GetWidgetForNativeWindow(window)->widget_delegate())
           ->SetIcon(*ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
-              IDR_LOGO_PLUGIN_VM_DEFAULT_32));
+              IDR_LOGO_PLUGIN_VM_DEFAULT_192));
     }
   }
 }
diff --git a/chrome/browser/ui/chrome_pages.cc b/chrome/browser/ui/chrome_pages.cc
index 21f68f7..647a6b4 100644
--- a/chrome/browser/ui/chrome_pages.cc
+++ b/chrome/browser/ui/chrome_pages.cc
@@ -521,7 +521,7 @@
   DCHECK(entry_point == PrintManagementAppEntryPoint::kSettings ||
          entry_point == PrintManagementAppEntryPoint::kNotification);
 
-  base::UmaHistogramEnumeration("Printing.Cups.PrintManagementAppEntryPoint",
+  base::UmaHistogramEnumeration("Printing.CUPS.PrintManagementAppEntryPoint",
                                 entry_point);
   LaunchSystemWebApp(profile, web_app::SystemAppType::PRINT_MANAGEMENT,
                      GURL(chrome::kChromeUIPrintManagementUrl));
diff --git a/chrome/browser/ui/hats/OWNERS b/chrome/browser/ui/hats/OWNERS
index b48067f..26ca81a 100644
--- a/chrome/browser/ui/hats/OWNERS
+++ b/chrome/browser/ui/hats/OWNERS
@@ -1,3 +1,3 @@
 feuunk@chromium.org
 msramek@chromium.org
-sauski@chromium.org
+sauski@google.com
diff --git a/chrome/browser/ui/omnibox/omnibox_pedals_unittest.cc b/chrome/browser/ui/omnibox/omnibox_pedals_unittest.cc
index 5be76842..5b66c2b3 100644
--- a/chrome/browser/ui/omnibox/omnibox_pedals_unittest.cc
+++ b/chrome/browser/ui/omnibox/omnibox_pedals_unittest.cc
@@ -21,63 +21,531 @@
 
   struct TestCase {
     std::string locale;
-    std::string trigger;
+    std::vector<std::string> triggers;
   };
   const TestCase test_cases[] = {
       // clang-format off
       // Test cases generated by pedal_processor:
-      {"am", "አስወግድ መሸጎጫ"},
-      {"ar", "إزالة ذاكرة التخزين المؤقت"},
-      {"bg", "премахване"},
-      {"bn", "মিটিয়ে দিন ক্যাশে ফাইল"},
-      {"ca", "suprimeix memòria cau"},
-      {"cs", "odstranit soubory cookie"},
-      {"da", "fjern browseroplysninger"},
-      {"de", "entfernen browserverlauf"},
-      {"el", "εκκαθάριση πληροφορίες"},
-      {"en", "delete information"},
-      {"en-GB", "delete information"},
-      {"es", "eliminar información"},
-      {"es-419", "eliminar información"},
-      {"et", "tühjenda küpsisefailid"},
-      {"fa", "حذف کردن حافظه پنهان"},
-      {"fi", "tyhjennä historiatiedot"},
-      {"fil", "i-delete impormasyon"},
-      {"fr", "suppression informations"},
-      {"gu", "ડિલીટ કરો ઇતિહાસ"},
-      {"he", "איפוס נתונים"},
-      {"hi", "वाइप करें कैश मेमोरी"},
-      {"hr", "odstranjivanje privremena memorija"},
-      {"hu", "eltávolítása gyorsítótár"},
-      {"id", "hapus informasi"},
-      {"it", "cancellare informazioni"},
-      {"ja", "クリアキャッシュ"},
-      {"kn", "ತೆರವುಗೊಳಿಸಿ ಕುಕೀಗಳು"},
-      {"ko", "완전 삭제 데이터"},
-      {"lt", "pašalinti informacija"},
-      {"lv", "notīrīt informācija"},
-      {"ml", "നീക്കം ചെയ്യുക വിവരങ്ങൾ"},
-      {"mr", "काढून टाका इतिहास"},
-      {"ms", "alih keluar maklumat"},
-      {"nl", "google chrome verwijderen cachegeheugen"},
-      {"pl", "wyczyść pamięć podręczną przeglądarki"},
-      {"pt-BR", "excluir permanentemente informações"},
-      {"pt-PT", "eliminar informações"},
-      {"ro", "elimină informații"},
-      {"ru", "почистить информацию"},
-      {"sk", "odstrániť vyrovnávacia pamäť"},
-      {"sl", "izbriši predpomnilnik"},
-      {"sr", "избрисати информације"},
-      {"sv", "ta bort information"},
-      {"sw", "ondoa kumbukumbu"},
-      {"ta", "வைப் செய் தற்காலிகச் சேமிப்பு"},
-      {"te", "క్లియర్ చేయడం కుక్కీలు"},
-      {"th", "ล้างข้อมูล ประวัติ"},
-      {"tr", "kaldır önbelleği"},
-      {"uk", "видалити інформація"},
-      {"vi", "dọn sạch bộ nhớ đệm"},
-      {"zh-CN", "清除浏览数据"},
-      {"zh-TW", "刪除 歷史記錄"},
+      { "am",
+        {
+          "አስወግድ መሸጎጫ",
+          "አቀናባሪ የይለፍ ቃላት",
+          "አቀናብር የክሬዲት ካርድ መረጃ",
+          "ማንነት የማያሳውቅ መስኮት",
+          "ቋንቋ ቀይር ይህ ገጽ",
+          "google chrome አዘምን",
+        }
+      },
+      { "ar",
+        {
+          "إزالة ذاكرة التخزين المؤقت",
+          "تعديل بيانات الاعتماد",
+          "إدارة معلومات بطاقة الائتمان",
+          "علامة تبويب في وضع التصفّح المتخفي",
+          "تغيير اللغة هذه الصفحة",
+          "google chrome ترقية",
+        }
+      },
+      { "bg",
+        {
+          "премахване",
+          "актуализиране идентификацията",
+          "актуализиране информацията кредитната карта",
+          "стартиране поверително сърфиране",
+          "промяна езика тази страница",
+          "браузъра актуализиране",
+        }
+      },
+      { "bn",
+        {
+          "মিটিয়ে দিন ক্যাশে ফাইল",
+          "পরিবর্তন করুন ক্রেডেনশিয়াল",
+          "পরিবর্তন করুন ক্রেডিট কার্ডের তথ্য",
+          "ব্যক্তিগত উইন্ডো",
+          "ভাষা পরিবর্তন করুন এই পৃষ্ঠা",
+          "ব্রাউজার আপগ্রেড করুন",
+        }
+      },
+      { "ca",
+        {
+          "suprimeix memòria cau",
+          "administrador contrasenyes",
+          "administrar informació targeta crèdit",
+          "finestra d'incògnit",
+          "canviar l'idioma aquesta pàgina",
+          "google chrome actualitzar versió",
+        }
+      },
+      { "cs",
+        {
+          "odstranit soubory cookie",
+          "aktualizovat přihlašovací údaje",
+          "aktualizovat informace o platební kartě",
+          "anonymní režim",
+          "změnit jazyk tato stránka",
+          "google chrome nainstalovat",
+        }
+      },
+      { "da",
+        {
+          "fjern browseroplysninger",
+          "administrator loginoplysninger",
+          "administrer",
+          "inkognitotilstand",
+          "skift sprog denne side",
+          "google chrome installer",
+        }
+      },
+      { "de",
+        {
+          "entfernen browserverlauf",
+          "verwalten passwörter",
+          "aktualisieren karteninformationen",
+          "arbeiten inkognito fenster",
+          "sprache ändern diese webseite",
+          "google chrome aktualisieren",
+        }
+      },
+      { "el",
+        {
+          "εκκαθάριση πληροφορίες",
+          "διαχειριστής κωδικοί πρόσβασης",
+          "επεξεργασία πληροφορίες πιστωτικής κάρτας",
+          "παράθυρο για ανώνυμη περιήγηση",
+          "αλλαγή γλώσσας αυτή η σελίδα",
+          "πρόγραμμα περιήγησης εγκατάσταση",
+        }
+      },
+      { "en",
+        {
+          "delete information",
+          "manager credentials",
+          "manage credit card information",
+          "incognito window",
+          "change language this page",
+          "google chrome upgrade",
+        }
+      },
+      { "en-GB",
+        {
+          "delete information",
+          "manager credentials",
+          "manage credit card information",
+          "incognito window",
+          "change language this page",
+          "google chrome upgrade",
+        }
+      },
+      { "es",
+        {
+          "eliminar información",
+          "administrador contraseñas",
+          "actualizar información de la tarjeta de crédito",
+          "pestaña de incógnito",
+          "cambiar idioma esta página",
+          "google chrome actualización",
+        }
+      },
+      { "es-419",
+        {
+          "eliminar información",
+          "administrador contraseñas",
+          "administrar información de la tarjeta de crédito",
+          "pestaña de incógnito",
+          "cambiar idioma esta página",
+          "google chrome actualización",
+        }
+      },
+      { "et",
+        {
+          "tühjenda küpsisefailid",
+          "värskenda paroolid",
+          "värskenda krediitkaardi teave",
+          "inkognito vaheleht",
+          "muuda keelt see leht",
+          "google chrome uuenda versiooni",
+        }
+      },
+      { "fa",
+        {
+          "حذف کردن حافظه پنهان",
+          "به‌روزرسانی کردن اطلاعات کاربری",
+          "به‌روزرسانی کردن اطلاعات کارت اعتباری",
+          "پنجره ناشناس",
+          "تغییر زبان این صفحه",
+          "google chrome به‌روزرسانی کردن",
+        }
+      },
+      { "fi",
+        {
+          "tyhjennä historiatiedot",
+          "hallinnointi kirjautumistiedot",
+          "ylläpidä luottokorttien tiedot",
+          "yksityinen välilehti",
+          "vaihda kieltä tämä sivu",
+          "google chrome päivitys",
+        }
+      },
+      { "fil",
+        {
+          "i-delete impormasyon",
+          "pamahalaan mga kredensyal",
+          "pamahalaan impormasyon ng credit card",
+          "incognito window",
+          "baguhin ang wika i-translate ang page",
+          "google chrome i-upgrade",
+        }
+      },
+      { "fr",
+        {
+          "suppression informations",
+          "mettre à jour informations de connexion",
+          "mettre à jour informations cartes bancaires",
+          "sélectionner navigation confidentielle",
+          "sélectionner la langue cette page",
+          "google chrome mettre à niveau dernière version",
+        }
+      },
+      { "gu",
+        {
+          "ડિલીટ કરો ઇતિહાસ",
+          "ફેરફાર કરો લૉગ ઇન વિગત",
+          "ફેરફાર કરો ક્રેડિટ કાર્ડની માહિતી",
+          "ખાનગી વિંડો",
+          "અનુવાદ કરો આ પેજ",
+          "બ્રાઉઝર ઇન્સ્ટૉલ કરો",
+        }
+      },
+      { "he",
+        {
+          "איפוס נתונים",
+          "עריכה פרטי כניסה",
+          "ניהול",
+          "כרטיסיית מצב אנונימי",
+          "שינוי שפה דף זה",
+          "google chrome להתקין",
+        }
+      },
+      { "hi",
+        {
+          "वाइप करें कैश मेमोरी",
+          "मैनेजर/प्रबंधक क्रेडेंशियल",
+          "प्रबंधित करें क्रेडिट कार्ड की जानकारी",
+          "गुप्त विंडो",
+          "अनुवाद करें यह पेज",
+          "ब्राउज़र अपग्रेड करें",
+        }
+      },
+      { "hr",
+        {
+          "odstranjivanje privremena memorija",
+          "uređivanje vjerodajnice",
+          "upravljanje informacije o kreditnoj kartici",
+          "anonimna kartica",
+          "promjena jezika ova stranica",
+          "google chrome instaliranje",
+        }
+      },
+      { "hu",
+        {
+          "eltávolítása gyorsítótár",
+          "megjelenítése bejelentkezési adatok",
+          "szerkesztése hitelkártya adatok",
+          "inkognitó ablak",
+          "nyelvének módosítása ezt az oldalt",
+          "google chrome frissítése",
+        }
+      },
+      { "id",
+        {
+          "hapus informasi",
+          "pengelola kredensial",
+          "kelola informasi kartu kredit",
+          "jendela samaran",
+          "ubah bahasa halaman ini",
+          "google chrome upgrade",
+        }
+      },
+      { "it",
+        {
+          "cancellare informazioni",
+          "visualizzare credenziali",
+          "aggiornare informazioni di carte di credito",
+          "modalità di navigazione in incognito",
+          "cambiare la lingua questa pagina",
+          "google chrome eseguire l'upgrade",
+        }
+      },
+      { "ja",
+        {
+          "クリアキャッシュ",
+          "アップデートパスワード",
+          "アップデートクレジットカード情報",
+          "使い方プライベートブラウジング",
+          "トランスレーションこのページ",
+          "グーグルクロームアップグレード",
+        }
+      },
+      { "kn",
+        {
+          "ತೆರವುಗೊಳಿಸಿ ಕುಕೀಗಳು",
+          "ಅಪ್‌ಡೇಟ್ ಮಾಡಿ ಪಾಸ್‌ವರ್ಡ್‌ಗಳು",
+          "ಅಪ್‌ಡೇಟ್ ಮಾಡಿ ಕ್ರೆಡಿಟ್ ಕಾರ್ಡ್ ಮಾಹಿತಿ",
+          "ಅಜ್ಞಾತ ಟ್ಯಾಬ್",
+          "ಭಾಷೆಯನ್ನು ಬದಲಾಯಿಸಿ ಈ ಪುಟ",
+          "ಬ್ರೌಸರ್ ಅಪ್‌ಗ್ರೇಡ್ ಮಾಡಿ",
+        }
+      },
+      { "ko",
+        {
+          "완전 삭제 데이터",
+          "업데이트 사용자 인증 정보",
+          "업데이트 신용카드 정보",
+          "시크릿 모드",
+          "언어 변경 이 페이지",
+          "브라우저 업그레이드",
+        }
+      },
+      { "lt",
+        {
+          "pašalinti informacija",
+          "peržiūrėti prisijungimo duomenys",
+          "atnaujinti kredito kortelės informacija",
+          "inkognito skirtukas",
+          "pakeisti kalbą šis puslapis",
+          "google chrome naujovinti",
+        }
+      },
+      { "lv",
+        {
+          "notīrīt informācija",
+          "pārvaldnieks akreditācijas dati",
+          "pārvaldīt informācija par kredītkarti",
+          "inkognito režīma logs",
+          "mainīt valodu šī lapa",
+          "google chrome atjaunināt",
+        }
+      },
+      { "ml",
+        {
+          "നീക്കം ചെയ്യുക വിവരങ്ങൾ",
+          "അപ്ഡേറ്റ് ചെയ്യുക ക്രെഡന്‍ഷ്യലുകൾ",
+          "അപ്ഡേറ്റ് ചെയ്യുക ക്രെഡിറ്റ് കാർഡ് വിവരങ്ങൾ",
+          "സ്വകാര്യ വിൻഡോ",
+          "വിവർത്തനം ചെയ്യുക ഈ പേജ്",
+          "ബ്രൗസർ അപ്‌ഗ്രേഡ് ചെയ്യുക",
+        }
+      },
+      { "mr",
+        {
+          "काढून टाका इतिहास",
+          "व्यवस्थापित करा क्रेडेंशियल",
+          "व्यवस्थापित करा क्रेडिट कार्डाची माहिती",
+          "गुप्त विंडो",
+          "भाषांतर करा हे पेज",
+          "ब्राउझर अपग्रेड करा",
+        }
+      },
+      { "ms",
+        {
+          "alih keluar maklumat",
+          "kemas kini bukti kelayakan",
+          "kemas kini maklumat kad kredit",
+          "tetingkap inkognito",
+          "tukar bahasa halaman ini",
+          "penyemak imbas tingkatkan",
+        }
+      },
+      { "nl",
+        {
+          "google chrome verwijderen cachegeheugen",
+          "veranderen inloggegevens",
+          "bijwerken creditcardinformatie",
+          "incognitovenster",
+          "taal wijzigen deze pagina",
+          "google chrome installeren",
+        }
+      },
+      { "pl",
+        {
+          "wyczyść pamięć podręczną przeglądarki",
+          "zaktualizuj dane do zalogowania",
+          "zaktualizuj informacje o karcie płatniczej",
+          "tryb niewidzialny",
+          "zmień język tę stronę",
+          "google chrome zaktualizuj",
+        }
+      },
+      { "pt-BR",
+        {
+          "excluir permanentemente informações",
+          "gerenciar credenciais",
+          "gerenciar informações do cartão de crédito",
+          "modo de navegação anônima",
+          "mudar idioma esta página",
+          "google chrome fazer upgrade",
+        }
+      },
+      { "pt-PT",
+        {
+          "eliminar informações",
+          "atualizar palavras-passe",
+          "atualizar informações do cartão de crédito",
+          "separador de navegação anónima",
+          "alterar idioma esta página",
+          "google chrome atualizar",
+        }
+      },
+      { "ro",
+        {
+          "elimină informații",
+          "actualizează date de conectare",
+          "actualizează informațiile cardului de credit",
+          "fereastră incognito",
+          "schimbă limba această pagină",
+          "google chrome actualizează",
+        }
+      },
+      { "ru",
+        {
+          "почистить информацию",
+          "управление учетные данные",
+          "управление данные кредитной карты",
+          "анонимный просмотр",
+          "изменить язык эту страницу",
+          "браузер установить обновление",
+        }
+      },
+      { "sk",
+        {
+          "odstrániť vyrovnávacia pamäť",
+          "aktualizovať prihlasovacie údaje",
+          "aktualizovať informácie o kreditnej karte",
+          "súkromný režim",
+          "zmeniť jazyk túto stránku",
+          "google chrome aktualizovať",
+        }
+      },
+      { "sl",
+        {
+          "izbriši predpomnilnik",
+          "upravitelj poverilnice",
+          "upravljaj podatke o kreditni kartici",
+          "zavihek brez beleženja dejavnosti",
+          "spremeni jezik to stran",
+          "google chrome nadgradi",
+        }
+      },
+      { "sr",
+        {
+          "избрисати информације",
+          "прегледати креденцијали",
+          "управљати информацијама о кредитним картицама",
+          "картица за приватно прегледање",
+          "променити језик ове странице",
+          "прегледача надограђивање",
+        }
+      },
+      { "sv",
+        {
+          "ta bort information",
+          "uppdatera användaruppgifter",
+          "uppdatera kreditkortsinformation",
+          "inkognitofönster",
+          "ändra språk denna sida",
+          "google chrome uppgradera",
+        }
+      },
+      { "sw",
+        {
+          "ondoa kumbukumbu",
+          "badilisha kitambulisho",
+          "badilisha maelezo ya kadi ya mikopo",
+          "dirisha la faragha",
+          "badilisha lugha ukurasa huu",
+          "google chrome uppgradera",
+        }
+      },
+      { "ta",
+        {
+          "வைப் செய் தற்காலிகச் சேமிப்பு",
+          "திருத்து அனுமதிச் சான்றுகள்",
+          "திருத்து கிரெடிட் கார்டு தகவல்",
+          "மறைநிலைப் பயன்முறை",
+          "மொழியை மாற்று மொழிபெயர்க்கவும்",
+          "உலாவி மேம்படுத்து",
+        }
+      },
+      { "te",
+        {
+          "క్లియర్ చేయడం కుక్కీలు",
+          "అప్‌డేట్ చేయడం పాస్‌వర్డ్‌లు",
+          "అప్‌డేట్ చేయడం క్రెడిట్ కార్డ్ సమాచారం",
+          "ప్రైవేట్ ట్యాబ్",
+          "భాష మార్చడం ఈ పేజీ",
+          "బ్రౌజర్ అప్‌గ్రేడ్ చేయడం",
+        }
+      },
+      { "th",
+        {
+          "ล้างข้อมูล ประวัติ",
+          "เปลี่ยนแปลง ข้อมูลเข้าสู่ระบบ",
+          "เปลี่ยนแปลง ข้อมูลบัตรเครดิต",
+          "หน้าต่างไม่ระบุตัวตน",
+          "เปลี่ยนภาษา หน้านี้",
+          "เบราว์เซอร์ อัปเกรด",
+        }
+      },
+      { "tr",
+        {
+          "kaldır önbelleği",
+          "görüntüle kimlik bilgilerini",
+          "değiştir credit card information",
+          "gizli pencereye",
+          "dili değiştir bu sayfayı",
+          "google chrome yeni sürüme geçir",
+        }
+      },
+      { "uk",
+        {
+          "видалити інформація",
+          "переглянути облікові дані",
+          "редагувати дані кредитної картки",
+          "вікно в режимі анонімного перегляду",
+          "змінити мову цієї сторінки",
+          "веб-переглядач установити",
+        }
+      },
+      { "vi",
+        {
+          "dọn sạch bộ nhớ đệm",
+          "trình quản lý thông tin đăng nhập",
+          "chỉnh sửa thông tin thẻ tín dụng",
+          "chế độ riêng tư",
+          "thay đổi ngôn ngữ trang này",
+          "trình duyệt cập nhật",
+        }
+      },
+      { "zh-CN",
+        {
+          "清除浏览数据",
+          "管理密码",
+          "更新信用卡信息",
+          "启动无痕模式",
+          "切换语言",
+          "google chrome更新",
+        }
+      },
+      { "zh-TW",
+        {
+          "刪除 歷史記錄",
+          "管理員 密碼",
+          "管理 信用卡資訊",
+          "私密瀏覽視窗",
+          "變更語言 這個頁面",
+          "google chrome 升級",
+        }
+      },
       // clang-format on
   };
   for (const TestCase& test_case : test_cases) {
@@ -95,15 +563,18 @@
     //  a soft failure so as to not block all other platforms. To ensure this
     //  is not going to cause failure in production, still test that English
     //  triggering functions. Data is there; it works; but warn about locale.
-    if (!provider.FindPedalMatch(base::UTF8ToUTF16(test_case.trigger))) {
+    if (!provider.FindPedalMatch(base::UTF8ToUTF16(test_case.triggers[0]))) {
       EXPECT_NE(provider.FindPedalMatch(base::UTF8ToUTF16("clear history")),
                 nullptr);
       LOG(WARNING) << "ChromeOS using English for locale " << test_case.locale;
       continue;
     }
 #endif
-    EXPECT_NE(provider.FindPedalMatch(base::UTF8ToUTF16(test_case.trigger)),
-              nullptr)
-        << "locale: " << test_case.locale;
+
+    for (const std::string& trigger : test_case.triggers) {
+      EXPECT_NE(provider.FindPedalMatch(base::UTF8ToUTF16(trigger)), nullptr)
+          << "locale: " << test_case.locale << std::endl
+          << "trigger: " << trigger;
+    }
   }
 }
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_bar_view_test.cc b/chrome/browser/ui/views/bookmarks/bookmark_bar_view_test.cc
index c2f2515d..f129911 100644
--- a/chrome/browser/ui/views/bookmarks/bookmark_bar_view_test.cc
+++ b/chrome/browser/ui/views/bookmarks/bookmark_bar_view_test.cc
@@ -1156,7 +1156,13 @@
   }
 };
 
-VIEW_TEST(BookmarkBarViewTest10, KeyEvents)
+#if defined(OS_WIN)  // Fails on latest versions of Windows.
+                     // https://crbug.com/1108551.
+#define MAYBE_KeyEvents DISABLED_KeyEvents
+#else
+#define MAYBE_KeyEvents KeyEvents
+#endif
+VIEW_TEST(BookmarkBarViewTest10, MAYBE_KeyEvents)
 
 // Make sure the menu closes with the following sequence: show menu, show
 // context menu, close context menu (via escape), then click else where. This
@@ -2054,7 +2060,13 @@
   BookmarkContextMenuNotificationObserver observer_;
 };
 
-VIEW_TEST(BookmarkBarViewTest23, ContextMenusKeyboard)
+#if defined(OS_WIN)  // Fails on latest versions of Windows.
+                     // https://crbug.com/1108551.
+#define MAYBE_ContextMenusKeyboard DISABLED_ContextMenusKeyboard
+#else
+#define MAYBE_ContextMenusKeyboard ContextMenusKeyboard
+#endif
+VIEW_TEST(BookmarkBarViewTest23, MAYBE_ContextMenusKeyboard)
 
 // Test that pressing escape on a menu opened via the keyboard dismisses the
 // context menu but not the parent menu.
@@ -2130,7 +2142,13 @@
   BookmarkContextMenuNotificationObserver observer_;
 };
 
-VIEW_TEST(BookmarkBarViewTest24, ContextMenusKeyboardEscape)
+#if defined(OS_WIN)  // Fails on latest versions of Windows.
+                     // https://crbug.com/1108551.
+#define MAYBE_ContextMenusKeyboardEscape DISABLED_ContextMenusKeyboardEscape
+#else
+#define MAYBE_ContextMenusKeyboardEscape ContextMenusKeyboardEscape
+#endif
+VIEW_TEST(BookmarkBarViewTest24, MAYBE_ContextMenusKeyboardEscape)
 
 #if defined(OS_WIN)
 // Tests that pressing the key KEYCODE closes the menu.
diff --git a/chrome/browser/ui/views/frame/browser_frame_header_ash.cc b/chrome/browser/ui/views/frame/browser_frame_header_ash.cc
index 07064be..b437f7b 100644
--- a/chrome/browser/ui/views/frame/browser_frame_header_ash.cc
+++ b/chrome/browser/ui/views/frame/browser_frame_header_ash.cc
@@ -34,31 +34,39 @@
                       SkColor background_color,
                       const gfx::Rect& bounds,
                       int image_inset_x,
-                      int image_inset_y) {
+                      int image_inset_y,
+                      int alpha) {
   SkColor opaque_background_color =
       SkColorSetA(background_color, SK_AlphaOPAQUE);
 
-  // When no images are used, just draw a color.
+  // When no images are used, just draw a color, with the animation |alpha|
+  // applied.
   if (frame_image.isNull() && frame_overlay_image.isNull()) {
-    canvas->DrawColor(opaque_background_color);
+    // We use kPlus blending mode so that between the active and inactive
+    // background colors, the result is 255 alpha (i.e. opaque).
+    canvas->DrawColor(SkColorSetA(opaque_background_color, alpha),
+                      SkBlendMode::kPlus);
     return;
   }
 
   // This handles the case where blending is required between one or more images
   // and the background color. In this case we use a SaveLayerWithFlags() call
-  // to draw all 2-3 components into a single layer.
+  // to draw all 2-3 components into a single layer then apply the alpha to them
+  // together.
   const bool blending_required =
-      !frame_image.isNull() && !frame_overlay_image.isNull();
+      alpha < 0xFF || (!frame_image.isNull() && !frame_overlay_image.isNull());
   if (blending_required) {
     cc::PaintFlags flags;
     // We use kPlus blending mode so that between the active and inactive
     // background colors, the result is 255 alpha (i.e. opaque).
     flags.setBlendMode(SkBlendMode::kPlus);
+    flags.setAlpha(alpha);
     canvas->SaveLayerWithFlags(flags);
   }
 
   // Images can be transparent and we expect the background color to be present
-  // behind them.
+  // behind them. Here the |alpha| will be applied to the background color by
+  // the SaveLayer call, so use |opaque_background_color|.
   canvas->DrawColor(opaque_background_color);
   if (!frame_image.isNull()) {
     canvas->TileImageInt(frame_image, image_inset_x, image_inset_y, 0, 0,
@@ -81,6 +89,7 @@
                                  const gfx::Rect& bounds,
                                  int image_inset_x,
                                  int image_inset_y,
+                                 int alpha,
                                  int corner_radius) {
   const SkScalar sk_corner_radius = SkIntToScalar(corner_radius);
   const SkScalar radii[8] = {sk_corner_radius,
@@ -100,7 +109,7 @@
   canvas->ClipPath(frame_path, antialias);
 
   PaintThemedFrame(canvas, frame_image, frame_overlay_image, background_color,
-                   bounds, image_inset_x, image_inset_y);
+                   bounds, image_inset_x, image_inset_y, alpha);
 }
 
 }  // namespace
@@ -138,7 +147,8 @@
 // BrowserFrameHeaderAsh, protected:
 
 void BrowserFrameHeaderAsh::DoPaintHeader(gfx::Canvas* canvas) {
-  PaintFrameImages(canvas);
+  PaintFrameImages(canvas, false /* active */);
+  PaintFrameImages(canvas, true /* active */);
   PaintTitleBar(canvas);
 }
 
@@ -168,8 +178,13 @@
 ///////////////////////////////////////////////////////////////////////////////
 // BrowserFrameHeaderAsh, private:
 
-void BrowserFrameHeaderAsh::PaintFrameImages(gfx::Canvas* canvas) {
-  const bool active = mode() == Mode::MODE_ACTIVE;
+void BrowserFrameHeaderAsh::PaintFrameImages(gfx::Canvas* canvas, bool active) {
+  int alpha = activation_animation().CurrentValueBetween(0, 0xFF);
+  if (!active)
+    alpha = 0xFF - alpha;
+
+  if (alpha == 0)
+    return;
 
   gfx::ImageSkia frame_image =
       appearance_provider_->GetFrameHeaderImage(active);
@@ -186,5 +201,5 @@
                               appearance_provider_->GetFrameHeaderColor(active),
                               GetPaintedBounds(), GetThemeBackgroundXInset(),
                               appearance_provider_->GetFrameHeaderImageYInset(),
-                              corner_radius);
+                              alpha, corner_radius);
 }
diff --git a/chrome/browser/ui/views/frame/browser_frame_header_ash.h b/chrome/browser/ui/views/frame/browser_frame_header_ash.h
index 842c76d..c1ea091c 100644
--- a/chrome/browser/ui/views/frame/browser_frame_header_ash.h
+++ b/chrome/browser/ui/views/frame/browser_frame_header_ash.h
@@ -49,8 +49,9 @@
   SkColor GetCurrentFrameColor() const override;
 
  private:
-  // Paints the frame image.
-  void PaintFrameImages(gfx::Canvas* canvas);
+  // Paints the frame image for the |active| state based on the current value of
+  // the activation animation.
+  void PaintFrameImages(gfx::Canvas* canvas, bool active);
 
   AppearanceProvider* appearance_provider_ = nullptr;
 
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc
index 8bb685c8..8bd3e26 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc
@@ -326,8 +326,11 @@
   if (!ShouldPaint())
     return;
 
+  const ash::FrameHeader::Mode header_mode =
+      ShouldPaintAsActive() ? ash::FrameHeader::MODE_ACTIVE
+                            : ash::FrameHeader::MODE_INACTIVE;
   if (frame_header_)
-    frame_header_->PaintHeader(canvas);
+    frame_header_->PaintHeader(canvas, header_mode);
 }
 
 void BrowserNonClientFrameViewAsh::Layout() {
diff --git a/chrome/browser/ui/views/touch_events_interactive_uitest_win.cc b/chrome/browser/ui/views/touch_events_interactive_uitest_win.cc
index 84e294a..20716413 100644
--- a/chrome/browser/ui/views/touch_events_interactive_uitest_win.cc
+++ b/chrome/browser/ui/views/touch_events_interactive_uitest_win.cc
@@ -4,6 +4,7 @@
 
 #include "base/run_loop.h"
 #include "base/win/windows_version.h"
+#include "build/build_config.h"
 #include "chrome/browser/ui/views/test/view_event_test_base.h"
 #include "chrome/test/base/testing_profile.h"
 #include "ui/aura/env.h"
@@ -192,7 +193,15 @@
   DISALLOW_COPY_AND_ASSIGN(TouchEventsViewTest);
 };
 
-VIEW_TEST(TouchEventsViewTest, CheckWindowsNativeMessageForTouchEvents)
+#if defined(OS_WIN)  // Fails on latest versions of Windows.
+                     // https://crbug.com/1108551.
+#define MAYBE_CheckWindowsNativeMessageForTouchEvents \
+  DISABLED_CheckWindowsNativeMessageForTouchEvents
+#else
+#define MAYBE_CheckWindowsNativeMessageForTouchEvents \
+  CheckWindowsNativeMessageForTouchEvents
+#endif
+VIEW_TEST(TouchEventsViewTest, MAYBE_CheckWindowsNativeMessageForTouchEvents)
 
 class TouchEventsRecursiveViewTest : public TouchEventsViewTest {
  public:
@@ -231,4 +240,10 @@
   DISALLOW_COPY_AND_ASSIGN(TouchEventsRecursiveViewTest);
 };
 
-VIEW_TEST(TouchEventsRecursiveViewTest, CheckWindowsRecursiveHandler)
+#if defined(OS_WIN)  // Fails on latest versions of Windows.
+                     // https://crbug.com/1108551.
+#define MAYBE_CheckWindowsRecursiveHandler DISABLED_CheckWindowsRecursiveHandler
+#else
+#define MAYBE_CheckWindowsRecursiveHandler CheckWindowsRecursiveHandler
+#endif
+VIEW_TEST(TouchEventsRecursiveViewTest, MAYBE_CheckWindowsRecursiveHandler)
diff --git a/chrome/browser/ui/web_applications/system_web_app_ui_utils.cc b/chrome/browser/ui/web_applications/system_web_app_ui_utils.cc
index 157ea74..cb99d98 100644
--- a/chrome/browser/ui/web_applications/system_web_app_ui_utils.cc
+++ b/chrome/browser/ui/web_applications/system_web_app_ui_utils.cc
@@ -44,10 +44,10 @@
 
 void LogPrintManagementEntryPoints(apps::mojom::AppLaunchSource source) {
   if (source == apps::mojom::AppLaunchSource::kSourceAppLauncher) {
-    base::UmaHistogramEnumeration("Printing.Cups.PrintManagementAppEntryPoint",
+    base::UmaHistogramEnumeration("Printing.CUPS.PrintManagementAppEntryPoint",
                                   PrintManagementAppEntryPoint::kLauncher);
   } else if (source == apps::mojom::AppLaunchSource::kSourceIntentUrl) {
-    base::UmaHistogramEnumeration("Printing.Cups.PrintManagementAppEntryPoint",
+    base::UmaHistogramEnumeration("Printing.CUPS.PrintManagementAppEntryPoint",
                                   PrintManagementAppEntryPoint::kBrowser);
   }
 }
diff --git a/chrome/browser/ui/webui/settings/chromeos/cups_printers_handler_unittest.cc b/chrome/browser/ui/webui/settings/chromeos/cups_printers_handler_unittest.cc
index 00cd8e56..412742de4 100644
--- a/chrome/browser/ui/webui/settings/chromeos/cups_printers_handler_unittest.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/cups_printers_handler_unittest.cc
@@ -267,16 +267,16 @@
   web_ui_.HandleReceivedMessage("openPrintManagementApp",
                                 &base::Value::AsListValue(args));
   histogram_tester_.ExpectBucketCount(
-      "Printing.Cups.PrintManagementAppEntryPoint",
+      "Printing.CUPS.PrintManagementAppEntryPoint",
       PrintManagementAppEntryPoint::kSettings, 1);
   histogram_tester_.ExpectBucketCount(
-      "Printing.Cups.PrintManagementAppEntryPoint",
+      "Printing.CUPS.PrintManagementAppEntryPoint",
       PrintManagementAppEntryPoint::kNotification, 0);
   histogram_tester_.ExpectBucketCount(
-      "Printing.Cups.PrintManagementAppEntryPoint",
+      "Printing.CUPS.PrintManagementAppEntryPoint",
       PrintManagementAppEntryPoint::kLauncher, 0);
   histogram_tester_.ExpectBucketCount(
-      "Printing.Cups.PrintManagementAppEntryPoint",
+      "Printing.CUPS.PrintManagementAppEntryPoint",
       PrintManagementAppEntryPoint::kBrowser, 0);
 }
 
diff --git a/chrome/browser/ui/webui/signin/profile_creation_customize_themes_handler.cc b/chrome/browser/ui/webui/signin/profile_creation_customize_themes_handler.cc
new file mode 100644
index 0000000..abfae488
--- /dev/null
+++ b/chrome/browser/ui/webui/signin/profile_creation_customize_themes_handler.cc
@@ -0,0 +1,70 @@
+// 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/webui/signin/profile_creation_customize_themes_handler.h"
+
+#include "chrome/common/search/generated_colors_info.h"
+#include "chrome/common/themes/autogenerated_theme_util.h"
+#include "ui/base/l10n/l10n_util.h"
+
+ProfileCreationCustomizeThemesHandler::ProfileCreationCustomizeThemesHandler(
+    mojo::PendingRemote<customize_themes::mojom::CustomizeThemesClient>
+        pending_client,
+    mojo::PendingReceiver<customize_themes::mojom::CustomizeThemesHandler>
+        pending_handler)
+    : remote_client_(std::move(pending_client)),
+      receiver_(this, std::move(pending_handler)) {}
+
+ProfileCreationCustomizeThemesHandler::
+    ~ProfileCreationCustomizeThemesHandler() = default;
+
+void ProfileCreationCustomizeThemesHandler::ApplyAutogeneratedTheme(
+    const SkColor& frame_color) {
+  auto theme = customize_themes::mojom::Theme::New();
+  theme->type = customize_themes::mojom::ThemeType::kAutogenerated;
+  auto theme_colors = customize_themes::mojom::ThemeColors::New();
+  auto colors = GetAutogeneratedThemeColors(frame_color);
+  theme_colors->frame = colors.frame_color;
+  theme_colors->active_tab = colors.active_tab_color;
+  theme_colors->active_tab_text = colors.active_tab_text_color;
+  theme->info = customize_themes::mojom::ThemeInfo::NewAutogeneratedThemeColors(
+      std::move(theme_colors));
+  remote_client_->SetTheme(std::move(theme));
+}
+
+void ProfileCreationCustomizeThemesHandler::ApplyDefaultTheme() {
+  auto theme = customize_themes::mojom::Theme::New();
+  theme->type = customize_themes::mojom::ThemeType::kDefault;
+  theme->info = customize_themes::mojom::ThemeInfo::NewChromeThemeId(-1);
+  remote_client_->SetTheme(std::move(theme));
+}
+
+void ProfileCreationCustomizeThemesHandler::ApplyChromeTheme(int32_t id) {
+  auto theme = customize_themes::mojom::Theme::New();
+  theme->type = customize_themes::mojom::ThemeType::kChrome;
+  theme->info = customize_themes::mojom::ThemeInfo::NewChromeThemeId(id);
+  remote_client_->SetTheme(std::move(theme));
+}
+
+void ProfileCreationCustomizeThemesHandler::GetChromeThemes(
+    GetChromeThemesCallback callback) {
+  std::vector<customize_themes::mojom::ChromeThemePtr> themes;
+  for (const auto& color_info : chrome_colors::kGeneratedColorsInfo) {
+    auto theme_colors = GetAutogeneratedThemeColors(color_info.color);
+    auto theme = customize_themes::mojom::ChromeTheme::New();
+    theme->id = color_info.id;
+    theme->label = l10n_util::GetStringUTF8(color_info.label_id);
+    auto colors = customize_themes::mojom::ThemeColors::New();
+    colors->frame = theme_colors.frame_color;
+    colors->active_tab = theme_colors.active_tab_color;
+    colors->active_tab_text = theme_colors.active_tab_text_color;
+    theme->colors = std::move(colors);
+    themes.push_back(std::move(theme));
+  }
+  std::move(callback).Run(std::move(themes));
+}
+
+void ProfileCreationCustomizeThemesHandler::ConfirmThemeChanges() {}
+
+void ProfileCreationCustomizeThemesHandler::RevertThemeChanges() {}
diff --git a/chrome/browser/ui/webui/signin/profile_creation_customize_themes_handler.h b/chrome/browser/ui/webui/signin/profile_creation_customize_themes_handler.h
new file mode 100644
index 0000000..d84830c
--- /dev/null
+++ b/chrome/browser/ui/webui/signin/profile_creation_customize_themes_handler.h
@@ -0,0 +1,39 @@
+// 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_WEBUI_SIGNIN_PROFILE_CREATION_CUSTOMIZE_THEMES_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_SIGNIN_PROFILE_CREATION_CUSTOMIZE_THEMES_HANDLER_H_
+
+#include "base/optional.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/receiver.h"
+#include "mojo/public/cpp/bindings/remote.h"
+#include "third_party/skia/include/core/SkColor.h"
+#include "ui/webui/resources/cr_components/customize_themes/customize_themes.mojom.h"
+
+class ProfileCreationCustomizeThemesHandler
+    : public customize_themes::mojom::CustomizeThemesHandler {
+ public:
+  explicit ProfileCreationCustomizeThemesHandler(
+      mojo::PendingRemote<customize_themes::mojom::CustomizeThemesClient>
+          pending_client,
+      mojo::PendingReceiver<customize_themes::mojom::CustomizeThemesHandler>
+          pending_handler);
+  ~ProfileCreationCustomizeThemesHandler() override;
+
+  // customize_themes::mojom::CustomizeThemesHandler:
+  void ApplyAutogeneratedTheme(const SkColor& frame_color) override;
+  void ApplyDefaultTheme() override;
+  void ApplyChromeTheme(int32_t id) override;
+  void GetChromeThemes(GetChromeThemesCallback callback) override;
+  void ConfirmThemeChanges() override;
+  void RevertThemeChanges() override;
+
+ private:
+  mojo::Remote<customize_themes::mojom::CustomizeThemesClient> remote_client_;
+  mojo::Receiver<customize_themes::mojom::CustomizeThemesHandler> receiver_;
+};
+
+#endif  // CHROME_BROWSER_UI_WEBUI_SIGNIN_PROFILE_CREATION_CUSTOMIZE_THEMES_HANDLER_H_
diff --git a/chrome/browser/ui/webui/signin/profile_picker_handler.cc b/chrome/browser/ui/webui/signin/profile_picker_handler.cc
index 395984d..2b9b64fe 100644
--- a/chrome/browser/ui/webui/signin/profile_picker_handler.cc
+++ b/chrome/browser/ui/webui/signin/profile_picker_handler.cc
@@ -6,6 +6,8 @@
 
 #include "base/bind.h"
 #include "base/bind_helpers.h"
+#include "base/optional.h"
+#include "base/strings/utf_string_conversions.h"
 #include "base/util/values/values_util.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/profiles/profile_attributes_entry.h"
@@ -17,6 +19,7 @@
 #include "chrome/browser/profiles/profile_window.h"
 #include "chrome/browser/profiles/profiles_state.h"
 #include "chrome/browser/signin/signin_util.h"
+#include "chrome/browser/themes/theme_properties.h"
 #include "chrome/browser/themes/theme_service.h"
 #include "chrome/browser/themes/theme_service_factory.h"
 #include "chrome/browser/ui/browser_finder.h"
@@ -28,6 +31,7 @@
 #include "chrome/common/themes/autogenerated_theme_util.h"
 #include "chrome/common/webui_url_constants.h"
 #include "components/signin/public/identity_manager/account_info.h"
+#include "third_party/skia/include/core/SkColor.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/webui/web_ui_util.h"
 #include "ui/gfx/color_utils.h"
@@ -37,11 +41,73 @@
 const size_t kAvatarIconSize = 74;
 const size_t kProfileCreationAvatarSize = 100;
 
+constexpr int kDefaultThemeColorId = -1;
+constexpr int kManuallyPickedColorId = 0;
+
 bool IsManaged(const std::string& hosted_domain) {
   return !hosted_domain.empty() && hosted_domain != kNoHostedDomainFound;
 }
+
+base::Optional<SkColor> GetChromeColorColorById(int color_id) {
+  for (chrome_colors::ColorInfo color_info :
+       chrome_colors::kGeneratedColorsInfo) {
+    if (color_id == color_info.id)
+      return color_info.color;
+  }
+
+  return base::nullopt;
 }
 
+base::Value GetAutogeneratedProfileThemeInfoValue(int color_id,
+                                                  base::Optional<SkColor> color,
+                                                  SkColor frame_color,
+                                                  SkColor active_tab_color,
+                                                  SkColor frame_text_color) {
+  base::Value dict(base::Value::Type::DICTIONARY);
+  dict.SetIntKey("colorId", color_id);
+  if (color.has_value())
+    dict.SetIntKey("color", *color);
+  dict.SetStringKey("themeFrameColor",
+                    color_utils::SkColorToRgbaString(frame_color));
+  dict.SetStringKey("themeShapeColor",
+                    color_utils::SkColorToRgbaString(active_tab_color));
+  dict.SetStringKey("themeFrameTextColor",
+                    color_utils::SkColorToRgbaString(frame_text_color));
+  gfx::Image icon = profiles::GetPlaceholderAvatarIconWithColors(
+      /*fill_color=*/frame_color,
+      /*stroke_color=*/GetAvatarStrokeColor(frame_color),
+      kProfileCreationAvatarSize);
+  dict.SetStringKey("themeGenericAvatar",
+                    webui::GetBitmapDataUrl(icon.AsBitmap()));
+  return dict;
+}
+
+base::Value CreateDefaultProfileThemeInfo() {
+  bool dark_mode =
+      ui::NativeTheme::GetInstanceForNativeUi()->ShouldUseDarkColors();
+  SkColor frame_color = ThemeProperties::GetDefaultColor(
+      ThemeProperties::COLOR_FRAME_ACTIVE, /*incognito=*/false, dark_mode);
+  SkColor active_tab_color = ThemeProperties::GetDefaultColor(
+      ThemeProperties::COLOR_TOOLBAR, /*incognito=*/false, dark_mode);
+  SkColor frame_text_color = ThemeProperties::GetDefaultColor(
+      ThemeProperties::COLOR_TAB_FOREGROUND_INACTIVE_FRAME_ACTIVE,
+      /*incognito=*/false, dark_mode);
+  return GetAutogeneratedProfileThemeInfoValue(
+      kDefaultThemeColorId, base::nullopt, frame_color, active_tab_color,
+      frame_text_color);
+}
+
+base::Value CreateAutogeneratedProfileThemeInfo(int color_id, SkColor color) {
+  auto theme_colors = GetAutogeneratedThemeColors(color);
+  SkColor frame_color = theme_colors.frame_color;
+  SkColor active_tab_color = theme_colors.active_tab_color;
+  SkColor frame_text_color = theme_colors.frame_text_color;
+  return GetAutogeneratedProfileThemeInfoValue(
+      color_id, color, frame_color, active_tab_color, frame_text_color);
+}
+
+}  // namespace
+
 ProfilePickerHandler::ProfilePickerHandler() = default;
 
 ProfilePickerHandler::~ProfilePickerHandler() {
@@ -75,6 +141,10 @@
           &ProfilePickerHandler::HandleGetNewProfileSuggestedThemeInfo,
           base::Unretained(this)));
   web_ui()->RegisterMessageCallback(
+      "getProfileThemeInfo",
+      base::BindRepeating(&ProfilePickerHandler::HandleGetProfileThemeInfo,
+                          base::Unretained(this)));
+  web_ui()->RegisterMessageCallback(
       "removeProfile",
       base::BindRepeating(&ProfilePickerHandler::HandleRemoveProfile,
                           base::Unretained(this)));
@@ -168,39 +238,47 @@
   CHECK_EQ(1U, args->GetSize());
   const base::Value& callback_id = args->GetList()[0];
   chrome_colors::ColorInfo color_info = GenerateNewProfileColor();
-  auto theme_colors = GetAutogeneratedThemeColors(color_info.color);
-  base::Value dict(base::Value::Type::DICTIONARY);
-  dict.SetIntKey("colorId", color_info.id);
-  dict.SetIntKey("color", color_info.color);
-  dict.SetStringKey("themeFrameColor",
-                    color_utils::SkColorToRgbaString(theme_colors.frame_color));
-  dict.SetStringKey("themeShapeColor", color_utils::SkColorToRgbaString(
-                                           theme_colors.active_tab_color));
-  dict.SetStringKey("themeFrameTextColor", color_utils::SkColorToRgbaString(
-                                               theme_colors.frame_text_color));
-  gfx::Image icon = profiles::GetPlaceholderAvatarIconWithColors(
-      /*fill_color=*/theme_colors.frame_color,
-      /*stroke_color=*/GetAvatarStrokeColor(theme_colors.frame_color),
-      kProfileCreationAvatarSize);
-  dict.SetStringKey("themeGenericAvatar",
-                    webui::GetBitmapDataUrl(icon.AsBitmap()));
+  base::Value dict =
+      CreateAutogeneratedProfileThemeInfo(color_info.id, color_info.color);
+  ResolveJavascriptCallback(callback_id, std::move(dict));
+}
 
+void ProfilePickerHandler::HandleGetProfileThemeInfo(
+    const base::ListValue* args) {
+  AllowJavascript();
+  CHECK_EQ(2U, args->GetList().size());
+  const base::Value& callback_id = args->GetList()[0];
+  const base::Value& user_theme_choice = args->GetList()[1];
+  int color_id = user_theme_choice.FindIntKey("colorId").value();
+  base::Optional<SkColor> color = user_theme_choice.FindDoubleKey("color");
+  base::Value dict;
+  switch (color_id) {
+    case kDefaultThemeColorId:
+      dict = CreateDefaultProfileThemeInfo();
+      break;
+    case kManuallyPickedColorId:
+      dict = CreateAutogeneratedProfileThemeInfo(color_id, *color);
+      break;
+    default:
+      dict = CreateAutogeneratedProfileThemeInfo(
+          color_id, *GetChromeColorColorById(color_id));
+      break;
+  }
   ResolveJavascriptCallback(callback_id, std::move(dict));
 }
 
 void ProfilePickerHandler::HandleCreateProfile(const base::ListValue* args) {
   // profileName, profileColor, avatarUrl, isGeneric, createShortcut
-  base::string16 profile_name;
-  int profile_color;
-  std::string avatar_url;
-  bool is_generic;
-  bool create_shortcut;
-  CHECK_EQ(5U, args->GetSize());
-  args->GetString(0, &profile_name);
-  args->GetInteger(1, &profile_color);
-  args->GetString(2, &avatar_url);
-  args->GetBoolean(3, &is_generic);
-  args->GetBoolean(4, &create_shortcut);
+  CHECK_EQ(5U, args->GetList().size());
+  base::string16 profile_name =
+      base::UTF8ToUTF16(args->GetList()[0].GetString());
+  // profileColor is undefined for the default theme.
+  base::Optional<SkColor> profile_color;
+  if (args->GetList()[1].is_int())
+    profile_color = args->GetList()[1].GetInt();
+  std::string avatar_url = args->GetList()[2].GetString();
+  bool is_generic = args->GetList()[3].GetBool();
+  bool create_shortcut = args->GetList()[4].GetBool();
   DCHECK(base::IsStringASCII(avatar_url));
   base::TrimWhitespace(profile_name, base::TRIM_ALL, &profile_name);
   CHECK(!profile_name.empty());
@@ -218,14 +296,15 @@
   ProfileManager::CreateMultiProfileAsync(
       profile_name, avatar_url,
       base::BindRepeating(&ProfilePickerHandler::OnProfileCreated,
-                          weak_factory_.GetWeakPtr(), SkColor(profile_color),
+                          weak_factory_.GetWeakPtr(), profile_color,
                           create_shortcut));
 }
 
-void ProfilePickerHandler::OnProfileCreated(SkColor profile_color,
-                                            bool create_shortcut,
-                                            Profile* profile,
-                                            Profile::CreateStatus status) {
+void ProfilePickerHandler::OnProfileCreated(
+    base::Optional<SkColor> profile_color,
+    bool create_shortcut,
+    Profile* profile,
+    Profile::CreateStatus status) {
   switch (status) {
     case Profile::CREATE_STATUS_LOCAL_FAIL: {
       NOTREACHED() << "Local fail in creating new profile";
@@ -254,15 +333,19 @@
     FireWebUIListener("create-profile-finished", base::Value());
 }
 
-void ProfilePickerHandler::OnProfileCreationSuccess(SkColor profile_color,
-                                                    bool create_shortcut,
-                                                    Profile* profile) {
+void ProfilePickerHandler::OnProfileCreationSuccess(
+    base::Optional<SkColor> profile_color,
+    bool create_shortcut,
+    Profile* profile) {
   DCHECK(profile);
   DCHECK(!signin_util::IsForceSigninEnabled());
 
-  // Apply a new color to the profile.
-  ThemeServiceFactory::GetForProfile(profile)->BuildAutogeneratedThemeFromColor(
-      profile_color);
+  // Apply a new color to the profile or use the default theme.
+  auto* theme_service = ThemeServiceFactory::GetForProfile(profile);
+  if (profile_color.has_value())
+    theme_service->BuildAutogeneratedThemeFromColor(*profile_color);
+  else
+    theme_service->UseDefaultTheme();
 
   // Create shortcut if edded.
   if (create_shortcut) {
diff --git a/chrome/browser/ui/webui/signin/profile_picker_handler.h b/chrome/browser/ui/webui/signin/profile_picker_handler.h
index cc8611f..bbbc834 100644
--- a/chrome/browser/ui/webui/signin/profile_picker_handler.h
+++ b/chrome/browser/ui/webui/signin/profile_picker_handler.h
@@ -36,6 +36,7 @@
   // TODO(crbug.com/1115056): Move to new handler for profile creation.
   void HandleLoadSignInProfileCreationFlow(const base::ListValue* args);
   void HandleGetNewProfileSuggestedThemeInfo(const base::ListValue* args);
+  void HandleGetProfileThemeInfo(const base::ListValue* args);
   void HandleCreateProfile(const base::ListValue* args);
 
   void GatherProfileStatistics(Profile* profile);
@@ -44,11 +45,11 @@
   void OnSwitchToProfileComplete(bool open_settings,
                                  Profile* profile,
                                  Profile::CreateStatus profile_create_status);
-  void OnProfileCreated(SkColor profile_color,
+  void OnProfileCreated(base::Optional<SkColor> profile_color,
                         bool create_shortcut,
                         Profile* profile,
                         Profile::CreateStatus status);
-  void OnProfileCreationSuccess(SkColor profile_color,
+  void OnProfileCreationSuccess(base::Optional<SkColor> profile_color,
                                 bool create_shortcut,
                                 Profile* profile);
   void PushProfilesList();
diff --git a/chrome/browser/ui/webui/signin/profile_picker_ui.cc b/chrome/browser/ui/webui/signin/profile_picker_ui.cc
index 57084b9..eec87f04 100644
--- a/chrome/browser/ui/webui/signin/profile_picker_ui.cc
+++ b/chrome/browser/ui/webui/signin/profile_picker_ui.cc
@@ -11,6 +11,7 @@
 #include "chrome/browser/profiles/profile_shortcut_manager.h"
 #include "chrome/browser/signin/signin_util.h"
 #include "chrome/browser/ui/ui_features.h"
+#include "chrome/browser/ui/webui/signin/profile_creation_customize_themes_handler.h"
 #include "chrome/browser/ui/webui/signin/profile_picker_handler.h"
 #include "chrome/browser/ui/webui/webui_util.h"
 #include "chrome/common/pref_names.h"
@@ -23,6 +24,7 @@
 #include "components/strings/grit/components_strings.h"
 #include "content/public/browser/web_ui_data_source.h"
 #include "ui/base/webui/web_ui_util.h"
+#include "ui/webui/mojo_web_ui_controller.h"
 
 namespace {
 
@@ -74,12 +76,20 @@
        IDS_PROFILE_PICKER_PROFILE_CREATION_FLOW_NOT_NOW_BUTTON_LABEL},
       {"localProfileCreationTitle",
        IDS_PROFILE_PICKER_PROFILE_CREATION_FLOW_LOCAL_PROFILE_CREATION_TITLE},
+      {"localProfileCreationThemeText",
+       IDS_PROFILE_PICKER_PROFILE_CREATION_FLOW_LOCAL_PROFILE_CREATION_THEME_TEXT},
       {"createProfileNamePlaceholder",
        IDS_PROFILE_PICKER_PROFILE_CREATION_FLOW_LOCAL_PROFILE_CREATION_INPUT_NAME},
       {"createDesktopShortcutLabel",
        IDS_PROFILE_PICKER_PROFILE_CREATION_FLOW_LOCAL_PROFILE_CREATION_SHORTCUT_TEXT},
       {"createProfileConfirm",
        IDS_PROFILE_PICKER_PROFILE_CREATION_FLOW_LOCAL_PROFILE_CREATION_DONE},
+
+      // Color picker.
+      {"colorPickerLabel", IDS_NTP_CUSTOMIZE_COLOR_PICKER_LABEL},
+      {"defaultThemeLabel", IDS_NTP_CUSTOMIZE_DEFAULT_LABEL},
+      {"thirdPartyThemeDescription", IDS_NTP_CUSTOMIZE_3PT_THEME_DESC},
+      {"uninstallThirdPartyThemeButton", IDS_NTP_CUSTOMIZE_3PT_THEME_UNINSTALL},
   };
   AddLocalizedStringsBulk(html_source, kLocalizedStrings);
   html_source->AddBoolean("askOnStartup",
@@ -106,7 +116,8 @@
 }  // namespace
 
 ProfilePickerUI::ProfilePickerUI(content::WebUI* web_ui)
-    : content::WebUIController(web_ui) {
+    : ui::MojoWebUIController(web_ui, /*enable_chrome_send=*/true),
+      customize_themes_factory_receiver_(this) {
   Profile* profile = Profile::FromWebUI(web_ui);
   content::WebUIDataSource* html_source =
       content::WebUIDataSource::Create(chrome::kChromeUIProfilePickerHost);
@@ -130,3 +141,25 @@
 gfx::Size ProfilePickerUI::GetMinimumSize() {
   return gfx::Size(kMinimumPickerSizePx, kMinimumPickerSizePx);
 }
+
+void ProfilePickerUI::BindInterface(
+    mojo::PendingReceiver<
+        customize_themes::mojom::CustomizeThemesHandlerFactory>
+        pending_receiver) {
+  if (customize_themes_factory_receiver_.is_bound()) {
+    customize_themes_factory_receiver_.reset();
+  }
+  customize_themes_factory_receiver_.Bind(std::move(pending_receiver));
+}
+
+void ProfilePickerUI::CreateCustomizeThemesHandler(
+    mojo::PendingRemote<customize_themes::mojom::CustomizeThemesClient>
+        pending_client,
+    mojo::PendingReceiver<customize_themes::mojom::CustomizeThemesHandler>
+        pending_handler) {
+  customize_themes_handler_ =
+      std::make_unique<ProfileCreationCustomizeThemesHandler>(
+          std::move(pending_client), std::move(pending_handler));
+}
+
+WEB_UI_CONTROLLER_TYPE_IMPL(ProfilePickerUI)
diff --git a/chrome/browser/ui/webui/signin/profile_picker_ui.h b/chrome/browser/ui/webui/signin/profile_picker_ui.h
index 1c58690d..0bc1e8e 100644
--- a/chrome/browser/ui/webui/signin/profile_picker_ui.h
+++ b/chrome/browser/ui/webui/signin/profile_picker_ui.h
@@ -7,19 +7,48 @@
 
 #include "base/macros.h"
 #include "content/public/browser/web_ui_controller.h"
+#include "mojo/public/cpp/bindings/receiver.h"
 #include "ui/gfx/geometry/size.h"
+#include "ui/webui/mojo_web_ui_controller.h"
+#include "ui/webui/resources/cr_components/customize_themes/customize_themes.mojom.h"
+
+class ProfileCreationCustomizeThemesHandler;
 
 // The WebUI controller for chrome://profile-picker/.
-class ProfilePickerUI : public content::WebUIController {
+class ProfilePickerUI
+    : public ui::MojoWebUIController,
+      public customize_themes::mojom::CustomizeThemesHandlerFactory {
  public:
   explicit ProfilePickerUI(content::WebUI* web_ui);
   ~ProfilePickerUI() override;
 
+  ProfilePickerUI(const ProfilePickerUI&) = delete;
+  ProfilePickerUI& operator=(const ProfilePickerUI&) = delete;
+
   // Get the minimum size for the picker UI.
   static gfx::Size GetMinimumSize();
 
+  // Instantiates the implementor of the
+  // customize_themes::mojom::CustomizeThemesHandlerFactory mojo interface
+  // passing the pending receiver that will be internally bound.
+  void BindInterface(mojo::PendingReceiver<
+                     customize_themes::mojom::CustomizeThemesHandlerFactory>
+                         pending_receiver);
+
  private:
-  DISALLOW_COPY_AND_ASSIGN(ProfilePickerUI);
+  // customize_themes::mojom::CustomizeThemesHandlerFactory:
+  void CreateCustomizeThemesHandler(
+      mojo::PendingRemote<customize_themes::mojom::CustomizeThemesClient>
+          pending_client,
+      mojo::PendingReceiver<customize_themes::mojom::CustomizeThemesHandler>
+          pending_handler) override;
+
+  std::unique_ptr<ProfileCreationCustomizeThemesHandler>
+      customize_themes_handler_;
+  mojo::Receiver<customize_themes::mojom::CustomizeThemesHandlerFactory>
+      customize_themes_factory_receiver_;
+
+  WEB_UI_CONTROLLER_TYPE_DECL();
 };
 
 #endif  // CHROME_BROWSER_UI_WEBUI_SIGNIN_PROFILE_PICKER_UI_H_
diff --git a/chrome/browser/web_applications/system_web_app_manager.cc b/chrome/browser/web_applications/system_web_app_manager.cc
index 33164fc..48575a4 100644
--- a/chrome/browser/web_applications/system_web_app_manager.cc
+++ b/chrome/browser/web_applications/system_web_app_manager.cc
@@ -446,6 +446,17 @@
   run_loop.Run();
 }
 
+std::vector<SystemAppInfo>
+SystemWebAppManager::GetRegisteredSystemAppsForTesting() const {
+  std::vector<SystemAppInfo> result;
+  result.reserve(system_app_infos_.size());
+
+  for (const auto& type_and_app_info : system_app_infos_)
+    result.push_back(type_and_app_info.second);
+
+  return result;
+}
+
 base::Optional<AppId> SystemWebAppManager::GetAppIdForSystemApp(
     SystemAppType id) const {
   auto app_url_it = system_app_infos_.find(id);
diff --git a/chrome/browser/web_applications/system_web_app_manager.h b/chrome/browser/web_applications/system_web_app_manager.h
index 319bd8e3..84f1f05 100644
--- a/chrome/browser/web_applications/system_web_app_manager.h
+++ b/chrome/browser/web_applications/system_web_app_manager.h
@@ -213,6 +213,10 @@
   // doesn't specify a minimum.
   gfx::Size GetMinimumWindowSize(const AppId& app_id) const;
 
+  // Returns a list of registered system app infos, these apps will be installed
+  // on the system.
+  std::vector<SystemAppInfo> GetRegisteredSystemAppsForTesting() const;
+
   const base::OneShotEvent& on_apps_synchronized() const {
     return *on_apps_synchronized_;
   }
diff --git a/chrome/browser/web_applications/system_web_app_manager_browsertest.cc b/chrome/browser/web_applications/system_web_app_manager_browsertest.cc
index fbadee0..ce7591a 100644
--- a/chrome/browser/web_applications/system_web_app_manager_browsertest.cc
+++ b/chrome/browser/web_applications/system_web_app_manager_browsertest.cc
@@ -1123,21 +1123,13 @@
   }
   ~SystemWebAppManagerUpgradeBrowserTest() override = default;
 
-  unsigned int GetExpectedNumberOfInstalledSystemApps() {
-#if defined(OFFICIAL_BUILD)
-    return 8;
-#else
-    return 10;
-#endif  // defined(OFFICIAL_BUILD)
-  }
-
  private:
   base::test::ScopedFeatureList features_;
 };
 
 IN_PROC_BROWSER_TEST_P(SystemWebAppManagerUpgradeBrowserTest, PRE_Upgrade) {
   WaitForTestSystemAppInstall();
-  EXPECT_GE(GetExpectedNumberOfInstalledSystemApps(),
+  EXPECT_GE(GetManager().GetRegisteredSystemAppsForTesting().size(),
             GetManager().GetAppIds().size());
 }
 
@@ -1145,7 +1137,8 @@
   WaitForTestSystemAppInstall();
   const auto& app_ids = GetManager().GetAppIds();
 
-  EXPECT_EQ(GetExpectedNumberOfInstalledSystemApps(), app_ids.size());
+  EXPECT_EQ(GetManager().GetRegisteredSystemAppsForTesting().size(),
+            app_ids.size());
 
   for (const auto& app_id : app_ids) {
     const auto type = GetManager().GetSystemAppTypeForAppId(app_id).value();
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index 6a6c80c..5489000 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-master-1598550957-d21440687343c81b6a2048a74a0e183994baca81.profdata
+chrome-mac-master-1598594188-9fa81c93dd83e8129d07cc38e4d22506253a8a4e.profdata
diff --git a/chrome/common/extensions/api/autotest_private.idl b/chrome/common/extensions/api/autotest_private.idl
index 27084a6..8067ac4 100644
--- a/chrome/common/extensions/api/autotest_private.idl
+++ b/chrome/common/extensions/api/autotest_private.idl
@@ -326,6 +326,11 @@
     boolean? showInSearch;
   };
 
+  dictionary SystemApp {
+    DOMString nameForLogging;
+    DOMString url;
+  };
+
   callback GetAllInstalledAppsCallback = void (App[] apps);
 
   dictionary ShelfItem {
@@ -570,6 +575,11 @@
   callback StopThroughputTrackerDataCollectionCallback = void
       (ThroughputTrackerAnimationData[] data);
 
+  // Callback invoked to report the number of system web apps that should be
+  // installed.
+  callback GetRegisteredSystemWebAppsCallback = void
+      (SystemApp[] systemApps);
+
   interface Functions {
     // Must be called to allow autotestPrivateAPI events to be fired.
     static void initializeEvents();
@@ -667,6 +677,13 @@
     static void getArcPackage(DOMString packageName,
                               GetArcPackageCallback callback);
 
+    // Waits for system web apps to complete the installation.
+    static void waitForSystemWebAppsInstall(VoidCallback callback);
+
+    // Returns the number of system web apps that should be installed.
+    static void getRegisteredSystemWebApps(
+        GetRegisteredSystemWebAppsCallback callback);
+
     // Launches ARC app with optional intent. Returns true if ARC is active,
     // app exists and launch request is passed to Android.
     static void launchArcApp(DOMString appId, DOMString intent,
diff --git a/chrome/common/extensions/api/file_manager_private.idl b/chrome/common/extensions/api/file_manager_private.idl
index a64b381..4990eb6 100644
--- a/chrome/common/extensions/api/file_manager_private.idl
+++ b/chrome/common/extensions/api/file_manager_private.idl
@@ -765,6 +765,11 @@
   AttachedImages[] attachedImages;
 };
 
+dictionary HoldingSpaceState {
+  // File system URLs of items pinned to the holding space.
+  DOMString[] itemUrls;
+};
+
 // Callback that does not take arguments.
 callback SimpleCallback = void();
 
@@ -897,6 +902,9 @@
 // |apps| List of Android picker apps.
 callback GetAndroidPickerAppsCallback = void(AndroidApp[] apps);
 
+// |state| Describes the current holding space state.
+callback HoldingSpaceStateCallback = void(HoldingSpaceState state);
+
 interface Functions {
   // Logout the current user for navigating to the re-authentication screen for
   // the Google account.
@@ -1312,6 +1320,22 @@
   [nocompile]
   static void invokeSharesheet([instanceof=Entry] object[] entries,
                                      SimpleCallback callback);
+
+  // Adds or removes a list of entries to temporary holding space. Any entries
+  // whose current holding space state matches the intended state will be
+  // skipped.
+  // |entries| The list of entries whose holding space needs to be updated.
+  // |add| Whether items should be added or removed from the holding space.
+  // |callback| Completion callback.
+  [nocompile]
+  static void toggleAddedToHoldingSpace([instanceOf=Entry] object[] entries,
+                                        boolean added,
+                                        optional SimpleCallback callback);
+
+  // Retrieves the current holding space state, for example the list of items
+  // the holding space currently contains.
+  // |callback| The result callback.
+  static void getHoldingSpaceState(HoldingSpaceStateCallback callback);
 };
 
 interface Events {
diff --git a/chrome/common/extensions/api/file_manager_private_internal.idl b/chrome/common/extensions/api/file_manager_private_internal.idl
index f60d5974..e409a97 100644
--- a/chrome/common/extensions/api/file_manager_private_internal.idl
+++ b/chrome/common/extensions/api/file_manager_private_internal.idl
@@ -121,5 +121,7 @@
                              GetThumbnailCallback callback);
     static void sharesheetHasTargets(DOMString[] urls, BooleanCallback callback);
     static void invokeSharesheet(DOMString[] urls, SimpleCallback callback);
+    static void toggleAddedToHoldingSpace(DOMString[] urls, boolean add,
+                                          optional SimpleCallback callback);
   };
 };
diff --git a/chrome/credential_provider/gaiacp/BUILD.gn b/chrome/credential_provider/gaiacp/BUILD.gn
index 96b5e80d5..b182615d 100644
--- a/chrome/credential_provider/gaiacp/BUILD.gn
+++ b/chrome/credential_provider/gaiacp/BUILD.gn
@@ -38,6 +38,9 @@
     "os_user_manager.h",
     "reg_utils.cc",
     "reg_utils.h",
+    "scoped_handle.h",
+    "scoped_lsa_policy.cc",
+    "scoped_lsa_policy.h",
     "user_policies.cc",
     "user_policies.h",
     "user_policies_manager.cc",
@@ -118,9 +121,6 @@
     "password_recovery_manager.h",
     "reauth_credential.cc",
     "reauth_credential.h",
-    "scoped_handle.h",
-    "scoped_lsa_policy.cc",
-    "scoped_lsa_policy.h",
     "scoped_user_profile.cc",
     "scoped_user_profile.h",
     "stdafx.h",
diff --git a/chrome/renderer/chrome_content_renderer_client.cc b/chrome/renderer/chrome_content_renderer_client.cc
index eb6e5d3..87adb8c4 100644
--- a/chrome/renderer/chrome_content_renderer_client.cc
+++ b/chrome/renderer/chrome_content_renderer_client.cc
@@ -1495,8 +1495,6 @@
   // Mojo implementation (e.g. WebView).  Web Share is shipped on Android.
 #if defined(OS_CHROMEOS) || defined(OS_WIN)
   if (base::FeatureList::IsEnabled(features::kWebShare))
-#endif
-#if defined(OS_CHROMEOS) || defined(OS_WIN) || defined(OS_ANDROID)
     blink::WebRuntimeFeatures::EnableWebShare(true);
 #endif
 
diff --git a/chrome/renderer/resources/extensions/file_manager_private_custom_bindings.js b/chrome/renderer/resources/extensions/file_manager_private_custom_bindings.js
index 486df76..3b5dd4f3 100644
--- a/chrome/renderer/resources/extensions/file_manager_private_custom_bindings.js
+++ b/chrome/renderer/resources/extensions/file_manager_private_custom_bindings.js
@@ -340,6 +340,13 @@
         });
         fileManagerPrivateInternal.invokeSharesheet(urls, callback);
       });
+
+  apiFunctions.setHandleRequest(
+      'toggleAddedToHoldingSpace', function(entries, added, callback) {
+        const urls = entries.map(entry => getEntryURL(entry));
+        fileManagerPrivateInternal.toggleAddedToHoldingSpace(
+            urls, added, callback);
+      });
 });
 
 bindingUtil.registerEventArgumentMassager(
diff --git a/chrome/services/sharing/nearby/nearby_connections.cc b/chrome/services/sharing/nearby/nearby_connections.cc
index 3a758e540..634bcba 100644
--- a/chrome/services/sharing/nearby/nearby_connections.cc
+++ b/chrome/services/sharing/nearby/nearby_connections.cc
@@ -24,7 +24,7 @@
   mojo::SharedRemote<mojom::ConnectionLifecycleListener> remote(
       std::move(listener));
   return ConnectionRequestInfo{
-      .name = std::string(endpoint_info.begin(), endpoint_info.end()),
+      .endpoint_info = ByteArrayFromMojom(endpoint_info),
       .listener = {
           .initiated_cb =
               [remote](const std::string& endpoint_id,
@@ -37,7 +37,7 @@
                     mojom::ConnectionInfo::New(
                         info.authentication_token,
                         ByteArrayToMojom(info.raw_authentication_token),
-                        ByteArrayToMojom(info.endpoint_info),
+                        ByteArrayToMojom(info.remote_endpoint_info),
                         info.is_incoming_connection));
               },
           .accepted_cb =
@@ -230,7 +230,7 @@
   DiscoveryListener discovery_listener{
       .endpoint_found_cb =
           [remote](const std::string& endpoint_id,
-                   const std::string& endpoint_name,
+                   const ByteArray& endpoint_info,
                    const std::string& service_id) {
             if (!remote) {
               return;
@@ -238,9 +238,7 @@
 
             remote->OnEndpointFound(
                 endpoint_id, mojom::DiscoveredEndpointInfo::New(
-                                 std::vector<uint8_t>(endpoint_name.begin(),
-                                                      endpoint_name.end()),
-                                 service_id));
+                                 ByteArrayToMojom(endpoint_info), service_id));
           },
       .endpoint_lost_cb =
           [remote](const std::string& endpoint_id) {
@@ -269,7 +267,8 @@
   core_->RequestConnection(
       endpoint_id,
       CreateConnectionRequestInfo(endpoint_info, std::move(listener)),
-      ResultCallbackFromMojom(std::move(callback)));
+      // TODO(alexchau): Add ConnectionsOptions to mojo.
+      /*options=*/{}, ResultCallbackFromMojom(std::move(callback)));
 }
 
 void NearbyConnections::DisconnectFromEndpoint(
diff --git a/chrome/services/sharing/nearby/nearby_connections_conversions.cc b/chrome/services/sharing/nearby/nearby_connections_conversions.cc
index 242f936..7349a87 100644
--- a/chrome/services/sharing/nearby/nearby_connections_conversions.cc
+++ b/chrome/services/sharing/nearby/nearby_connections_conversions.cc
@@ -52,6 +52,9 @@
       return mojom::Status::kNotConnectedToEndpoint;
     case Status::Value::kBluetoothError:
       return mojom::Status::kBluetoothError;
+    // TODO(alexchau): Add kBleError to mojo.
+    case Status::Value::kBleError:
+      return mojom::Status::kError;
     case Status::Value::kWifiLanError:
       return mojom::Status::kWifiLanError;
     case Status::Value::kPayloadUnknown:
diff --git a/chrome/services/sharing/nearby/nearby_connections_unittest.cc b/chrome/services/sharing/nearby/nearby_connections_unittest.cc
index 77a5318..b377ae1 100644
--- a/chrome/services/sharing/nearby/nearby_connections_unittest.cc
+++ b/chrome/services/sharing/nearby/nearby_connections_unittest.cc
@@ -216,7 +216,8 @@
       FakeConnectionLifecycleListener& fake_connection_life_cycle_listener,
       const EndpointData& endpoint_data) {
     ClientProxy* client_proxy;
-
+    std::vector<uint8_t> endpoint_info(std::begin(kEndpointInfo),
+                                       std::end(kEndpointInfo));
     EXPECT_CALL(*service_controller_ptr_, StartAdvertising)
         .WillOnce([&](ClientProxy* client, const std::string& service_id,
                       const ConnectionOptions& options,
@@ -229,30 +230,26 @@
           EXPECT_TRUE(options.allowed.wifi_lan);
           EXPECT_TRUE(options.auto_upgrade_bandwidth);
           EXPECT_TRUE(options.enforce_topology_constraints);
-          EXPECT_EQ(
-              std::string(std::begin(kEndpointInfo), std::end(kEndpointInfo)),
-              info.name);
+          EXPECT_EQ(endpoint_info, ByteArrayToMojom(info.endpoint_info));
 
           client_proxy->StartedAdvertising(service_id, options.strategy,
                                            info.listener,
                                            /*mediums=*/{});
           client_proxy->OnConnectionInitiated(
               endpoint_data.remote_endpoint_id,
-              {.authentication_token = kAuthenticationToken,
+              {.remote_endpoint_info =
+                   ByteArrayFromMojom(endpoint_data.remote_endpoint_info),
+               .authentication_token = kAuthenticationToken,
                .raw_authentication_token = ByteArray(
                    kRawAuthenticationToken, sizeof(kRawAuthenticationToken)),
-               .endpoint_info =
-                   ByteArrayFromMojom(endpoint_data.remote_endpoint_info),
                .is_incoming_connection = false},
-              info.listener);
+              options, info.listener);
           return Status{Status::kSuccess};
         });
 
     base::RunLoop start_advertising_run_loop;
     nearby_connections_->StartAdvertising(
-        std::vector<uint8_t>(std::begin(kEndpointInfo),
-                             std::end(kEndpointInfo)),
-        kServiceId, CreateAdvertisingOptions(),
+        endpoint_info, kServiceId, CreateAdvertisingOptions(),
         fake_connection_life_cycle_listener.receiver.BindNewPipeAndPassRemote(),
         base::BindLambdaForTesting([&](mojom::Status status) {
           EXPECT_EQ(mojom::Status::kSuccess, status);
@@ -267,32 +264,30 @@
       FakeConnectionLifecycleListener& fake_connection_life_cycle_listener,
       const EndpointData& endpoint_data) {
     ClientProxy* client_proxy;
+    std::vector<uint8_t> endpoint_info(std::begin(kEndpointInfo),
+                                       std::end(kEndpointInfo));
     EXPECT_CALL(*service_controller_ptr_, RequestConnection)
         .WillOnce([&](ClientProxy* client, const std::string& endpoint_id,
-                      const ConnectionRequestInfo& info) {
+                      const ConnectionRequestInfo& info,
+                      const ConnectionOptions& options) {
           client_proxy = client;
           EXPECT_EQ(endpoint_data.remote_endpoint_id, endpoint_id);
-          EXPECT_EQ(
-              std::string(std::begin(kEndpointInfo), std::end(kEndpointInfo)),
-              info.name);
+          EXPECT_EQ(endpoint_info, ByteArrayToMojom(info.endpoint_info));
           client_proxy->OnConnectionInitiated(
               endpoint_id,
-              {.authentication_token = kAuthenticationToken,
+              {.remote_endpoint_info =
+                   ByteArrayFromMojom(endpoint_data.remote_endpoint_info),
+               .authentication_token = kAuthenticationToken,
                .raw_authentication_token = ByteArray(
                    kRawAuthenticationToken, sizeof(kRawAuthenticationToken)),
-               .endpoint_info = ByteArray(
-                   std::string(endpoint_data.remote_endpoint_info.begin(),
-                               endpoint_data.remote_endpoint_info.end())),
                .is_incoming_connection = false},
-              info.listener);
+              options, info.listener);
           return Status{Status::kSuccess};
         });
 
     base::RunLoop request_connection_run_loop;
     nearby_connections_->RequestConnection(
-        std::vector<uint8_t>(std::begin(kEndpointInfo),
-                             std::end(kEndpointInfo)),
-        endpoint_data.remote_endpoint_id,
+        endpoint_info, endpoint_data.remote_endpoint_id,
         fake_connection_life_cycle_listener.receiver.BindNewPipeAndPassRemote(),
         base::BindLambdaForTesting([&](mojom::Status status) {
           EXPECT_EQ(mojom::Status::kSuccess, status);
@@ -390,8 +385,7 @@
 
   client_proxy->OnEndpointFound(
       kServiceId, endpoint_data.remote_endpoint_id,
-      std::string(endpoint_data.remote_endpoint_info.begin(),
-                  endpoint_data.remote_endpoint_info.end()),
+      ByteArrayFromMojom(endpoint_data.remote_endpoint_info),
       /*mediums=*/{});
   endpoint_found_run_loop.Run();
 
@@ -429,8 +423,7 @@
   ClientProxy* client_proxy = StartDiscovery(fake_discovery_listener);
   client_proxy->OnEndpointFound(
       kServiceId, endpoint_data.remote_endpoint_id,
-      std::string(endpoint_data.remote_endpoint_info.begin(),
-                  endpoint_data.remote_endpoint_info.end()),
+      ByteArrayFromMojom(endpoint_data.remote_endpoint_info),
       /*mediums=*/{});
 
   base::RunLoop initiated_run_loop;
@@ -457,8 +450,7 @@
   ClientProxy* client_proxy = StartDiscovery(fake_discovery_listener);
   client_proxy->OnEndpointFound(
       kServiceId, endpoint_data.remote_endpoint_id,
-      std::string(endpoint_data.remote_endpoint_info.begin(),
-                  endpoint_data.remote_endpoint_info.end()),
+      ByteArrayFromMojom(endpoint_data.remote_endpoint_info),
       /*mediums=*/{});
 
   FakeConnectionLifecycleListener fake_connection_life_cycle_listener;
@@ -482,8 +474,7 @@
   ClientProxy* client_proxy = StartDiscovery(fake_discovery_listener);
   client_proxy->OnEndpointFound(
       kServiceId, endpoint_data.remote_endpoint_id,
-      std::string(endpoint_data.remote_endpoint_info.begin(),
-                  endpoint_data.remote_endpoint_info.end()),
+      ByteArrayFromMojom(endpoint_data.remote_endpoint_info),
       /*mediums=*/{});
 
   FakeConnectionLifecycleListener fake_connection_life_cycle_listener;
@@ -509,8 +500,7 @@
   ClientProxy* client_proxy = StartDiscovery(fake_discovery_listener);
   client_proxy->OnEndpointFound(
       kServiceId, endpoint_data.remote_endpoint_id,
-      std::string(endpoint_data.remote_endpoint_info.begin(),
-                  endpoint_data.remote_endpoint_info.end()),
+      ByteArrayFromMojom(endpoint_data.remote_endpoint_info),
       /*mediums=*/{});
 
   FakeConnectionLifecycleListener fake_connection_life_cycle_listener;
@@ -556,8 +546,7 @@
   ClientProxy* client_proxy = StartDiscovery(fake_discovery_listener);
   client_proxy->OnEndpointFound(
       kServiceId, endpoint_data.remote_endpoint_id,
-      std::string(endpoint_data.remote_endpoint_info.begin(),
-                  endpoint_data.remote_endpoint_info.end()),
+      ByteArrayFromMojom(endpoint_data.remote_endpoint_info),
       /*mediums=*/{});
 
   FakeConnectionLifecycleListener fake_connection_life_cycle_listener;
@@ -585,8 +574,7 @@
   ClientProxy* client_proxy = StartDiscovery(fake_discovery_listener);
   client_proxy->OnEndpointFound(
       kServiceId, endpoint_data.remote_endpoint_id,
-      std::string(endpoint_data.remote_endpoint_info.begin(),
-                  endpoint_data.remote_endpoint_info.end()),
+      ByteArrayFromMojom(endpoint_data.remote_endpoint_info),
       /*mediums=*/{});
 
   FakeConnectionLifecycleListener fake_connection_life_cycle_listener;
@@ -626,8 +614,7 @@
   ClientProxy* client_proxy = StartDiscovery(fake_discovery_listener);
   client_proxy->OnEndpointFound(
       kServiceId, endpoint_data.remote_endpoint_id,
-      std::string(endpoint_data.remote_endpoint_info.begin(),
-                  endpoint_data.remote_endpoint_info.end()),
+      ByteArrayFromMojom(endpoint_data.remote_endpoint_info),
       /*mediums=*/{});
 
   FakeConnectionLifecycleListener fake_connection_life_cycle_listener;
@@ -649,8 +636,7 @@
   payload_progress_run_loop.Run();
 }
 
-// TODO(crbug/1076008): Re-enable test after upprev NearbyConnections.
-TEST_F(NearbyConnectionsTest, DISABLED_SendBytesPayload) {
+TEST_F(NearbyConnectionsTest, SendBytesPayload) {
   const std::vector<uint8_t> expected_payload(std::begin(kPayload),
                                               std::end(kPayload));
 
@@ -659,8 +645,7 @@
   ClientProxy* client_proxy = StartDiscovery(fake_discovery_listener);
   client_proxy->OnEndpointFound(
       kServiceId, endpoint_data.remote_endpoint_id,
-      std::string(endpoint_data.remote_endpoint_info.begin(),
-                  endpoint_data.remote_endpoint_info.end()),
+      ByteArrayFromMojom(endpoint_data.remote_endpoint_info),
       /*mediums=*/{});
 
   FakeConnectionLifecycleListener fake_connection_life_cycle_listener;
@@ -693,8 +678,7 @@
   send_payload_run_loop.Run();
 }
 
-// TODO(crbug/1076008): Re-enable test after upprev NearbyConnections.
-TEST_F(NearbyConnectionsTest, DISABLED_SendBytesPayloadCancelled) {
+TEST_F(NearbyConnectionsTest, SendBytesPayloadCancelled) {
   const std::vector<uint8_t> expected_payload(std::begin(kPayload),
                                               std::end(kPayload));
 
@@ -703,8 +687,7 @@
   EndpointData endpoint_data = CreateEndpointData(1);
   client_proxy->OnEndpointFound(
       kServiceId, endpoint_data.remote_endpoint_id,
-      std::string(endpoint_data.remote_endpoint_info.begin(),
-                  endpoint_data.remote_endpoint_info.end()),
+      ByteArrayFromMojom(endpoint_data.remote_endpoint_info),
       /*mediums=*/{});
 
   FakeConnectionLifecycleListener fake_connection_life_cycle_listener;
@@ -751,8 +734,7 @@
   cancel_payload_run_loop.Run();
 }
 
-// TODO(crbug/1076008): Re-enable test after upprev NearbyConnections.
-TEST_F(NearbyConnectionsTest, DISABLED_SendFilePayload) {
+TEST_F(NearbyConnectionsTest, SendFilePayload) {
   const std::vector<uint8_t> expected_payload(std::begin(kPayload),
                                               std::end(kPayload));
 
@@ -761,8 +743,7 @@
   ClientProxy* client_proxy = StartDiscovery(fake_discovery_listener);
   client_proxy->OnEndpointFound(
       kServiceId, endpoint_data.remote_endpoint_id,
-      std::string(endpoint_data.remote_endpoint_info.begin(),
-                  endpoint_data.remote_endpoint_info.end()),
+      ByteArrayFromMojom(endpoint_data.remote_endpoint_info),
       /*mediums=*/{});
 
   FakeConnectionLifecycleListener fake_connection_life_cycle_listener;
@@ -786,19 +767,25 @@
       });
 
   base::FilePath path;
-  ASSERT_TRUE(base::CreateTemporaryFile(&path));
-  base::File file(path, base::File::Flags::FLAG_CREATE_ALWAYS |
-                            base::File::Flags::FLAG_READ |
-                            base::File::Flags::FLAG_WRITE);
-  EXPECT_TRUE(file.WriteAndCheck(
+  EXPECT_TRUE(base::CreateTemporaryFile(&path));
+  base::File output_file(path, base::File::Flags::FLAG_CREATE_ALWAYS |
+                                   base::File::Flags::FLAG_WRITE);
+  ASSERT_TRUE(output_file.IsValid());
+  EXPECT_TRUE(output_file.WriteAndCheck(
       /*offset=*/0, base::make_span(expected_payload)));
+  EXPECT_TRUE(output_file.Flush());
+  output_file.Close();
+
+  base::File input_file(
+      path, base::File::Flags::FLAG_OPEN | base::File::Flags::FLAG_READ);
+  EXPECT_TRUE(input_file.IsValid());
 
   base::RunLoop send_payload_run_loop;
   nearby_connections_->SendPayload(
       {endpoint_data.remote_endpoint_id},
       mojom::Payload::New(kPayloadId,
                           mojom::PayloadContent::NewFile(
-                              mojom::FilePayload::New(std::move(file)))),
+                              mojom::FilePayload::New(std::move(input_file)))),
       base::BindLambdaForTesting([&](mojom::Status status) {
         EXPECT_EQ(mojom::Status::kSuccess, status);
         send_payload_run_loop.Quit();
@@ -899,8 +886,7 @@
   ClientProxy* client_proxy = StartDiscovery(fake_discovery_listener);
   client_proxy->OnEndpointFound(
       kServiceId, endpoint_data.remote_endpoint_id,
-      std::string(endpoint_data.remote_endpoint_info.begin(),
-                  endpoint_data.remote_endpoint_info.end()),
+      ByteArrayFromMojom(endpoint_data.remote_endpoint_info),
       /*mediums=*/{});
 
   // Set up a connection to one endpoint.
@@ -915,8 +901,7 @@
   EndpointData endpoint_data2 = CreateEndpointData(2);
   client_proxy->OnEndpointFound(
       kServiceId, endpoint_data2.remote_endpoint_id,
-      std::string(endpoint_data2.remote_endpoint_info.begin(),
-                  endpoint_data2.remote_endpoint_info.end()),
+      ByteArrayFromMojom(endpoint_data2.remote_endpoint_info),
       /*mediums=*/{});
 
   FakeConnectionLifecycleListener fake_connection_life_cycle_listener2;
@@ -971,8 +956,7 @@
   ClientProxy* client_proxy = StartDiscovery(fake_discovery_listener);
   client_proxy->OnEndpointFound(
       kServiceId, endpoint_data.remote_endpoint_id,
-      std::string(endpoint_data.remote_endpoint_info.begin(),
-                  endpoint_data.remote_endpoint_info.end()),
+      ByteArrayFromMojom(endpoint_data.remote_endpoint_info),
       /*mediums=*/{});
 
   // Requesting a bandwidth upgrade should fail.
diff --git a/chrome/services/sharing/nearby/platform_v2/ble_medium.cc b/chrome/services/sharing/nearby/platform_v2/ble_medium.cc
index 1cc980e1..34c9294 100644
--- a/chrome/services/sharing/nearby/platform_v2/ble_medium.cc
+++ b/chrome/services/sharing/nearby/platform_v2/ble_medium.cc
@@ -12,36 +12,36 @@
 
 BleMedium::~BleMedium() = default;
 
-bool BleMedium::StartAdvertising(absl::string_view service_id,
+bool BleMedium::StartAdvertising(const std::string& service_id,
                                  const ByteArray& advertisement) {
   // TODO(b/154845685): Implement this method.
   NOTIMPLEMENTED();
   return false;
 }
 
-void BleMedium::StopAdvertising(absl::string_view service_id) {
+bool BleMedium::StopAdvertising(const std::string& service_id) {
   // TODO(b/154845685): Implement this method.
   NOTIMPLEMENTED();
+  return false;
 }
 
-bool BleMedium::StartScanning(
-    absl::string_view service_id,
-    const api::BleMedium::DiscoveredPeripheralCallback&
-        discovered_peripheral_callback) {
+bool BleMedium::StartScanning(const std::string& service_id,
+                              api::BleMedium::DiscoveredPeripheralCallback
+                                  discovered_peripheral_callback) {
   // TODO(b/154848193): Implement this method.
   NOTIMPLEMENTED();
   return true;
 }
 
-void BleMedium::StopScanning(absl::string_view service_id) {
+bool BleMedium::StopScanning(const std::string& service_id) {
   // TODO(b/154848193): Implement this method.
   NOTIMPLEMENTED();
+  return false;
 }
 
 bool BleMedium::StartAcceptingConnections(
-    absl::string_view service_id,
-    const api::BleMedium::AcceptedConnectionCallback&
-        accepted_connection_callback) {
+    const std::string& service_id,
+    api::BleMedium::AcceptedConnectionCallback accepted_connection_callback) {
   // Do not actually start a GATT server, because BLE connections are not yet
   // supported in Chrome Nearby. However, return true in order to allow
   // BLE advertising to continue.
@@ -50,13 +50,14 @@
   return true;
 }
 
-void BleMedium::StopAcceptingConnections(const std::string& service_id) {
+bool BleMedium::StopAcceptingConnections(const std::string& service_id) {
   // Do nothing. BLE connections are not yet supported in Chrome Nearby.
+  return false;
 }
 
 std::unique_ptr<api::BleSocket> BleMedium::Connect(
-    api::BlePeripheral* ble_peripheral,
-    absl::string_view service_id) {
+    api::BlePeripheral& ble_peripheral,
+    const std::string& service_id) {
   // Do nothing. BLE connections are not yet supported in Chrome Nearby.
   return nullptr;
 }
diff --git a/chrome/services/sharing/nearby/platform_v2/ble_medium.h b/chrome/services/sharing/nearby/platform_v2/ble_medium.h
index 6400374..455061f 100644
--- a/chrome/services/sharing/nearby/platform_v2/ble_medium.h
+++ b/chrome/services/sharing/nearby/platform_v2/ble_medium.h
@@ -24,20 +24,20 @@
   BleMedium& operator=(const BleMedium&) = delete;
 
   // api::BleMedium:
-  bool StartAdvertising(absl::string_view service_id,
+  bool StartAdvertising(const std::string& service_id,
                         const ByteArray& advertisement) override;
-  void StopAdvertising(absl::string_view service_id) override;
-  bool StartScanning(absl::string_view service_id,
-                     const DiscoveredPeripheralCallback&
-                         discovered_peripheral_callback) override;
-  void StopScanning(absl::string_view service_id) override;
+  bool StopAdvertising(const std::string& service_id) override;
+  bool StartScanning(
+      const std::string& service_id,
+      DiscoveredPeripheralCallback discovered_peripheral_callback) override;
+  bool StopScanning(const std::string& service_id) override;
   bool StartAcceptingConnections(
-      absl::string_view service_id,
-      const AcceptedConnectionCallback& accepted_connection_callback) override;
-  void StopAcceptingConnections(const std::string& service_id) override;
+      const std::string& service_id,
+      AcceptedConnectionCallback accepted_connection_callback) override;
+  bool StopAcceptingConnections(const std::string& service_id) override;
   std::unique_ptr<api::BleSocket> Connect(
-      api::BlePeripheral* ble_peripheral,
-      absl::string_view service_id) override;
+      api::BlePeripheral& ble_peripheral,
+      const std::string& service_id) override;
 };
 
 }  // namespace chrome
diff --git a/chrome/services/sharing/nearby/platform_v2/ble_medium_unittest.cc b/chrome/services/sharing/nearby/platform_v2/ble_medium_unittest.cc
index eea98768..d4f3c32 100644
--- a/chrome/services/sharing/nearby/platform_v2/ble_medium_unittest.cc
+++ b/chrome/services/sharing/nearby/platform_v2/ble_medium_unittest.cc
@@ -19,16 +19,6 @@
 
 const char kServiceName[] = "NearbySharing";
 
-class FakeAcceptedConnectionCallback
-    : public BleMedium::AcceptedConnectionCallback {
- public:
-  ~FakeAcceptedConnectionCallback() override = default;
-
-  // BleMedium::AcceptedConnectionCallback:
-  void OnConnectionAccepted(std::unique_ptr<api::BleSocket> socket,
-                            absl::string_view service_id) override {}
-};
-
 }  // namespace
 
 class BleMediumTest : public testing::Test {
@@ -42,7 +32,6 @@
 
  protected:
   std::unique_ptr<BleMedium> ble_medium_;
-  FakeAcceptedConnectionCallback fake_accepted_connection_callback_;
 
   base::test::TaskEnvironment task_environment_;
 };
@@ -57,8 +46,8 @@
 
 TEST_F(BleMediumTest, TestStartAcceptingConnections) {
   // StartAcceptingConnections() should do nothing but still return true.
-  EXPECT_TRUE(ble_medium_->StartAcceptingConnections(
-      kServiceName, fake_accepted_connection_callback_));
+  EXPECT_TRUE(
+      ble_medium_->StartAcceptingConnections(kServiceName, /*callback=*/{}));
 }
 
 TEST_F(BleMediumTest, TestConnect) {
@@ -66,7 +55,7 @@
   BlePeripheral ble_peripheral(bluetooth_device);
 
   // Connect() should do nothing and not return a valid api::BleSocket.
-  EXPECT_FALSE(ble_medium_->Connect(&ble_peripheral, kServiceName));
+  EXPECT_FALSE(ble_medium_->Connect(ble_peripheral, kServiceName));
 }
 
 }  // namespace chrome
diff --git a/chrome/services/sharing/nearby/platform_v2/ble_peripheral.cc b/chrome/services/sharing/nearby/platform_v2/ble_peripheral.cc
index 2ed68f7f..ee07f93d 100644
--- a/chrome/services/sharing/nearby/platform_v2/ble_peripheral.cc
+++ b/chrome/services/sharing/nearby/platform_v2/ble_peripheral.cc
@@ -8,13 +8,19 @@
 namespace nearby {
 namespace chrome {
 
-BlePeripheral::BlePeripheral(api::BluetoothDevice& bluetooth_device)
-    : bluetooth_device_(bluetooth_device) {}
+BlePeripheral::BlePeripheral(api::BluetoothDevice& bluetooth_device) {}
 
 BlePeripheral::~BlePeripheral() = default;
 
-api::BluetoothDevice& BlePeripheral::GetBluetoothDevice() {
-  return bluetooth_device_;
+std::string BlePeripheral::GetName() const {
+  // TODO(hansberry): Implement.
+  return std::string();
+}
+
+ByteArray BlePeripheral::GetAdvertisementBytes(
+    const std::string& service_id) const {
+  // TODO(hansberry): Implement.
+  return ByteArray();
 }
 
 }  // namespace chrome
diff --git a/chrome/services/sharing/nearby/platform_v2/ble_peripheral.h b/chrome/services/sharing/nearby/platform_v2/ble_peripheral.h
index 47a4839..79f23a0 100644
--- a/chrome/services/sharing/nearby/platform_v2/ble_peripheral.h
+++ b/chrome/services/sharing/nearby/platform_v2/ble_peripheral.h
@@ -21,10 +21,8 @@
   BlePeripheral& operator=(const BlePeripheral&) = delete;
 
   // api::BlePeripheral:
-  api::BluetoothDevice& GetBluetoothDevice() override;
-
- private:
-  api::BluetoothDevice& bluetooth_device_;
+  std::string GetName() const override;
+  ByteArray GetAdvertisementBytes(const std::string& service_id) const override;
 };
 
 }  // namespace chrome
diff --git a/chrome/services/sharing/nearby/platform_v2/bluetooth_adapter.cc b/chrome/services/sharing/nearby/platform_v2/bluetooth_adapter.cc
index 84de7c67f..52996da 100644
--- a/chrome/services/sharing/nearby/platform_v2/bluetooth_adapter.cc
+++ b/chrome/services/sharing/nearby/platform_v2/bluetooth_adapter.cc
@@ -64,6 +64,11 @@
   return call_success && set_name_success;
 }
 
+std::string BluetoothAdapter::GetMacAddress() const {
+  // TODO(hansberry): Implement.
+  return std::string();
+}
+
 }  // namespace chrome
 }  // namespace nearby
 }  // namespace location
diff --git a/chrome/services/sharing/nearby/platform_v2/bluetooth_adapter.h b/chrome/services/sharing/nearby/platform_v2/bluetooth_adapter.h
index 9292ad4..41f12986 100644
--- a/chrome/services/sharing/nearby/platform_v2/bluetooth_adapter.h
+++ b/chrome/services/sharing/nearby/platform_v2/bluetooth_adapter.h
@@ -32,6 +32,7 @@
   bool SetScanMode(ScanMode scan_mode) override;
   std::string GetName() const override;
   bool SetName(absl::string_view name) override;
+  std::string GetMacAddress() const override;
 
  private:
   // This reference is owned by the top-level Nearby Connections interface and
diff --git a/chrome/services/sharing/nearby/platform_v2/bluetooth_classic_medium.cc b/chrome/services/sharing/nearby/platform_v2/bluetooth_classic_medium.cc
index f5157d4..f6b652c 100644
--- a/chrome/services/sharing/nearby/platform_v2/bluetooth_classic_medium.cc
+++ b/chrome/services/sharing/nearby/platform_v2/bluetooth_classic_medium.cc
@@ -111,6 +111,12 @@
   return nullptr;
 }
 
+BluetoothDevice* BluetoothClassicMedium::FindRemoteDevice(
+    const std::string& mac_address) {
+  // TODO(hansberry): Implement.
+  return nullptr;
+}
+
 void BluetoothClassicMedium::PresentChanged(bool present) {
   // TODO(hansberry): It is unclear to me how the API implementation can signal
   // to Core that |present| has become unexpectedly false. Need to ask
diff --git a/chrome/services/sharing/nearby/platform_v2/bluetooth_classic_medium.h b/chrome/services/sharing/nearby/platform_v2/bluetooth_classic_medium.h
index b23be02..67bc556 100644
--- a/chrome/services/sharing/nearby/platform_v2/bluetooth_classic_medium.h
+++ b/chrome/services/sharing/nearby/platform_v2/bluetooth_classic_medium.h
@@ -41,6 +41,7 @@
   std::unique_ptr<api::BluetoothServerSocket> ListenForService(
       const std::string& service_name,
       const std::string& service_uuid) override;
+  BluetoothDevice* FindRemoteDevice(const std::string& mac_address) override;
 
  private:
   // bluetooth::mojom::AdapterObserver:
diff --git a/chrome/services/sharing/nearby/platform_v2/bluetooth_device.cc b/chrome/services/sharing/nearby/platform_v2/bluetooth_device.cc
index 171672b4..bc04e22 100644
--- a/chrome/services/sharing/nearby/platform_v2/bluetooth_device.cc
+++ b/chrome/services/sharing/nearby/platform_v2/bluetooth_device.cc
@@ -17,6 +17,11 @@
   return device_info_->name_for_display;
 }
 
+std::string BluetoothDevice::GetMacAddress() const {
+  // TODO(hansberry): Implement.
+  return std::string();
+}
+
 std::string BluetoothDevice::GetAddress() const {
   return device_info_->address;
 }
diff --git a/chrome/services/sharing/nearby/platform_v2/bluetooth_device.h b/chrome/services/sharing/nearby/platform_v2/bluetooth_device.h
index fe17f21..1f87b54 100644
--- a/chrome/services/sharing/nearby/platform_v2/bluetooth_device.h
+++ b/chrome/services/sharing/nearby/platform_v2/bluetooth_device.h
@@ -25,6 +25,7 @@
 
   // api::BluetoothDevice:
   std::string GetName() const override;
+  std::string GetMacAddress() const override;
 
   std::string GetAddress() const;
   void UpdateDeviceInfo(bluetooth::mojom::DeviceInfoPtr device_info);
diff --git a/chrome/test/DEPS b/chrome/test/DEPS
index dc073df..719964b 100644
--- a/chrome/test/DEPS
+++ b/chrome/test/DEPS
@@ -3,6 +3,7 @@
   # rely on components.
   "+ash",
   "+chrome",
+  "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
   "+chrome/grit",
   "+chromeos",
   "+components/autofill/content",
diff --git a/chrome/test/data/android/manage_wpr_archives.py b/chrome/test/data/android/manage_wpr_archives.py
index 297325f..376dc22 100755
--- a/chrome/test/data/android/manage_wpr_archives.py
+++ b/chrome/test/data/android/manage_wpr_archives.py
@@ -7,10 +7,12 @@
 # for Android WPR record replay tests or upload any newly generated ones.
 
 import argparse
+import logging
 import os
 
 from upload_download_utils import download
 from upload_download_utils import upload
+from upload_download_utils import verify_file_exists
 
 STORAGE_BUCKET = 'chrome-wpr-archives'
 THIS_DIR = os.path.abspath(os.path.dirname(__file__))
@@ -39,6 +41,8 @@
     for d in WPR_RECORD_REPLAY_TEST_DIRECTORIES:
       download(d, _is_file_of_interest,
                'WPR archives', STORAGE_BUCKET)
+      if not verify_file_exists(d, _is_file_of_interest):
+        logging.error('There is not file of interest in dir {}'.format(d))
   else:
     for d in WPR_RECORD_REPLAY_TEST_DIRECTORIES:
       upload(d, _is_file_of_interest,
diff --git a/chrome/test/data/android/upload_download_utils.py b/chrome/test/data/android/upload_download_utils.py
index 57b7eaf..103ce29c 100644
--- a/chrome/test/data/android/upload_download_utils.py
+++ b/chrome/test/data/android/upload_download_utils.py
@@ -57,6 +57,16 @@
                   '%d: %s', scenario, directory, e.returncode, e.output)
 
 
+# TODO(crbug/1096656): Remove this after root cause is found.
+def verify_file_exists(directory, filter):
+  """Verifies that some file exists in the directory."""
+  for _, _, file_list in os.walk(directory):
+    for f in file_list:
+      if filter(f):
+        return True
+  return False
+
+
 def _get_files_to_delete(directory, filter):
   """Returns a list of local files to delete.
 
diff --git a/chrome/test/data/android/upload_download_utils_test.py b/chrome/test/data/android/upload_download_utils_test.py
index 9a17c69..2841d5b6 100755
--- a/chrome/test/data/android/upload_download_utils_test.py
+++ b/chrome/test/data/android/upload_download_utils_test.py
@@ -82,7 +82,8 @@
         os.path.join(pathname, 'scenario1.wprgo'),
     ].sort()
     self.assertEquals(
-        upload_download_utils._get_files_to_upload(pathname, filter_all).sort(),
+        upload_download_utils._get_files_to_upload(
+            pathname, filter_all).sort(),
         expected_file_list)
 
     expected_file_list = [
@@ -90,8 +91,8 @@
         os.path.join(pathname, 'image2.png'),
     ].sort()
     self.assertEquals(
-        upload_download_utils._get_files_to_upload(pathname, filter_image_only)
-            .sort(),
+        upload_download_utils._get_files_to_upload(
+            pathname, filter_image_only).sort(),
         expected_file_list)
 
   def test_get_files_to_upload_no_entries(self):
@@ -105,6 +106,56 @@
         upload_download_utils._get_files_to_upload(pathname, filter_image_only),
         [])
 
+  def test_verify_file_exists_no_file(self):
+    pathname = os.path.join(THIS_DIR, 'tmp')
+    self.fs.create_dir(pathname)
+    self.assertFalse(upload_download_utils.verify_file_exists(
+        pathname, filter_all))
+
+  def test_verify_file_exists_in_top_directory(self):
+    pathname = os.path.join(THIS_DIR, 'tmp')
+    self.fs.create_dir(pathname)
+    self.fs.create_file(os.path.join(pathname, 'file'))
+    self.assertTrue(upload_download_utils.verify_file_exists(
+        pathname, filter_all))
+
+  def test_verify_file_exists_not_matching_filter(self):
+    pathname = os.path.join(THIS_DIR, 'tmp')
+    self.fs.create_dir(pathname)
+    self.fs.create_file(os.path.join(pathname, 'file'))
+    self.assertFalse(upload_download_utils.verify_file_exists(
+        pathname, filter_image_only))
+
+  def test_verify_file_exists_deep_in_directory(self):
+    pathname = os.path.join(THIS_DIR, 'tmp')
+    self.fs.create_dir(pathname)
+    pathname_2 = os.path.join(pathname, 'deep')
+    self.fs.create_dir(pathname_2)
+
+    pathname_3 = os.path.join(pathname_2, 'deeper')
+    self.fs.create_dir(pathname_3)
+
+    pathname_4 = os.path.join(pathname_3, 'deeper')
+    self.fs.create_dir(pathname_4)
+    self.fs.create_file(os.path.join(pathname_4, 'file'))
+    self.assertTrue(upload_download_utils.verify_file_exists(
+        pathname, filter_all))
+
+  def test_verify_file_exists_deep_in_directory_not_match_filter(self):
+    pathname = os.path.join(THIS_DIR, 'tmp')
+    self.fs.create_dir(pathname)
+    pathname_2 = os.path.join(pathname, 'deep')
+    self.fs.create_dir(pathname_2)
+
+    pathname_3 = os.path.join(pathname_2, 'deeper')
+    self.fs.create_dir(pathname_3)
+
+    pathname_4 = os.path.join(pathname_3, 'deeper')
+    self.fs.create_dir(pathname_4)
+    self.fs.create_file(os.path.join(pathname_4, 'file'))
+    self.assertFalse(upload_download_utils.verify_file_exists(
+        pathname, filter_image_only))
+
   @mock.patch.object(multiprocessing, 'cpu_count')
   def test_get_thread_count_not_implemented(self, multiplecore_mock):
     multiplecore_mock.side_effect = NotImplementedError('abc')
diff --git a/chrome/test/data/extensions/api_test/autotest_private/test.js b/chrome/test/data/extensions/api_test/autotest_private/test.js
index 21982c7..d7b3a38 100644
--- a/chrome/test/data/extensions/api_test/autotest_private/test.js
+++ b/chrome/test/data/extensions/api_test/autotest_private/test.js
@@ -184,6 +184,11 @@
             });
         });
   },
+  function waitForSystemWebAppsInstall() {
+    chrome.autotestPrivate.waitForSystemWebAppsInstall(
+      chrome.test.callbackPass()
+    );
+  },
   // This launches and closes Chrome.
   function launchCloseApp() {
     chrome.autotestPrivate.launchApp('mgndgikekgjfcpckkfioiadnlibdjbkf',
@@ -1250,6 +1255,19 @@
       }));
 }];
 
+// Tests that requires a concrete system web app installation.
+var systemWebAppsTests = [
+  function getRegisteredSystemWebApps() {
+    chrome.autotestPrivate.getRegisteredSystemWebApps(
+      chrome.test.callbackPass(apps => {
+        chrome.test.assertEq(1, apps.length)
+        chrome.test.assertEq('OSSettings', apps[0].nameForLogging);
+        chrome.test.assertEq('chrome://test-system-app/', apps[0].url);
+      })
+    );
+  },
+]
+
 var test_suites = {
   'default': defaultTests,
   'arcEnabled': arcEnabledTests,
@@ -1261,6 +1279,7 @@
   'startStopTracing': startStopTracingTests,
   'scrollableShelf': scrollableShelfTests,
   'shelf': shelfTests,
+  'systemWebApps': systemWebAppsTests,
 };
 
 chrome.test.getConfig(function(config) {
diff --git a/chrome/test/data/extensions/api_test/file_browser/holding_space/manifest.json b/chrome/test/data/extensions/api_test/file_browser/holding_space/manifest.json
new file mode 100644
index 0000000..31bb8d1
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/file_browser/holding_space/manifest.json
@@ -0,0 +1,19 @@
+{
+  // chrome-extension://pkplfbidichfdicaijlchgnapepdginl
+  "key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtDfX9dHNh948bt00YhZBm3P6E5QLaOt+v8kXVtibQfiPtOD2FTScB/f0wX/EQWVO7BkaSOsRkTPcPIgocyMPYr2FLgqGLFlYT9nQpKJZUFNF5oJ5rG6Nv7ppf4zEB3j6da1IBRTz2yOZ+6O1TMZxol/V62/QcqrJeggsHTEPGLdr9Ua4b1Ka0xKJnJngZljsbw93FI1o+P9dAh5BS6wTPiZI/vmJVjvMTkSTnaZ3n9Go2t7A0XLcSxLcVyuLAd2mAvSN0mIviOukdM66wr7llif71nKuUt+4qvlr/r9HfwzN6pA4jkwhtS1UD+3CmB+wsHwsnohNcuu4FIQ6rgq/7QIDAQAB",
+  "name": "chrome.fileManagerPrivate tests",
+  "version": "0.1",
+  "manifest_version": 2,
+  "description": "Tests of chrome.fileManagerPrivate holding space methods",
+  "app": {
+    "background": {
+      "scripts": ["test.js"]
+    }
+  },
+  "permissions": [
+    "fileManagerPrivate",
+    {
+      "fileSystem": ["requestFileSystem"]
+    }
+  ]
+}
diff --git a/chrome/test/data/extensions/api_test/file_browser/holding_space/test.js b/chrome/test/data/extensions/api_test/file_browser/holding_space/test.js
new file mode 100644
index 0000000..50aa50e3b
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/file_browser/holding_space/test.js
@@ -0,0 +1,140 @@
+// 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.
+
+const callbackPass = chrome.test.callbackPass;
+
+/**
+ * Gets an external file entry from specified path.
+ * @param {string} volumeType volume type for entry.
+ * @param {string} path path of entry.
+ * @return {!Promise<Entry>} specified entry.
+ */
+function getFileEntry(volumeType, path) {
+  return new Promise(resolve => {
+    chrome.fileManagerPrivate.getVolumeMetadataList(list => {
+      const volume = list.find(v => v.volumeType === volumeType);
+      chrome.fileSystem.requestFileSystem({volumeId: volume.volumeId}, fs => {
+        fs.root.getFile(path, {}, entry => {
+          chrome.fileManagerPrivate.resolveIsolatedEntries(
+              [entry], externalEntries => {
+                resolve(externalEntries[0]);
+              });
+        });
+      });
+    });
+  });
+}
+
+/**
+ * Wrapper around <code>getFileEntry()</code> that resolves multiple paths.
+ * @param {string}  volumeType
+ * @param {Array<string>} paths
+ * @return {!Promise<Array<Entry>>}
+ */
+function getFileEntries(volumeType, paths) {
+  return Promise.all(paths.map(path => getFileEntry(volumeType, path)));
+}
+
+/**
+ * Converts test file entry indices in |testEntries| into their associated URL.
+ * @param {Array<number>} indices
+ * @return {Array<string>}
+ */
+function testItemIndicesToUrls(indices) {
+  return indices.map(index => testEntries[index]).map(entry => entry.toURL());
+}
+
+/**
+ * List of entries used in tests.
+ * @type {Array<Entry>}
+ */
+let testEntries = [];
+
+// Run the tests.
+chrome.test.runTests([
+  function testGetTestEntries() {
+    getFileEntries('testing', [
+      'test_dir/test_file.txt', 'test_audio.mp3', 'test_image.jpg'
+    ]).then(callbackPass(entries => {
+      testEntries = entries;
+    }));
+  },
+
+  function testEmptyHoldingSpace() {
+    chrome.fileManagerPrivate.getHoldingSpaceState(callbackPass(state => {
+      chrome.test.assertEq({itemUrls: []}, state);
+    }));
+  },
+
+  function testAddSingleEntry() {
+    chrome.fileManagerPrivate.toggleAddedToHoldingSpace(
+        [testEntries[0]], true, callbackPass(() => {
+          chrome.fileManagerPrivate.getHoldingSpaceState(callbackPass(state => {
+            const expectedUrls = testItemIndicesToUrls([0]);
+            chrome.test.assertEq({itemUrls: expectedUrls}, state);
+          }));
+        }));
+  },
+
+  function testAddTwoEntries() {
+    chrome.fileManagerPrivate.toggleAddedToHoldingSpace(
+        [testEntries[1], testEntries[2]], true, callbackPass(() => {
+          chrome.fileManagerPrivate.getHoldingSpaceState(callbackPass(state => {
+            const expectedUrls = testItemIndicesToUrls([0, 1, 2]);
+            chrome.test.assertEq({itemUrls: expectedUrls}, state);
+          }));
+        }));
+  },
+
+  function testRemoveTwoEntries() {
+    chrome.fileManagerPrivate.toggleAddedToHoldingSpace(
+        [testEntries[0], testEntries[2]], false, callbackPass(() => {
+          chrome.fileManagerPrivate.getHoldingSpaceState(callbackPass(state => {
+            const expectedUrls = testItemIndicesToUrls([1]);
+            chrome.test.assertEq({itemUrls: expectedUrls}, state);
+          }));
+        }));
+  },
+
+  function testAddPreviouslyAddedItem() {
+    chrome.fileManagerPrivate.toggleAddedToHoldingSpace(
+        [testEntries[0], testEntries[1]], true, callbackPass(() => {
+          chrome.fileManagerPrivate.getHoldingSpaceState(callbackPass(state => {
+            const expectedUrls = testItemIndicesToUrls([1, 0]);
+            chrome.test.assertEq({itemUrls: expectedUrls}, state);
+          }));
+        }));
+  },
+
+  function testRemoveSingleItem() {
+    chrome.fileManagerPrivate.toggleAddedToHoldingSpace(
+        [testEntries[1]], false, callbackPass(() => {
+          chrome.fileManagerPrivate.getHoldingSpaceState(callbackPass(state => {
+            const expectedUrls = testItemIndicesToUrls([0]);
+            chrome.test.assertEq({itemUrls: expectedUrls}, state);
+          }));
+        }));
+  },
+
+  function testRemoveAllItemsItem() {
+    chrome.fileManagerPrivate.toggleAddedToHoldingSpace(
+        [testEntries[0], testEntries[1], testEntries[2]], false,
+        callbackPass(() => {
+          chrome.fileManagerPrivate.getHoldingSpaceState(callbackPass(state => {
+            chrome.test.assertEq({itemUrls: []}, state);
+          }));
+        }));
+  },
+
+  function testAddAllItemsItem() {
+    chrome.fileManagerPrivate.toggleAddedToHoldingSpace(
+        [testEntries[0], testEntries[1], testEntries[2]], true,
+        callbackPass(() => {
+          chrome.fileManagerPrivate.getHoldingSpaceState(callbackPass(state => {
+            const expectedUrls = testItemIndicesToUrls([0, 1, 2]);
+            chrome.test.assertEq({itemUrls: expectedUrls}, state);
+          }));
+        }));
+  },
+]);
diff --git a/chrome/test/data/extensions/api_test/file_browser/holding_space_disabled/manifest.json b/chrome/test/data/extensions/api_test/file_browser/holding_space_disabled/manifest.json
new file mode 100644
index 0000000..31bb8d1
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/file_browser/holding_space_disabled/manifest.json
@@ -0,0 +1,19 @@
+{
+  // chrome-extension://pkplfbidichfdicaijlchgnapepdginl
+  "key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtDfX9dHNh948bt00YhZBm3P6E5QLaOt+v8kXVtibQfiPtOD2FTScB/f0wX/EQWVO7BkaSOsRkTPcPIgocyMPYr2FLgqGLFlYT9nQpKJZUFNF5oJ5rG6Nv7ppf4zEB3j6da1IBRTz2yOZ+6O1TMZxol/V62/QcqrJeggsHTEPGLdr9Ua4b1Ka0xKJnJngZljsbw93FI1o+P9dAh5BS6wTPiZI/vmJVjvMTkSTnaZ3n9Go2t7A0XLcSxLcVyuLAd2mAvSN0mIviOukdM66wr7llif71nKuUt+4qvlr/r9HfwzN6pA4jkwhtS1UD+3CmB+wsHwsnohNcuu4FIQ6rgq/7QIDAQAB",
+  "name": "chrome.fileManagerPrivate tests",
+  "version": "0.1",
+  "manifest_version": 2,
+  "description": "Tests of chrome.fileManagerPrivate holding space methods",
+  "app": {
+    "background": {
+      "scripts": ["test.js"]
+    }
+  },
+  "permissions": [
+    "fileManagerPrivate",
+    {
+      "fileSystem": ["requestFileSystem"]
+    }
+  ]
+}
diff --git a/chrome/test/data/extensions/api_test/file_browser/holding_space_disabled/test.js b/chrome/test/data/extensions/api_test/file_browser/holding_space_disabled/test.js
new file mode 100644
index 0000000..456461a
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/file_browser/holding_space_disabled/test.js
@@ -0,0 +1,41 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * Gets an external file entry from a path.
+ * @param {string} volumeType volume type for entry.
+ * @param {string} path path of entry.
+ * @return {!Promise<Entry>} specified entry.
+ */
+function getFileEntry(volumeType, path) {
+  return new Promise(resolve => {
+    chrome.fileManagerPrivate.getVolumeMetadataList(list => {
+      const volume = list.find(v => v.volumeType === volumeType);
+      chrome.fileSystem.requestFileSystem({volumeId: volume.volumeId}, fs => {
+        fs.root.getFile(path, {}, entry => {
+          chrome.fileManagerPrivate.resolveIsolatedEntries(
+              [entry], externalEntries => {
+                resolve(externalEntries[0]);
+              });
+        });
+      });
+    });
+  });
+}
+
+// Run the tests.
+chrome.test.runTests([
+  function testGetHoldingSpaceFails() {
+    chrome.fileManagerPrivate.getHoldingSpaceState(
+        chrome.test.callbackFail('Not enabled'));
+  },
+
+  function testAddSingleEntry() {
+    getFileEntry('testing', 'test_dir/test_file.txt')
+        .then(chrome.test.callbackPass(entry => {
+          chrome.fileManagerPrivate.toggleAddedToHoldingSpace(
+              [entry], true, chrome.test.callbackFail('Not enabled'));
+        }));
+  },
+]);
diff --git a/chrome/test/data/policy/policy_test_cases.json b/chrome/test/data/policy/policy_test_cases.json
index 160800ff..d5968281 100644
--- a/chrome/test/data/policy/policy_test_cases.json
+++ b/chrome/test/data/policy/policy_test_cases.json
@@ -766,7 +766,7 @@
   },
 
   "SafeBrowsingEnabled": {
-    "os": ["win", "linux", "mac", "chromeos"],
+    "os": ["win", "linux", "mac", "chromeos", "android"],
     "can_be_recommended": true,
     "policy_pref_mapping_test": [
       {
@@ -777,7 +777,7 @@
   },
 
   "SafeBrowsingProtectionLevel": {
-    "os": ["win", "linux", "mac", "chromeos"],
+    "os": ["win", "linux", "mac", "chromeos", "android"],
     "can_be_recommended": true,
     "policy_pref_mapping_test": [
       {
@@ -6167,7 +6167,7 @@
   },
 
   "SafeBrowsingExtendedReportingEnabled": {
-    "os": ["win", "linux", "mac", "chromeos"],
+    "os": ["win", "linux", "mac", "chromeos", "android"],
     "policy_pref_mapping_test": [
       {
         "policies": {
diff --git a/chrome/test/data/webui/settings/chromeos/os_languages_page_v2_tests.js b/chrome/test/data/webui/settings/chromeos/os_languages_page_v2_tests.js
index 67a81e9..93c83a7 100644
--- a/chrome/test/data/webui/settings/chromeos/os_languages_page_v2_tests.js
+++ b/chrome/test/data/webui/settings/chromeos/os_languages_page_v2_tests.js
@@ -212,14 +212,56 @@
     });
   });
 
-  suite('opens dialog', () => {
-    test('when clicking changeSystemLanguage', () => {
+  suite('change device language dialog', () => {
+    let dialog;
+    let dialogItems;
+    let cancelButton;
+    let actionButton;
+
+    setup(() => {
       assertFalse(
           !!languagesPage.$$('os-settings-change-device-language-dialog'));
       languagesPage.$$('#changeSystemLanguage').click();
       Polymer.dom.flush();
-      assertTrue(
-          !!languagesPage.$$('os-settings-change-device-language-dialog'));
+
+      dialog = languagesPage.$$('os-settings-change-device-language-dialog');
+      assertTrue(!!dialog);
+
+      actionButton = dialog.$$('.action-button');
+      assertTrue(!!actionButton);
+      cancelButton = dialog.$$('.cancel-button');
+      assertTrue(!!cancelButton);
+
+      // The fixed-height dialog's iron-list should stamp far fewer than
+      // 50 items.
+      dialogItems =
+          dialog.$.dialog.querySelectorAll('.list-item:not([hidden])');
+      assertGT(dialogItems.length, 1);
+      assertLT(dialogItems.length, 50);
+
+      // No language has been selected, so the action button is disabled.
+      assertTrue(actionButton.disabled);
+      assertFalse(cancelButton.disabled);
+    });
+
+    test('has action button working correctly', () => {
+      // selecting a language enables action button
+      dialogItems[0].click();
+      assertFalse(actionButton.disabled);
+
+      // selecting the same language again disables action button
+      dialogItems[0].click();
+      assertTrue(actionButton.disabled);
+    });
+
+    test('sets device language', async () => {
+      // selects a language
+      dialogItems[0].click();  // en-CA
+      assertFalse(actionButton.disabled);
+
+      actionButton.click();
+      assertEquals(
+          'en-CA', await browserProxy.whenCalled('setProspectiveUILanguage'));
     });
   });
 
diff --git a/chrome/test/data/webui/signin/signin_browsertest.js b/chrome/test/data/webui/signin/signin_browsertest.js
index 2d974ef..5c68933 100644
--- a/chrome/test/data/webui/signin/signin_browsertest.js
+++ b/chrome/test/data/webui/signin/signin_browsertest.js
@@ -90,6 +90,16 @@
   get browsePreload() {
     return 'chrome://profile-picker/test_loader.html?module=signin/profile_creation_flow_test.js';
   }
+
+  /** @override */
+  get featureList() {
+    return {
+      enabled: [
+        'features::kSignInProfileCreationFlow',
+        'features::kNewProfilePicker',
+      ]
+    };
+  }
 };
 
 TEST_F('ProfileCreationFlowTest', 'Buttons', function() {
@@ -110,7 +120,12 @@
 
   /** @override */
   get featureList() {
-    return {enabled: ['features::kSignInProfileCreationFlow']};
+    return {
+      enabled: [
+        'features::kSignInProfileCreationFlow',
+        'features::kNewProfilePicker',
+      ]
+    };
   }
 };
 
diff --git a/chrome/test/data/webui/signin/test_manage_profiles_browser_proxy.js b/chrome/test/data/webui/signin/test_manage_profiles_browser_proxy.js
index 5939713..e95f03e5 100644
--- a/chrome/test/data/webui/signin/test_manage_profiles_browser_proxy.js
+++ b/chrome/test/data/webui/signin/test_manage_profiles_browser_proxy.js
@@ -16,6 +16,7 @@
       'launchSelectedProfile',
       'askOnStartupChanged',
       'getNewProfileSuggestedThemeInfo',
+      'getProfileThemeInfo',
       'removeProfile',
       'getProfileStatistics',
       'loadSignInProfileCreationFlow',
@@ -62,6 +63,19 @@
   }
 
   /** @override */
+  getProfileThemeInfo(theme) {
+    this.methodCalled('getProfileThemeInfo');
+    return Promise.resolve({
+      colorId: theme.colorId,
+      color: theme.color || 0,
+      themeFrameColor: '',
+      themeShapeColor: '',
+      themeFrameTextColor: '',
+      themeGenericAvatar: ''
+    });
+  }
+
+  /** @override */
   removeProfile(profilePath) {
     this.methodCalled('removeProfile', profilePath);
   }
diff --git a/chromeos/components/camera_app_ui/camera_app_helper.mojom b/chromeos/components/camera_app_ui/camera_app_helper.mojom
index 2a8b1e7..5dd9ace0 100644
--- a/chromeos/components/camera_app_ui/camera_app_helper.mojom
+++ b/chromeos/components/camera_app_ui/camera_app_helper.mojom
@@ -79,4 +79,9 @@
   // state changes.
   SetExternalScreenMonitor(pending_remote<ExternalScreenMonitor> monitor)
       => (bool has_external_screen);
+
+  // Opens the file in Downloads folder by its |name| in gallery. It is only
+  // usable for SWA version. For platform app version, we use Chrome
+  // FileManagerPrivate API.
+  OpenFileInGallery(string name);
 };
diff --git a/chromeos/components/camera_app_ui/camera_app_helper_impl.cc b/chromeos/components/camera_app_ui/camera_app_helper_impl.cc
index 88f309c..35657cb 100644
--- a/chromeos/components/camera_app_ui/camera_app_helper_impl.cc
+++ b/chromeos/components/camera_app_ui/camera_app_helper_impl.cc
@@ -124,6 +124,11 @@
     external_screen_monitor_->Update(has_external_screen_);
 }
 
+void CameraAppHelperImpl::OpenFileInGallery(const std::string& name) {
+  DCHECK_NE(camera_app_ui_, nullptr);
+  camera_app_ui_->delegate()->OpenFileInGallery(name);
+}
+
 void CameraAppHelperImpl::OnTabletModeStarted() {
   if (tablet_monitor_.is_bound())
     tablet_monitor_->Update(true);
diff --git a/chromeos/components/camera_app_ui/camera_app_helper_impl.h b/chromeos/components/camera_app_ui/camera_app_helper_impl.h
index e87ce4b..0e03cd8 100644
--- a/chromeos/components/camera_app_ui/camera_app_helper_impl.h
+++ b/chromeos/components/camera_app_ui/camera_app_helper_impl.h
@@ -59,6 +59,7 @@
   void SetExternalScreenMonitor(
       mojo::PendingRemote<ExternalScreenMonitor> monitor,
       SetExternalScreenMonitorCallback callback) override;
+  void OpenFileInGallery(const std::string& name) override;
 
  private:
   void CheckExternalScreenState();
diff --git a/chromeos/components/camera_app_ui/camera_app_ui_delegate.h b/chromeos/components/camera_app_ui/camera_app_ui_delegate.h
index 68655a6..755ab3e5 100644
--- a/chromeos/components/camera_app_ui/camera_app_ui_delegate.h
+++ b/chromeos/components/camera_app_ui/camera_app_ui_delegate.h
@@ -5,6 +5,8 @@
 #ifndef CHROMEOS_COMPONENTS_CAMERA_APP_UI_CAMERA_APP_UI_DELEGATE_H_
 #define CHROMEOS_COMPONENTS_CAMERA_APP_UI_CAMERA_APP_UI_DELEGATE_H_
 
+#include <string>
+
 namespace content {
 class WebUIDataSource;
 }
@@ -25,6 +27,9 @@
   // TODO(crbug.com/1113567): Remove this method once we migrate to use UMA to
   // collect metrics. Checks if the logging consent option is enabled.
   virtual bool IsMetricsAndCrashReportingEnabled() = 0;
+
+  // Opens the file in Downloads folder by its |name| in gallery.
+  virtual void OpenFileInGallery(const std::string& name) = 0;
 };
 
 #endif  // CHROMEOS_COMPONENTS_CAMERA_APP_UI_CAMERA_APP_UI_DELEGATE_H_
diff --git a/chromeos/components/camera_app_ui/resources/src/js/browser_proxy/webui_browser_proxy.js b/chromeos/components/camera_app_ui/resources/src/js/browser_proxy/webui_browser_proxy.js
index b4a6e2f..59d256f9 100644
--- a/chromeos/components/camera_app_ui/resources/src/js/browser_proxy/webui_browser_proxy.js
+++ b/chromeos/components/camera_app_ui/resources/src/js/browser_proxy/webui_browser_proxy.js
@@ -115,7 +115,7 @@
 
   /** @override */
   async openGallery(file) {
-    throw new NotImplementedError();
+    ChromeHelper.getInstance().openFileInGallery(file.name);
   }
 
   /** @override */
diff --git a/chromeos/components/camera_app_ui/resources/src/js/mojo/chrome_helper.js b/chromeos/components/camera_app_ui/resources/src/js/mojo/chrome_helper.js
index f8b9b626..1fcb3de 100644
--- a/chromeos/components/camera_app_ui/resources/src/js/mojo/chrome_helper.js
+++ b/chromeos/components/camera_app_ui/resources/src/js/mojo/chrome_helper.js
@@ -102,6 +102,14 @@
   }
 
   /**
+   * Opens the file in Downloads folder by its |name| in gallery.
+   * @param {string} name Name of the target file.
+   */
+  openFileInGallery(name) {
+    this.remote_.openFileInGallery(name);
+  }
+
+  /**
    * Checks return value from |handleCameraResult|.
    * @param {string} caller Caller identifier.
    * @param {!Promise<{isSuccess: boolean}>|null} value
diff --git a/chromeos/components/camera_app_ui/url_constants.cc b/chromeos/components/camera_app_ui/url_constants.cc
index 96894c1d..b1883e29 100644
--- a/chromeos/components/camera_app_ui/url_constants.cc
+++ b/chromeos/components/camera_app_ui/url_constants.cc
@@ -7,6 +7,8 @@
 namespace chromeos {
 
 const char kChromeUICameraAppHost[] = "camera-app";
+const char kChromeUICameraAppMainURL[] =
+    "chrome://camera-app/src/views/main.html";
 const char kChromeUICameraAppURL[] = "chrome://camera-app/";
 
 }  // namespace chromeos
diff --git a/chromeos/components/camera_app_ui/url_constants.h b/chromeos/components/camera_app_ui/url_constants.h
index a39606f5..a7ad45a 100644
--- a/chromeos/components/camera_app_ui/url_constants.h
+++ b/chromeos/components/camera_app_ui/url_constants.h
@@ -8,6 +8,7 @@
 namespace chromeos {
 
 extern const char kChromeUICameraAppHost[];
+extern const char kChromeUICameraAppMainURL[];
 extern const char kChromeUICameraAppURL[];
 
 }  // namespace chromeos
diff --git a/chromeos/components/telemetry_extension_ui/test/untrusted_browsertest.js b/chromeos/components/telemetry_extension_ui/test/untrusted_browsertest.js
index bbf9232..a1ceb33 100644
--- a/chromeos/components/telemetry_extension_ui/test/untrusted_browsertest.js
+++ b/chromeos/components/telemetry_extension_ui/test/untrusted_browsertest.js
@@ -9,8 +9,7 @@
  * @type {!TrustedTypePolicy}
  */
 const workerUrlPolicy = trustedTypes.createPolicy(
-    'telemetry-extension-static',
-    {createScriptURL: () => 'worker.js'});
+    'telemetry-extension-static', {createScriptURL: () => 'worker.js'});
 
 // Tests that web workers can be spawned from
 // chrome-untrusted://telemetry_extension.
diff --git a/chromeos/crosapi/mojom/crosapi.mojom b/chromeos/crosapi/mojom/crosapi.mojom
index 10425f2..6a50bdd4 100644
--- a/chromeos/crosapi/mojom/crosapi.mojom
+++ b/chromeos/crosapi/mojom/crosapi.mojom
@@ -26,9 +26,19 @@
   BindSelectFile@0(pending_receiver<SelectFile> receiver);
 };
 
+// LacrosInitParams is a set of parameters for initialization of lacros-chrome,
+// which is passed from ash-chrome.
+struct LacrosInitParams {
+  // This is placeholder, so now it is empty.
+};
+
 // LacrosChromeService defines the APIs that live in lacros-chrome and
 // are accessed from ash-chrome.
 interface LacrosChromeService {
+  // Ash-chrome can pass initialize parameters via this method.
+  // The parameters are available on lacros-chrome startup.
+  Init@2(LacrosInitParams params);
+
   // Returns the pending_receiver of AshChromeService.
   // Ash-chrome is expected to call this method on initialization.
   // Lacros-chrome may call AshChromeService APIs earlier than this
diff --git a/chromeos/lacros/lacros_chrome_service_impl.cc b/chromeos/lacros/lacros_chrome_service_impl.cc
index b3e26a7..fd59c3f 100644
--- a/chromeos/lacros/lacros_chrome_service_impl.cc
+++ b/chromeos/lacros/lacros_chrome_service_impl.cc
@@ -30,8 +30,12 @@
  public:
   LacrosChromeServiceNeverBlockingState(
       scoped_refptr<base::SequencedTaskRunner> owner_sequence,
-      base::WeakPtr<LacrosChromeServiceImpl> owner)
-      : owner_sequence_(owner_sequence), owner_(owner) {
+      base::WeakPtr<LacrosChromeServiceImpl> owner,
+      crosapi::mojom::LacrosInitParamsPtr* init_params)
+      : owner_sequence_(owner_sequence),
+        owner_(owner),
+        init_params_(init_params) {
+    DCHECK(init_params);
     DETACH_FROM_SEQUENCE(sequence_checker_);
   }
   ~LacrosChromeServiceNeverBlockingState() override {
@@ -39,6 +43,11 @@
   }
 
   // crosapi::mojom::LacrosChromeService:
+  void Init(crosapi::mojom::LacrosInitParamsPtr params) override {
+    *init_params_ = std::move(params);
+    initialized_.Signal();
+  }
+
   void RequestAshChromeServiceReceiver(
       RequestAshChromeServiceReceiverCallback callback) override {
     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
@@ -55,6 +64,13 @@
         std::move(callback));
   }
 
+  // Unlike most of other methods of this class, this is called on the
+  // affined thread. Specifically, it is intended to be called before starting
+  // the message pumping of the affined thread to pass the initialization
+  // parameter from ash-chrome needed for the procedure running before the
+  // message pumping.
+  void WaitForInit() { initialized_.Wait(); }
+
   // AshChromeService is the interface that lacros-chrome uses to message
   // ash-chrome. This method binds the remote, which allows queuing of message
   // to ash-chrome. The messages will not go through until
@@ -123,6 +139,14 @@
   scoped_refptr<base::SequencedTaskRunner> owner_sequence_;
   base::WeakPtr<LacrosChromeServiceImpl> owner_;
 
+  // Owned by LacrosChromeServiceImpl.
+  crosapi::mojom::LacrosInitParamsPtr* const init_params_;
+
+  // Lock to wait for Init() invocation.
+  // Because the parameters are needed before starting the affined thread's
+  // message pumping, it is necessary to use sync primitive here, instead.
+  base::WaitableEvent initialized_;
+
   SEQUENCE_CHECKER(sequence_checker_);
 
   base::WeakPtrFactory<LacrosChromeServiceNeverBlockingState> weak_factory_{
@@ -148,8 +172,8 @@
 
   sequenced_state_ = std::unique_ptr<LacrosChromeServiceNeverBlockingState,
                                      base::OnTaskRunnerDeleter>(
-      new LacrosChromeServiceNeverBlockingState(affine_sequence,
-                                                weak_factory_.GetWeakPtr()),
+      new LacrosChromeServiceNeverBlockingState(
+          affine_sequence, weak_factory_.GetWeakPtr(), &init_params_),
       base::OnTaskRunnerDeleter(never_blocking_sequence_));
   weak_sequenced_state_ = sequenced_state_->GetWeakPtr();
 
@@ -204,6 +228,7 @@
       FROM_HERE, base::BindOnce(&LacrosChromeServiceNeverBlockingState::
                                     BindLacrosChromeServiceReceiver,
                                 weak_sequenced_state_, std::move(receiver)));
+  sequenced_state_->WaitForInit();
 }
 
 void LacrosChromeServiceImpl::BindScreenManagerReceiver(
diff --git a/chromeos/lacros/lacros_chrome_service_impl.h b/chromeos/lacros/lacros_chrome_service_impl.h
index 42d2f110..f791c1f 100644
--- a/chromeos/lacros/lacros_chrome_service_impl.h
+++ b/chromeos/lacros/lacros_chrome_service_impl.h
@@ -89,6 +89,10 @@
   void BindScreenManagerReceiver(
       mojo::PendingReceiver<crosapi::mojom::ScreenManager> pending_receiver);
 
+  const crosapi::mojom::LacrosInitParams* init_params() const {
+    return init_params_.get();
+  }
+
  private:
   // LacrosChromeServiceNeverBlockingState is an implementation detail of this
   // class.
@@ -101,6 +105,9 @@
   // affine sequence.
   std::unique_ptr<LacrosChromeServiceDelegate> delegate_;
 
+  // Parameters passed from ash-chrome.
+  crosapi::mojom::LacrosInitParamsPtr init_params_;
+
   // This member allows lacros-chrome to use the SelectFile interface. This
   // member is affine to the affine sequence. It is initialized in the
   // constructor and it is immediately available for use.
diff --git a/chromeos/services/device_sync/cryptauth_enroller.h b/chromeos/services/device_sync/cryptauth_enroller.h
index 2e3caef..533a236 100644
--- a/chromeos/services/device_sync/cryptauth_enroller.h
+++ b/chromeos/services/device_sync/cryptauth_enroller.h
@@ -28,12 +28,12 @@
   // |invocation_reason|: The reason why the enrollment occurred.
   // |callback|: Called will be called with true if the enrollment
   //     succeeds and false otherwise.
-  typedef base::Callback<void(bool)> EnrollmentFinishedCallback;
+  typedef base::OnceCallback<void(bool)> EnrollmentFinishedCallback;
   virtual void Enroll(const std::string& user_public_key,
                       const std::string& user_private_key,
                       const cryptauth::GcmDeviceInfo& device_info,
                       cryptauth::InvocationReason invocation_reason,
-                      const EnrollmentFinishedCallback& callback) = 0;
+                      EnrollmentFinishedCallback callback) = 0;
 };
 
 // Interface for creating CryptAuthEnroller instances.
diff --git a/chromeos/services/device_sync/cryptauth_enroller_impl.cc b/chromeos/services/device_sync/cryptauth_enroller_impl.cc
index 4ba7c61..38db1d1 100644
--- a/chromeos/services/device_sync/cryptauth_enroller_impl.cc
+++ b/chromeos/services/device_sync/cryptauth_enroller_impl.cc
@@ -73,10 +73,10 @@
     const std::string& user_private_key,
     const cryptauth::GcmDeviceInfo& device_info,
     cryptauth::InvocationReason invocation_reason,
-    const EnrollmentFinishedCallback& callback) {
-  if (!callback_.is_null()) {
+    EnrollmentFinishedCallback callback) {
+  if (enroll_called_) {
     PA_LOG(ERROR) << "Enroll() already called. Do not reuse.";
-    callback.Run(false);
+    std::move(callback).Run(false);
     return;
   }
 
@@ -84,16 +84,17 @@
   user_private_key_ = user_private_key;
   device_info_ = device_info;
   invocation_reason_ = invocation_reason;
-  callback_ = callback;
+  callback_ = std::move(callback);
+  enroll_called_ = true;
 
   if (!ValidateDeviceInfo(device_info)) {
-    callback.Run(false);
+    std::move(callback_).Run(false);
     return;
   }
 
   secure_message_delegate_->GenerateKeyPair(
-      base::Bind(&CryptAuthEnrollerImpl::OnKeyPairGenerated,
-                 weak_ptr_factory_.GetWeakPtr()));
+      base::BindOnce(&CryptAuthEnrollerImpl::OnKeyPairGenerated,
+                     weak_ptr_factory_.GetWeakPtr()));
 }
 
 void CryptAuthEnrollerImpl::OnKeyPairGenerated(const std::string& public_key,
@@ -109,10 +110,10 @@
   request.set_invocation_reason(invocation_reason_);
   cryptauth_client_->SetupEnrollment(
       request,
-      base::Bind(&CryptAuthEnrollerImpl::OnSetupEnrollmentSuccess,
-                 weak_ptr_factory_.GetWeakPtr()),
-      base::Bind(&CryptAuthEnrollerImpl::OnSetupEnrollmentFailure,
-                 weak_ptr_factory_.GetWeakPtr()));
+      base::BindOnce(&CryptAuthEnrollerImpl::OnSetupEnrollmentSuccess,
+                     weak_ptr_factory_.GetWeakPtr()),
+      base::BindOnce(&CryptAuthEnrollerImpl::OnSetupEnrollmentFailure,
+                     weak_ptr_factory_.GetWeakPtr()));
 }
 
 void CryptAuthEnrollerImpl::OnSetupEnrollmentSuccess(
@@ -120,13 +121,13 @@
   if (response.status() != kResponseStatusOk) {
     PA_LOG(WARNING) << "Unexpected status for SetupEnrollment: "
                     << response.status();
-    callback_.Run(false);
+    std::move(callback_).Run(false);
     return;
   }
 
   if (response.infos_size() == 0) {
     PA_LOG(ERROR) << "No response info returned by server for SetupEnrollment";
-    callback_.Run(false);
+    std::move(callback_).Run(false);
     return;
   }
 
@@ -137,14 +138,14 @@
 
   secure_message_delegate_->DeriveKey(
       session_private_key_, setup_info_.server_ephemeral_key(),
-      base::Bind(&CryptAuthEnrollerImpl::OnKeyDerived,
-                 weak_ptr_factory_.GetWeakPtr()));
+      base::BindOnce(&CryptAuthEnrollerImpl::OnKeyDerived,
+                     weak_ptr_factory_.GetWeakPtr()));
 }
 
 void CryptAuthEnrollerImpl::OnSetupEnrollmentFailure(
     NetworkRequestError error) {
   PA_LOG(WARNING) << "SetupEnrollment API failed with error: " << error;
-  callback_.Run(false);
+  std::move(callback_).Run(false);
 }
 
 void CryptAuthEnrollerImpl::OnKeyDerived(const std::string& symmetric_key) {
@@ -175,15 +176,15 @@
   // sent to CryptAuth.
   secure_message_delegate_->CreateSecureMessage(
       device_info_.SerializeAsString(), user_private_key_, options,
-      base::Bind(&CryptAuthEnrollerImpl::OnInnerSecureMessageCreated,
-                 weak_ptr_factory_.GetWeakPtr()));
+      base::BindOnce(&CryptAuthEnrollerImpl::OnInnerSecureMessageCreated,
+                     weak_ptr_factory_.GetWeakPtr()));
 }
 
 void CryptAuthEnrollerImpl::OnInnerSecureMessageCreated(
     const std::string& inner_message) {
   if (inner_message.empty()) {
     PA_LOG(ERROR) << "Error creating inner message";
-    callback_.Run(false);
+    std::move(callback_).Run(false);
     return;
   }
 
@@ -196,8 +197,8 @@
   // symmetric session key.
   secure_message_delegate_->CreateSecureMessage(
       inner_message, symmetric_key_, options,
-      base::Bind(&CryptAuthEnrollerImpl::OnOuterSecureMessageCreated,
-                 weak_ptr_factory_.GetWeakPtr()));
+      base::BindOnce(&CryptAuthEnrollerImpl::OnOuterSecureMessageCreated,
+                     weak_ptr_factory_.GetWeakPtr()));
 }
 
 void CryptAuthEnrollerImpl::OnOuterSecureMessageCreated(
@@ -213,10 +214,10 @@
   cryptauth_client_ = client_factory_->CreateInstance();
   cryptauth_client_->FinishEnrollment(
       request,
-      base::Bind(&CryptAuthEnrollerImpl::OnFinishEnrollmentSuccess,
-                 weak_ptr_factory_.GetWeakPtr()),
-      base::Bind(&CryptAuthEnrollerImpl::OnFinishEnrollmentFailure,
-                 weak_ptr_factory_.GetWeakPtr()));
+      base::BindOnce(&CryptAuthEnrollerImpl::OnFinishEnrollmentSuccess,
+                     weak_ptr_factory_.GetWeakPtr()),
+      base::BindOnce(&CryptAuthEnrollerImpl::OnFinishEnrollmentFailure,
+                     weak_ptr_factory_.GetWeakPtr()));
 }
 
 void CryptAuthEnrollerImpl::OnFinishEnrollmentSuccess(
@@ -229,14 +230,14 @@
   }
 
   RecordEnrollmentResult(success);
-  callback_.Run(success);
+  std::move(callback_).Run(success);
 }
 
 void CryptAuthEnrollerImpl::OnFinishEnrollmentFailure(
     NetworkRequestError error) {
   PA_LOG(WARNING) << "FinishEnrollment API failed with error: " << error;
   RecordEnrollmentResult(false /* success */);
-  callback_.Run(false);
+  std::move(callback_).Run(false);
 }
 
 }  // namespace device_sync
diff --git a/chromeos/services/device_sync/cryptauth_enroller_impl.h b/chromeos/services/device_sync/cryptauth_enroller_impl.h
index 6668afe..e5baa7f 100644
--- a/chromeos/services/device_sync/cryptauth_enroller_impl.h
+++ b/chromeos/services/device_sync/cryptauth_enroller_impl.h
@@ -47,7 +47,7 @@
               const std::string& user_private_key,
               const cryptauth::GcmDeviceInfo& device_info,
               cryptauth::InvocationReason invocation_reason,
-              const EnrollmentFinishedCallback& callback) override;
+              EnrollmentFinishedCallback callback) override;
 
  private:
   // Callbacks for SetupEnrollment.
@@ -96,6 +96,9 @@
   // Callback invoked when the enrollment is done.
   EnrollmentFinishedCallback callback_;
 
+  // True if Enroll() has been called.
+  bool enroll_called_ = false;
+
   // The derived ephemeral symmetric key.
   std::string symmetric_key_;
 
diff --git a/chromeos/services/device_sync/cryptauth_enroller_impl_unittest.cc b/chromeos/services/device_sync/cryptauth_enroller_impl_unittest.cc
index 3b50c42d..31e31271 100644
--- a/chromeos/services/device_sync/cryptauth_enroller_impl_unittest.cc
+++ b/chromeos/services/device_sync/cryptauth_enroller_impl_unittest.cc
@@ -114,8 +114,8 @@
 
     // This call is actually synchronous.
     secure_message_delegate_->GenerateKeyPair(
-        base::Bind(&DeviceSyncCryptAuthEnrollerTest::OnKeyPairGenerated,
-                   base::Unretained(this)));
+        base::BindOnce(&DeviceSyncCryptAuthEnrollerTest::OnKeyPairGenerated,
+                       base::Unretained(this)));
   }
 
   // Starts the enroller.
@@ -124,8 +124,8 @@
     enroller_result_.reset();
     enroller_.Enroll(
         user_public_key_, user_private_key_, device_info, kInvocationReason,
-        base::Bind(&DeviceSyncCryptAuthEnrollerTest::OnEnrollerCompleted,
-                   base::Unretained(this)));
+        base::BindOnce(&DeviceSyncCryptAuthEnrollerTest::OnEnrollerCompleted,
+                       base::Unretained(this)));
   }
 
   // Verifies that |serialized_message| is a valid SecureMessage sent with the
@@ -138,7 +138,7 @@
     std::string symmetric_key;
     secure_message_delegate_->DeriveKey(
         server_session_private_key, kClientSessionPublicKey,
-        base::Bind(&SaveDerivedKey, &symmetric_key));
+        base::BindOnce(&SaveDerivedKey, &symmetric_key));
 
     std::string inner_message;
     std::string inner_payload;
@@ -151,7 +151,8 @@
       unwrap_options.signature_scheme = securemessage::HMAC_SHA256;
       secure_message_delegate_->UnwrapSecureMessage(
           serialized_message, symmetric_key, unwrap_options,
-          base::Bind(&SaveUnwrapResults, &verified, &inner_message, &header));
+          base::BindOnce(&SaveUnwrapResults, &verified, &inner_message,
+                         &header));
       EXPECT_TRUE(verified);
 
       cryptauth::GcmMetadata metadata;
@@ -169,7 +170,8 @@
       unwrap_options.signature_scheme = securemessage::ECDSA_P256_SHA256;
       secure_message_delegate_->UnwrapSecureMessage(
           inner_message, user_public_key_, unwrap_options,
-          base::Bind(&SaveUnwrapResults, &verified, &inner_payload, &header));
+          base::BindOnce(&SaveUnwrapResults, &verified, &inner_payload,
+                         &header));
       EXPECT_TRUE(verified);
       EXPECT_EQ(user_public_key_, header.verification_key_id());
     }
diff --git a/chromeos/services/device_sync/cryptauth_enrollment_manager_impl_unittest.cc b/chromeos/services/device_sync/cryptauth_enrollment_manager_impl_unittest.cc
index 3d686413..606952d 100644
--- a/chromeos/services/device_sync/cryptauth_enrollment_manager_impl_unittest.cc
+++ b/chromeos/services/device_sync/cryptauth_enrollment_manager_impl_unittest.cc
@@ -10,6 +10,7 @@
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
 #include "base/memory/weak_ptr.h"
+#include "base/test/gmock_move_support.h"
 #include "base/test/simple_test_clock.h"
 #include "base/time/clock.h"
 #include "base/time/time.h"
@@ -26,7 +27,6 @@
 using ::testing::_;
 using ::testing::NiceMock;
 using ::testing::Return;
-using ::testing::SaveArg;
 
 namespace chromeos {
 
@@ -65,7 +65,7 @@
                     const std::string& user_private_key,
                     const cryptauth::GcmDeviceInfo& device_info,
                     cryptauth::InvocationReason invocation_reason,
-                    const EnrollmentFinishedCallback& callback));
+                    EnrollmentFinishedCallback callback));
 
  private:
   DISALLOW_COPY_AND_ASSIGN(MockCryptAuthEnroller);
@@ -211,7 +211,7 @@
     EXPECT_CALL(
         *next_cryptauth_enroller(),
         Enroll(public_key_, private_key_, _, expected_invocation_reason, _))
-        .WillOnce(SaveArg<4>(&completion_callback));
+        .WillOnce(MoveArg<4>(&completion_callback));
 
     auto sync_request = std::make_unique<SyncScheduler::SyncRequest>(
         enrollment_manager_.GetSyncScheduler());
@@ -354,7 +354,7 @@
 
   clock_.SetNow(base::Time::FromDoubleT(kLaterTimeNow));
   EXPECT_CALL(*this, OnEnrollmentFinishedProxy(true));
-  completion_callback.Run(true);
+  std::move(completion_callback).Run(true);
   EXPECT_EQ(clock_.Now(), enrollment_manager_.GetLastEnrollmentTime());
 }
 
@@ -370,7 +370,7 @@
       FireSchedulerForEnrollment(cryptauth::INVOCATION_REASON_PERIODIC);
   clock_.SetNow(base::Time::FromDoubleT(kLaterTimeNow));
   EXPECT_CALL(*this, OnEnrollmentFinishedProxy(false));
-  completion_callback.Run(false);
+  std::move(completion_callback).Run(false);
   EXPECT_EQ(old_enrollment_time, enrollment_manager_.GetLastEnrollmentTime());
   EXPECT_TRUE(pref_service_.GetBoolean(
       prefs::kCryptAuthEnrollmentIsRecoveringFromFailure));
@@ -382,7 +382,7 @@
       FireSchedulerForEnrollment(cryptauth::INVOCATION_REASON_FAILURE_RECOVERY);
   clock_.SetNow(base::Time::FromDoubleT(kLaterTimeNow + 30));
   EXPECT_CALL(*this, OnEnrollmentFinishedProxy(true));
-  completion_callback.Run(true);
+  std::move(completion_callback).Run(true);
   EXPECT_EQ(clock_.Now(), enrollment_manager_.GetLastEnrollmentTime());
   EXPECT_FALSE(pref_service_.GetBoolean(
       prefs::kCryptAuthEnrollmentIsRecoveringFromFailure));
@@ -412,7 +412,7 @@
   EXPECT_CALL(*next_cryptauth_enroller(),
               Enroll(public_key_, private_key_, _,
                      cryptauth::INVOCATION_REASON_INITIALIZATION, _))
-      .WillOnce(SaveArg<4>(&enrollment_callback));
+      .WillOnce(MoveArg<4>(&enrollment_callback));
   ASSERT_TRUE(gcm_manager_.registration_in_progress());
   gcm_manager_.CompleteRegistration(kGCMRegistrationId);
 
@@ -420,7 +420,7 @@
   ASSERT_FALSE(enrollment_callback.is_null());
   clock_.SetNow(base::Time::FromDoubleT(kLaterTimeNow));
   EXPECT_CALL(*this, OnEnrollmentFinishedProxy(true));
-  enrollment_callback.Run(true);
+  std::move(enrollment_callback).Run(true);
   EXPECT_EQ(clock_.Now(), enrollment_manager_.GetLastEnrollmentTime());
   EXPECT_TRUE(enrollment_manager_.IsEnrollmentValid());
 
@@ -458,7 +458,7 @@
       FireSchedulerForEnrollment(cryptauth::INVOCATION_REASON_SERVER_INITIATED);
 
   EXPECT_CALL(*this, OnEnrollmentFinishedProxy(true));
-  completion_callback.Run(true);
+  std::move(completion_callback).Run(true);
 }
 
 }  // namespace device_sync
diff --git a/chromeos/services/device_sync/device_sync_service_unittest.cc b/chromeos/services/device_sync/device_sync_service_unittest.cc
index dfa9c8f..eeac51e0 100644
--- a/chromeos/services/device_sync/device_sync_service_unittest.cc
+++ b/chromeos/services/device_sync/device_sync_service_unittest.cc
@@ -1905,7 +1905,7 @@
   EXPECT_FALSE(GetLastSetSoftwareFeatureStateResponseAndReset());
 
   // Now, invoke the success callback.
-  set_software_calls[0]->success_callback.Run();
+  std::move(set_software_calls[0]->success_callback).Run();
 
   // The callback still has not yet been invoked, since a device sync has not
   // confirmed the feature state change yet.
@@ -2017,7 +2017,8 @@
   EXPECT_FALSE(GetLastSetSoftwareFeatureStateResponseAndReset());
 
   // Now, invoke the error callback.
-  set_software_calls[0]->error_callback.Run(NetworkRequestError::kOffline);
+  std::move(set_software_calls[0]->error_callback)
+      .Run(NetworkRequestError::kOffline);
   base::RunLoop().RunUntilIdle();
   auto last_response = GetLastSetSoftwareFeatureStateResponseAndReset();
   EXPECT_TRUE(last_response);
@@ -2286,12 +2287,12 @@
 
   // Now, invoke the success callback, simultating that device 0 is eligible and
   // devices 1-4 are not.
-  find_eligible_calls[0]->success_callback.Run(
-      std::vector<cryptauth::ExternalDeviceInfo>(test_device_infos().begin(),
-                                                 test_device_infos().begin()),
-      std::vector<cryptauth::IneligibleDevice>(
-          test_ineligible_devices().begin() + 1,
-          test_ineligible_devices().end()));
+  std::move(find_eligible_calls[0]->success_callback)
+      .Run(std::vector<cryptauth::ExternalDeviceInfo>(
+               test_device_infos().begin(), test_device_infos().begin()),
+           std::vector<cryptauth::IneligibleDevice>(
+               test_ineligible_devices().begin() + 1,
+               test_ineligible_devices().end()));
   base::RunLoop().RunUntilIdle();
   auto last_response = GetLastFindEligibleDevicesResponseAndReset();
   EXPECT_TRUE(last_response);
@@ -2318,7 +2319,8 @@
   EXPECT_FALSE(GetLastFindEligibleDevicesResponseAndReset());
 
   // Now, invoke the error callback.
-  find_eligible_calls[1]->error_callback.Run(NetworkRequestError::kOffline);
+  std::move(find_eligible_calls[1]->error_callback)
+      .Run(NetworkRequestError::kOffline);
   base::RunLoop().RunUntilIdle();
   last_response = GetLastFindEligibleDevicesResponseAndReset();
   EXPECT_TRUE(last_response);
diff --git a/chromeos/services/device_sync/fake_software_feature_manager.cc b/chromeos/services/device_sync/fake_software_feature_manager.cc
index f6aff287..2aaed18 100644
--- a/chromeos/services/device_sync/fake_software_feature_manager.cc
+++ b/chromeos/services/device_sync/fake_software_feature_manager.cc
@@ -15,14 +15,14 @@
         const std::string& public_key,
         multidevice::SoftwareFeature software_feature,
         bool enabled,
-        const base::Closure& success_callback,
-        const base::Callback<void(NetworkRequestError)>& error_callback,
+        base::OnceClosure success_callback,
+        base::OnceCallback<void(NetworkRequestError)> error_callback,
         bool is_exclusive)
     : public_key(public_key),
       software_feature(software_feature),
       enabled(enabled),
-      success_callback(success_callback),
-      error_callback(error_callback),
+      success_callback(std::move(success_callback)),
+      error_callback(std::move(error_callback)),
       is_exclusive(is_exclusive) {}
 
 FakeSoftwareFeatureManager::SetSoftwareFeatureStateArgs::
@@ -45,13 +45,13 @@
 
 FakeSoftwareFeatureManager::FindEligibleDevicesArgs::FindEligibleDevicesArgs(
     multidevice::SoftwareFeature software_feature,
-    const base::Callback<void(const std::vector<cryptauth::ExternalDeviceInfo>&,
-                              const std::vector<cryptauth::IneligibleDevice>&)>&
+    base::OnceCallback<void(const std::vector<cryptauth::ExternalDeviceInfo>&,
+                            const std::vector<cryptauth::IneligibleDevice>&)>
         success_callback,
-    const base::Callback<void(NetworkRequestError)>& error_callback)
+    base::OnceCallback<void(NetworkRequestError)> error_callback)
     : software_feature(software_feature),
-      success_callback(success_callback),
-      error_callback(error_callback) {}
+      success_callback(std::move(success_callback)),
+      error_callback(std::move(error_callback)) {}
 
 FakeSoftwareFeatureManager::FindEligibleDevicesArgs::
     ~FindEligibleDevicesArgs() = default;
@@ -64,13 +64,13 @@
     const std::string& public_key,
     multidevice::SoftwareFeature software_feature,
     bool enabled,
-    const base::Closure& success_callback,
-    const base::Callback<void(NetworkRequestError)>& error_callback,
+    base::OnceClosure success_callback,
+    base::OnceCallback<void(NetworkRequestError)> error_callback,
     bool is_exclusive) {
   set_software_feature_state_calls_.emplace_back(
       std::make_unique<SetSoftwareFeatureStateArgs>(
-          public_key, software_feature, enabled, success_callback,
-          error_callback, is_exclusive));
+          public_key, software_feature, enabled, std::move(success_callback),
+          std::move(error_callback), is_exclusive));
 
   if (delegate_)
     delegate_->OnSetSoftwareFeatureStateCalled();
@@ -92,13 +92,14 @@
 
 void FakeSoftwareFeatureManager::FindEligibleDevices(
     multidevice::SoftwareFeature software_feature,
-    const base::Callback<void(const std::vector<cryptauth::ExternalDeviceInfo>&,
-                              const std::vector<cryptauth::IneligibleDevice>&)>&
+    base::OnceCallback<void(const std::vector<cryptauth::ExternalDeviceInfo>&,
+                            const std::vector<cryptauth::IneligibleDevice>&)>
         success_callback,
-    const base::Callback<void(NetworkRequestError)>& error_callback) {
+    base::OnceCallback<void(NetworkRequestError)> error_callback) {
   find_eligible_multidevice_host_calls_.emplace_back(
-      std::make_unique<FindEligibleDevicesArgs>(
-          software_feature, success_callback, error_callback));
+      std::make_unique<FindEligibleDevicesArgs>(software_feature,
+                                                std::move(success_callback),
+                                                std::move(error_callback)));
 
   if (delegate_)
     delegate_->OnFindEligibleDevicesCalled();
diff --git a/chromeos/services/device_sync/fake_software_feature_manager.h b/chromeos/services/device_sync/fake_software_feature_manager.h
index 6bc31e6..9542d620 100644
--- a/chromeos/services/device_sync/fake_software_feature_manager.h
+++ b/chromeos/services/device_sync/fake_software_feature_manager.h
@@ -35,16 +35,16 @@
         const std::string& public_key,
         multidevice::SoftwareFeature software_feature,
         bool enabled,
-        const base::Closure& success_callback,
-        const base::Callback<void(NetworkRequestError)>& error_callback,
+        base::OnceClosure success_callback,
+        base::OnceCallback<void(NetworkRequestError)> error_callback,
         bool is_exclusive);
     ~SetSoftwareFeatureStateArgs();
 
     std::string public_key;
     multidevice::SoftwareFeature software_feature;
     bool enabled;
-    base::Closure success_callback;
-    base::Callback<void(NetworkRequestError)> error_callback;
+    base::OnceClosure success_callback;
+    base::OnceCallback<void(NetworkRequestError)> error_callback;
     bool is_exclusive;
 
    private:
@@ -73,17 +73,17 @@
   struct FindEligibleDevicesArgs {
     FindEligibleDevicesArgs(
         multidevice::SoftwareFeature software_feature,
-        const base::Callback<void(
+        base::OnceCallback<void(
             const std::vector<cryptauth::ExternalDeviceInfo>&,
-            const std::vector<cryptauth::IneligibleDevice>&)>& success_callback,
-        const base::Callback<void(NetworkRequestError)>& error_callback);
+            const std::vector<cryptauth::IneligibleDevice>&)> success_callback,
+        base::OnceCallback<void(NetworkRequestError)> error_callback);
     ~FindEligibleDevicesArgs();
 
     multidevice::SoftwareFeature software_feature;
-    base::Callback<void(const std::vector<cryptauth::ExternalDeviceInfo>&,
-                        const std::vector<cryptauth::IneligibleDevice>&)>
+    base::OnceCallback<void(const std::vector<cryptauth::ExternalDeviceInfo>&,
+                            const std::vector<cryptauth::IneligibleDevice>&)>
         success_callback;
-    base::Callback<void(NetworkRequestError)> error_callback;
+    base::OnceCallback<void(NetworkRequestError)> error_callback;
 
    private:
     DISALLOW_COPY_AND_ASSIGN(FindEligibleDevicesArgs);
@@ -114,8 +114,8 @@
       const std::string& public_key,
       multidevice::SoftwareFeature software_feature,
       bool enabled,
-      const base::Closure& success_callback,
-      const base::Callback<void(NetworkRequestError)>& error_callback,
+      base::OnceClosure success_callback,
+      base::OnceCallback<void(NetworkRequestError)> error_callback,
       bool is_exclusive = false) override;
   void SetFeatureStatus(
       const std::string& device_id,
@@ -125,10 +125,10 @@
       base::OnceCallback<void(NetworkRequestError)> error_callback) override;
   void FindEligibleDevices(
       multidevice::SoftwareFeature software_feature,
-      const base::Callback<void(
-          const std::vector<cryptauth::ExternalDeviceInfo>&,
-          const std::vector<cryptauth::IneligibleDevice>&)>& success_callback,
-      const base::Callback<void(NetworkRequestError)>& error_callback) override;
+      base::OnceCallback<void(const std::vector<cryptauth::ExternalDeviceInfo>&,
+                              const std::vector<cryptauth::IneligibleDevice>&)>
+          success_callback,
+      base::OnceCallback<void(NetworkRequestError)> error_callback) override;
 
  private:
   Delegate* delegate_ = nullptr;
diff --git a/chromeos/services/device_sync/remote_device_loader.cc b/chromeos/services/device_sync/remote_device_loader.cc
index b44104d..ee10391 100644
--- a/chromeos/services/device_sync/remote_device_loader.cc
+++ b/chromeos/services/device_sync/remote_device_loader.cc
@@ -91,14 +91,14 @@
 
 RemoteDeviceLoader::~RemoteDeviceLoader() {}
 
-void RemoteDeviceLoader::Load(const RemoteDeviceCallback& callback) {
+void RemoteDeviceLoader::Load(RemoteDeviceCallback callback) {
   DCHECK(callback_.is_null());
-  callback_ = callback;
+  callback_ = std::move(callback);
   PA_LOG(VERBOSE) << "Loading " << remaining_devices_.size()
                   << " remote devices";
 
   if (remaining_devices_.empty()) {
-    callback_.Run(remote_devices_);
+    std::move(callback_).Run(remote_devices_);
     return;
   }
 
@@ -146,7 +146,7 @@
   if (remaining_devices_.empty()) {
     PA_LOG(VERBOSE) << "Derived keys for " << remote_devices_.size()
                     << " devices.";
-    callback_.Run(remote_devices_);
+    std::move(callback_).Run(remote_devices_);
   }
 }
 
diff --git a/chromeos/services/device_sync/remote_device_loader.h b/chromeos/services/device_sync/remote_device_loader.h
index 907ca50..49aa5554 100644
--- a/chromeos/services/device_sync/remote_device_loader.h
+++ b/chromeos/services/device_sync/remote_device_loader.h
@@ -66,9 +66,9 @@
   virtual ~RemoteDeviceLoader();
 
   // Loads the RemoteDevice objects. |callback| will be invoked upon completion.
-  typedef base::Callback<void(const multidevice::RemoteDeviceList&)>
+  typedef base::OnceCallback<void(const multidevice::RemoteDeviceList&)>
       RemoteDeviceCallback;
-  virtual void Load(const RemoteDeviceCallback& callback);
+  virtual void Load(RemoteDeviceCallback callback);
 
  private:
   // Called when the PSK is derived for each device. If the PSKs for all devices
diff --git a/chromeos/services/device_sync/remote_device_loader_unittest.cc b/chromeos/services/device_sync/remote_device_loader_unittest.cc
index 1184d28b..61d6efa2 100644
--- a/chromeos/services/device_sync/remote_device_loader_unittest.cc
+++ b/chromeos/services/device_sync/remote_device_loader_unittest.cc
@@ -95,8 +95,8 @@
 
   EXPECT_CALL(*this, LoadCompleted());
   loader.Load(
-      base::Bind(&DeviceSyncRemoteDeviceLoaderTest::OnRemoteDevicesLoaded,
-                 base::Unretained(this)));
+      base::BindOnce(&DeviceSyncRemoteDeviceLoaderTest::OnRemoteDevicesLoaded,
+                     base::Unretained(this)));
 
   EXPECT_EQ(0u, remote_devices_.size());
 }
@@ -109,8 +109,8 @@
 
   EXPECT_CALL(*this, LoadCompleted());
   loader.Load(
-      base::Bind(&DeviceSyncRemoteDeviceLoaderTest::OnRemoteDevicesLoaded,
-                 base::Unretained(this)));
+      base::BindOnce(&DeviceSyncRemoteDeviceLoaderTest::OnRemoteDevicesLoaded,
+                     base::Unretained(this)));
 
   EXPECT_EQ(1u, remote_devices_.size());
   EXPECT_FALSE(remote_devices_[0].persistent_symmetric_key.empty());
@@ -141,8 +141,8 @@
 
   EXPECT_CALL(*this, LoadCompleted());
   loader.Load(
-      base::Bind(&DeviceSyncRemoteDeviceLoaderTest::OnRemoteDevicesLoaded,
-                 base::Unretained(this)));
+      base::BindOnce(&DeviceSyncRemoteDeviceLoaderTest::OnRemoteDevicesLoaded,
+                     base::Unretained(this)));
 
   EXPECT_EQ(2u, remote_devices_.size());
 
@@ -174,8 +174,8 @@
 
   EXPECT_CALL(*this, LoadCompleted());
   loader.Load(
-      base::Bind(&DeviceSyncRemoteDeviceLoaderTest::OnRemoteDevicesLoaded,
-                 base::Unretained(this)));
+      base::BindOnce(&DeviceSyncRemoteDeviceLoaderTest::OnRemoteDevicesLoaded,
+                     base::Unretained(this)));
 
   EXPECT_EQ(1u, remote_devices_.size());
 
diff --git a/chromeos/services/device_sync/remote_device_provider_impl_unittest.cc b/chromeos/services/device_sync/remote_device_provider_impl_unittest.cc
index d3b60f5..0ff4cf6e 100644
--- a/chromeos/services/device_sync/remote_device_provider_impl_unittest.cc
+++ b/chromeos/services/device_sync/remote_device_provider_impl_unittest.cc
@@ -198,16 +198,15 @@
             devices.push_back(remote_device);
         }
       }
-      callback_.Run(devices);
-      callback_.Reset();
+      std::move(callback_).Run(devices);
     }
 
     // Fetch is only started if the change result passed to OnSyncFinished() is
     // CHANGED and sync is SUCCESS.
     bool HasQueuedCallback() { return !callback_.is_null(); }
 
-    void QueueCallback(const RemoteDeviceCallback& callback) {
-      callback_ = callback;
+    void QueueCallback(RemoteDeviceCallback callback) {
+      callback_ = std::move(callback);
     }
 
    private:
@@ -224,8 +223,8 @@
 
   TestRemoteDeviceLoaderFactory* remote_device_loader_factory_;
 
-  void Load(const RemoteDeviceCallback& callback) override {
-    remote_device_loader_factory_->QueueCallback(callback);
+  void Load(RemoteDeviceCallback callback) override {
+    remote_device_loader_factory_->QueueCallback(std::move(callback));
   }
 };
 
diff --git a/chromeos/services/device_sync/software_feature_manager.h b/chromeos/services/device_sync/software_feature_manager.h
index f3fa5c0..08f1146 100644
--- a/chromeos/services/device_sync/software_feature_manager.h
+++ b/chromeos/services/device_sync/software_feature_manager.h
@@ -34,8 +34,8 @@
       const std::string& public_key,
       multidevice::SoftwareFeature software_feature,
       bool enabled,
-      const base::Closure& success_callback,
-      const base::Callback<void(NetworkRequestError)>& error_callback,
+      base::OnceClosure success_callback,
+      base::OnceCallback<void(NetworkRequestError)> error_callback,
       bool is_exclusive = false) = 0;
 
   // Enables or disables |feature| for the device with Instance ID |device_id|.
@@ -55,10 +55,10 @@
   // |software_feature|.
   virtual void FindEligibleDevices(
       multidevice::SoftwareFeature software_feature,
-      const base::Callback<void(
-          const std::vector<cryptauth::ExternalDeviceInfo>&,
-          const std::vector<cryptauth::IneligibleDevice>&)>& success_callback,
-      const base::Callback<void(NetworkRequestError)>& error_callback) = 0;
+      base::OnceCallback<void(const std::vector<cryptauth::ExternalDeviceInfo>&,
+                              const std::vector<cryptauth::IneligibleDevice>&)>
+          success_callback,
+      base::OnceCallback<void(NetworkRequestError)> error_callback) = 0;
 };
 
 }  // namespace device_sync
diff --git a/chromeos/services/device_sync/software_feature_manager_impl.cc b/chromeos/services/device_sync/software_feature_manager_impl.cc
index 06cf5dd..7486a83 100644
--- a/chromeos/services/device_sync/software_feature_manager_impl.cc
+++ b/chromeos/services/device_sync/software_feature_manager_impl.cc
@@ -43,12 +43,12 @@
 
 SoftwareFeatureManagerImpl::Request::Request(
     std::unique_ptr<cryptauth::ToggleEasyUnlockRequest> toggle_request,
-    const base::Closure& set_software_success_callback,
-    const base::Callback<void(NetworkRequestError)> error_callback)
+    base::OnceClosure set_software_success_callback,
+    base::OnceCallback<void(NetworkRequestError)> error_callback)
     : request_type(RequestType::kSetSoftwareFeature),
-      error_callback(error_callback),
+      error_callback(std::move(error_callback)),
       toggle_request(std::move(toggle_request)),
-      set_software_success_callback(set_software_success_callback) {}
+      set_software_success_callback(std::move(set_software_success_callback)) {}
 
 SoftwareFeatureManagerImpl::Request::Request(
     const std::string& device_id,
@@ -68,14 +68,14 @@
 
 SoftwareFeatureManagerImpl::Request::Request(
     std::unique_ptr<cryptauth::FindEligibleUnlockDevicesRequest> find_request,
-    const base::Callback<void(const std::vector<cryptauth::ExternalDeviceInfo>&,
-                              const std::vector<cryptauth::IneligibleDevice>&)>
+    base::OnceCallback<void(const std::vector<cryptauth::ExternalDeviceInfo>&,
+                            const std::vector<cryptauth::IneligibleDevice>&)>
         find_hosts_success_callback,
-    const base::Callback<void(NetworkRequestError)> error_callback)
+    base::OnceCallback<void(NetworkRequestError)> error_callback)
     : request_type(RequestType::kFindEligibleMultideviceHosts),
-      error_callback(error_callback),
+      error_callback(std::move(error_callback)),
       find_request(std::move(find_request)),
-      find_hosts_success_callback(find_hosts_success_callback) {}
+      find_hosts_success_callback(std::move(find_hosts_success_callback)) {}
 
 SoftwareFeatureManagerImpl::Request::~Request() = default;
 
@@ -91,8 +91,8 @@
     const std::string& public_key,
     multidevice::SoftwareFeature software_feature,
     bool enabled,
-    const base::Closure& success_callback,
-    const base::Callback<void(NetworkRequestError)>& error_callback,
+    base::OnceClosure success_callback,
+    base::OnceCallback<void(NetworkRequestError)> error_callback,
     bool is_exclusive) {
   // Note: For legacy reasons, this proto message mentions "ToggleEasyUnlock"
   // instead of "SetSoftwareFeature" in its name.
@@ -111,8 +111,9 @@
   if (!turn_off_easy_unlock_special_case)
     request->set_public_key(public_key);
 
-  pending_requests_.emplace(std::make_unique<Request>(
-      std::move(request), success_callback, error_callback));
+  pending_requests_.emplace(
+      std::make_unique<Request>(std::move(request), std::move(success_callback),
+                                std::move(error_callback)));
   ProcessRequestQueue();
 }
 
@@ -130,10 +131,10 @@
 
 void SoftwareFeatureManagerImpl::FindEligibleDevices(
     multidevice::SoftwareFeature software_feature,
-    const base::Callback<void(const std::vector<cryptauth::ExternalDeviceInfo>&,
-                              const std::vector<cryptauth::IneligibleDevice>&)>&
+    base::OnceCallback<void(const std::vector<cryptauth::ExternalDeviceInfo>&,
+                            const std::vector<cryptauth::IneligibleDevice>&)>
         success_callback,
-    const base::Callback<void(NetworkRequestError)>& error_callback) {
+    base::OnceCallback<void(NetworkRequestError)> error_callback) {
   // Note: For legacy reasons, this proto message mentions "UnlockDevices"
   // instead of "MultiDeviceHosts" in its name.
   auto request =
@@ -147,8 +148,9 @@
   request->set_callback_bluetooth_address(SoftwareFeatureEnumToStringAllCaps(
       multidevice::ToCryptAuthFeature(software_feature)));
 
-  pending_requests_.emplace(std::make_unique<Request>(
-      std::move(request), success_callback, error_callback));
+  pending_requests_.emplace(
+      std::make_unique<Request>(std::move(request), std::move(success_callback),
+                                std::move(error_callback)));
   ProcessRequestQueue();
 }
 
@@ -178,10 +180,10 @@
 
   current_cryptauth_client_->ToggleEasyUnlock(
       *current_request_->toggle_request,
-      base::Bind(&SoftwareFeatureManagerImpl::OnToggleEasyUnlockResponse,
-                 weak_ptr_factory_.GetWeakPtr()),
-      base::Bind(&SoftwareFeatureManagerImpl::OnErrorResponse,
-                 weak_ptr_factory_.GetWeakPtr()));
+      base::BindOnce(&SoftwareFeatureManagerImpl::OnToggleEasyUnlockResponse,
+                     weak_ptr_factory_.GetWeakPtr()),
+      base::BindOnce(&SoftwareFeatureManagerImpl::OnErrorResponse,
+                     weak_ptr_factory_.GetWeakPtr()));
 }
 
 void SoftwareFeatureManagerImpl::ProcessSetFeatureStatusRequest() {
@@ -202,17 +204,17 @@
 
   current_cryptauth_client_->FindEligibleUnlockDevices(
       *current_request_->find_request,
-      base::Bind(
+      base::BindOnce(
           &SoftwareFeatureManagerImpl::OnFindEligibleUnlockDevicesResponse,
           weak_ptr_factory_.GetWeakPtr()),
-      base::Bind(&SoftwareFeatureManagerImpl::OnErrorResponse,
-                 weak_ptr_factory_.GetWeakPtr()));
+      base::BindOnce(&SoftwareFeatureManagerImpl::OnErrorResponse,
+                     weak_ptr_factory_.GetWeakPtr()));
 }
 
 void SoftwareFeatureManagerImpl::OnToggleEasyUnlockResponse(
     const cryptauth::ToggleEasyUnlockResponse& response) {
   current_cryptauth_client_.reset();
-  current_request_->set_software_success_callback.Run();
+  std::move(current_request_->set_software_success_callback).Run();
   current_request_.reset();
   ProcessRequestQueue();
 }
@@ -233,20 +235,20 @@
 void SoftwareFeatureManagerImpl::OnFindEligibleUnlockDevicesResponse(
     const cryptauth::FindEligibleUnlockDevicesResponse& response) {
   current_cryptauth_client_.reset();
-  current_request_->find_hosts_success_callback.Run(
-      std::vector<cryptauth::ExternalDeviceInfo>(
-          response.eligible_devices().begin(),
-          response.eligible_devices().end()),
-      std::vector<cryptauth::IneligibleDevice>(
-          response.ineligible_devices().begin(),
-          response.ineligible_devices().end()));
+  std::move(current_request_->find_hosts_success_callback)
+      .Run(std::vector<cryptauth::ExternalDeviceInfo>(
+               response.eligible_devices().begin(),
+               response.eligible_devices().end()),
+           std::vector<cryptauth::IneligibleDevice>(
+               response.ineligible_devices().begin(),
+               response.ineligible_devices().end()));
   current_request_.reset();
   ProcessRequestQueue();
 }
 
 void SoftwareFeatureManagerImpl::OnErrorResponse(NetworkRequestError error) {
   current_cryptauth_client_.reset();
-  current_request_->error_callback.Run(error);
+  std::move(current_request_->error_callback).Run(error);
   current_request_.reset();
   ProcessRequestQueue();
 }
diff --git a/chromeos/services/device_sync/software_feature_manager_impl.h b/chromeos/services/device_sync/software_feature_manager_impl.h
index 2b02c5e..c290f7c 100644
--- a/chromeos/services/device_sync/software_feature_manager_impl.h
+++ b/chromeos/services/device_sync/software_feature_manager_impl.h
@@ -57,8 +57,8 @@
       const std::string& public_key,
       multidevice::SoftwareFeature software_feature,
       bool enabled,
-      const base::Closure& success_callback,
-      const base::Callback<void(NetworkRequestError)>& error_callback,
+      base::OnceClosure success_callback,
+      base::OnceCallback<void(NetworkRequestError)> error_callback,
       bool is_exclusive = false) override;
   void SetFeatureStatus(
       const std::string& device_id,
@@ -68,10 +68,10 @@
       base::OnceCallback<void(NetworkRequestError)> error_callback) override;
   void FindEligibleDevices(
       multidevice::SoftwareFeature software_feature,
-      const base::Callback<void(
-          const std::vector<cryptauth::ExternalDeviceInfo>&,
-          const std::vector<cryptauth::IneligibleDevice>&)>& success_callback,
-      const base::Callback<void(NetworkRequestError)>& error_callback) override;
+      base::OnceCallback<void(const std::vector<cryptauth::ExternalDeviceInfo>&,
+                              const std::vector<cryptauth::IneligibleDevice>&)>
+          success_callback,
+      base::OnceCallback<void(NetworkRequestError)> error_callback) override;
 
  private:
   enum class RequestType {
@@ -83,8 +83,8 @@
   struct Request {
     // Used for kSetSoftwareFeature Requests.
     Request(std::unique_ptr<cryptauth::ToggleEasyUnlockRequest> toggle_request,
-            const base::Closure& set_software_success_callback,
-            const base::Callback<void(NetworkRequestError)> error_callback);
+            base::OnceClosure set_software_success_callback,
+            base::OnceCallback<void(NetworkRequestError)> error_callback);
 
     // Used for kSetFeatureStatus Requests.
     Request(const std::string& device_id,
@@ -97,11 +97,11 @@
     // Used for kFindEligibleMultideviceHosts Requests.
     Request(std::unique_ptr<cryptauth::FindEligibleUnlockDevicesRequest>
                 find_request,
-            const base::Callback<
+            base::OnceCallback<
                 void(const std::vector<cryptauth::ExternalDeviceInfo>&,
                      const std::vector<cryptauth::IneligibleDevice>&)>
                 find_hosts_success_callback,
-            const base::Callback<void(NetworkRequestError)> error_callback);
+            base::OnceCallback<void(NetworkRequestError)> error_callback);
 
     ~Request();
 
@@ -109,11 +109,11 @@
 
     // Set for kSetSoftwareFeature and kFindEligibleMultideviceHosts; unset
     // otherwise.
-    const base::Callback<void(NetworkRequestError)> error_callback;
+    base::OnceCallback<void(NetworkRequestError)> error_callback;
 
     // Set for kSetSoftwareFeature; unset otherwise.
     std::unique_ptr<cryptauth::ToggleEasyUnlockRequest> toggle_request;
-    const base::Closure set_software_success_callback;
+    base::OnceClosure set_software_success_callback;
 
     // Set for kSetFeatureStatus; unset otherwise.
     std::string device_id;
@@ -125,8 +125,8 @@
 
     // Set for kFindEligibleMultideviceHosts; unset otherwise.
     std::unique_ptr<cryptauth::FindEligibleUnlockDevicesRequest> find_request;
-    const base::Callback<void(const std::vector<cryptauth::ExternalDeviceInfo>&,
-                              const std::vector<cryptauth::IneligibleDevice>&)>
+    base::OnceCallback<void(const std::vector<cryptauth::ExternalDeviceInfo>&,
+                            const std::vector<cryptauth::IneligibleDevice>&)>
         find_hosts_success_callback;
   };
 
diff --git a/chromeos/services/device_sync/software_feature_manager_impl_unittest.cc b/chromeos/services/device_sync/software_feature_manager_impl_unittest.cc
index 6587bcf0..68c849f7 100644
--- a/chromeos/services/device_sync/software_feature_manager_impl_unittest.cc
+++ b/chromeos/services/device_sync/software_feature_manager_impl_unittest.cc
@@ -188,11 +188,11 @@
                                bool is_exclusive = false) {
     software_feature_manager_->SetSoftwareFeatureState(
         device_info.public_key(), feature, enabled,
-        base::Bind(&DeviceSyncSoftwareFeatureManagerImplTest::
-                       OnSoftwareFeatureStateSet,
-                   base::Unretained(this)),
-        base::Bind(&DeviceSyncSoftwareFeatureManagerImplTest::OnError,
-                   base::Unretained(this)),
+        base::BindOnce(&DeviceSyncSoftwareFeatureManagerImplTest::
+                           OnSoftwareFeatureStateSet,
+                       base::Unretained(this)),
+        base::BindOnce(&DeviceSyncSoftwareFeatureManagerImplTest::OnError,
+                       base::Unretained(this)),
         is_exclusive);
   }
 
@@ -211,11 +211,11 @@
   void FindEligibleDevices(multidevice::SoftwareFeature feature) {
     software_feature_manager_->FindEligibleDevices(
         feature,
-        base::Bind(
+        base::BindOnce(
             &DeviceSyncSoftwareFeatureManagerImplTest::OnEligibleDevicesFound,
             base::Unretained(this)),
-        base::Bind(&DeviceSyncSoftwareFeatureManagerImplTest::OnError,
-                   base::Unretained(this)));
+        base::BindOnce(&DeviceSyncSoftwareFeatureManagerImplTest::OnError,
+                       base::Unretained(this)));
   }
 
   void VerifyLastSetFeatureStatusRequest(
diff --git a/chromeos/services/secure_channel/ble_advertiser_impl.cc b/chromeos/services/secure_channel/ble_advertiser_impl.cc
index f139f672..504eb0f 100644
--- a/chromeos/services/secure_channel/ble_advertiser_impl.cc
+++ b/chromeos/services/secure_channel/ble_advertiser_impl.cc
@@ -365,8 +365,8 @@
     return;
 
   active_advertisement->Stop(
-      base::Bind(&BleAdvertiserImpl::OnActiveAdvertisementStopped,
-                 base::Unretained(this), index));
+      base::BindOnce(&BleAdvertiserImpl::OnActiveAdvertisementStopped,
+                     base::Unretained(this), index));
 }
 
 void BleAdvertiserImpl::OnActiveAdvertisementStopped(size_t index) {
diff --git a/chromeos/services/secure_channel/ble_characteristics_finder.cc b/chromeos/services/secure_channel/ble_characteristics_finder.cc
index d9bf874..ab5eadb 100644
--- a/chromeos/services/secure_channel/ble_characteristics_finder.cc
+++ b/chromeos/services/secure_channel/ble_characteristics_finder.cc
@@ -35,8 +35,8 @@
         const RemoteAttribute& remote_service,
         const RemoteAttribute& to_peripheral_char,
         const RemoteAttribute& from_peripheral_char,
-        const SuccessCallback& success_callback,
-        const ErrorCallback& error_callback,
+        SuccessCallback success_callback,
+        base::OnceClosure error_callback,
         const multidevice::RemoteDeviceRef& remote_device,
         std::unique_ptr<BackgroundEidGenerator> background_eid_generator)
     : adapter_(adapter),
@@ -44,8 +44,8 @@
       remote_service_(remote_service),
       to_peripheral_char_(to_peripheral_char),
       from_peripheral_char_(from_peripheral_char),
-      success_callback_(success_callback),
-      error_callback_(error_callback),
+      success_callback_(std::move(success_callback)),
+      error_callback_(std::move(error_callback)),
       remote_device_(remote_device),
       background_eid_generator_(std::move(background_eid_generator)) {
   adapter_->AddObserver(this);
@@ -143,8 +143,8 @@
   from_peripheral_char_.id = rx_id;
   to_peripheral_char_.id = tx_id;
   remote_service_.id = service_id;
-  success_callback_.Run(remote_service_, to_peripheral_char_,
-                        from_peripheral_char_);
+  std::move(success_callback_)
+      .Run(remote_service_, to_peripheral_char_, from_peripheral_char_);
 }
 
 void BluetoothLowEnergyCharacteristicsFinder::
@@ -153,7 +153,7 @@
     return;
   DCHECK(!has_callback_been_invoked_);
   has_callback_been_invoked_ = true;
-  error_callback_.Run();
+  std::move(error_callback_).Run();
 }
 
 void BluetoothLowEnergyCharacteristicsFinder::TryToVerifyEid(
diff --git a/chromeos/services/secure_channel/ble_characteristics_finder.h b/chromeos/services/secure_channel/ble_characteristics_finder.h
index 4ecc024..23c0f91 100644
--- a/chromeos/services/secure_channel/ble_characteristics_finder.h
+++ b/chromeos/services/secure_channel/ble_characteristics_finder.h
@@ -37,15 +37,11 @@
   // |to_peripheral_char_| and |from_peripheral_char_|. Note that, since this is
   // called after the characteristics were discovered, their id field (e.g.
   // to_peripheral_char_.id) will be non-blank.
-  typedef base::Callback<void(const RemoteAttribute&,
-                              const RemoteAttribute&,
-                              const RemoteAttribute&)>
+  typedef base::OnceCallback<void(const RemoteAttribute&,
+                                  const RemoteAttribute&,
+                                  const RemoteAttribute&)>
       SuccessCallback;
 
-  // Error callback indicating that no valid GATT service with all required
-  // characteristic was found on the |device_|.
-  typedef base::Callback<void()> ErrorCallback;
-
   // Constructs the object and registers itself as an observer for |adapter|,
   // waiting for |to_peripheral_char| and |from_peripheral_char| to be found.
   // When both characteristics were found |success_callback| is called. After
@@ -58,8 +54,8 @@
       const RemoteAttribute& remote_service,
       const RemoteAttribute& to_peripheral_char,
       const RemoteAttribute& from_peripheral_char,
-      const SuccessCallback& success_callback,
-      const ErrorCallback& error_callback,
+      SuccessCallback success_callback,
+      base::OnceClosure error_callback,
       const multidevice::RemoteDeviceRef& remote_device,
       std::unique_ptr<BackgroundEidGenerator> background_eid_generator);
 
@@ -126,7 +122,7 @@
   bool have_services_been_parsed_ = false;
 
   // Called when there is an error.
-  ErrorCallback error_callback_;
+  base::OnceClosure error_callback_;
 
   const multidevice::RemoteDeviceRef remote_device_;
 
diff --git a/chromeos/services/secure_channel/ble_characteristics_finder_unittest.cc b/chromeos/services/secure_channel/ble_characteristics_finder_unittest.cc
index c3179e715..cfbf78b 100644
--- a/chromeos/services/secure_channel/ble_characteristics_finder_unittest.cc
+++ b/chromeos/services/secure_channel/ble_characteristics_finder_unittest.cc
@@ -83,17 +83,16 @@
 
 class SecureChannelBluetoothLowEnergyCharacteristicFinderTest
     : public testing::Test {
+ public:
+  MOCK_METHOD3(OnCharacteristicsFound,
+               void(const RemoteAttribute&,
+                    const RemoteAttribute&,
+                    const RemoteAttribute&));
+  MOCK_METHOD0(OnCharacteristicsFinderError, void());
+
  protected:
   SecureChannelBluetoothLowEnergyCharacteristicFinderTest()
       : adapter_(new NiceMock<device::MockBluetoothAdapter>),
-        success_callback_(base::Bind(
-            &SecureChannelBluetoothLowEnergyCharacteristicFinderTest::
-                OnCharacteristicsFound,
-            base::Unretained(this))),
-        error_callback_(base::Bind(
-            &SecureChannelBluetoothLowEnergyCharacteristicFinderTest::
-                OnCharacteristicsFinderError,
-            base::Unretained(this))),
         device_(
             new NiceMock<device::MockBluetoothDevice>(adapter_.get(),
                                                       0,
@@ -121,16 +120,18 @@
     characteristic_finder_ =
         std::make_unique<BluetoothLowEnergyCharacteristicsFinder>(
             adapter_, device_.get(), remote_service_, to_peripheral_char_,
-            from_peripheral_char_, success_callback_, error_callback_,
+            from_peripheral_char_,
+            base::BindOnce(
+                &SecureChannelBluetoothLowEnergyCharacteristicFinderTest::
+                    OnCharacteristicsFound,
+                base::Unretained(this)),
+            base::BindOnce(
+                &SecureChannelBluetoothLowEnergyCharacteristicFinderTest::
+                    OnCharacteristicsFinderError,
+                base::Unretained(this)),
             remote_device_, CreateBackgroundEidGenerator());
   }
 
-  MOCK_METHOD3(OnCharacteristicsFound,
-               void(const RemoteAttribute&,
-                    const RemoteAttribute&,
-                    const RemoteAttribute&));
-  MOCK_METHOD0(OnCharacteristicsFinderError, void());
-
   std::unique_ptr<device::MockBluetoothGattCharacteristic>
   ExpectToFindCharacteristic(const device::BluetoothUUID& uuid,
                              const std::string& id,
@@ -262,8 +263,6 @@
       characteristic_finder_;
   base::test::TaskEnvironment task_environment_;
   scoped_refptr<device::MockBluetoothAdapter> adapter_;
-  BluetoothLowEnergyCharacteristicsFinder::SuccessCallback success_callback_;
-  BluetoothLowEnergyCharacteristicsFinder::ErrorCallback error_callback_;
   std::unique_ptr<device::MockBluetoothDevice> device_;
   std::vector<std::unique_ptr<device::BluetoothRemoteGattService>> services_;
   std::vector<std::unique_ptr<device::MockBluetoothGattCharacteristic>>
@@ -279,8 +278,14 @@
        ConstructAndDestroyDontCrash) {
   std::make_unique<BluetoothLowEnergyCharacteristicsFinder>(
       adapter_, device_.get(), remote_service_, to_peripheral_char_,
-      from_peripheral_char_, success_callback_, error_callback_, remote_device_,
-      CreateBackgroundEidGenerator());
+      from_peripheral_char_,
+      base::BindOnce(&SecureChannelBluetoothLowEnergyCharacteristicFinderTest::
+                         OnCharacteristicsFound,
+                     base::Unretained(this)),
+      base::BindOnce(&SecureChannelBluetoothLowEnergyCharacteristicFinderTest::
+                         OnCharacteristicsFinderError,
+                     base::Unretained(this)),
+      remote_device_, CreateBackgroundEidGenerator());
 }
 
 TEST_F(SecureChannelBluetoothLowEnergyCharacteristicFinderTest,
@@ -309,8 +314,14 @@
           /* connected */ false, /* paired */ false));
   BluetoothLowEnergyCharacteristicsFinder characteristic_finder(
       adapter_, device.get(), remote_service_, to_peripheral_char_,
-      from_peripheral_char_, success_callback_, error_callback_, remote_device_,
-      CreateBackgroundEidGenerator());
+      from_peripheral_char_,
+      base::BindOnce(&SecureChannelBluetoothLowEnergyCharacteristicFinderTest::
+                         OnCharacteristicsFound,
+                     base::Unretained(this)),
+      base::BindOnce(&SecureChannelBluetoothLowEnergyCharacteristicFinderTest::
+                         OnCharacteristicsFinderError,
+                     base::Unretained(this)),
+      remote_device_, CreateBackgroundEidGenerator());
   device::BluetoothAdapter::Observer* observer =
       static_cast<device::BluetoothAdapter::Observer*>(&characteristic_finder);
 
@@ -426,8 +437,14 @@
 
   std::make_unique<BluetoothLowEnergyCharacteristicsFinder>(
       adapter_, device_.get(), remote_service_, to_peripheral_char_,
-      from_peripheral_char_, success_callback_, error_callback_, remote_device_,
-      CreateBackgroundEidGenerator());
+      from_peripheral_char_,
+      base::BindOnce(&SecureChannelBluetoothLowEnergyCharacteristicFinderTest::
+                         OnCharacteristicsFound,
+                     base::Unretained(this)),
+      base::BindOnce(&SecureChannelBluetoothLowEnergyCharacteristicFinderTest::
+                         OnCharacteristicsFinderError,
+                     base::Unretained(this)),
+      remote_device_, CreateBackgroundEidGenerator());
 
   EXPECT_EQ(kToPeripheralCharID, found_to_char.id);
   EXPECT_EQ(kFromPeripheralCharID, found_from_char.id);
diff --git a/chromeos/services/secure_channel/ble_scanner_impl.cc b/chromeos/services/secure_channel/ble_scanner_impl.cc
index fd2345d..d19cb75 100644
--- a/chromeos/services/secure_channel/ble_scanner_impl.cc
+++ b/chromeos/services/secure_channel/ble_scanner_impl.cc
@@ -134,8 +134,8 @@
   ble_synchronizer_->StartDiscoverySession(
       base::BindOnce(&BleScannerImpl::OnDiscoverySessionStarted,
                      weak_ptr_factory_.GetWeakPtr()),
-      base::Bind(&BleScannerImpl::OnStartDiscoverySessionError,
-                 weak_ptr_factory_.GetWeakPtr()));
+      base::BindOnce(&BleScannerImpl::OnStartDiscoverySessionError,
+                     weak_ptr_factory_.GetWeakPtr()));
 }
 
 void BleScannerImpl::OnDiscoverySessionStarted(
@@ -165,10 +165,10 @@
 
   ble_synchronizer_->StopDiscoverySession(
       discovery_session_weak_ptr_factory_->GetWeakPtr(),
-      base::Bind(&BleScannerImpl::OnDiscoverySessionStopped,
-                 weak_ptr_factory_.GetWeakPtr()),
-      base::Bind(&BleScannerImpl::OnStopDiscoverySessionError,
-                 weak_ptr_factory_.GetWeakPtr()));
+      base::BindOnce(&BleScannerImpl::OnDiscoverySessionStopped,
+                     weak_ptr_factory_.GetWeakPtr()),
+      base::BindOnce(&BleScannerImpl::OnStopDiscoverySessionError,
+                     weak_ptr_factory_.GetWeakPtr()));
 }
 
 void BleScannerImpl::OnDiscoverySessionStopped() {
diff --git a/chromeos/services/secure_channel/ble_synchronizer_unittest.cc b/chromeos/services/secure_channel/ble_synchronizer_unittest.cc
index 907b321d..74ff12e6 100644
--- a/chromeos/services/secure_channel/ble_synchronizer_unittest.cc
+++ b/chromeos/services/secure_channel/ble_synchronizer_unittest.cc
@@ -332,7 +332,7 @@
         base::BindOnce(
             &SecureChannelBleSynchronizerTest::OnDiscoverySessionStarted,
             base::Unretained(this)),
-        base::Bind(
+        base::BindOnce(
             &SecureChannelBleSynchronizerTest::OnErrorStartingDiscoverySession,
             base::Unretained(this)));
   }
@@ -374,9 +374,10 @@
       base::WeakPtr<device::BluetoothDiscoverySession> discovery_session) {
     synchronizer_->StopDiscoverySession(
         discovery_session,
-        base::Bind(&SecureChannelBleSynchronizerTest::OnDiscoverySessionStopped,
-                   base::Unretained(this)),
-        base::Bind(
+        base::BindOnce(
+            &SecureChannelBleSynchronizerTest::OnDiscoverySessionStopped,
+            base::Unretained(this)),
+        base::BindOnce(
             &SecureChannelBleSynchronizerTest::OnErrorStoppingDiscoverySession,
             base::Unretained(this)));
   }
diff --git a/chromeos/services/secure_channel/ble_weave_client_connection.cc b/chromeos/services/secure_channel/ble_weave_client_connection.cc
index a4c4e54..216ef95 100644
--- a/chromeos/services/secure_channel/ble_weave_client_connection.cc
+++ b/chromeos/services/secure_channel/ble_weave_client_connection.cc
@@ -536,23 +536,22 @@
   PA_LOG(INFO) << "Finding GATT characteristics for "
                << GetDeviceInfoLogString() << ".";
   characteristic_finder_.reset(CreateCharacteristicsFinder(
-      base::Bind(
+      base::BindOnce(
           &BluetoothLowEnergyWeaveClientConnection::OnCharacteristicsFound,
           weak_ptr_factory_.GetWeakPtr()),
-      base::Bind(&BluetoothLowEnergyWeaveClientConnection::
-                     OnCharacteristicsFinderError,
-                 weak_ptr_factory_.GetWeakPtr())));
+      base::BindOnce(&BluetoothLowEnergyWeaveClientConnection::
+                         OnCharacteristicsFinderError,
+                     weak_ptr_factory_.GetWeakPtr())));
 }
 
 BluetoothLowEnergyCharacteristicsFinder*
 BluetoothLowEnergyWeaveClientConnection::CreateCharacteristicsFinder(
-    const BluetoothLowEnergyCharacteristicsFinder::SuccessCallback&
-        success_callback,
-    const BluetoothLowEnergyCharacteristicsFinder::ErrorCallback&
-        error_callback) {
+    BluetoothLowEnergyCharacteristicsFinder::SuccessCallback success_callback,
+    base::OnceClosure error_callback) {
   return new BluetoothLowEnergyCharacteristicsFinder(
       adapter_, GetBluetoothDevice(), remote_service_, tx_characteristic_,
-      rx_characteristic_, success_callback, error_callback, remote_device(),
+      rx_characteristic_, std::move(success_callback),
+      std::move(error_callback), remote_device(),
       std::make_unique<BackgroundEidGenerator>());
 }
 
@@ -878,8 +877,8 @@
   // instead of a base::Callback, so use a wrapper for now.
   auto callback_holder = base::AdaptCallbackForRepeating(std::move(callback));
   bluetooth_device->GetConnectionInfo(
-      base::Bind(&BluetoothLowEnergyWeaveClientConnection::OnConnectionInfo,
-                 weak_ptr_factory_.GetWeakPtr(), callback_holder));
+      base::BindOnce(&BluetoothLowEnergyWeaveClientConnection::OnConnectionInfo,
+                     weak_ptr_factory_.GetWeakPtr(), callback_holder));
 }
 
 void BluetoothLowEnergyWeaveClientConnection::OnConnectionInfo(
diff --git a/chromeos/services/secure_channel/ble_weave_client_connection.h b/chromeos/services/secure_channel/ble_weave_client_connection.h
index ca5f6ae..ead726ce6 100644
--- a/chromeos/services/secure_channel/ble_weave_client_connection.h
+++ b/chromeos/services/secure_channel/ble_weave_client_connection.h
@@ -144,10 +144,8 @@
       std::unique_ptr<BluetoothLowEnergyWeavePacketReceiver> test_receiver);
 
   virtual BluetoothLowEnergyCharacteristicsFinder* CreateCharacteristicsFinder(
-      const BluetoothLowEnergyCharacteristicsFinder::SuccessCallback&
-          success_callback,
-      const BluetoothLowEnergyCharacteristicsFinder::ErrorCallback&
-          error_callback);
+      BluetoothLowEnergyCharacteristicsFinder::SuccessCallback success_callback,
+      base::OnceClosure error_callback);
 
   // Connection:
   void SendMessageImpl(std::unique_ptr<WireMessage> message) override;
diff --git a/chromeos/services/secure_channel/ble_weave_client_connection_unittest.cc b/chromeos/services/secure_channel/ble_weave_client_connection_unittest.cc
index ed4c9da6..6f7851d 100644
--- a/chromeos/services/secure_channel/ble_weave_client_connection_unittest.cc
+++ b/chromeos/services/secure_channel/ble_weave_client_connection_unittest.cc
@@ -232,12 +232,16 @@
         should_set_low_connection_latency();
   }
 
+  BluetoothLowEnergyCharacteristicsFinder* CreateCharacteristicsFinder(
+      BluetoothLowEnergyCharacteristicsFinder::SuccessCallback success,
+      base::OnceClosure error) override {
+    return CreateCharacteristicsFinder_(success, error);
+  }
   MOCK_METHOD2(
-      CreateCharacteristicsFinder,
+      CreateCharacteristicsFinder_,
       BluetoothLowEnergyCharacteristicsFinder*(
-          const BluetoothLowEnergyCharacteristicsFinder::SuccessCallback&
-              success,
-          const BluetoothLowEnergyCharacteristicsFinder::ErrorCallback& error));
+          BluetoothLowEnergyCharacteristicsFinder::SuccessCallback& success,
+          base::OnceClosure& error));
 
   MOCK_METHOD1(OnBytesReceived, void(const std::string& bytes));
 
@@ -450,10 +454,10 @@
     // Preparing |connection| to run |create_gatt_connection_success_callback_|.
     EXPECT_FALSE(create_gatt_connection_error_callback_.is_null());
     ASSERT_FALSE(create_gatt_connection_success_callback_.is_null());
-    EXPECT_CALL(*connection, CreateCharacteristicsFinder(_, _))
+    EXPECT_CALL(*connection, CreateCharacteristicsFinder_(_, _))
         .WillOnce(DoAll(
-            SaveArg<0>(&characteristics_finder_success_callback_),
-            SaveArg<1>(&characteristics_finder_error_callback_),
+            MoveArg<0>(&characteristics_finder_success_callback_),
+            MoveArg<1>(&characteristics_finder_error_callback_),
             Return(new NiceMock<MockBluetoothLowEnergyCharacteristicsFinder>(
                 remote_device_))));
 
@@ -475,10 +479,10 @@
     EXPECT_FALSE(characteristics_finder_error_callback_.is_null());
     ASSERT_FALSE(characteristics_finder_success_callback_.is_null());
 
-    characteristics_finder_success_callback_.Run(
-        {service_uuid_, kServiceID},
-        {tx_characteristic_uuid_, kTXCharacteristicID},
-        {rx_characteristic_uuid_, kRXCharacteristicID});
+    std::move(characteristics_finder_success_callback_)
+        .Run({service_uuid_, kServiceID},
+             {tx_characteristic_uuid_, kTXCharacteristicID},
+             {rx_characteristic_uuid_, kRXCharacteristicID});
 
     EXPECT_EQ(connection->sub_status(), SubStatus::WAITING_NOTIFY_SESSION);
     EXPECT_EQ(connection->status(), Connection::Status::IN_PROGRESS);
@@ -683,8 +687,7 @@
 
   BluetoothLowEnergyCharacteristicsFinder::SuccessCallback
       characteristics_finder_success_callback_;
-  BluetoothLowEnergyCharacteristicsFinder::ErrorCallback
-      characteristics_finder_error_callback_;
+  base::OnceClosure characteristics_finder_error_callback_;
 
   device::BluetoothRemoteGattCharacteristic::NotifySessionCallback
       notify_session_success_callback_;
@@ -873,7 +876,7 @@
   EXPECT_FALSE(characteristics_finder_success_callback_.is_null());
   ASSERT_FALSE(characteristics_finder_error_callback_.is_null());
 
-  characteristics_finder_error_callback_.Run();
+  std::move(characteristics_finder_error_callback_).Run();
 
   EXPECT_EQ(connection->sub_status(), SubStatus::DISCONNECTED);
   EXPECT_EQ(connection->status(), Connection::Status::DISCONNECTED);
@@ -896,10 +899,10 @@
 
   EXPECT_FALSE(characteristics_finder_error_callback_.is_null());
   ASSERT_FALSE(characteristics_finder_success_callback_.is_null());
-  characteristics_finder_success_callback_.Run(
-      {service_uuid_, kServiceID},
-      {tx_characteristic_uuid_, kTXCharacteristicID},
-      {rx_characteristic_uuid_, kRXCharacteristicID});
+  std::move(characteristics_finder_success_callback_)
+      .Run({service_uuid_, kServiceID},
+           {tx_characteristic_uuid_, kTXCharacteristicID},
+           {rx_characteristic_uuid_, kRXCharacteristicID});
 
   EXPECT_EQ(connection->sub_status(), SubStatus::DISCONNECTED);
   EXPECT_EQ(connection->status(), Connection::Status::DISCONNECTED);
@@ -1386,10 +1389,10 @@
   ASSERT_FALSE(create_gatt_connection_success_callback_.is_null());
 
   // Preparing |connection| to run |create_gatt_connection_success_callback_|.
-  EXPECT_CALL(*connection, CreateCharacteristicsFinder(_, _))
+  EXPECT_CALL(*connection, CreateCharacteristicsFinder_(_, _))
       .WillOnce(DoAll(
-          SaveArg<0>(&characteristics_finder_success_callback_),
-          SaveArg<1>(&characteristics_finder_error_callback_),
+          MoveArg<0>(&characteristics_finder_success_callback_),
+          MoveArg<1>(&characteristics_finder_error_callback_),
           Return(new NiceMock<MockBluetoothLowEnergyCharacteristicsFinder>(
               remote_device_))));
 
@@ -1432,10 +1435,10 @@
   ASSERT_FALSE(create_gatt_connection_success_callback_.is_null());
 
   // Preparing |connection| to run |create_gatt_connection_success_callback_|.
-  EXPECT_CALL(*connection, CreateCharacteristicsFinder(_, _))
+  EXPECT_CALL(*connection, CreateCharacteristicsFinder_(_, _))
       .WillOnce(DoAll(
-          SaveArg<0>(&characteristics_finder_success_callback_),
-          SaveArg<1>(&characteristics_finder_error_callback_),
+          MoveArg<0>(&characteristics_finder_success_callback_),
+          MoveArg<1>(&characteristics_finder_error_callback_),
           Return(new NiceMock<MockBluetoothLowEnergyCharacteristicsFinder>(
               remote_device_))));
 
@@ -1489,10 +1492,10 @@
   std::move(connection_latency_callback_).Run();
 
   // Preparing |connection| to run |create_gatt_connection_success_callback_|.
-  EXPECT_CALL(*connection, CreateCharacteristicsFinder(_, _))
+  EXPECT_CALL(*connection, CreateCharacteristicsFinder_(_, _))
       .WillOnce(DoAll(
-          SaveArg<0>(&characteristics_finder_success_callback_),
-          SaveArg<1>(&characteristics_finder_error_callback_),
+          MoveArg<0>(&characteristics_finder_success_callback_),
+          MoveArg<1>(&characteristics_finder_error_callback_),
           Return(new NiceMock<MockBluetoothLowEnergyCharacteristicsFinder>(
               remote_device_))));
 
diff --git a/chromeos/services/secure_channel/secure_channel.cc b/chromeos/services/secure_channel/secure_channel.cc
index 9279f55..8bc59e5 100644
--- a/chromeos/services/secure_channel/secure_channel.cc
+++ b/chromeos/services/secure_channel/secure_channel.cc
@@ -167,8 +167,8 @@
 
   secure_context_->Decode(
       wire_message.payload(),
-      base::Bind(&SecureChannel::OnMessageDecoded,
-                 weak_ptr_factory_.GetWeakPtr(), wire_message.feature()));
+      base::BindOnce(&SecureChannel::OnMessageDecoded,
+                     weak_ptr_factory_.GetWeakPtr(), wire_message.feature()));
 }
 
 void SecureChannel::OnSendCompleted(const Connection& connection,
@@ -243,7 +243,7 @@
   authenticator_ = DeviceToDeviceAuthenticator::Factory::Create(
       connection_.get(),
       multidevice::SecureMessageDelegateImpl::Factory::Create());
-  authenticator_->Authenticate(base::Bind(
+  authenticator_->Authenticate(base::BindOnce(
       &SecureChannel::OnAuthenticationResult, weak_ptr_factory_.GetWeakPtr()));
 
   TransitionToStatus(Status::AUTHENTICATING);
@@ -267,9 +267,9 @@
 
   secure_context_->Encode(
       pending_message_->payload,
-      base::Bind(&SecureChannel::OnMessageEncoded,
-                 weak_ptr_factory_.GetWeakPtr(), pending_message_->feature,
-                 pending_message_->sequence_number));
+      base::BindOnce(&SecureChannel::OnMessageEncoded,
+                     weak_ptr_factory_.GetWeakPtr(), pending_message_->feature,
+                     pending_message_->sequence_number));
 }
 
 void SecureChannel::OnMessageEncoded(const std::string& feature,
diff --git a/chromeos/services/secure_channel/secure_channel_initializer.cc b/chromeos/services/secure_channel/secure_channel_initializer.cc
index 383c832..e646b09 100644
--- a/chromeos/services/secure_channel/secure_channel_initializer.cc
+++ b/chromeos/services/secure_channel/secure_channel_initializer.cc
@@ -67,8 +67,8 @@
       base::BindOnce(
           &device::BluetoothAdapterFactory::GetAdapter,
           base::Unretained(factory),
-          base::Bind(&SecureChannelInitializer::OnBluetoothAdapterReceived,
-                     weak_ptr_factory_.GetWeakPtr())));
+          base::BindOnce(&SecureChannelInitializer::OnBluetoothAdapterReceived,
+                         weak_ptr_factory_.GetWeakPtr())));
 }
 
 SecureChannelInitializer::~SecureChannelInitializer() = default;
diff --git a/chromeos/services/secure_channel/secure_channel_unittest.cc b/chromeos/services/secure_channel/secure_channel_unittest.cc
index 847052b4..1a75367 100644
--- a/chromeos/services/secure_channel/secure_channel_unittest.cc
+++ b/chromeos/services/secure_channel/secure_channel_unittest.cc
@@ -300,9 +300,9 @@
     // the implementation will call |VerifyWireMessageContents()| synchronously.
     fake_secure_context_->Encode(
         payload,
-        base::Bind(&SecureChannelConnectionTest::VerifyWireMessageContents,
-                   weak_ptr_factory_.GetWeakPtr(), message_being_sent,
-                   feature));
+        base::BindOnce(&SecureChannelConnectionTest::VerifyWireMessageContents,
+                       weak_ptr_factory_.GetWeakPtr(), message_being_sent,
+                       feature));
   }
 
   void VerifyWireMessageContents(WireMessage* wire_message,
diff --git a/components/autofill/core/browser/personal_data_manager.cc b/components/autofill/core/browser/personal_data_manager.cc
index dddab34b..d60138c 100644
--- a/components/autofill/core/browser/personal_data_manager.cc
+++ b/components/autofill/core/browser/personal_data_manager.cc
@@ -124,7 +124,7 @@
 
 // Receives the loaded profiles from the web data service and stores them in
 // |*dest|. The pending handle is the address of the pending handle
-// corresponding to this request type. This function is used to save both
+// corresponding to this request type. This function is used to save bShouldoth
 // server and local profiles and credit cards.
 template <typename ValueType>
 void ReceiveLoadedDbValues(WebDataServiceBase::Handle h,
@@ -1916,11 +1916,8 @@
 
 bool PersonalDataManager::ShouldShowCardsFromAccountOption() const {
 // The feature is only for Linux, Windows and Mac.
-#if defined(OS_CHROMEOS)
-  return false;
-#elif !defined(OS_LINUX) && !defined(OS_WIN) && !defined(OS_APPLE)
-  return false;
-#else
+#if (defined(OS_LINUX) && !defined(OS_CHROMEOS)) || defined(OS_WIN) || \
+    defined(OS_APPLE)
   // This option should only be shown for users that have not enabled the Sync
   // Feature and that have server credit cards available.
   if (!sync_service_ || sync_service_->IsSyncFeatureEnabled() ||
@@ -1941,7 +1938,10 @@
 
   // The option should only be shown if the user has not already opted-in.
   return !is_opted_in;
-#endif
+#else
+  return false;
+#endif // #if (defined(OS_LINUX) && !defined(OS_CHROMEOS)) || defined(OS_WIN) || \
+       //     defined(OS_APPLE)
 }
 
 void PersonalDataManager::OnUserAcceptedCardsFromAccountOption() {
diff --git a/components/autofill_assistant/browser/controller.cc b/components/autofill_assistant/browser/controller.cc
index 8c5333b8..c5af0d39 100644
--- a/components/autofill_assistant/browser/controller.cc
+++ b/components/autofill_assistant/browser/controller.cc
@@ -1128,9 +1128,12 @@
 }
 
 void Controller::ShowFirstMessageAndStart() {
-  SetStatusMessage(
-      l10n_util::GetStringFUTF8(IDS_AUTOFILL_ASSISTANT_LOADING,
-                                base::UTF8ToUTF16(GetCurrentURL().host())));
+  // Only show default status message if necessary.
+  if (status_message_.empty()) {
+    SetStatusMessage(
+        l10n_util::GetStringFUTF8(IDS_AUTOFILL_ASSISTANT_LOADING,
+                                  base::UTF8ToUTF16(GetCurrentURL().host())));
+  }
   if (step_progress_bar_configuration_.has_value() &&
       step_progress_bar_configuration_->use_step_progress_bar()) {
     SetProgressActiveStep(0);
diff --git a/components/dom_distiller/core/css/distilledpage_desktop.css b/components/dom_distiller/core/css/distilledpage_desktop.css
index 52b3238..f72b3f7 100644
--- a/components/dom_distiller/core/css/distilledpage_desktop.css
+++ b/components/dom_distiller/core/css/distilledpage_desktop.css
@@ -181,9 +181,11 @@
 }
 
 #theme-selection ul {
+  column-gap: 16px;
   display: grid;
   grid-template-columns: 1fr 1fr 1fr;
   list-style-type: none;
+  margin-inline-start: 16px;
 }
 
 .theme-option {
@@ -232,6 +234,7 @@
 
 #font-size-wrapper {
   grid-area: size;
+  padding: 0 8px;
 }
 
 #font-size-selection {
diff --git a/components/exo/wayland/wayland_display_observer.cc b/components/exo/wayland/wayland_display_observer.cc
index a9b12d98..00fb373 100644
--- a/components/exo/wayland/wayland_display_observer.cc
+++ b/components/exo/wayland/wayland_display_observer.cc
@@ -115,7 +115,11 @@
 
   if (wl_resource_get_version(output_resource_) >=
       WL_OUTPUT_SCALE_SINCE_VERSION) {
-    wl_output_send_scale(output_resource_, display.device_scale_factor());
+    // Sending 100% if the scale is less then 100%, because wl_output_send_scale
+    // doesn't support fractional scale.
+    wl_output_send_scale(
+        output_resource_,
+        std::max(1, static_cast<int32_t>(display.device_scale_factor())));
   }
 
   // TODO(reveman): Send real list of modes.
diff --git a/components/omnibox/browser/omnibox_pedal_concepts.h b/components/omnibox/browser/omnibox_pedal_concepts.h
index ef27158..4caa5a1 100644
--- a/components/omnibox/browser/omnibox_pedal_concepts.h
+++ b/components/omnibox/browser/omnibox_pedal_concepts.h
@@ -10,7 +10,7 @@
 // This value is generated during Pedal concept data processing, and written
 // to all data files as well as the source code here to ensure synchrony.
 // The runtime loaded data must match this version exactly or it won't load.
-constexpr int OMNIBOX_PEDAL_CONCEPTS_DATA_VERSION = 15476904;
+constexpr int OMNIBOX_PEDAL_CONCEPTS_DATA_VERSION = 15487260;
 
 // Unique identifiers for Pedals, used to bind loaded data to implementations.
 // Also used in the Omnibox.SuggestionUsed.Pedal histogram. Do not remove or
diff --git a/components/omnibox/resources/omnibox_pedal_concepts.json b/components/omnibox/resources/omnibox_pedal_concepts.json
index e54f659..4b6f582d 100644
--- a/components/omnibox/resources/omnibox_pedal_concepts.json
+++ b/components/omnibox/resources/omnibox_pedal_concepts.json
@@ -1,7 +1,7 @@
 {
   "schema": "pedals_translation_model",
   "schema_version": 1,
-  "time_generated": "2020-08-20T20:24:15.027402404+00:00",
+  "time_generated": "2020-08-28T01:00:43.759095665+00:00",
   "primary_language_code": "en",
   "locales": [
     {
@@ -549,7 +549,7 @@
               ]
             },
             {
-              "required": false,
+              "required": true,
               "single": true,
               "synonyms": [
                 "актуализиране",
@@ -602,7 +602,7 @@
               ]
             },
             {
-              "required": false,
+              "required": true,
               "single": true,
               "synonyms": [
                 "идентификацията",
@@ -631,7 +631,7 @@
               ]
             },
             {
-              "required": false,
+              "required": true,
               "single": true,
               "synonyms": [
                 "актуализиране",
@@ -671,7 +671,7 @@
               ]
             },
             {
-              "required": false,
+              "required": true,
               "single": true,
               "synonyms": [
                 "информацията кредитната карта",
@@ -713,7 +713,7 @@
               ]
             },
             {
-              "required": false,
+              "required": true,
               "single": true,
               "synonyms": [
                 "стартиране",
@@ -742,7 +742,7 @@
               ]
             },
             {
-              "required": false,
+              "required": true,
               "single": true,
               "synonyms": [
                 "поверително сърфиране",
@@ -781,7 +781,7 @@
               ]
             },
             {
-              "required": false,
+              "required": true,
               "single": true,
               "synonyms": [
                 "промяна езика",
@@ -797,7 +797,7 @@
               ]
             },
             {
-              "required": false,
+              "required": true,
               "single": true,
               "synonyms": [
                 "тази страница",
@@ -822,7 +822,7 @@
               ]
             },
             {
-              "required": false,
+              "required": true,
               "single": true,
               "synonyms": [
                 "актуализиране",
@@ -1755,7 +1755,7 @@
               ]
             },
             {
-              "required": false,
+              "required": true,
               "single": true,
               "synonyms": [
                 "inkognitotilstand",
@@ -1948,6 +1948,7 @@
               "required": true,
               "single": true,
               "synonyms": [
+                "karteninformationen",
                 "kreditkartendaten",
                 "kredit karte",
                 "kreditkarte",
@@ -2023,13 +2024,7 @@
                 "diese seite",
                 "webseite",
                 "website",
-                "seite"
-              ]
-            },
-            {
-              "required": true,
-              "single": true,
-              "synonyms": [
+                "seite",
                 "diese"
               ]
             }
@@ -5833,18 +5828,31 @@
         "required": false,
         "single": false,
         "synonyms": [
-          "in/nel/nell'/nello/nei/negli/nella/nelle",
-          "mio/mia/mie/miei",
           "all'interno",
-          "il/lo/la",
           "dentro",
-          "un/una",
+          "nell'",
+          "nello",
+          "negli",
+          "nella",
+          "nelle",
           "come",
           "fare",
+          "miei",
           "per",
+          "mio",
           "un'",
+          "una",
+          "nel",
+          "nei",
+          "mia",
+          "mie",
+          "il",
+          "un",
+          "in",
           "su",
           "io",
+          "lo",
+          "la",
           "a"
         ]
       },
diff --git a/components/omnibox/resources/omnibox_pedal_concepts_am.json b/components/omnibox/resources/omnibox_pedal_concepts_am.json
index ce14ced9..d04d9b1 100644
--- a/components/omnibox/resources/omnibox_pedal_concepts_am.json
+++ b/components/omnibox/resources/omnibox_pedal_concepts_am.json
@@ -1,7 +1,7 @@
 {
   "schema": "pedal_concepts_runtime",
   "schema_version": 1,
-  "data_version": 15476904,
+  "data_version": 15487260,
   "tokenize_characters": " -",
   "dictionary": [
     "chrome",
diff --git a/components/omnibox/resources/omnibox_pedal_concepts_ar.json b/components/omnibox/resources/omnibox_pedal_concepts_ar.json
index 5e1f99d..21875f9 100644
--- a/components/omnibox/resources/omnibox_pedal_concepts_ar.json
+++ b/components/omnibox/resources/omnibox_pedal_concepts_ar.json
@@ -1,7 +1,7 @@
 {
   "schema": "pedal_concepts_runtime",
   "schema_version": 1,
-  "data_version": 15476904,
+  "data_version": 15487260,
   "tokenize_characters": " -",
   "dictionary": [
     "a",
diff --git a/components/omnibox/resources/omnibox_pedal_concepts_bg.json b/components/omnibox/resources/omnibox_pedal_concepts_bg.json
index 780aa21..311eb1c5 100644
--- a/components/omnibox/resources/omnibox_pedal_concepts_bg.json
+++ b/components/omnibox/resources/omnibox_pedal_concepts_bg.json
@@ -1,7 +1,7 @@
 {
   "schema": "pedal_concepts_runtime",
   "schema_version": 1,
-  "data_version": 15476904,
+  "data_version": 15487260,
   "tokenize_characters": " -",
   "dictionary": [
     "chrome",
@@ -320,7 +320,7 @@
           ]
         },
         {
-          "required": false,
+          "required": true,
           "single": true,
           "synonyms": [
             [
@@ -467,7 +467,7 @@
           ]
         },
         {
-          "required": false,
+          "required": true,
           "single": true,
           "synonyms": [
             [
@@ -524,7 +524,7 @@
           ]
         },
         {
-          "required": false,
+          "required": true,
           "single": true,
           "synonyms": [
             [
@@ -632,7 +632,7 @@
           ]
         },
         {
-          "required": false,
+          "required": true,
           "single": true,
           "synonyms": [
             [
@@ -751,7 +751,7 @@
           ]
         },
         {
-          "required": false,
+          "required": true,
           "single": true,
           "synonyms": [
             [
@@ -826,7 +826,7 @@
           ]
         },
         {
-          "required": false,
+          "required": true,
           "single": true,
           "synonyms": [
             [
@@ -927,7 +927,7 @@
           ]
         },
         {
-          "required": false,
+          "required": true,
           "single": true,
           "synonyms": [
             [
@@ -968,7 +968,7 @@
           ]
         },
         {
-          "required": false,
+          "required": true,
           "single": true,
           "synonyms": [
             [
@@ -1012,7 +1012,7 @@
           ]
         },
         {
-          "required": false,
+          "required": true,
           "single": true,
           "synonyms": [
             [
diff --git a/components/omnibox/resources/omnibox_pedal_concepts_bn.json b/components/omnibox/resources/omnibox_pedal_concepts_bn.json
index e828838..420c81b 100644
--- a/components/omnibox/resources/omnibox_pedal_concepts_bn.json
+++ b/components/omnibox/resources/omnibox_pedal_concepts_bn.json
@@ -1,7 +1,7 @@
 {
   "schema": "pedal_concepts_runtime",
   "schema_version": 1,
-  "data_version": 15476904,
+  "data_version": 15487260,
   "tokenize_characters": " -",
   "dictionary": [
     "chrome",
diff --git a/components/omnibox/resources/omnibox_pedal_concepts_ca.json b/components/omnibox/resources/omnibox_pedal_concepts_ca.json
index be52ced..8bc59121 100644
--- a/components/omnibox/resources/omnibox_pedal_concepts_ca.json
+++ b/components/omnibox/resources/omnibox_pedal_concepts_ca.json
@@ -1,7 +1,7 @@
 {
   "schema": "pedal_concepts_runtime",
   "schema_version": 1,
-  "data_version": 15476904,
+  "data_version": 15487260,
   "tokenize_characters": " -",
   "dictionary": [
     "a",
diff --git a/components/omnibox/resources/omnibox_pedal_concepts_cs.json b/components/omnibox/resources/omnibox_pedal_concepts_cs.json
index c5cee6b..e276d8e4 100644
--- a/components/omnibox/resources/omnibox_pedal_concepts_cs.json
+++ b/components/omnibox/resources/omnibox_pedal_concepts_cs.json
@@ -1,7 +1,7 @@
 {
   "schema": "pedal_concepts_runtime",
   "schema_version": 1,
-  "data_version": 15476904,
+  "data_version": 15487260,
   "tokenize_characters": " -",
   "dictionary": [
     "aktualizovat",
diff --git a/components/omnibox/resources/omnibox_pedal_concepts_da.json b/components/omnibox/resources/omnibox_pedal_concepts_da.json
index 4ebcc6d..c03e7d1 100644
--- a/components/omnibox/resources/omnibox_pedal_concepts_da.json
+++ b/components/omnibox/resources/omnibox_pedal_concepts_da.json
@@ -1,7 +1,7 @@
 {
   "schema": "pedal_concepts_runtime",
   "schema_version": 1,
-  "data_version": 15476904,
+  "data_version": 15487260,
   "tokenize_characters": " -",
   "dictionary": [
     "adgangskode",
@@ -331,7 +331,7 @@
           ]
         },
         {
-          "required": false,
+          "required": true,
           "single": true,
           "synonyms": [
             [
diff --git a/components/omnibox/resources/omnibox_pedal_concepts_de.json b/components/omnibox/resources/omnibox_pedal_concepts_de.json
index d1d6623..094ce951 100644
--- a/components/omnibox/resources/omnibox_pedal_concepts_de.json
+++ b/components/omnibox/resources/omnibox_pedal_concepts_de.json
@@ -1,7 +1,7 @@
 {
   "schema": "pedal_concepts_runtime",
   "schema_version": 1,
-  "data_version": 15476904,
+  "data_version": 15487260,
   "tokenize_characters": " -",
   "dictionary": [
     "aktualisieren",
@@ -33,6 +33,7 @@
     "kann",
     "karte",
     "kartendaten",
+    "karteninformationen",
     "kredit",
     "kreditkarte",
     "kreditkartendaten",
@@ -100,10 +101,10 @@
               16
             ],
             [
-              33
+              34
             ],
             [
-              32
+              33
             ]
           ]
         },
@@ -118,10 +119,10 @@
               7
             ],
             [
-              52
+              53
             ],
             [
-              57
+              58
             ],
             [
               9
@@ -158,7 +159,7 @@
           "single": true,
           "synonyms": [
             [
-              58
+              59
             ],
             [
               1
@@ -167,7 +168,7 @@
               2
             ],
             [
-              63
+              64
             ]
           ]
         },
@@ -176,10 +177,10 @@
           "single": true,
           "synonyms": [
             [
-              42
+              43
             ],
             [
-              41
+              42
             ]
           ]
         }
@@ -216,7 +217,7 @@
               5
             ],
             [
-              63
+              64
             ]
           ]
         },
@@ -225,14 +226,17 @@
           "single": true,
           "synonyms": [
             [
-              29,
+              30,
               27
             ],
             [
-              31
+              29
             ],
             [
-              30
+              32
+            ],
+            [
+              31
             ],
             [
               28
@@ -269,13 +273,13 @@
               3
             ],
             [
-              51
+              52
             ],
             [
-              64
+              65
             ],
             [
-              53
+              54
             ]
           ]
         },
@@ -289,11 +293,11 @@
             ],
             [
               22,
-              40
+              41
             ],
             [
-              43,
-              40
+              44,
+              41
             ],
             [
               23
@@ -302,13 +306,13 @@
               24
             ],
             [
-              44
+              45
             ],
             [
               22
             ],
             [
-              43
+              44
             ]
           ]
         }
@@ -339,11 +343,11 @@
           "single": true,
           "synonyms": [
             [
-              50,
-              63
+              51,
+              64
             ],
             [
-              65
+              66
             ]
           ]
         },
@@ -353,31 +357,25 @@
           "synonyms": [
             [
               14,
-              59
-            ],
-            [
-              14,
               60
             ],
             [
               14,
-              49
+              61
             ],
             [
-              59
+              14,
+              50
             ],
             [
               60
             ],
             [
-              49
-            ]
-          ]
-        },
-        {
-          "required": true,
-          "single": true,
-          "synonyms": [
+              61
+            ],
+            [
+              50
+            ],
             [
               14
             ]
@@ -416,13 +414,13 @@
               25
             ],
             [
+              57
+            ],
+            [
               56
             ],
             [
               55
-            ],
-            [
-              54
             ]
           ]
         }
@@ -434,16 +432,25 @@
     "single": false,
     "synonyms": [
       [
+        38
+      ],
+      [
+        48
+      ],
+      [
+        49
+      ],
+      [
+        39
+      ],
+      [
         37
       ],
       [
         47
       ],
       [
-        48
-      ],
-      [
-        38
+        26
       ],
       [
         36
@@ -452,22 +459,13 @@
         46
       ],
       [
-        26
-      ],
-      [
-        35
-      ],
-      [
-        45
-      ],
-      [
-        61
+        62
       ],
       [
         19
       ],
       [
-        34
+        35
       ],
       [
         13
@@ -476,7 +474,7 @@
         4
       ],
       [
-        39
+        40
       ],
       [
         12
@@ -485,7 +483,7 @@
         15
       ],
       [
-        62
+        63
       ],
       [
         21
diff --git a/components/omnibox/resources/omnibox_pedal_concepts_el.json b/components/omnibox/resources/omnibox_pedal_concepts_el.json
index 656c94e..4ffeab6 100644
--- a/components/omnibox/resources/omnibox_pedal_concepts_el.json
+++ b/components/omnibox/resources/omnibox_pedal_concepts_el.json
@@ -1,7 +1,7 @@
 {
   "schema": "pedal_concepts_runtime",
   "schema_version": 1,
-  "data_version": 15476904,
+  "data_version": 15487260,
   "tokenize_characters": " -",
   "dictionary": [
     "chrome",
diff --git a/components/omnibox/resources/omnibox_pedal_concepts_en-GB.json b/components/omnibox/resources/omnibox_pedal_concepts_en-GB.json
index 176f186a..a5152ec9 100644
--- a/components/omnibox/resources/omnibox_pedal_concepts_en-GB.json
+++ b/components/omnibox/resources/omnibox_pedal_concepts_en-GB.json
@@ -1,7 +1,7 @@
 {
   "schema": "pedal_concepts_runtime",
   "schema_version": 1,
-  "data_version": 15476904,
+  "data_version": 15487260,
   "tokenize_characters": " -",
   "dictionary": [
     "a",
diff --git a/components/omnibox/resources/omnibox_pedal_concepts_en.json b/components/omnibox/resources/omnibox_pedal_concepts_en.json
index 4e9a7d0..a73c81b6 100644
--- a/components/omnibox/resources/omnibox_pedal_concepts_en.json
+++ b/components/omnibox/resources/omnibox_pedal_concepts_en.json
@@ -1,7 +1,7 @@
 {
   "schema": "pedal_concepts_runtime",
   "schema_version": 1,
-  "data_version": 15476904,
+  "data_version": 15487260,
   "tokenize_characters": " -",
   "dictionary": [
     "a",
diff --git a/components/omnibox/resources/omnibox_pedal_concepts_es-419.json b/components/omnibox/resources/omnibox_pedal_concepts_es-419.json
index 88ee92dd..95fb83e8 100644
--- a/components/omnibox/resources/omnibox_pedal_concepts_es-419.json
+++ b/components/omnibox/resources/omnibox_pedal_concepts_es-419.json
@@ -1,7 +1,7 @@
 {
   "schema": "pedal_concepts_runtime",
   "schema_version": 1,
-  "data_version": 15476904,
+  "data_version": 15487260,
   "tokenize_characters": " -",
   "dictionary": [
     "abrir",
diff --git a/components/omnibox/resources/omnibox_pedal_concepts_es.json b/components/omnibox/resources/omnibox_pedal_concepts_es.json
index 73c818c..914517d 100644
--- a/components/omnibox/resources/omnibox_pedal_concepts_es.json
+++ b/components/omnibox/resources/omnibox_pedal_concepts_es.json
@@ -1,7 +1,7 @@
 {
   "schema": "pedal_concepts_runtime",
   "schema_version": 1,
-  "data_version": 15476904,
+  "data_version": 15487260,
   "tokenize_characters": " -",
   "dictionary": [
     "a",
diff --git a/components/omnibox/resources/omnibox_pedal_concepts_et.json b/components/omnibox/resources/omnibox_pedal_concepts_et.json
index edf3279..f89f7b89 100644
--- a/components/omnibox/resources/omnibox_pedal_concepts_et.json
+++ b/components/omnibox/resources/omnibox_pedal_concepts_et.json
@@ -1,7 +1,7 @@
 {
   "schema": "pedal_concepts_runtime",
   "schema_version": 1,
-  "data_version": 15476904,
+  "data_version": 15487260,
   "tokenize_characters": " -",
   "dictionary": [
     "ajalugu",
diff --git a/components/omnibox/resources/omnibox_pedal_concepts_fa.json b/components/omnibox/resources/omnibox_pedal_concepts_fa.json
index 92d4204..ceeb649f 100644
--- a/components/omnibox/resources/omnibox_pedal_concepts_fa.json
+++ b/components/omnibox/resources/omnibox_pedal_concepts_fa.json
@@ -1,7 +1,7 @@
 {
   "schema": "pedal_concepts_runtime",
   "schema_version": 1,
-  "data_version": 15476904,
+  "data_version": 15487260,
   "tokenize_characters": " -",
   "dictionary": [
     "chrome",
diff --git a/components/omnibox/resources/omnibox_pedal_concepts_fi.json b/components/omnibox/resources/omnibox_pedal_concepts_fi.json
index 974e7f0..c2ee0b7 100644
--- a/components/omnibox/resources/omnibox_pedal_concepts_fi.json
+++ b/components/omnibox/resources/omnibox_pedal_concepts_fi.json
@@ -1,7 +1,7 @@
 {
   "schema": "pedal_concepts_runtime",
   "schema_version": 1,
-  "data_version": 15476904,
+  "data_version": 15487260,
   "tokenize_characters": " -",
   "dictionary": [
     "asenna",
diff --git a/components/omnibox/resources/omnibox_pedal_concepts_fil.json b/components/omnibox/resources/omnibox_pedal_concepts_fil.json
index 853de86f..76a2badd 100644
--- a/components/omnibox/resources/omnibox_pedal_concepts_fil.json
+++ b/components/omnibox/resources/omnibox_pedal_concepts_fil.json
@@ -1,7 +1,7 @@
 {
   "schema": "pedal_concepts_runtime",
   "schema_version": 1,
-  "data_version": 15476904,
+  "data_version": 15487260,
   "tokenize_characters": " -",
   "dictionary": [
     "akin",
diff --git a/components/omnibox/resources/omnibox_pedal_concepts_fr.json b/components/omnibox/resources/omnibox_pedal_concepts_fr.json
index 0e9d5f05..d2d0092d 100644
--- a/components/omnibox/resources/omnibox_pedal_concepts_fr.json
+++ b/components/omnibox/resources/omnibox_pedal_concepts_fr.json
@@ -1,7 +1,7 @@
 {
   "schema": "pedal_concepts_runtime",
   "schema_version": 1,
-  "data_version": 15476904,
+  "data_version": 15487260,
   "tokenize_characters": " -",
   "dictionary": [
     "actualise",
diff --git a/components/omnibox/resources/omnibox_pedal_concepts_gu.json b/components/omnibox/resources/omnibox_pedal_concepts_gu.json
index 25524ea..23ee650c 100644
--- a/components/omnibox/resources/omnibox_pedal_concepts_gu.json
+++ b/components/omnibox/resources/omnibox_pedal_concepts_gu.json
@@ -1,7 +1,7 @@
 {
   "schema": "pedal_concepts_runtime",
   "schema_version": 1,
-  "data_version": 15476904,
+  "data_version": 15487260,
   "tokenize_characters": " -",
   "dictionary": [
     "chrome",
diff --git a/components/omnibox/resources/omnibox_pedal_concepts_he.json b/components/omnibox/resources/omnibox_pedal_concepts_he.json
index 96a2e8e..22ec780 100644
--- a/components/omnibox/resources/omnibox_pedal_concepts_he.json
+++ b/components/omnibox/resources/omnibox_pedal_concepts_he.json
@@ -1,7 +1,7 @@
 {
   "schema": "pedal_concepts_runtime",
   "schema_version": 1,
-  "data_version": 15476904,
+  "data_version": 15487260,
   "tokenize_characters": " -",
   "dictionary": [
     "chrome",
diff --git a/components/omnibox/resources/omnibox_pedal_concepts_hi.json b/components/omnibox/resources/omnibox_pedal_concepts_hi.json
index 6ad8b44..998a24c 100644
--- a/components/omnibox/resources/omnibox_pedal_concepts_hi.json
+++ b/components/omnibox/resources/omnibox_pedal_concepts_hi.json
@@ -1,7 +1,7 @@
 {
   "schema": "pedal_concepts_runtime",
   "schema_version": 1,
-  "data_version": 15476904,
+  "data_version": 15487260,
   "tokenize_characters": " -",
   "dictionary": [
     "chrome",
diff --git a/components/omnibox/resources/omnibox_pedal_concepts_hr.json b/components/omnibox/resources/omnibox_pedal_concepts_hr.json
index bbcf1b9..cba95a0 100644
--- a/components/omnibox/resources/omnibox_pedal_concepts_hr.json
+++ b/components/omnibox/resources/omnibox_pedal_concepts_hr.json
@@ -1,7 +1,7 @@
 {
   "schema": "pedal_concepts_runtime",
   "schema_version": 1,
-  "data_version": 15476904,
+  "data_version": 15487260,
   "tokenize_characters": " -",
   "dictionary": [
     "anonimna",
diff --git a/components/omnibox/resources/omnibox_pedal_concepts_hu.json b/components/omnibox/resources/omnibox_pedal_concepts_hu.json
index 269ea05..8cb9951 100644
--- a/components/omnibox/resources/omnibox_pedal_concepts_hu.json
+++ b/components/omnibox/resources/omnibox_pedal_concepts_hu.json
@@ -1,7 +1,7 @@
 {
   "schema": "pedal_concepts_runtime",
   "schema_version": 1,
-  "data_version": 15476904,
+  "data_version": 15487260,
   "tokenize_characters": " -",
   "dictionary": [
     "a",
diff --git a/components/omnibox/resources/omnibox_pedal_concepts_id.json b/components/omnibox/resources/omnibox_pedal_concepts_id.json
index 626818c..07a9f59 100644
--- a/components/omnibox/resources/omnibox_pedal_concepts_id.json
+++ b/components/omnibox/resources/omnibox_pedal_concepts_id.json
@@ -1,7 +1,7 @@
 {
   "schema": "pedal_concepts_runtime",
   "schema_version": 1,
-  "data_version": 15476904,
+  "data_version": 15487260,
   "tokenize_characters": " -",
   "dictionary": [
     "bagaimana",
diff --git a/components/omnibox/resources/omnibox_pedal_concepts_it.json b/components/omnibox/resources/omnibox_pedal_concepts_it.json
index 26b35655..c952d06f 100644
--- a/components/omnibox/resources/omnibox_pedal_concepts_it.json
+++ b/components/omnibox/resources/omnibox_pedal_concepts_it.json
@@ -1,7 +1,7 @@
 {
   "schema": "pedal_concepts_runtime",
   "schema_version": 1,
-  "data_version": 15476904,
+  "data_version": 15487260,
   "tokenize_characters": " -",
   "dictionary": [
     "a",
@@ -32,9 +32,8 @@
     "finestra",
     "gestire",
     "google",
-    "il/lo/la",
+    "il",
     "in",
-    "in/nel/nell'/nello/nei/negli/nella/nelle",
     "incognito",
     "info",
     "informazioni",
@@ -44,13 +43,24 @@
     "la",
     "lanciare",
     "lingua",
+    "lo",
     "manager",
     "metodi",
-    "mio/mia/mie/miei",
+    "mia",
+    "mie",
+    "miei",
+    "mio",
     "modalità",
     "modificare",
     "mostrare",
     "navigazione",
+    "negli",
+    "nei",
+    "nel",
+    "nell'",
+    "nella",
+    "nelle",
+    "nello",
     "pagamenti",
     "pagamento",
     "pagina",
@@ -65,8 +75,9 @@
     "su",
     "svuotare",
     "tradurre",
+    "un",
     "un'",
-    "un/una",
+    "una",
     "visualizzare"
   ],
   "pedals": [
@@ -83,7 +94,7 @@
               12
             ],
             [
-              46
+              49
             ],
             [
               6
@@ -104,13 +115,13 @@
               22
             ],
             [
-              55
+              65
             ],
             [
-              54
+              64
             ],
             [
-              59
+              69
             ]
           ]
         },
@@ -119,7 +130,7 @@
           "single": true,
           "synonyms": [
             [
-              33
+              32
             ],
             [
               18
@@ -134,7 +145,7 @@
               19
             ],
             [
-              32
+              31
             ]
           ]
         }
@@ -165,10 +176,10 @@
           "single": true,
           "synonyms": [
             [
-              63
+              74
             ],
             [
-              44
+              47
             ],
             [
               1
@@ -177,7 +188,7 @@
               8
             ],
             [
-              45
+              48
             ],
             [
               40
@@ -186,7 +197,7 @@
               26
             ],
             [
-              56
+              66
             ]
           ]
         },
@@ -198,10 +209,10 @@
               16
             ],
             [
-              50
+              60
             ],
             [
-              50
+              60
             ]
           ]
         }
@@ -235,7 +246,7 @@
               1
             ],
             [
-              44
+              47
             ],
             [
               8
@@ -244,7 +255,7 @@
               26
             ],
             [
-              56
+              66
             ]
           ]
         },
@@ -253,7 +264,7 @@
           "single": true,
           "synonyms": [
             [
-              33,
+              32,
               21,
               11,
               21,
@@ -267,14 +278,14 @@
               17
             ],
             [
-              33,
+              32,
               21,
               11
             ],
             [
               41,
               21,
-              48
+              58
             ],
             [
               11,
@@ -292,10 +303,10 @@
               11
             ],
             [
-              48
+              58
             ],
             [
-              47
+              57
             ],
             [
               11
@@ -329,7 +340,7 @@
           "single": true,
           "synonyms": [
             [
-              38
+              37
             ],
             [
               4
@@ -350,41 +361,38 @@
           "single": true,
           "synonyms": [
             [
-              43,
-              21,
               46,
-              29,
-              31
+              21,
+              49,
+              30
             ],
             [
               25,
               21,
-              46,
-              29,
-              31
+              49,
+              30
             ],
             [
-              57,
+              67,
               21,
-              46,
-              29,
-              31
+              49,
+              30
+            ],
+            [
+              49,
+              30
             ],
             [
               46,
-              31
-            ],
-            [
-              43,
-              52
+              62
             ],
             [
               25,
-              52
+              62
             ],
             [
-              57,
-              52
+              67,
+              62
             ]
           ]
         }
@@ -416,11 +424,10 @@
           "synonyms": [
             [
               8,
-              37,
-              39
+              38
             ],
             [
-              60
+              70
             ]
           ]
         },
@@ -429,14 +436,14 @@
           "single": true,
           "synonyms": [
             [
-              53,
-              49
+              63,
+              59
             ],
             [
-              49
+              59
             ],
             [
-              53
+              63
             ]
           ]
         }
@@ -468,10 +475,10 @@
           "synonyms": [
             [
               23,
-              36
+              35
             ],
             [
-              34
+              33
             ],
             [
               1
@@ -486,22 +493,25 @@
     "single": false,
     "synonyms": [
       [
-        30
-      ],
-      [
-        42
-      ],
-      [
         2
       ],
       [
-        28
-      ],
-      [
         20
       ],
       [
-        62
+        53
+      ],
+      [
+        56
+      ],
+      [
+        50
+      ],
+      [
+        54
+      ],
+      [
+        55
       ],
       [
         13
@@ -510,16 +520,52 @@
         24
       ],
       [
-        51
+        44
       ],
       [
         61
       ],
       [
-        58
+        45
       ],
       [
-        35
+        72
+      ],
+      [
+        73
+      ],
+      [
+        52
+      ],
+      [
+        51
+      ],
+      [
+        42
+      ],
+      [
+        43
+      ],
+      [
+        28
+      ],
+      [
+        71
+      ],
+      [
+        29
+      ],
+      [
+        68
+      ],
+      [
+        34
+      ],
+      [
+        39
+      ],
+      [
+        36
       ],
       [
         0
diff --git a/components/omnibox/resources/omnibox_pedal_concepts_ja.json b/components/omnibox/resources/omnibox_pedal_concepts_ja.json
index 89470fce..7d4bb73 100644
--- a/components/omnibox/resources/omnibox_pedal_concepts_ja.json
+++ b/components/omnibox/resources/omnibox_pedal_concepts_ja.json
@@ -1,7 +1,7 @@
 {
   "schema": "pedal_concepts_runtime",
   "schema_version": 1,
-  "data_version": 15476904,
+  "data_version": 15487260,
   "tokenize_characters": "",
   "dictionary": [
     " ",
diff --git a/components/omnibox/resources/omnibox_pedal_concepts_kn.json b/components/omnibox/resources/omnibox_pedal_concepts_kn.json
index 659a2cb..3a977c3 100644
--- a/components/omnibox/resources/omnibox_pedal_concepts_kn.json
+++ b/components/omnibox/resources/omnibox_pedal_concepts_kn.json
@@ -1,7 +1,7 @@
 {
   "schema": "pedal_concepts_runtime",
   "schema_version": 1,
-  "data_version": 15476904,
+  "data_version": 15487260,
   "tokenize_characters": " -",
   "dictionary": [
     "chrome",
diff --git a/components/omnibox/resources/omnibox_pedal_concepts_ko.json b/components/omnibox/resources/omnibox_pedal_concepts_ko.json
index adc497f..8b62e6d 100644
--- a/components/omnibox/resources/omnibox_pedal_concepts_ko.json
+++ b/components/omnibox/resources/omnibox_pedal_concepts_ko.json
@@ -1,7 +1,7 @@
 {
   "schema": "pedal_concepts_runtime",
   "schema_version": 1,
-  "data_version": 15476904,
+  "data_version": 15487260,
   "tokenize_characters": " -",
   "dictionary": [
     "chrome",
diff --git a/components/omnibox/resources/omnibox_pedal_concepts_lt.json b/components/omnibox/resources/omnibox_pedal_concepts_lt.json
index 7d5de8e7..0fe61ed 100644
--- a/components/omnibox/resources/omnibox_pedal_concepts_lt.json
+++ b/components/omnibox/resources/omnibox_pedal_concepts_lt.json
@@ -1,7 +1,7 @@
 {
   "schema": "pedal_concepts_runtime",
   "schema_version": 1,
-  "data_version": 15476904,
+  "data_version": 15487260,
   "tokenize_characters": " -",
   "dictionary": [
     "atidaryti",
diff --git a/components/omnibox/resources/omnibox_pedal_concepts_lv.json b/components/omnibox/resources/omnibox_pedal_concepts_lv.json
index 0d4420f..c49d3dc 100644
--- a/components/omnibox/resources/omnibox_pedal_concepts_lv.json
+++ b/components/omnibox/resources/omnibox_pedal_concepts_lv.json
@@ -1,7 +1,7 @@
 {
   "schema": "pedal_concepts_runtime",
   "schema_version": 1,
-  "data_version": 15476904,
+  "data_version": 15487260,
   "tokenize_characters": " -",
   "dictionary": [
     "akreditācijas",
diff --git a/components/omnibox/resources/omnibox_pedal_concepts_ml.json b/components/omnibox/resources/omnibox_pedal_concepts_ml.json
index 88789a8..5172443 100644
--- a/components/omnibox/resources/omnibox_pedal_concepts_ml.json
+++ b/components/omnibox/resources/omnibox_pedal_concepts_ml.json
@@ -1,7 +1,7 @@
 {
   "schema": "pedal_concepts_runtime",
   "schema_version": 1,
-  "data_version": 15476904,
+  "data_version": 15487260,
   "tokenize_characters": " -",
   "dictionary": [
     "chrome",
diff --git a/components/omnibox/resources/omnibox_pedal_concepts_mr.json b/components/omnibox/resources/omnibox_pedal_concepts_mr.json
index f8283d4c..ed68575 100644
--- a/components/omnibox/resources/omnibox_pedal_concepts_mr.json
+++ b/components/omnibox/resources/omnibox_pedal_concepts_mr.json
@@ -1,7 +1,7 @@
 {
   "schema": "pedal_concepts_runtime",
   "schema_version": 1,
-  "data_version": 15476904,
+  "data_version": 15487260,
   "tokenize_characters": " -",
   "dictionary": [
     "chrome",
diff --git a/components/omnibox/resources/omnibox_pedal_concepts_ms.json b/components/omnibox/resources/omnibox_pedal_concepts_ms.json
index 53b7558..71e37c8 100644
--- a/components/omnibox/resources/omnibox_pedal_concepts_ms.json
+++ b/components/omnibox/resources/omnibox_pedal_concepts_ms.json
@@ -1,7 +1,7 @@
 {
   "schema": "pedal_concepts_runtime",
   "schema_version": 1,
-  "data_version": 15476904,
+  "data_version": 15487260,
   "tokenize_characters": " -",
   "dictionary": [
     "alih",
diff --git a/components/omnibox/resources/omnibox_pedal_concepts_nl.json b/components/omnibox/resources/omnibox_pedal_concepts_nl.json
index 0bc4a780..a53b365 100644
--- a/components/omnibox/resources/omnibox_pedal_concepts_nl.json
+++ b/components/omnibox/resources/omnibox_pedal_concepts_nl.json
@@ -1,7 +1,7 @@
 {
   "schema": "pedal_concepts_runtime",
   "schema_version": 1,
-  "data_version": 15476904,
+  "data_version": 15487260,
   "tokenize_characters": " -",
   "dictionary": [
     "aan",
diff --git a/components/omnibox/resources/omnibox_pedal_concepts_pl.json b/components/omnibox/resources/omnibox_pedal_concepts_pl.json
index d672c7b..7301000 100644
--- a/components/omnibox/resources/omnibox_pedal_concepts_pl.json
+++ b/components/omnibox/resources/omnibox_pedal_concepts_pl.json
@@ -1,7 +1,7 @@
 {
   "schema": "pedal_concepts_runtime",
   "schema_version": 1,
-  "data_version": 15476904,
+  "data_version": 15487260,
   "tokenize_characters": " -",
   "dictionary": [
     "a",
diff --git a/components/omnibox/resources/omnibox_pedal_concepts_pt-BR.json b/components/omnibox/resources/omnibox_pedal_concepts_pt-BR.json
index 38548c8..bb41f35 100644
--- a/components/omnibox/resources/omnibox_pedal_concepts_pt-BR.json
+++ b/components/omnibox/resources/omnibox_pedal_concepts_pt-BR.json
@@ -1,7 +1,7 @@
 {
   "schema": "pedal_concepts_runtime",
   "schema_version": 1,
-  "data_version": 15476904,
+  "data_version": 15487260,
   "tokenize_characters": " -",
   "dictionary": [
     "abrir",
diff --git a/components/omnibox/resources/omnibox_pedal_concepts_pt-PT.json b/components/omnibox/resources/omnibox_pedal_concepts_pt-PT.json
index c973af6..987b407 100644
--- a/components/omnibox/resources/omnibox_pedal_concepts_pt-PT.json
+++ b/components/omnibox/resources/omnibox_pedal_concepts_pt-PT.json
@@ -1,7 +1,7 @@
 {
   "schema": "pedal_concepts_runtime",
   "schema_version": 1,
-  "data_version": 15476904,
+  "data_version": 15487260,
   "tokenize_characters": " -",
   "dictionary": [
     "abrir",
diff --git a/components/omnibox/resources/omnibox_pedal_concepts_ro.json b/components/omnibox/resources/omnibox_pedal_concepts_ro.json
index ff2d42c..11eb186 100644
--- a/components/omnibox/resources/omnibox_pedal_concepts_ro.json
+++ b/components/omnibox/resources/omnibox_pedal_concepts_ro.json
@@ -1,7 +1,7 @@
 {
   "schema": "pedal_concepts_runtime",
   "schema_version": 1,
-  "data_version": 15476904,
+  "data_version": 15487260,
   "tokenize_characters": " -",
   "dictionary": [
     "a",
diff --git a/components/omnibox/resources/omnibox_pedal_concepts_ru.json b/components/omnibox/resources/omnibox_pedal_concepts_ru.json
index 71a07adb..e27c3e0 100644
--- a/components/omnibox/resources/omnibox_pedal_concepts_ru.json
+++ b/components/omnibox/resources/omnibox_pedal_concepts_ru.json
@@ -1,7 +1,7 @@
 {
   "schema": "pedal_concepts_runtime",
   "schema_version": 1,
-  "data_version": 15476904,
+  "data_version": 15487260,
   "tokenize_characters": " -",
   "dictionary": [
     "chrome",
diff --git a/components/omnibox/resources/omnibox_pedal_concepts_sk.json b/components/omnibox/resources/omnibox_pedal_concepts_sk.json
index 1fccbbb..4ee908d 100644
--- a/components/omnibox/resources/omnibox_pedal_concepts_sk.json
+++ b/components/omnibox/resources/omnibox_pedal_concepts_sk.json
@@ -1,7 +1,7 @@
 {
   "schema": "pedal_concepts_runtime",
   "schema_version": 1,
-  "data_version": 15476904,
+  "data_version": 15487260,
   "tokenize_characters": " -",
   "dictionary": [
     "a",
diff --git a/components/omnibox/resources/omnibox_pedal_concepts_sl.json b/components/omnibox/resources/omnibox_pedal_concepts_sl.json
index 600a0f5..0ea6eb25 100644
--- a/components/omnibox/resources/omnibox_pedal_concepts_sl.json
+++ b/components/omnibox/resources/omnibox_pedal_concepts_sl.json
@@ -1,7 +1,7 @@
 {
   "schema": "pedal_concepts_runtime",
   "schema_version": 1,
-  "data_version": 15476904,
+  "data_version": 15487260,
   "tokenize_characters": " -",
   "dictionary": [
     "beleženja",
diff --git a/components/omnibox/resources/omnibox_pedal_concepts_sr.json b/components/omnibox/resources/omnibox_pedal_concepts_sr.json
index 12007622..5705516 100644
--- a/components/omnibox/resources/omnibox_pedal_concepts_sr.json
+++ b/components/omnibox/resources/omnibox_pedal_concepts_sr.json
@@ -1,7 +1,7 @@
 {
   "schema": "pedal_concepts_runtime",
   "schema_version": 1,
-  "data_version": 15476904,
+  "data_version": 15487260,
   "tokenize_characters": " -",
   "dictionary": [
     "chrome",
diff --git a/components/omnibox/resources/omnibox_pedal_concepts_sv.json b/components/omnibox/resources/omnibox_pedal_concepts_sv.json
index 1f68513..b3f5492 100644
--- a/components/omnibox/resources/omnibox_pedal_concepts_sv.json
+++ b/components/omnibox/resources/omnibox_pedal_concepts_sv.json
@@ -1,7 +1,7 @@
 {
   "schema": "pedal_concepts_runtime",
   "schema_version": 1,
-  "data_version": 15476904,
+  "data_version": 15487260,
   "tokenize_characters": " -",
   "dictionary": [
     "aktivera",
diff --git a/components/omnibox/resources/omnibox_pedal_concepts_sw.json b/components/omnibox/resources/omnibox_pedal_concepts_sw.json
index 7319ab3..5f4ae7c7 100644
--- a/components/omnibox/resources/omnibox_pedal_concepts_sw.json
+++ b/components/omnibox/resources/omnibox_pedal_concepts_sw.json
@@ -1,7 +1,7 @@
 {
   "schema": "pedal_concepts_runtime",
   "schema_version": 1,
-  "data_version": 15476904,
+  "data_version": 15487260,
   "tokenize_characters": " -",
   "dictionary": [
     "***",
diff --git a/components/omnibox/resources/omnibox_pedal_concepts_ta.json b/components/omnibox/resources/omnibox_pedal_concepts_ta.json
index 4307658d..9f1e10d 100644
--- a/components/omnibox/resources/omnibox_pedal_concepts_ta.json
+++ b/components/omnibox/resources/omnibox_pedal_concepts_ta.json
@@ -1,7 +1,7 @@
 {
   "schema": "pedal_concepts_runtime",
   "schema_version": 1,
-  "data_version": 15476904,
+  "data_version": 15487260,
   "tokenize_characters": " -",
   "dictionary": [
     "chrome",
diff --git a/components/omnibox/resources/omnibox_pedal_concepts_te.json b/components/omnibox/resources/omnibox_pedal_concepts_te.json
index 7bf182c6..47e7c6f2 100644
--- a/components/omnibox/resources/omnibox_pedal_concepts_te.json
+++ b/components/omnibox/resources/omnibox_pedal_concepts_te.json
@@ -1,7 +1,7 @@
 {
   "schema": "pedal_concepts_runtime",
   "schema_version": 1,
-  "data_version": 15476904,
+  "data_version": 15487260,
   "tokenize_characters": " -",
   "dictionary": [
     "chrome",
diff --git a/components/omnibox/resources/omnibox_pedal_concepts_th.json b/components/omnibox/resources/omnibox_pedal_concepts_th.json
index ce94c5e..ace9faf 100644
--- a/components/omnibox/resources/omnibox_pedal_concepts_th.json
+++ b/components/omnibox/resources/omnibox_pedal_concepts_th.json
@@ -1,7 +1,7 @@
 {
   "schema": "pedal_concepts_runtime",
   "schema_version": 1,
-  "data_version": 15476904,
+  "data_version": 15487260,
   "tokenize_characters": " -",
   "dictionary": [
     "chrome",
diff --git a/components/omnibox/resources/omnibox_pedal_concepts_tr.json b/components/omnibox/resources/omnibox_pedal_concepts_tr.json
index b1eb4f6..274cc950 100644
--- a/components/omnibox/resources/omnibox_pedal_concepts_tr.json
+++ b/components/omnibox/resources/omnibox_pedal_concepts_tr.json
@@ -1,7 +1,7 @@
 {
   "schema": "pedal_concepts_runtime",
   "schema_version": 1,
-  "data_version": 15476904,
+  "data_version": 15487260,
   "tokenize_characters": " -",
   "dictionary": [
     "aç",
diff --git a/components/omnibox/resources/omnibox_pedal_concepts_uk.json b/components/omnibox/resources/omnibox_pedal_concepts_uk.json
index 3b2967a..226116b4 100644
--- a/components/omnibox/resources/omnibox_pedal_concepts_uk.json
+++ b/components/omnibox/resources/omnibox_pedal_concepts_uk.json
@@ -1,7 +1,7 @@
 {
   "schema": "pedal_concepts_runtime",
   "schema_version": 1,
-  "data_version": 15476904,
+  "data_version": 15487260,
   "tokenize_characters": " -",
   "dictionary": [
     "a",
diff --git a/components/omnibox/resources/omnibox_pedal_concepts_vi.json b/components/omnibox/resources/omnibox_pedal_concepts_vi.json
index 98aca47d..46b20a4 100644
--- a/components/omnibox/resources/omnibox_pedal_concepts_vi.json
+++ b/components/omnibox/resources/omnibox_pedal_concepts_vi.json
@@ -1,7 +1,7 @@
 {
   "schema": "pedal_concepts_runtime",
   "schema_version": 1,
-  "data_version": 15476904,
+  "data_version": 15487260,
   "tokenize_characters": " -",
   "dictionary": [
     "a",
diff --git a/components/omnibox/resources/omnibox_pedal_concepts_zh-CN.json b/components/omnibox/resources/omnibox_pedal_concepts_zh-CN.json
index 928ab5e..85f7e94 100644
--- a/components/omnibox/resources/omnibox_pedal_concepts_zh-CN.json
+++ b/components/omnibox/resources/omnibox_pedal_concepts_zh-CN.json
@@ -1,7 +1,7 @@
 {
   "schema": "pedal_concepts_runtime",
   "schema_version": 1,
-  "data_version": 15476904,
+  "data_version": 15487260,
   "tokenize_characters": "",
   "dictionary": [
     " ",
diff --git a/components/omnibox/resources/omnibox_pedal_concepts_zh-TW.json b/components/omnibox/resources/omnibox_pedal_concepts_zh-TW.json
index c4d986e..b789b37 100644
--- a/components/omnibox/resources/omnibox_pedal_concepts_zh-TW.json
+++ b/components/omnibox/resources/omnibox_pedal_concepts_zh-TW.json
@@ -1,7 +1,7 @@
 {
   "schema": "pedal_concepts_runtime",
   "schema_version": 1,
-  "data_version": 15476904,
+  "data_version": 15487260,
   "tokenize_characters": " -",
   "dictionary": [
     "chrome",
diff --git a/components/password_manager/core/browser/form_fetcher_impl.h b/components/password_manager/core/browser/form_fetcher_impl.h
index 7ace0c6..bf9dc91 100644
--- a/components/password_manager/core/browser/form_fetcher_impl.h
+++ b/components/password_manager/core/browser/form_fetcher_impl.h
@@ -98,6 +98,9 @@
   // non-federated matches.
   std::vector<std::unique_ptr<autofill::PasswordForm>> federated_;
 
+  // List of compromised credentials for the current domain.
+  std::vector<CompromisedCredentials> compromised_credentials_;
+
   // Indicates whether HTTP passwords should be migrated to HTTPS.
   const bool should_migrate_http_passwords_;
 
@@ -138,9 +141,6 @@
   // Statistics for the current domain.
   std::vector<InteractionsStats> interactions_stats_;
 
-  // List of compromised credentials for the current domain.
-  std::vector<CompromisedCredentials> compromised_credentials_;
-
   // Consumers of the fetcher, all are assumed to either outlive |this| or
   // remove themselves from the list during their destruction.
   base::ObserverList<FormFetcher::Consumer> consumers_;
diff --git a/components/password_manager/core/browser/multi_store_form_fetcher.cc b/components/password_manager/core/browser/multi_store_form_fetcher.cc
index e46ca51..7bb02c1 100644
--- a/components/password_manager/core/browser/multi_store_form_fetcher.cc
+++ b/components/password_manager/core/browser/multi_store_form_fetcher.cc
@@ -5,6 +5,7 @@
 #include "components/password_manager/core/browser/multi_store_form_fetcher.h"
 
 #include "base/check_op.h"
+#include "base/util/ranges/algorithm.h"
 #include "build/build_config.h"
 #include "components/autofill/core/common/save_password_progress_logger.h"
 #include "components/password_manager/core/browser/password_feature_manager.h"
@@ -55,6 +56,11 @@
   if (account_password_store) {
     state_ = State::WAITING;
     account_password_store->GetLogins(form_digest_, this);
+#if !defined(OS_IOS) && !defined(OS_ANDROID)
+    // The desktop bubble needs this information.
+    account_password_store->GetMatchingCompromisedCredentials(
+        form_digest_.signon_realm, this);
+#endif
   }
 }
 
@@ -158,6 +164,14 @@
   AggregatePasswordStoreResults(std::move(forms));
 }
 
+void MultiStoreFormFetcher::OnGetCompromisedCredentials(
+    std::vector<CompromisedCredentials> compromised_credentials) {
+  // Both the profile and account store has been queried. Therefore, append the
+  // received credentials to the existing ones.
+  util::ranges::move(compromised_credentials,
+                     std::back_inserter(compromised_credentials_));
+}
+
 void MultiStoreFormFetcher::SplitResults(
     std::vector<std::unique_ptr<PasswordForm>> results) {
   // Compute the |is_blacklisted_in_profile_store_| and
diff --git a/components/password_manager/core/browser/multi_store_form_fetcher.h b/components/password_manager/core/browser/multi_store_form_fetcher.h
index cbd89651..5857d6c 100644
--- a/components/password_manager/core/browser/multi_store_form_fetcher.h
+++ b/components/password_manager/core/browser/multi_store_form_fetcher.h
@@ -35,11 +35,15 @@
       PasswordStore* store,
       std::vector<std::unique_ptr<autofill::PasswordForm>> results) override;
 
- private:
   // HttpPasswordStoreMigrator::Consumer:
   void ProcessMigratedForms(
       std::vector<std::unique_ptr<autofill::PasswordForm>> forms) override;
 
+  // CompromisedCredentialsConsumer:
+  void OnGetCompromisedCredentials(
+      std::vector<CompromisedCredentials> compromised_credentials) override;
+
+ private:
   void AggregatePasswordStoreResults(
       std::vector<std::unique_ptr<autofill::PasswordForm>> results);
 
diff --git a/components/password_manager/core/browser/multi_store_form_fetcher_unittest.cc b/components/password_manager/core/browser/multi_store_form_fetcher_unittest.cc
index c285d862..10ff1eb 100644
--- a/components/password_manager/core/browser/multi_store_form_fetcher_unittest.cc
+++ b/components/password_manager/core/browser/multi_store_form_fetcher_unittest.cc
@@ -440,4 +440,28 @@
   EXPECT_FALSE(form_fetcher_->IsMovingBlocked(kUser, psl_form.username_value));
 }
 
+TEST_F(MultiStoreFormFetcherTest, CompromisedCredentials) {
+  Fetch();
+  const CompromisedCredentials profile_store_compromised_credentials{
+      form_digest_.signon_realm, base::ASCIIToUTF16("profile_username"),
+      base::Time::FromTimeT(1), CompromiseType::kLeaked,
+      autofill::PasswordForm::Store::kProfileStore};
+
+  const CompromisedCredentials account_store_compromised_credentials{
+      form_digest_.signon_realm, base::ASCIIToUTF16("account_username"),
+      base::Time::FromTimeT(1), CompromiseType::kLeaked,
+      autofill::PasswordForm::Store::kAccountStore};
+
+  static_cast<CompromisedCredentialsConsumer*>(form_fetcher_.get())
+      ->OnGetCompromisedCredentials({profile_store_compromised_credentials});
+
+  static_cast<CompromisedCredentialsConsumer*>(form_fetcher_.get())
+      ->OnGetCompromisedCredentials({account_store_compromised_credentials});
+
+  EXPECT_THAT(form_fetcher_->GetCompromisedCredentials(),
+              testing::UnorderedElementsAreArray(
+                  {profile_store_compromised_credentials,
+                   account_store_compromised_credentials}));
+}
+
 }  // namespace password_manager
diff --git a/components/policy/resources/policy_templates.json b/components/policy/resources/policy_templates.json
index 543feb3..835c8438 100644
--- a/components/policy/resources/policy_templates.json
+++ b/components/policy/resources/policy_templates.json
@@ -3007,6 +3007,7 @@
       'supported_on': [
         'chrome.*:83-',
         'chrome_os:83-',
+        'android:87-',
       ],
       'future_on': [ 'ios' ],
       'features': {
@@ -3031,6 +3032,7 @@
       If this policy is left not set, Safe Browsing will operate in Standard Protection mode but users can change this setting.
 
       See https://developers.google.com/safe-browsing for more info on Safe Browsing.''',
+      'arc_support': 'This policy is not supported within Arc.',
     },
     {
       'name': 'MetricsReportingEnabled',
diff --git a/content/browser/back_forward_cache_browsertest.cc b/content/browser/back_forward_cache_browsertest.cc
index 32cecbee7..95a9e5cbf 100644
--- a/content/browser/back_forward_cache_browsertest.cc
+++ b/content/browser/back_forward_cache_browsertest.cc
@@ -57,6 +57,7 @@
 #include "content/public/test/test_navigation_throttle.h"
 #include "content/public/test/test_navigation_throttle_inserter.h"
 #include "content/public/test/test_utils.h"
+#include "content/public/test/text_input_test_utils.h"
 #include "content/public/test/url_loader_interceptor.h"
 #include "content/shell/browser/shell.h"
 #include "content/shell/browser/shell_javascript_dialog_manager.h"
@@ -7152,4 +7153,195 @@
   ExpectUniqueSample(kEligibilityDuringCommitHistogramName, false, 1);
 }
 
+// Tests that we're getting the correct TextInputState and focus updates when a
+// page enters the back-forward cache and when it gets restored.
+IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest, TextInputStateUpdated) {
+  ASSERT_TRUE(embedded_test_server()->Start());
+  GURL url_1(embedded_test_server()->GetURL("a.com", "/title1.html"));
+  GURL url_2(embedded_test_server()->GetURL("b.com", "/title2.html"));
+
+  // 1) Navigate to |url_1| and add a text input with "foo" as the value.
+  EXPECT_TRUE(NavigateToURL(shell(), url_1));
+  RenderFrameHostImpl* rfh_1 = current_frame_host();
+  EXPECT_TRUE(ExecJs(rfh_1,
+                     "document.title='bfcached';"
+                     "var input = document.createElement('input');"
+                     "input.setAttribute('type', 'text');"
+                     "input.setAttribute('value', 'foo');"
+                     "document.body.appendChild(input);"
+                     "var focusCount = 0;"
+                     "var blurCount = 0;"
+                     "input.onfocus = () => { focusCount++;};"
+                     "input.onblur = () => { blurCount++; };"));
+
+  {
+    TextInputManagerTypeObserver type_observer(web_contents(),
+                                               ui::TEXT_INPUT_TYPE_TEXT);
+    TextInputManagerValueObserver value_observer(web_contents(), "foo");
+    // 2) Press tab key to focus the <input>, and verify the type & value.
+    SimulateKeyPress(web_contents(), ui::DomKey::TAB, ui::DomCode::TAB,
+                     ui::VKEY_TAB, false, false, false, false);
+    type_observer.Wait();
+    value_observer.Wait();
+
+    EXPECT_EQ(rfh_1, web_contents()->GetFocusedFrame());
+    EXPECT_EQ(EvalJs(rfh_1, "focusCount").ExtractInt(), 1);
+    EXPECT_EQ(EvalJs(rfh_1, "blurCount").ExtractInt(), 0);
+  }
+
+  {
+    TextInputManagerTester tester(web_contents());
+    TextInputManagerValueObserver value_observer(web_contents(), "A");
+    // 3) Press the "A" key to change the text input value. This should notify
+    // the browser that the text input value has changed.
+    SimulateKeyPress(web_contents(), ui::DomKey::FromCharacter('A'),
+                     ui::DomCode::US_A, ui::VKEY_A, false, false, false, false);
+    value_observer.Wait();
+
+    EXPECT_EQ(rfh_1, web_contents()->GetFocusedFrame());
+    EXPECT_EQ(EvalJs(rfh_1, "focusCount").ExtractInt(), 1);
+    EXPECT_EQ(EvalJs(rfh_1, "blurCount").ExtractInt(), 0);
+  }
+
+  {
+    TextInputManagerTypeObserver type_observer(web_contents(),
+                                               ui::TEXT_INPUT_TYPE_NONE);
+    // 4) Navigating to |url_2| should reset type to TEXT_INPUT_TYPE_NONE.
+    EXPECT_TRUE(NavigateToURL(shell(), url_2));
+    type_observer.Wait();
+    // |rfh_1| should get into the back-forward cache.
+    EXPECT_TRUE(rfh_1->IsInBackForwardCache());
+    EXPECT_EQ(current_frame_host(), web_contents()->GetFocusedFrame());
+    EXPECT_NE(rfh_1, web_contents()->GetFocusedFrame());
+  }
+
+  {
+    // 5) Navigating back to |url_1|, we shouldn't restore the focus to the
+    // text input, but |rfh_1| will be focused again as we will restore focus
+    // to main frame after navigation.
+    web_contents()->GetController().GoBack();
+    EXPECT_TRUE(WaitForLoadStop(web_contents()));
+
+    EXPECT_EQ(rfh_1, web_contents()->GetFocusedFrame());
+    EXPECT_EQ(EvalJs(rfh_1, "focusCount").ExtractInt(), 1);
+    EXPECT_EQ(EvalJs(rfh_1, "blurCount").ExtractInt(), 1);
+  }
+
+  {
+    TextInputManagerTypeObserver type_observer(web_contents(),
+                                               ui::TEXT_INPUT_TYPE_TEXT);
+    TextInputManagerValueObserver value_observer(web_contents(), "A");
+    // 6) Press tab key to focus the <input> again. Note that we need to press
+    // the tab key twice here, because the last "tab focus" point was the
+    // <input> element. The first tab key press would focus on the UI/url bar,
+    // then the second tab key would go back to the <input>.
+    SimulateKeyPress(web_contents(), ui::DomKey::TAB, ui::DomCode::TAB,
+                     ui::VKEY_TAB, false, false, false, false);
+    SimulateKeyPress(web_contents(), ui::DomKey::TAB, ui::DomCode::TAB,
+                     ui::VKEY_TAB, false, false, false, false);
+    type_observer.Wait();
+    value_observer.Wait();
+
+    EXPECT_EQ(rfh_1, web_contents()->GetFocusedFrame());
+    EXPECT_EQ(EvalJs(rfh_1, "focusCount").ExtractInt(), 2);
+    EXPECT_EQ(EvalJs(rfh_1, "blurCount").ExtractInt(), 1);
+  }
+}
+
+IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
+                       SubframeTextInputStateUpdated) {
+  ASSERT_TRUE(embedded_test_server()->Start());
+  GURL url_1(embedded_test_server()->GetURL(
+      "a.com", "/cross_site_iframe_factory.html?a(b(a))"));
+  GURL url_2(embedded_test_server()->GetURL("b.com", "/title2.html"));
+
+  // 1) Navigate to |url_1| and add a text input with "foo" as the value in the
+  // a.com subframe.
+  EXPECT_TRUE(NavigateToURL(shell(), url_1));
+  RenderFrameHostImpl* rfh_a = current_frame_host();
+  RenderFrameHostImpl* rfh_b = rfh_a->child_at(0)->current_frame_host();
+  RenderFrameHostImpl* rfh_subframe_a =
+      rfh_b->child_at(0)->current_frame_host();
+  EXPECT_TRUE(ExecJs(rfh_subframe_a,
+                     "var input = document.createElement('input');"
+                     "input.setAttribute('type', 'text');"
+                     "input.setAttribute('value', 'foo');"
+                     "document.body.appendChild(input);"
+                     "var focusCount = 0;"
+                     "var blurCount = 0;"
+                     "input.onfocus = () => { focusCount++;};"
+                     "input.onblur = () => { blurCount++; };"));
+
+  {
+    TextInputManagerTypeObserver type_observer(web_contents(),
+                                               ui::TEXT_INPUT_TYPE_TEXT);
+    TextInputManagerValueObserver value_observer(web_contents(), "foo");
+    // 2) Press tab key to focus the <input>, and verify the type & value.
+    SimulateKeyPress(web_contents(), ui::DomKey::TAB, ui::DomCode::TAB,
+                     ui::VKEY_TAB, false, false, false, false);
+    type_observer.Wait();
+    value_observer.Wait();
+
+    EXPECT_EQ(rfh_subframe_a, web_contents()->GetFocusedFrame());
+    EXPECT_EQ(EvalJs(rfh_subframe_a, "focusCount").ExtractInt(), 1);
+    EXPECT_EQ(EvalJs(rfh_subframe_a, "blurCount").ExtractInt(), 0);
+  }
+
+  {
+    TextInputManagerTester tester(web_contents());
+    TextInputManagerValueObserver value_observer(web_contents(), "A");
+    // 3) Press the "A" key to change the text input value. This should notify
+    // the browser that the text input value has changed.
+    SimulateKeyPress(web_contents(), ui::DomKey::FromCharacter('A'),
+                     ui::DomCode::US_A, ui::VKEY_A, false, false, false, false);
+    value_observer.Wait();
+
+    EXPECT_EQ(rfh_subframe_a, web_contents()->GetFocusedFrame());
+    EXPECT_EQ(EvalJs(rfh_subframe_a, "focusCount").ExtractInt(), 1);
+    EXPECT_EQ(EvalJs(rfh_subframe_a, "blurCount").ExtractInt(), 0);
+  }
+
+  {
+    TextInputManagerTypeObserver type_observer(web_contents(),
+                                               ui::TEXT_INPUT_TYPE_NONE);
+    // 4) Navigating to |url_2| should reset type to TEXT_INPUT_TYPE_NONE and
+    // changed focus to the new page's main frame.
+    EXPECT_TRUE(NavigateToURL(shell(), url_2));
+    type_observer.Wait();
+
+    // |rfh_a| and its subframes should get into the back-forward cache.
+    EXPECT_TRUE(rfh_a->IsInBackForwardCache());
+    EXPECT_TRUE(rfh_b->IsInBackForwardCache());
+    EXPECT_TRUE(rfh_subframe_a->IsInBackForwardCache());
+    EXPECT_EQ(current_frame_host(), web_contents()->GetFocusedFrame());
+  }
+
+  {
+    // 5) Navigating back to |url_1|, we shouldn't restore the focus to the
+    // text input in the subframe (we will focus on the main frame |rfh_a|
+    // instead).
+    web_contents()->GetController().GoBack();
+    EXPECT_TRUE(WaitForLoadStop(web_contents()));
+
+    EXPECT_EQ(rfh_a, web_contents()->GetFocusedFrame());
+    EXPECT_EQ(EvalJs(rfh_subframe_a, "focusCount").ExtractInt(), 1);
+    EXPECT_EQ(EvalJs(rfh_subframe_a, "blurCount").ExtractInt(), 1);
+  }
+
+  {
+    TextInputManagerTypeObserver type_observer(web_contents(),
+                                               ui::TEXT_INPUT_TYPE_TEXT);
+    TextInputManagerValueObserver value_observer(web_contents(), "A");
+    // 6) Press tab key to focus the <input> again.
+    SimulateKeyPress(web_contents(), ui::DomKey::TAB, ui::DomCode::TAB,
+                     ui::VKEY_TAB, false, false, false, false);
+    type_observer.Wait();
+    value_observer.Wait();
+
+    EXPECT_EQ(rfh_subframe_a, web_contents()->GetFocusedFrame());
+    EXPECT_EQ(EvalJs(rfh_subframe_a, "focusCount").ExtractInt(), 2);
+    EXPECT_EQ(EvalJs(rfh_subframe_a, "blurCount").ExtractInt(), 1);
+  }
+}
+
 }  // namespace content
diff --git a/content/browser/frame_host/clipboard_host_impl.cc b/content/browser/frame_host/clipboard_host_impl.cc
index 67d0ae9..bd0e7db03 100644
--- a/content/browser/frame_host/clipboard_host_impl.cc
+++ b/content/browser/frame_host/clipboard_host_impl.cc
@@ -236,6 +236,24 @@
           fragment_end, std::move(callback)));
 }
 
+void ClipboardHostImpl::ReadSvg(ui::ClipboardBuffer clipboard_buffer,
+                                ReadSvgCallback callback) {
+  base::string16 markup;
+  clipboard_->ReadSvg(clipboard_buffer, /*data_dst=*/nullptr, &markup);
+
+  std::string data = base::UTF16ToUTF8(markup);
+  PerformPasteIfAllowed(clipboard_->GetSequenceNumber(clipboard_buffer),
+                        ui::ClipboardFormatType::GetSvgType(), std::move(data),
+                        base::BindOnce(
+                            [](base::string16 markup, ReadSvgCallback callback,
+                               ClipboardPasteAllowed allowed) {
+                              if (!allowed)
+                                markup.clear();
+                              std::move(callback).Run(std::move(markup));
+                            },
+                            std::move(markup), std::move(callback)));
+}
+
 void ClipboardHostImpl::ReadRtf(ui::ClipboardBuffer clipboard_buffer,
                                 ReadRtfCallback callback) {
   std::string result;
@@ -313,6 +331,10 @@
   clipboard_writer_->WriteHTML(markup, url.spec());
 }
 
+void ClipboardHostImpl::WriteSvg(const base::string16& markup) {
+  clipboard_writer_->WriteSvg(markup);
+}
+
 void ClipboardHostImpl::WriteSmartPasteMarker() {
   clipboard_writer_->WriteWebSmartPaste();
 }
diff --git a/content/browser/frame_host/clipboard_host_impl.h b/content/browser/frame_host/clipboard_host_impl.h
index d9d3d9b..9f82c55f 100644
--- a/content/browser/frame_host/clipboard_host_impl.h
+++ b/content/browser/frame_host/clipboard_host_impl.h
@@ -144,6 +144,8 @@
                 ReadTextCallback callback) override;
   void ReadHtml(ui::ClipboardBuffer clipboard_buffer,
                 ReadHtmlCallback callback) override;
+  void ReadSvg(ui::ClipboardBuffer clipboard_buffer,
+               ReadSvgCallback callback) override;
   void ReadRtf(ui::ClipboardBuffer clipboard_buffer,
                ReadRtfCallback callback) override;
   void ReadImage(ui::ClipboardBuffer clipboard_buffer,
@@ -153,6 +155,7 @@
                       ReadCustomDataCallback callback) override;
   void WriteText(const base::string16& text) override;
   void WriteHtml(const base::string16& markup, const GURL& url) override;
+  void WriteSvg(const base::string16& markup) override;
   void WriteSmartPasteMarker() override;
   void WriteCustomData(
       const base::flat_map<base::string16, base::string16>& data) override;
diff --git a/content/browser/media/capture/web_contents_video_capture_device_browsertest.cc b/content/browser/media/capture/web_contents_video_capture_device_browsertest.cc
index 0408bac8..7c3bd25 100644
--- a/content/browser/media/capture/web_contents_video_capture_device_browsertest.cc
+++ b/content/browser/media/capture/web_contents_video_capture_device_browsertest.cc
@@ -402,8 +402,8 @@
 // compositing, whether the WebContents is visible/hidden or occluded/unoccluded
 // and whether the main document contains a cross-site iframe.
 
-// Fails on LACROS only. http://crbug.com/1108205
-#if BUILDFLAG(IS_LACROS)
+// Fails on LACROS and linux. http://crbug.com/1108205
+#if BUILDFLAG(IS_LACROS) || defined(OS_LINUX)
 #define MAYBE_CapturesContentChanges DISABLED_CapturesContentChanges
 #else
 #define MAYBE_CapturesContentChanges CapturesContentChanges
diff --git a/content/browser/renderer_host/render_widget_host_view_aura_vk_browsertest.cc b/content/browser/renderer_host/render_widget_host_view_aura_vk_browsertest.cc
index e81a468b..ede76b7 100644
--- a/content/browser/renderer_host/render_widget_host_view_aura_vk_browsertest.cc
+++ b/content/browser/renderer_host/render_widget_host_view_aura_vk_browsertest.cc
@@ -31,50 +31,6 @@
 
 namespace content {
 
-// TextInputManager Observers
-
-// A base class for observing the TextInputManager owned by the given
-// WebContents. Subclasses could observe the TextInputManager for different
-// changes. The class wraps a public tester which accepts callbacks that
-// are run after specific changes in TextInputManager. Different observers can
-// be subclassed from this by providing their specific callback methods.
-class TextInputManagerObserverBase {
- public:
-  explicit TextInputManagerObserverBase(WebContents* web_contents)
-      : tester_(std::make_unique<TextInputManagerTester>(web_contents)) {}
-  virtual ~TextInputManagerObserverBase() = default;
-
-  TextInputManagerObserverBase(const TextInputManagerObserverBase&) = delete;
-  TextInputManagerObserverBase operator=(const TextInputManagerObserverBase&) =
-      delete;
-
-  // Wait for derived class's definition of success.
-  void Wait() {
-    if (success_)
-      return;
-    run_loop.Run();
-  }
-
-  bool success() const { return success_; }
-
- protected:
-  TextInputManagerTester* tester() { return tester_.get(); }
-
-  void OnSuccess() {
-    success_ = true;
-    run_loop.Quit();
-
-    // By deleting |tester_| we make sure that the internal observer used in
-    // content/ is removed from the observer list of TextInputManager.
-    tester_.reset(nullptr);
-  }
-
- private:
-  std::unique_ptr<TextInputManagerTester> tester_;
-  bool success_ = false;
-  base::RunLoop run_loop;
-};
-
 // This class observes TextInputManager for changes in
 // |TextInputState.vk_policy|.
 class TextInputManagerVkPolicyObserver : public TextInputManagerObserverBase {
diff --git a/content/browser/webrtc/webrtc_audio_debug_recordings_browsertest.cc b/content/browser/webrtc/webrtc_audio_debug_recordings_browsertest.cc
index 8b0c52d..21013c613 100644
--- a/content/browser/webrtc/webrtc_audio_debug_recordings_browsertest.cc
+++ b/content/browser/webrtc/webrtc_audio_debug_recordings_browsertest.cc
@@ -84,7 +84,7 @@
   ~WebRtcAudioDebugRecordingsBrowserTest() override {}
 };
 
-#if defined(OS_ANDROID) || defined(OS_LINUX)
+#if defined(OS_ANDROID) || defined(OS_LINUX) || defined(OS_CHROMEOS)
 // Renderer crashes under Android ASAN: https://crbug.com/408496.
 // Renderer crashes under Android: https://crbug.com/820934.
 // Failures on Android M. https://crbug.com/535728.
diff --git a/content/browser/webrtc/webrtc_data_browsertest.cc b/content/browser/webrtc/webrtc_data_browsertest.cc
index 74f48645e..849914c 100644
--- a/content/browser/webrtc/webrtc_data_browsertest.cc
+++ b/content/browser/webrtc/webrtc_data_browsertest.cc
@@ -67,7 +67,7 @@
 // dataChannel and audio and video tracks.
 // TODO(mallinath) - Remove this test after rtp based data channel is disabled.
 // Flaky. crbug.com/986872
-#if defined(OS_LINUX) || defined(OS_WIN)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_WIN)
 #define MAYBE_CallWithDataAndMedia DISABLED_CallWithDataAndMedia
 #else
 #define MAYBE_CallWithDataAndMedia CallWithDataAndMedia
diff --git a/content/browser/webrtc/webrtc_getusermedia_browsertest.cc b/content/browser/webrtc/webrtc_getusermedia_browsertest.cc
index f5df371..463be0c7 100644
--- a/content/browser/webrtc/webrtc_getusermedia_browsertest.cc
+++ b/content/browser/webrtc/webrtc_getusermedia_browsertest.cc
@@ -275,7 +275,7 @@
 }
 
 // TODO(crbug.com/571389): Flaky on TSAN bots.
-#if defined(OS_LINUX)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS)
 #define MAYBE_GetUserMediaWithMandatorySourceID \
   DISABLED_GetUserMediaWithMandatorySourceID
 #else
@@ -739,7 +739,7 @@
 
 // Flaky on Win, see https://crbug.com/915135
 // Flaky on Linux, see https://crbug.com/952381
-#if defined(OS_WIN) || defined(OS_LINUX)
+#if defined(OS_WIN) || defined(OS_LINUX) || defined(OS_CHROMEOS)
 #define MAYBE_ApplyConstraintsNonDevice DISABLED_ApplyConstraintsNonDevice
 #else
 #define MAYBE_ApplyConstraintsNonDevice ApplyConstraintsNonDevice
diff --git a/content/browser/webrtc/webrtc_image_capture_browsertest.cc b/content/browser/webrtc/webrtc_image_capture_browsertest.cc
index 84e4cb4..7c56e681 100644
--- a/content/browser/webrtc/webrtc_image_capture_browsertest.cc
+++ b/content/browser/webrtc/webrtc_image_capture_browsertest.cc
@@ -42,13 +42,13 @@
 
 // TODO(crbug.com/793859, crbug.com/986602): This test is broken on Android
 // (see above) and flaky on Linux.
-#if defined(OS_ANDROID) || defined(OS_LINUX)
+#if defined(OS_ANDROID) || defined(OS_LINUX) || defined(OS_CHROMEOS)
 #define MAYBE_ManipulateExposureTime DISABLED_ManipulateExposureTime
 #else
 #define MAYBE_ManipulateExposureTime ManipulateExposureTime
 #endif
 
-#if defined(OS_LINUX)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS)
 // See crbug/986470
 #define MAYBE_GetPhotoSettings DISABLED_GetPhotoSettings
 #define MAYBE_GetTrackSettings DISABLED_GetTrackSettings
@@ -191,7 +191,7 @@
 };
 
 // TODO(crbug.com/998305): Flaky on Linux.
-#if defined(OS_LINUX)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS)
 #define MAYBE_GetPhotoCapabilities DISABLED_GetPhotoCapabilities
 #else
 #define MAYBE_GetPhotoCapabilities GetPhotoCapabilities
@@ -221,7 +221,7 @@
 }
 
 // Flaky. crbug.com/998116
-#if defined(OS_LINUX)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS)
 #define MAYBE_GetTrackCapabilities DISABLED_GetTrackCapabilities
 #else
 #define MAYBE_GetTrackCapabilities GetTrackCapabilities
@@ -247,7 +247,7 @@
 // TODO(crbug.com/998304): Flaky on Linux.
 // TODO(crbug.com/793859): Re-enable test on Android as soon as the cause for
 // the bug is understood and fixed.
-#if defined(OS_LINUX) || defined(OS_ANDROID)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_ANDROID)
 #define MAYBE_ManipulateTilt DISABLED_ManipulateTilt
 #else
 #define MAYBE_ManipulateTilt ManipulateTilt
@@ -289,8 +289,8 @@
 // API has already been implemented.
 // Note, these tests must be run sequentially, since multiple parallel test runs
 // competing for a single physical webcam typically causes failures.
-#if defined(OS_LINUX) || defined(OS_MAC) || defined(OS_ANDROID) || \
-    defined(OS_WIN)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_MAC) || \
+    defined(OS_ANDROID) || defined(OS_WIN)
 
 const TargetVideoCaptureImplementation
     kTargetVideoCaptureImplementationsForRealWebcam[] = {
diff --git a/content/browser/webrtc/webrtc_media_recorder_browsertest.cc b/content/browser/webrtc/webrtc_media_recorder_browsertest.cc
index 04263ca..c2f4105 100644
--- a/content/browser/webrtc/webrtc_media_recorder_browsertest.cc
+++ b/content/browser/webrtc/webrtc_media_recorder_browsertest.cc
@@ -125,7 +125,7 @@
 }
 
 // TODO(crbug.com/571389): Flaky on TSAN bots.
-#if defined(OS_LINUX)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS)
 #define MAYBE_PauseStop DISABLED_PauseStop
 #else
 #define MAYBE_PauseStop PauseStop
@@ -141,7 +141,7 @@
 }
 
 // TODO (crbug.com/736268): Flaky on Linux TSan bots.
-#if defined(OS_LINUX)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS)
 #define MAYBE_IllegalPauseThrowsDOMError DISABLED_IllegalPauseThrowsDOMError
 #else
 #define MAYBE_IllegalPauseThrowsDOMError IllegalPauseThrowsDOMError
@@ -181,7 +181,7 @@
 }
 
 // Flaky on Linux Tsan (crbug.com/736268)
-#if defined(OS_LINUX)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS)
 #define MAYBE_IllegalRequestDataThrowsDOMError \
   DISABLED_IllegalRequestDataThrowsDOMError
 #else
@@ -197,7 +197,7 @@
 // These tests are flakily timing out on emulators (https://crbug.com/716691)
 // and/or under Android ASAN (https://crbug.com/693565);
 #define MAYBE_PeerConnection DISABLED_PeerConnection
-#elif defined(OS_LINUX) && defined(THREAD_SANITIZER)
+#elif (defined(OS_LINUX) || defined(OS_CHROMEOS)) && defined(THREAD_SANITIZER)
 // Flaky on Linux TSan, https://crbug.com/694373.
 #define MAYBE_PeerConnection DISABLED_PeerConnection
 #elif defined(OS_WIN) && !defined(NDEBUG)
@@ -215,7 +215,7 @@
 }
 
 // Flaky on Linux Tsan (crbug.com/736268)
-#if defined(OS_LINUX)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS)
 #define MAYBE_AddingTrackToMediaStreamFiresErrorEvent \
   DISABLED_AddingTrackToMediaStreamFiresErrorEvent
 #else
diff --git a/content/browser/webrtc/webrtc_stress_image_capture_browsertest.cc b/content/browser/webrtc/webrtc_stress_image_capture_browsertest.cc
index 04a6a05..7fc2c5b 100644
--- a/content/browser/webrtc/webrtc_stress_image_capture_browsertest.cc
+++ b/content/browser/webrtc/webrtc_stress_image_capture_browsertest.cc
@@ -105,8 +105,8 @@
 // API has already been implemented.
 // Note, these tests must be run sequentially, since multiple parallel test runs
 // competing for a single physical webcam typically causes failures.
-#if defined(OS_LINUX) || defined(OS_MAC) || defined(OS_ANDROID) || \
-    defined(OS_WIN)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_MAC) || \
+    defined(OS_ANDROID) || defined(OS_WIN)
 
 const TargetVideoCaptureImplementation
     kTargetVideoCaptureImplementationsForRealWebcam[] = {
diff --git a/content/browser/webrtc/webrtc_video_capture_shared_device_browsertest.cc b/content/browser/webrtc/webrtc_video_capture_shared_device_browsertest.cc
index 0579bf5..337f53f 100644
--- a/content/browser/webrtc/webrtc_video_capture_shared_device_browsertest.cc
+++ b/content/browser/webrtc/webrtc_video_capture_shared_device_browsertest.cc
@@ -282,7 +282,7 @@
         TestParams {
           ServiceApi::kMultiClient, media::VideoCaptureBufferType::kSharedMemory
         }
-#if defined(OS_LINUX)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS)
         ,
         TestParams{
             ServiceApi::kSingleClient,
@@ -291,7 +291,7 @@
           ServiceApi::kMultiClient,
               media::VideoCaptureBufferType::kSharedMemoryViaRawFileDescriptor
         }
-#endif  // defined(OS_LINUX)
+#endif  // defined(OS_LINUX) || defined(OS_CHROMEOS)
         ));
 
 }  // namespace content
diff --git a/content/child/runtime_features.cc b/content/child/runtime_features.cc
index f47393b..84ec87f 100644
--- a/content/child/runtime_features.cc
+++ b/content/child/runtime_features.cc
@@ -604,6 +604,7 @@
   // TODO(rodneyding): Investigate more on proper treatments of
   // these features.
   if (enable_experimental_web_platform_features) {
+    WebRuntimeFeatures::EnableCompositingOptimizations(true);
     WebRuntimeFeatures::EnableNetInfoDownlinkMax(true);
     WebRuntimeFeatures::EnableSignedExchangePrefetchCacheForNavigations(true);
     WebRuntimeFeatures::EnableSignedExchangeSubresourcePrefetch(true);
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/JavaBridgeActivityTestRule.java b/content/public/android/javatests/src/org/chromium/content/browser/JavaBridgeActivityTestRule.java
index 850efea0..f0d153f 100644
--- a/content/public/android/javatests/src/org/chromium/content/browser/JavaBridgeActivityTestRule.java
+++ b/content/public/android/javatests/src/org/chromium/content/browser/JavaBridgeActivityTestRule.java
@@ -11,6 +11,8 @@
 import org.chromium.base.Log;
 import org.chromium.base.test.SetUpStatement;
 import org.chromium.base.test.SetUpTestRule;
+import org.chromium.base.test.params.ParameterProvider;
+import org.chromium.base.test.params.ParameterSet;
 import org.chromium.base.test.util.UrlUtils;
 import org.chromium.content_public.browser.JavascriptInjector;
 import org.chromium.content_public.browser.LoadUrlParams;
@@ -20,14 +22,44 @@
 import org.chromium.content_shell_apk.ContentShellActivityTestRule;
 
 import java.lang.annotation.Annotation;
+import java.util.Arrays;
+import java.util.List;
 
 /**
  * ActivityTestRule with common functionality for testing the Java Bridge.
  */
 public class JavaBridgeActivityTestRule
         extends ContentShellActivityTestRule implements SetUpTestRule<JavaBridgeActivityTestRule> {
+    /**
+     * {@link ParameterProvider} used for parameterized test that provides the Mojo usage state.
+     */
+    public static class MojoTestParams implements ParameterProvider {
+        private static List<ParameterSet> sMojoTestParams =
+                Arrays.asList(new ParameterSet().value(false).name("MojoUnused"),
+                        new ParameterSet().value(true).name("MojoUsed"));
+
+        @Override
+        public List<ParameterSet> getParameters() {
+            return sMojoTestParams;
+        }
+    }
+
+    /**
+     * {@link ParameterProvider} used for parameterized test that keeps the legacy tests.
+     */
+    public static class LegacyTestParams implements ParameterProvider {
+        private static List<ParameterSet> sLegacyTestParams =
+                Arrays.asList(new ParameterSet().value(false));
+
+        @Override
+        public List<ParameterSet> getParameters() {
+            return sLegacyTestParams;
+        }
+    }
+
     private TestCallbackHelperContainer mTestCallbackHelperContainer;
     private boolean mSetup;
+    private boolean mUseMojo;
 
     public static class Controller {
         private static final int RESULT_WAIT_TIME = 5000;
@@ -116,7 +148,8 @@
                 @Override
                 public void run() {
                     WebContents webContents = getWebContents();
-                    JavascriptInjector injector = JavascriptInjector.fromWebContents(webContents);
+                    JavascriptInjector injector =
+                            JavascriptInjector.fromWebContents(webContents, mUseMojo);
                     injector.addPossiblyUnsafeInterface(object1, name1, requiredAnnotation);
                     if (object2 != null && name2 != null) {
                         injector.addPossiblyUnsafeInterface(object2, name2, requiredAnnotation);
@@ -130,6 +163,9 @@
                     "Failed to injectObjectsAndReload: " + Log.getStackTraceString(e));
         }
     }
+    public void setupMojoTest(boolean useMojo) {
+        mUseMojo = useMojo;
+    }
 
     public void synchronousPageReload() throws Throwable {
         TestCallbackHelperContainer.OnPageFinishedHelper onPageFinishedHelper =
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/JavaBridgeArrayCoercionTest.java b/content/public/android/javatests/src/org/chromium/content/browser/JavaBridgeArrayCoercionTest.java
index 01125bc0..f942e47 100644
--- a/content/public/android/javatests/src/org/chromium/content/browser/JavaBridgeArrayCoercionTest.java
+++ b/content/public/android/javatests/src/org/chromium/content/browser/JavaBridgeArrayCoercionTest.java
@@ -12,7 +12,11 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import org.chromium.base.test.BaseJUnit4ClassRunner;
+import org.chromium.base.test.params.BaseJUnit4RunnerDelegate;
+import org.chromium.base.test.params.ParameterAnnotations.UseMethodParameter;
+import org.chromium.base.test.params.ParameterAnnotations.UseMethodParameterBefore;
+import org.chromium.base.test.params.ParameterAnnotations.UseRunnerDelegate;
+import org.chromium.base.test.params.ParameterizedRunner;
 import org.chromium.base.test.util.Feature;
 import org.chromium.content.browser.JavaBridgeActivityTestRule.Controller;
 
@@ -27,7 +31,8 @@
  * FIXME: Consider making our implementation more compliant, if it will not
  * break backwards-compatibility. See b/4408210.
  */
-@RunWith(BaseJUnit4ClassRunner.class)
+@RunWith(ParameterizedRunner.class)
+@UseRunnerDelegate(BaseJUnit4RunnerDelegate.class)
 public class JavaBridgeArrayCoercionTest {
     private static final double ASSERTION_DELTA = 0;
 
@@ -158,6 +163,11 @@
     private static class CustomType {
     }
 
+    @UseMethodParameterBefore(JavaBridgeActivityTestRule.MojoTestParams.class)
+    public void setupMojoTest(boolean useMojo) {
+        mActivityTestRule.setupMojoTest(useMojo);
+    }
+
     private TestObject mTestObject;
 
     @Before
@@ -174,7 +184,8 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testPassNumberInt32() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.MojoTestParams.class)
+    public void testPassNumberInt32(boolean useMojo) throws Throwable {
         mActivityTestRule.executeJavaScript("testObject.setBooleanArray([0]);");
         Assert.assertFalse(mTestObject.waitForBooleanArray()[0]);
         // LIVECONNECT_COMPLIANCE: Should convert to boolean.
@@ -220,7 +231,8 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testPassNumberDouble() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.MojoTestParams.class)
+    public void testPassNumberDouble(boolean useMojo) throws Throwable {
         // LIVECONNECT_COMPLIANCE: Should convert to boolean.
         mActivityTestRule.executeJavaScript("testObject.setBooleanArray([42.1]);");
         Assert.assertFalse(mTestObject.waitForBooleanArray()[0]);
@@ -265,7 +277,8 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testPassNumberNaN() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.MojoTestParams.class)
+    public void testPassNumberNaN(boolean useMojo) throws Throwable {
         mActivityTestRule.executeJavaScript("testObject.setBooleanArray([Number.NaN]);");
         Assert.assertFalse(mTestObject.waitForBooleanArray()[0]);
 
@@ -308,7 +321,8 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testPassNumberInfinity() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.MojoTestParams.class)
+    public void testPassNumberInfinity(boolean useMojo) throws Throwable {
         mActivityTestRule.executeJavaScript("testObject.setBooleanArray([Infinity]);");
         Assert.assertFalse(mTestObject.waitForBooleanArray()[0]);
 
@@ -354,7 +368,8 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testPassBoolean() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.MojoTestParams.class)
+    public void testPassBoolean(boolean useMojo) throws Throwable {
         mActivityTestRule.executeJavaScript("testObject.setBooleanArray([true]);");
         Assert.assertTrue(mTestObject.waitForBooleanArray()[0]);
         mActivityTestRule.executeJavaScript("testObject.setBooleanArray([false]);");
@@ -420,7 +435,8 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testPassString() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.MojoTestParams.class)
+    public void testPassString(boolean useMojo) throws Throwable {
         // LIVECONNECT_COMPLIANCE: Non-empty string should convert to true.
         mActivityTestRule.executeJavaScript("testObject.setBooleanArray([\"+042.10\"]);");
         Assert.assertFalse(mTestObject.waitForBooleanArray()[0]);
@@ -470,7 +486,8 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testPassJavaScriptObject() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.MojoTestParams.class)
+    public void testPassJavaScriptObject(boolean useMojo) throws Throwable {
         // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception.
         mActivityTestRule.executeJavaScript("testObject.setBooleanArray([{foo: 42}]);");
         Assert.assertFalse(mTestObject.waitForBooleanArray()[0]);
@@ -521,7 +538,8 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testPassJavaObject() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.MojoTestParams.class)
+    public void testPassJavaObject(boolean useMojo) throws Throwable {
         // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception.
         mActivityTestRule.executeJavaScript(
                 "testObject.setBooleanArray([testObject.getObjectInstance()]);");
@@ -586,7 +604,8 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testPassNull() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.MojoTestParams.class)
+    public void testPassNull(boolean useMojo) throws Throwable {
         mActivityTestRule.executeJavaScript("testObject.setByteArray([null]);");
         Assert.assertEquals(0, mTestObject.waitForByteArray()[0]);
 
@@ -628,7 +647,8 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testPassUndefined() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.MojoTestParams.class)
+    public void testPassUndefined(boolean useMojo) throws Throwable {
         mActivityTestRule.executeJavaScript("testObject.setByteArray([undefined]);");
         Assert.assertEquals(0, mTestObject.waitForByteArray()[0]);
 
@@ -669,7 +689,8 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testPassInt8Array() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.LegacyTestParams.class)
+    public void testPassInt8Array(boolean useMojo) throws Throwable {
         mActivityTestRule.executeJavaScript("buffer = new ArrayBuffer(1);");
         mActivityTestRule.executeJavaScript("int8_array = new Int8Array(buffer);");
         mActivityTestRule.executeJavaScript("int8_array[0] = 42;");
@@ -712,7 +733,8 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testPassUint8Array() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.LegacyTestParams.class)
+    public void testPassUint8Array(boolean useMojo) throws Throwable {
         mActivityTestRule.executeJavaScript("buffer = new ArrayBuffer(1);");
         mActivityTestRule.executeJavaScript("uint8_array = new Uint8Array(buffer);");
         mActivityTestRule.executeJavaScript("uint8_array[0] = 42;");
@@ -755,7 +777,8 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testPassInt16Array() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.LegacyTestParams.class)
+    public void testPassInt16Array(boolean useMojo) throws Throwable {
         mActivityTestRule.executeJavaScript("buffer = new ArrayBuffer(2);");
         mActivityTestRule.executeJavaScript("int16_array = new Int16Array(buffer);");
         mActivityTestRule.executeJavaScript("int16_array[0] = 42;");
@@ -798,7 +821,8 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testPassUint16Array() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.LegacyTestParams.class)
+    public void testPassUint16Array(boolean useMojo) throws Throwable {
         mActivityTestRule.executeJavaScript("buffer = new ArrayBuffer(2);");
         mActivityTestRule.executeJavaScript("uint16_array = new Uint16Array(buffer);");
         mActivityTestRule.executeJavaScript("uint16_array[0] = 42;");
@@ -841,7 +865,8 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testPassInt32Array() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.LegacyTestParams.class)
+    public void testPassInt32Array(boolean useMojo) throws Throwable {
         mActivityTestRule.executeJavaScript("buffer = new ArrayBuffer(4);");
         mActivityTestRule.executeJavaScript("int32_array = new Int32Array(buffer);");
         mActivityTestRule.executeJavaScript("int32_array[0] = 42;");
@@ -884,7 +909,8 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testPassUint32Array() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.LegacyTestParams.class)
+    public void testPassUint32Array(boolean useMojo) throws Throwable {
         mActivityTestRule.executeJavaScript("buffer = new ArrayBuffer(4);");
         mActivityTestRule.executeJavaScript("uint32_array = new Uint32Array(buffer);");
         mActivityTestRule.executeJavaScript("uint32_array[0] = 42;");
@@ -927,7 +953,8 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testPassFloat32Array() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.LegacyTestParams.class)
+    public void testPassFloat32Array(boolean useMojo) throws Throwable {
         mActivityTestRule.executeJavaScript("buffer = new ArrayBuffer(4);");
         mActivityTestRule.executeJavaScript("float32_array = new Float32Array(buffer);");
         mActivityTestRule.executeJavaScript("float32_array[0] = 42.0;");
@@ -970,7 +997,8 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testPassFloat64Array() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.LegacyTestParams.class)
+    public void testPassFloat64Array(boolean useMojo) throws Throwable {
         mActivityTestRule.executeJavaScript("buffer = new ArrayBuffer(8);");
         mActivityTestRule.executeJavaScript("float64_array = new Float64Array(buffer);");
         mActivityTestRule.executeJavaScript("float64_array[0] = 42.0;");
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/JavaBridgeArrayTest.java b/content/public/android/javatests/src/org/chromium/content/browser/JavaBridgeArrayTest.java
index 721d510..3c1b3476 100644
--- a/content/public/android/javatests/src/org/chromium/content/browser/JavaBridgeArrayTest.java
+++ b/content/public/android/javatests/src/org/chromium/content/browser/JavaBridgeArrayTest.java
@@ -12,7 +12,11 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import org.chromium.base.test.BaseJUnit4ClassRunner;
+import org.chromium.base.test.params.BaseJUnit4RunnerDelegate;
+import org.chromium.base.test.params.ParameterAnnotations.UseMethodParameter;
+import org.chromium.base.test.params.ParameterAnnotations.UseMethodParameterBefore;
+import org.chromium.base.test.params.ParameterAnnotations.UseRunnerDelegate;
+import org.chromium.base.test.params.ParameterizedRunner;
 import org.chromium.base.test.util.Feature;
 import org.chromium.content.browser.JavaBridgeActivityTestRule.Controller;
 
@@ -26,7 +30,8 @@
  * FIXME: Consider making our implementation more compliant, if it will not
  * break backwards-compatibility. See b/4408210.
  */
-@RunWith(BaseJUnit4ClassRunner.class)
+@RunWith(ParameterizedRunner.class)
+@UseRunnerDelegate(BaseJUnit4RunnerDelegate.class)
 public class JavaBridgeArrayTest {
     @Rule
     public JavaBridgeActivityTestRule mActivityTestRule =
@@ -96,6 +101,11 @@
         }
     }
 
+    @UseMethodParameterBefore(JavaBridgeActivityTestRule.MojoTestParams.class)
+    public void setupMojoTest(boolean useMojo) {
+        mActivityTestRule.setupMojoTest(useMojo);
+    }
+
     private TestObject mTestObject;
 
     @Before
@@ -107,7 +117,8 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testArrayLength() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.MojoTestParams.class)
+    public void testArrayLength(boolean useMojo) throws Throwable {
         mActivityTestRule.executeJavaScript("testObject.setIntArray([42, 43, 44]);");
         int[] result = mTestObject.waitForIntArray();
         Assert.assertEquals(3, result.length);
@@ -119,7 +130,8 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testPassNull() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.MojoTestParams.class)
+    public void testPassNull(boolean useMojo) throws Throwable {
         mActivityTestRule.executeJavaScript("testObject.setIntArray(null);");
         Assert.assertNull(mTestObject.waitForIntArray());
     }
@@ -127,7 +139,8 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testPassUndefined() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.MojoTestParams.class)
+    public void testPassUndefined(boolean useMojo) throws Throwable {
         mActivityTestRule.executeJavaScript("testObject.setIntArray(undefined);");
         Assert.assertNull(mTestObject.waitForIntArray());
     }
@@ -135,7 +148,8 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testPassEmptyArray() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.MojoTestParams.class)
+    public void testPassEmptyArray(boolean useMojo) throws Throwable {
         mActivityTestRule.executeJavaScript("testObject.setIntArray([]);");
         Assert.assertEquals(0, mTestObject.waitForIntArray().length);
     }
@@ -145,7 +159,8 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testPassArrayToStringMethod() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.MojoTestParams.class)
+    public void testPassArrayToStringMethod(boolean useMojo) throws Throwable {
         // LIVECONNECT_COMPLIANCE: Should call toString() on array.
         mActivityTestRule.executeJavaScript("testObject.setStringValue([42, 42, 42]);");
         Assert.assertEquals("undefined", mTestObject.waitForStringValue());
@@ -156,7 +171,8 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testPassArrayToNonStringNonArrayMethod() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.MojoTestParams.class)
+    public void testPassArrayToNonStringNonArrayMethod(boolean useMojo) throws Throwable {
         // LIVECONNECT_COMPLIANCE: Should raise JavaScript exception.
         mActivityTestRule.executeJavaScript("testObject.setIntValue([42, 42, 42]);");
         Assert.assertEquals(0, mTestObject.waitForIntValue());
@@ -165,7 +181,8 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testPassNonArrayToArrayMethod() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.MojoTestParams.class)
+    public void testPassNonArrayToArrayMethod(boolean useMojo) throws Throwable {
         // LIVECONNECT_COMPLIANCE: Should raise JavaScript exception.
         mActivityTestRule.executeJavaScript("testObject.setIntArray(42);");
         Assert.assertNull(mTestObject.waitForIntArray());
@@ -174,7 +191,8 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testObjectWithLengthProperty() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.LegacyTestParams.class)
+    public void testObjectWithLengthProperty(boolean useMojo) throws Throwable {
         mActivityTestRule.executeJavaScript("testObject.setIntArray({length: 3, 1: 42});");
         int[] result = mTestObject.waitForIntArray();
         Assert.assertEquals(3, result.length);
@@ -186,7 +204,8 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testNonNumericLengthProperty() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.LegacyTestParams.class)
+    public void testNonNumericLengthProperty(boolean useMojo) throws Throwable {
         // LIVECONNECT_COMPLIANCE: This should not count as an array, so we
         // should raise a JavaScript exception.
         mActivityTestRule.executeJavaScript("testObject.setIntArray({length: \"foo\"});");
@@ -196,7 +215,8 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testLengthOutOfBounds() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.LegacyTestParams.class)
+    public void testLengthOutOfBounds(boolean useMojo) throws Throwable {
         // LIVECONNECT_COMPLIANCE: This should not count as an array, so we
         // should raise a JavaScript exception.
         mActivityTestRule.executeJavaScript("testObject.setIntArray({length: -1});");
@@ -218,7 +238,8 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testSparseArray() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.MojoTestParams.class)
+    public void testSparseArray(boolean useMojo) throws Throwable {
         mActivityTestRule.executeJavaScript(
                 "var x = [42, 43]; x[3] = 45; testObject.setIntArray(x);");
         int[] result = mTestObject.waitForIntArray();
@@ -234,7 +255,8 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testMethodReturningArrayNotCalled() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.MojoTestParams.class)
+    public void testMethodReturningArrayNotCalled(boolean useMojo) throws Throwable {
         // We don't invoke methods which return arrays, but note that no
         // exception is raised.
         // LIVECONNECT_COMPLIANCE: Should call method and convert result to
@@ -248,7 +270,8 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testMultiDimensionalArrayMethod() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.MojoTestParams.class)
+    public void testMultiDimensionalArrayMethod(boolean useMojo) throws Throwable {
         // LIVECONNECT_COMPLIANCE: Should handle multi-dimensional arrays.
         mActivityTestRule.executeJavaScript("testObject.setIntIntArray([ [42, 43], [44, 45] ]);");
         Assert.assertNull(mTestObject.waitForIntIntArray());
@@ -257,7 +280,8 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testPassMultiDimensionalArray() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.MojoTestParams.class)
+    public void testPassMultiDimensionalArray(boolean useMojo) throws Throwable {
         // LIVECONNECT_COMPLIANCE: Should handle multi-dimensional arrays.
         mActivityTestRule.executeJavaScript("testObject.setIntArray([ [42, 43], [44, 45] ]);");
         int[] result = mTestObject.waitForIntArray();
@@ -272,7 +296,8 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testPassArrayBuffer() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.LegacyTestParams.class)
+    public void testPassArrayBuffer(boolean useMojo) throws Throwable {
         mActivityTestRule.executeJavaScript("buffer = new ArrayBuffer(16);");
         mActivityTestRule.executeJavaScript("testObject.setIntArray(buffer);");
         Assert.assertNull(mTestObject.waitForIntArray());
@@ -287,7 +312,8 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testPassDataView() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.LegacyTestParams.class)
+    public void testPassDataView(boolean useMojo) throws Throwable {
         mActivityTestRule.executeJavaScript("buffer = new ArrayBuffer(16);");
         mActivityTestRule.executeJavaScript("testObject.setIntArray(new DataView(buffer));");
         Assert.assertNull(mTestObject.waitForIntArray());
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/JavaBridgeBareboneTest.java b/content/public/android/javatests/src/org/chromium/content/browser/JavaBridgeBareboneTest.java
index a28e11e6..660140fd 100644
--- a/content/public/android/javatests/src/org/chromium/content/browser/JavaBridgeBareboneTest.java
+++ b/content/public/android/javatests/src/org/chromium/content/browser/JavaBridgeBareboneTest.java
@@ -12,7 +12,12 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import org.chromium.base.test.BaseJUnit4ClassRunner;
+import org.chromium.base.test.params.BaseJUnit4RunnerDelegate;
+import org.chromium.base.test.params.ParameterAnnotations.UseMethodParameter;
+import org.chromium.base.test.params.ParameterAnnotations.UseMethodParameterBefore;
+import org.chromium.base.test.params.ParameterAnnotations.UseRunnerDelegate;
+import org.chromium.base.test.params.ParameterizedRunner;
+import org.chromium.base.test.util.DisabledTest;
 import org.chromium.base.test.util.Feature;
 import org.chromium.base.test.util.UrlUtils;
 import org.chromium.content_public.browser.test.util.TestCallbackHelperContainer;
@@ -22,13 +27,21 @@
 /**
  * Common functionality for testing the Java Bridge.
  */
-@RunWith(BaseJUnit4ClassRunner.class)
+@RunWith(ParameterizedRunner.class)
+@UseRunnerDelegate(BaseJUnit4RunnerDelegate.class)
 public class JavaBridgeBareboneTest {
     @Rule
     public JavaBridgeActivityTestRule mActivityTestRule =
             new JavaBridgeActivityTestRule().shouldSetUp(false);
 
     private TestCallbackHelperContainer mTestCallbackHelperContainer;
+    private boolean mUseMojo;
+
+    @UseMethodParameterBefore(JavaBridgeActivityTestRule.MojoTestParams.class)
+    public void setupMojoTest(boolean useMojo) {
+        mUseMojo = useMojo;
+        mActivityTestRule.setupMojoTest(useMojo);
+    }
 
     @Before
     public void setUp() {
@@ -43,7 +56,7 @@
         mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
-                mActivityTestRule.getJavascriptInjector().addPossiblyUnsafeInterface(
+                mActivityTestRule.getJavascriptInjector(mUseMojo).addPossiblyUnsafeInterface(
                         new Object(), name, null);
             }
         });
@@ -83,7 +96,9 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testImmediateAddition() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.MojoTestParams.class)
+    @DisabledTest(message = "Flaky - https://crbug.com/1116744")
+    public void testImmediateAddition(boolean useMojo) throws Throwable {
         injectDummyObject("testObject");
         Assert.assertEquals("\"object\"", evaluateJsSync("typeof testObject"));
     }
@@ -93,7 +108,8 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testNoImmediateAdditionAfterJSEvaluation() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.MojoTestParams.class)
+    public void testNoImmediateAdditionAfterJSEvaluation(boolean useMojo) throws Throwable {
         evaluateJsSync("true");
         injectDummyObject("testObject");
         Assert.assertEquals("\"undefined\"", evaluateJsSync("typeof testObject"));
@@ -102,7 +118,9 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testImmediateAdditionAfterReload() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.MojoTestParams.class)
+    @DisabledTest(message = "Flaky - https://crbug.com/1117003")
+    public void testImmediateAdditionAfterReload(boolean useMojo) throws Throwable {
         reloadSync();
         injectDummyObject("testObject");
         Assert.assertEquals("\"object\"", evaluateJsSync("typeof testObject"));
@@ -111,7 +129,8 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testReloadAfterAddition() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.MojoTestParams.class)
+    public void testReloadAfterAddition(boolean useMojo) throws Throwable {
         injectDummyObject("testObject");
         reloadSync();
         Assert.assertEquals("\"object\"", evaluateJsSync("typeof testObject"));
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/JavaBridgeBasicsTest.java b/content/public/android/javatests/src/org/chromium/content/browser/JavaBridgeBasicsTest.java
index 1916a1a..d62df02 100644
--- a/content/public/android/javatests/src/org/chromium/content/browser/JavaBridgeBasicsTest.java
+++ b/content/public/android/javatests/src/org/chromium/content/browser/JavaBridgeBasicsTest.java
@@ -16,12 +16,16 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import org.chromium.base.test.params.ParameterAnnotations.UseMethodParameter;
+import org.chromium.base.test.params.ParameterAnnotations.UseMethodParameterBefore;
+import org.chromium.base.test.params.ParameterAnnotations.UseRunnerDelegate;
+import org.chromium.base.test.params.ParameterizedRunner;
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.DisabledTest;
 import org.chromium.base.test.util.Feature;
 import org.chromium.content.browser.JavaBridgeActivityTestRule.Controller;
 import org.chromium.content_public.browser.LoadUrlParams;
-import org.chromium.content_public.browser.test.ContentJUnit4ClassRunner;
+import org.chromium.content_public.browser.test.ContentJUnit4RunnerDelegate;
 import org.chromium.content_public.browser.test.util.TestCallbackHelperContainer;
 
 import java.lang.annotation.ElementType;
@@ -43,7 +47,8 @@
  * - Threading
  * - Inheritance
  */
-@RunWith(ContentJUnit4ClassRunner.class)
+@RunWith(ParameterizedRunner.class)
+@UseRunnerDelegate(ContentJUnit4RunnerDelegate.class)
 public class JavaBridgeBasicsTest {
     @Rule
     public JavaBridgeActivityTestRule mActivityTestRule =
@@ -100,6 +105,11 @@
         }
     }
 
+    @UseMethodParameterBefore(JavaBridgeActivityTestRule.MojoTestParams.class)
+    public void setupMojoTest(boolean useMojo) {
+        mActivityTestRule.setupMojoTest(useMojo);
+    }
+
     TestController mTestController;
 
     @Before
@@ -137,19 +147,21 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testTypeOfInjectedObject() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.MojoTestParams.class)
+    public void testTypeOfInjectedObject(boolean useMojo) throws Throwable {
         Assert.assertEquals("object", executeJavaScriptAndGetStringResult("typeof testController"));
     }
 
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testAdditionNotReflectedUntilReload() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.MojoTestParams.class)
+    public void testAdditionNotReflectedUntilReload(boolean useMojo) throws Throwable {
         Assert.assertEquals("undefined", executeJavaScriptAndGetStringResult("typeof testObject"));
         InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
             @Override
             public void run() {
-                mActivityTestRule.getJavascriptInjector().addPossiblyUnsafeInterface(
+                mActivityTestRule.getJavascriptInjector(useMojo).addPossiblyUnsafeInterface(
                         new Object(), "testObject", null);
             }
         });
@@ -161,7 +173,8 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testRemovalNotReflectedUntilReload() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.LegacyTestParams.class)
+    public void testRemovalNotReflectedUntilReload(boolean useMojo) throws Throwable {
         mActivityTestRule.injectObjectAndReload(new Object() {
             public void method() {
                 mTestController.setStringValue("I'm here");
@@ -173,7 +186,7 @@
         InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
             @Override
             public void run() {
-                mActivityTestRule.getJavascriptInjector().removeInterface("testObject");
+                mActivityTestRule.getJavascriptInjector(useMojo).removeInterface("testObject");
             }
         });
         // Check that the Java object is being held by the Java bridge, thus it's not
@@ -190,14 +203,15 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testRemoveObjectNotAdded() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.MojoTestParams.class)
+    public void testRemoveObjectNotAdded(boolean useMojo) throws Throwable {
         TestCallbackHelperContainer.OnPageFinishedHelper onPageFinishedHelper =
                 mActivityTestRule.getTestCallBackHelperContainer().getOnPageFinishedHelper();
         int currentCallCount = onPageFinishedHelper.getCallCount();
         InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
             @Override
             public void run() {
-                mActivityTestRule.getJavascriptInjector().removeInterface("foo");
+                mActivityTestRule.getJavascriptInjector(useMojo).removeInterface("foo");
                 mActivityTestRule.getWebContents().getNavigationController().reload(true);
             }
         });
@@ -208,7 +222,8 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testTypeOfMethod() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.MojoTestParams.class)
+    public void testTypeOfMethod(boolean useMojo) throws Throwable {
         Assert.assertEquals("function",
                 executeJavaScriptAndGetStringResult("typeof testController.setStringValue"));
     }
@@ -216,7 +231,8 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testTypeOfInvalidMethod() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.MojoTestParams.class)
+    public void testTypeOfInvalidMethod(boolean useMojo) throws Throwable {
         Assert.assertEquals(
                 "undefined", executeJavaScriptAndGetStringResult("typeof testController.foo"));
     }
@@ -224,14 +240,17 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testCallingInvalidMethodRaisesException() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.MojoTestParams.class)
+    public void testCallingInvalidMethodRaisesException(boolean useMojo) throws Throwable {
         assertRaisesException("testController.foo()");
     }
 
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testUncaughtJavaExceptionRaisesJavaScriptException() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.MojoTestParams.class)
+    public void testUncaughtJavaExceptionRaisesJavaScriptException(boolean useMojo)
+            throws Throwable {
         mActivityTestRule.injectObjectAndReload(new Object() {
             public void method() {
                 throw new RuntimeException("foo");
@@ -243,21 +262,24 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testCallingAsConstructorRaisesException() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.MojoTestParams.class)
+    public void testCallingAsConstructorRaisesException(boolean useMojo) throws Throwable {
         assertRaisesException("new testController.setStringValue('foo')");
     }
 
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testCallingOnNonInjectedObjectRaisesException() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.MojoTestParams.class)
+    public void testCallingOnNonInjectedObjectRaisesException(boolean useMojo) throws Throwable {
         assertRaisesException("testController.setStringValue.call({}, 'foo')");
     }
 
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testCallingOnInstanceOfOtherClassRaisesException() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.MojoTestParams.class)
+    public void testCallingOnInstanceOfOtherClassRaisesException(boolean useMojo) throws Throwable {
         mActivityTestRule.injectObjectAndReload(new Object(), "testObject");
         Assert.assertEquals("object", executeJavaScriptAndGetStringResult("typeof testObject"));
         Assert.assertEquals("object", executeJavaScriptAndGetStringResult("typeof testController"));
@@ -270,7 +292,8 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testTypeOfStaticMethod() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.MojoTestParams.class)
+    public void testTypeOfStaticMethod(boolean useMojo) throws Throwable {
         mActivityTestRule.injectObjectAndReload(new ObjectWithStaticMethod(), "testObject");
         mActivityTestRule.executeJavaScript(
                 "testController.setStringValue(typeof testObject.staticMethod)");
@@ -281,7 +304,8 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testCallStaticMethod() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.MojoTestParams.class)
+    public void testCallStaticMethod(boolean useMojo) throws Throwable {
         mActivityTestRule.injectObjectAndReload(new ObjectWithStaticMethod(), "testObject");
         mActivityTestRule.executeJavaScript(
                 "testController.setStringValue(testObject.staticMethod())");
@@ -291,7 +315,8 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testPrivateMethodNotExposed() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.MojoTestParams.class)
+    public void testPrivateMethodNotExposed(boolean useMojo) throws Throwable {
         mActivityTestRule.injectObjectAndReload(new Object() {
             private void method() {}
             protected void method2() {}
@@ -305,7 +330,8 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testReplaceInjectedObject() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.LegacyTestParams.class)
+    public void testReplaceInjectedObject(boolean useMojo) throws Throwable {
         mActivityTestRule.injectObjectAndReload(new Object() {
             public void method() {
                 mTestController.setStringValue("object 1");
@@ -326,7 +352,8 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testInjectNullObjectIsIgnored() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.MojoTestParams.class)
+    public void testInjectNullObjectIsIgnored(boolean useMojo) throws Throwable {
         mActivityTestRule.injectObjectAndReload(null, "testObject");
         Assert.assertEquals("undefined", executeJavaScriptAndGetStringResult("typeof testObject"));
     }
@@ -334,7 +361,8 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testReplaceInjectedObjectWithNullObjectIsIgnored() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.LegacyTestParams.class)
+    public void testReplaceInjectedObjectWithNullObjectIsIgnored(boolean useMojo) throws Throwable {
         mActivityTestRule.injectObjectAndReload(new Object(), "testObject");
         Assert.assertEquals("object", executeJavaScriptAndGetStringResult("typeof testObject"));
         mActivityTestRule.injectObjectAndReload(null, "testObject");
@@ -344,7 +372,9 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testCallOverloadedMethodWithDifferentNumberOfArguments() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.MojoTestParams.class)
+    public void testCallOverloadedMethodWithDifferentNumberOfArguments(boolean useMojo)
+            throws Throwable {
         mActivityTestRule.injectObjectAndReload(new Object() {
             public void method() {
                 mTestController.setStringValue("0 args");
@@ -373,7 +403,9 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testCallMethodWithWrongNumberOfArgumentsRaisesException() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.MojoTestParams.class)
+    public void testCallMethodWithWrongNumberOfArgumentsRaisesException(boolean useMojo)
+            throws Throwable {
         assertRaisesException("testController.setIntValue()");
         assertRaisesException("testController.setIntValue(42, 42)");
     }
@@ -381,7 +413,8 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testObjectPersistsAcrossPageLoads() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.MojoTestParams.class)
+    public void testObjectPersistsAcrossPageLoads(boolean useMojo) throws Throwable {
         Assert.assertEquals("object", executeJavaScriptAndGetStringResult("typeof testController"));
         mActivityTestRule.synchronousPageReload();
         Assert.assertEquals("object", executeJavaScriptAndGetStringResult("typeof testController"));
@@ -390,7 +423,8 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testCustomPropertiesCleanedUpOnPageReloads() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.MojoTestParams.class)
+    public void testCustomPropertiesCleanedUpOnPageReloads(boolean useMojo) throws Throwable {
         Assert.assertEquals("object", executeJavaScriptAndGetStringResult("typeof testController"));
         mActivityTestRule.executeJavaScript("testController.myProperty = 42;");
         Assert.assertEquals("42", executeJavaScriptAndGetStringResult("testController.myProperty"));
@@ -403,7 +437,8 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testSameObjectInjectedMultipleTimes() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.MojoTestParams.class)
+    public void testSameObjectInjectedMultipleTimes(boolean useMojo) throws Throwable {
         class TestObject {
             private int mNumMethodInvocations;
 
@@ -423,7 +458,8 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testCallMethodOnReturnedObject() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.MojoTestParams.class)
+    public void testCallMethodOnReturnedObject(boolean useMojo) throws Throwable {
         mActivityTestRule.injectObjectAndReload(new Object() {
             public Object getInnerObject() {
                 return new Object() {
@@ -440,7 +476,8 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testReturnedObjectInjectedElsewhere() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.MojoTestParams.class)
+    public void testReturnedObjectInjectedElsewhere(boolean useMojo) throws Throwable {
         class InnerObject {
             private int mNumMethodInvocations;
 
@@ -470,7 +507,8 @@
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
     @CommandLineFlags.Add("js-flags=--expose-gc")
-    public void testReturnedObjectIsGarbageCollected() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.MojoTestParams.class)
+    public void testReturnedObjectIsGarbageCollected(boolean useMojo) throws Throwable {
         Assert.assertEquals("function", executeJavaScriptAndGetStringResult("typeof gc"));
         class InnerObject {
         }
@@ -511,7 +549,8 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testSameReturnedObjectUsesSameWrapper() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.LegacyTestParams.class)
+    public void testSameReturnedObjectUsesSameWrapper(boolean useMojo) throws Throwable {
         class InnerObject {
         }
         final InnerObject innerObject = new InnerObject();
@@ -531,7 +570,8 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testMethodInvokedOnBackgroundThread() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.LegacyTestParams.class)
+    public void testMethodInvokedOnBackgroundThread(boolean useMojo) throws Throwable {
         mActivityTestRule.injectObjectAndReload(new Object() {
             public void captureThreadId() {
                 mTestController.setLongValue(Thread.currentThread().getId());
@@ -551,7 +591,8 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testBlockingUiThreadDoesNotBlockCallsFromJs() {
+    @UseMethodParameter(JavaBridgeActivityTestRule.LegacyTestParams.class)
+    public void testBlockingUiThreadDoesNotBlockCallsFromJs(boolean useMojo) {
         class TestObject {
             private CountDownLatch mLatch;
             public TestObject() {
@@ -594,7 +635,8 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testPublicInheritedMethod() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.MojoTestParams.class)
+    public void testPublicInheritedMethod(boolean useMojo) throws Throwable {
         class Base {
             public void method(int x) {
                 mTestController.setIntValue(x);
@@ -612,7 +654,8 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testPrivateInheritedMethod() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.MojoTestParams.class)
+    public void testPrivateInheritedMethod(boolean useMojo) throws Throwable {
         class Base {
             private void method() {}
         }
@@ -626,7 +669,8 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testOverriddenMethod() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.MojoTestParams.class)
+    public void testOverriddenMethod(boolean useMojo) throws Throwable {
         class Base {
             public void method() {
                 mTestController.setStringValue("base");
@@ -646,7 +690,8 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testEnumerateMembers() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.MojoTestParams.class)
+    public void testEnumerateMembers(boolean useMojo) throws Throwable {
         mActivityTestRule.injectObjectAndReload(new Object() {
             public void method() {}
             private void privateMethod() {}
@@ -663,7 +708,8 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testReflectPublicMethod() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.LegacyTestParams.class)
+    public void testReflectPublicMethod(boolean useMojo) throws Throwable {
         mActivityTestRule.injectObjectAndReload(new Object() {
             public Class<?> myGetClass() {
                 return getClass();
@@ -682,7 +728,8 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testReflectPublicField() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.LegacyTestParams.class)
+    public void testReflectPublicField(boolean useMojo) throws Throwable {
         mActivityTestRule.injectObjectAndReload(new Object() {
             public Class<?> myGetClass() {
                 return getClass();
@@ -698,7 +745,8 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testReflectPrivateMethodRaisesException() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.LegacyTestParams.class)
+    public void testReflectPrivateMethodRaisesException(boolean useMojo) throws Throwable {
         mActivityTestRule.injectObjectAndReload(new Object() {
             public Class<?> myGetClass() {
                 return getClass();
@@ -718,7 +766,8 @@
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
     @DisabledTest(message = "https://crbug.com/795378")
-    public void testReflectPrivateFieldRaisesException() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.MojoTestParams.class)
+    public void testReflectPrivateFieldRaisesException(boolean useMojo) throws Throwable {
         mActivityTestRule.injectObjectAndReload(new Object() {
             public Class<?> myGetClass() {
                 return getClass();
@@ -738,7 +787,8 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testAllowNonAnnotatedMethods() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.MojoTestParams.class)
+    public void testAllowNonAnnotatedMethods(boolean useMojo) throws Throwable {
         mActivityTestRule.injectObjectAndReload(new Object() {
             public String allowed() {
                 return "foo";
@@ -756,7 +806,8 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testAllowOnlyAnnotatedMethods() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.LegacyTestParams.class)
+    public void testAllowOnlyAnnotatedMethods(boolean useMojo) throws Throwable {
         mActivityTestRule.injectObjectAndReload(new Object() {
             @JavascriptInterface
             public String allowed() {
@@ -788,7 +839,9 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testAnnotationRequirementRetainsPropertyAcrossObjects() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.LegacyTestParams.class)
+    public void testAnnotationRequirementRetainsPropertyAcrossObjects(boolean useMojo)
+            throws Throwable {
         class Test {
             @JavascriptInterface
             public String safe() {
@@ -838,7 +891,8 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testAnnotationDoesNotGetInherited() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.LegacyTestParams.class)
+    public void testAnnotationDoesNotGetInherited(boolean useMojo) throws Throwable {
         class Base {
             @JavascriptInterface
             public void base() { }
@@ -868,7 +922,8 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testCustomAnnotationRestriction() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.LegacyTestParams.class)
+    public void testCustomAnnotationRestriction(boolean useMojo) throws Throwable {
         class Test {
             @TestAnnotation
             public String checkTestAnnotationFoo() {
@@ -914,7 +969,8 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testObjectsInspection() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.LegacyTestParams.class)
+    public void testObjectsInspection(boolean useMojo) throws Throwable {
         class Test {
             @JavascriptInterface
             public String m1() {
@@ -954,7 +1010,7 @@
         InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
             @Override
             public void run() {
-                mActivityTestRule.getJavascriptInjector().setAllowInspection(false);
+                mActivityTestRule.getJavascriptInjector(useMojo).setAllowInspection(false);
             }
         });
 
@@ -972,7 +1028,8 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testAccessToObjectGetClassIsBlocked() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.MojoTestParams.class)
+    public void testAccessToObjectGetClassIsBlocked(boolean useMojo) throws Throwable {
         mActivityTestRule.injectObjectAndReload(new Object(), "testObject");
         Assert.assertEquals(
                 "function", executeJavaScriptAndGetStringResult("typeof testObject.getClass"));
@@ -982,7 +1039,8 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testReplaceJavascriptInterface() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.LegacyTestParams.class)
+    public void testReplaceJavascriptInterface(boolean useMojo) throws Throwable {
         class Test {
             public Test(int value) {
                 mValue = value;
@@ -1005,7 +1063,8 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testMethodCalledOnAnotherInstance() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.MojoTestParams.class)
+    public void testMethodCalledOnAnotherInstance(boolean useMojo) throws Throwable {
         class TestObject {
             private int mIndex;
             TestObject(int index) {
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/JavaBridgeChildFrameTest.java b/content/public/android/javatests/src/org/chromium/content/browser/JavaBridgeChildFrameTest.java
index 66f2479..fd43eca 100644
--- a/content/public/android/javatests/src/org/chromium/content/browser/JavaBridgeChildFrameTest.java
+++ b/content/public/android/javatests/src/org/chromium/content/browser/JavaBridgeChildFrameTest.java
@@ -14,6 +14,10 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import org.chromium.base.test.params.ParameterAnnotations.UseMethodParameter;
+import org.chromium.base.test.params.ParameterAnnotations.UseMethodParameterBefore;
+import org.chromium.base.test.params.ParameterAnnotations.UseRunnerDelegate;
+import org.chromium.base.test.params.ParameterizedRunner;
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.DisabledTest;
 import org.chromium.base.test.util.Feature;
@@ -22,7 +26,7 @@
 import org.chromium.content_public.browser.LoadUrlParams;
 import org.chromium.content_public.browser.NavigationController;
 import org.chromium.content_public.browser.WebContents;
-import org.chromium.content_public.browser.test.ContentJUnit4ClassRunner;
+import org.chromium.content_public.browser.test.ContentJUnit4RunnerDelegate;
 
 import java.lang.ref.WeakReference;
 import java.util.concurrent.CountDownLatch;
@@ -35,7 +39,8 @@
  * Ensures that injected objects are exposed to child frames as well as the
  * main frame.
  */
-@RunWith(ContentJUnit4ClassRunner.class)
+@RunWith(ParameterizedRunner.class)
+@UseRunnerDelegate(ContentJUnit4RunnerDelegate.class)
 public class JavaBridgeChildFrameTest {
     @Rule
     public JavaBridgeActivityTestRule mActivityTestRule =
@@ -56,6 +61,11 @@
         }
     }
 
+    @UseMethodParameterBefore(JavaBridgeActivityTestRule.MojoTestParams.class)
+    public void setupMojoTest(boolean useMojo) {
+        mActivityTestRule.setupMojoTest(useMojo);
+    }
+
     TestController mTestController;
 
     @Before
@@ -67,7 +77,8 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testInjectedObjectPresentInChildFrame() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.LegacyTestParams.class)
+    public void testInjectedObjectPresentInChildFrame(boolean useMojo) throws Throwable {
         loadDataSync(mActivityTestRule.getWebContents().getNavigationController(),
                 "<html><body><iframe></iframe></body></html>", "text/html", false);
         // We are not executing this code as a part of page loading routine to avoid races
@@ -85,7 +96,8 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testMainPageWrapperIsNotBrokenByChildFrame() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.MojoTestParams.class)
+    public void testMainPageWrapperIsNotBrokenByChildFrame(boolean useMojo) throws Throwable {
         loadDataSync(mActivityTestRule.getWebContents().getNavigationController(),
                 "<html><body><iframe></iframe></body></html>", "text/html", false);
         // In case there is anything wrong with the JS wrapper, an attempt
@@ -108,7 +120,8 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testWrapperIsNotSharedWithChildFrame() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.LegacyTestParams.class)
+    public void testWrapperIsNotSharedWithChildFrame(boolean useMojo) throws Throwable {
         // Test by setting a custom property on the parent page's injected
         // object and then checking that child frame doesn't see the property.
         loadDataSync(mActivityTestRule.getWebContents().getNavigationController(),
@@ -137,7 +150,8 @@
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
     @DisabledTest(message = "https://crbug.com/677182")
-    public void testRemovingTransientObjectHolders() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.MojoTestParams.class)
+    public void testRemovingTransientObjectHolders(boolean useMojo) throws Throwable {
         class Test {
             private Object mInner = new Object();
             // Expecting the inner object to be retrieved twice.
@@ -199,7 +213,8 @@
     @Feature({"AndroidWebView", "Android-JavaBridge"})
     @CommandLineFlags.Add("js-flags=--expose-gc")
     @DisabledTest(message = "https://crbug.com/646843")
-    public void testHolderFrame() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.MojoTestParams.class)
+    public void testHolderFrame(boolean useMojo) throws Throwable {
         class Test {
             WeakReference<Object> mWeakRefForInner;
             private CountDownLatch mLatch = new CountDownLatch(1);
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/JavaBridgeCoercionTest.java b/content/public/android/javatests/src/org/chromium/content/browser/JavaBridgeCoercionTest.java
index 9fba0f2e..4c617c9 100644
--- a/content/public/android/javatests/src/org/chromium/content/browser/JavaBridgeCoercionTest.java
+++ b/content/public/android/javatests/src/org/chromium/content/browser/JavaBridgeCoercionTest.java
@@ -14,7 +14,11 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import org.chromium.base.test.BaseJUnit4ClassRunner;
+import org.chromium.base.test.params.BaseJUnit4RunnerDelegate;
+import org.chromium.base.test.params.ParameterAnnotations.UseMethodParameter;
+import org.chromium.base.test.params.ParameterAnnotations.UseMethodParameterBefore;
+import org.chromium.base.test.params.ParameterAnnotations.UseRunnerDelegate;
+import org.chromium.base.test.params.ParameterizedRunner;
 import org.chromium.base.test.util.Feature;
 import org.chromium.base.test.util.UrlUtils;
 import org.chromium.content.browser.JavaBridgeActivityTestRule.Controller;
@@ -33,7 +37,8 @@
  * FIXME: Consider making our implementation more compliant, if it will not
  * break backwards-compatibility. See b/4408210.
  */
-@RunWith(BaseJUnit4ClassRunner.class)
+@RunWith(ParameterizedRunner.class)
+@UseRunnerDelegate(BaseJUnit4RunnerDelegate.class)
 public class JavaBridgeCoercionTest {
     private static final double ASSERTION_DELTA = 0;
 
@@ -171,6 +176,11 @@
     private static class CustomType2 {
     }
 
+    @UseMethodParameterBefore(JavaBridgeActivityTestRule.MojoTestParams.class)
+    public void setupMojoTest(boolean useMojo) {
+        mActivityTestRule.setupMojoTest(useMojo);
+    }
+
     private TestObject mTestObject;
 
     private static class TestController extends Controller {
@@ -213,7 +223,8 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testPassNumberInt32() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.MojoTestParams.class)
+    public void testPassNumberInt32(boolean useMojo) throws Throwable {
         mActivityTestRule.executeJavaScript("testObject.setByteValue(42);");
         Assert.assertEquals(42, mTestObject.waitForByteValue());
         mActivityTestRule.executeJavaScript(
@@ -266,7 +277,8 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testPassNumberDouble() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.LegacyTestParams.class)
+    public void testPassNumberDouble(boolean useMojo) throws Throwable {
         mActivityTestRule.executeJavaScript("testObject.setByteValue(42.1);");
         Assert.assertEquals(42, mTestObject.waitForByteValue());
         mActivityTestRule.executeJavaScript(
@@ -347,7 +359,8 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testPassNumberNaN() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.MojoTestParams.class)
+    public void testPassNumberNaN(boolean useMojo) throws Throwable {
         mActivityTestRule.executeJavaScript("testObject.setByteValue(Number.NaN);");
         Assert.assertEquals(0, mTestObject.waitForByteValue());
 
@@ -388,7 +401,8 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testPassNumberInfinity() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.MojoTestParams.class)
+    public void testPassNumberInfinity(boolean useMojo) throws Throwable {
         mActivityTestRule.executeJavaScript("testObject.setByteValue(Infinity);");
         Assert.assertEquals(-1, mTestObject.waitForByteValue());
 
@@ -432,7 +446,8 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testPassBoolean() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.MojoTestParams.class)
+    public void testPassBoolean(boolean useMojo) throws Throwable {
         mActivityTestRule.executeJavaScript("testObject.setBooleanValue(true);");
         Assert.assertTrue(mTestObject.waitForBooleanValue());
         mActivityTestRule.executeJavaScript("testObject.setBooleanValue(false);");
@@ -498,7 +513,8 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testPassString() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.MojoTestParams.class)
+    public void testPassString(boolean useMojo) throws Throwable {
         mActivityTestRule.executeJavaScript("testObject.setStringValue(\"+042.10\");");
         Assert.assertEquals("+042.10", mTestObject.waitForStringValue());
 
@@ -551,7 +567,8 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testPassJavaScriptObject() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.LegacyTestParams.class)
+    public void testPassJavaScriptObject(boolean useMojo) throws Throwable {
         // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception.
         mActivityTestRule.executeJavaScript("testObject.setObjectValue({foo: 42});");
         Assert.assertNull(mTestObject.waitForObjectValue());
@@ -603,7 +620,8 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testPassJavaObject() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.LegacyTestParams.class)
+    public void testPassJavaObject(boolean useMojo) throws Throwable {
         mActivityTestRule.executeJavaScript(
                 "testObject.setObjectValue(testObject.getObjectInstance());");
         Assert.assertTrue(mTestObject.getObjectInstance() == mTestObject.waitForObjectValue());
@@ -682,7 +700,8 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testPassJavaObjectFromCustomClassLoader() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.LegacyTestParams.class)
+    public void testPassJavaObjectFromCustomClassLoader(boolean useMojo) throws Throwable {
         // Compiled bytecode (dex) for the following class:
         //
         // package org.example;
@@ -706,7 +725,7 @@
         mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
-                mActivityTestRule.getJavascriptInjector().addPossiblyUnsafeInterface(
+                mActivityTestRule.getJavascriptInjector(useMojo).addPossiblyUnsafeInterface(
                         selfConsuming, "selfConsuming", null);
             }
         });
@@ -720,7 +739,8 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testPassNull() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.MojoTestParams.class)
+    public void testPassNull(boolean useMojo) throws Throwable {
         mActivityTestRule.executeJavaScript("testObject.setObjectValue(null);");
         Assert.assertNull(mTestObject.waitForObjectValue());
 
@@ -759,7 +779,8 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testPassUndefined() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.MojoTestParams.class)
+    public void testPassUndefined(boolean useMojo) throws Throwable {
         mActivityTestRule.executeJavaScript("testObject.setObjectValue(undefined);");
         Assert.assertNull(mTestObject.waitForObjectValue());
 
@@ -800,7 +821,8 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testPassArrayBuffer() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.LegacyTestParams.class)
+    public void testPassArrayBuffer(boolean useMojo) throws Throwable {
         mActivityTestRule.executeJavaScript("buffer = new ArrayBuffer(16);");
 
         mActivityTestRule.executeJavaScript("testObject.setObjectValue(buffer);");
@@ -817,7 +839,8 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testPassDataView() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.LegacyTestParams.class)
+    public void testPassDataView(boolean useMojo) throws Throwable {
         mActivityTestRule.executeJavaScript("buffer = new ArrayBuffer(16);");
 
         mActivityTestRule.executeJavaScript("testObject.setObjectValue(new DataView(buffer));");
@@ -831,7 +854,8 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testPassDateObject() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.LegacyTestParams.class)
+    public void testPassDateObject(boolean useMojo) throws Throwable {
         mActivityTestRule.executeJavaScript("testObject.setDoubleValue(new Date(2000, 0, 1));");
         Assert.assertEquals(0.0, mTestObject.waitForDoubleValue(), ASSERTION_DELTA);
 
@@ -846,7 +870,8 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testPassRegExpObject() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.LegacyTestParams.class)
+    public void testPassRegExpObject(boolean useMojo) throws Throwable {
         mActivityTestRule.executeJavaScript("testObject.setStringValue(/abc/);");
         Assert.assertEquals("undefined", mTestObject.waitForStringValue());
 
@@ -858,7 +883,8 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testPassFunctionObject() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.LegacyTestParams.class)
+    public void testPassFunctionObject(boolean useMojo) throws Throwable {
         mActivityTestRule.executeJavaScript("func = new Function('a', 'b', 'return a + b');");
 
         mActivityTestRule.executeJavaScript("testObject.setStringValue(func);");
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/JavaBridgeFieldsTest.java b/content/public/android/javatests/src/org/chromium/content/browser/JavaBridgeFieldsTest.java
index bb28a8c..b978ece 100644
--- a/content/public/android/javatests/src/org/chromium/content/browser/JavaBridgeFieldsTest.java
+++ b/content/public/android/javatests/src/org/chromium/content/browser/JavaBridgeFieldsTest.java
@@ -12,7 +12,11 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import org.chromium.base.test.BaseJUnit4ClassRunner;
+import org.chromium.base.test.params.BaseJUnit4RunnerDelegate;
+import org.chromium.base.test.params.ParameterAnnotations.UseMethodParameter;
+import org.chromium.base.test.params.ParameterAnnotations.UseMethodParameterBefore;
+import org.chromium.base.test.params.ParameterAnnotations.UseRunnerDelegate;
+import org.chromium.base.test.params.ParameterizedRunner;
 import org.chromium.base.test.util.Feature;
 import org.chromium.content.browser.JavaBridgeActivityTestRule.Controller;
 
@@ -20,7 +24,8 @@
  * Part of the test suite for the Java Bridge. This test tests the
  * use of fields.
  */
-@RunWith(BaseJUnit4ClassRunner.class)
+@RunWith(ParameterizedRunner.class)
+@UseRunnerDelegate(BaseJUnit4RunnerDelegate.class)
 public class JavaBridgeFieldsTest {
     @Rule
     public JavaBridgeActivityTestRule mActivityTestRule =
@@ -56,6 +61,11 @@
     private static class CustomType {
     }
 
+    @UseMethodParameterBefore(JavaBridgeActivityTestRule.MojoTestParams.class)
+    public void setupMojoTest(boolean useMojo) {
+        mActivityTestRule.setupMojoTest(useMojo);
+    }
+
     TestObject mTestObject;
 
     @Before
@@ -75,7 +85,8 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testFieldTypes() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.MojoTestParams.class)
+    public void testFieldTypes(boolean useMojo) throws Throwable {
         Assert.assertEquals(
                 "undefined", executeJavaScriptAndGetStringResult("typeof testObject.booleanField"));
         Assert.assertEquals(
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/JavaBridgeReturnValuesTest.java b/content/public/android/javatests/src/org/chromium/content/browser/JavaBridgeReturnValuesTest.java
index b4705d7..e0ab9f5 100644
--- a/content/public/android/javatests/src/org/chromium/content/browser/JavaBridgeReturnValuesTest.java
+++ b/content/public/android/javatests/src/org/chromium/content/browser/JavaBridgeReturnValuesTest.java
@@ -12,7 +12,11 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import org.chromium.base.test.BaseJUnit4ClassRunner;
+import org.chromium.base.test.params.BaseJUnit4RunnerDelegate;
+import org.chromium.base.test.params.ParameterAnnotations.UseMethodParameter;
+import org.chromium.base.test.params.ParameterAnnotations.UseMethodParameterBefore;
+import org.chromium.base.test.params.ParameterAnnotations.UseRunnerDelegate;
+import org.chromium.base.test.params.ParameterizedRunner;
 import org.chromium.base.test.util.Feature;
 import org.chromium.content.browser.JavaBridgeActivityTestRule.Controller;
 
@@ -27,7 +31,8 @@
  * FIXME: Consider making our implementation more compliant, if it will not
  * break backwards-compatibility. See b/4408210.
  */
-@RunWith(BaseJUnit4ClassRunner.class)
+@RunWith(ParameterizedRunner.class)
+@UseRunnerDelegate(BaseJUnit4RunnerDelegate.class)
 public class JavaBridgeReturnValuesTest {
     @Rule
     public JavaBridgeActivityTestRule mActivityTestRule =
@@ -113,6 +118,11 @@
     private static class CustomType {
     }
 
+    @UseMethodParameterBefore(JavaBridgeActivityTestRule.MojoTestParams.class)
+    public void setupMojoTest(boolean useMojo) {
+        mActivityTestRule.setupMojoTest(useMojo);
+    }
+
     TestObject mTestObject;
 
     @Before
@@ -136,7 +146,8 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testMethodReturnTypes() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.MojoTestParams.class)
+    public void testMethodReturnTypes(boolean useMojo) throws Throwable {
         Assert.assertEquals("boolean",
                 executeJavaScriptAndGetStringResult("typeof testObject.getBooleanValue()"));
         Assert.assertEquals(
@@ -178,7 +189,8 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Android-JavaBridge"})
-    public void testMethodReturnValues() throws Throwable {
+    @UseMethodParameter(JavaBridgeActivityTestRule.MojoTestParams.class)
+    public void testMethodReturnValues(boolean useMojo) throws Throwable {
         // We do the string comparison in JavaScript, to avoid relying on the
         // coercion algorithm from JavaScript to Java.
         Assert.assertTrue(executeJavaScriptAndGetBooleanResult("testObject.getBooleanValue()"));
diff --git a/content/public/browser/push_messaging_service.cc b/content/public/browser/push_messaging_service.cc
index 5d46147..1ef27ec 100644
--- a/content/public/browser/push_messaging_service.cc
+++ b/content/public/browser/push_messaging_service.cc
@@ -56,6 +56,19 @@
       base::BindOnce(&CallClosureFromIO, std::move(callback)));
 }
 
+void UpdatePushSubscriptionIdOnIO(
+    scoped_refptr<ServiceWorkerContextWrapper> service_worker_context,
+    int64_t service_worker_registration_id,
+    const GURL& origin,
+    const std::string& subscription_id,
+    base::OnceClosure callback) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  service_worker_context->StoreRegistrationUserData(
+      service_worker_registration_id, origin,
+      {{kPushRegistrationIdServiceWorkerKey, subscription_id}},
+      base::BindOnce(&CallClosureFromIO, std::move(callback)));
+}
+
 void StorePushSubscriptionOnIOForTesting(
     scoped_refptr<ServiceWorkerContextWrapper> service_worker_context,
     int64_t service_worker_registration_id,
@@ -152,6 +165,22 @@
 }
 
 // static
+void PushMessagingService::UpdatePushSubscriptionId(
+    BrowserContext* browser_context,
+    const GURL& origin,
+    int64_t service_worker_registration_id,
+    const std::string& subscription_id,
+    base::OnceClosure callback) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  GetIOThreadTaskRunner({})->PostTask(
+      FROM_HERE,
+      base::BindOnce(&UpdatePushSubscriptionIdOnIO,
+                     GetServiceWorkerContext(browser_context, origin),
+                     service_worker_registration_id, origin, subscription_id,
+                     std::move(callback)));
+}
+
+// static
 void PushMessagingService::StorePushSubscriptionForTesting(
     BrowserContext* browser_context,
     const GURL& origin,
diff --git a/content/public/browser/push_messaging_service.h b/content/public/browser/push_messaging_service.h
index 81dee340..e81ffbac 100644
--- a/content/public/browser/push_messaging_service.h
+++ b/content/public/browser/push_messaging_service.h
@@ -141,6 +141,13 @@
                                       int64_t service_worker_registration_id,
                                       base::OnceClosure callback);
 
+  // Update |subscription_id| stored in Service Worker database.
+  static void UpdatePushSubscriptionId(BrowserContext* browser_context,
+                                       const GURL& origin,
+                                       int64_t service_worker_registration_id,
+                                       const std::string& subscription_id,
+                                       base::OnceClosure callback);
+
   // Stores a push subscription in the service worker for the given |origin|.
   // Must only be used by tests.
   static void StorePushSubscriptionForTesting(
diff --git a/content/public/test/android/BUILD.gn b/content/public/test/android/BUILD.gn
index 1d34821..7002dd5 100644
--- a/content/public/test/android/BUILD.gn
+++ b/content/public/test/android/BUILD.gn
@@ -34,6 +34,7 @@
     "javatests/src/org/chromium/content_public/browser/test/ChildProcessAllocatorSettings.java",
     "javatests/src/org/chromium/content_public/browser/test/ChildProcessAllocatorSettingsHook.java",
     "javatests/src/org/chromium/content_public/browser/test/ContentJUnit4ClassRunner.java",
+    "javatests/src/org/chromium/content_public/browser/test/ContentJUnit4RunnerDelegate.java",
     "javatests/src/org/chromium/content_public/browser/test/NativeLibraryTestUtils.java",
     "javatests/src/org/chromium/content_public/browser/test/RenderFrameHostTestExt.java",
     "javatests/src/org/chromium/content_public/browser/test/mock/MockNavigationController.java",
diff --git a/content/public/test/android/javatests/src/org/chromium/content_public/browser/test/ContentJUnit4RunnerDelegate.java b/content/public/test/android/javatests/src/org/chromium/content_public/browser/test/ContentJUnit4RunnerDelegate.java
new file mode 100644
index 0000000..90808fc
--- /dev/null
+++ b/content/public/test/android/javatests/src/org/chromium/content_public/browser/test/ContentJUnit4RunnerDelegate.java
@@ -0,0 +1,43 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.content_public.browser.test;
+
+import org.junit.runners.model.FrameworkMethod;
+import org.junit.runners.model.InitializationError;
+
+import org.chromium.base.test.params.ParameterizedRunner.ParameterizedTestInstantiationException;
+import org.chromium.base.test.params.ParameterizedRunnerDelegate;
+import org.chromium.base.test.params.ParameterizedRunnerDelegateCommon;
+
+import java.util.List;
+
+/**
+ * A custom runner delegate for running //content JUnit4 parameterized tests.
+ */
+public final class ContentJUnit4RunnerDelegate
+        extends ContentJUnit4ClassRunner implements ParameterizedRunnerDelegate {
+    private final ParameterizedRunnerDelegateCommon mDelegateCommon;
+
+    public ContentJUnit4RunnerDelegate(Class<?> klass,
+            ParameterizedRunnerDelegateCommon delegateCommon) throws InitializationError {
+        super(klass);
+        mDelegateCommon = delegateCommon;
+    }
+
+    @Override
+    public void collectInitializationErrors(List<Throwable> errors) {
+        ParameterizedRunnerDelegateCommon.collectInitializationErrors(errors);
+    }
+
+    @Override
+    public List<FrameworkMethod> computeTestMethods() {
+        return mDelegateCommon.computeTestMethods();
+    }
+
+    @Override
+    public Object createTest() throws ParameterizedTestInstantiationException {
+        return mDelegateCommon.createTest();
+    }
+}
diff --git a/content/public/test/text_input_test_utils.cc b/content/public/test/text_input_test_utils.cc
index 1dd4c2a..2960ac3 100644
--- a/content/public/test/text_input_test_utils.cc
+++ b/content/public/test/text_input_test_utils.cc
@@ -437,6 +437,63 @@
   return observer_->text_input_state_changed();
 }
 
+TextInputManagerObserverBase::TextInputManagerObserverBase(
+    content::WebContents* web_contents)
+    : tester_(std::make_unique<TextInputManagerTester>(web_contents)),
+      success_(false) {}
+
+TextInputManagerObserverBase::~TextInputManagerObserverBase() = default;
+
+void TextInputManagerObserverBase::Wait() {
+  if (success_)
+    return;
+
+  base::RunLoop loop;
+  quit_ = loop.QuitClosure();
+  loop.Run();
+}
+
+void TextInputManagerObserverBase::OnSuccess() {
+  success_ = true;
+  if (quit_)
+    std::move(quit_).Run();
+
+  // By deleting |tester_| we make sure that the internal observer used in
+  // content/ is removed from the observer list of TextInputManager.
+  tester_.reset();
+}
+
+TextInputManagerValueObserver::TextInputManagerValueObserver(
+    content::WebContents* web_contents,
+    const std::string& expected_value)
+    : TextInputManagerObserverBase(web_contents),
+      expected_value_(expected_value) {
+  tester()->SetUpdateTextInputStateCalledCallback(base::BindRepeating(
+      &TextInputManagerValueObserver::VerifyValue, base::Unretained(this)));
+}
+
+void TextInputManagerValueObserver::VerifyValue() {
+  std::string value;
+  if (tester()->GetTextInputValue(&value) && expected_value_ == value)
+    OnSuccess();
+}
+
+TextInputManagerTypeObserver::TextInputManagerTypeObserver(
+    content::WebContents* web_contents,
+    ui::TextInputType expected_type)
+    : TextInputManagerObserverBase(web_contents),
+      expected_type_(expected_type) {
+  tester()->SetUpdateTextInputStateCalledCallback(base::BindRepeating(
+      &TextInputManagerTypeObserver::VerifyType, base::Unretained(this)));
+}
+
+void TextInputManagerTypeObserver::VerifyType() {
+  ui::TextInputType type =
+      tester()->GetTextInputType(&type) ? type : ui::TEXT_INPUT_TYPE_NONE;
+  if (expected_type_ == type)
+    OnSuccess();
+}
+
 TestRenderWidgetHostViewDestructionObserver::
     TestRenderWidgetHostViewDestructionObserver(RenderWidgetHostView* view)
     : observer_(
diff --git a/content/public/test/text_input_test_utils.h b/content/public/test/text_input_test_utils.h
index c4f5299..a0d0a0dc 100644
--- a/content/public/test/text_input_test_utils.h
+++ b/content/public/test/text_input_test_utils.h
@@ -11,6 +11,7 @@
 #include "base/callback.h"
 #include "base/strings/string16.h"
 #include "build/build_config.h"
+#include "content/public/test/test_utils.h"
 #include "ui/base/ime/mojom/text_input_state.mojom.h"
 #include "ui/base/ime/mojom/virtual_keyboard_types.mojom.h"
 #include "ui/base/ime/text_input_mode.h"
@@ -172,6 +173,70 @@
   DISALLOW_COPY_AND_ASSIGN(TextInputManagerTester);
 };
 
+// TextInputManager Observers
+
+// A base class for observing the TextInputManager owned by the given
+// WebContents. Subclasses can observe the TextInputManager for different
+// changes. The class wraps a public tester which accepts callbacks that
+// are run after specific changes in TextInputManager. Different observers can
+// be subclassed from this by providing their specific callback methods.
+class TextInputManagerObserverBase {
+ public:
+  explicit TextInputManagerObserverBase(content::WebContents* web_contents);
+
+  virtual ~TextInputManagerObserverBase();
+
+  TextInputManagerObserverBase(const TextInputManagerObserverBase&) = delete;
+  TextInputManagerObserverBase& operator=(const TextInputManagerObserverBase&) =
+      delete;
+
+  // Wait for derived class's definition of success.
+  void Wait();
+
+  bool success() const { return success_; }
+
+ protected:
+  content::TextInputManagerTester* tester() { return tester_.get(); }
+
+  void OnSuccess();
+
+ private:
+  std::unique_ptr<content::TextInputManagerTester> tester_;
+  bool success_;
+  base::OnceClosure quit_;
+};
+
+// This class observes TextInputManager for changes in |TextInputState.value|.
+class TextInputManagerValueObserver : public TextInputManagerObserverBase {
+ public:
+  TextInputManagerValueObserver(content::WebContents* web_contents,
+                                const std::string& expected_value);
+
+  TextInputManagerValueObserver(const TextInputManagerValueObserver&) = delete;
+  TextInputManagerValueObserver& operator=(
+      const TextInputManagerValueObserver&) = delete;
+
+ private:
+  void VerifyValue();
+  const std::string expected_value_;
+};
+
+// This class observes TextInputManager for changes in |TextInputState.type|.
+class TextInputManagerTypeObserver : public TextInputManagerObserverBase {
+ public:
+  TextInputManagerTypeObserver(content::WebContents* web_contents,
+                               ui::TextInputType expected_type);
+
+  TextInputManagerTypeObserver(const TextInputManagerTypeObserver&) = delete;
+  TextInputManagerTypeObserver& operator=(const TextInputManagerTypeObserver&) =
+      delete;
+
+ private:
+  void VerifyType();
+
+  const ui::TextInputType expected_type_;
+};
+
 // This class observes the lifetime of a RenderWidgetHostView.
 class TestRenderWidgetHostViewDestructionObserver {
  public:
diff --git a/content/shell/android/javatests/src/org/chromium/content_shell_apk/ContentShellActivityTestRule.java b/content/shell/android/javatests/src/org/chromium/content_shell_apk/ContentShellActivityTestRule.java
index 4db38e90f..c1e0d25 100644
--- a/content/shell/android/javatests/src/org/chromium/content_shell_apk/ContentShellActivityTestRule.java
+++ b/content/shell/android/javatests/src/org/chromium/content_shell_apk/ContentShellActivityTestRule.java
@@ -226,7 +226,11 @@
     }
 
     public JavascriptInjector getJavascriptInjector() {
-        return JavascriptInjector.fromWebContents(getWebContents());
+        return getJavascriptInjector(false);
+    }
+
+    public JavascriptInjector getJavascriptInjector(boolean useMojo) {
+        return JavascriptInjector.fromWebContents(getWebContents(), useMojo);
     }
 
     /**
diff --git a/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
index 0ac59bd..867787a 100644
--- a/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
@@ -615,6 +615,7 @@
 crbug.com/1056830 [ android qualcomm-adreno-(tm)-418 ] conformance/extensions/webgl-compressed-texture-astc.html [ Failure ]
 # Timing out on this device for unknown reasons.
 crbug.com/1099148 [ android qualcomm-adreno-(tm)-418 ] deqp/data/gles2/shaders/swizzles.html [ Skip ]
+crbug.com/1122644 [ android qualcomm-adreno-(tm)-418 ] conformance/textures/misc/texture-upload-size.html [ Skip ]
 
 # Nexus 6 (Adreno 420) and 6P (Adreno 430)
 crbug.com/499555 [ android qualcomm-adreno-(tm)-420 ] conformance/context/context-attributes-alpha-depth-stencil-antialias.html [ Failure ]
@@ -630,6 +631,7 @@
 crbug.com/1056830 [ android qualcomm-adreno-(tm)-420 ] conformance/extensions/webgl-compressed-texture-astc.html [ Failure ]
 crbug.com/1083320 [ android qualcomm-adreno-(tm)-430 ] conformance/misc/uninitialized-test.html [ Skip ]
 crbug.com/1056830 [ android qualcomm-adreno-(tm)-430 ] conformance/extensions/webgl-compressed-texture-astc.html [ Failure ]
+crbug.com/1122644 [ android qualcomm-adreno-(tm)-420 ] conformance/textures/misc/texture-upload-size.html [ Skip ]
 
 # This test is skipped because running it causes a future test to fail.
 # The list of tests which may be that future test is very long. It is
@@ -680,7 +682,6 @@
 crbug.com/981579 [ android opengles ] conformance/textures/misc/tex-video-using-tex-unit-non-zero.html [ RetryOnFailure ]
 crbug.com/906724 [ android opengles ] conformance/textures/video/tex-2d-luminance_alpha-luminance_alpha-unsigned_byte.html [ Failure ]
 crbug.com/906724 [ android opengles ] conformance/textures/video/tex-2d-luminance-luminance-unsigned_byte.html [ Failure ]
-crbug.com/1044847 [ android opengles ] conformance/textures/misc/texture-upload-size.html [ Skip ]
 
 # Misc failures
 crbug.com/angleproject/2988 [ android opengles ] conformance/context/context-size-change.html [ Failure ]
diff --git a/content/test/mock_clipboard_host.cc b/content/test/mock_clipboard_host.cc
index 6835e73..2a0bc1c 100644
--- a/content/test/mock_clipboard_host.cc
+++ b/content/test/mock_clipboard_host.cc
@@ -21,6 +21,7 @@
 void MockClipboardHost::Reset() {
   plain_text_ = base::string16();
   html_text_ = base::string16();
+  svg_text_ = base::string16();
   url_ = GURL();
   image_.reset();
   custom_data_.clear();
@@ -41,6 +42,8 @@
     types.push_back(base::ASCIIToUTF16("text/plain"));
   if (!html_text_.empty())
     types.push_back(base::ASCIIToUTF16("text/html"));
+  if (!svg_text_.empty())
+    types.push_back(base::ASCIIToUTF16("image/svg+xml"));
   if (!image_.isNull())
     types.push_back(base::ASCIIToUTF16("image/png"));
   for (auto& it : custom_data_) {
@@ -81,6 +84,11 @@
   std::move(callback).Run(html_text_, url_, 0, html_text_.length());
 }
 
+void MockClipboardHost::ReadSvg(ui::ClipboardBuffer clipboard_buffer,
+                                ReadSvgCallback callback) {
+  std::move(callback).Run(svg_text_);
+}
+
 void MockClipboardHost::ReadRtf(ui::ClipboardBuffer clipboard_buffer,
                                 ReadRtfCallback callback) {
   std::move(callback).Run(std::string());
@@ -113,6 +121,12 @@
   url_ = url;
 }
 
+void MockClipboardHost::WriteSvg(const base::string16& markup) {
+  if (needs_reset_)
+    Reset();
+  svg_text_ = markup;
+}
+
 void MockClipboardHost::WriteSmartPasteMarker() {
   if (needs_reset_)
     Reset();
diff --git a/content/test/mock_clipboard_host.h b/content/test/mock_clipboard_host.h
index d8bbea5f..6778f87 100644
--- a/content/test/mock_clipboard_host.h
+++ b/content/test/mock_clipboard_host.h
@@ -35,6 +35,8 @@
                 ReadTextCallback callback) override;
   void ReadHtml(ui::ClipboardBuffer clipboard_buffer,
                 ReadHtmlCallback callback) override;
+  void ReadSvg(ui::ClipboardBuffer clipboard_buffer,
+               ReadSvgCallback callback) override;
   void ReadRtf(ui::ClipboardBuffer clipboard_buffer,
                ReadRtfCallback callback) override;
   void ReadImage(ui::ClipboardBuffer clipboard_buffer,
@@ -44,6 +46,7 @@
                       ReadCustomDataCallback callback) override;
   void WriteText(const base::string16& text) override;
   void WriteHtml(const base::string16& markup, const GURL& url) override;
+  void WriteSvg(const base::string16& markup) override;
   void WriteSmartPasteMarker() override;
   void WriteCustomData(
       const base::flat_map<base::string16, base::string16>& data) override;
@@ -59,6 +62,7 @@
   uint64_t sequence_number_ = 0;
   base::string16 plain_text_;
   base::string16 html_text_;
+  base::string16 svg_text_;
   GURL url_;
   SkBitmap image_;
   std::map<base::string16, base::string16> custom_data_;
diff --git a/device/fido/cable/fido_cable_discovery.cc b/device/fido/cable/fido_cable_discovery.cc
index 89c2ac7..f4e8c2f 100644
--- a/device/fido/cable/fido_cable_discovery.cc
+++ b/device/fido/cable/fido_cable_discovery.cc
@@ -198,13 +198,14 @@
 FidoCableDiscovery::CreateV1HandshakeHandler(
     FidoCableDevice* device,
     const CableDiscoveryData& discovery_data,
-    const CableEidArray& eid) {
+    const CableEidArray& authenticator_eid) {
   std::unique_ptr<FidoCableHandshakeHandler> handler;
   switch (discovery_data.version) {
     case CableDiscoveryData::Version::V1: {
       // Nonce is embedded as first 8 bytes of client EID.
       std::array<uint8_t, 8> nonce;
-      const bool ok = fido_parsing_utils::ExtractArray(eid, 0, &nonce);
+      const bool ok = fido_parsing_utils::ExtractArray(
+          discovery_data.v1->client_eid, 0, &nonce);
       DCHECK(ok);
 
       return std::make_unique<FidoCableV1HandshakeHandler>(
@@ -681,7 +682,8 @@
 FidoCableDiscovery::GetCableDiscoveryDataFromAuthenticatorEid(
     CableEidArray authenticator_eid) const {
   for (const auto& candidate : discovery_data_) {
-    if (candidate.MatchV1(authenticator_eid)) {
+    if (candidate.version == CableDiscoveryData::Version::V1 &&
+        candidate.MatchV1(authenticator_eid)) {
       return Result(candidate, authenticator_eid, base::nullopt, base::nullopt);
     }
   }
diff --git a/device/fido/cable/fido_cable_discovery.h b/device/fido/cable/fido_cable_discovery.h
index 5b51168d..7104c57 100644
--- a/device/fido/cable/fido_cable_discovery.h
+++ b/device/fido/cable/fido_cable_discovery.h
@@ -61,7 +61,7 @@
   virtual std::unique_ptr<FidoCableHandshakeHandler> CreateV1HandshakeHandler(
       FidoCableDevice* device,
       const CableDiscoveryData& discovery_data,
-      const CableEidArray& eid);
+      const CableEidArray& authenticator_eid);
 
  private:
   enum class CableV1DiscoveryEvent : int;
diff --git a/device/vr/android/arcore/BUILD.gn b/device/vr/android/arcore/BUILD.gn
index 7676a20..85f76776 100644
--- a/device/vr/android/arcore/BUILD.gn
+++ b/device/vr/android/arcore/BUILD.gn
@@ -11,6 +11,7 @@
   defines = [ "IS_VR_ARCORE_IMPL" ]
   sources = [
     "address_to_id_map.h",
+    "arcore.cc",
     "arcore.h",
     "arcore_anchor_manager.cc",
     "arcore_anchor_manager.h",
@@ -18,6 +19,8 @@
     "arcore_device_provider_factory.h",
     "arcore_impl.cc",
     "arcore_impl.h",
+    "arcore_math_utils.cc",
+    "arcore_math_utils.h",
     "arcore_plane_manager.cc",
     "arcore_plane_manager.h",
     "arcore_sdk.h",
diff --git a/device/vr/android/arcore/arcore.cc b/device/vr/android/arcore/arcore.cc
new file mode 100644
index 0000000..714b3b4
--- /dev/null
+++ b/device/vr/android/arcore/arcore.cc
@@ -0,0 +1,28 @@
+// 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 "device/vr/android/arcore/arcore.h"
+
+#include "device/vr/android/arcore/arcore_math_utils.h"
+
+namespace device {
+
+gfx::Transform ArCore::GetCameraUvFromScreenUvTransform() const {
+  //
+  // Observe how kInputCoordinatesForTransform are transformed by ArCore,
+  // compute a matrix based on that and post-multiply with a matrix that
+  // performs a Y-flip.
+  //
+  // We need to add a Y flip because ArCore's
+  // AR_COORDINATES_2D_TEXTURE_NORMALIZED coordinates have the origin at the top
+  // left to match 2D Android APIs, so it needs a Y flip to get an origin at
+  // bottom left as used for textures.
+  // The post-multiplied matrix is performing a mapping: (x, y) -> (x, 1 - y).
+  //
+  return MatrixFromTransformedPoints(
+             TransformDisplayUvCoords(kInputCoordinatesForTransform)) *
+         gfx::Transform(1, 0, 0, -1, 0, 1);
+}
+
+}  // namespace device
diff --git a/device/vr/android/arcore/arcore.h b/device/vr/android/arcore/arcore.h
index 787b6f1..18e1e87 100644
--- a/device/vr/android/arcore/arcore.h
+++ b/device/vr/android/arcore/arcore.h
@@ -9,6 +9,7 @@
 #include <vector>
 
 #include "base/android/scoped_java_ref.h"
+#include "base/component_export.h"
 #include "base/macros.h"
 #include "base/optional.h"
 #include "base/time/time.h"
@@ -20,7 +21,7 @@
 
 // This allows a real or fake implementation of ArCore to
 // be used as appropriate (i.e. for testing).
-class ArCore {
+class COMPONENT_EXPORT(VR_ARCORE) ArCore {
  public:
   virtual ~ArCore() = default;
 
@@ -33,9 +34,9 @@
       const gfx::Size& frame_size,
       display::Display::Rotation display_rotation) = 0;
   virtual void SetCameraTexture(uint32_t camera_texture_id) = 0;
-  // Transform the given UV coordinates by the current display rotation.
-  virtual std::vector<float> TransformDisplayUvCoords(
-      const base::span<const float> uvs) = 0;
+
+  gfx::Transform GetCameraUvFromScreenUvTransform() const;
+
   virtual gfx::Transform GetProjectionMatrix(float near, float far) = 0;
 
   // Update ArCore state. This call blocks for up to 1/30s while waiting for a
@@ -136,6 +137,10 @@
 
   virtual void Pause() = 0;
   virtual void Resume() = 0;
+
+ protected:
+  virtual std::vector<float> TransformDisplayUvCoords(
+      const base::span<const float> uvs) const = 0;
 };
 
 class ArCoreFactory {
diff --git a/device/vr/android/arcore/arcore_impl.cc b/device/vr/android/arcore/arcore_impl.cc
index 88262a2..d319742 100644
--- a/device/vr/android/arcore/arcore_impl.cc
+++ b/device/vr/android/arcore/arcore_impl.cc
@@ -604,19 +604,25 @@
 }
 
 std::vector<float> ArCoreImpl::TransformDisplayUvCoords(
-    const base::span<const float> uvs) {
+    const base::span<const float> uvs) const {
   DCHECK(IsOnGlThread());
   DCHECK(arcore_session_.is_valid());
   DCHECK(arcore_frame_.is_valid());
 
   size_t num_elements = uvs.size();
   DCHECK(num_elements % 2 == 0);
-  std::vector<float> uvs_out(num_elements);
+  DCHECK_GE(num_elements, 6u);
 
+  std::vector<float> uvs_out(num_elements);
   ArFrame_transformCoordinates2d(
       arcore_session_.get(), arcore_frame_.get(),
       AR_COORDINATES_2D_VIEW_NORMALIZED, num_elements / 2, &uvs[0],
       AR_COORDINATES_2D_TEXTURE_NORMALIZED, &uvs_out[0]);
+
+  DVLOG(3) << __func__ << ": transformed uvs=[ " << uvs_out[0] << " , "
+           << uvs_out[1] << " , " << uvs_out[2] << " , " << uvs_out[3] << " , "
+           << uvs_out[4] << " , " << uvs_out[5] << " ]";
+
   return uvs_out;
 }
 
@@ -1412,7 +1418,7 @@
   anchor_manager_->DetachAnchor(AnchorId(anchor_id));
 }
 
-bool ArCoreImpl::IsOnGlThread() {
+bool ArCoreImpl::IsOnGlThread() const {
   return gl_thread_task_runner_->BelongsToCurrentThread();
 }
 
diff --git a/device/vr/android/arcore/arcore_impl.h b/device/vr/android/arcore/arcore_impl.h
index c6e51ff..75b72e0e 100644
--- a/device/vr/android/arcore/arcore_impl.h
+++ b/device/vr/android/arcore/arcore_impl.h
@@ -111,8 +111,6 @@
   void SetDisplayGeometry(const gfx::Size& frame_size,
                           display::Display::Rotation display_rotation) override;
   void SetCameraTexture(uint32_t camera_texture_id) override;
-  std::vector<float> TransformDisplayUvCoords(
-      const base::span<const float> uvs) override;
   gfx::Transform GetProjectionMatrix(float near, float far) override;
   mojom::VRPosePtr Update(bool* camera_updated) override;
   base::TimeDelta GetFrameTimestamp() override;
@@ -170,8 +168,12 @@
 
   void DetachAnchor(uint64_t anchor_id) override;
 
+ protected:
+  std::vector<float> TransformDisplayUvCoords(
+      const base::span<const float> uvs) const override;
+
  private:
-  bool IsOnGlThread();
+  bool IsOnGlThread() const;
   base::WeakPtr<ArCoreImpl> GetWeakPtr() {
     return weak_ptr_factory_.GetWeakPtr();
   }
diff --git a/device/vr/android/arcore/arcore_math_utils.cc b/device/vr/android/arcore/arcore_math_utils.cc
new file mode 100644
index 0000000..987353a
--- /dev/null
+++ b/device/vr/android/arcore/arcore_math_utils.cc
@@ -0,0 +1,63 @@
+// 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 "device/vr/android/arcore/arcore_math_utils.h"
+
+#include "base/check_op.h"
+#include "base/logging.h"
+
+namespace device {
+
+gfx::Transform MatrixFromTransformedPoints(const std::vector<float>& uvs) {
+  DCHECK_GE(uvs.size(), 6u);
+
+  //
+  // In order to compute the matrix, we need to solve the following 3 equations
+  // for 6 unknowns:
+  //
+  //  | a b c |   | u |   | u' |
+  //  | d e f | * | v | = | v' |
+  //  | 0 0 1 |   | 1 |   | 1  |
+  //
+  //  where 3 (u', v') pairs are passed in as an input to the method, and (u,v)
+  //  pairs are assumed to come from kInputCoordinatesForTransform.
+  //
+  // 1. From substituting point (0, 0) for (u,v), we get:
+  //
+  //  c = uvs[0]
+  //  f = uvs[1]
+  //
+  // 2. From substituting point (1, 0) for (u,v), we get:
+  //
+  //  a + c = uvs[2]  ->  a = uvs[2] - uvs[0]
+  //  d + f = uvs[3]  ->  d = uvs[3] - uvs[1]
+  //
+  // 3. From substituting point (0, 1) for (u,v), we get:
+  //
+  //  b + c = uvs[4]  ->  b = uvs[4] - uvs[0]
+  //  e + f = uvs[5]  ->  e = uvs[5] - uvs[1]
+  //
+
+  DVLOG(3) << __func__ << ": uvs=[ " << uvs[0] << " , " << uvs[1] << " , "
+           << uvs[2] << " , " << uvs[3] << " , " << uvs[4] << " , " << uvs[5]
+           << " ]";
+
+  // Assumes that |uvs| is the result of transforming the display coordinates
+  // from kInputCoordinatesForTransform - size must match.
+  DCHECK_EQ(uvs.size(), kInputCoordinatesForTransform.size());
+
+  // Transform initializes to the identity matrix and then is modified by uvs.
+  gfx::Transform result;
+  result.matrix().set(0, 0, uvs[2] - uvs[0]);
+  result.matrix().set(0, 1, uvs[4] - uvs[0]);
+  result.matrix().set(0, 3, uvs[0]);
+
+  result.matrix().set(1, 0, uvs[3] - uvs[1]);
+  result.matrix().set(1, 1, uvs[5] - uvs[1]);
+  result.matrix().set(1, 3, uvs[1]);
+
+  return result;
+}
+
+}  // namespace device
diff --git a/device/vr/android/arcore/arcore_math_utils.h b/device/vr/android/arcore/arcore_math_utils.h
new file mode 100644
index 0000000..2d3faaf
--- /dev/null
+++ b/device/vr/android/arcore/arcore_math_utils.h
@@ -0,0 +1,30 @@
+// 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 DEVICE_VR_ANDROID_ARCORE_ARCORE_MATH_UTILS_H_
+#define DEVICE_VR_ANDROID_ARCORE_ARCORE_MATH_UTILS_H_
+
+#include <array>
+#include <vector>
+
+#include "ui/gfx/transform.h"
+
+namespace device {
+
+// Creates a matrix that transforms UV coordinates based on how a well known
+// input was transformed. ArCore doesn't provide a way to get a matrix directly.
+// There's a function to transform UV vectors individually, which can't be used
+// from a shader, so we run that on selected well-known vectors
+// (kDisplayCoordinatesForTransform) and recreate the matrix from the result.
+gfx::Transform MatrixFromTransformedPoints(const std::vector<float>& uvs);
+
+// Input coordinates used when computing UV transform.
+// |MatrixFromTransformedPoints(uvs)| function above assumes that the |uvs| are
+// the result of transforming kInputCoordinatesForTransform by some matrix.
+constexpr std::array<float, 6> kInputCoordinatesForTransform = {0.f, 0.f, 1.f,
+                                                                0.f, 0.f, 1.f};
+
+}  // namespace device
+
+#endif  // DEVICE_VR_ANDROID_ARCORE_ARCORE_MATH_UTILS_H_
diff --git a/extensions/browser/extension_function_histogram_value.h b/extensions/browser/extension_function_histogram_value.h
index 1e0b466..d51635a8 100644
--- a/extensions/browser/extension_function_histogram_value.h
+++ b/extensions/browser/extension_function_histogram_value.h
@@ -1561,6 +1561,10 @@
   FILEMANAGERPRIVATEINTERNAL_GETCONTENTMETADATA = 1498,
   SEARCH_QUERY = 1499,
   FILEMANAGERPRIVATEINTERNAL_COPYIMAGETOCLIPBOARD = 1500,
+  AUTOTESTPRIVATE_WAITFORSYSTEMWEBAPPSINSTALLFUNCTION = 1501,
+  AUTOTESTPRIVATE_GETREGISTEREDSYSTEMWEBAPPSFUNCTION = 1502,
+  FILEMANAGERPRIVATEINTERNAL_TOGGLEADDEDTOHOLDINGSPACE = 1503,
+  FILEMANAGERPRIVATEINTERNAL_GETHOLDINGSPACESTATE = 1504,
   // Last entry: Add new entries above, then run:
   // python tools/metrics/histograms/update_extension_histograms.py
   ENUM_BOUNDARY
diff --git a/fuchsia/engine/web_engine_integration_test.cc b/fuchsia/engine/web_engine_integration_test.cc
index 9fefa1a..17e1daf 100644
--- a/fuchsia/engine/web_engine_integration_test.cc
+++ b/fuchsia/engine/web_engine_integration_test.cc
@@ -663,8 +663,9 @@
 #endif
 class MAYBE_VulkanWebEngineIntegrationTest : public WebEngineIntegrationTest {};
 
+// TODO(crbug.com/1104563): Flakily times-out.
 TEST_F(MAYBE_VulkanWebEngineIntegrationTest,
-       WebGLContextPresentWithVulkanFeature) {
+       DISABLED_WebGLContextPresentWithVulkanFeature) {
   StartWebEngine();
 
   fuchsia::web::CreateContextParams create_params = DefaultContextParams();
@@ -703,7 +704,8 @@
   navigation_listener_->RunUntilTitleEquals("ended");
 }
 
-TEST_F(WebEngineIntegrationTest, CameraAccess_WithPermission) {
+// TODO(crbug.com/1104562): Flakily times-out.
+TEST_F(WebEngineIntegrationTest, DISABLED_CameraAccess_WithPermission) {
   StartWebEngine();
   RunCameraTest(/*grant_permission=*/true);
 }
@@ -718,4 +720,4 @@
   command_line.AppendSwitchASCII("disable-features", "MojoVideoCapture");
   StartWebEngine(std::move(command_line));
   RunCameraTest(/*grant_permission=*/true);
-}
\ No newline at end of file
+}
diff --git a/headless/lib/browser/headless_clipboard.cc b/headless/lib/browser/headless_clipboard.cc
index 603f28d..3c503d4e 100644
--- a/headless/lib/browser/headless_clipboard.cc
+++ b/headless/lib/browser/headless_clipboard.cc
@@ -126,6 +126,18 @@
 
 // |data_dst| is not used. It's only passed to be consistent with other
 // platforms.
+void HeadlessClipboard::ReadSvg(ui::ClipboardBuffer buffer,
+                                const ui::ClipboardDataEndpoint* data_dst,
+                                base::string16* result) const {
+  result->clear();
+  const DataStore& store = GetStore(buffer);
+  auto it = store.data.find(ui::ClipboardFormatType::GetSvgType());
+  if (it != store.data.end())
+    *result = base::UTF8ToUTF16(it->second);
+}
+
+// |data_dst| is not used. It's only passed to be consistent with other
+// platforms.
 void HeadlessClipboard::ReadRTF(ui::ClipboardBuffer buffer,
                                 const ui::ClipboardDataEndpoint* data_dst,
                                 std::string* result) const {
@@ -227,6 +239,11 @@
   GetDefaultStore().html_src_url = std::string(url_data, url_len);
 }
 
+void HeadlessClipboard::WriteSvg(const char* markup_data, size_t markup_len) {
+  std::string markup(markup_data, markup_len);
+  GetDefaultStore().data[ui::ClipboardFormatType::GetSvgType()] = markup;
+}
+
 void HeadlessClipboard::WriteRTF(const char* rtf_data, size_t data_len) {
   GetDefaultStore().data[ui::ClipboardFormatType::GetRtfType()] =
       std::string(rtf_data, data_len);
diff --git a/headless/lib/browser/headless_clipboard.h b/headless/lib/browser/headless_clipboard.h
index 20bbf7bc..82351962d 100644
--- a/headless/lib/browser/headless_clipboard.h
+++ b/headless/lib/browser/headless_clipboard.h
@@ -51,6 +51,9 @@
                 std::string* src_url,
                 uint32_t* fragment_start,
                 uint32_t* fragment_end) const override;
+  void ReadSvg(ui::ClipboardBuffer buffer,
+               const ui::ClipboardDataEndpoint* data_dst,
+               base::string16* result) const override;
   void ReadRTF(ui::ClipboardBuffer buffer,
                const ui::ClipboardDataEndpoint* data_dst,
                std::string* result) const override;
@@ -83,6 +86,7 @@
                  size_t markup_len,
                  const char* url_data,
                  size_t url_len) override;
+  void WriteSvg(const char* markup_data, size_t markup_len) override;
   void WriteRTF(const char* rtf_data, size_t data_len) override;
   void WriteBookmark(const char* title_data,
                      size_t title_len,
diff --git a/infra/config/generated/cr-buildbucket.cfg b/infra/config/generated/cr-buildbucket.cfg
index 9492ace..c3dd850c 100644
--- a/infra/config/generated/cr-buildbucket.cfg
+++ b/infra/config/generated/cr-buildbucket.cfg
@@ -17846,7 +17846,7 @@
         cipd_version: "refs/heads/master"
         cmd: "recipes"
       }
-      properties: "{\"$build/goma\":{\"enable_ats\":true,\"rpc_extra_params\":\"?prod\",\"server_host\":\"goma.chromium.org\"},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"mastername\":\"tryserver.chromium.angle\",\"recipe\":\"chromium_trybot\"}"
+      properties: "{\"$build/goma\":{\"enable_ats\":true,\"jobs\":150,\"rpc_extra_params\":\"?prod\",\"server_host\":\"goma.chromium.org\"},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"mastername\":\"tryserver.chromium.angle\",\"recipe\":\"chromium_trybot\"}"
       execution_timeout_secs: 14400
       expiration_secs: 7200
       caches {
@@ -17882,7 +17882,7 @@
         cipd_version: "refs/heads/master"
         cmd: "recipes"
       }
-      properties: "{\"$build/goma\":{\"enable_ats\":true,\"rpc_extra_params\":\"?prod\",\"server_host\":\"goma.chromium.org\"},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"mastername\":\"tryserver.chromium.angle\",\"recipe\":\"chromium_trybot\"}"
+      properties: "{\"$build/goma\":{\"enable_ats\":true,\"jobs\":150,\"rpc_extra_params\":\"?prod\",\"server_host\":\"goma.chromium.org\"},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"mastername\":\"tryserver.chromium.angle\",\"recipe\":\"chromium_trybot\"}"
       execution_timeout_secs: 14400
       expiration_secs: 7200
       caches {
@@ -17918,7 +17918,7 @@
         cipd_version: "refs/heads/master"
         cmd: "recipes"
       }
-      properties: "{\"$build/goma\":{\"enable_ats\":true,\"rpc_extra_params\":\"?prod\",\"server_host\":\"goma.chromium.org\"},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"mastername\":\"tryserver.chromium.angle\",\"recipe\":\"chromium_trybot\"}"
+      properties: "{\"$build/goma\":{\"enable_ats\":true,\"jobs\":150,\"rpc_extra_params\":\"?prod\",\"server_host\":\"goma.chromium.org\"},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"mastername\":\"tryserver.chromium.angle\",\"recipe\":\"chromium_trybot\"}"
       execution_timeout_secs: 14400
       expiration_secs: 7200
       caches {
@@ -17954,7 +17954,7 @@
         cipd_version: "refs/heads/master"
         cmd: "recipes"
       }
-      properties: "{\"$build/goma\":{\"enable_ats\":true,\"rpc_extra_params\":\"?prod\",\"server_host\":\"goma.chromium.org\"},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"mastername\":\"tryserver.chromium.angle\",\"recipe\":\"chromium_trybot\"}"
+      properties: "{\"$build/goma\":{\"enable_ats\":true,\"jobs\":150,\"rpc_extra_params\":\"?prod\",\"server_host\":\"goma.chromium.org\"},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"mastername\":\"tryserver.chromium.angle\",\"recipe\":\"chromium_trybot\"}"
       execution_timeout_secs: 14400
       expiration_secs: 7200
       caches {
@@ -17990,7 +17990,7 @@
         cipd_version: "refs/heads/master"
         cmd: "recipes"
       }
-      properties: "{\"$build/goma\":{\"enable_ats\":true,\"rpc_extra_params\":\"?prod\",\"server_host\":\"goma.chromium.org\"},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"mastername\":\"tryserver.chromium.angle\",\"recipe\":\"chromium_trybot\"}"
+      properties: "{\"$build/goma\":{\"enable_ats\":true,\"jobs\":150,\"rpc_extra_params\":\"?prod\",\"server_host\":\"goma.chromium.org\"},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"mastername\":\"tryserver.chromium.angle\",\"recipe\":\"chromium_trybot\"}"
       execution_timeout_secs: 14400
       expiration_secs: 7200
       caches {
@@ -18026,7 +18026,7 @@
         cipd_version: "refs/heads/master"
         cmd: "recipes"
       }
-      properties: "{\"$build/goma\":{\"enable_ats\":true,\"rpc_extra_params\":\"?prod\",\"server_host\":\"goma.chromium.org\"},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"mastername\":\"tryserver.chromium.angle\",\"recipe\":\"chromium_trybot\"}"
+      properties: "{\"$build/goma\":{\"enable_ats\":true,\"jobs\":150,\"rpc_extra_params\":\"?prod\",\"server_host\":\"goma.chromium.org\"},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"mastername\":\"tryserver.chromium.angle\",\"recipe\":\"chromium_trybot\"}"
       execution_timeout_secs: 14400
       expiration_secs: 7200
       caches {
@@ -19196,7 +19196,7 @@
         cipd_version: "refs/heads/master"
         cmd: "recipes"
       }
-      properties: "{\"$build/goma\":{\"enable_ats\":true,\"rpc_extra_params\":\"?prod\",\"server_host\":\"goma.chromium.org\"},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"mastername\":\"tryserver.chromium.angle\",\"recipe\":\"chromium_trybot\"}"
+      properties: "{\"$build/goma\":{\"enable_ats\":true,\"jobs\":150,\"rpc_extra_params\":\"?prod\",\"server_host\":\"goma.chromium.org\"},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"mastername\":\"tryserver.chromium.angle\",\"recipe\":\"chromium_trybot\"}"
       execution_timeout_secs: 14400
       expiration_secs: 7200
       caches {
@@ -22507,7 +22507,7 @@
         cipd_version: "refs/heads/master"
         cmd: "recipes"
       }
-      properties: "{\"$build/goma\":{\"enable_ats\":true,\"rpc_extra_params\":\"?prod\",\"server_host\":\"goma.chromium.org\"},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"mastername\":\"tryserver.chromium.angle\",\"recipe\":\"chromium_trybot\"}"
+      properties: "{\"$build/goma\":{\"enable_ats\":true,\"jobs\":150,\"rpc_extra_params\":\"?prod\",\"server_host\":\"goma.chromium.org\"},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"mastername\":\"tryserver.chromium.angle\",\"recipe\":\"chromium_trybot\"}"
       execution_timeout_secs: 14400
       expiration_secs: 7200
       caches {
@@ -23873,7 +23873,7 @@
         cipd_version: "refs/heads/master"
         cmd: "recipes"
       }
-      properties: "{\"$build/goma\":{\"enable_ats\":true,\"rpc_extra_params\":\"?prod\",\"server_host\":\"goma.chromium.org\"},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"mastername\":\"tryserver.chromium.angle\",\"recipe\":\"chromium_trybot\"}"
+      properties: "{\"$build/goma\":{\"enable_ats\":true,\"jobs\":150,\"rpc_extra_params\":\"?prod\",\"server_host\":\"goma.chromium.org\"},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"mastername\":\"tryserver.chromium.angle\",\"recipe\":\"chromium_trybot\"}"
       execution_timeout_secs: 14400
       expiration_secs: 7200
       caches {
@@ -23909,7 +23909,7 @@
         cipd_version: "refs/heads/master"
         cmd: "recipes"
       }
-      properties: "{\"$build/goma\":{\"enable_ats\":true,\"rpc_extra_params\":\"?prod\",\"server_host\":\"goma.chromium.org\"},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"mastername\":\"tryserver.chromium.angle\",\"recipe\":\"chromium_trybot\"}"
+      properties: "{\"$build/goma\":{\"enable_ats\":true,\"jobs\":150,\"rpc_extra_params\":\"?prod\",\"server_host\":\"goma.chromium.org\"},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"mastername\":\"tryserver.chromium.angle\",\"recipe\":\"chromium_trybot\"}"
       execution_timeout_secs: 14400
       expiration_secs: 7200
       caches {
@@ -24765,7 +24765,7 @@
         cipd_version: "refs/heads/master"
         cmd: "recipes"
       }
-      properties: "{\"$build/goma\":{\"rpc_extra_params\":\"?prod\",\"server_host\":\"goma.chromium.org\"},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"mastername\":\"tryserver.chromium.angle\",\"recipe\":\"chromium_trybot\"}"
+      properties: "{\"$build/goma\":{\"jobs\":150,\"rpc_extra_params\":\"?prod\",\"server_host\":\"goma.chromium.org\"},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"mastername\":\"tryserver.chromium.angle\",\"recipe\":\"chromium_trybot\"}"
       execution_timeout_secs: 14400
       expiration_secs: 7200
       caches {
@@ -25777,7 +25777,7 @@
         cipd_version: "refs/heads/master"
         cmd: "recipes"
       }
-      properties: "{\"$build/goma\":{\"enable_ats\":true,\"rpc_extra_params\":\"?prod\",\"server_host\":\"goma.chromium.org\"},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"mastername\":\"tryserver.chromium.angle\",\"recipe\":\"chromium_trybot\"}"
+      properties: "{\"$build/goma\":{\"enable_ats\":true,\"jobs\":150,\"rpc_extra_params\":\"?prod\",\"server_host\":\"goma.chromium.org\"},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"mastername\":\"tryserver.chromium.angle\",\"recipe\":\"chromium_trybot\"}"
       execution_timeout_secs: 14400
       expiration_secs: 7200
       caches {
@@ -25813,7 +25813,7 @@
         cipd_version: "refs/heads/master"
         cmd: "recipes"
       }
-      properties: "{\"$build/goma\":{\"enable_ats\":true,\"rpc_extra_params\":\"?prod\",\"server_host\":\"goma.chromium.org\"},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"mastername\":\"tryserver.chromium.angle\",\"recipe\":\"chromium_trybot\"}"
+      properties: "{\"$build/goma\":{\"enable_ats\":true,\"jobs\":150,\"rpc_extra_params\":\"?prod\",\"server_host\":\"goma.chromium.org\"},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"mastername\":\"tryserver.chromium.angle\",\"recipe\":\"chromium_trybot\"}"
       execution_timeout_secs: 14400
       expiration_secs: 7200
       caches {
@@ -25849,7 +25849,7 @@
         cipd_version: "refs/heads/master"
         cmd: "recipes"
       }
-      properties: "{\"$build/goma\":{\"enable_ats\":true,\"rpc_extra_params\":\"?prod\",\"server_host\":\"goma.chromium.org\"},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"mastername\":\"tryserver.chromium.angle\",\"recipe\":\"chromium_trybot\"}"
+      properties: "{\"$build/goma\":{\"enable_ats\":true,\"jobs\":150,\"rpc_extra_params\":\"?prod\",\"server_host\":\"goma.chromium.org\"},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"mastername\":\"tryserver.chromium.angle\",\"recipe\":\"chromium_trybot\"}"
       execution_timeout_secs: 14400
       expiration_secs: 7200
       caches {
@@ -25885,7 +25885,7 @@
         cipd_version: "refs/heads/master"
         cmd: "recipes"
       }
-      properties: "{\"$build/goma\":{\"enable_ats\":true,\"rpc_extra_params\":\"?prod\",\"server_host\":\"goma.chromium.org\"},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"mastername\":\"tryserver.chromium.angle\",\"recipe\":\"chromium_trybot\"}"
+      properties: "{\"$build/goma\":{\"enable_ats\":true,\"jobs\":150,\"rpc_extra_params\":\"?prod\",\"server_host\":\"goma.chromium.org\"},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"mastername\":\"tryserver.chromium.angle\",\"recipe\":\"chromium_trybot\"}"
       execution_timeout_secs: 14400
       expiration_secs: 7200
       caches {
diff --git a/infra/config/lib/try.star b/infra/config/lib/try.star
index 04bab76..46562bf9 100644
--- a/infra/config/lib/try.star
+++ b/infra/config/lib/try.star
@@ -340,6 +340,7 @@
         name = name,
         builderless = False,
         goma_backend = builders.goma.backend.RBE_PROD,
+        goma_jobs = builders.goma.jobs.J150,
         mastername = "tryserver.chromium.angle",
         service_account = "chromium-try-gpu-builder@chops-service-accounts.iam.gserviceaccount.com",
         **kwargs
diff --git a/ios/chrome/browser/autofill/BUILD.gn b/ios/chrome/browser/autofill/BUILD.gn
index 6389a64..ee03f0b 100644
--- a/ios/chrome/browser/autofill/BUILD.gn
+++ b/ios/chrome/browser/autofill/BUILD.gn
@@ -551,7 +551,7 @@
     "//ios/web/public",
     "//ios/web/public/deprecated",
     "//ios/web/public/test:element_selector",
-    "//ios/web/public/test/http_server",
+    "//net:test_support",
   ]
   frameworks = [ "UIKit.framework" ]
 }
diff --git a/ios/chrome/browser/autofill/form_input_egtest.mm b/ios/chrome/browser/autofill/form_input_egtest.mm
index 13e0e15..23909c20 100644
--- a/ios/chrome/browser/autofill/form_input_egtest.mm
+++ b/ios/chrome/browser/autofill/form_input_egtest.mm
@@ -15,9 +15,8 @@
 #import "ios/testing/earl_grey/earl_grey_test.h"
 #import "ios/web/public/deprecated/crw_js_injection_receiver.h"
 #include "ios/web/public/test/element_selector.h"
-#import "ios/web/public/test/http_server/http_server.h"
-#include "ios/web/public/test/http_server/http_server_util.h"
 #import "ios/web/public/web_state.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -74,8 +73,8 @@
     EARL_GREY_TEST_SKIPPED(@"Skipped for iPad (no hidden toolbar in tablet)");
   }
 
-  GURL URL = web::test::HttpServer::MakeUrl(
-      "http://ios/testing/data/http_server_files/multi_field_form.html");
+  GREYAssertTrue(self.testServer->Start(), @"Test server failed to start.");
+  GURL URL = self.testServer->GetURL("/multi_field_form.html");
   [ChromeEarlGrey loadURL:URL];
 
   [ChromeEarlGrey waitForWebStateContainingText:"hello!"];
diff --git a/ios/chrome/browser/context_menu/context_menu_egtest.mm b/ios/chrome/browser/context_menu/context_menu_egtest.mm
index ee13d5c..72c4c61 100644
--- a/ios/chrome/browser/context_menu/context_menu_egtest.mm
+++ b/ios/chrome/browser/context_menu/context_menu_egtest.mm
@@ -38,7 +38,7 @@
 namespace {
 // Directory containing the |kLogoPagePath| and |kLogoPageImageSourcePath|
 // resources.
-const char kServerFilesDir[] = "ios/testing/data/http_server_files/";
+// const char kServerFilesDir[] = "ios/testing/data/http_server_files/";
 // Path to a page containing the chromium logo and the text |kLogoPageText|.
 const char kLogoPagePath[] = "/chromium_logo_page.html";
 // Path to the chromium logo.
@@ -139,8 +139,6 @@
   [super setUp];
   self.testServer->RegisterRequestHandler(
       base::BindRepeating(&StandardResponse));
-  self.testServer->ServeFilesFromSourceDirectory(
-      base::FilePath(kServerFilesDir));
   GREYAssertTrue(self.testServer->Start(), @"Server did not start.");
 }
 
diff --git a/ios/chrome/browser/device_sharing/BUILD.gn b/ios/chrome/browser/device_sharing/BUILD.gn
index 6d2231b..f938868 100644
--- a/ios/chrome/browser/device_sharing/BUILD.gn
+++ b/ios/chrome/browser/device_sharing/BUILD.gn
@@ -95,8 +95,8 @@
     "//ios/chrome/test/earl_grey:eg_test_support+eg2",
     "//ios/testing/earl_grey:eg_test_support+eg2",
     "//ios/third_party/earl_grey2:test_lib",
-    "//ios/web/public/test/http_server",
     "//net",
+    "//net:test_support",
     "//url",
   ]
 
diff --git a/ios/chrome/browser/device_sharing/handoff_manager_egtest.mm b/ios/chrome/browser/device_sharing/handoff_manager_egtest.mm
index fa4bac3..d6cda3db 100644
--- a/ios/chrome/browser/device_sharing/handoff_manager_egtest.mm
+++ b/ios/chrome/browser/device_sharing/handoff_manager_egtest.mm
@@ -8,9 +8,8 @@
 #import "ios/chrome/test/earl_grey/chrome_earl_grey.h"
 #import "ios/chrome/test/earl_grey/chrome_test_case.h"
 #import "ios/testing/earl_grey/earl_grey_test.h"
-#include "ios/web/public/test/http_server/http_server.h"
-#include "ios/web/public/test/http_server/http_server_util.h"
 #import "net/base/mac/url_conversions.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
 #include "url/gurl.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -47,6 +46,11 @@
 
 @implementation HandoffManagerTestCase
 
+- (void)setUp {
+  [super setUp];
+  GREYAssertTrue(self.testServer->Start(), @"Server did not start.");
+}
+
 #pragma mark - Tests
 
 // Tests that an empty new tab page should result in no Handoff URL.
@@ -56,8 +60,7 @@
 
 // Tests that the simple case of Handoff URL for a single page.
 - (void)testTypicalURL {
-  const GURL destinationUrl = web::test::HttpServer::MakeUrl(
-      "http://ios/testing/data/http_server_files/destination.html");
+  const GURL destinationUrl = self.testServer->GetURL("/destination.html");
   [ChromeEarlGrey loadURL:destinationUrl];
   AssertHandoffURL(destinationUrl);
 }
@@ -65,8 +68,7 @@
 // Tests Handoff URL for a new tab.
 - (void)testTypicalURLInNewTab {
   [ChromeEarlGrey openNewTab];
-  const GURL destinationUrl = web::test::HttpServer::MakeUrl(
-      "http://ios/testing/data/http_server_files/pony.html");
+  const GURL destinationUrl = self.testServer->GetURL("/pony.html");
   [ChromeEarlGrey loadURL:destinationUrl];
   AssertHandoffURL(destinationUrl);
 }
@@ -75,15 +77,13 @@
 - (void)testTypicalURLInNewIncognitoTab {
   // Opens an incognito tab and loads a web page. Check that Handoff URL is nil.
   [ChromeEarlGrey openNewIncognitoTab];
-  const GURL destinationUrl = web::test::HttpServer::MakeUrl(
-      "http://ios/testing/data/http_server_files/destination.html");
+  const GURL destinationUrl = self.testServer->GetURL("/destination.html");
   [ChromeEarlGrey loadURL:destinationUrl];
   AssertHandoffURL(GURL());
 
   // Loads a second URL on the same incognito tab. Handoff URL should still be
   // nil.
-  const GURL destinationUrl2 = web::test::HttpServer::MakeUrl(
-      "http://ios/testing/data/http_server_files/pony.html");
+  const GURL destinationUrl2 = self.testServer->GetURL("/pony.html");
   [ChromeEarlGrey loadURL:destinationUrl2];
   AssertHandoffURL(GURL());
 }
@@ -91,12 +91,9 @@
 // Tests the state for Handoff URL when creating, closing tab, and switching
 // tab.
 - (void)testMultipleSwitchingTabs {
-  const GURL tab1URL = web::test::HttpServer::MakeUrl(
-      "http://ios/testing/data/http_server_files/destination.html");
-  const GURL tab2URL = web::test::HttpServer::MakeUrl(
-      "http://ios/testing/data/http_server_files/pony.html");
-  const GURL tab3URL = web::test::HttpServer::MakeUrl(
-      "http://ios/testing/data/http_server_files/chromium_logo_page.html");
+  const GURL tab1URL = self.testServer->GetURL("/destination.html");
+  const GURL tab2URL = self.testServer->GetURL("/pony.html");
+  const GURL tab3URL = self.testServer->GetURL("/chromium_logo_page.html");
 
   // Sets up the state for 3 tabs.
   [ChromeEarlGrey loadURL:tab1URL];
@@ -118,12 +115,9 @@
 // Tests the state for Handoff URL when switching between normal tabs and
 // incognito tabs.
 - (void)testSwitchBetweenNormalAndIncognitoTabs {
-  const GURL tab1URL = web::test::HttpServer::MakeUrl(
-      "http://ios/testing/data/http_server_files/destination.html");
-  const GURL tab2URL = web::test::HttpServer::MakeUrl(
-      "http://ios/testing/data/http_server_files/pony.html");
-  const GURL tab3URL = web::test::HttpServer::MakeUrl(
-      "http://ios/testing/data/http_server_files/chromium_logo_page.html");
+  const GURL tab1URL = self.testServer->GetURL("/destination.html");
+  const GURL tab2URL = self.testServer->GetURL("/pony.html");
+  const GURL tab3URL = self.testServer->GetURL("/chromium_logo_page.html");
 
   // Loads one page.
   [ChromeEarlGrey loadURL:tab1URL];
diff --git a/ios/chrome/browser/ui/autofill/save_profile_egtest.mm b/ios/chrome/browser/ui/autofill/save_profile_egtest.mm
index 5e73229..3f9b270 100644
--- a/ios/chrome/browser/ui/autofill/save_profile_egtest.mm
+++ b/ios/chrome/browser/ui/autofill/save_profile_egtest.mm
@@ -10,6 +10,7 @@
 #import "ios/chrome/test/earl_grey/chrome_test_case.h"
 #import "ios/testing/earl_grey/earl_grey_test.h"
 #import "ios/web/public/test/http_server/http_server.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -19,8 +20,7 @@
 namespace {
 
 // URLs of the test pages.
-const char kProfileForm[] =
-    "http://ios/testing/data/http_server_files/autofill_smoke_test.html";
+const char kProfileForm[] = "/autofill_smoke_test.html";
 
 }  // namepsace
 
@@ -48,7 +48,8 @@
 
 // Ensures that the profile is saved to Chrome after submitting the form.
 - (void)testUserData_LocalSave {
-  [ChromeEarlGrey loadURL:web::test::HttpServer::MakeUrl(kProfileForm)];
+  GREYAssertTrue(self.testServer->Start(), @"Server did not start.");
+  [ChromeEarlGrey loadURL:self.testServer->GetURL(kProfileForm)];
 
   // Ensure there are no saved profiles.
   GREYAssertEqual(0U, [AutofillAppInterface profilesCount],
diff --git a/ios/chrome/browser/ui/bookmarks/BUILD.gn b/ios/chrome/browser/ui/bookmarks/BUILD.gn
index 58d06083..4542a51 100644
--- a/ios/chrome/browser/ui/bookmarks/BUILD.gn
+++ b/ios/chrome/browser/ui/bookmarks/BUILD.gn
@@ -217,8 +217,8 @@
     "//ios/public/provider/chrome/browser/signin:fake_chrome_identity",
     "//ios/testing/earl_grey:eg_test_support+eg2",
     "//ios/third_party/earl_grey2:test_lib",
-    "//ios/web/public/test/http_server",
     "//net",
+    "//net:test_support",
     "//ui/base",
   ]
   frameworks = [ "UIKit.framework" ]
diff --git a/ios/chrome/browser/ui/bookmarks/bookmarks_egtest.mm b/ios/chrome/browser/ui/bookmarks/bookmarks_egtest.mm
index 97ba5ca17..c4b9ab71 100644
--- a/ios/chrome/browser/ui/bookmarks/bookmarks_egtest.mm
+++ b/ios/chrome/browser/ui/bookmarks/bookmarks_egtest.mm
@@ -19,8 +19,8 @@
 #import "ios/chrome/test/earl_grey/chrome_matchers.h"
 #import "ios/chrome/test/earl_grey/chrome_test_case.h"
 #import "ios/testing/earl_grey/earl_grey_test.h"
-#import "ios/web/public/test/http_server/http_server.h"
 #include "net/base/net_errors.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
 #include "ui/base/l10n/l10n_util.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -63,8 +63,9 @@
 // Verifies that adding a bookmark and removing a bookmark via the UI properly
 // updates the BookmarkModel.
 - (void)testAddRemoveBookmark {
-  const GURL bookmarkedURL = web::test::HttpServer::MakeUrl(
-      "http://ios/testing/data/http_server_files/pony.html");
+  GREYAssertTrue(self.testServer->Start(), @"Server did not start.");
+
+  const GURL bookmarkedURL = self.testServer->GetURL("/pony.html");
   std::string expectedURLContent = bookmarkedURL.GetContent();
   NSString* bookmarkTitle = @"my bookmark";
 
@@ -143,10 +144,10 @@
 
 // Test to set bookmarks in multiple tabs.
 - (void)testBookmarkMultipleTabs {
-  const GURL firstURL = web::test::HttpServer::MakeUrl(
-      "http://ios/testing/data/http_server_files/pony.html");
-  const GURL secondURL = web::test::HttpServer::MakeUrl(
-      "http://ios/testing/data/http_server_files/destination.html");
+  GREYAssertTrue(self.testServer->Start(), @"Server did not start.");
+
+  const GURL firstURL = self.testServer->GetURL("/pony.html");
+  const GURL secondURL = self.testServer->GetURL("/destination.html");
   [ChromeEarlGrey loadURL:firstURL];
   [ChromeEarlGrey openNewTab];
   [ChromeEarlGrey loadURL:secondURL];
diff --git a/ios/chrome/browser/ui/bookmarks/bookmarks_folders_egtest.mm b/ios/chrome/browser/ui/bookmarks/bookmarks_folders_egtest.mm
index 6ec9f8a..03b498df 100644
--- a/ios/chrome/browser/ui/bookmarks/bookmarks_folders_egtest.mm
+++ b/ios/chrome/browser/ui/bookmarks/bookmarks_folders_egtest.mm
@@ -20,7 +20,7 @@
 #import "ios/chrome/test/earl_grey/chrome_matchers.h"
 #import "ios/chrome/test/earl_grey/chrome_test_case.h"
 #import "ios/testing/earl_grey/earl_grey_test.h"
-#import "ios/web/public/test/http_server/http_server.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
 #include "ui/base/l10n/l10n_util.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -850,8 +850,8 @@
 
   // Verify that the bookmark that is going to be added is not in the
   // BookmarkModel.
-  const GURL bookmarkedURL = web::test::HttpServer::MakeUrl(
-      "http://ios/testing/data/http_server_files/fullscreen.html");
+  GREYAssertTrue(self.testServer->Start(), @"Server did not start.");
+  const GURL bookmarkedURL = self.testServer->GetURL("/fullscreen.html");
   NSString* const bookmarkedURLString =
       base::SysUTF8ToNSString(bookmarkedURL.spec());
   [BookmarkEarlGrey verifyBookmarksWithTitle:bookmarkedURLString
@@ -991,8 +991,8 @@
 
 // Test the creation of a bookmark and new folder (by tapping on the star).
 - (void)testAddBookmarkInNewFolder {
-  const GURL bookmarkedURL = web::test::HttpServer::MakeUrl(
-      "http://ios/testing/data/http_server_files/pony.html");
+  GREYAssertTrue(self.testServer->Start(), @"Server did not start.");
+  const GURL bookmarkedURL = self.testServer->GetURL("/pony.html");
   std::string expectedURLContent = bookmarkedURL.GetContent();
 
   [ChromeEarlGrey loadURL:bookmarkedURL];
diff --git a/ios/chrome/browser/ui/infobars/BUILD.gn b/ios/chrome/browser/ui/infobars/BUILD.gn
index d7af0e60..ad6d7bc 100644
--- a/ios/chrome/browser/ui/infobars/BUILD.gn
+++ b/ios/chrome/browser/ui/infobars/BUILD.gn
@@ -178,6 +178,7 @@
     "//ios/third_party/earl_grey2:test_lib",
     "//ios/web/public/test/http_server",
     "//net",
+    "//net:test_support",
     "//ui/base",
     "//url",
   ]
diff --git a/ios/chrome/browser/ui/infobars/infobar_egtest.mm b/ios/chrome/browser/ui/infobars/infobar_egtest.mm
index 31a0efcd..61c1792f 100644
--- a/ios/chrome/browser/ui/infobars/infobar_egtest.mm
+++ b/ios/chrome/browser/ui/infobars/infobar_egtest.mm
@@ -17,8 +17,7 @@
 #import "ios/chrome/test/earl_grey/chrome_test_case.h"
 #import "ios/testing/earl_grey/app_launch_manager.h"
 #import "ios/testing/earl_grey/earl_grey_test.h"
-#import "ios/web/public/test/http_server/http_server.h"
-#include "ios/web/public/test/http_server/http_server_util.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
 #include "url/gurl.h"
 #include "url/url_constants.h"
 
@@ -95,11 +94,15 @@
   return config;
 }
 
+- (void)setUp {
+  [super setUp];
+  GREYAssertTrue(self.testServer->Start(), @"Server did not start.");
+}
+
 // Tests that page infobars don't persist on navigation.
 - (void)testInfobarsDismissOnNavigate {
   // Open a new tab and navigate to the test page.
-  const GURL testURL = web::test::HttpServer::MakeUrl(
-      "http://ios/testing/data/http_server_files/pony.html");
+  const GURL testURL = self.testServer->GetURL("/pony.html");
   [ChromeEarlGrey loadURL:testURL];
   [ChromeEarlGrey waitForMainTabCount:1];
 
@@ -126,10 +129,8 @@
 // Tests that page infobars persist only on the tabs they are opened on, and
 // that navigation in other tabs doesn't affect them.
 - (void)testInfobarTabSwitch {
-  const GURL destinationURL = web::test::HttpServer::MakeUrl(
-      "http://ios/testing/data/http_server_files/destination.html");
-  const GURL ponyURL = web::test::HttpServer::MakeUrl(
-      "http://ios/testing/data/http_server_files/pony.html");
+  const GURL destinationURL = self.testServer->GetURL("/destination.html");
+  const GURL ponyURL = self.testServer->GetURL("/pony.html");
 
   // Create the first tab and navigate to the test page.
   [ChromeEarlGrey loadURL:destinationURL];
@@ -170,8 +171,7 @@
 // Tests that the Infobar dissapears once the "OK" button is tapped.
 - (void)testInfobarButtonDismissal {
   // Open a new tab and navigate to the test page.
-  const GURL testURL = web::test::HttpServer::MakeUrl(
-      "http://ios/testing/data/http_server_files/pony.html");
+  const GURL testURL = self.testServer->GetURL("/pony.html");
   [ChromeEarlGrey loadURL:testURL];
   [ChromeEarlGrey waitForMainTabCount:1];
 
@@ -207,8 +207,7 @@
 #endif
 
   // Open a new tab and navigate to the test page.
-  const GURL testURL = web::test::HttpServer::MakeUrl(
-      "http://ios/testing/data/http_server_files/pony.html");
+  const GURL testURL = self.testServer->GetURL("/pony.html");
   [ChromeEarlGrey loadURL:testURL];
   [ChromeEarlGrey waitForMainTabCount:1];
 
@@ -253,8 +252,7 @@
 #endif
 
   // Open a new tab and navigate to the test page.
-  const GURL testURL = web::test::HttpServer::MakeUrl(
-      "http://ios/testing/data/http_server_files/pony.html");
+  const GURL testURL = self.testServer->GetURL("/pony.html");
   [ChromeEarlGrey loadURL:testURL];
   [ChromeEarlGrey waitForMainTabCount:1];
 
diff --git a/ios/chrome/browser/ui/sad_tab/BUILD.gn b/ios/chrome/browser/ui/sad_tab/BUILD.gn
index c4ea269..db264fda 100644
--- a/ios/chrome/browser/ui/sad_tab/BUILD.gn
+++ b/ios/chrome/browser/ui/sad_tab/BUILD.gn
@@ -97,7 +97,7 @@
     "//ios/testing:embedded_test_server_support",
     "//ios/testing/earl_grey:eg_test_support+eg2",
     "//ios/third_party/earl_grey2:test_lib",
-    "//ios/web/public/test/http_server",
+    "//net:test_support",
     "//ui/base",
   ]
 
diff --git a/ios/chrome/browser/ui/sad_tab/sad_tab_view_egtest.mm b/ios/chrome/browser/ui/sad_tab/sad_tab_view_egtest.mm
index 7ba22bb..a12538a 100644
--- a/ios/chrome/browser/ui/sad_tab/sad_tab_view_egtest.mm
+++ b/ios/chrome/browser/ui/sad_tab/sad_tab_view_egtest.mm
@@ -10,8 +10,7 @@
 #import "ios/chrome/test/earl_grey/chrome_matchers.h"
 #import "ios/chrome/test/earl_grey/chrome_test_case.h"
 #import "ios/testing/earl_grey/earl_grey_test.h"
-#import "ios/web/public/test/http_server/http_server.h"
-#include "ios/web/public/test/http_server/http_server_util.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
 #include "ui/base/l10n/l10n_util_mac.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -70,8 +69,8 @@
     EARL_GREY_TEST_DISABLED(@"Fails on iOS 13.");
   }
   // Prepare a simple but known URL to avoid testing from the NTP.
-  const GURL simple_URL = web::test::HttpServer::MakeUrl(
-      "http://ios/testing/data/http_server_files/destination.html");
+  GREYAssertTrue(self.testServer->Start(), @"Server did not start.");
+  const GURL simple_URL = self.testServer->GetURL("/destination.html");
 
   // Prepare a helper block to test Sad Tab navigating from and to normal pages.
   void (^loadAndCheckSimpleURL)() = ^void() {
diff --git a/ios/chrome/browser/ui/toolbar/toolbar_egtest.mm b/ios/chrome/browser/ui/toolbar/toolbar_egtest.mm
index af82e39..e765b72 100644
--- a/ios/chrome/browser/ui/toolbar/toolbar_egtest.mm
+++ b/ios/chrome/browser/ui/toolbar/toolbar_egtest.mm
@@ -15,6 +15,7 @@
 #import "ios/testing/earl_grey/earl_grey_test.h"
 #import "ios/web/public/test/http_server/http_server.h"
 #include "ios/web/public/test/http_server/http_server_util.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
 #include "ui/base/l10n/l10n_util_mac.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -36,8 +37,8 @@
 // Verifies that entering a URL in the omnibox navigates to the correct URL and
 // displays content.
 - (void)testEnterURL {
-  const GURL URL = web::test::HttpServer::MakeUrl(
-      "http://ios/testing/data/http_server_files/destination.html");
+  GREYAssertTrue(self.testServer->Start(), @"Server did not start.");
+  const GURL URL = self.testServer->GetURL("/destination.html");
   [ChromeEarlGrey loadURL:URL];
   [[EarlGrey selectElementWithMatcher:OmniboxText(URL.GetContent())]
       assertWithMatcher:grey_notNil()];
diff --git a/ios/chrome/browser/ui/webui/inspect/inspect_ui_egtest.mm b/ios/chrome/browser/ui/webui/inspect/inspect_ui_egtest.mm
index 40a2530a8..81c9b94 100644
--- a/ios/chrome/browser/ui/webui/inspect/inspect_ui_egtest.mm
+++ b/ios/chrome/browser/ui/webui/inspect/inspect_ui_egtest.mm
@@ -20,10 +20,6 @@
 #endif
 
 namespace {
-// Directory containing the |kLogoPagePath| and |kLogoPageImageSourcePath|
-// resources.
-const char kServerFilesDir[] = "ios/testing/data/http_server_files/";
-
 // Id of the "Start Logging" button.
 NSString* const kStartLoggingButtonId = @"start-logging";
 // Id of the "Stop Logging" button.
@@ -100,8 +96,6 @@
 
 - (void)setUp {
   [super setUp];
-  self.testServer->ServeFilesFromSourceDirectory(
-      base::FilePath(kServerFilesDir));
   GREYAssertTrue(self.testServer->Start(), @"Server did not start.");
 }
 
diff --git a/ios/chrome/browser/web/browsing_egtest.mm b/ios/chrome/browser/web/browsing_egtest.mm
index b0a88155..c9a7d3c 100644
--- a/ios/chrome/browser/web/browsing_egtest.mm
+++ b/ios/chrome/browser/web/browsing_egtest.mm
@@ -21,6 +21,7 @@
 #import "ios/web/public/test/http_server/http_server.h"
 #include "ios/web/public/test/http_server/http_server_util.h"
 #include "net/http/http_response_headers.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "url/gurl.h"
 
@@ -110,8 +111,8 @@
     EARL_GREY_TEST_SKIPPED(@"Tab Title not displayed on handset.");
   }
 
-  const GURL destinationURL = web::test::HttpServer::MakeUrl(
-      "http://ios/testing/data/http_server_files/destination.html");
+  GREYAssertTrue(self.testServer->Start(), @"Server did not start.");
+  const GURL destinationURL = self.testServer->GetURL("/destination.html");
   [ChromeEarlGrey loadURL:destinationURL];
 
   // Add 3 for the "://" which is not considered part of the scheme
@@ -128,8 +129,8 @@
     EARL_GREY_TEST_SKIPPED(@"Tab Title not displayed on handset.");
   }
 
-  const GURL destinationURL = web::test::HttpServer::MakeUrl(
-      "http://ios/testing/data/http_server_files/testpage.pdf");
+  GREYAssertTrue(self.testServer->Start(), @"Server did not start.");
+  const GURL destinationURL = self.testServer->GetURL("/testpage.pdf");
   [ChromeEarlGrey loadURL:destinationURL];
 
   // Add 3 for the "://" which is not considered part of the scheme
diff --git a/ios/chrome/browser/web/browsing_prevent_default_egtest.mm b/ios/chrome/browser/web/browsing_prevent_default_egtest.mm
index 398f371..10926185 100644
--- a/ios/chrome/browser/web/browsing_prevent_default_egtest.mm
+++ b/ios/chrome/browser/web/browsing_prevent_default_egtest.mm
@@ -8,8 +8,7 @@
 #import "ios/chrome/test/earl_grey/chrome_test_case.h"
 #include "ios/chrome/test/earl_grey/scoped_block_popups_pref.h"
 #import "ios/testing/earl_grey/earl_grey_test.h"
-#import "ios/web/public/test/http_server/http_server.h"
-#include "ios/web/public/test/http_server/http_server_util.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
 #include "url/url_constants.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -20,13 +19,6 @@
 
 // Timeout to use when waiting for a condition to be true.
 const CFTimeInterval kConditionTimeout = 4.0;
-
-// Returns the URL for the HTML that is used for testing purposes in this file.
-GURL GetTestUrl() {
-  return web::test::HttpServer::MakeUrl(
-      "http://ios/testing/data/http_server_files/"
-      "browsing_prevent_default_test_page.html");
-}
 }  // namespace
 
 // Tests that the javascript preventDefault() function correctly prevents new
@@ -43,7 +35,9 @@
   // new tabs.
   ScopedBlockPopupsPref scoper(CONTENT_SETTING_ALLOW);
 
-  const GURL testURL = GetTestUrl();
+  GREYAssertTrue(self.testServer->Start(), @"Test server failed to start.");
+  const GURL testURL =
+      self.testServer->GetURL("/browsing_prevent_default_test_page.html");
   [ChromeEarlGrey loadURL:testURL];
   [ChromeEarlGrey waitForMainTabCount:1];
 
@@ -81,7 +75,9 @@
   // new tabs.
   ScopedBlockPopupsPref scoper(CONTENT_SETTING_ALLOW);
 
-  const GURL testURL = GetTestUrl();
+  GREYAssertTrue(self.testServer->Start(), @"Test server failed to start.");
+  const GURL testURL =
+      self.testServer->GetURL("/browsing_prevent_default_test_page.html");
   [ChromeEarlGrey loadURL:testURL];
   [ChromeEarlGrey waitForMainTabCount:1];
 
diff --git a/ios/chrome/browser/web/child_window_open_by_dom_egtest.mm b/ios/chrome/browser/web/child_window_open_by_dom_egtest.mm
index 5dd23051..4158b71 100644
--- a/ios/chrome/browser/web/child_window_open_by_dom_egtest.mm
+++ b/ios/chrome/browser/web/child_window_open_by_dom_egtest.mm
@@ -9,18 +9,16 @@
 #import "ios/chrome/test/earl_grey/chrome_matchers.h"
 #import "ios/chrome/test/earl_grey/chrome_test_case.h"
 #import "ios/testing/earl_grey/earl_grey_test.h"
-#import "ios/web/public/test/http_server/http_server.h"
-#include "ios/web/public/test/http_server/http_server_util.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "net/test/embedded_test_server/http_request.h"
 #include "net/test/embedded_test_server/http_response.h"
+#include "net/test/embedded_test_server/request_handler_util.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
 #endif
 
 using chrome_test_util::OmniboxText;
-using web::test::HttpServer;
 
 namespace {
 // Test link text and ids.
@@ -40,17 +38,9 @@
 const char kSlowPathContent[] = "Slow Page";
 int kSlowPathDelay = 3;
 
-// net::EmbeddedTestServer handler for kWriteReloadPath and kSlowPath.
-std::unique_ptr<net::test_server::HttpResponse> WriteReloadHandler(
+// net::EmbeddedTestServer handler for kWriteReloadPath.
+std::unique_ptr<net::test_server::HttpResponse> ReloadHandler(
     const net::test_server::HttpRequest& request) {
-  if (request.GetURL().path() == kSlowPath) {
-    auto slow_http_response =
-        std::make_unique<net::test_server::DelayedHttpResponse>(
-            base::TimeDelta::FromSeconds(kSlowPathDelay));
-    slow_http_response->set_content_type("text/html");
-    slow_http_response->set_content(kSlowPathContent);
-    return std::move(slow_http_response);
-  }
   auto http_response = std::make_unique<net::test_server::BasicHttpResponse>();
   http_response->set_content_type("text/html");
   http_response->set_content(base::StringPrintf(
@@ -62,6 +52,17 @@
   return std::move(http_response);
 }
 
+// net::EmbeddedTestServer handler for kSlowPath.
+std::unique_ptr<net::test_server::HttpResponse> SlowResponseHandler(
+    const net::test_server::HttpRequest& request) {
+  auto slow_http_response =
+      std::make_unique<net::test_server::DelayedHttpResponse>(
+          base::TimeDelta::FromSeconds(kSlowPathDelay));
+  slow_http_response->set_content_type("text/html");
+  slow_http_response->set_content(kSlowPathContent);
+  return std::move(slow_http_response);
+}
+
 }  // namespace
 
 // Test case for child windows opened by DOM.
@@ -95,10 +96,17 @@
 
 - (void)setUp {
   [super setUp];
+  self.testServer->RegisterDefaultHandler(base::BindRepeating(
+      net::test_server::HandlePrefixedRequest, kWriteReloadPath,
+      base::BindRepeating(&ReloadHandler)));
+  self.testServer->RegisterDefaultHandler(
+      base::BindRepeating(net::test_server::HandlePrefixedRequest, kSlowPath,
+                          base::BindRepeating(&SlowResponseHandler)));
+  GREYAssertTrue(self.testServer->Start(), @"Test server failed to start.");
+
   // Open the test page. There should only be one tab open.
-  const char kChildWindowTestURL[] =
-      "http://ios/testing/data/http_server_files/window_proxy.html";
-  [ChromeEarlGrey loadURL:HttpServer::MakeUrl(kChildWindowTestURL)];
+  const char kChildWindowTestURL[] = "/window_proxy.html";
+  [ChromeEarlGrey loadURL:self.testServer->GetURL(kChildWindowTestURL)];
   [ChromeEarlGrey waitForWebStateContainingText:(base::SysNSStringToUTF8(
                                                     kNamedWindowLink))];
   [ChromeEarlGrey waitForMainTabCount:1];
@@ -243,8 +251,6 @@
 // Tests that reloading a window.open with a document.write does not leave a
 // dangling pending item. This is a regression test from crbug.com/1011898
 - (void)testWindowOpenWriteAndReload {
-  self.testServer->RegisterRequestHandler(base::Bind(&WriteReloadHandler));
-  GREYAssertTrue(self.testServer->Start(), @"Test server failed to start.");
   [ChromeEarlGrey loadURL:self.testServer->GetURL(kWriteReloadPath)];
   [ChromeEarlGrey tapWebStateElementWithID:@"button"];
   [ChromeEarlGrey reload];
diff --git a/media/capture/video/win/video_capture_device_factory_win.cc b/media/capture/video/win/video_capture_device_factory_win.cc
index ef013ab..d882823 100644
--- a/media/capture/video/win/video_capture_device_factory_win.cc
+++ b/media/capture/video/win/video_capture_device_factory_win.cc
@@ -13,6 +13,8 @@
 #include <wrl.h>
 #include <wrl/client.h>
 
+#include <utility>
+
 #include "base/bind.h"
 #include "base/command_line.h"
 #include "base/feature_list.h"
@@ -62,6 +64,10 @@
     L"\"{24e552d7-6523-47f7-a647-d3465bf1f5ca}\" AND "
     L"System.Devices.InterfaceEnabled:=System.StructuredQueryType.Boolean#True";
 
+// Class GUID for KSCATEGORY_VIDEO_CAMERA. Only devices from that category will
+// contain this GUID in their |device_id|.
+const char kVideoCameraGuid[] = "e5323777-f976-4f5b-9b55-b94699c46e44";
+
 // Avoid enumerating and/or using certain devices due to they provoking crashes
 // or any other reason (http://crbug.com/378494). This enum is defined for the
 // purposes of UMA collection. Existing entries cannot be removed.
@@ -271,6 +277,25 @@
   return true;
 }
 
+void FindAndSetDefaultVideoCamera(
+    std::vector<VideoCaptureDeviceInfo>* devices_info) {
+  // When available, the default video camera should be external with
+  // MEDIA_VIDEO_FACING_NONE. Otherwise, it should be internal with
+  // MEDIA_VIDEO_FACING_USER. It occupies the first index in |devices_info|.
+  for (auto it = devices_info->begin(); it != devices_info->end(); ++it) {
+    // Default video camera belongs to KSCATEGORY_VIDEO_CAMERA.
+    if (it->descriptor.device_id.find(kVideoCameraGuid) != std::string::npos) {
+      if (it->descriptor.facing == VideoFacingMode::MEDIA_VIDEO_FACING_NONE) {
+        std::iter_swap(devices_info->begin(), it);
+        break;  // Stop iterating once an external video camera is found.
+      } else if (it->descriptor.facing ==
+                 VideoFacingMode::MEDIA_VIDEO_FACING_USER) {
+        std::iter_swap(devices_info->begin(), it);
+      }
+    }
+  }
+}
+
 }  // namespace
 
 // Returns true if the current platform supports the Media Foundation API
@@ -645,6 +670,8 @@
     }
   }
 
+  FindAndSetDefaultVideoCamera(&devices_info);
+
   origin_task_runner_->PostTask(
       FROM_HERE, base::BindOnce(&VideoCaptureDeviceFactoryWin::DeviceInfoReady,
                                 base::Unretained(this), std::move(devices_info),
diff --git a/media/gpu/test/video_encoder/decoder_buffer_validator.cc b/media/gpu/test/video_encoder/decoder_buffer_validator.cc
index f7573ae..e704a37 100644
--- a/media/gpu/test/video_encoder/decoder_buffer_validator.cc
+++ b/media/gpu/test/video_encoder/decoder_buffer_validator.cc
@@ -45,7 +45,7 @@
 void DecoderBufferValidator::ProcessBitstream(
     scoped_refptr<BitstreamRef> bitstream,
     size_t frame_index) {
-  if (!Validate(*bitstream->buffer))
+  if (!Validate(*bitstream->buffer, bitstream->metadata))
     num_errors_++;
 }
 
@@ -53,6 +53,53 @@
   return num_errors_ == 0;
 }
 
+TemporalLayerValidator::TemporalLayerValidator(size_t num_temporal_layers)
+    : num_temporal_layers_(num_temporal_layers) {
+  reference_frames_.fill(0);
+}
+
+TemporalLayerValidator::~TemporalLayerValidator() = default;
+
+bool TemporalLayerValidator::ValidateAndUpdate(bool keyframe,
+                                               uint8_t temporal_index,
+                                               uint8_t reference_index,
+                                               uint8_t refresh_frame_index) {
+  if (temporal_index >= num_temporal_layers_) {
+    LOG(ERROR) << "Temporal layer index is not less than the number of temporal"
+               << " layers, temporal_index=" << temporal_index
+               << ", num_temporal_layers=" << num_temporal_layers_;
+    return false;
+  }
+  if (keyframe) {
+    if (temporal_index != 0) {
+      LOG(ERROR) << "Key frame exists in non base layer, temporal_index="
+                 << temporal_index;
+      return false;
+    }
+    reference_frames_.fill(temporal_index);
+    return true;
+  }
+
+  const std::bitset<kReferenceFramePoolSize> reference(reference_index);
+  for (size_t i = 0; i < kReferenceFramePoolSize; ++i) {
+    if (!reference[i])
+      continue;
+    const uint8_t referenced_index = reference_frames_[i];
+    if (referenced_index > temporal_index) {
+      LOG(ERROR) << "Frame in upper layer referenced, temporal_index="
+                 << temporal_index
+                 << ", referenced temporal index=" << referenced_index;
+      return false;
+    }
+  }
+  const std::bitset<kReferenceFramePoolSize> refresh(refresh_frame_index);
+  for (size_t i = 0; i < kReferenceFramePoolSize; ++i) {
+    if (refresh[i])
+      reference_frames_[i] = temporal_index;
+  }
+  return true;
+}
+
 H264Validator::H264Validator(VideoCodecProfile profile,
                              const gfx::Rect& visible_rect,
                              base::Optional<uint8_t> level)
@@ -63,7 +110,8 @@
 
 H264Validator::~H264Validator() = default;
 
-bool H264Validator::Validate(const DecoderBuffer& decoder_buffer) {
+bool H264Validator::Validate(const DecoderBuffer& decoder_buffer,
+                             const BitstreamBufferMetadata& metadata) {
   parser_.SetStream(decoder_buffer.data(), decoder_buffer.data_size());
 
   size_t num_frames = 0;
@@ -190,7 +238,8 @@
 
 VP8Validator::~VP8Validator() = default;
 
-bool VP8Validator::Validate(const DecoderBuffer& decoder_buffer) {
+bool VP8Validator::Validate(const DecoderBuffer& decoder_buffer,
+                            const BitstreamBufferMetadata& metadata) {
   // TODO(hiroh): We could be getting more frames in the buffer, but there is
   // no simple way to detect this. We'd need to parse the frames and go through
   // partition numbers/sizes. For now assume one frame per buffer.
@@ -215,14 +264,20 @@
 }
 
 VP9Validator::VP9Validator(VideoCodecProfile profile,
-                           const gfx::Rect& visible_rect)
+                           const gfx::Rect& visible_rect,
+                           size_t num_temporal_layers)
     : DecoderBufferValidator(visible_rect),
       parser_(false /* parsing_compressed_header */),
-      profile_(VideoCodecProfileToVP9Profile(profile)) {}
+      profile_(VideoCodecProfileToVP9Profile(profile)),
+      temporal_layer_validator_(
+          num_temporal_layers > 1u
+              ? std::make_unique<TemporalLayerValidator>(num_temporal_layers)
+              : nullptr) {}
 
 VP9Validator::~VP9Validator() = default;
 
-bool VP9Validator::Validate(const DecoderBuffer& decoder_buffer) {
+bool VP9Validator::Validate(const DecoderBuffer& decoder_buffer,
+                            const BitstreamBufferMetadata& metadata) {
   // TODO(hiroh): We could be getting more frames in the buffer, but there is
   // no simple way to detect this. We'd need to parse the frames and go through
   // partition numbers/sizes. For now assume one frame per buffer.
@@ -234,6 +289,11 @@
     LOG(ERROR) << "Failed parsing";
     return false;
   }
+  if (metadata.key_frame != header.IsKeyframe()) {
+    LOG(ERROR) << "Keyframe info in metadata is wrong, metadata.keyframe="
+               << metadata.key_frame;
+    return false;
+  }
 
   if (header.IsKeyframe()) {
     seen_keyframe_ = true;
@@ -252,7 +312,36 @@
     }
   }
 
-  return seen_keyframe_ && header.show_frame;
+  if (!seen_keyframe_) {
+    LOG(ERROR) << "First frame is not key frame";
+    return false;
+  }
+
+  if (!header.show_frame) {
+    LOG(ERROR) << "VideoEncodeAccelerator outputs non showable frame";
+    return false;
+  }
+
+  if (!temporal_layer_validator_)
+    return true;
+
+  if (!metadata.vp9.has_value()) {
+    LOG(ERROR) << "No metadata in temporal layer encoding";
+    return false;
+  }
+  uint8_t reference_index = 0;
+  for (size_t i = 0; i < kVp9NumRefsPerFrame; ++i) {
+    uint8_t ref_frame_index = header.ref_frame_idx[i];
+    if (ref_frame_index >= static_cast<uint8_t>(kVp9NumRefFrames)) {
+      LOG(ERROR) << "Invalid reference frame index: "
+                 << static_cast<int>(ref_frame_index);
+      return false;
+    }
+    reference_index |= (1u << ref_frame_index);
+  }
+  return temporal_layer_validator_->ValidateAndUpdate(
+      header.IsKeyframe(), metadata.vp9->temporal_idx, reference_index,
+      header.refresh_frame_flags);
 }
 }  // namespace test
 }  // namespace media
diff --git a/media/gpu/test/video_encoder/decoder_buffer_validator.h b/media/gpu/test/video_encoder/decoder_buffer_validator.h
index d0fa8fa..9d09675 100644
--- a/media/gpu/test/video_encoder/decoder_buffer_validator.h
+++ b/media/gpu/test/video_encoder/decoder_buffer_validator.h
@@ -32,7 +32,8 @@
 
  protected:
   // Returns true if decoder_buffer is valid and expected, otherwise false.
-  virtual bool Validate(const DecoderBuffer& decoder_buffer) = 0;
+  virtual bool Validate(const DecoderBuffer& decoder_buffer,
+                        const BitstreamBufferMetadata& metadata) = 0;
 
   // The expected visible rectangle that |decoder_buffer| has.
   const gfx::Rect visible_rect_;
@@ -42,6 +43,24 @@
   size_t num_errors_ = 0;
 };
 
+// TemporalLayerValidator checks whether the stream is valid on each temporal
+// layer.
+class TemporalLayerValidator {
+ public:
+  TemporalLayerValidator(size_t num_temporal_layers);
+  ~TemporalLayerValidator();
+
+  bool ValidateAndUpdate(bool keyframe,
+                         uint8_t temporal_index,
+                         uint8_t reference_index,
+                         uint8_t refresh_frame_index);
+
+ private:
+  static constexpr size_t kReferenceFramePoolSize = 8;
+  const size_t num_temporal_layers_ = 3;
+  std::array<uint8_t, kReferenceFramePoolSize> reference_frames_;
+};
+
 class H264Validator : public DecoderBufferValidator {
  public:
   H264Validator(VideoCodecProfile profile,
@@ -50,7 +69,8 @@
   ~H264Validator() override;
 
  private:
-  bool Validate(const DecoderBuffer& decoder_buffer) override;
+  bool Validate(const DecoderBuffer& decoder_buffer,
+                const BitstreamBufferMetadata& metadata) override;
 
   // Returns whether the |slice_hdr| is the first slice of a new frame.
   bool IsNewPicture(const H264SliceHeader& slice_hdr);
@@ -85,7 +105,8 @@
   ~VP8Validator() override;
 
  private:
-  bool Validate(const DecoderBuffer& decoder_buffer) override;
+  bool Validate(const DecoderBuffer& decoder_buffer,
+                const BitstreamBufferMetadata& metadata) override;
 
   Vp8Parser parser_;
   // Whether key frame has been input.
@@ -94,11 +115,14 @@
 
 class VP9Validator : public DecoderBufferValidator {
  public:
-  VP9Validator(VideoCodecProfile profile, const gfx::Rect& visible_rect);
+  VP9Validator(VideoCodecProfile profile,
+               const gfx::Rect& visible_rect,
+               size_t num_temporal_layers);
   ~VP9Validator() override;
 
  private:
-  bool Validate(const DecoderBuffer& decoder_buffer) override;
+  bool Validate(const DecoderBuffer& decoder_buffer,
+                const BitstreamBufferMetadata& metadata) override;
 
   Vp9Parser parser_;
 
@@ -107,6 +131,8 @@
 
   // The expected h264 profile of |decoder_buffer|.
   const int profile_;
+
+  const std::unique_ptr<TemporalLayerValidator> temporal_layer_validator_;
 };
 }  // namespace test
 }  // namespace media
diff --git a/media/gpu/test/video_encoder/video_encoder_client.cc b/media/gpu/test/video_encoder/video_encoder_client.cc
index b5ec0e1..7594d7f 100644
--- a/media/gpu/test/video_encoder/video_encoder_client.cc
+++ b/media/gpu/test/video_encoder/video_encoder_client.cc
@@ -4,6 +4,7 @@
 
 #include "media/gpu/test/video_encoder/video_encoder_client.h"
 
+#include <algorithm>
 #include <numeric>
 #include <string>
 #include <utility>
@@ -40,13 +41,35 @@
   task_runner->PostTask(FROM_HERE,
                         base::BindOnce(func, *encoder_client, args...));
 }
+
+std::vector<VideoEncodeAccelerator::Config::SpatialLayer>
+CreateSpatialLayersConfig(const gfx::Size& resolution,
+                          const VideoEncoderClientConfig& config) {
+  // Returns empty spatial layer config because one temporal layer stream is
+  // equivalent to a simple stream.
+  if (config.num_temporal_layers == 1u)
+    return {};
+
+  // VideoEncodeAccelerator supports only temporal layer encoding.
+  VideoEncodeAccelerator::Config::SpatialLayer spatial_layer;
+  spatial_layer.width = resolution.width();
+  spatial_layer.height = resolution.height();
+  spatial_layer.bitrate_bps = config.bitrate;
+  spatial_layer.framerate = config.framerate;
+  spatial_layer.num_of_temporal_layers = config.num_temporal_layers;
+  // Note: VideoEncodeAccelerator currently ignores this max_qp parameter.
+  spatial_layer.max_qp = 30u;
+  return {spatial_layer};
+}
 }  // namespace
 
 VideoEncoderClientConfig::VideoEncoderClientConfig(
     const Video* video,
     VideoCodecProfile output_profile,
+    size_t num_temporal_layers,
     uint32_t bitrate)
     : output_profile(output_profile),
+      num_temporal_layers(num_temporal_layers),
       bitrate(bitrate),
       framerate(video->FrameRate()),
       num_frames_to_encode(video->NumFrames()) {}
@@ -54,24 +77,65 @@
 VideoEncoderClientConfig::VideoEncoderClientConfig(
     const VideoEncoderClientConfig&) = default;
 
-VideoEncoderStats::VideoEncoderStats(uint32_t framerate)
-    : framerate(framerate) {}
+VideoEncoderStats::VideoEncoderStats() = default;
+VideoEncoderStats::~VideoEncoderStats() = default;
+VideoEncoderStats::VideoEncoderStats(const VideoEncoderStats&) = default;
+
+VideoEncoderStats::VideoEncoderStats(uint32_t framerate,
+                                     size_t num_temporal_layers)
+    : framerate(framerate),
+      num_encoded_frames_per_layer(num_temporal_layers, 0),
+      encoded_frames_size_per_layer(num_temporal_layers, 0) {}
 
 uint32_t VideoEncoderStats::Bitrate() const {
-  const size_t average_frame_size_in_bits =
-      total_encoded_frames_size * 8 / num_encoded_frames;
-  const uint32_t average_bitrate = average_frame_size_in_bits * framerate;
-  VLOGF(2) << "encoded_frames=" << num_encoded_frames
-           << ", framerate=" << framerate
-           << ", total_encoded_frames_size=" << total_encoded_frames_size
-           << ", average_frame_size_in_bits=" << average_frame_size_in_bits
-           << ", average bitrate=" << average_bitrate;
-  return average_bitrate;
+  auto compute_bitrate = [](double framerate, size_t num_frames,
+                            size_t total_size,
+                            base::Optional<size_t> layer_index) {
+    const size_t average_frame_size_in_bits = total_size * 8 / num_frames;
+    const uint32_t average_bitrate = average_frame_size_in_bits * framerate;
+    const std::string prefix =
+        layer_index ? "[TL#" + std::to_string(*layer_index) + "] " : "[Total] ";
+    VLOGF(2) << prefix << "encoded_frames=" << num_frames
+             << ", framerate=" << framerate
+             << ", total_encoded_frames_size=" << total_size
+             << ", average_frame_size_in_bits=" << average_frame_size_in_bits
+             << ", average bitrate=" << average_bitrate;
+    return average_bitrate;
+  };
+
+  const size_t num_layers = num_encoded_frames_per_layer.size();
+  if (num_layers == 1) {
+    return compute_bitrate(framerate, total_num_encoded_frames,
+                           total_encoded_frames_size, base::nullopt);
+  }
+
+  for (size_t i = 0; i < num_layers; ++i) {
+    // Used to compute the ratio of the framerate on each layer. For example,
+    // when the number of temporal layers is three, the ratio of framerate of
+    // layers are 1/4, 1/4 and 1/2 for the first, second and third layer,
+    // respectively.
+    constexpr size_t kFramerateDenom[][3] = {
+        {1, 0, 0},
+        {2, 2, 0},
+        {4, 4, 2},
+    };
+    const size_t num_frames = num_encoded_frames_per_layer[i];
+    const size_t frames_size = encoded_frames_size_per_layer[i];
+    const uint32_t layer_framerate =
+        static_cast<double>(framerate) / kFramerateDenom[num_layers - 1][i];
+    compute_bitrate(layer_framerate, num_frames, frames_size, i);
+  }
+  return compute_bitrate(framerate, total_num_encoded_frames,
+                         total_encoded_frames_size, base::nullopt);
 }
 
 void VideoEncoderStats::Reset() {
-  num_encoded_frames = 0;
+  total_num_encoded_frames = 0;
   total_encoded_frames_size = 0;
+  std::fill(num_encoded_frames_per_layer.begin(),
+            num_encoded_frames_per_layer.end(), 0u);
+  std::fill(encoded_frames_size_per_layer.begin(),
+            encoded_frames_size_per_layer.end(), 0u);
 }
 
 VideoEncoderClient::VideoEncoderClient(
@@ -84,7 +148,8 @@
       encoder_client_config_(config),
       encoder_client_thread_("VDAClientEncoderThread"),
       encoder_client_state_(VideoEncoderClientState::kUninitialized),
-      current_stats_(encoder_client_config_.framerate),
+      current_stats_(encoder_client_config_.framerate,
+                     config.num_temporal_layers),
       gpu_memory_buffer_factory_(gpu_memory_buffer_factory) {
   DETACH_FROM_SEQUENCE(encoder_client_sequence_checker_);
 
@@ -263,8 +328,18 @@
             << ", encoded image size=" << metadata.payload_size_bytes;
   {
     base::AutoLock auto_lock(stats_lock_);
-    current_stats_.num_encoded_frames++;
+    current_stats_.total_num_encoded_frames++;
     current_stats_.total_encoded_frames_size += metadata.payload_size_bytes;
+    if (metadata.vp9.has_value()) {
+      uint8_t temporal_id = metadata.vp9->temporal_idx;
+      ASSERT_LE(temporal_id,
+                current_stats_.num_encoded_frames_per_layer.size());
+      ASSERT_LE(temporal_id,
+                current_stats_.encoded_frames_size_per_layer.size());
+      current_stats_.num_encoded_frames_per_layer[temporal_id]++;
+      current_stats_.encoded_frames_size_per_layer[temporal_id] +=
+          metadata.payload_size_bytes;
+    }
   }
 
   auto it = bitstream_buffers_.find(bitstream_buffer_id);
@@ -337,7 +412,9 @@
       encoder_client_config_.output_profile, encoder_client_config_.bitrate,
       encoder_client_config_.framerate, base::nullopt /* gop_length */,
       base::nullopt /* h264_output_level*/, false /* is_constrained_h264 */,
-      encoder_client_config_.input_storage_type);
+      encoder_client_config_.input_storage_type,
+      VideoEncodeAccelerator::Config::ContentType::kCamera,
+      CreateSpatialLayersConfig(video_->Resolution(), encoder_client_config_));
 
   encoder_ = GpuVideoEncodeAcceleratorFactory::CreateVEA(config, this,
                                                          gpu::GpuPreferences());
diff --git a/media/gpu/test/video_encoder/video_encoder_client.h b/media/gpu/test/video_encoder/video_encoder_client.h
index 9461ab66..be6a7ca6 100644
--- a/media/gpu/test/video_encoder/video_encoder_client.h
+++ b/media/gpu/test/video_encoder/video_encoder_client.h
@@ -37,11 +37,14 @@
   static constexpr uint32_t kDefaultBitrate = 200000;
   VideoEncoderClientConfig(const Video* video,
                            VideoCodecProfile output_profile,
+                           size_t num_temporal_layers,
                            uint32_t bitrate);
   VideoEncoderClientConfig(const VideoEncoderClientConfig&);
 
-  // The output output profile to be used.
+  // The output profile to be used.
   VideoCodecProfile output_profile = VideoCodecProfile::H264PROFILE_MAIN;
+  // The number of temporal layers of the output stream.
+  size_t num_temporal_layers = 1u;
   // The maximum number of bitstream buffer encodes that can be requested
   // without waiting for the result of the previous encodes requests.
   size_t max_outstanding_encode_requests = 1;
@@ -59,13 +62,19 @@
 };
 
 struct VideoEncoderStats {
-  explicit VideoEncoderStats(uint32_t framerate = 1u);
+  VideoEncoderStats();
+  VideoEncoderStats(const VideoEncoderStats&);
+  ~VideoEncoderStats();
+  VideoEncoderStats(uint32_t framerate, size_t num_temporal_layers);
   uint32_t Bitrate() const;
   void Reset();
 
   uint32_t framerate = 0;
-  size_t num_encoded_frames = 0;
+  size_t total_num_encoded_frames = 0;
   size_t total_encoded_frames_size = 0;
+  // Filled in temporal layer encoding and codec is vp9.
+  std::vector<size_t> num_encoded_frames_per_layer;
+  std::vector<size_t> encoded_frames_size_per_layer;
 };
 
 // The video encoder client is responsible for the communication between the
diff --git a/media/gpu/test/video_encoder/video_encoder_test_environment.cc b/media/gpu/test/video_encoder/video_encoder_test_environment.cc
index 6bd80b42..9bb2784 100644
--- a/media/gpu/test/video_encoder/video_encoder_test_environment.cc
+++ b/media/gpu/test/video_encoder/video_encoder_test_environment.cc
@@ -87,6 +87,7 @@
     bool enable_bitstream_validator,
     const base::FilePath& output_folder,
     const std::string& codec,
+    size_t num_temporal_layers,
     bool save_output_bitstream,
     const FrameOutputConfig& frame_output_config) {
   if (video_path.empty()) {
@@ -126,12 +127,18 @@
     return nullptr;
   }
 
+  const VideoCodecProfile profile = it->profile;
+  if (num_temporal_layers > 1u && profile != VP9PROFILE_PROFILE0) {
+    LOG(ERROR) << "Temporal layer encoding supported "
+               << "only if output profile is vp9";
+    return nullptr;
+  }
+
   const uint32_t bitrate =
       GetDefaultTargetBitrate(video->Resolution(), video->FrameRate());
-  VideoCodecProfile profile = it->profile;
   return new VideoEncoderTestEnvironment(
       std::move(video), enable_bitstream_validator, output_folder, profile,
-      bitrate, save_output_bitstream, frame_output_config);
+      num_temporal_layers, bitrate, save_output_bitstream, frame_output_config);
 }
 
 VideoEncoderTestEnvironment::VideoEncoderTestEnvironment(
@@ -139,6 +146,7 @@
     bool enable_bitstream_validator,
     const base::FilePath& output_folder,
     VideoCodecProfile profile,
+    size_t num_temporal_layers,
     uint32_t bitrate,
     bool save_output_bitstream,
     const FrameOutputConfig& frame_output_config)
@@ -148,6 +156,7 @@
       enable_bitstream_validator_(enable_bitstream_validator),
       output_folder_(output_folder),
       profile_(profile),
+      num_temporal_layers_(num_temporal_layers),
       bitrate_(bitrate),
       save_output_bitstream_(save_output_bitstream),
       frame_output_config_(frame_output_config),
@@ -172,6 +181,10 @@
   return profile_;
 }
 
+size_t VideoEncoderTestEnvironment::NumTemporalLayers() const {
+  return num_temporal_layers_;
+}
+
 uint32_t VideoEncoderTestEnvironment::Bitrate() const {
   return bitrate_;
 }
diff --git a/media/gpu/test/video_encoder/video_encoder_test_environment.h b/media/gpu/test/video_encoder/video_encoder_test_environment.h
index d6e1ed3..16c6868 100644
--- a/media/gpu/test/video_encoder/video_encoder_test_environment.h
+++ b/media/gpu/test/video_encoder/video_encoder_test_environment.h
@@ -39,6 +39,7 @@
       bool enable_bitstream_validator,
       const base::FilePath& output_folder,
       const std::string& codec,
+      size_t num_temporal_layers,
       bool output_bitstream,
       const FrameOutputConfig& frame_output_config = FrameOutputConfig());
 
@@ -52,6 +53,8 @@
   const base::FilePath& OutputFolder() const;
   // Get the output codec profile.
   VideoCodecProfile Profile() const;
+  // Get the number of temporal layers.
+  size_t NumTemporalLayers() const;
   // Get the target bitrate (bits/second).
   uint32_t Bitrate() const;
   // Whether the encoded bitstream is saved to disk.
@@ -71,6 +74,7 @@
                               bool enable_bitstream_validator,
                               const base::FilePath& output_folder,
                               VideoCodecProfile profile,
+                              size_t num_temporal_layers,
                               uint32_t bitrate,
                               bool save_output_bitstream,
                               const FrameOutputConfig& frame_output_config);
@@ -83,6 +87,9 @@
   const base::FilePath output_folder_;
   // VideoCodecProfile to be produced by VideoEncoder.
   const VideoCodecProfile profile_;
+  // The number of temporal layers of the stream to be produced by VideoEncoder.
+  // This is only for vp9 stream.
+  const size_t num_temporal_layers_;
   // Targeted bitrate (bits/second) of the stream produced by VideoEncoder.
   const uint32_t bitrate_;
   // Whether the bitstream produced by VideoEncoder is saved to disk.
diff --git a/media/gpu/video_encode_accelerator_perf_tests.cc b/media/gpu/video_encode_accelerator_perf_tests.cc
index f56de0dc..f8e9e3a 100644
--- a/media/gpu/video_encode_accelerator_perf_tests.cc
+++ b/media/gpu/video_encode_accelerator_perf_tests.cc
@@ -280,7 +280,9 @@
     performance_evaluator_ = performance_evaluator.get();
     bitstream_processors.push_back(std::move(performance_evaluator));
 
-    VideoEncoderClientConfig config(video, profile, bitrate);
+    constexpr size_t kNumTemporalLayers = 1u;
+    VideoEncoderClientConfig config(video, profile, kNumTemporalLayers,
+                                    bitrate);
     auto video_encoder =
         VideoEncoder::Create(config, g_env->GetGpuMemoryBufferFactory(),
                              std::move(bitstream_processors));
@@ -368,7 +370,7 @@
   media::test::VideoEncoderTestEnvironment* test_environment =
       media::test::VideoEncoderTestEnvironment::Create(
           video_path, video_metadata_path, false, base::FilePath(output_folder),
-          codec, false /* output_bitstream */);
+          codec, 1u /* num_temporal_layers */, false /* output_bitstream */);
   if (!test_environment)
     return EXIT_FAILURE;
 
diff --git a/media/gpu/video_encode_accelerator_tests.cc b/media/gpu/video_encode_accelerator_tests.cc
index 7e05457..56c0ec7 100644
--- a/media/gpu/video_encode_accelerator_tests.cc
+++ b/media/gpu/video_encode_accelerator_tests.cc
@@ -13,6 +13,7 @@
 #include "media/base/test_data_util.h"
 #include "media/base/video_bitrate_allocation.h"
 #include "media/base/video_decoder_config.h"
+#include "media/gpu/buildflags.h"
 #include "media/gpu/test/video.h"
 #include "media/gpu/test/video_encoder/bitstream_file_writer.h"
 #include "media/gpu/test/video_encoder/bitstream_validator.h"
@@ -37,9 +38,10 @@
 // TODO(dstaessens): Add video_encoder_test_usage.md
 constexpr const char* usage_msg =
     "usage: video_encode_accelerator_tests\n"
-    "           [--codec=<codec>] [--disable_validator]\n"
-    "           [--output_bitstream] [--output_images=(all|corrupt)]\n"
-    "           [--output_format=(png|yuv)] [--output_folder=<filepath>]\n"
+    "           [--codec=<codec>] [--num_temporal_layers=<number>]\n"
+    "           [--disable_validator] [--output_bitstream]\n"
+    "           [--output_images=(all|corrupt)] [--output_format=(png|yuv)]\n"
+    "           [--output_folder=<filepath>] [--output_limit=<number>]\n"
     "           [-v=<level>] [--vmodule=<config>] [--gtest_help] [--help]\n"
     "           [<video path>] [<video metadata path>]\n";
 
@@ -52,27 +54,30 @@
     "containing the video's metadata, such as frame checksums. By default\n"
     "<video path>.json will be used.\n"
     "\nThe following arguments are supported:\n"
-    "  --codec              codec profile to encode, \"h264\" (baseline),\n"
-    "                       \"h264main, \"h264high\", \"vp8\" and \"vp9\".\n"
-    "                       H264 Baseline is selected if unspecified.\n"
-    "  --disable_validator  disable validation of encoded bitstream.\n\n"
-    "  --output_bitstream   save the output bitstream in either H264 AnnexB\n"
-    "                       format (for H264) or IVF format (for vp8 and vp9)\n"
-    "                       to <output_folder>/<testname>."
-    "  --output_images      in addition to saving the full encoded bitstream,\n"
-    "                       it's also possible to dump individual frames to\n"
-    "                       <output_folder>/<testname>, possible values\n"
-    "                       are \"all|corrupt\"\n"
-    "  --output_format      set the format of images saved to disk, supported\n"
-    "                       formats are \"png\" (default) and \"yuv\".\n"
-    "  --output_limit       limit the number of images saved to disk.\n"
-    "  --output_folder      set the basic folder used to store test artifacts\n"
-    "                       The default is the current directory.\n"
-    "   -v                  enable verbose mode, e.g. -v=2.\n"
-    "  --vmodule            enable verbose mode for the specified module,\n"
-    "                       e.g. --vmodule=*media/gpu*=2.\n\n"
-    "  --gtest_help         display the gtest help and exit.\n"
-    "  --help               display this help and exit.\n";
+    "  --codec               codec profile to encode, \"h264\" (baseline),\n"
+    "                        \"h264main, \"h264high\", \"vp8\" and \"vp9\".\n"
+    "                        H264 Baseline is selected if unspecified.\n"
+    "  --num_temporal_layers the number of temporal layers of the encoded\n"
+    "                        bitstream. Only used in --codec=vp9 currently.\n"
+    "  --disable_validator   disable validation of encoded bitstream.\n"
+    "  --output_bitstream    save the output bitstream in either H264 AnnexB\n"
+    "                        format (for H264) or IVF format (for vp8 and\n"
+    "                        vp9) to <output_folder>/<testname>.\n"
+    "  --output_images       in addition to saving the full encoded,\n"
+    "                        bitstream it's also possible to dump individual\n"
+    "                        frames to <output_folder>/<testname>, possible\n"
+    "                        values are \"all|corrupt\"\n"
+    "  --output_format       set the format of images saved to disk,\n"
+    "                        supported formats are \"png\" (default) and\n"
+    "                        \"yuv\".\n"
+    "  --output_limit        limit the number of images saved to disk.\n"
+    "  --output_folder       set the basic folder used to store test\n"
+    "                        artifacts. The default is the current directory.\n"
+    "   -v                   enable verbose mode, e.g. -v=2.\n"
+    "  --vmodule             enable verbose mode for the specified module,\n"
+    "                        e.g. --vmodule=*media/gpu*=2.\n\n"
+    "  --gtest_help          display the gtest help and exit.\n"
+    "  --help                display this help and exit.\n";
 
 // Default video to be used if no test video was specified.
 constexpr base::FilePath::CharType kDefaultTestVideoPath[] =
@@ -89,6 +94,12 @@
 // Video encode test class. Performs setup and teardown for each single test.
 class VideoEncoderTest : public ::testing::Test {
  public:
+  VideoEncoderClientConfig GetDefaultConfig() {
+    return VideoEncoderClientConfig(g_env->Video(), g_env->Profile(),
+                                    g_env->NumTemporalLayers(),
+                                    g_env->Bitrate());
+  }
+
   std::unique_ptr<VideoEncoder> CreateVideoEncoder(
       Video* video,
       const VideoEncoderClientConfig& config) {
@@ -141,8 +152,8 @@
         bitstream_processors.emplace_back(new VP8Validator(visible_rect));
         break;
       case kCodecVP9:
-        bitstream_processors.emplace_back(
-            new VP9Validator(config.output_profile, visible_rect));
+        bitstream_processors.emplace_back(new VP9Validator(
+            config.output_profile, visible_rect, config.num_temporal_layers));
         break;
       default:
         LOG(ERROR) << "Unsupported profile: "
@@ -213,9 +224,7 @@
 // Encode video from start to end. Wait for the kFlushDone event at the end of
 // the stream, that notifies us all frames have been encoded.
 TEST_F(VideoEncoderTest, FlushAtEndOfStream) {
-  VideoEncoderClientConfig config(g_env->Video(), g_env->Profile(),
-                                  g_env->Bitrate());
-  auto encoder = CreateVideoEncoder(g_env->Video(), config);
+  auto encoder = CreateVideoEncoder(g_env->Video(), GetDefaultConfig());
 
   encoder->Encode();
   EXPECT_TRUE(encoder->WaitForFlushDone());
@@ -230,9 +239,7 @@
 // resolution. The test only verifies initialization and doesn't do any
 // encoding.
 TEST_F(VideoEncoderTest, Initialize) {
-  VideoEncoderClientConfig config(g_env->Video(), g_env->Profile(),
-                                  g_env->Bitrate());
-  auto encoder = CreateVideoEncoder(g_env->Video(), config);
+  auto encoder = CreateVideoEncoder(g_env->Video(), GetDefaultConfig());
 
   EXPECT_EQ(encoder->GetEventCount(VideoEncoder::kInitialized), 1u);
 }
@@ -242,10 +249,8 @@
 // of scope at the end of the test. The test will pass if no asserts or crashes
 // are triggered upon destroying.
 TEST_F(VideoEncoderTest, DestroyBeforeInitialize) {
-  VideoEncoderClientConfig config(g_env->Video(), g_env->Profile(),
-                                  g_env->Bitrate());
-  auto video_encoder =
-      VideoEncoder::Create(config, g_env->GetGpuMemoryBufferFactory());
+  auto video_encoder = VideoEncoder::Create(GetDefaultConfig(),
+                                            g_env->GetGpuMemoryBufferFactory());
 
   EXPECT_NE(video_encoder, nullptr);
 }
@@ -253,8 +258,7 @@
 // Encode video from start to end. Multiple buffer encodes will be queued in the
 // encoder, without waiting for the result of the previous encode requests.
 TEST_F(VideoEncoderTest, FlushAtEndOfStream_MultipleOutstandingEncodes) {
-  VideoEncoderClientConfig config(g_env->Video(), g_env->Profile(),
-                                  g_env->Bitrate());
+  auto config = GetDefaultConfig();
   config.max_outstanding_encode_requests = 4;
   auto encoder = CreateVideoEncoder(g_env->Video(), config);
 
@@ -271,8 +275,7 @@
   // The minimal number of concurrent encoders we expect to be supported.
   constexpr size_t kMinSupportedConcurrentEncoders = 3;
 
-  VideoEncoderClientConfig config(g_env->Video(), g_env->Profile(),
-                                  g_env->Bitrate());
+  auto config = GetDefaultConfig();
   std::vector<std::unique_ptr<VideoEncoder>> encoders(
       kMinSupportedConcurrentEncoders);
   for (size_t i = 0; i < kMinSupportedConcurrentEncoders; ++i)
@@ -291,8 +294,7 @@
 }
 
 TEST_F(VideoEncoderTest, BitrateCheck) {
-  VideoEncoderClientConfig config(g_env->Video(), g_env->Profile(),
-                                  g_env->Bitrate());
+  auto config = GetDefaultConfig();
   config.num_frames_to_encode = kNumFramesToEncodeForBitrateCheck;
   auto encoder = CreateVideoEncoder(g_env->Video(), config);
 
@@ -307,8 +309,7 @@
 }
 
 TEST_F(VideoEncoderTest, DynamicBitrateChange) {
-  VideoEncoderClientConfig config(g_env->Video(), g_env->Profile(),
-                                  g_env->Bitrate());
+  auto config = GetDefaultConfig();
   config.num_frames_to_encode = kNumFramesToEncodeForBitrateCheck * 2;
   auto encoder = CreateVideoEncoder(g_env->Video(), config);
 
@@ -336,8 +337,7 @@
 }
 
 TEST_F(VideoEncoderTest, DynamicFramerateChange) {
-  VideoEncoderClientConfig config(g_env->Video(), g_env->Profile(),
-                                  g_env->Bitrate());
+  auto config = GetDefaultConfig();
   config.num_frames_to_encode = kNumFramesToEncodeForBitrateCheck * 2;
   auto encoder = CreateVideoEncoder(g_env->Video(), config);
 
@@ -370,7 +370,7 @@
   ASSERT_TRUE(nv12_video);
 
   VideoEncoderClientConfig config(nv12_video.get(), g_env->Profile(),
-                                  g_env->Bitrate());
+                                  g_env->NumTemporalLayers(), g_env->Bitrate());
   config.input_storage_type =
       VideoEncodeAccelerator::Config::StorageType::kDmabuf;
 
@@ -407,6 +407,7 @@
   base::FilePath video_metadata_path =
       (args.size() >= 2) ? base::FilePath(args[1]) : base::FilePath();
   std::string codec = "h264";
+  size_t num_temporal_layers = 1u;
   bool output_bitstream = false;
   media::test::FrameOutputConfig frame_output_config;
   base::FilePath output_folder =
@@ -424,6 +425,12 @@
 
     if (it->first == "codec") {
       codec = it->second;
+    } else if (it->first == "num_temporal_layers") {
+      if (!base::StringToSizeT(it->second, &num_temporal_layers)) {
+        std::cout << "invalid number of temporal layers: " << it->second
+                  << "\n";
+        return EXIT_FAILURE;
+      }
     } else if (it->first == "disable_validator") {
       enable_bitstream_validator = false;
     } else if (it->first == "output_bitstream") {
@@ -472,7 +479,8 @@
   media::test::VideoEncoderTestEnvironment* test_environment =
       media::test::VideoEncoderTestEnvironment::Create(
           video_path, video_metadata_path, enable_bitstream_validator,
-          output_folder, codec, output_bitstream, frame_output_config);
+          output_folder, codec, num_temporal_layers, output_bitstream,
+          frame_output_config);
 
   if (!test_environment)
     return EXIT_FAILURE;
diff --git a/pdf/document_loader_impl.cc b/pdf/document_loader_impl.cc
index e0266bc..0c7aa8f2 100644
--- a/pdf/document_loader_impl.cc
+++ b/pdf/document_loader_impl.cc
@@ -110,8 +110,8 @@
   int64_t bytes_received = 0;
   int64_t total_bytes_to_be_received = 0;
   if (GetDocumentSize() == 0 &&
-      loader_->GetDownloadProgress(&bytes_received,
-                                   &total_bytes_to_be_received)) {
+      loader_->GetDownloadProgress(bytes_received,
+                                   total_bytes_to_be_received)) {
     chunk_stream_.set_eof_pos(
         std::max(0, static_cast<int>(total_bytes_to_be_received)));
   }
diff --git a/pdf/document_loader_impl_unittest.cc b/pdf/document_loader_impl_unittest.cc
index 04f70bf..a1682f8f3 100644
--- a/pdf/document_loader_impl_unittest.cc
+++ b/pdf/document_loader_impl_unittest.cc
@@ -188,8 +188,8 @@
     data_->SetReadCallback(std::move(callback), buffer, buffer_size);
   }
 
-  bool GetDownloadProgress(int64_t* bytes_received,
-                           int64_t* total_bytes_to_be_received) const override {
+  bool GetDownloadProgress(int64_t& bytes_received,
+                           int64_t& total_bytes_to_be_received) const override {
     return false;
   }
 
diff --git a/pdf/out_of_process_instance.cc b/pdf/out_of_process_instance.cc
index 943dee9..2b7c850 100644
--- a/pdf/out_of_process_instance.cc
+++ b/pdf/out_of_process_instance.cc
@@ -58,7 +58,6 @@
 #include "ppapi/cpp/rect.h"
 #include "ppapi/cpp/resource.h"
 #include "ppapi/cpp/size.h"
-#include "ppapi/cpp/url_request_info.h"
 #include "ppapi/cpp/var_array.h"
 #include "ppapi/cpp/var_array_buffer.h"
 #include "ppapi/cpp/var_dictionary.h"
@@ -1443,10 +1442,10 @@
 void OutOfProcessInstance::SubmitForm(const std::string& url,
                                       const void* data,
                                       int length) {
-  pp::URLRequestInfo request(this);
-  request.SetURL(url);
-  request.SetMethod("POST");
-  request.AppendDataToBody(reinterpret_cast<const char*>(data), length);
+  UrlRequest request;
+  request.url = url;
+  request.method = "POST";
+  request.body.assign(static_cast<const char*>(data), length);
 
   form_loader_ = CreateUrlLoaderInternal();
   form_loader_->Open(request, base::BindOnce(&OutOfProcessInstance::FormDidOpen,
@@ -2069,10 +2068,10 @@
 
 void OutOfProcessInstance::LoadUrl(const std::string& url,
                                    bool is_print_preview) {
-  pp::URLRequestInfo request(this);
-  request.SetURL(url);
-  request.SetMethod("GET");
-  request.SetFollowRedirects(false);
+  UrlRequest request;
+  request.url = url;
+  request.method = "GET";
+  request.ignore_redirects = true;
 
   scoped_refptr<UrlLoader>& loader =
       is_print_preview ? embed_preview_loader_ : embed_loader_;
diff --git a/pdf/pdfium/pdfium_engine.cc b/pdf/pdfium/pdfium_engine.cc
index 733e9be..d23a11b 100644
--- a/pdf/pdfium/pdfium_engine.cc
+++ b/pdf/pdfium/pdfium_engine.cc
@@ -637,8 +637,8 @@
       base::FeatureList::IsEnabled(features::kPdfIncrementalLoading);
 
   if (!doc_loader_set_for_testing_) {
-    auto loader_wrapper = std::make_unique<URLLoaderWrapperImpl>(
-        GetPluginInstance(), std::move(loader));
+    auto loader_wrapper =
+        std::make_unique<URLLoaderWrapperImpl>(std::move(loader));
     loader_wrapper->SetResponseHeaders(headers_);
 
     doc_loader_ = std::make_unique<DocumentLoaderImpl>(this);
@@ -657,8 +657,7 @@
 }
 
 std::unique_ptr<URLLoaderWrapper> PDFiumEngine::CreateURLLoader() {
-  return std::make_unique<URLLoaderWrapperImpl>(GetPluginInstance(),
-                                                client_->CreateUrlLoader());
+  return std::make_unique<URLLoaderWrapperImpl>(client_->CreateUrlLoader());
 }
 
 void PDFiumEngine::AppendPage(PDFEngine* engine, int index) {
diff --git a/pdf/ppapi_migration/url_loader.cc b/pdf/ppapi_migration/url_loader.cc
index 9fad8e7..c58a4b9 100644
--- a/pdf/ppapi_migration/url_loader.cc
+++ b/pdf/ppapi_migration/url_loader.cc
@@ -8,6 +8,7 @@
 
 #include <utility>
 
+#include "base/bind.h"
 #include "base/callback.h"
 #include "pdf/ppapi_migration/callback.h"
 #include "ppapi/c/pp_errors.h"
@@ -18,13 +19,28 @@
 #include "ppapi/cpp/url_loader.h"
 #include "ppapi/cpp/url_request_info.h"
 #include "ppapi/cpp/url_response_info.h"
+#include "ppapi/cpp/var.h"
 
 namespace chrome_pdf {
 
-UrlLoader::UrlLoader() = default;
+UrlRequest::UrlRequest() = default;
+UrlRequest::UrlRequest(const UrlRequest& other) = default;
+UrlRequest::UrlRequest(UrlRequest&& other) noexcept = default;
+UrlRequest& UrlRequest::operator=(const UrlRequest& other) = default;
+UrlRequest& UrlRequest::operator=(UrlRequest&& other) noexcept = default;
+UrlRequest::~UrlRequest() = default;
+
+UrlResponse::UrlResponse() = default;
+UrlResponse::UrlResponse(const UrlResponse& other) = default;
+UrlResponse::UrlResponse(UrlResponse&& other) noexcept = default;
+UrlResponse& UrlResponse::operator=(const UrlResponse& other) = default;
+UrlResponse& UrlResponse::operator=(UrlResponse&& other) noexcept = default;
+UrlResponse::~UrlResponse() = default;
+
+UrlLoader::UrlLoader() : plugin_instance_(0) {}
 
 UrlLoader::UrlLoader(pp::InstanceHandle plugin_instance)
-    : pepper_loader_(plugin_instance) {}
+    : plugin_instance_(plugin_instance), pepper_loader_(plugin_instance) {}
 
 UrlLoader::~UrlLoader() = default;
 
@@ -37,23 +53,35 @@
     trusted_interface->GrantUniversalAccess(pepper_loader_.pp_resource());
 }
 
-void UrlLoader::Open(const pp::URLRequestInfo& request_info,
-                     ResultCallback callback) {
-  pp::CompletionCallback pp_callback =
-      PPCompletionCallbackFromResultCallback(std::move(callback));
-  int32_t result = pepper_loader_.Open(request_info, pp_callback);
+void UrlLoader::Open(const UrlRequest& request, ResultCallback callback) {
+  pp::URLRequestInfo pp_request(plugin_instance_);
+  pp_request.SetURL(request.url);
+  pp_request.SetMethod(request.method);
+
+  if (request.ignore_redirects)
+    pp_request.SetFollowRedirects(false);
+
+  if (request.custom_referrer_url.has_value())
+    pp_request.SetCustomReferrerURL(request.custom_referrer_url.value());
+
+  if (request.headers.has_value())
+    pp_request.SetHeaders(request.headers.value());
+
+  if (!request.body.empty())
+    pp_request.AppendDataToBody(request.body.data(), request.body.size());
+
+  pp::CompletionCallback pp_callback = PPCompletionCallbackFromResultCallback(
+      base::BindOnce(&UrlLoader::DidOpen, weak_factory_.GetWeakPtr(),
+                     std::move(callback)));
+  int32_t result = pepper_loader_.Open(pp_request, pp_callback);
   if (result != PP_OK_COMPLETIONPENDING)
     pp_callback.Run(result);
 }
 
-bool UrlLoader::GetDownloadProgress(int64_t* bytes_received,
-                                    int64_t* total_bytes_to_be_received) const {
-  return pepper_loader_.GetDownloadProgress(bytes_received,
-                                            total_bytes_to_be_received);
-}
-
-pp::URLResponseInfo UrlLoader::GetResponseInfo() const {
-  return pepper_loader_.GetResponseInfo();
+bool UrlLoader::GetDownloadProgress(int64_t& bytes_received,
+                                    int64_t& total_bytes_to_be_received) const {
+  return pepper_loader_.GetDownloadProgress(&bytes_received,
+                                            &total_bytes_to_be_received);
 }
 
 void UrlLoader::ReadResponseBody(base::span<char> buffer,
@@ -70,4 +98,18 @@
   pepper_loader_.Close();
 }
 
+void UrlLoader::DidOpen(ResultCallback callback, int32_t result) {
+  pp::URLResponseInfo pp_response = pepper_loader_.GetResponseInfo();
+  response_.status_code = pp_response.GetStatusCode();
+
+  pp::Var headers_var = pp_response.GetHeaders();
+  if (headers_var.is_string()) {
+    response_.headers = headers_var.AsString();
+  } else {
+    response_.headers.reset();
+  }
+
+  std::move(callback).Run(result);
+}
+
 }  // namespace chrome_pdf
diff --git a/pdf/ppapi_migration/url_loader.h b/pdf/ppapi_migration/url_loader.h
index 6081d64d..f6b4a40 100644
--- a/pdf/ppapi_migration/url_loader.h
+++ b/pdf/ppapi_migration/url_loader.h
@@ -7,19 +7,63 @@
 
 #include <stdint.h>
 
+#include <string>
+
 #include "base/containers/span.h"
 #include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/optional.h"
 #include "pdf/ppapi_migration/callback.h"
+#include "ppapi/cpp/instance_handle.h"
 #include "ppapi/cpp/url_loader.h"
 
-namespace pp {
-class InstanceHandle;
-class URLRequestInfo;
-class URLResponseInfo;
-}  // namespace pp
-
 namespace chrome_pdf {
 
+// Properties for making a URL request.
+struct UrlRequest final {
+  UrlRequest();
+  UrlRequest(const UrlRequest& other);
+  UrlRequest(UrlRequest&& other) noexcept;
+  UrlRequest& operator=(const UrlRequest& other);
+  UrlRequest& operator=(UrlRequest&& other) noexcept;
+  ~UrlRequest();
+
+  // Request URL.
+  std::string url;
+
+  // HTTP method.
+  std::string method;
+
+  // Whether to ignore redirects. By default, redirects are followed
+  // automatically.
+  bool ignore_redirects = false;
+
+  // Custom referrer URL.
+  base::Optional<std::string> custom_referrer_url;
+
+  // HTTP headers as a single string of `\n`-delimited key-value pairs.
+  base::Optional<std::string> headers;
+
+  // Request body.
+  std::string body;
+};
+
+// Properties returned from a URL request. Does not include the response body.
+struct UrlResponse final {
+  UrlResponse();
+  UrlResponse(const UrlResponse& other);
+  UrlResponse(UrlResponse&& other) noexcept;
+  UrlResponse& operator=(const UrlResponse& other);
+  UrlResponse& operator=(UrlResponse&& other) noexcept;
+  ~UrlResponse();
+
+  // HTTP status code.
+  int32_t status_code = 0;
+
+  // HTTP headers as a single string of `\n`-delimited key-value pairs.
+  base::Optional<std::string> headers;
+};
+
 // Thin wrapper around a `pp::URLLoader`. Unlike a `pp::URLLoader`, this class
 // does not perform its own reference counting, but relies on `scoped_refptr`.
 //
@@ -36,10 +80,10 @@
   void GrantUniversalAccess();
 
   // Mimic `pp::URLLoader`:
-  void Open(const pp::URLRequestInfo& request_info, ResultCallback callback);
-  bool GetDownloadProgress(int64_t* bytes_received,
-                           int64_t* total_bytes_to_be_received) const;
-  pp::URLResponseInfo GetResponseInfo() const;
+  void Open(const UrlRequest& request, ResultCallback callback);
+  bool GetDownloadProgress(int64_t& bytes_received,
+                           int64_t& total_bytes_to_be_received) const;
+  const UrlResponse& response() const { return response_; }
   void ReadResponseBody(base::span<char> buffer, ResultCallback callback);
   void Close();
 
@@ -48,7 +92,13 @@
 
   ~UrlLoader();
 
+  void DidOpen(ResultCallback callback, int32_t result);
+
+  pp::InstanceHandle plugin_instance_;
   pp::URLLoader pepper_loader_;
+  UrlResponse response_;
+
+  base::WeakPtrFactory<UrlLoader> weak_factory_{this};
 };
 
 }  // namespace chrome_pdf
diff --git a/pdf/url_loader_wrapper.h b/pdf/url_loader_wrapper.h
index e594acf..e45a531 100644
--- a/pdf/url_loader_wrapper.h
+++ b/pdf/url_loader_wrapper.h
@@ -64,8 +64,8 @@
   // If false, progress is unknown, bytes_received/total_bytes_to_be_received
   // will be undefined.
   virtual bool GetDownloadProgress(
-      int64_t* bytes_received,
-      int64_t* total_bytes_to_be_received) const = 0;
+      int64_t& bytes_received,
+      int64_t& total_bytes_to_be_received) const = 0;
 };
 
 }  // namespace chrome_pdf
diff --git a/pdf/url_loader_wrapper_impl.cc b/pdf/url_loader_wrapper_impl.cc
index 1571049..afc6920 100644
--- a/pdf/url_loader_wrapper_impl.cc
+++ b/pdf/url_loader_wrapper_impl.cc
@@ -20,9 +20,6 @@
 #include "base/strings/stringprintf.h"
 #include "net/http/http_util.h"
 #include "pdf/ppapi_migration/url_loader.h"
-#include "ppapi/cpp/url_request_info.h"
-#include "ppapi/cpp/url_response_info.h"
-#include "ppapi/cpp/var.h"
 #include "ui/gfx/range/range.h"
 
 namespace chrome_pdf {
@@ -32,25 +29,22 @@
 // We should read with delay to prevent block UI thread, and reduce CPU usage.
 constexpr base::TimeDelta kReadDelayMs = base::TimeDelta::FromMilliseconds(2);
 
-pp::URLRequestInfo MakeRangeRequest(pp::Instance* plugin_instance,
-                                    const std::string& url,
-                                    const std::string& referrer_url,
-                                    uint32_t position,
-                                    uint32_t size) {
-  pp::URLRequestInfo request(plugin_instance);
-  request.SetURL(url);
-  request.SetMethod("GET");
-  request.SetFollowRedirects(false);
-  request.SetCustomReferrerURL(referrer_url);
+UrlRequest MakeRangeRequest(const std::string& url,
+                            const std::string& referrer_url,
+                            uint32_t position,
+                            uint32_t size) {
+  UrlRequest request;
+  request.url = url;
+  request.method = "GET";
+  request.ignore_redirects = true;
+  request.custom_referrer_url = referrer_url;
 
   // According to rfc2616, byte range specifies position of the first and last
   // bytes in the requested range inclusively. Therefore we should subtract 1
   // from the position + size, to get index of the last byte that needs to be
   // downloaded.
-  std::string str_header =
+  request.headers =
       base::StringPrintf("Range: bytes=%d-%d", position, position + size - 1);
-  pp::Var header(str_header.c_str());
-  request.SetHeaders(header);
 
   return request;
 }
@@ -106,9 +100,8 @@
 
 }  // namespace
 
-URLLoaderWrapperImpl::URLLoaderWrapperImpl(pp::Instance* plugin_instance,
-                                           scoped_refptr<UrlLoader> url_loader)
-    : plugin_instance_(plugin_instance), url_loader_(std::move(url_loader)) {
+URLLoaderWrapperImpl::URLLoaderWrapperImpl(scoped_refptr<UrlLoader> url_loader)
+    : url_loader_(std::move(url_loader)) {
   SetHeadersFromLoader();
 }
 
@@ -136,7 +129,7 @@
 }
 
 int URLLoaderWrapperImpl::GetStatusCode() const {
-  return url_loader_->GetResponseInfo().GetStatusCode();
+  return url_loader_->response().status_code;
 }
 
 bool URLLoaderWrapperImpl::IsMultipart() const {
@@ -150,8 +143,8 @@
 }
 
 bool URLLoaderWrapperImpl::GetDownloadProgress(
-    int64_t* bytes_received,
-    int64_t* total_bytes_to_be_received) const {
+    int64_t& bytes_received,
+    int64_t& total_bytes_to_be_received) const {
   return url_loader_->GetDownloadProgress(bytes_received,
                                           total_bytes_to_be_received);
 }
@@ -167,7 +160,7 @@
                                      uint32_t size,
                                      ResultCallback callback) {
   url_loader_->Open(
-      MakeRangeRequest(plugin_instance_, url, referrer_url, position, size),
+      MakeRangeRequest(url, referrer_url, position, size),
       base::BindOnce(&URLLoaderWrapperImpl::DidOpen, weak_factory_.GetWeakPtr(),
                      std::move(callback)));
 }
@@ -185,7 +178,7 @@
 
 void URLLoaderWrapperImpl::ReadResponseBodyImpl(ResultCallback callback) {
   url_loader_->ReadResponseBody(
-      base::span<char>(buffer_, buffer_size_),
+      base::make_span(buffer_, buffer_size_),
       base::BindOnce(&URLLoaderWrapperImpl::DidRead, weak_factory_.GetWeakPtr(),
                      std::move(callback)));
 }
@@ -299,10 +292,7 @@
 }
 
 void URLLoaderWrapperImpl::SetHeadersFromLoader() {
-  pp::URLResponseInfo response = url_loader_->GetResponseInfo();
-  pp::Var headers_var = response.GetHeaders();
-
-  SetResponseHeaders(headers_var.is_string() ? headers_var.AsString() : "");
+  SetResponseHeaders(url_loader_->response().headers.value_or(""));
 }
 
 }  // namespace chrome_pdf
diff --git a/pdf/url_loader_wrapper_impl.h b/pdf/url_loader_wrapper_impl.h
index 625fb31c..c6a1ac5f 100644
--- a/pdf/url_loader_wrapper_impl.h
+++ b/pdf/url_loader_wrapper_impl.h
@@ -17,18 +17,13 @@
 #include "pdf/url_loader_wrapper.h"
 #include "ui/gfx/range/range.h"
 
-namespace pp {
-class Instance;
-}
-
 namespace chrome_pdf {
 
 class UrlLoader;
 
 class URLLoaderWrapperImpl : public URLLoaderWrapper {
  public:
-  URLLoaderWrapperImpl(pp::Instance* plugin_instance,
-                       scoped_refptr<UrlLoader> url_loader);
+  explicit URLLoaderWrapperImpl(scoped_refptr<UrlLoader> url_loader);
   URLLoaderWrapperImpl(const URLLoaderWrapperImpl&) = delete;
   URLLoaderWrapperImpl& operator=(const URLLoaderWrapperImpl&) = delete;
   ~URLLoaderWrapperImpl() override;
@@ -42,8 +37,8 @@
   int GetStatusCode() const override;
   bool IsMultipart() const override;
   bool GetByteRangeStart(int* start) const override;
-  bool GetDownloadProgress(int64_t* bytes_received,
-                           int64_t* total_bytes_to_be_received) const override;
+  bool GetDownloadProgress(int64_t& bytes_received,
+                           int64_t& total_bytes_to_be_received) const override;
   void Close() override;
   void OpenRange(const std::string& url,
                  const std::string& referrer_url,
@@ -64,7 +59,6 @@
 
   void ReadResponseBodyImpl(ResultCallback callback);
 
-  pp::Instance* const plugin_instance_;
   scoped_refptr<UrlLoader> url_loader_;
   std::string response_headers_;
 
diff --git a/services/shape_detection/BUILD.gn b/services/shape_detection/BUILD.gn
index 32d9982..3e6609b 100644
--- a/services/shape_detection/BUILD.gn
+++ b/services/shape_detection/BUILD.gn
@@ -33,6 +33,7 @@
       "text_detection_impl_mac.mm",
     ]
     frameworks = [ "QuartzCore.framework" ]
+    weak_frameworks = [ "Vision.framework" ]
   } else if (is_win) {
     sources += [
       "barcode_detection_provider_impl.cc",
diff --git a/services/shape_detection/barcode_detection_impl_mac_unittest.mm b/services/shape_detection/barcode_detection_impl_mac_unittest.mm
index 91d6a89..2a1216b 100644
--- a/services/shape_detection/barcode_detection_impl_mac_unittest.mm
+++ b/services/shape_detection/barcode_detection_impl_mac_unittest.mm
@@ -4,7 +4,8 @@
 
 #include "services/shape_detection/barcode_detection_impl_mac.h"
 
-#include <dlfcn.h>
+#import <Vision/Vision.h>
+
 #include <memory>
 #include <string>
 
@@ -49,16 +50,6 @@
   return nullptr;
 }
 
-void* LoadVisionLibrary() {
-  if (@available(macOS 10.13, *)) {
-    return dlopen("/System/Library/Frameworks/Vision.framework/Vision",
-                  RTLD_LAZY);
-  }
-  return nullptr;
-}
-
-using LibraryLoadCB = base::RepeatingCallback<void*(void)>;
-
 using BarcodeDetectorFactory =
     base::RepeatingCallback<std::unique_ptr<mojom::BarcodeDetection>(
         mojom::BarcodeDetectorOptionsPtr)>;
@@ -68,25 +59,23 @@
 struct TestParams {
   size_t num_barcodes;
   mojom::BarcodeFormat symbology;
-  LibraryLoadCB library_load_callback;
   BarcodeDetectorFactory factory;
   NSString* test_code_generator;
 } kTestParams[] = {
     // CoreImage only supports QR Codes.
     {1, mojom::BarcodeFormat::QR_CODE,
-     base::BindRepeating([]() { return static_cast<void*>(nullptr); }),
      base::BindRepeating(&CreateBarcodeDetectorImplMacCoreImage),
      @"CIQRCodeGenerator"},
     // Vision only supports a number of 1D/2D codes. Not all of them are
     // available for generation, though, only a few.
-    {1, mojom::BarcodeFormat::PDF417, base::BindRepeating(&LoadVisionLibrary),
+    {1, mojom::BarcodeFormat::PDF417,
      base::BindRepeating(&CreateBarcodeDetectorImplMacVision),
      @"CIPDF417BarcodeGenerator"},
-    {1, mojom::BarcodeFormat::QR_CODE, base::BindRepeating(&LoadVisionLibrary),
+    {1, mojom::BarcodeFormat::QR_CODE,
      base::BindRepeating(&CreateBarcodeDetectorImplMacVision),
      @"CIQRCodeGenerator"},
     {6 /* 1D barcode makes the detector find the same code several times. */,
-     mojom::BarcodeFormat::CODE_128, base::BindRepeating(&LoadVisionLibrary),
+     mojom::BarcodeFormat::CODE_128,
      base::BindRepeating(&CreateBarcodeDetectorImplMacVision),
      @"CICode128BarcodeGenerator"}};
 }
@@ -95,13 +84,6 @@
  public:
   ~BarcodeDetectionImplMacTest() override = default;
 
-  void SetUp() override {}
-
-  void TearDown() override {
-    if (vision_framework_)
-      dlclose(vision_framework_);
-  }
-
   void DetectCallback(size_t num_barcodes,
                       mojom::BarcodeFormat symbology,
                       const std::string& barcode_value,
@@ -118,7 +100,6 @@
 
   std::unique_ptr<mojom::BarcodeDetection> impl_;
   base::test::SingleThreadTaskEnvironment task_environment_;
-  void* vision_framework_ = nullptr;
 };
 
 TEST_P(BarcodeDetectionImplMacTest, CreateAndDestroy) {
@@ -132,7 +113,7 @@
 
 // This test generates a single barcode and scans it back.
 TEST_P(BarcodeDetectionImplMacTest, ScanOneBarcode) {
-  // Barcode detection needs at least MAC OS X 10.10, and GPU infrastructure.
+  // Barcode detection needs GPU infrastructure.
   if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
           switches::kUseGpuInTests)) {
     return;
@@ -144,7 +125,6 @@
                  << "Skipping test.";
     return;
   }
-  vision_framework_ = GetParam().library_load_callback.Run();
 
   // Generate a barcode image as a CIImage by using |qr_code_generator|.
   NSData* const qr_code_data =
@@ -155,11 +135,7 @@
       [CIFilter filterWithName:GetParam().test_code_generator];
   [qr_code_generator setValue:qr_code_data forKey:@"inputMessage"];
 
-  // [CIImage outputImage] is available in macOS 10.10+.  Could be added to
-  // sdk_forward_declarations.h but seems hardly worth it.
-  EXPECT_TRUE([qr_code_generator respondsToSelector:@selector(outputImage)]);
-  CIImage* qr_code_image =
-      [qr_code_generator performSelector:@selector(outputImage)];
+  CIImage* qr_code_image = qr_code_generator.outputImage;
 
   const gfx::Size size([qr_code_image extent].size.width,
                        [qr_code_image extent].size.height);
@@ -191,8 +167,6 @@
 
 TEST_F(BarcodeDetectionImplMacTest, HintFormats) {
   if (@available(macOS 10.13, *)) {
-    vision_framework_ = LoadVisionLibrary();
-
     auto vision_impl = std::make_unique<BarcodeDetectionImplMacVision>(
         mojom::BarcodeDetectorOptions::New());
     EXPECT_EQ([vision_impl->GetSymbologyHintsForTesting() count], 0u);
@@ -204,11 +178,11 @@
         mojom::BarcodeFormat::CODE_128, mojom::BarcodeFormat::ITF};
     vision_impl =
         std::make_unique<BarcodeDetectionImplMacVision>(std::move(options));
-    NSSet* expected = [NSSet
-        setWithObjects:@"VNBarcodeSymbologyPDF417", @"VNBarcodeSymbologyQR",
-                       @"VNBarcodeSymbologyCode128", @"VNBarcodeSymbologyITF14",
-                       @"VNBarcodeSymbologyI2of5",
-                       @"VNBarcodeSymbologyI2of5Checksum", nil];
+    NSArray<VNBarcodeSymbology>* expected = @[
+      VNBarcodeSymbologyPDF417, VNBarcodeSymbologyQR, VNBarcodeSymbologyCode128,
+      VNBarcodeSymbologyITF14, VNBarcodeSymbologyI2of5,
+      VNBarcodeSymbologyI2of5Checksum
+    ];
     EXPECT_TRUE(
         [vision_impl->GetSymbologyHintsForTesting() isEqualTo:expected]);
   } else {
diff --git a/services/shape_detection/barcode_detection_impl_mac_vision.h b/services/shape_detection/barcode_detection_impl_mac_vision.h
index 983193ed..bc56852 100644
--- a/services/shape_detection/barcode_detection_impl_mac_vision.h
+++ b/services/shape_detection/barcode_detection_impl_mac_vision.h
@@ -26,7 +26,7 @@
 namespace shape_detection {
 
 // This class is the implementation of Barcode Detection based on Mac OS Vision
-// framework(https://developer.apple.com/documentation/vision).
+// framework (https://developer.apple.com/documentation/vision).
 class API_AVAILABLE(macos(10.13)) BarcodeDetectionImplMacVision
     : public mojom::BarcodeDetection {
  public:
@@ -47,13 +47,13 @@
   static std::vector<shape_detection::mojom::BarcodeFormat>
   GetSupportedSymbologies(VisionAPIInterface* vision_api = nullptr);
 
-  NSSet<NSString*>* GetSymbologyHintsForTesting();
+  NSArray<VNBarcodeSymbology>* GetSymbologyHintsForTesting();
 
  private:
   void OnBarcodesDetected(VNRequest* request, NSError* error);
 
   CGSize image_size_;
-  base::scoped_nsobject<NSSet<NSString*>> symbology_hints_;
+  base::scoped_nsobject<NSArray<VNBarcodeSymbology>> symbology_hints_;
   std::unique_ptr<VisionAPIAsyncRequestMac> barcodes_async_request_;
   DetectCallback detected_callback_;
   mojo::SelfOwnedReceiverRef<mojom::BarcodeDetection> receiver_;
diff --git a/services/shape_detection/barcode_detection_impl_mac_vision.mm b/services/shape_detection/barcode_detection_impl_mac_vision.mm
index 2dc2144..888fb2f8 100644
--- a/services/shape_detection/barcode_detection_impl_mac_vision.mm
+++ b/services/shape_detection/barcode_detection_impl_mac_vision.mm
@@ -4,7 +4,8 @@
 
 #include "services/shape_detection/barcode_detection_impl_mac_vision.h"
 
-#include <Foundation/Foundation.h>
+#import <Foundation/Foundation.h>
+#import <Vision/Vision.h>
 
 #include <vector>
 
@@ -20,90 +21,91 @@
 
 namespace {
 
-mojom::BarcodeFormat ToBarcodeFormat(NSString* symbology) {
-  if ([symbology isEqual:@"VNBarcodeSymbologyAztec"])
+mojom::BarcodeFormat ToBarcodeFormat(NSString* symbology)
+    API_AVAILABLE(macos(10.13)) {
+  if ([symbology isEqual:VNBarcodeSymbologyAztec])
     return mojom::BarcodeFormat::AZTEC;
-  if ([symbology isEqual:@"VNBarcodeSymbologyCode128"])
+  if ([symbology isEqual:VNBarcodeSymbologyCode128])
     return mojom::BarcodeFormat::CODE_128;
-  if ([symbology isEqual:@"VNBarcodeSymbologyCode39"] ||
-      [symbology isEqual:@"VNBarcodeSymbologyCode39Checksum"] ||
-      [symbology isEqual:@"VNBarcodeSymbologyCode39FullASCII"] ||
-      [symbology isEqual:@"VNBarcodeSymbologyCode39FullASCIIChecksum"]) {
+  if ([symbology isEqual:VNBarcodeSymbologyCode39] ||
+      [symbology isEqual:VNBarcodeSymbologyCode39Checksum] ||
+      [symbology isEqual:VNBarcodeSymbologyCode39FullASCII] ||
+      [symbology isEqual:VNBarcodeSymbologyCode39FullASCIIChecksum]) {
     return mojom::BarcodeFormat::CODE_39;
   }
-  if ([symbology isEqual:@"VNBarcodeSymbologyCode93"] ||
-      [symbology isEqual:@"VNBarcodeSymbologyCode93i"]) {
+  if ([symbology isEqual:VNBarcodeSymbologyCode93] ||
+      [symbology isEqual:VNBarcodeSymbologyCode93i]) {
     return mojom::BarcodeFormat::CODE_93;
   }
-  if ([symbology isEqual:@"VNBarcodeSymbologyDataMatrix"])
+  if ([symbology isEqual:VNBarcodeSymbologyDataMatrix])
     return mojom::BarcodeFormat::DATA_MATRIX;
-  if ([symbology isEqual:@"VNBarcodeSymbologyEAN13"])
+  if ([symbology isEqual:VNBarcodeSymbologyEAN13])
     return mojom::BarcodeFormat::EAN_13;
-  if ([symbology isEqual:@"VNBarcodeSymbologyEAN8"])
+  if ([symbology isEqual:VNBarcodeSymbologyEAN8])
     return mojom::BarcodeFormat::EAN_8;
-  if ([symbology isEqual:@"VNBarcodeSymbologyITF14"] ||
-      [symbology isEqual:@"VNBarcodeSymbologyI2of5"] ||
-      [symbology isEqual:@"VNBarcodeSymbologyI2of5Checksum"]) {
+  if ([symbology isEqual:VNBarcodeSymbologyITF14] ||
+      [symbology isEqual:VNBarcodeSymbologyI2of5] ||
+      [symbology isEqual:VNBarcodeSymbologyI2of5Checksum]) {
     return mojom::BarcodeFormat::ITF;
   }
-  if ([symbology isEqual:@"VNBarcodeSymbologyPDF417"])
+  if ([symbology isEqual:VNBarcodeSymbologyPDF417])
     return mojom::BarcodeFormat::PDF417;
-  if ([symbology isEqual:@"VNBarcodeSymbologyQR"])
+  if ([symbology isEqual:VNBarcodeSymbologyQR])
     return mojom::BarcodeFormat::QR_CODE;
-  if ([symbology isEqual:@"VNBarcodeSymbologyUPCE"])
+  if ([symbology isEqual:VNBarcodeSymbologyUPCE])
     return mojom::BarcodeFormat::UPC_E;
   return mojom::BarcodeFormat::UNKNOWN;
 }
 
 void UpdateSymbologyHint(mojom::BarcodeFormat format,
-                         NSMutableSet<NSString*>* hint) {
-  // TODO(crbug/943106): use SDK header-declared constants after updating SDK.
+                         NSMutableArray<VNBarcodeSymbology>* hint)
+    API_AVAILABLE(macos(10.13)) {
   switch (format) {
     case mojom::BarcodeFormat::AZTEC:
-      [hint addObject:@"VNBarcodeSymbologyAztec"];
+      [hint addObject:VNBarcodeSymbologyAztec];
       return;
     case mojom::BarcodeFormat::CODE_128:
-      [hint addObject:@"VNBarcodeSymbologyCode128"];
+      [hint addObject:VNBarcodeSymbologyCode128];
       return;
     case mojom::BarcodeFormat::CODE_39:
       [hint addObjectsFromArray:@[
-        @"VNBarcodeSymbologyCode39", @"VNBarcodeSymbologyCode39Checksum",
-        @"VNBarcodeSymbologyCode39FullASCII",
-        @"VNBarcodeSymbologyCode39FullASCIIChecksum"
+        VNBarcodeSymbologyCode39, VNBarcodeSymbologyCode39Checksum,
+        VNBarcodeSymbologyCode39FullASCII,
+        VNBarcodeSymbologyCode39FullASCIIChecksum
       ]];
       return;
     case mojom::BarcodeFormat::CODE_93:
       [hint addObjectsFromArray:@[
-        @"VNBarcodeSymbologyCode93", @"VNBarcodeSymbologyCode93i"
+        VNBarcodeSymbologyCode93, VNBarcodeSymbologyCode93i
       ]];
       return;
     case mojom::BarcodeFormat::CODABAR:
       return;
     case mojom::BarcodeFormat::DATA_MATRIX:
-      [hint addObject:@"VNBarcodeSymbologyDataMatrix"];
+      [hint addObject:VNBarcodeSymbologyDataMatrix];
       return;
     case mojom::BarcodeFormat::EAN_13:
-      [hint addObject:@"VNBarcodeSymbologyEAN13"];
+      [hint addObject:VNBarcodeSymbologyEAN13];
       return;
     case mojom::BarcodeFormat::EAN_8:
-      [hint addObject:@"VNBarcodeSymbologyEAN8"];
+      [hint addObject:VNBarcodeSymbologyEAN8];
       return;
     case mojom::BarcodeFormat::ITF:
       [hint addObjectsFromArray:@[
-        @"VNBarcodeSymbologyITF14", @"VNBarcodeSymbologyI2of5",
-        @"VNBarcodeSymbologyI2of5Checksum"
+        VNBarcodeSymbologyITF14, VNBarcodeSymbologyI2of5,
+        VNBarcodeSymbologyI2of5Checksum
       ]];
       return;
     case mojom::BarcodeFormat::PDF417:
-      [hint addObject:@"VNBarcodeSymbologyPDF417"];
+      [hint addObject:VNBarcodeSymbologyPDF417];
       return;
     case mojom::BarcodeFormat::QR_CODE:
-      [hint addObject:@"VNBarcodeSymbologyQR"];
+      [hint addObject:VNBarcodeSymbologyQR];
       return;
     case mojom::BarcodeFormat::UPC_A:
       return;
     case mojom::BarcodeFormat::UPC_E:
-      [hint addObject:@"VNBarcodeSymbologyUPC_E"];
+      [hint addObject:VNBarcodeSymbologyUPCE];
       return;
     case mojom::BarcodeFormat::UNKNOWN:
       NOTREACHED();
@@ -127,13 +129,7 @@
 BarcodeDetectionImplMacVision::BarcodeDetectionImplMacVision(
     mojom::BarcodeDetectorOptionsPtr options)
     : weak_factory_(this) {
-  Class request_class = NSClassFromString(@"VNDetectBarcodesRequest");
-  if (!request_class) {
-    DLOG(ERROR) << "Failed to load VNDetectBarcodesRequest class";
-    return;
-  }
-
-  NSMutableSet<NSString*>* symbology_hints = [NSMutableSet set];
+  NSMutableArray<VNBarcodeSymbology>* symbology_hints = [NSMutableArray array];
   for (const auto& hint : options->formats) {
     if (hint == mojom::BarcodeFormat::UNKNOWN) {
       mojo::ReportBadMessage("Formats hint contains UNKNOWN BarcodeFormat.");
@@ -147,7 +143,7 @@
   // The repeating callback will not be run if BarcodeDetectionImplMacVision
   // object has already been destroyed.
   barcodes_async_request_ = VisionAPIAsyncRequestMac::Create(
-      request_class,
+      [VNDetectBarcodesRequest class],
       base::BindRepeating(&BarcodeDetectionImplMacVision::OnBarcodesDetected,
                           weak_factory_.GetWeakPtr()),
       symbology_hints_.get());
@@ -232,7 +228,7 @@
   NSArray<NSString*>* symbologies = vision_api->GetSupportedSymbologies();
 
   results.reserve([symbologies count]);
-  for (NSString* symbology : symbologies) {
+  for (VNBarcodeSymbology symbology : symbologies) {
     auto converted = ToBarcodeFormat(symbology);
     if (converted == shape_detection::mojom::BarcodeFormat::UNKNOWN) {
       DLOG(WARNING) << "Symbology " << base::SysNSStringToUTF8(symbology)
@@ -245,7 +241,8 @@
                                                             results.end());
 }
 
-NSSet<NSString*>* BarcodeDetectionImplMacVision::GetSymbologyHintsForTesting() {
+NSArray<VNBarcodeSymbology>*
+BarcodeDetectionImplMacVision::GetSymbologyHintsForTesting() {
   return symbology_hints_.get();
 }
 
diff --git a/services/shape_detection/barcode_detection_impl_mac_vision_api.h b/services/shape_detection/barcode_detection_impl_mac_vision_api.h
index 809c4b57..09006b78 100644
--- a/services/shape_detection/barcode_detection_impl_mac_vision_api.h
+++ b/services/shape_detection/barcode_detection_impl_mac_vision_api.h
@@ -5,28 +5,25 @@
 #ifndef SERVICES_SHAPE_DETECTION_BARCODE_DETECTION_IMPL_MAC_VISION_API_H_
 #define SERVICES_SHAPE_DETECTION_BARCODE_DETECTION_IMPL_MAC_VISION_API_H_
 
-#include <memory>
-
 #ifdef __OBJC__
 
 #import <Foundation/Foundation.h>
+#import <Vision/Vision.h>
 
-#include "base/mac/scoped_nsobject.h"
-#include "base/macros.h"
+#include <memory>
 
 namespace shape_detection {
 
 class VisionAPIInterface {
  public:
-  VisionAPIInterface() {}
-  virtual ~VisionAPIInterface() {}
+  VisionAPIInterface() = default;
+  virtual ~VisionAPIInterface() = default;
+  VisionAPIInterface(const VisionAPIInterface&) = delete;
+  VisionAPIInterface& operator=(const VisionAPIInterface&) = delete;
 
   static std::unique_ptr<VisionAPIInterface> Create();
 
-  virtual NSArray* GetSupportedSymbologies() const = 0;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(VisionAPIInterface);
+  virtual NSArray<VNBarcodeSymbology>* GetSupportedSymbologies() const = 0;
 };
 
 }  // namespace shape_detection
diff --git a/services/shape_detection/barcode_detection_impl_mac_vision_api.mm b/services/shape_detection/barcode_detection_impl_mac_vision_api.mm
index 983dc0b..2ce1698c 100644
--- a/services/shape_detection/barcode_detection_impl_mac_vision_api.mm
+++ b/services/shape_detection/barcode_detection_impl_mac_vision_api.mm
@@ -16,25 +16,16 @@
 
   ~VisionAPI() override = default;
 
-  NSArray* GetSupportedSymbologies() const override {
-    Class request_class = NSClassFromString(@"VNDetectBarcodesRequest");
-    if (!request_class) {
-      DPLOG(ERROR) << "Failed to load VNDetectBarcodesRequest class";
-      return [NSArray array];
+  NSArray<VNBarcodeSymbology>* GetSupportedSymbologies() const override {
+    if (@available(macOS 10.13, *)) {
+      return [VNDetectBarcodesRequest supportedSymbologies];
     }
 
-    SEL sel = NSSelectorFromString(@"supportedSymbologies");
-    id symbologies = [request_class performSelector:sel];
-    if (![symbologies isKindOfClass:[NSArray class]]) {
-      DLOG(ERROR)
-          << "Failed to get NSArray of supportedSymbologies (wrong type)";
-      return [NSArray array];
-    }
-    return symbologies;
+    return @[];
   }
 };
 
-}  // anonymous namespace
+}  // namespace
 
 // static
 std::unique_ptr<VisionAPIInterface> VisionAPIInterface::Create() {
diff --git a/services/shape_detection/barcode_detection_provider_mac_unittest.mm b/services/shape_detection/barcode_detection_provider_mac_unittest.mm
index 6c47a6e..afdf7c7 100644
--- a/services/shape_detection/barcode_detection_provider_mac_unittest.mm
+++ b/services/shape_detection/barcode_detection_provider_mac_unittest.mm
@@ -4,7 +4,8 @@
 
 #include "services/shape_detection/barcode_detection_provider_mac.h"
 
-#include <dlfcn.h>
+#import <Vision/Vision.h>
+
 #include <memory>
 #include <string>
 
@@ -46,14 +47,18 @@
     mojom::BarcodeFormat::AZTEC, mojom::BarcodeFormat::DATA_MATRIX,
     mojom::BarcodeFormat::QR_CODE};
 
-static NSArray* MockVisionSupportedSymbologyStrings = @[
+// Use strings here because, while these will only be used on 10.13 or later
+// when the real symbols will be available, this array is constructed statically
+// on all OS versions.
+static NSArray<VNBarcodeSymbology>* MockVisionSupportedSymbologyStrings = @[
   @"VNBarcodeSymbologyAztec", @"VNBarcodeSymbologyDataMatrix",
   @"VNBarcodeSymbologyQR"
 ];
 
 class MockVisionAPI : public VisionAPIInterface {
  public:
-  MOCK_CONST_METHOD0(GetSupportedSymbologies, NSArray*(void));
+  MOCK_CONST_METHOD0(GetSupportedSymbologies,
+                     NSArray<VNBarcodeSymbology>*(void));
 };
 
 std::unique_ptr<mojom::BarcodeDetectionProvider> CreateBarcodeProviderMac(
@@ -70,7 +75,7 @@
 }
 
 std::unique_ptr<VisionAPIInterface> CreateMockVisionAPI(
-    NSArray* returned_symbologies) {
+    NSArray<VNBarcodeSymbology>* returned_symbologies) {
   std::unique_ptr<NiceMock<MockVisionAPI>> mock_vision_api =
       std::make_unique<NiceMock<MockVisionAPI>>();
   ON_CALL(*mock_vision_api, GetSupportedSymbologies())
@@ -106,11 +111,6 @@
               MockVisionSupportedFormats.size());
   }
 
-  void TearDown() override {
-    if (vision_framework_)
-      dlclose(vision_framework_);
-  }
-
   void EnumerateSupportedFormatsCallback(
       const std::vector<mojom::BarcodeFormat>& expected,
       const std::vector<mojom::BarcodeFormat>& results) {
@@ -123,7 +123,6 @@
 
   std::unique_ptr<mojom::BarcodeDetectionProvider> provider_;
   base::test::SingleThreadTaskEnvironment task_environment_;
-  void* vision_framework_ = nullptr;
   bool is_vision_available_ = false;
 };
 
@@ -134,12 +133,6 @@
     return;
   }
 
-  // Only load Vision if we're testing it.
-  if (GetParam().test_vision_api) {
-    vision_framework_ =
-        dlopen("/System/Library/Frameworks/Vision.framework/Vision", RTLD_LAZY);
-  }
-
   provider_ = CreateBarcodeProviderMac(GetParam().vision_api.Run());
 
   base::RunLoop run_loop;
@@ -228,9 +221,6 @@
     return;
   }
 
-  vision_framework_ =
-      dlopen("/System/Library/Frameworks/Vision.framework/Vision", RTLD_LAZY);
-
   mojo::Remote<mojom::BarcodeDetectionProvider> provider_remote;
   mojo::MakeSelfOwnedReceiver(CreateBarcodeProviderMac(CreateVisionAPI()),
                               provider_remote.BindNewPipeAndPassReceiver());
diff --git a/services/shape_detection/detection_utils_mac.h b/services/shape_detection/detection_utils_mac.h
index 39d1356..fab7d7b1 100644
--- a/services/shape_detection/detection_utils_mac.h
+++ b/services/shape_detection/detection_utils_mac.h
@@ -43,17 +43,17 @@
   static std::unique_ptr<VisionAPIAsyncRequestMac> Create(
       Class request_class,
       Callback callback,
-      NSSet<NSString*>* symbology_hints = nullptr);
+      NSArray<VNBarcodeSymbology>* symbology_hints = nullptr);
 
   // Processes asynchronously an image analysis request and returns results with
   // |callback_| when the asynchronous request completes, the callers should
-  // only enqueue one request at a timer.
+  // only enqueue one request at a time.
   bool PerformRequest(const SkBitmap& bitmap);
 
  private:
   VisionAPIAsyncRequestMac(Callback callback,
                            Class request_class,
-                           NSSet<NSString*>* symbology_hints);
+                           NSArray<VNBarcodeSymbology>* symbology_hints);
 
   base::scoped_nsobject<VNRequest> request_;
   const Callback callback_;
diff --git a/services/shape_detection/detection_utils_mac.mm b/services/shape_detection/detection_utils_mac.mm
index 06a2ac12..32c42f8 100644
--- a/services/shape_detection/detection_utils_mac.mm
+++ b/services/shape_detection/detection_utils_mac.mm
@@ -4,10 +4,13 @@
 
 #include "services/shape_detection/detection_utils_mac.h"
 
+#import <Vision/Vision.h>
+
 #include <vector>
 
 #include "base/bind.h"
 #include "base/logging.h"
+#include "base/mac/foundation_util.h"
 #include "base/mac/scoped_cftyperef.h"
 #include "base/memory/ptr_util.h"
 #include "base/numerics/checked_math.h"
@@ -60,7 +63,7 @@
 std::unique_ptr<VisionAPIAsyncRequestMac> VisionAPIAsyncRequestMac::Create(
     Class request_class,
     Callback callback,
-    NSSet<NSString*>* symbology_hints) {
+    NSArray<VNBarcodeSymbology>* symbology_hints) {
   return base::WrapUnique(new VisionAPIAsyncRequestMac(
       std::move(callback), request_class, symbology_hints));
 }
@@ -68,7 +71,7 @@
 VisionAPIAsyncRequestMac::VisionAPIAsyncRequestMac(
     Callback callback,
     Class request_class,
-    NSSet<NSString*>* symbology_hints)
+    NSArray<VNBarcodeSymbology>* symbology_hints)
     : callback_(std::move(callback)) {
   DCHECK(callback_);
 
@@ -81,10 +84,12 @@
 
   request_.reset([[request_class alloc] initWithCompletionHandler:handler]);
 
-  // Pass symbology hints to request.
-  SEL sel = NSSelectorFromString(@"setSymbologies:");
-  if ([symbology_hints count] > 0)
-    [request_ performSelector:sel withObject:symbology_hints];
+  // Pass symbology hints to request. Only valid for VNDetectBarcodesRequest.
+  if ([symbology_hints count] > 0) {
+    VNDetectBarcodesRequest* barcode_request =
+        base::mac::ObjCCastStrict<VNDetectBarcodesRequest>(request_.get());
+    barcode_request.symbologies = symbology_hints;
+  }
 }
 
 VisionAPIAsyncRequestMac::~VisionAPIAsyncRequestMac() = default;
@@ -92,12 +97,6 @@
 // Processes asynchronously an image analysis request and returns results with
 // |callback_| when the asynchronous request completes.
 bool VisionAPIAsyncRequestMac::PerformRequest(const SkBitmap& bitmap) {
-  Class image_handler_class = NSClassFromString(@"VNImageRequestHandler");
-  if (!image_handler_class) {
-    DLOG(ERROR) << "Failed to load VNImageRequestHandler class";
-    return false;
-  }
-
   base::scoped_nsobject<CIImage> ci_image = CreateCIImageFromSkBitmap(bitmap);
   if (!ci_image) {
     DLOG(ERROR) << "Failed to create image from SkBitmap";
@@ -105,7 +104,7 @@
   }
 
   base::scoped_nsobject<VNImageRequestHandler> image_handler(
-      [[image_handler_class alloc] initWithCIImage:ci_image options:@{}]);
+      [[VNImageRequestHandler alloc] initWithCIImage:ci_image options:@{}]);
   if (!image_handler) {
     DLOG(ERROR) << "Failed to create image request handler";
     return false;
diff --git a/services/shape_detection/face_detection_impl_mac_unittest.mm b/services/shape_detection/face_detection_impl_mac_unittest.mm
index ae8a01f..9a4fd48 100644
--- a/services/shape_detection/face_detection_impl_mac_unittest.mm
+++ b/services/shape_detection/face_detection_impl_mac_unittest.mm
@@ -4,7 +4,6 @@
 
 #include "services/shape_detection/face_detection_impl_mac.h"
 
-#include <dlfcn.h>
 #include <memory>
 #include <utility>
 
@@ -102,21 +101,7 @@
 
 class FaceDetectionImplMacTest : public TestWithParam<struct TestParams> {
  public:
-  ~FaceDetectionImplMacTest() override {}
-
-  void SetUp() override {
-    if (@available(macOS 10.13, *)) {
-      vision_framework_ = dlopen(
-          "/System/Library/Frameworks/Vision.framework/Vision", RTLD_LAZY);
-    }
-  }
-
-  void TearDown() override {
-    if (@available(macOS 10.13, *)) {
-      if (vision_framework_)
-        dlclose(vision_framework_);
-    }
-  }
+  ~FaceDetectionImplMacTest() override = default;
 
   void DetectCallback(size_t num_faces,
                       size_t num_landmarks,
@@ -136,7 +121,6 @@
 
   std::unique_ptr<mojom::FaceDetection> impl_;
   base::test::SingleThreadTaskEnvironment task_environment_;
-  void* vision_framework_;
 };
 
 TEST_P(FaceDetectionImplMacTest, CreateAndDestroy) {
diff --git a/services/shape_detection/face_detection_impl_mac_vision.mm b/services/shape_detection/face_detection_impl_mac_vision.mm
index 92b87b09..c4b11596 100644
--- a/services/shape_detection/face_detection_impl_mac_vision.mm
+++ b/services/shape_detection/face_detection_impl_mac_vision.mm
@@ -35,15 +35,10 @@
 }
 
 FaceDetectionImplMacVision::FaceDetectionImplMacVision() : weak_factory_(this) {
-  Class request_class = NSClassFromString(@"VNDetectFaceLandmarksRequest");
-  if (!request_class) {
-    DLOG(ERROR) << "Failed to load VNDetectFaceLandmarksRequest class";
-    return;
-  }
   // The repeating callback will not be run if FaceDetectionImplMacVision object
   // has already been destroyed.
   landmarks_async_request_ = VisionAPIAsyncRequestMac::Create(
-      request_class,
+      [VNDetectFaceLandmarksRequest class],
       base::BindRepeating(&FaceDetectionImplMacVision::OnFacesDetected,
                           weak_factory_.GetWeakPtr()));
 }
diff --git a/services/shape_detection/shape_detection_service.cc b/services/shape_detection/shape_detection_service.cc
index f5238af..96c15ee 100644
--- a/services/shape_detection/shape_detection_service.cc
+++ b/services/shape_detection/shape_detection_service.cc
@@ -13,7 +13,6 @@
 #include "services/shape_detection/barcode_detection_provider_impl.h"
 #include "services/shape_detection/face_detection_provider_win.h"
 #elif defined(OS_MAC)
-#include <dlfcn.h>
 #include "services/shape_detection/barcode_detection_provider_mac.h"
 #include "services/shape_detection/face_detection_provider_mac.h"
 #else
@@ -32,22 +31,9 @@
 ShapeDetectionService::ShapeDetectionService(
     mojo::PendingReceiver<mojom::ShapeDetectionService> receiver)
     : receiver_(this, std::move(receiver)) {
-#if defined(OS_MAC)
-  if (__builtin_available(macOS 10.13, *)) {
-    vision_framework_ =
-        dlopen("/System/Library/Frameworks/Vision.framework/Vision", RTLD_LAZY);
-  }
-#endif
 }
 
-ShapeDetectionService::~ShapeDetectionService() {
-#if defined(OS_MAC)
-  if (__builtin_available(macOS 10.13, *)) {
-    if (vision_framework_)
-      dlclose(vision_framework_);
-  }
-#endif
-}
+ShapeDetectionService::~ShapeDetectionService() = default;
 
 void ShapeDetectionService::BindBarcodeDetectionProvider(
     mojo::PendingReceiver<mojom::BarcodeDetectionProvider> receiver) {
diff --git a/services/shape_detection/shape_detection_service.h b/services/shape_detection/shape_detection_service.h
index e9e2654..5c9eb99 100644
--- a/services/shape_detection/shape_detection_service.h
+++ b/services/shape_detection/shape_detection_service.h
@@ -33,10 +33,6 @@
  private:
   mojo::Receiver<mojom::ShapeDetectionService> receiver_;
 
-#if defined(OS_MAC)
-  void* vision_framework_;
-#endif
-
   DISALLOW_COPY_AND_ASSIGN(ShapeDetectionService);
 };
 
diff --git a/services/tracing/public/mojom/perfetto_service.mojom b/services/tracing/public/mojom/perfetto_service.mojom
index 7af80cb..790c636bc 100644
--- a/services/tracing/public/mojom/perfetto_service.mojom
+++ b/services/tracing/public/mojom/perfetto_service.mojom
@@ -178,13 +178,15 @@
   array<string> producer_name_filter;
 };
 
-// Allows to disable the built-in data sources implicitly enabled in the tracing service.
-// See comments in third_party/perfetto/protos/perfetto/config/trace_config.proto.
+// Allows to disable the built-in data sources implicitly enabled in the
+// tracing service. See comments in
+// third_party/perfetto/protos/perfetto/config/trace_config.proto.
 struct PerfettoBuiltinDataSource {
   bool disable_clock_snapshotting;
   bool disable_trace_config;
   bool disable_system_info;
   bool disable_service_events;
+  int32 primary_trace_clock_id;
 };
 
 struct IncrementalStateConfig {
diff --git a/services/tracing/public/mojom/trace_config_mojom_traits.cc b/services/tracing/public/mojom/trace_config_mojom_traits.cc
index 49ea3c5..3d207996 100644
--- a/services/tracing/public/mojom/trace_config_mojom_traits.cc
+++ b/services/tracing/public/mojom/trace_config_mojom_traits.cc
@@ -57,6 +57,8 @@
   out->set_disable_trace_config(data.disable_trace_config());
   out->set_disable_system_info(data.disable_system_info());
   out->set_disable_service_events(data.disable_service_events());
+  out->set_primary_trace_clock(static_cast<perfetto::protos::gen::BuiltinClock>(
+      data.primary_trace_clock_id()));
   return true;
 }
 
diff --git a/services/tracing/public/mojom/trace_config_mojom_traits.h b/services/tracing/public/mojom/trace_config_mojom_traits.h
index 87d73c76..2781afc 100644
--- a/services/tracing/public/mojom/trace_config_mojom_traits.h
+++ b/services/tracing/public/mojom/trace_config_mojom_traits.h
@@ -112,6 +112,11 @@
     return src.disable_service_events();
   }
 
+  static int32_t primary_trace_clock_id(
+      const perfetto::TraceConfig::BuiltinDataSource& src) {
+    return src.primary_trace_clock();
+  }
+
   static bool Read(tracing::mojom::PerfettoBuiltinDataSourceDataView data,
                    perfetto::TraceConfig::BuiltinDataSource* out);
 };
diff --git a/storage/browser/file_system/file_stream_reader.h b/storage/browser/file_system/file_stream_reader.h
index b3cfc7f..d9ac296 100644
--- a/storage/browser/file_system/file_stream_reader.h
+++ b/storage/browser/file_system/file_stream_reader.h
@@ -60,6 +60,7 @@
   // ERR_UPLOAD_FILE_CHANGED error.
   COMPONENT_EXPORT(STORAGE_BROWSER)
   static std::unique_ptr<FileStreamReader> CreateForMemoryFile(
+      scoped_refptr<base::TaskRunner> task_runner,
       base::WeakPtr<ObfuscatedFileUtilMemoryDelegate> memory_file_util,
       const base::FilePath& file_path,
       int64_t initial_offset,
diff --git a/storage/browser/file_system/file_stream_test_utils.cc b/storage/browser/file_system/file_stream_test_utils.cc
index 835a423..e66dfc7 100644
--- a/storage/browser/file_system/file_stream_test_utils.cc
+++ b/storage/browser/file_system/file_stream_test_utils.cc
@@ -40,6 +40,14 @@
   }
 }
 
+int64_t GetLengthFromReader(FileStreamReader* reader) {
+  EXPECT_NE(nullptr, reader);
+  net::TestInt64CompletionCallback callback;
+
+  int rv = reader->GetLength(callback.callback());
+  return callback.GetResult(rv);
+}
+
 int WriteStringToWriter(FileStreamWriter* writer, const std::string& data) {
   scoped_refptr<net::StringIOBuffer> buffer =
       base::MakeRefCounted<net::StringIOBuffer>(data);
diff --git a/storage/browser/file_system/file_stream_test_utils.h b/storage/browser/file_system/file_stream_test_utils.h
index 5714f7a..d6425f1 100644
--- a/storage/browser/file_system/file_stream_test_utils.h
+++ b/storage/browser/file_system/file_stream_test_utils.h
@@ -20,8 +20,12 @@
                     size_t size,
                     int* result);
 
-// Writes |data| to |writer|, an intialized FileStreamWriter. Returns net::OK if
-// successful, otherwise a net error.
+// Returns the length of the file if it could be successfully retrieved,
+// otherwise a net error.
+int64_t GetLengthFromReader(FileStreamReader* reader);
+
+// Writes |data| to |writer|, an initialized FileStreamWriter. Returns net::OK
+// if successful, otherwise a net error.
 int WriteStringToWriter(FileStreamWriter* writer, const std::string& data);
 
 }  // namespace storage
diff --git a/storage/browser/file_system/file_stream_writer.h b/storage/browser/file_system/file_stream_writer.h
index 2ddbecc6..11ce21c 100644
--- a/storage/browser/file_system/file_stream_writer.h
+++ b/storage/browser/file_system/file_stream_writer.h
@@ -48,10 +48,9 @@
 
   // Creates a writer for the existing memory file in the path |file_path|
   // starting from |initial_offset|.
-  // TODO(mek): Remove or use |open_or_create| field here, as it is not
-  // currently used. https://crbug.com/1041048
   COMPONENT_EXPORT(STORAGE_BROWSER)
   static std::unique_ptr<FileStreamWriter> CreateForMemoryFile(
+      scoped_refptr<base::TaskRunner> task_runner,
       base::WeakPtr<ObfuscatedFileUtilMemoryDelegate> memory_file_util,
       const base::FilePath& file_path,
       int64_t initial_offset);
diff --git a/storage/browser/file_system/file_system_file_stream_reader.cc b/storage/browser/file_system/file_system_file_stream_reader.cc
index 8a3b8516..9d37b807 100644
--- a/storage/browser/file_system/file_system_file_stream_reader.cc
+++ b/storage/browser/file_system/file_system_file_stream_reader.cc
@@ -112,6 +112,7 @@
           file_system_context_->sandbox_delegate()->memory_file_util_delegate();
     }
     file_reader_ = FileStreamReader::CreateForMemoryFile(
+        file_system_context_->default_file_task_runner(),
         memory_file_util_delegate, platform_path, initial_offset_,
         expected_modification_time_);
   } else {
diff --git a/storage/browser/file_system/memory_file_stream_reader.cc b/storage/browser/file_system/memory_file_stream_reader.cc
index f5d895c6..e91016d 100644
--- a/storage/browser/file_system/memory_file_stream_reader.cc
+++ b/storage/browser/file_system/memory_file_stream_reader.cc
@@ -8,68 +8,112 @@
 #include <utility>
 
 #include "base/memory/ptr_util.h"
+#include "base/task_runner_util.h"
 #include "net/base/net_errors.h"
 
 namespace storage {
 
 std::unique_ptr<FileStreamReader> FileStreamReader::CreateForMemoryFile(
+    scoped_refptr<base::TaskRunner> task_runner,
     base::WeakPtr<ObfuscatedFileUtilMemoryDelegate> memory_file_util,
     const base::FilePath& file_path,
     int64_t initial_offset,
     const base::Time& expected_modification_time) {
-  return base::WrapUnique(
-      new MemoryFileStreamReader(std::move(memory_file_util), file_path,
-                                 initial_offset, expected_modification_time));
+  return base::WrapUnique(new MemoryFileStreamReader(
+      std::move(task_runner), std::move(memory_file_util), file_path,
+      initial_offset, expected_modification_time));
 }
 
 MemoryFileStreamReader::MemoryFileStreamReader(
+    scoped_refptr<base::TaskRunner> task_runner,
     base::WeakPtr<ObfuscatedFileUtilMemoryDelegate> memory_file_util,
     const base::FilePath& file_path,
     int64_t initial_offset,
     const base::Time& expected_modification_time)
     : memory_file_util_(std::move(memory_file_util)),
+      task_runner_(std::move(task_runner)),
       file_path_(file_path),
       expected_modification_time_(expected_modification_time),
       offset_(initial_offset) {
-  DCHECK(memory_file_util_);
+  DCHECK(memory_file_util_.MaybeValid());
 }
 
 MemoryFileStreamReader::~MemoryFileStreamReader() = default;
 
 int MemoryFileStreamReader::Read(net::IOBuffer* buf,
                                  int buf_len,
-                                 net::CompletionOnceCallback /*callback*/) {
-  base::File::Info file_info;
-  if (memory_file_util_->GetFileInfo(file_path_, &file_info) !=
-      base::File::FILE_OK) {
-    return net::ERR_FILE_NOT_FOUND;
-  }
+                                 net::CompletionOnceCallback callback) {
+  task_runner_->PostTaskAndReplyWithResult(
+      FROM_HERE,
+      base::BindOnce(
+          [](base::WeakPtr<ObfuscatedFileUtilMemoryDelegate> util,
+             const base::FilePath& path, base::Time expected_modification_time,
+             int64_t offset, net::IOBuffer* buf, int buf_len) -> int {
+            if (!util)
+              return net::ERR_FILE_NOT_FOUND;
+            base::File::Info file_info;
+            if (util->GetFileInfo(path, &file_info) != base::File::FILE_OK)
+              return net::ERR_FILE_NOT_FOUND;
 
-  if (!FileStreamReader::VerifySnapshotTime(expected_modification_time_,
-                                            file_info)) {
-    return net::ERR_UPLOAD_FILE_CHANGED;
-  }
+            if (!FileStreamReader::VerifySnapshotTime(
+                    expected_modification_time, file_info)) {
+              return net::ERR_UPLOAD_FILE_CHANGED;
+            }
 
-  int result = memory_file_util_->ReadFile(file_path_, offset_, buf, buf_len);
+            return util->ReadFile(path, offset, buf, buf_len);
+          },
+          memory_file_util_, file_path_, expected_modification_time_, offset_,
+          buf, buf_len),
+      base::BindOnce(&MemoryFileStreamReader::OnReadCompleted,
+                     weak_factory_.GetWeakPtr(), std::move(callback)));
+
+  return net::ERR_IO_PENDING;
+}
+
+void MemoryFileStreamReader::OnReadCompleted(
+    net::CompletionOnceCallback callback,
+    int result) {
   if (result > 0)
     offset_ += result;
-  return result;
+
+  std::move(callback).Run(result);
 }
 
 int64_t MemoryFileStreamReader::GetLength(
-    net::Int64CompletionOnceCallback /*callback*/) {
-  base::File::Info file_info;
-  if (memory_file_util_->GetFileInfo(file_path_, &file_info) !=
-      base::File::FILE_OK) {
-    return net::ERR_FILE_NOT_FOUND;
-  }
+    net::Int64CompletionOnceCallback callback) {
+  task_runner_->PostTaskAndReplyWithResult(
+      FROM_HERE,
+      base::BindOnce(
+          [](base::WeakPtr<ObfuscatedFileUtilMemoryDelegate> util,
+             const base::FilePath& path,
+             base::Time expected_modification_time) -> int64_t {
+            if (!util)
+              return net::ERR_FILE_NOT_FOUND;
+            base::File::Info file_info;
+            if (util->GetFileInfo(path, &file_info) != base::File::FILE_OK) {
+              return net::ERR_FILE_NOT_FOUND;
+            }
 
-  if (!FileStreamReader::VerifySnapshotTime(expected_modification_time_,
-                                            file_info)) {
-    return net::ERR_UPLOAD_FILE_CHANGED;
-  }
+            if (!FileStreamReader::VerifySnapshotTime(
+                    expected_modification_time, file_info)) {
+              return net::ERR_UPLOAD_FILE_CHANGED;
+            }
 
-  return file_info.size;
+            return file_info.size;
+          },
+          memory_file_util_, file_path_, expected_modification_time_),
+      // |callback| is not directly used to make sure that it is not called if
+      // stream is deleted while this function is in flight.
+      base::BindOnce(&MemoryFileStreamReader::OnGetLengthCompleted,
+                     weak_factory_.GetWeakPtr(), std::move(callback)));
+
+  return net::ERR_IO_PENDING;
+}
+
+void MemoryFileStreamReader::OnGetLengthCompleted(
+    net::Int64CompletionOnceCallback callback,
+    int64_t result) {
+  std::move(callback).Run(result);
 }
 
 }  // namespace storage
diff --git a/storage/browser/file_system/memory_file_stream_reader.h b/storage/browser/file_system/memory_file_stream_reader.h
index 909db6b..4f05d45 100644
--- a/storage/browser/file_system/memory_file_stream_reader.h
+++ b/storage/browser/file_system/memory_file_stream_reader.h
@@ -32,17 +32,25 @@
   friend class FileStreamReader;
 
   MemoryFileStreamReader(
+      scoped_refptr<base::TaskRunner> task_runner,
       base::WeakPtr<ObfuscatedFileUtilMemoryDelegate> memory_file_util,
       const base::FilePath& file_path,
       int64_t initial_offset,
       const base::Time& expected_modification_time);
 
+  void OnReadCompleted(net::CompletionOnceCallback callback, int result);
+  void OnGetLengthCompleted(net::Int64CompletionOnceCallback callback,
+                            int64_t result);
+
   base::WeakPtr<ObfuscatedFileUtilMemoryDelegate> memory_file_util_;
 
+  const scoped_refptr<base::TaskRunner> task_runner_;
   const base::FilePath file_path_;
   const base::Time expected_modification_time_;
   int64_t offset_;
 
+  base::WeakPtrFactory<MemoryFileStreamReader> weak_factory_{this};
+
   DISALLOW_COPY_AND_ASSIGN(MemoryFileStreamReader);
 };
 
diff --git a/storage/browser/file_system/memory_file_stream_reader_unittest.cc b/storage/browser/file_system/memory_file_stream_reader_unittest.cc
index 7cbaf6e..99bcfcb 100644
--- a/storage/browser/file_system/memory_file_stream_reader_unittest.cc
+++ b/storage/browser/file_system/memory_file_stream_reader_unittest.cc
@@ -17,6 +17,7 @@
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
 #include "base/macros.h"
+#include "base/test/task_environment.h"
 #include "net/base/io_buffer.h"
 #include "net/base/net_errors.h"
 #include "storage/browser/file_system/file_stream_reader.h"
@@ -62,9 +63,9 @@
       const base::FilePath& path,
       int64_t initial_offset,
       const base::Time& expected_modification_time) {
-    return FileStreamReader::CreateForMemoryFile(file_util_->GetWeakPtr(), path,
-                                                 initial_offset,
-                                                 expected_modification_time);
+    return FileStreamReader::CreateForMemoryFile(
+        base::ThreadTaskRunnerHandle::Get(), file_util_->GetWeakPtr(), path,
+        initial_offset, expected_modification_time);
   }
 
   void TouchTestFile(base::TimeDelta delta) {
@@ -83,6 +84,7 @@
   }
 
  private:
+  base::test::TaskEnvironment task_environment_;
   base::ScopedTempDir file_system_directory_;
   std::unique_ptr<ObfuscatedFileUtilMemoryDelegate> file_util_;
   base::Time test_file_modification_time_;
@@ -113,14 +115,14 @@
   ASSERT_EQ(net::OK, result);
   ASSERT_EQ(0U, data.size());
 
-  int64_t length_result = reader->GetLength(base::DoNothing());
+  int64_t length_result = GetLengthFromReader(reader.get());
   ASSERT_EQ(0, length_result);
 }
 
 TEST_F(MemoryFileStreamReaderTest, GetLengthNormal) {
   std::unique_ptr<FileStreamReader> reader(
       CreateFileReader(test_path(), 0, test_file_modification_time()));
-  int64_t result = reader->GetLength(base::DoNothing());
+  int64_t result = GetLengthFromReader(reader.get());
   ASSERT_EQ(kTestDataSize, result);
 }
 
@@ -131,7 +133,7 @@
 
   std::unique_ptr<FileStreamReader> reader(
       CreateFileReader(test_path(), 0, test_file_modification_time()));
-  int64_t result = reader->GetLength(base::DoNothing());
+  int64_t result = GetLengthFromReader(reader.get());
   ASSERT_EQ(net::ERR_UPLOAD_FILE_CHANGED, result);
 }
 
@@ -142,14 +144,14 @@
 
   std::unique_ptr<FileStreamReader> reader(
       CreateFileReader(test_path(), 0, base::Time()));
-  int64_t result = reader->GetLength(base::DoNothing());
+  int64_t result = GetLengthFromReader(reader.get());
   ASSERT_EQ(kTestDataSize, result);
 }
 
 TEST_F(MemoryFileStreamReaderTest, GetLengthWithOffset) {
   std::unique_ptr<FileStreamReader> reader(
       CreateFileReader(test_path(), 3, base::Time()));
-  int64_t result = reader->GetLength(base::DoNothing());
+  int64_t result = GetLengthFromReader(reader.get());
   // Initial offset does not affect the result of GetLength.
   ASSERT_EQ(kTestDataSize, result);
 }
diff --git a/storage/browser/file_system/memory_file_stream_writer.cc b/storage/browser/file_system/memory_file_stream_writer.cc
index 9c421145..f5a68be 100644
--- a/storage/browser/file_system/memory_file_stream_writer.cc
+++ b/storage/browser/file_system/memory_file_stream_writer.cc
@@ -8,43 +8,66 @@
 #include <utility>
 
 #include "base/memory/ptr_util.h"
+#include "base/task_runner_util.h"
 #include "net/base/net_errors.h"
 
 namespace storage {
 
 std::unique_ptr<FileStreamWriter> FileStreamWriter::CreateForMemoryFile(
+    scoped_refptr<base::TaskRunner> task_runner,
     base::WeakPtr<ObfuscatedFileUtilMemoryDelegate> memory_file_util,
     const base::FilePath& file_path,
     int64_t initial_offset) {
   return base::WrapUnique(new MemoryFileStreamWriter(
-      std::move(memory_file_util), file_path, initial_offset));
+      std::move(task_runner), std::move(memory_file_util), file_path,
+      initial_offset));
 }
 
 MemoryFileStreamWriter::MemoryFileStreamWriter(
+    scoped_refptr<base::TaskRunner> task_runner,
     base::WeakPtr<ObfuscatedFileUtilMemoryDelegate> memory_file_util,
     const base::FilePath& file_path,
     int64_t initial_offset)
     : memory_file_util_(std::move(memory_file_util)),
+      task_runner_(std::move(task_runner)),
       file_path_(file_path),
       offset_(initial_offset) {
-  DCHECK(memory_file_util_);
+  DCHECK(memory_file_util_.MaybeValid());
 }
 
 MemoryFileStreamWriter::~MemoryFileStreamWriter() = default;
 
 int MemoryFileStreamWriter::Write(net::IOBuffer* buf,
                                   int buf_len,
-                                  net::CompletionOnceCallback /*callback*/) {
-  base::File::Info file_info;
-  if (memory_file_util_->GetFileInfo(file_path_, &file_info) !=
-      base::File::FILE_OK) {
-    return net::ERR_FILE_NOT_FOUND;
-  }
+                                  net::CompletionOnceCallback callback) {
+  task_runner_->PostTaskAndReplyWithResult(
+      FROM_HERE,
+      base::BindOnce(
+          [](base::WeakPtr<ObfuscatedFileUtilMemoryDelegate> util,
+             const base::FilePath& path, int64_t offset, net::IOBuffer* buf,
+             int buf_len) -> int {
+            if (!util)
+              return net::ERR_FILE_NOT_FOUND;
+            base::File::Info file_info;
+            if (util->GetFileInfo(path, &file_info) != base::File::FILE_OK)
+              return net::ERR_FILE_NOT_FOUND;
 
-  int result = memory_file_util_->WriteFile(file_path_, offset_, buf, buf_len);
+            return util->WriteFile(path, offset, buf, buf_len);
+          },
+          memory_file_util_, file_path_, offset_, buf, buf_len),
+      base::BindOnce(&MemoryFileStreamWriter::OnWriteCompleted,
+                     weak_factory_.GetWeakPtr(), std::move(callback)));
+
+  return net::ERR_IO_PENDING;
+}
+
+void MemoryFileStreamWriter::OnWriteCompleted(
+    net::CompletionOnceCallback callback,
+    int result) {
   if (result > 0)
     offset_ += result;
-  return result;
+
+  std::move(callback).Run(result);
 }
 
 int MemoryFileStreamWriter::Cancel(net::CompletionOnceCallback /*callback*/) {
diff --git a/storage/browser/file_system/memory_file_stream_writer.h b/storage/browser/file_system/memory_file_stream_writer.h
index fe1c9d1..74f6213f 100644
--- a/storage/browser/file_system/memory_file_stream_writer.h
+++ b/storage/browser/file_system/memory_file_stream_writer.h
@@ -30,15 +30,21 @@
  private:
   friend class FileStreamWriter;
   MemoryFileStreamWriter(
+      scoped_refptr<base::TaskRunner> task_runner,
       base::WeakPtr<ObfuscatedFileUtilMemoryDelegate> memory_file_util,
       const base::FilePath& file_path,
       int64_t initial_offset);
 
+  void OnWriteCompleted(net::CompletionOnceCallback callback, int result);
+
   base::WeakPtr<ObfuscatedFileUtilMemoryDelegate> memory_file_util_;
 
+  const scoped_refptr<base::TaskRunner> task_runner_;
   const base::FilePath file_path_;
   int64_t offset_;
 
+  base::WeakPtrFactory<MemoryFileStreamWriter> weak_factory_{this};
+
   DISALLOW_COPY_AND_ASSIGN(MemoryFileStreamWriter);
 };
 
diff --git a/storage/browser/file_system/memory_file_stream_writer_unittest.cc b/storage/browser/file_system/memory_file_stream_writer_unittest.cc
index 7fcda3df..44342a3 100644
--- a/storage/browser/file_system/memory_file_stream_writer_unittest.cc
+++ b/storage/browser/file_system/memory_file_stream_writer_unittest.cc
@@ -13,6 +13,7 @@
 #include "base/bind_helpers.h"
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
+#include "base/test/task_environment.h"
 #include "net/base/io_buffer.h"
 #include "net/base/net_errors.h"
 #include "storage/browser/file_system/file_stream_test_utils.h"
@@ -59,11 +60,13 @@
 
   std::unique_ptr<FileStreamWriter> CreateWriter(const base::FilePath& path,
                                                  int64_t offset) {
-    return FileStreamWriter::CreateForMemoryFile(file_util_->GetWeakPtr(), path,
-                                                 offset);
+    return FileStreamWriter::CreateForMemoryFile(
+        base::ThreadTaskRunnerHandle::Get(), file_util_->GetWeakPtr(), path,
+        offset);
   }
 
  private:
+  base::test::TaskEnvironment task_environment_;
   base::ScopedTempDir file_system_directory_;
   std::unique_ptr<ObfuscatedFileUtilMemoryDelegate> file_util_;
 };
diff --git a/storage/browser/file_system/obfuscated_file_util_memory_delegate.cc b/storage/browser/file_system/obfuscated_file_util_memory_delegate.cc
index 5919d1f1..2265307 100644
--- a/storage/browser/file_system/obfuscated_file_util_memory_delegate.cc
+++ b/storage/browser/file_system/obfuscated_file_util_memory_delegate.cc
@@ -56,13 +56,17 @@
 ObfuscatedFileUtilMemoryDelegate::ObfuscatedFileUtilMemoryDelegate(
     const base::FilePath& file_system_directory)
     : root_(std::make_unique<Entry>(Entry::kDirectory)) {
+  DETACH_FROM_SEQUENCE(sequence_checker_);
   file_system_directory.GetComponents(&root_path_components_);
 }
 
-ObfuscatedFileUtilMemoryDelegate::~ObfuscatedFileUtilMemoryDelegate() = default;
+ObfuscatedFileUtilMemoryDelegate::~ObfuscatedFileUtilMemoryDelegate() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+}
 
 base::Optional<ObfuscatedFileUtilMemoryDelegate::DecomposedPath>
 ObfuscatedFileUtilMemoryDelegate::ParsePath(const base::FilePath& path) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DecomposedPath dp;
 
   path.GetComponents(&dp.components);
@@ -118,6 +122,7 @@
 
 bool ObfuscatedFileUtilMemoryDelegate::DirectoryExists(
     const base::FilePath& path) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   base::Optional<DecomposedPath> dp = ParsePath(path);
   return dp && dp->entry && dp->entry->type == Entry::kDirectory;
 }
@@ -126,6 +131,7 @@
     const base::FilePath& path,
     bool exclusive,
     bool recursive) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   base::Optional<DecomposedPath> dp = ParsePath(path);
   if (!dp)
     return base::File::FILE_ERROR_NOT_FOUND;
@@ -169,6 +175,7 @@
 bool ObfuscatedFileUtilMemoryDelegate::DeleteFileOrDirectory(
     const base::FilePath& path,
     bool recursive) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   base::Optional<DecomposedPath> dp = ParsePath(path);
   if (!dp)
     return false;
@@ -185,11 +192,13 @@
 }
 
 bool ObfuscatedFileUtilMemoryDelegate::IsLink(const base::FilePath& file_path) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   // In-memory file system does not support links.
   return false;
 }
 
 bool ObfuscatedFileUtilMemoryDelegate::PathExists(const base::FilePath& path) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   base::Optional<DecomposedPath> dp = ParsePath(path);
   return dp && dp->entry;
 }
@@ -197,6 +206,7 @@
 base::File ObfuscatedFileUtilMemoryDelegate::CreateOrOpen(
     const base::FilePath& path,
     int file_flags) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   // TODO:(https://crbug.com/936722): Once the output of this function is
   // changed to base::File::Error, it can use CreateOrOpenInternal to perform
   // the task and return the result.
@@ -206,6 +216,7 @@
 void ObfuscatedFileUtilMemoryDelegate::CreateOrOpenInternal(
     const DecomposedPath& dp,
     int file_flags) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (!dp.entry) {
     dp.parent->directory_content.emplace(dp.components.back(), Entry::kFile);
     return;
@@ -221,6 +232,7 @@
 
 base::File::Error ObfuscatedFileUtilMemoryDelegate::DeleteFile(
     const base::FilePath& path) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   base::Optional<DecomposedPath> dp = ParsePath(path);
   if (!dp || !dp->entry)
     return base::File::FILE_ERROR_NOT_FOUND;
@@ -235,6 +247,7 @@
 base::File::Error ObfuscatedFileUtilMemoryDelegate::EnsureFileExists(
     const base::FilePath& path,
     bool* created) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   base::Optional<DecomposedPath> dp = ParsePath(path);
   *created = false;
   if (!dp || !dp->parent)
@@ -253,6 +266,7 @@
 base::File::Error ObfuscatedFileUtilMemoryDelegate::GetFileInfo(
     const base::FilePath& path,
     base::File::Info* file_info) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   base::Optional<DecomposedPath> dp = ParsePath(path);
   if (!dp || !dp->entry)
     return base::File::FILE_ERROR_NOT_FOUND;
@@ -272,6 +286,7 @@
     const base::FilePath& path,
     const base::Time& last_access_time,
     const base::Time& last_modified_time) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   base::Optional<DecomposedPath> dp = ParsePath(path);
   if (!dp || !dp->entry)
     return base::File::FILE_ERROR_FAILED;
@@ -285,6 +300,7 @@
 base::File::Error ObfuscatedFileUtilMemoryDelegate::Truncate(
     const base::FilePath& path,
     int64_t length) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   base::Optional<DecomposedPath> dp = ParsePath(path);
   if (!dp || !dp->entry || dp->entry->type != Entry::kFile)
     return base::File::FILE_ERROR_NOT_FOUND;
@@ -297,6 +313,7 @@
 ObfuscatedFileUtilMemoryDelegate::CopyOrMoveModeForDestination(
     const FileSystemURL& /*dest_url*/,
     bool copy) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   return copy ? NativeFileUtil::CopyOrMoveMode::COPY_SYNC
               : NativeFileUtil::CopyOrMoveMode::MOVE;
 }
@@ -306,6 +323,7 @@
     const base::FilePath& dest_path,
     FileSystemOperation::CopyOrMoveOption option,
     NativeFileUtil::CopyOrMoveMode mode) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   base::Optional<DecomposedPath> src_dp = ParsePath(src_path);
   base::Optional<DecomposedPath> dest_dp = ParsePath(dest_path);
 
@@ -361,6 +379,7 @@
 bool ObfuscatedFileUtilMemoryDelegate::MoveDirectoryInternal(
     const DecomposedPath& src_dp,
     const DecomposedPath& dest_dp) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(src_dp.entry->type == Entry::kDirectory);
   if (!dest_dp.entry) {
     dest_dp.parent->directory_content.insert(
@@ -379,6 +398,7 @@
     const DecomposedPath& src_dp,
     const DecomposedPath& dest_dp,
     bool move) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(src_dp.entry->type == Entry::kFile);
   if (dest_dp.entry)
     dest_dp.parent->directory_content.erase(dest_dp.components.back());
@@ -404,6 +424,7 @@
 
 size_t ObfuscatedFileUtilMemoryDelegate::ComputeDirectorySize(
     const base::FilePath& path) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   base::Optional<DecomposedPath> dp = ParsePath(path);
   if (!dp || !dp->entry || dp->entry->type != Entry::kDirectory)
     return 0;
@@ -429,6 +450,7 @@
                                                int64_t offset,
                                                net::IOBuffer* buf,
                                                int buf_len) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   base::Optional<DecomposedPath> dp = ParsePath(path);
   if (!dp || dp->entry->type != Entry::kFile)
     return net::ERR_FILE_NOT_FOUND;
@@ -449,9 +471,10 @@
                                                 int64_t offset,
                                                 net::IOBuffer* buf,
                                                 int buf_len) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   base::Optional<DecomposedPath> dp = ParsePath(path);
 
-  if (!dp || dp->entry->type != Entry::kFile)
+  if (!dp || !dp->entry || dp->entry->type != Entry::kFile)
     return net::ERR_FILE_NOT_FOUND;
 
   size_t offset_u = static_cast<size_t>(offset);
@@ -479,6 +502,7 @@
 base::File::Error ObfuscatedFileUtilMemoryDelegate::CreateFileForTesting(
     const base::FilePath& path,
     base::span<const char> content) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   bool created;
   base::File::Error result = EnsureFileExists(path, &created);
   if (result != base::File::FILE_OK)
@@ -498,6 +522,7 @@
     const base::FilePath& dest_path,
     FileSystemOperation::CopyOrMoveOption /* option */,
     NativeFileUtil::CopyOrMoveMode /* mode */) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   base::Optional<DecomposedPath> dest_dp = ParsePath(dest_path);
 
   if (!dest_dp || !dest_dp->parent)
diff --git a/storage/browser/file_system/obfuscated_file_util_memory_delegate.h b/storage/browser/file_system/obfuscated_file_util_memory_delegate.h
index 4dd25b4..3d49a0b 100644
--- a/storage/browser/file_system/obfuscated_file_util_memory_delegate.h
+++ b/storage/browser/file_system/obfuscated_file_util_memory_delegate.h
@@ -126,6 +126,8 @@
                               const DecomposedPath& dest_dp,
                               bool move);
 
+  SEQUENCE_CHECKER(sequence_checker_);
+
   // The root of the directory tree.
   std::unique_ptr<Entry> root_;
 
diff --git a/storage/browser/file_system/sandbox_file_stream_writer.cc b/storage/browser/file_system/sandbox_file_stream_writer.cc
index afaedd2a..ea0c93a6 100644
--- a/storage/browser/file_system/sandbox_file_stream_writer.cc
+++ b/storage/browser/file_system/sandbox_file_stream_writer.cc
@@ -159,6 +159,7 @@
           file_system_context_->sandbox_delegate()->memory_file_util_delegate();
     }
     file_writer_ = FileStreamWriter::CreateForMemoryFile(
+        file_system_context_->default_file_task_runner(),
         memory_file_util_delegate, platform_path, initial_offset_);
 
   } else {
diff --git a/testing/buildbot/chromium.android.fyi.json b/testing/buildbot/chromium.android.fyi.json
index d6e3c819..402f24a 100644
--- a/testing/buildbot/chromium.android.fyi.json
+++ b/testing/buildbot/chromium.android.fyi.json
@@ -439,7 +439,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M83",
-              "revision": "version:83.0.4103.83"
+              "revision": "version:dup-83.0.4103.83"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -513,7 +513,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M83",
-              "revision": "version:83.0.4103.83"
+              "revision": "version:dup-83.0.4103.83"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -587,7 +587,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M84",
-              "revision": "version:84.0.4147.89"
+              "revision": "version:dup-84.0.4147.89"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -661,7 +661,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M84",
-              "revision": "version:84.0.4147.89"
+              "revision": "version:dup-84.0.4147.89"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -735,7 +735,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M85",
-              "revision": "version:85.0.4183.69"
+              "revision": "version:dup-85.0.4183.69"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -809,7 +809,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M85",
-              "revision": "version:85.0.4183.69"
+              "revision": "version:dup-85.0.4183.69"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
diff --git a/testing/buildbot/chromium.android.json b/testing/buildbot/chromium.android.json
index 8b07820..69151d8 100644
--- a/testing/buildbot/chromium.android.json
+++ b/testing/buildbot/chromium.android.json
@@ -9497,58 +9497,6 @@
       {
         "args": [
           "--gs-results-bucket=chromium-result-details",
-          "--recover-devices",
-          "--git-revision=${got_revision}"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "chrome_java_test_wpr_tests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "precommit_args": [
-          "--gerrit-issue=${patch_issue}",
-          "--gerrit-patchset=${patch_set}",
-          "--buildbucket-id=${buildbucket_build_id}"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_os_type": "userdebug",
-              "device_type": "bullhead",
-              "os": "Android"
-            }
-          ],
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ],
-          "service_account": "chrome-gpu-gold@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "chrome_java_test_wpr_tests",
-        "test_id_prefix": "ninja://chrome/test/android:chrome_java_test_wpr_tests/"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
           "--recover-devices"
         ],
         "merge": {
diff --git a/testing/buildbot/chromium.ci.json b/testing/buildbot/chromium.ci.json
index 30183c00..d93595f7 100644
--- a/testing/buildbot/chromium.ci.json
+++ b/testing/buildbot/chromium.ci.json
@@ -85054,58 +85054,6 @@
       {
         "args": [
           "--gs-results-bucket=chromium-result-details",
-          "--recover-devices",
-          "--git-revision=${got_revision}"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "chrome_java_test_wpr_tests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "precommit_args": [
-          "--gerrit-issue=${patch_issue}",
-          "--gerrit-patchset=${patch_set}",
-          "--buildbucket-id=${buildbucket_build_id}"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_os_type": "userdebug",
-              "device_type": "bullhead",
-              "os": "Android"
-            }
-          ],
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ],
-          "service_account": "chrome-gpu-gold@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "chrome_java_test_wpr_tests",
-        "test_id_prefix": "ninja://chrome/test/android:chrome_java_test_wpr_tests/"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
           "--recover-devices"
         ],
         "merge": {
@@ -199821,7 +199769,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M83",
-              "revision": "version:83.0.4103.83"
+              "revision": "version:dup-83.0.4103.83"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -199895,7 +199843,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M83",
-              "revision": "version:83.0.4103.83"
+              "revision": "version:dup-83.0.4103.83"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -199969,7 +199917,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M84",
-              "revision": "version:84.0.4147.89"
+              "revision": "version:dup-84.0.4147.89"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -200043,7 +199991,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M84",
-              "revision": "version:84.0.4147.89"
+              "revision": "version:dup-84.0.4147.89"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -200117,7 +200065,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M85",
-              "revision": "version:85.0.4183.69"
+              "revision": "version:dup-85.0.4183.69"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -200191,7 +200139,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M85",
-              "revision": "version:85.0.4183.69"
+              "revision": "version:dup-85.0.4183.69"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
diff --git a/testing/buildbot/test_suite_exceptions.pyl b/testing/buildbot/test_suite_exceptions.pyl
index 0ae31cee..d6f9c22 100644
--- a/testing/buildbot/test_suite_exceptions.pyl
+++ b/testing/buildbot/test_suite_exceptions.pyl
@@ -48,9 +48,6 @@
     # crbug/1060399: only enable wpr tests on marshmallow CI bot
     # due to the restriction that render test only works on marshamllow.
     'modifications': {
-      'Marshmallow 64 bit Tester': {
-        'experiment_percentage': 100, # https://crbug.com/1060399
-      },
       'android-marshmallow-x86-rel': {
         'experiment_percentage': 100, # https://crbug.com/1060399
       },
diff --git a/testing/buildbot/variants.pyl b/testing/buildbot/variants.pyl
index 5956eeb6..a9a67deb 100644
--- a/testing/buildbot/variants.pyl
+++ b/testing/buildbot/variants.pyl
@@ -280,7 +280,7 @@
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M85',
-          'revision': 'version:85.0.4183.69',
+          'revision': 'version:dup-85.0.4183.69',
         }
       ],
     },
@@ -303,7 +303,7 @@
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M84',
-          'revision': 'version:84.0.4147.89',
+          'revision': 'version:dup-84.0.4147.89',
         }
       ],
     },
@@ -326,7 +326,7 @@
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M83',
-          'revision': 'version:83.0.4103.83',
+          'revision': 'version:dup-83.0.4103.83',
         }
       ],
     },
@@ -349,7 +349,7 @@
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M85',
-          'revision': 'version:85.0.4183.69',
+          'revision': 'version:dup-85.0.4183.69',
         }
       ],
     },
@@ -372,7 +372,7 @@
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M84',
-          'revision': 'version:84.0.4147.89',
+          'revision': 'version:dup-84.0.4147.89',
         }
       ],
     },
@@ -395,7 +395,7 @@
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M83',
-          'revision': 'version:83.0.4103.83',
+          'revision': 'version:dup-83.0.4103.83',
         }
       ],
     },
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl
index aaa7434..9a4c6ae 100644
--- a/testing/buildbot/waterfalls.pyl
+++ b/testing/buildbot/waterfalls.pyl
@@ -450,7 +450,7 @@
           'bullhead',
         ],
         'test_suites': {
-          'gtest_tests': 'android_marshmallow_wpr_gtests',
+          'gtest_tests': 'android_lollipop_marshmallow_gtests',
         },
         'os_type': 'android',
       },
diff --git a/third_party/abseil-cpp/BUILD.gn b/third_party/abseil-cpp/BUILD.gn
index bce9cb2ae..9de7af41 100644
--- a/third_party/abseil-cpp/BUILD.gn
+++ b/third_party/abseil-cpp/BUILD.gn
@@ -66,6 +66,8 @@
     "//third_party/abseil-cpp/absl/container:flat_hash_map",
     "//third_party/abseil-cpp/absl/container:flat_hash_set",
     "//third_party/abseil-cpp/absl/container:inlined_vector",
+    "//third_party/abseil-cpp/absl/container:node_hash_map",
+    "//third_party/abseil-cpp/absl/container:node_hash_set",
     "//third_party/abseil-cpp/absl/debugging:failure_signal_handler",
     "//third_party/abseil-cpp/absl/debugging:stacktrace",
     "//third_party/abseil-cpp/absl/debugging:symbolize",
@@ -90,6 +92,8 @@
     public_deps -= [
       "//third_party/abseil-cpp/absl/container:flat_hash_map",
       "//third_party/abseil-cpp/absl/container:flat_hash_set",
+      "//third_party/abseil-cpp/absl/container:node_hash_map",
+      "//third_party/abseil-cpp/absl/container:node_hash_set",
       "//third_party/abseil-cpp/absl/debugging:failure_signal_handler",
     ]
   }
diff --git a/third_party/arcore-android-sdk-client/core-1.18.0.info b/third_party/arcore-android-sdk-client/core-1.18.0.info
index 7a92cb8..2590bcd5 100644
--- a/third_party/arcore-android-sdk-client/core-1.18.0.info
+++ b/third_party/arcore-android-sdk-client/core-1.18.0.info
@@ -8,6 +8,7 @@
 has_proguard_flags = true
 has_r_text_file = true
 is_manifest_empty = false
+manifest_package = "com.google.ar.core"
 native_libraries = [
   "jni/arm64-v8a/libarcore_sdk_c.so",
   "jni/arm64-v8a/libarcore_sdk_jni.so",
diff --git a/third_party/blink/public/mojom/clipboard/clipboard.mojom b/third_party/blink/public/mojom/clipboard/clipboard.mojom
index 8454a77..ccc2b7d 100644
--- a/third_party/blink/public/mojom/clipboard/clipboard.mojom
+++ b/third_party/blink/public/mojom/clipboard/clipboard.mojom
@@ -51,6 +51,9 @@
                                       uint32 fragment_start,
                                       uint32 fragment_end);
 
+  // Read SVG from the OS clipboard.
+  ReadSvg(ClipboardBuffer buffer) => (mojo_base.mojom.BigString16 result);
+
   [Sync]
   ReadRtf(ClipboardBuffer buffer) => (string result);
 
@@ -69,6 +72,10 @@
 
   WriteHtml(mojo_base.mojom.BigString16 markup, url.mojom.Url url);
 
+  // Takes sanitized SVG in a UTF16 string format and writes it to the
+  // OS clipboard.
+  WriteSvg(mojo_base.mojom.BigString16 markup);
+
   WriteSmartPasteMarker();
 
   // Chrome-specific pickled data.
diff --git a/third_party/blink/public/mojom/push_messaging/push_messaging_status.mojom b/third_party/blink/public/mojom/push_messaging/push_messaging_status.mojom
index d210c1d..550f78b 100644
--- a/third_party/blink/public/mojom/push_messaging/push_messaging_status.mojom
+++ b/third_party/blink/public/mojom/push_messaging/push_messaging_status.mojom
@@ -186,6 +186,10 @@
   // Only happens if features::kPushSubscriptionWithExpirationTime is enabled.
   SUBSCRIPTION_EXPIRED = 11,
 
+  // The subscription was successfully refreshed, which is why we want to remove
+  // the old subscription
+  REFRESH_FINISHED = 12,
+
   // NOTE: Do not renumber or delete these as that would confuse interpretation
   // of previously logged data. When making changes, also update the enum list
   // in tools/metrics/histograms/histograms.xml to keep it in sync.
diff --git a/third_party/blink/renderer/bindings/generated_in_modules.gni b/third_party/blink/renderer/bindings/generated_in_modules.gni
index 51f4c2c..59ffcfa 100644
--- a/third_party/blink/renderer/bindings/generated_in_modules.gni
+++ b/third_party/blink/renderer/bindings/generated_in_modules.gni
@@ -163,8 +163,14 @@
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_double_range.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_dynamics_compressor_options.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_dynamics_compressor_options.h",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_encoded_audio_chunk_init.cc",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_encoded_audio_chunk_init.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_encoded_audio_config.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_encoded_audio_config.h",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_encoded_video_chunk_init.cc",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_encoded_video_chunk_init.h",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_encoded_video_config.cc",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_encoded_video_config.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_event_source_init.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_event_source_init.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_extendable_cookie_change_event_init.cc",
diff --git a/third_party/blink/renderer/bindings/scripts/web_idl/validator.py b/third_party/blink/renderer/bindings/scripts/web_idl/validator.py
index cb02909..045797e 100644
--- a/third_party/blink/renderer/bindings/scripts/web_idl/validator.py
+++ b/third_party/blink/renderer/bindings/scripts/web_idl/validator.py
@@ -2,10 +2,13 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import itertools
+
 from .ir_map import IRMap
 
 
 def validate_after_resolve_references(ir_map):
+    _validate_forbidden_nullable_dictionary_type(ir_map)
     _validate_literal_constant_type(ir_map)
 
 
@@ -14,8 +17,9 @@
     irs = ir_map.irs_of_kinds(IRMap.IR.Kind.CALLBACK_INTERFACE,
                               IRMap.IR.Kind.INTERFACE, IRMap.IR.Kind.NAMESPACE)
     for ir in irs:
-        accumulated.extend(ir.constants)
-    return accumulated
+        if ir.constants:
+            accumulated.append(ir.constants)
+    return itertools.chain(*accumulated)
 
 
 def _all_function_likes(ir_map):
@@ -23,11 +27,34 @@
     irs = ir_map.irs_of_kinds(IRMap.IR.Kind.CALLBACK_INTERFACE,
                               IRMap.IR.Kind.INTERFACE, IRMap.IR.Kind.NAMESPACE)
     for ir in irs:
-        accumulated.extend(ir.constructors)
-        accumulated.extend(ir.named_constructors)
-        accumulated.extend(ir.operations)
-    accumulated.extend(ir_map.irs_of_kinds(IRMap.IR.Kind.CALLBACK_FUNCTION))
-    return accumulated
+        if ir.constructors:
+            accumulated.append(ir.constructors)
+        if ir.named_constructors:
+            accumulated.append(ir.named_constructors)
+        if ir.operations:
+            accumulated.append(ir.operations)
+    accumulated.append(ir_map.irs_of_kinds(IRMap.IR.Kind.CALLBACK_FUNCTION))
+    return itertools.chain(*accumulated)
+
+
+def _validate_forbidden_nullable_dictionary_type(ir_map):
+    for function in _all_function_likes(ir_map):
+        for argument in function.arguments:
+            assert (not (argument.idl_type.is_nullable
+                         and argument.idl_type.unwrap().is_dictionary)
+                    ), ("{}: {}, {}: Nullable dictionary type is forbidden as "
+                        "an argument type.".format(
+                            function.debug_info.location, function.identifier,
+                            argument.identifier))
+
+    for dictionary in ir_map.irs_of_kind(IRMap.IR.Kind.DICTIONARY):
+        for dict_member in dictionary.own_members:
+            assert (not (dict_member.idl_type.is_nullable
+                         and dict_member.idl_type.unwrap().is_dictionary)
+                    ), ("{}: {}, {}: Nullable dictionary type is forbidden as "
+                        "a dictionary member type.".format(
+                            dict_member.debug_info.location,
+                            dictionary.identifier, dict_member.identifier))
 
 
 def _validate_literal_constant_type(ir_map):
diff --git a/third_party/blink/renderer/core/css/font_face_set_document.cc b/third_party/blink/renderer/core/css/font_face_set_document.cc
index c1a4d6ba..2b865e6 100644
--- a/third_party/blink/renderer/core/css/font_face_set_document.cc
+++ b/third_party/blink/renderer/core/css/font_face_set_document.cc
@@ -136,7 +136,7 @@
 const HeapLinkedHashSet<Member<FontFace>>&
 FontFaceSetDocument::CSSConnectedFontFaceList() const {
   Document* document = this->GetDocument();
-  document->UpdateActiveStyle();
+  document->GetStyleEngine().UpdateActiveStyle();
   return GetFontSelector()->GetFontFaceCache()->CssConnectedFontFaces();
 }
 
diff --git a/third_party/blink/renderer/core/css/resolver/style_resolver_test.cc b/third_party/blink/renderer/core/css/resolver/style_resolver_test.cc
index a0a2735..8c9996ba 100644
--- a/third_party/blink/renderer/core/css/resolver/style_resolver_test.cc
+++ b/third_party/blink/renderer/core/css/resolver/style_resolver_test.cc
@@ -566,7 +566,7 @@
     </style>
   )HTML");
 
-  GetDocument().UpdateActiveStyle();
+  GetDocument().GetStyleEngine().UpdateActiveStyle();
   scoped_refptr<const ComputedStyle> page_style =
       GetDocument().GetStyleResolver().StyleForPage(0, "");
   ASSERT_TRUE(page_style);
diff --git a/third_party/blink/renderer/core/css/style_engine.cc b/third_party/blink/renderer/core/css/style_engine.cc
index ed104b5e..96a2d22 100644
--- a/third_party/blink/renderer/core/css/style_engine.cc
+++ b/third_party/blink/renderer/core/css/style_engine.cc
@@ -597,6 +597,8 @@
 
 void StyleEngine::UpdateActiveStyle() {
   DCHECK(GetDocument().IsActive());
+  DCHECK(IsMainThread());
+  TRACE_EVENT0("blink", "Document::updateActiveStyle");
   UpdateViewport();
   UpdateActiveStyleSheets();
   UpdateGlobalRuleSet();
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc
index d6056a1..f7e4c6b 100644
--- a/third_party/blink/renderer/core/dom/document.cc
+++ b/third_party/blink/renderer/core/dom/document.cc
@@ -2451,7 +2451,7 @@
 
   UpdateDistributionForLegacyDistributedNodes();
 
-  UpdateActiveStyle();
+  GetStyleEngine().UpdateActiveStyle();
   InvalidateStyleAndLayoutForFontUpdates();
   UpdateStyleInvalidationIfNeeded();
   UpdateStyle();
@@ -2475,13 +2475,6 @@
 #endif
 }
 
-void Document::UpdateActiveStyle() {
-  DCHECK(IsActive());
-  DCHECK(IsMainThread());
-  TRACE_EVENT0("blink", "Document::updateActiveStyle");
-  GetStyleEngine().UpdateActiveStyle();
-}
-
 void Document::InvalidateStyleAndLayoutForFontUpdates() {
   DCHECK(IsActive());
   DCHECK(IsMainThread());
@@ -2879,7 +2872,7 @@
       page_name = mapper->NamedPageAtIndex(page_index);
   }
 
-  UpdateActiveStyle();
+  GetStyleEngine().UpdateActiveStyle();
   return GetStyleEngine().GetStyleResolver().StyleForPage(page_index,
                                                           page_name);
 }
diff --git a/third_party/blink/renderer/core/dom/document.h b/third_party/blink/renderer/core/dom/document.h
index fe69959..317cfa8 100644
--- a/third_party/blink/renderer/core/dom/document.h
+++ b/third_party/blink/renderer/core/dom/document.h
@@ -1386,7 +1386,6 @@
   void SetResizedForViewportUnits();
   void ClearResizedForViewportUnits();
 
-  void UpdateActiveStyle();
   void InvalidateStyleAndLayoutForFontUpdates();
 
   void Trace(Visitor*) const override;
diff --git a/third_party/blink/renderer/core/dom/node.cc b/third_party/blink/renderer/core/dom/node.cc
index e04ba069..a94edef 100644
--- a/third_party/blink/renderer/core/dom/node.cc
+++ b/third_party/blink/renderer/core/dom/node.cc
@@ -549,10 +549,9 @@
   // TODO: This should use updateStyleAndLayoutForNode.
   GetDocument().UpdateStyleAndLayout(DocumentUpdateReason::kScroll);
 
-  LayoutBox* box_to_scroll = ToLayoutBox(GetLayoutObject());
-
   ScrollableArea* scrollable_area =
-      box_to_scroll->EnclosingBox()->GetScrollableArea();
+      ScrollableArea::GetForScrolling(ToLayoutBox(GetLayoutObject()));
+  LayoutBox* box_to_scroll = scrollable_area->GetLayoutBox();
 
   // TODO(bokan): This is a hack to fix https://crbug.com/977954. If we have a
   // non-default root scroller, scrolling from one of its siblings or a fixed
diff --git a/third_party/blink/renderer/core/editing/BUILD.gn b/third_party/blink/renderer/core/editing/BUILD.gn
index 5801d3b5..3c462e55 100644
--- a/third_party/blink/renderer/core/editing/BUILD.gn
+++ b/third_party/blink/renderer/core/editing/BUILD.gn
@@ -6,6 +6,8 @@
 
 blink_core_sources("editing") {
   sources = [
+    "bidi_adjustment.cc",
+    "bidi_adjustment.h",
     "caret_display_item_client.cc",
     "caret_display_item_client.h",
     "commands/append_node_command.cc",
@@ -159,8 +161,6 @@
     "ime/text_update_event.h",
     "inline_box_position.cc",
     "inline_box_position.h",
-    "inline_box_traversal.cc",
-    "inline_box_traversal.h",
     "iterators/backwards_character_iterator.cc",
     "iterators/backwards_character_iterator.h",
     "iterators/bit_stack.cc",
diff --git a/third_party/blink/renderer/core/editing/inline_box_traversal.cc b/third_party/blink/renderer/core/editing/bidi_adjustment.cc
similarity index 99%
rename from third_party/blink/renderer/core/editing/inline_box_traversal.cc
rename to third_party/blink/renderer/core/editing/bidi_adjustment.cc
index b3748d25..dfd15148 100644
--- a/third_party/blink/renderer/core/editing/inline_box_traversal.cc
+++ b/third_party/blink/renderer/core/editing/bidi_adjustment.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 "third_party/blink/renderer/core/editing/inline_box_traversal.h"
+#include "third_party/blink/renderer/core/editing/bidi_adjustment.h"
 
 #include <unicode/ubidi.h>
 
@@ -16,8 +16,6 @@
 #include "third_party/blink/renderer/core/layout/ng/inline/ng_caret_position.h"
 #include "third_party/blink/renderer/platform/text/text_direction.h"
 
-// TODO(xiaochengh): Rename this file to |bidi_adjustment.cc|
-
 namespace blink {
 
 namespace {
diff --git a/third_party/blink/renderer/core/editing/inline_box_traversal.h b/third_party/blink/renderer/core/editing/bidi_adjustment.h
similarity index 84%
rename from third_party/blink/renderer/core/editing/inline_box_traversal.h
rename to third_party/blink/renderer/core/editing/bidi_adjustment.h
index c3c3ea2..41a9a64 100644
--- a/third_party/blink/renderer/core/editing/inline_box_traversal.h
+++ b/third_party/blink/renderer/core/editing/bidi_adjustment.h
@@ -2,10 +2,8 @@
 // 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_CORE_EDITING_INLINE_BOX_TRAVERSAL_H_
-#define THIRD_PARTY_BLINK_RENDERER_CORE_EDITING_INLINE_BOX_TRAVERSAL_H_
-
-// TODO(xiaochengh): Rename this file to |bidi_adjustment.h|
+#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_EDITING_BIDI_ADJUSTMENT_H_
+#define THIRD_PARTY_BLINK_RENDERER_CORE_EDITING_BIDI_ADJUSTMENT_H_
 
 #include "third_party/blink/renderer/core/editing/forward.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
@@ -48,4 +46,4 @@
 
 }  // namespace blink
 
-#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_EDITING_INLINE_BOX_TRAVERSAL_H_
+#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_EDITING_BIDI_ADJUSTMENT_H_
diff --git a/third_party/blink/renderer/core/editing/inline_box_position.cc b/third_party/blink/renderer/core/editing/inline_box_position.cc
index 4380bf7..db35d4c 100644
--- a/third_party/blink/renderer/core/editing/inline_box_position.cc
+++ b/third_party/blink/renderer/core/editing/inline_box_position.cc
@@ -30,8 +30,8 @@
 
 #include "third_party/blink/renderer/core/editing/inline_box_position.h"
 
+#include "third_party/blink/renderer/core/editing/bidi_adjustment.h"
 #include "third_party/blink/renderer/core/editing/editing_utilities.h"
-#include "third_party/blink/renderer/core/editing/inline_box_traversal.h"
 #include "third_party/blink/renderer/core/editing/position.h"
 #include "third_party/blink/renderer/core/editing/visible_position.h"
 #include "third_party/blink/renderer/core/editing/visible_units.h"
diff --git a/third_party/blink/renderer/core/editing/selection_controller.cc b/third_party/blink/renderer/core/editing/selection_controller.cc
index 916293fc..edd2430 100644
--- a/third_party/blink/renderer/core/editing/selection_controller.cc
+++ b/third_party/blink/renderer/core/editing/selection_controller.cc
@@ -34,13 +34,13 @@
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/dom/events/event.h"
 #include "third_party/blink/renderer/core/dom/node_computed_style.h"
+#include "third_party/blink/renderer/core/editing/bidi_adjustment.h"
 #include "third_party/blink/renderer/core/editing/editing_behavior.h"
 #include "third_party/blink/renderer/core/editing/editing_boundary.h"
 #include "third_party/blink/renderer/core/editing/editing_utilities.h"
 #include "third_party/blink/renderer/core/editing/editor.h"
 #include "third_party/blink/renderer/core/editing/ephemeral_range.h"
 #include "third_party/blink/renderer/core/editing/frame_selection.h"
-#include "third_party/blink/renderer/core/editing/inline_box_traversal.h"
 #include "third_party/blink/renderer/core/editing/iterators/text_iterator.h"
 #include "third_party/blink/renderer/core/editing/markers/document_marker_controller.h"
 #include "third_party/blink/renderer/core/editing/selection_template.h"
diff --git a/third_party/blink/renderer/core/editing/selection_modifier.cc b/third_party/blink/renderer/core/editing/selection_modifier.cc
index e06d5224..db057c71 100644
--- a/third_party/blink/renderer/core/editing/selection_modifier.cc
+++ b/third_party/blink/renderer/core/editing/selection_modifier.cc
@@ -26,12 +26,12 @@
 
 #include "third_party/blink/renderer/core/editing/selection_modifier.h"
 
+#include "third_party/blink/renderer/core/editing/bidi_adjustment.h"
 #include "third_party/blink/renderer/core/editing/editing_behavior.h"
 #include "third_party/blink/renderer/core/editing/editing_utilities.h"
 #include "third_party/blink/renderer/core/editing/editor.h"
 #include "third_party/blink/renderer/core/editing/ephemeral_range.h"
 #include "third_party/blink/renderer/core/editing/inline_box_position.h"
-#include "third_party/blink/renderer/core/editing/inline_box_traversal.h"
 #include "third_party/blink/renderer/core/editing/local_caret_rect.h"
 #include "third_party/blink/renderer/core/editing/selection_template.h"
 #include "third_party/blink/renderer/core/editing/visible_position.h"
diff --git a/third_party/blink/renderer/core/exported/web_view_impl.cc b/third_party/blink/renderer/core/exported/web_view_impl.cc
index 51969bc6..73bc22f 100644
--- a/third_party/blink/renderer/core/exported/web_view_impl.cc
+++ b/third_party/blink/renderer/core/exported/web_view_impl.cc
@@ -2489,6 +2489,9 @@
        mojom::blink::PagehideDispatch::kNotDispatched) &&
       GetPage()->DispatchedPagehideAndStillHidden();
 
+  if (dispatching_pagehide) {
+    RemoveFocusAndTextInputState();
+  }
   if (hiding_page) {
     SetVisibilityState(new_state->visibility, /*is_initial_state=*/false);
   }
@@ -2553,6 +2556,24 @@
   GetPage()->GetPageScheduler()->AudioStateChanged(is_audio_playing);
 }
 
+void WebViewImpl::RemoveFocusAndTextInputState() {
+  auto& focus_controller = GetPage()->GetFocusController();
+  auto* focused_frame = focus_controller.FocusedFrame();
+  if (!focused_frame)
+    return;
+  // Remove focus from the currently focused element and frame.
+  focus_controller.SetFocusedElement(nullptr, nullptr);
+  // Clear composing state, and make sure we send a TextInputState update.
+  // Note that the TextInputState itself is cleared when we clear the focus,
+  // but no updates to the browser will be triggered until the next animation
+  // frame, which won't happen if we're freezing the page.
+  if (auto* widget = static_cast<WebFrameWidgetBase*>(
+          focused_frame->GetWidgetForLocalRoot())) {
+    widget->FinishComposingText(false /* keep_selection */);
+    widget->UpdateTextInputState();
+  }
+}
+
 void WebViewImpl::DispatchPagehide(
     mojom::blink::PagehideDispatch pagehide_dispatch) {
   DCHECK_NE(pagehide_dispatch, mojom::blink::PagehideDispatch::kNotDispatched);
diff --git a/third_party/blink/renderer/core/exported/web_view_impl.h b/third_party/blink/renderer/core/exported/web_view_impl.h
index 03f200d..3392e436 100644
--- a/third_party/blink/renderer/core/exported/web_view_impl.h
+++ b/third_party/blink/renderer/core/exported/web_view_impl.h
@@ -571,6 +571,11 @@
   LocalFrame* FocusedLocalFrameInWidget() const;
   LocalFrame* FocusedLocalFrameAvailableForIme() const;
 
+  // Clear focus and text input state of the page. If there was a focused
+  // element, this will trigger updates to observers and send focus, selection,
+  // and text input-related events.
+  void RemoveFocusAndTextInputState();
+
   bool ScrollFocusedEditableElementIntoView();
   // Finds the zoom and scroll parameters for zooming into an editable element
   // with bounds |element_bounds_in_document| and caret bounds
diff --git a/third_party/blink/renderer/core/html/forms/date_time_edit_element.cc b/third_party/blink/renderer/core/html/forms/date_time_edit_element.cc
index 841b8313..a6ff1e3 100644
--- a/third_party/blink/renderer/core/html/forms/date_time_edit_element.cc
+++ b/third_party/blink/renderer/core/html/forms/date_time_edit_element.cc
@@ -701,6 +701,24 @@
   return false;
 }
 
+void DateTimeEditElement::HandleAmPmRollover(
+    DateTimeFieldElement::FieldRolloverType rollover_type) {
+  auto* ampm_field = GetField(DateTimeField::kAMPM);
+  if (!ampm_field)
+    return;
+  DateTimeFieldsState date_time_fields_state = ValueAsDateTimeFieldsState();
+  ampm_field->PopulateDateTimeFieldsState(date_time_fields_state);
+  bool was_am =
+      (date_time_fields_state.Ampm() == DateTimeFieldsState::kAMPMValueAM);
+  date_time_fields_state.SetAMPM(DateTimeFieldsState::kAMPMValuePM);
+  if (rollover_type != DateTimeFieldElement::FieldRolloverType::kToPm &&
+      !was_am)
+    date_time_fields_state.SetAMPM(DateTimeFieldsState::kAMPMValueAM);
+  ampm_field->SetValueAsDateTimeFieldsState(date_time_fields_state);
+  // No need to call EditControlValueChanged since change that triggered
+  // rollover will do so.
+}
+
 bool DateTimeEditElement::IsDateTimeEditElement() const {
   return true;
 }
@@ -829,6 +847,15 @@
     field->SetEmptyValue(DateTimeFieldElement::kDispatchNoEvent);
 }
 
+DateTimeFieldElement* DateTimeEditElement::GetField(DateTimeField type) const {
+  auto* it = std::find_if(
+      fields_.begin(), fields_.end(),
+      [&type](const auto& field) { return field->Type() == type; });
+  if (it == fields_.end())
+    return nullptr;
+  return *it;
+}
+
 bool DateTimeEditElement::HasField(DateTimeField type) const {
   for (const auto& field : fields_) {
     if (field->Type() == type)
diff --git a/third_party/blink/renderer/core/html/forms/date_time_edit_element.h b/third_party/blink/renderer/core/html/forms/date_time_edit_element.h
index 87b6543f..c2e7ab3 100644
--- a/third_party/blink/renderer/core/html/forms/date_time_edit_element.h
+++ b/third_party/blink/renderer/core/html/forms/date_time_edit_element.h
@@ -128,6 +128,7 @@
   //  8. AM/PM
   static const int kMaximumNumberOfFields = 8;
 
+  DateTimeFieldElement* GetField(DateTimeField) const;
   DateTimeFieldElement* FieldAt(wtf_size_t) const;
   wtf_size_t FieldIndexOf(const DateTimeFieldElement&) const;
   DateTimeFieldElement* FocusedField() const;
@@ -147,6 +148,7 @@
   void FieldValueChanged() override;
   bool FocusOnNextField(const DateTimeFieldElement&) override;
   bool FocusOnPreviousField(const DateTimeFieldElement&) override;
+  void HandleAmPmRollover(DateTimeFieldElement::FieldRolloverType) override;
   bool IsFieldOwnerDisabled() const override;
   bool IsFieldOwnerReadOnly() const override;
   AtomicString LocaleIdentifier() const override;
diff --git a/third_party/blink/renderer/core/html/forms/date_time_field_element.cc b/third_party/blink/renderer/core/html/forms/date_time_field_element.cc
index bad44bb..bd662d1 100644
--- a/third_party/blink/renderer/core/html/forms/date_time_field_element.cc
+++ b/third_party/blink/renderer/core/html/forms/date_time_field_element.cc
@@ -161,6 +161,12 @@
   field_owner_->FocusOnNextField(*this);
 }
 
+void DateTimeFieldElement::HandleAmPmRollover(FieldRolloverType type) {
+  if (!field_owner_)
+    return;
+  field_owner_->HandleAmPmRollover(type);
+}
+
 void DateTimeFieldElement::Initialize(const AtomicString& pseudo,
                                       const String& ax_help_text,
                                       int ax_minimum,
diff --git a/third_party/blink/renderer/core/html/forms/date_time_field_element.h b/third_party/blink/renderer/core/html/forms/date_time_field_element.h
index ba6b356..041bc792 100644
--- a/third_party/blink/renderer/core/html/forms/date_time_field_element.h
+++ b/third_party/blink/renderer/core/html/forms/date_time_field_element.h
@@ -55,6 +55,7 @@
     kDispatchNoEvent,
     kDispatchEvent,
   };
+  enum FieldRolloverType { kPastMin, kPastMax, kToPm };
 
   // FieldOwner implementer must call removeEventHandler when
   // it doesn't handle event, e.g. at destruction.
@@ -66,6 +67,7 @@
     virtual void FieldValueChanged() = 0;
     virtual bool FocusOnNextField(const DateTimeFieldElement&) = 0;
     virtual bool FocusOnPreviousField(const DateTimeFieldElement&) = 0;
+    virtual void HandleAmPmRollover(FieldRolloverType) {}
     virtual bool IsFieldOwnerDisabled() const = 0;
     virtual bool IsFieldOwnerReadOnly() const = 0;
     virtual AtomicString LocaleIdentifier() const = 0;
@@ -96,6 +98,7 @@
  protected:
   DateTimeFieldElement(Document&, FieldOwner&, DateTimeField);
   void FocusOnNextField();
+  void HandleAmPmRollover(FieldRolloverType);
   virtual void HandleKeyboardEvent(KeyboardEvent&) = 0;
   void Initialize(const AtomicString& pseudo,
                   const String& ax_help_text,
diff --git a/third_party/blink/renderer/core/html/forms/date_time_field_elements.cc b/third_party/blink/renderer/core/html/forms/date_time_field_elements.cc
index 42222138..6f49276 100644
--- a/third_party/blink/renderer/core/html/forms/date_time_field_elements.cc
+++ b/third_party/blink/renderer/core/html/forms/date_time_field_elements.cc
@@ -26,6 +26,7 @@
 #include "third_party/blink/renderer/core/html/forms/date_time_field_elements.h"
 
 #include "third_party/blink/public/strings/grit/blink_strings.h"
+#include "third_party/blink/renderer/core/html/forms/date_time_field_element.h"
 #include "third_party/blink/renderer/core/html/forms/date_time_fields_state.h"
 #include "third_party/blink/renderer/platform/text/date_components.h"
 #include "third_party/blink/renderer/platform/text/platform_locale.h"
@@ -212,6 +213,8 @@
 void DateTimeHour11FieldElement::SetValueAsInteger(
     int value,
     EventBehavior event_behavior) {
+  if (value > 12)
+    DateTimeNumericFieldElement::HandleAmPmRollover(FieldRolloverType::kToPm);
   value = Range(0, 23).ClampValue(value) % 12;
   DateTimeNumericFieldElement::SetValueAsInteger(value, event_behavior);
 }
@@ -267,10 +270,32 @@
 void DateTimeHour12FieldElement::SetValueAsInteger(
     int value,
     EventBehavior event_behavior) {
+  if (value > 12)
+    DateTimeNumericFieldElement::HandleAmPmRollover(FieldRolloverType::kToPm);
   value = Range(0, 24).ClampValue(value) % 12;
   DateTimeNumericFieldElement::SetValueAsInteger(value ? value : 12,
                                                  event_behavior);
 }
+void DateTimeHour12FieldElement::NotifyOwnerIfStepDownRollOver(bool has_value,
+                                                               Step step,
+                                                               int old_value,
+                                                               int new_value) {
+  if (!has_value || old_value == new_value || step.step > 12 ||
+      old_value - step.step != new_value)
+    return;
+  if (old_value > 11 && new_value <= 11)
+    HandleAmPmRollover(DateTimeFieldElement::FieldRolloverType::kPastMin);
+}
+void DateTimeHour12FieldElement::NotifyOwnerIfStepUpRollOver(bool has_value,
+                                                             Step step,
+                                                             int old_value,
+                                                             int new_value) {
+  if (!has_value || old_value == new_value || step.step > 12 ||
+      old_value + step.step != new_value)
+    return;
+  if (new_value == 12)
+    HandleAmPmRollover(DateTimeFieldElement::FieldRolloverType::kPastMax);
+}
 
 // ----------------------------
 
diff --git a/third_party/blink/renderer/core/html/forms/date_time_field_elements.h b/third_party/blink/renderer/core/html/forms/date_time_field_elements.h
index f5ad133..1d46fc2e 100644
--- a/third_party/blink/renderer/core/html/forms/date_time_field_elements.h
+++ b/third_party/blink/renderer/core/html/forms/date_time_field_elements.h
@@ -105,6 +105,14 @@
   // DateTimeFieldElement functions.
   void PopulateDateTimeFieldsState(DateTimeFieldsState&) override;
   void SetValueAsInteger(int, EventBehavior = kDispatchNoEvent) override;
+  void NotifyOwnerIfStepDownRollOver(bool has_value,
+                                     Step step,
+                                     int old_value,
+                                     int new_value) override;
+  void NotifyOwnerIfStepUpRollOver(bool has_value,
+                                   Step step,
+                                   int old_value,
+                                   int new_value) override;
 
   DISALLOW_COPY_AND_ASSIGN(DateTimeHour12FieldElement);
 };
diff --git a/third_party/blink/renderer/core/html/forms/date_time_numeric_field_element.cc b/third_party/blink/renderer/core/html/forms/date_time_numeric_field_element.cc
index 68cc7e4..5fb6190 100644
--- a/third_party/blink/renderer/core/html/forms/date_time_numeric_field_element.cc
+++ b/third_party/blink/renderer/core/html/forms/date_time_numeric_field_element.cc
@@ -194,6 +194,7 @@
       RoundDown(has_value_ ? value_ - 1 : DefaultValueForStepDown());
   if (!range_.IsInRange(new_value))
     new_value = RoundDown(range_.maximum);
+  NotifyOwnerIfStepDownRollOver(has_value_, step_, value_, new_value);
   type_ahead_buffer_.Clear();
   SetValueAsInteger(new_value, kDispatchEvent);
 }
@@ -202,6 +203,7 @@
   int new_value = RoundUp(has_value_ ? value_ + 1 : DefaultValueForStepUp());
   if (!range_.IsInRange(new_value))
     new_value = RoundUp(range_.minimum);
+  NotifyOwnerIfStepUpRollOver(has_value_, step_, value_, new_value);
   type_ahead_buffer_.Clear();
   SetValueAsInteger(new_value, kDispatchEvent);
 }
diff --git a/third_party/blink/renderer/core/html/forms/date_time_numeric_field_element.h b/third_party/blink/renderer/core/html/forms/date_time_numeric_field_element.h
index 02c576c..3216015a 100644
--- a/third_party/blink/renderer/core/html/forms/date_time_numeric_field_element.h
+++ b/third_party/blink/renderer/core/html/forms/date_time_numeric_field_element.h
@@ -73,6 +73,14 @@
   int ClampValue(int value) const { return range_.ClampValue(value); }
   virtual int DefaultValueForStepDown() const;
   virtual int DefaultValueForStepUp() const;
+  virtual void NotifyOwnerIfStepDownRollOver(bool has_value,
+                                             Step step,
+                                             int old_value,
+                                             int new_value) {}
+  virtual void NotifyOwnerIfStepUpRollOver(bool has_value,
+                                           Step step,
+                                           int old_value,
+                                           int new_value) {}
   const Range& GetRange() const { return range_; }
 
   // DateTimeFieldElement functions.
diff --git a/third_party/blink/renderer/core/html/forms/html_field_set_element.cc b/third_party/blink/renderer/core/html/forms/html_field_set_element.cc
index e06b9e8..6b9e3d8 100644
--- a/third_party/blink/renderer/core/html/forms/html_field_set_element.cc
+++ b/third_party/blink/renderer/core/html/forms/html_field_set_element.cc
@@ -125,6 +125,16 @@
   return LayoutObjectFactory::CreateFieldset(*this, style, legacy);
 }
 
+LayoutBox* HTMLFieldSetElement::GetLayoutBoxForScrolling() const {
+  auto* layout_box = GetLayoutBox();
+  if (!layout_box || !layout_box->IsLayoutNGFieldset())
+    return HTMLFormControlElement::GetLayoutBoxForScrolling();
+  LayoutObject* child = layout_box->SlowFirstChild();
+  if (child && child->IsAnonymous())
+    return ToLayoutBox(child);
+  return HTMLFormControlElement::GetLayoutBoxForScrolling();
+}
+
 bool HTMLFieldSetElement::TypeShouldForceLegacyLayout() const {
   return !RuntimeEnabledFeatures::LayoutNGFieldsetEnabled();
 }
diff --git a/third_party/blink/renderer/core/html/forms/html_field_set_element.h b/third_party/blink/renderer/core/html/forms/html_field_set_element.h
index eb7b5a5..b0538ed 100644
--- a/third_party/blink/renderer/core/html/forms/html_field_set_element.h
+++ b/third_party/blink/renderer/core/html/forms/html_field_set_element.h
@@ -47,6 +47,7 @@
   bool IsEnumeratable() const override { return true; }
   bool SupportsFocus() const override;
   LayoutObject* CreateLayoutObject(const ComputedStyle&, LegacyLayout) override;
+  LayoutBox* GetLayoutBoxForScrolling() const override;
   bool TypeShouldForceLegacyLayout() const final;
   const AtomicString& FormControlType() const override;
   bool RecalcWillValidate() const override { return false; }
diff --git a/third_party/blink/renderer/core/input/context_menu_allowed_scope.h b/third_party/blink/renderer/core/input/context_menu_allowed_scope.h
index 218f7f0..056741c 100644
--- a/third_party/blink/renderer/core/input/context_menu_allowed_scope.h
+++ b/third_party/blink/renderer/core/input/context_menu_allowed_scope.h
@@ -5,7 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_INPUT_CONTEXT_MENU_ALLOWED_SCOPE_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_INPUT_CONTEXT_MENU_ALLOWED_SCOPE_H_
 
-#include "base/macros.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
 
@@ -13,10 +12,11 @@
 
 class CORE_EXPORT ContextMenuAllowedScope {
   STACK_ALLOCATED();
-  DISALLOW_COPY_AND_ASSIGN(ContextMenuAllowedScope);
 
  public:
   ContextMenuAllowedScope();
+  ContextMenuAllowedScope(const ContextMenuAllowedScope&) = delete;
+  ContextMenuAllowedScope& operator=(const ContextMenuAllowedScope&) = delete;
   ~ContextMenuAllowedScope();
 
   static bool IsContextMenuAllowed();
diff --git a/third_party/blink/renderer/core/input/event_handler.h b/third_party/blink/renderer/core/input/event_handler.h
index 64315cb..f40c46d 100644
--- a/third_party/blink/renderer/core/input/event_handler.h
+++ b/third_party/blink/renderer/core/input/event_handler.h
@@ -26,7 +26,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_INPUT_EVENT_HANDLER_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_INPUT_EVENT_HANDLER_H_
 
-#include "base/macros.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/optional.h"
 #include "third_party/blink/public/common/input/web_input_event.h"
@@ -81,6 +80,8 @@
 class CORE_EXPORT EventHandler final : public GarbageCollected<EventHandler> {
  public:
   explicit EventHandler(LocalFrame&);
+  EventHandler(const EventHandler&) = delete;
+  EventHandler& operator=(const EventHandler&) = delete;
   void Trace(Visitor*) const;
 
   void Clear();
@@ -459,8 +460,6 @@
   FRIEND_TEST_ALL_PREFIXES(EventHandlerTest,
                            CursorForInlineVerticalWritingMode);
   FRIEND_TEST_ALL_PREFIXES(EventHandlerTest, CursorForBlockVerticalWritingMode);
-
-  DISALLOW_COPY_AND_ASSIGN(EventHandler);
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/input/gesture_manager.h b/third_party/blink/renderer/core/input/gesture_manager.h
index f0d783f..b93ecf96 100644
--- a/third_party/blink/renderer/core/input/gesture_manager.h
+++ b/third_party/blink/renderer/core/input/gesture_manager.h
@@ -5,7 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_INPUT_GESTURE_MANAGER_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_INPUT_GESTURE_MANAGER_H_
 
-#include "base/macros.h"
 #include "third_party/blink/public/platform/web_input_event_result.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/layout/hit_test_request.h"
@@ -30,6 +29,8 @@
                  MouseEventManager&,
                  PointerEventManager&,
                  SelectionController&);
+  GestureManager(const GestureManager&) = delete;
+  GestureManager& operator=(const GestureManager&) = delete;
   void Trace(Visitor*) const;
 
   void Clear();
@@ -78,8 +79,6 @@
   bool long_tap_should_invoke_context_menu_;
 
   const Member<SelectionController> selection_controller_;
-
-  DISALLOW_COPY_AND_ASSIGN(GestureManager);
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/input/keyboard_event_manager.h b/third_party/blink/renderer/core/input/keyboard_event_manager.h
index 8a7a434..5756c46 100644
--- a/third_party/blink/renderer/core/input/keyboard_event_manager.h
+++ b/third_party/blink/renderer/core/input/keyboard_event_manager.h
@@ -5,7 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_INPUT_KEYBOARD_EVENT_MANAGER_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_INPUT_KEYBOARD_EVENT_MANAGER_H_
 
-#include "base/macros.h"
 #include "build/build_config.h"
 #include "third_party/blink/public/common/input/web_input_event.h"
 #include "third_party/blink/public/platform/web_input_event_result.h"
@@ -35,6 +34,8 @@
 #endif
 
   KeyboardEventManager(LocalFrame&, ScrollManager&);
+  KeyboardEventManager(const KeyboardEventManager&) = delete;
+  KeyboardEventManager& operator=(const KeyboardEventManager&) = delete;
   void Trace(Visitor*) const;
 
   bool HandleAccessKey(const WebKeyboardEvent&);
@@ -61,8 +62,6 @@
   const Member<LocalFrame> frame_;
 
   Member<ScrollManager> scroll_manager_;
-
-  DISALLOW_COPY_AND_ASSIGN(KeyboardEventManager);
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/input/mouse_event_manager.h b/third_party/blink/renderer/core/input/mouse_event_manager.h
index 4881f977..7a76fbc 100644
--- a/third_party/blink/renderer/core/input/mouse_event_manager.h
+++ b/third_party/blink/renderer/core/input/mouse_event_manager.h
@@ -5,7 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_INPUT_MOUSE_EVENT_MANAGER_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_INPUT_MOUSE_EVENT_MANAGER_H_
 
-#include "base/macros.h"
 #include "third_party/blink/public/common/input/web_mouse_event.h"
 #include "third_party/blink/public/platform/web_input_event_result.h"
 #include "third_party/blink/renderer/core/core_export.h"
@@ -36,6 +35,8 @@
       public SynchronousMutationObserver {
  public:
   MouseEventManager(LocalFrame&, ScrollManager&);
+  MouseEventManager(const MouseEventManager&) = delete;
+  MouseEventManager& operator=(const MouseEventManager&) = delete;
   virtual ~MouseEventManager();
   void Trace(Visitor*) const override;
 
@@ -156,6 +157,10 @@
                                       const WebMouseEvent*,
                                       EventTarget* exited_target,
                                       const String& canvas_region_id);
+    MouseEventBoundaryEventDispatcher(
+        const MouseEventBoundaryEventDispatcher&) = delete;
+    MouseEventBoundaryEventDispatcher& operator=(
+        const MouseEventBoundaryEventDispatcher&) = delete;
 
    protected:
     void DispatchOut(EventTarget*, EventTarget* related_target) override;
@@ -180,7 +185,6 @@
     const WebMouseEvent* web_mouse_event_;
     EventTarget* exited_target_;
     String canvas_region_id_;
-    DISALLOW_COPY_AND_ASSIGN(MouseEventBoundaryEventDispatcher);
   };
 
   // If the given element is a shadow host and its root has delegatesFocus=false
@@ -246,8 +250,6 @@
   // ends, and at each begin frame, we will dispatch a fake mouse move event to
   // update hover when this is true.
   bool hover_state_dirty_ = false;
-
-  DISALLOW_COPY_AND_ASSIGN(MouseEventManager);
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/input/mouse_wheel_event_manager.h b/third_party/blink/renderer/core/input/mouse_wheel_event_manager.h
index 1156ec8..c1cdab7 100644
--- a/third_party/blink/renderer/core/input/mouse_wheel_event_manager.h
+++ b/third_party/blink/renderer/core/input/mouse_wheel_event_manager.h
@@ -5,7 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_INPUT_MOUSE_WHEEL_EVENT_MANAGER_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_INPUT_MOUSE_WHEEL_EVENT_MANAGER_H_
 
-#include "base/macros.h"
 #include "third_party/blink/public/platform/web_input_event_result.h"
 #include "third_party/blink/renderer/core/input/scroll_manager.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
@@ -23,6 +22,8 @@
     : public GarbageCollected<MouseWheelEventManager> {
  public:
   explicit MouseWheelEventManager(LocalFrame&, ScrollManager&);
+  MouseWheelEventManager(const MouseWheelEventManager&) = delete;
+  MouseWheelEventManager& operator=(const MouseWheelEventManager&) = delete;
   void Trace(Visitor*) const;
 
   void Clear();
@@ -39,8 +40,6 @@
   const Member<LocalFrame> frame_;
   Member<Node> wheel_target_;
   Member<ScrollManager> scroll_manager_;
-
-  DISALLOW_COPY_AND_ASSIGN(MouseWheelEventManager);
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/input/pointer_event_manager.h b/third_party/blink/renderer/core/input/pointer_event_manager.h
index 1f0132c..b76e220 100644
--- a/third_party/blink/renderer/core/input/pointer_event_manager.h
+++ b/third_party/blink/renderer/core/input/pointer_event_manager.h
@@ -5,7 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_INPUT_POINTER_EVENT_MANAGER_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_INPUT_POINTER_EVENT_MANAGER_H_
 
-#include "base/macros.h"
 #include "third_party/blink/public/common/input/web_pointer_properties.h"
 #include "third_party/blink/public/platform/web_input_event_result.h"
 #include "third_party/blink/renderer/core/core_export.h"
@@ -28,6 +27,8 @@
     : public GarbageCollected<PointerEventManager> {
  public:
   PointerEventManager(LocalFrame&, MouseEventManager&);
+  PointerEventManager(const PointerEventManager&) = delete;
+  PointerEventManager& operator=(const PointerEventManager&) = delete;
   void Trace(Visitor*) const;
 
   // This is the unified path for handling all input device events. This may
@@ -129,6 +130,10 @@
   class PointerEventBoundaryEventDispatcher : public BoundaryEventDispatcher {
    public:
     PointerEventBoundaryEventDispatcher(PointerEventManager*, PointerEvent*);
+    PointerEventBoundaryEventDispatcher(
+        const PointerEventBoundaryEventDispatcher&) = delete;
+    PointerEventBoundaryEventDispatcher& operator=(
+        const PointerEventBoundaryEventDispatcher&) = delete;
 
    protected:
     void DispatchOut(EventTarget*, EventTarget* related_target) override;
@@ -149,7 +154,6 @@
                   bool check_for_listener);
     PointerEventManager* pointer_event_manager_;
     PointerEvent* pointer_event_;
-    DISALLOW_COPY_AND_ASSIGN(PointerEventBoundaryEventDispatcher);
   };
 
   // Sends pointercancels for existing PointerEvents that are interrupted.
@@ -276,8 +280,6 @@
   // main thread, or all events (touch start/end/move).
   bool skip_touch_filter_discrete_ = false;
   bool skip_touch_filter_all_ = false;
-
-  DISALLOW_COPY_AND_ASSIGN(PointerEventManager);
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/input/scroll_manager.cc b/third_party/blink/renderer/core/input/scroll_manager.cc
index 0366499..3627274 100644
--- a/third_party/blink/renderer/core/input/scroll_manager.cc
+++ b/third_party/blink/renderer/core/input/scroll_manager.cc
@@ -190,13 +190,16 @@
 
 bool ScrollManager::CanScroll(const ScrollState& scroll_state,
                               const Node& current_node) {
-  if (!current_node.GetLayoutBox())
+  LayoutBox* scrolling_box = current_node.GetLayoutBox();
+  if (auto* element = DynamicTo<Element>(current_node))
+    scrolling_box = element->GetLayoutBoxForScrolling();
+  if (!scrolling_box)
     return false;
 
   // We need to always add the global root scroller even if it isn't scrollable
   // since we can always pinch-zoom and scroll as well as for overscroll
   // effects.
-  if (current_node.GetLayoutBox()->IsGlobalRootScroller())
+  if (scrolling_box->IsGlobalRootScroller())
     return true;
 
   // If this is the main LayoutView, and it's not the root scroller, that means
@@ -205,13 +208,12 @@
   // so ensure it gets added to the scroll chain. See LTHI::ApplyScroll for the
   // equivalent behavior in CC. Node::NativeApplyScroll contains a special
   // handler for this case.
-  if (IsA<LayoutView>(current_node.GetLayoutBox()) &&
+  if (IsA<LayoutView>(scrolling_box) &&
       current_node.GetDocument().GetFrame()->IsMainFrame()) {
     return true;
   }
 
-  ScrollableArea* scrollable_area =
-      current_node.GetLayoutBox()->GetScrollableArea();
+  ScrollableArea* scrollable_area = scrolling_box->GetScrollableArea();
 
   if (!scrollable_area)
     return false;
diff --git a/third_party/blink/renderer/core/input/scroll_manager.h b/third_party/blink/renderer/core/input/scroll_manager.h
index 5be4849..dd372e74 100644
--- a/third_party/blink/renderer/core/input/scroll_manager.h
+++ b/third_party/blink/renderer/core/input/scroll_manager.h
@@ -8,7 +8,6 @@
 #include <memory>
 
 #include "base/callback_helpers.h"
-#include "base/macros.h"
 #include "cc/input/snap_fling_controller.h"
 #include "third_party/blink/public/platform/web_input_event_result.h"
 #include "third_party/blink/renderer/core/core_export.h"
@@ -42,6 +41,8 @@
                                   public cc::SnapFlingClient {
  public:
   explicit ScrollManager(LocalFrame&);
+  ScrollManager(const ScrollManager&) = delete;
+  ScrollManager& operator=(const ScrollManager&) = delete;
   virtual ~ScrollManager() = default;
   void Trace(Visitor*) const;
 
@@ -191,8 +192,6 @@
 
   LayoutSize
       offset_from_resize_corner_;  // In the coords of m_resizeScrollableArea.
-
-  DISALLOW_COPY_AND_ASSIGN(ScrollManager);
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/input/touch_event_manager.h b/third_party/blink/renderer/core/input/touch_event_manager.h
index 26a1c2c..46deb25 100644
--- a/third_party/blink/renderer/core/input/touch_event_manager.h
+++ b/third_party/blink/renderer/core/input/touch_event_manager.h
@@ -5,7 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_INPUT_TOUCH_EVENT_MANAGER_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_INPUT_TOUCH_EVENT_MANAGER_H_
 
-#include "base/macros.h"
 #include "third_party/blink/public/common/input/web_coalesced_input_event.h"
 #include "third_party/blink/public/common/input/web_pointer_event.h"
 #include "third_party/blink/public/common/input/web_touch_event.h"
@@ -31,6 +30,9 @@
  public:
 
   explicit TouchEventManager(LocalFrame&);
+  TouchEventManager(const TouchEventManager&) = delete;
+  TouchEventManager& operator=(const TouchEventManager&) = delete;
+
   void Trace(Visitor*) const;
 
   void HandleTouchPoint(const WebPointerEvent&,
@@ -134,8 +136,6 @@
   // intersection of all the previously calculated effective touch action values
   // during the sequence.
   base::Optional<TouchAction> delayed_effective_touch_action_;
-
-  DISALLOW_COPY_AND_ASSIGN(TouchEventManager);
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/layout_text.cc b/third_party/blink/renderer/core/layout/layout_text.cc
index d8160d9..d79a1f0 100644
--- a/third_party/blink/renderer/core/layout/layout_text.cc
+++ b/third_party/blink/renderer/core/layout/layout_text.cc
@@ -31,10 +31,10 @@
 #include "third_party/blink/renderer/core/content_capture/content_capture_manager.h"
 #include "third_party/blink/renderer/core/dom/dom_node_ids.h"
 #include "third_party/blink/renderer/core/dom/text.h"
+#include "third_party/blink/renderer/core/editing/bidi_adjustment.h"
 #include "third_party/blink/renderer/core/editing/ephemeral_range.h"
 #include "third_party/blink/renderer/core/editing/frame_selection.h"
 #include "third_party/blink/renderer/core/editing/inline_box_position.h"
-#include "third_party/blink/renderer/core/editing/inline_box_traversal.h"
 #include "third_party/blink/renderer/core/editing/iterators/text_iterator.h"
 #include "third_party/blink/renderer/core/editing/text_affinity.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_caret_position.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_caret_position.cc
index 06f312e..dc599758 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_caret_position.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_caret_position.cc
@@ -4,7 +4,7 @@
 
 #include "third_party/blink/renderer/core/layout/ng/inline/ng_caret_position.h"
 
-#include "third_party/blink/renderer/core/editing/inline_box_traversal.h"
+#include "third_party/blink/renderer/core/editing/bidi_adjustment.h"
 #include "third_party/blink/renderer/core/editing/position_with_affinity.h"
 #include "third_party/blink/renderer/core/editing/text_affinity.h"
 #include "third_party/blink/renderer/core/layout/layout_block_flow.h"
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.cc
index 58ccbee..17f53a1 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.cc
@@ -4,7 +4,7 @@
 
 #include "third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.h"
 
-#include "third_party/blink/renderer/core/editing/inline_box_traversal.h"
+#include "third_party/blink/renderer/core/editing/bidi_adjustment.h"
 #include "third_party/blink/renderer/core/editing/position_with_affinity.h"
 #include "third_party/blink/renderer/core/layout/ng/inline/ng_caret_position.h"
 #include "third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items_builder.h"
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment.cc
index d3139180..268b91ef 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment.cc
@@ -30,30 +30,6 @@
 
 }  // anonymous namespace
 
-NGPhysicalTextFragment::NGPhysicalTextFragment(
-    PassKey key,
-    const NGPhysicalTextFragment& source,
-    unsigned start_offset,
-    unsigned end_offset,
-    scoped_refptr<const ShapeResultView> shape_result)
-    : NGPhysicalFragment(
-          source.GetMutableLayoutObject(),
-          source.StyleVariant(),
-          source.IsHorizontal()
-              ? PhysicalSize{shape_result->SnappedWidth(), source.Size().height}
-              : PhysicalSize{source.Size().width, shape_result->SnappedWidth()},
-          kFragmentText,
-          static_cast<unsigned>(source.TextType())),
-      text_(source.text_),
-      text_offset_(start_offset, end_offset),
-      shape_result_(std::move(shape_result)) {
-  DCHECK_GE(text_offset_.start, source.StartOffset());
-  DCHECK_LE(text_offset_.end, source.EndOffset());
-  DCHECK(shape_result_ || IsFlowControl()) << *this;
-  base_or_resolved_direction_ = source.base_or_resolved_direction_;
-  ink_overflow_computed_ = false;
-}
-
 NGPhysicalTextFragment::NGPhysicalTextFragment(NGTextFragmentBuilder* builder)
     : NGPhysicalFragment(builder,
                          kFragmentText,
@@ -220,20 +196,6 @@
   return builder.ToTextFragment();
 }
 
-scoped_refptr<const NGPhysicalTextFragment> NGPhysicalTextFragment::TrimText(
-    unsigned new_start_offset,
-    unsigned new_end_offset) const {
-  DCHECK(shape_result_);
-  DCHECK_GE(new_start_offset, StartOffset());
-  DCHECK_GT(new_end_offset, new_start_offset);
-  DCHECK_LE(new_end_offset, EndOffset());
-  scoped_refptr<ShapeResultView> new_shape_result = ShapeResultView::Create(
-      shape_result_.get(), new_start_offset, new_end_offset);
-  return base::AdoptRef(
-      new NGPhysicalTextFragment(PassKey(), *this, new_start_offset,
-                                 new_end_offset, std::move(new_shape_result)));
-}
-
 unsigned NGPhysicalTextFragment::TextOffsetForPoint(
     const PhysicalOffset& point) const {
   const ComputedStyle& style = Style();
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment.h b/third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment.h
index f682eab..7684f68 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment.h
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment.h
@@ -29,14 +29,6 @@
  public:
   NGPhysicalTextFragment(NGTextFragmentBuilder*);
 
-  using PassKey = util::PassKey<NGPhysicalTextFragment>;
-  // For use by TrimText only
-  NGPhysicalTextFragment(PassKey,
-                         const NGPhysicalTextFragment& source,
-                         unsigned start_offset,
-                         unsigned end_offset,
-                         scoped_refptr<const ShapeResultView> shape_result);
-
   NGTextType TextType() const { return static_cast<NGTextType>(sub_type_); }
   // Returns true if the text is generated (from, e.g., list marker,
   // pseudo-element, ...) instead of from a DOM text node.
@@ -93,12 +85,6 @@
 
   scoped_refptr<const NGPhysicalTextFragment> CloneAsHiddenForPaint() const;
 
-  // Create a new fragment that has part of the text of this fragment.
-  // All other properties are the same as this fragment.
-  scoped_refptr<const NGPhysicalTextFragment> TrimText(
-      unsigned start_offset,
-      unsigned end_offset) const;
-
   scoped_refptr<const NGPhysicalFragment> CloneWithoutOffset() const;
 
   NGTextFragmentPaintInfo PaintInfo() const {
diff --git a/third_party/blink/renderer/core/layout/ng/layout_ng_fieldset.cc b/third_party/blink/renderer/core/layout/ng/layout_ng_fieldset.cc
index 1cad3c6d..0e41da7 100644
--- a/third_party/blink/renderer/core/layout/ng/layout_ng_fieldset.cc
+++ b/third_party/blink/renderer/core/layout/ng/layout_ng_fieldset.cc
@@ -63,9 +63,6 @@
   // Inherit all properties listed here:
   // https://html.spec.whatwg.org/C/#anonymous-fieldset-content-box
 
-  // TODO(crbug.com/1101976): When the paint code is ready for anonymous
-  // scrollable containers, inherit overflow-x and overflow-y here.
-
   child_style.SetAlignContent(StyleRef().AlignContent());
   child_style.SetAlignItems(StyleRef().AlignItems());
 
@@ -113,6 +110,8 @@
 
   child_style.SetJustifyContent(StyleRef().JustifyContent());
   child_style.SetJustifyItems(StyleRef().JustifyItems());
+  child_style.SetOverflowX(StyleRef().OverflowX());
+  child_style.SetOverflowY(StyleRef().OverflowY());
   child_style.SetUnicodeBidi(StyleRef().GetUnicodeBidi());
 }
 
@@ -141,4 +140,18 @@
   return LayoutBlockFlow::BackgroundIsKnownToBeOpaqueInRect(local_rect);
 }
 
+LayoutUnit LayoutNGFieldset::ScrollWidth() const {
+  const LayoutObject* child = FirstChild();
+  if (child && child->IsAnonymous())
+    return ToLayoutBox(child)->ScrollWidth();
+  return LayoutNGBlockFlow::ScrollWidth();
+}
+
+LayoutUnit LayoutNGFieldset::ScrollHeight() const {
+  const LayoutObject* child = FirstChild();
+  if (child && child->IsAnonymous())
+    return ToLayoutBox(child)->ScrollHeight();
+  return LayoutNGBlockFlow::ScrollHeight();
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/ng/layout_ng_fieldset.h b/third_party/blink/renderer/core/layout/ng/layout_ng_fieldset.h
index 3580187d..2f71b28 100644
--- a/third_party/blink/renderer/core/layout/ng/layout_ng_fieldset.h
+++ b/third_party/blink/renderer/core/layout/ng/layout_ng_fieldset.h
@@ -27,6 +27,11 @@
                                  ComputedStyle& child_style) const override;
   void InvalidatePaint(const PaintInvalidatorContext& context) const final;
   bool BackgroundIsKnownToBeOpaqueInRect(const PhysicalRect&) const override;
+
+  bool AllowsNonVisibleOverflow() const override { return false; }
+  // Override to forward to the anonymous fieldset content box.
+  LayoutUnit ScrollWidth() const override;
+  LayoutUnit ScrollHeight() const override;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/loader/frame_loader.cc b/third_party/blink/renderer/core/loader/frame_loader.cc
index 3858e26..7da1e96 100644
--- a/third_party/blink/renderer/core/loader/frame_loader.cc
+++ b/third_party/blink/renderer/core/loader/frame_loader.cc
@@ -917,17 +917,19 @@
     return true;
 
   int status_code = params->response.HttpStatusCode();
-  if (status_code == 204 || status_code == 205) {
-    // The server does not want us to replace the page contents.
-    return false;
-  }
+  // If the server sends 204 or 205, this means the server does not want to
+  // replace the page contents. However, PlzNavigate should have handled it
+  // browser-side and never sent a commit request to the renderer.
+  if (status_code == 204 || status_code == 205)
+    CHECK(false);
 
+  // If the server attached a Content-Disposition indicating that the resource
+  // is an attachment, this is actually a download. However, PlzNavigate should
+  // have handled it browser-side and never sent a commit request to the
+  // renderer.
   if (IsContentDispositionAttachment(
           params->response.HttpHeaderField(http_names::kContentDisposition))) {
-    // The server wants us to download instead of replacing the page contents.
-    // Downloading is handled by the embedder, but we still get the initial
-    // response so that we can ignore it and clean up properly.
-    return false;
+    CHECK(false);
   }
 
   const String& mime_type = params->response.MimeType();
@@ -936,6 +938,8 @@
   PluginData* plugin_data = frame->GetPluginData();
   bool can_load_with_plugin = !mime_type.IsEmpty() && plugin_data &&
                               plugin_data->SupportsMimeType(mime_type);
+  // TODO(dcheng): Ideally, this should commit a failed navigation or something,
+  // instead of silently ignoring the commit request.
   if (!can_load_with_plugin) {
     WebLocalFrameImpl::FromFrame(frame)->Client()->WillFailCommitNavigation(
         WebLocalFrameClient::CommitFailureReason::kNoPluginForMimeType);
diff --git a/third_party/blink/renderer/core/paint/ng/ng_paint_fragment.cc b/third_party/blink/renderer/core/paint/ng/ng_paint_fragment.cc
index cc45458..35318d1 100644
--- a/third_party/blink/renderer/core/paint/ng/ng_paint_fragment.cc
+++ b/third_party/blink/renderer/core/paint/ng/ng_paint_fragment.cc
@@ -5,8 +5,8 @@
 #include "third_party/blink/renderer/core/paint/ng/ng_paint_fragment.h"
 
 #include "third_party/blink/renderer/core/dom/pseudo_element.h"
+#include "third_party/blink/renderer/core/editing/bidi_adjustment.h"
 #include "third_party/blink/renderer/core/editing/frame_selection.h"
-#include "third_party/blink/renderer/core/editing/inline_box_traversal.h"
 #include "third_party/blink/renderer/core/editing/position_with_affinity.h"
 #include "third_party/blink/renderer/core/editing/text_affinity.h"
 #include "third_party/blink/renderer/core/layout/geometry/logical_rect.h"
diff --git a/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc b/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc
index 5c425ca1..25121195 100644
--- a/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc
+++ b/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc
@@ -675,10 +675,8 @@
   if (HasBeenDisposed())
     return;
   // Schedule the scroll DOM event.
-  if (GetLayoutBox()->GetNode()) {
-    GetLayoutBox()->GetNode()->GetDocument().EnqueueScrollEventForNode(
-        GetLayoutBox()->GetNode());
-  }
+  if (auto* node = EventTargetNode())
+    node->GetDocument().EnqueueScrollEventForNode(node);
 }
 
 IntSize PaintLayerScrollableArea::MinimumScrollOffsetInt() const {
@@ -1546,7 +1544,8 @@
   DCHECK(GetLayoutBox()->GetFrame()->GetSettings());
   if (VisualViewportSuppliesScrollbars() ||
       !CanHaveOverflowScrollbars(*GetLayoutBox()) ||
-      GetLayoutBox()->GetFrame()->GetSettings()->GetHideScrollbars()) {
+      GetLayoutBox()->GetFrame()->GetSettings()->GetHideScrollbars() ||
+      GetLayoutBox()->IsLayoutNGFieldset()) {
     needs_horizontal_scrollbar = false;
     needs_vertical_scrollbar = false;
     return;
diff --git a/third_party/blink/renderer/core/scroll/scrollable_area.cc b/third_party/blink/renderer/core/scroll/scrollable_area.cc
index b5c9797..29a9fb7 100644
--- a/third_party/blink/renderer/core/scroll/scrollable_area.cc
+++ b/third_party/blink/renderer/core/scroll/scrollable_area.cc
@@ -772,6 +772,16 @@
   }
 }
 
+Node* ScrollableArea::EventTargetNode() const {
+  const LayoutBox* box = GetLayoutBox();
+  Node* node = box->GetNode();
+  if (!node && box->Parent() && box->Parent()->IsLayoutNGFieldset())
+    node = box->Parent()->GetNode();
+  if (node && IsA<Element>(node))
+    DCHECK_EQ(box, To<Element>(node)->GetLayoutBoxForScrolling());
+  return node;
+}
+
 const Document* ScrollableArea::GetDocument() const {
   if (auto* box = GetLayoutBox())
     return &box->GetDocument();
@@ -874,7 +884,7 @@
 void ScrollableArea::OnScrollFinished() {
   if (GetLayoutBox()) {
     if (RuntimeEnabledFeatures::OverscrollCustomizationEnabled()) {
-      if (Node* node = GetLayoutBox()->GetNode())
+      if (Node* node = EventTargetNode())
         node->GetDocument().EnqueueScrollEndEventForNode(node);
     }
     GetLayoutBox()
@@ -1002,8 +1012,13 @@
   if (!layout_box)
     return nullptr;
 
-  if (!layout_box->IsGlobalRootScroller())
+  if (!layout_box->IsGlobalRootScroller()) {
+    if (const auto* element = DynamicTo<Element>(layout_box->GetNode())) {
+      if (auto* scrolling_box = element->GetLayoutBoxForScrolling())
+        return scrolling_box->GetScrollableArea();
+    }
     return layout_box->GetScrollableArea();
+  }
 
   // The global root scroller should be scrolled by the root frame view's
   // ScrollableArea.
diff --git a/third_party/blink/renderer/core/scroll/scrollable_area.h b/third_party/blink/renderer/core/scroll/scrollable_area.h
index 8598821..2f58b455 100644
--- a/third_party/blink/renderer/core/scroll/scrollable_area.h
+++ b/third_party/blink/renderer/core/scroll/scrollable_area.h
@@ -568,6 +568,10 @@
 
   bool HasBeenDisposed() const { return has_been_disposed_; }
 
+  // Returns a Node at which 'scroll' events should be dispatched.
+  // For <fieldset>, a ScrollableArea is associated to its internal anonymous
+  // box. GetLayoutBox()->GetNode() doesn't work in this case.
+  Node* EventTargetNode() const;
   virtual const Document* GetDocument() const;
 
   // Resolves into un-zoomed physical pixels a scroll |delta| based on its
diff --git a/third_party/blink/renderer/core/svg/BUILD.gn b/third_party/blink/renderer/core/svg/BUILD.gn
index 8fd172b..aa6b33ea 100644
--- a/third_party/blink/renderer/core/svg/BUILD.gn
+++ b/third_party/blink/renderer/core/svg/BUILD.gn
@@ -9,6 +9,7 @@
     "animation/element_smil_animations.cc",
     "animation/element_smil_animations.h",
     "animation/priority_queue.h",
+    "animation/smil_animation_effect_parameters.h",
     "animation/smil_animation_sandwich.cc",
     "animation/smil_animation_sandwich.h",
     "animation/smil_repeat_count.h",
diff --git a/third_party/blink/renderer/core/svg/animation/smil_animation_effect_parameters.h b/third_party/blink/renderer/core/svg/animation/smil_animation_effect_parameters.h
new file mode 100644
index 0000000..81086aa
--- /dev/null
+++ b/third_party/blink/renderer/core/svg/animation/smil_animation_effect_parameters.h
@@ -0,0 +1,45 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_SVG_ANIMATION_SMIL_ANIMATION_EFFECT_PARAMETERS_H_
+#define THIRD_PARTY_BLINK_RENDERER_CORE_SVG_ANIMATION_SMIL_ANIMATION_EFFECT_PARAMETERS_H_
+
+namespace blink {
+
+// This struct describes the parameters needed to apply the animation
+// effect function.
+// https://www.w3.org/TR/SMIL/smil-animation.html#animationNS-AnimationEffectFcn
+struct SMILAnimationEffectParameters {
+  bool is_discrete = false;
+  bool is_to_animation = false;
+  bool is_additive = false;
+  bool is_cumulative = false;
+};
+
+// Compute the animated number value based on effect parameters and timing data.
+inline void AnimateAdditiveNumber(
+    const SMILAnimationEffectParameters& parameters,
+    float percentage,
+    unsigned repeat_count,
+    float from_number,
+    float to_number,
+    float to_at_end_of_duration_number,
+    float& animated_number) {
+  float number;
+  if (parameters.is_discrete)
+    number = percentage < 0.5 ? from_number : to_number;
+  else
+    number = (to_number - from_number) * percentage + from_number;
+  if (!parameters.is_to_animation) {
+    if (repeat_count && parameters.is_cumulative)
+      number += to_at_end_of_duration_number * repeat_count;
+    if (parameters.is_additive)
+      number += animated_number;
+  }
+  animated_number = number;
+}
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_SVG_ANIMATION_SMIL_ANIMATION_EFFECT_PARAMETERS_H_
diff --git a/third_party/blink/renderer/core/svg/properties/svg_property.h b/third_party/blink/renderer/core/svg/properties/svg_property.h
index 75f40ea..8934b3e5 100644
--- a/third_party/blink/renderer/core/svg/properties/svg_property.h
+++ b/third_party/blink/renderer/core/svg/properties/svg_property.h
@@ -39,7 +39,7 @@
 namespace blink {
 
 class SVGElement;
-class SVGAnimateElement;
+struct SMILAnimationEffectParameters;
 
 class SVGPropertyBase : public GarbageCollected<SVGPropertyBase> {
  public:
@@ -65,7 +65,7 @@
   // WebAnimations transition.
   virtual void Add(SVGPropertyBase*, SVGElement*) = 0;
   virtual void CalculateAnimatedValue(
-      const SVGAnimateElement&,
+      const SMILAnimationEffectParameters&,
       float percentage,
       unsigned repeat_count,
       SVGPropertyBase* from,
diff --git a/third_party/blink/renderer/core/svg/svg_angle.cc b/third_party/blink/renderer/core/svg/svg_angle.cc
index 01ce1928..bc1761d 100644
--- a/third_party/blink/renderer/core/svg/svg_angle.cc
+++ b/third_party/blink/renderer/core/svg/svg_angle.cc
@@ -21,7 +21,7 @@
 
 #include "third_party/blink/renderer/core/svg/svg_angle.h"
 
-#include "third_party/blink/renderer/core/svg/svg_animate_element.h"
+#include "third_party/blink/renderer/core/svg/animation/smil_animation_effect_parameters.h"
 #include "third_party/blink/renderer/core/svg/svg_enumeration_map.h"
 #include "third_party/blink/renderer/core/svg/svg_parser_utilities.h"
 #include "third_party/blink/renderer/platform/heap/heap.h"
@@ -364,7 +364,7 @@
 }
 
 void SVGAngle::CalculateAnimatedValue(
-    const SVGAnimateElement& animation_element,
+    const SMILAnimationEffectParameters& parameters,
     float percentage,
     unsigned repeat_count,
     SVGPropertyBase* from,
@@ -382,9 +382,10 @@
   }
 
   float animated_value = Value();
-  animation_element.AnimateAdditiveNumber(
-      percentage, repeat_count, from_angle->Value(), to_angle->Value(),
-      To<SVGAngle>(to_at_end_of_duration)->Value(), animated_value);
+  AnimateAdditiveNumber(parameters, percentage, repeat_count,
+                        from_angle->Value(), to_angle->Value(),
+                        To<SVGAngle>(to_at_end_of_duration)->Value(),
+                        animated_value);
   OrientType()->SetEnumValue(kSVGMarkerOrientAngle);
   SetValue(animated_value);
 }
diff --git a/third_party/blink/renderer/core/svg/svg_angle.h b/third_party/blink/renderer/core/svg/svg_angle.h
index df55c46..97afad5 100644
--- a/third_party/blink/renderer/core/svg/svg_angle.h
+++ b/third_party/blink/renderer/core/svg/svg_angle.h
@@ -92,7 +92,7 @@
   SVGParsingError SetValueAsString(const String&);
 
   void Add(SVGPropertyBase*, SVGElement*) override;
-  void CalculateAnimatedValue(const SVGAnimateElement&,
+  void CalculateAnimatedValue(const SMILAnimationEffectParameters&,
                               float percentage,
                               unsigned repeat_count,
                               SVGPropertyBase* from,
diff --git a/third_party/blink/renderer/core/svg/svg_animate_element.cc b/third_party/blink/renderer/core/svg/svg_animate_element.cc
index aa3a502..fd57a79 100644
--- a/third_party/blink/renderer/core/svg/svg_animate_element.cc
+++ b/third_party/blink/renderer/core/svg/svg_animate_element.cc
@@ -27,6 +27,7 @@
 #include "third_party/blink/renderer/core/css/style_change_reason.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/dom/qualified_name.h"
+#include "third_party/blink/renderer/core/svg/animation/smil_animation_effect_parameters.h"
 #include "third_party/blink/renderer/core/svg/properties/svg_animated_property.h"
 #include "third_party/blink/renderer/core/svg/properties/svg_property.h"
 #include "third_party/blink/renderer/core/svg/svg_animated_color.h"
@@ -362,9 +363,6 @@
   if (GetCalcMode() == kCalcModeDiscrete)
     percentage = percentage < 0.5 ? 0 : 1;
 
-  // Target element might have changed.
-  SVGElement* target_element = targetElement();
-
   // Values-animation accumulates using the last values entry corresponding to
   // the end of duration time.
   SVGPropertyBase* animated_value = result_animation_element->animated_value_;
@@ -388,9 +386,10 @@
     return;
   }
 
+  SMILAnimationEffectParameters parameters = ComputeEffectParameters();
   animated_value->CalculateAnimatedValue(
-      *this, percentage, repeat_count, from_value, to_value,
-      to_at_end_of_duration_value, target_element);
+      parameters, percentage, repeat_count, from_value, to_value,
+      to_at_end_of_duration_value, targetElement());
 }
 
 bool SVGAnimateElement::CalculateToAtEndOfDurationValue(
diff --git a/third_party/blink/renderer/core/svg/svg_animate_motion_element.cc b/third_party/blink/renderer/core/svg/svg_animate_motion_element.cc
index 6f5f39e6..04fe1b6 100644
--- a/third_party/blink/renderer/core/svg/svg_animate_motion_element.cc
+++ b/third_party/blink/renderer/core/svg/svg_animate_motion_element.cc
@@ -23,6 +23,7 @@
 
 #include "third_party/blink/renderer/core/dom/element_traversal.h"
 #include "third_party/blink/renderer/core/layout/layout_object.h"
+#include "third_party/blink/renderer/core/svg/animation/smil_animation_effect_parameters.h"
 #include "third_party/blink/renderer/core/svg/svg_mpath_element.h"
 #include "third_party/blink/renderer/core/svg/svg_parser_utilities.h"
 #include "third_party/blink/renderer/core/svg/svg_path_element.h"
@@ -206,29 +207,32 @@
 void SVGAnimateMotionElement::CalculateAnimatedValue(float percentage,
                                                      unsigned repeat_count,
                                                      SVGSMILElement*) const {
+  SMILAnimationEffectParameters parameters = ComputeEffectParameters();
+
   SVGElement* target_element = targetElement();
   DCHECK(target_element);
   AffineTransform* transform = target_element->AnimateMotionTransform();
   DCHECK(transform);
 
-  if (!IsAdditive())
+  if (!parameters.is_additive)
     transform->MakeIdentity();
 
   if (GetAnimationMode() != kPathAnimation) {
     FloatPoint to_point_at_end_of_duration = to_point_;
-    if (GetAnimationMode() != kToAnimation) {
-      if (repeat_count && IsAccumulated() && has_to_point_at_end_of_duration_) {
+    if (!parameters.is_to_animation) {
+      if (repeat_count && parameters.is_cumulative &&
+          has_to_point_at_end_of_duration_) {
         to_point_at_end_of_duration = to_point_at_end_of_duration_;
       }
     }
 
     float animated_x = 0;
-    AnimateAdditiveNumber(percentage, repeat_count, from_point_.X(),
+    AnimateAdditiveNumber(parameters, percentage, repeat_count, from_point_.X(),
                           to_point_.X(), to_point_at_end_of_duration.X(),
                           animated_x);
 
     float animated_y = 0;
-    AnimateAdditiveNumber(percentage, repeat_count, from_point_.Y(),
+    AnimateAdditiveNumber(parameters, percentage, repeat_count, from_point_.Y(),
                           to_point_.Y(), to_point_at_end_of_duration.Y(),
                           animated_y);
 
@@ -244,7 +248,7 @@
   animation_path_.PointAndNormalAtLength(position_on_path, position, angle);
 
   // Handle accumulate="sum".
-  if (repeat_count && IsAccumulated()) {
+  if (repeat_count && parameters.is_cumulative) {
     FloatPoint position_at_end_of_duration =
         animation_path_.PointAtLength(animation_path_.length());
     position.Move(position_at_end_of_duration.X() * repeat_count,
diff --git a/third_party/blink/renderer/core/svg/svg_animated_color.cc b/third_party/blink/renderer/core/svg/svg_animated_color.cc
index 02d7cc53..a05d49d 100644
--- a/third_party/blink/renderer/core/svg/svg_animated_color.cc
+++ b/third_party/blink/renderer/core/svg/svg_animated_color.cc
@@ -22,8 +22,9 @@
 #include "third_party/blink/renderer/core/css/css_color_value.h"
 #include "third_party/blink/renderer/core/css/parser/css_parser.h"
 #include "third_party/blink/renderer/core/dom/node_computed_style.h"
+#include "third_party/blink/renderer/core/svg/animation/smil_animation_effect_parameters.h"
 #include "third_party/blink/renderer/core/svg/color_distance.h"
-#include "third_party/blink/renderer/core/svg/svg_animate_element.h"
+#include "third_party/blink/renderer/core/svg/svg_element.h"
 
 namespace blink {
 
@@ -75,7 +76,7 @@
 }
 
 void SVGColorProperty::CalculateAnimatedValue(
-    const SVGAnimateElement& animation_element,
+    const SMILAnimationEffectParameters& parameters,
     float percentage,
     unsigned repeat_count,
     SVGPropertyBase* from_value,
@@ -98,24 +99,24 @@
   Color animated_color = style_color_.Resolve(fallback_color, color_scheme);
 
   float animated_red = animated_color.Red();
-  animation_element.AnimateAdditiveNumber(
-      percentage, repeat_count, from_color.Red(), to_color.Red(),
-      to_at_end_of_duration_color.Red(), animated_red);
+  AnimateAdditiveNumber(parameters, percentage, repeat_count, from_color.Red(),
+                        to_color.Red(), to_at_end_of_duration_color.Red(),
+                        animated_red);
 
   float animated_green = animated_color.Green();
-  animation_element.AnimateAdditiveNumber(
-      percentage, repeat_count, from_color.Green(), to_color.Green(),
-      to_at_end_of_duration_color.Green(), animated_green);
+  AnimateAdditiveNumber(parameters, percentage, repeat_count,
+                        from_color.Green(), to_color.Green(),
+                        to_at_end_of_duration_color.Green(), animated_green);
 
   float animated_blue = animated_color.Blue();
-  animation_element.AnimateAdditiveNumber(
-      percentage, repeat_count, from_color.Blue(), to_color.Blue(),
-      to_at_end_of_duration_color.Blue(), animated_blue);
+  AnimateAdditiveNumber(parameters, percentage, repeat_count, from_color.Blue(),
+                        to_color.Blue(), to_at_end_of_duration_color.Blue(),
+                        animated_blue);
 
   float animated_alpha = animated_color.Alpha();
-  animation_element.AnimateAdditiveNumber(
-      percentage, repeat_count, from_color.Alpha(), to_color.Alpha(),
-      to_at_end_of_duration_color.Alpha(), animated_alpha);
+  AnimateAdditiveNumber(parameters, percentage, repeat_count,
+                        from_color.Alpha(), to_color.Alpha(),
+                        to_at_end_of_duration_color.Alpha(), animated_alpha);
 
   style_color_ =
       StyleColor(MakeRGBA(roundf(animated_red), roundf(animated_green),
diff --git a/third_party/blink/renderer/core/svg/svg_animated_color.h b/third_party/blink/renderer/core/svg/svg_animated_color.h
index 18ea4d1..87d6a87 100644
--- a/third_party/blink/renderer/core/svg/svg_animated_color.h
+++ b/third_party/blink/renderer/core/svg/svg_animated_color.h
@@ -48,7 +48,7 @@
   String ValueAsString() const override;
 
   void Add(SVGPropertyBase*, SVGElement*) override;
-  void CalculateAnimatedValue(const SVGAnimateElement&,
+  void CalculateAnimatedValue(const SMILAnimationEffectParameters&,
                               float percentage,
                               unsigned repeat_count,
                               SVGPropertyBase* from,
diff --git a/third_party/blink/renderer/core/svg/svg_animation_element.cc b/third_party/blink/renderer/core/svg/svg_animation_element.cc
index 2d4648d1..d59ce6e 100644
--- a/third_party/blink/renderer/core/svg/svg_animation_element.cc
+++ b/third_party/blink/renderer/core/svg/svg_animation_element.cc
@@ -27,6 +27,7 @@
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/frame/web_feature.h"
 #include "third_party/blink/renderer/core/svg/animation/element_smil_animations.h"
+#include "third_party/blink/renderer/core/svg/animation/smil_animation_effect_parameters.h"
 #include "third_party/blink/renderer/core/svg/svg_animate_element.h"
 #include "third_party/blink/renderer/core/svg/svg_parser_utilities.h"
 #include "third_party/blink/renderer/core/svg_names.h"
@@ -648,6 +649,16 @@
   return false;
 }
 
+SMILAnimationEffectParameters SVGAnimationElement::ComputeEffectParameters()
+    const {
+  SMILAnimationEffectParameters parameters;
+  parameters.is_discrete = GetCalcMode() == kCalcModeDiscrete;
+  parameters.is_to_animation = GetAnimationMode() == kToAnimation;
+  parameters.is_additive = IsAdditive();
+  parameters.is_cumulative = IsAccumulated();
+  return parameters;
+}
+
 void SVGAnimationElement::ApplyAnimation(SVGAnimationElement* result_element) {
   if (animation_valid_ == AnimationValidity::kUnknown) {
     if (CheckAnimationParameters()) {
diff --git a/third_party/blink/renderer/core/svg/svg_animation_element.h b/third_party/blink/renderer/core/svg/svg_animation_element.h
index bc55e0b..c5bd935 100644
--- a/third_party/blink/renderer/core/svg/svg_animation_element.h
+++ b/third_party/blink/renderer/core/svg/svg_animation_element.h
@@ -51,6 +51,8 @@
   kCalcModeSpline
 };
 
+struct SMILAnimationEffectParameters;
+
 class CORE_EXPORT SVGAnimationElement : public SVGSMILElement {
   DEFINE_WRAPPERTYPEINFO();
 
@@ -82,29 +84,11 @@
   bool OverwritesUnderlyingAnimationValue() const;
   void ApplyAnimation(SVGAnimationElement* result_element);
 
-  void AnimateAdditiveNumber(float percentage,
-                             unsigned repeat_count,
-                             float from_number,
-                             float to_number,
-                             float to_at_end_of_duration_number,
-                             float& animated_number) const {
-    float number;
-    if (GetCalcMode() == kCalcModeDiscrete)
-      number = percentage < 0.5 ? from_number : to_number;
-    else
-      number = (to_number - from_number) * percentage + from_number;
-    if (GetAnimationMode() != kToAnimation) {
-      if (repeat_count && IsAccumulated())
-        number += to_at_end_of_duration_number * repeat_count;
-      if (IsAdditive())
-        number += animated_number;
-    }
-    animated_number = number;
-  }
-
  protected:
   SVGAnimationElement(const QualifiedName&, Document&);
 
+  SMILAnimationEffectParameters ComputeEffectParameters() const;
+
   void ParseAttribute(const AttributeModificationParams&) override;
 
   virtual void UpdateAnimationMode();
diff --git a/third_party/blink/renderer/core/svg/svg_boolean.cc b/third_party/blink/renderer/core/svg/svg_boolean.cc
index b30964b..29c1fad3 100644
--- a/third_party/blink/renderer/core/svg/svg_boolean.cc
+++ b/third_party/blink/renderer/core/svg/svg_boolean.cc
@@ -52,14 +52,13 @@
   NOTREACHED();
 }
 
-void SVGBoolean::CalculateAnimatedValue(
-    const SVGAnimateElement& animation_element,
-    float percentage,
-    unsigned repeat_count,
-    SVGPropertyBase* from,
-    SVGPropertyBase* to,
-    SVGPropertyBase*,
-    SVGElement*) {
+void SVGBoolean::CalculateAnimatedValue(const SMILAnimationEffectParameters&,
+                                        float percentage,
+                                        unsigned repeat_count,
+                                        SVGPropertyBase* from,
+                                        SVGPropertyBase* to,
+                                        SVGPropertyBase*,
+                                        SVGElement*) {
   NOTREACHED();
 }
 
diff --git a/third_party/blink/renderer/core/svg/svg_boolean.h b/third_party/blink/renderer/core/svg/svg_boolean.h
index 62858b5..34a1dec 100644
--- a/third_party/blink/renderer/core/svg/svg_boolean.h
+++ b/third_party/blink/renderer/core/svg/svg_boolean.h
@@ -51,7 +51,7 @@
   SVGParsingError SetValueAsString(const String&);
 
   void Add(SVGPropertyBase*, SVGElement*) override;
-  void CalculateAnimatedValue(const SVGAnimateElement&,
+  void CalculateAnimatedValue(const SMILAnimationEffectParameters&,
                               float percentage,
                               unsigned repeat_count,
                               SVGPropertyBase* from,
diff --git a/third_party/blink/renderer/core/svg/svg_enumeration.cc b/third_party/blink/renderer/core/svg/svg_enumeration.cc
index 7f4ec4d..bbd0254 100644
--- a/third_party/blink/renderer/core/svg/svg_enumeration.cc
+++ b/third_party/blink/renderer/core/svg/svg_enumeration.cc
@@ -76,7 +76,7 @@
 }
 
 void SVGEnumeration::CalculateAnimatedValue(
-    const SVGAnimateElement& animation_element,
+    const SMILAnimationEffectParameters&,
     float percentage,
     unsigned repeat_count,
     SVGPropertyBase* from,
diff --git a/third_party/blink/renderer/core/svg/svg_enumeration.h b/third_party/blink/renderer/core/svg/svg_enumeration.h
index b1efe1b2..f8e2538 100644
--- a/third_party/blink/renderer/core/svg/svg_enumeration.h
+++ b/third_party/blink/renderer/core/svg/svg_enumeration.h
@@ -81,7 +81,7 @@
   SVGParsingError SetValueAsString(const String&);
 
   void Add(SVGPropertyBase*, SVGElement*) override;
-  void CalculateAnimatedValue(const SVGAnimateElement&,
+  void CalculateAnimatedValue(const SMILAnimationEffectParameters&,
                               float percentage,
                               unsigned repeat_count,
                               SVGPropertyBase* from,
diff --git a/third_party/blink/renderer/core/svg/svg_integer.cc b/third_party/blink/renderer/core/svg/svg_integer.cc
index d39b494..c1b53d1 100644
--- a/third_party/blink/renderer/core/svg/svg_integer.cc
+++ b/third_party/blink/renderer/core/svg/svg_integer.cc
@@ -31,7 +31,7 @@
 #include "third_party/blink/renderer/core/svg/svg_integer.h"
 
 #include "third_party/blink/renderer/core/html/parser/html_parser_idioms.h"
-#include "third_party/blink/renderer/core/svg/svg_animate_element.h"
+#include "third_party/blink/renderer/core/svg/animation/smil_animation_effect_parameters.h"
 #include "third_party/blink/renderer/platform/heap/heap.h"
 
 namespace blink {
@@ -63,7 +63,7 @@
 }
 
 void SVGInteger::CalculateAnimatedValue(
-    const SVGAnimateElement& animation_element,
+    const SMILAnimationEffectParameters& parameters,
     float percentage,
     unsigned repeat_count,
     SVGPropertyBase* from,
@@ -75,9 +75,9 @@
   auto* to_at_end_of_duration_integer = To<SVGInteger>(to_at_end_of_duration);
 
   float animated_float = value_;
-  animation_element.AnimateAdditiveNumber(
-      percentage, repeat_count, from_integer->Value(), to_integer->Value(),
-      to_at_end_of_duration_integer->Value(), animated_float);
+  AnimateAdditiveNumber(parameters, percentage, repeat_count,
+                        from_integer->Value(), to_integer->Value(),
+                        to_at_end_of_duration_integer->Value(), animated_float);
   value_ = clampTo<int>(roundf(animated_float));
 }
 
diff --git a/third_party/blink/renderer/core/svg/svg_integer.h b/third_party/blink/renderer/core/svg/svg_integer.h
index 90924b8..3fac33f6 100644
--- a/third_party/blink/renderer/core/svg/svg_integer.h
+++ b/third_party/blink/renderer/core/svg/svg_integer.h
@@ -53,7 +53,7 @@
   SVGParsingError SetValueAsString(const String&);
 
   void Add(SVGPropertyBase*, SVGElement*) override;
-  void CalculateAnimatedValue(const SVGAnimateElement&,
+  void CalculateAnimatedValue(const SMILAnimationEffectParameters&,
                               float percentage,
                               unsigned repeat_count,
                               SVGPropertyBase* from,
diff --git a/third_party/blink/renderer/core/svg/svg_integer_optional_integer.cc b/third_party/blink/renderer/core/svg/svg_integer_optional_integer.cc
index 2628466..a59092ee 100644
--- a/third_party/blink/renderer/core/svg/svg_integer_optional_integer.cc
+++ b/third_party/blink/renderer/core/svg/svg_integer_optional_integer.cc
@@ -97,7 +97,7 @@
 }
 
 void SVGIntegerOptionalInteger::CalculateAnimatedValue(
-    const SVGAnimateElement& animation_element,
+    const SMILAnimationEffectParameters& parameters,
     float percentage,
     unsigned repeat_count,
     SVGPropertyBase* from,
@@ -110,12 +110,12 @@
       To<SVGIntegerOptionalInteger>(to_at_end_of_duration);
 
   first_integer_->CalculateAnimatedValue(
-      animation_element, percentage, repeat_count, from_integer->FirstInteger(),
+      parameters, percentage, repeat_count, from_integer->FirstInteger(),
       to_integer->FirstInteger(), to_at_end_of_duration_integer->FirstInteger(),
       context_element);
   second_integer_->CalculateAnimatedValue(
-      animation_element, percentage, repeat_count,
-      from_integer->SecondInteger(), to_integer->SecondInteger(),
+      parameters, percentage, repeat_count, from_integer->SecondInteger(),
+      to_integer->SecondInteger(),
       to_at_end_of_duration_integer->SecondInteger(), context_element);
 }
 
diff --git a/third_party/blink/renderer/core/svg/svg_integer_optional_integer.h b/third_party/blink/renderer/core/svg/svg_integer_optional_integer.h
index 3e7eab7..f10a87d 100644
--- a/third_party/blink/renderer/core/svg/svg_integer_optional_integer.h
+++ b/third_party/blink/renderer/core/svg/svg_integer_optional_integer.h
@@ -56,7 +56,7 @@
   static constexpr int kInitialValueBits = SVGInteger::kInitialValueBits;
 
   void Add(SVGPropertyBase*, SVGElement*) override;
-  void CalculateAnimatedValue(const SVGAnimateElement&,
+  void CalculateAnimatedValue(const SMILAnimationEffectParameters&,
                               float percentage,
                               unsigned repeat_count,
                               SVGPropertyBase* from,
diff --git a/third_party/blink/renderer/core/svg/svg_length.cc b/third_party/blink/renderer/core/svg/svg_length.cc
index 8609421..c07a7b6 100644
--- a/third_party/blink/renderer/core/svg/svg_length.cc
+++ b/third_party/blink/renderer/core/svg/svg_length.cc
@@ -26,7 +26,7 @@
 #include "third_party/blink/renderer/core/css/css_primitive_value.h"
 #include "third_party/blink/renderer/core/css/css_value.h"
 #include "third_party/blink/renderer/core/css/parser/css_parser.h"
-#include "third_party/blink/renderer/core/svg/svg_animate_element.h"
+#include "third_party/blink/renderer/core/svg/animation/smil_animation_effect_parameters.h"
 #include "third_party/blink/renderer/core/svg_names.h"
 #include "third_party/blink/renderer/platform/heap/heap.h"
 #include "third_party/blink/renderer/platform/wtf/math_extras.h"
@@ -319,7 +319,7 @@
 }
 
 void SVGLength::CalculateAnimatedValue(
-    const SVGAnimateElement& animation_element,
+    const SMILAnimationEffectParameters& parameters,
     float percentage,
     unsigned repeat_count,
     SVGPropertyBase* from_value,
@@ -333,14 +333,11 @@
 
   SVGLengthContext length_context(context_element);
   float animated_number = Value(length_context);
-  animation_element.AnimateAdditiveNumber(
-      percentage, repeat_count, from_length->Value(length_context),
+  AnimateAdditiveNumber(
+      parameters, percentage, repeat_count, from_length->Value(length_context),
       to_length->Value(length_context),
       to_at_end_of_duration_length->Value(length_context), animated_number);
 
-  DCHECK_EQ(UnitMode(), LengthModeForAnimatedLengthAttribute(
-                            animation_element.AttributeName()));
-
   // TODO(shanmuga.m): Construct a calc() expression if the units fall in
   // different categories.
   CSSPrimitiveValue::UnitType new_unit =
diff --git a/third_party/blink/renderer/core/svg/svg_length.h b/third_party/blink/renderer/core/svg/svg_length.h
index c49540d..a02b804 100644
--- a/third_party/blink/renderer/core/svg/svg_length.h
+++ b/third_party/blink/renderer/core/svg/svg_length.h
@@ -127,7 +127,7 @@
       const QualifiedName&);
 
   void Add(SVGPropertyBase*, SVGElement*) override;
-  void CalculateAnimatedValue(const SVGAnimateElement&,
+  void CalculateAnimatedValue(const SMILAnimationEffectParameters&,
                               float percentage,
                               unsigned repeat_count,
                               SVGPropertyBase* from,
diff --git a/third_party/blink/renderer/core/svg/svg_length_list.cc b/third_party/blink/renderer/core/svg/svg_length_list.cc
index 66248fd9..6f8c440d 100644
--- a/third_party/blink/renderer/core/svg/svg_length_list.cc
+++ b/third_party/blink/renderer/core/svg/svg_length_list.cc
@@ -20,7 +20,7 @@
 
 #include "third_party/blink/renderer/core/svg/svg_length_list.h"
 
-#include "third_party/blink/renderer/core/svg/svg_animate_element.h"
+#include "third_party/blink/renderer/core/svg/animation/smil_animation_effect_parameters.h"
 #include "third_party/blink/renderer/core/svg/svg_parser_utilities.h"
 #include "third_party/blink/renderer/platform/heap/heap.h"
 
@@ -102,7 +102,7 @@
 }
 
 void SVGLengthList::CalculateAnimatedValue(
-    const SVGAnimateElement& animation_element,
+    const SMILAnimationEffectParameters& parameters,
     float percentage,
     unsigned repeat_count,
     SVGPropertyBase* from_value,
@@ -115,17 +115,14 @@
       To<SVGLengthList>(to_at_end_of_duration_value);
 
   SVGLengthContext length_context(context_element);
-  DCHECK_EQ(mode_, SVGLength::LengthModeForAnimatedLengthAttribute(
-                       animation_element.AttributeName()));
 
   uint32_t from_length_list_size = from_list->length();
   uint32_t to_length_list_size = to_list->length();
   uint32_t to_at_end_of_duration_list_size =
       to_at_end_of_duration_list->length();
 
-  const bool is_to_animation =
-      animation_element.GetAnimationMode() == kToAnimation;
-  if (!AdjustFromToListValues(from_list, to_list, percentage, is_to_animation))
+  if (!AdjustFromToListValues(from_list, to_list, percentage,
+                              parameters.is_to_animation))
     return;
 
   for (uint32_t i = 0; i < to_length_list_size; ++i) {
@@ -144,9 +141,8 @@
             ? to_at_end_of_duration_list->at(i)->Value(length_context)
             : 0;
 
-    animation_element.AnimateAdditiveNumber(
-        percentage, repeat_count, effective_from, effective_to,
-        effective_to_at_end, animated_number);
+    AnimateAdditiveNumber(parameters, percentage, repeat_count, effective_from,
+                          effective_to, effective_to_at_end, animated_number);
     // |animated_number| is in user units.
     CSSPrimitiveValue::UnitType unit_type =
         length_for_unit_type->IsCalculated()
diff --git a/third_party/blink/renderer/core/svg/svg_length_list.h b/third_party/blink/renderer/core/svg/svg_length_list.h
index bf41334..91f835c8 100644
--- a/third_party/blink/renderer/core/svg/svg_length_list.h
+++ b/third_party/blink/renderer/core/svg/svg_length_list.h
@@ -56,7 +56,7 @@
   SVGLengthMode UnitMode() const { return mode_; }
 
   void Add(SVGPropertyBase*, SVGElement*) override;
-  void CalculateAnimatedValue(const SVGAnimateElement&,
+  void CalculateAnimatedValue(const SMILAnimationEffectParameters&,
                               float percentage,
                               unsigned repeat_count,
                               SVGPropertyBase* from_value,
diff --git a/third_party/blink/renderer/core/svg/svg_number.cc b/third_party/blink/renderer/core/svg/svg_number.cc
index 10e6f1a..81f2ab0 100644
--- a/third_party/blink/renderer/core/svg/svg_number.cc
+++ b/third_party/blink/renderer/core/svg/svg_number.cc
@@ -30,7 +30,7 @@
 
 #include "third_party/blink/renderer/core/svg/svg_number.h"
 
-#include "third_party/blink/renderer/core/svg/svg_animate_element.h"
+#include "third_party/blink/renderer/core/svg/animation/smil_animation_effect_parameters.h"
 #include "third_party/blink/renderer/core/svg/svg_parser_utilities.h"
 #include "third_party/blink/renderer/platform/heap/heap.h"
 
@@ -79,7 +79,7 @@
 }
 
 void SVGNumber::CalculateAnimatedValue(
-    const SVGAnimateElement& animation_element,
+    const SMILAnimationEffectParameters& parameters,
     float percentage,
     unsigned repeat_count,
     SVGPropertyBase* from,
@@ -90,9 +90,9 @@
   auto* to_number = To<SVGNumber>(to);
   auto* to_at_end_of_duration_number = To<SVGNumber>(to_at_end_of_duration);
 
-  animation_element.AnimateAdditiveNumber(
-      percentage, repeat_count, from_number->Value(), to_number->Value(),
-      to_at_end_of_duration_number->Value(), value_);
+  AnimateAdditiveNumber(parameters, percentage, repeat_count,
+                        from_number->Value(), to_number->Value(),
+                        to_at_end_of_duration_number->Value(), value_);
 }
 
 float SVGNumber::CalculateDistance(SVGPropertyBase* other, SVGElement*) {
diff --git a/third_party/blink/renderer/core/svg/svg_number.h b/third_party/blink/renderer/core/svg/svg_number.h
index d081ee5..b776207a 100644
--- a/third_party/blink/renderer/core/svg/svg_number.h
+++ b/third_party/blink/renderer/core/svg/svg_number.h
@@ -56,7 +56,7 @@
   virtual SVGParsingError SetValueAsString(const String&);
 
   void Add(SVGPropertyBase*, SVGElement*) override;
-  void CalculateAnimatedValue(const SVGAnimateElement&,
+  void CalculateAnimatedValue(const SMILAnimationEffectParameters&,
                               float percentage,
                               unsigned repeat_count,
                               SVGPropertyBase* from,
diff --git a/third_party/blink/renderer/core/svg/svg_number_list.cc b/third_party/blink/renderer/core/svg/svg_number_list.cc
index 6d56253..ee16550 100644
--- a/third_party/blink/renderer/core/svg/svg_number_list.cc
+++ b/third_party/blink/renderer/core/svg/svg_number_list.cc
@@ -20,7 +20,7 @@
 
 #include "third_party/blink/renderer/core/svg/svg_number_list.h"
 
-#include "third_party/blink/renderer/core/svg/svg_animate_element.h"
+#include "third_party/blink/renderer/core/svg/animation/smil_animation_effect_parameters.h"
 #include "third_party/blink/renderer/core/svg/svg_parser_utilities.h"
 #include "third_party/blink/renderer/platform/heap/heap.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
@@ -74,7 +74,7 @@
 }
 
 void SVGNumberList::CalculateAnimatedValue(
-    const SVGAnimateElement& animation_element,
+    const SMILAnimationEffectParameters& parameters,
     float percentage,
     unsigned repeat_count,
     SVGPropertyBase* from_value,
@@ -91,9 +91,8 @@
   uint32_t to_at_end_of_duration_list_size =
       to_at_end_of_duration_list->length();
 
-  const bool is_to_animation =
-      animation_element.GetAnimationMode() == kToAnimation;
-  if (!AdjustFromToListValues(from_list, to_list, percentage, is_to_animation))
+  if (!AdjustFromToListValues(from_list, to_list, percentage,
+                              parameters.is_to_animation))
     return;
 
   for (uint32_t i = 0; i < to_list_size; ++i) {
@@ -104,9 +103,8 @@
                                     : 0;
 
     float animated = at(i)->Value();
-    animation_element.AnimateAdditiveNumber(percentage, repeat_count,
-                                            effective_from, effective_to,
-                                            effective_to_at_end, animated);
+    AnimateAdditiveNumber(parameters, percentage, repeat_count, effective_from,
+                          effective_to, effective_to_at_end, animated);
     at(i)->SetValue(animated);
   }
 }
diff --git a/third_party/blink/renderer/core/svg/svg_number_list.h b/third_party/blink/renderer/core/svg/svg_number_list.h
index 83c7180..713f0730 100644
--- a/third_party/blink/renderer/core/svg/svg_number_list.h
+++ b/third_party/blink/renderer/core/svg/svg_number_list.h
@@ -52,7 +52,7 @@
 
   // SVGPropertyBase:
   void Add(SVGPropertyBase*, SVGElement*) override;
-  void CalculateAnimatedValue(const SVGAnimateElement&,
+  void CalculateAnimatedValue(const SMILAnimationEffectParameters&,
                               float percentage,
                               unsigned repeat_count,
                               SVGPropertyBase* from_value,
diff --git a/third_party/blink/renderer/core/svg/svg_number_optional_number.cc b/third_party/blink/renderer/core/svg/svg_number_optional_number.cc
index c97775e2..e9d4c5a 100644
--- a/third_party/blink/renderer/core/svg/svg_number_optional_number.cc
+++ b/third_party/blink/renderer/core/svg/svg_number_optional_number.cc
@@ -99,7 +99,7 @@
 }
 
 void SVGNumberOptionalNumber::CalculateAnimatedValue(
-    const SVGAnimateElement& animation_element,
+    const SMILAnimationEffectParameters& parameters,
     float percentage,
     unsigned repeat_count,
     SVGPropertyBase* from,
@@ -112,11 +112,11 @@
       To<SVGNumberOptionalNumber>(to_at_end_of_duration);
 
   first_number_->CalculateAnimatedValue(
-      animation_element, percentage, repeat_count, from_number->FirstNumber(),
+      parameters, percentage, repeat_count, from_number->FirstNumber(),
       to_number->FirstNumber(), to_at_end_of_duration_number->FirstNumber(),
       context_element);
   second_number_->CalculateAnimatedValue(
-      animation_element, percentage, repeat_count, from_number->SecondNumber(),
+      parameters, percentage, repeat_count, from_number->SecondNumber(),
       to_number->SecondNumber(), to_at_end_of_duration_number->SecondNumber(),
       context_element);
 }
diff --git a/third_party/blink/renderer/core/svg/svg_number_optional_number.h b/third_party/blink/renderer/core/svg/svg_number_optional_number.h
index b1f26f5a..dcc7cdc 100644
--- a/third_party/blink/renderer/core/svg/svg_number_optional_number.h
+++ b/third_party/blink/renderer/core/svg/svg_number_optional_number.h
@@ -55,7 +55,7 @@
   static constexpr int kInitialValueBits = SVGNumber::kInitialValueBits;
 
   void Add(SVGPropertyBase*, SVGElement*) override;
-  void CalculateAnimatedValue(const SVGAnimateElement&,
+  void CalculateAnimatedValue(const SMILAnimationEffectParameters&,
                               float percentage,
                               unsigned repeat_count,
                               SVGPropertyBase* from,
diff --git a/third_party/blink/renderer/core/svg/svg_path.cc b/third_party/blink/renderer/core/svg/svg_path.cc
index e4468034..3d1cf19 100644
--- a/third_party/blink/renderer/core/svg/svg_path.cc
+++ b/third_party/blink/renderer/core/svg/svg_path.cc
@@ -26,7 +26,7 @@
 #include <memory>
 #include <utility>
 
-#include "third_party/blink/renderer/core/svg/svg_animate_element.h"
+#include "third_party/blink/renderer/core/svg/animation/smil_animation_effect_parameters.h"
 #include "third_party/blink/renderer/core/svg/svg_path_blender.h"
 #include "third_party/blink/renderer/core/svg/svg_path_byte_stream.h"
 #include "third_party/blink/renderer/core/svg/svg_path_byte_stream_builder.h"
@@ -124,15 +124,13 @@
 }
 
 void SVGPath::CalculateAnimatedValue(
-    const SVGAnimateElement& animation_element,
+    const SMILAnimationEffectParameters& parameters,
     float percentage,
     unsigned repeat_count,
     SVGPropertyBase* from_value,
     SVGPropertyBase* to_value,
     SVGPropertyBase* to_at_end_of_duration_value,
     SVGElement*) {
-  bool is_to_animation = animation_element.GetAnimationMode() == kToAnimation;
-
   const auto& to = To<SVGPath>(*to_value);
   const SVGPathByteStream& to_stream = to.ByteStream();
 
@@ -144,7 +142,7 @@
   const SVGPathByteStream* from_stream = &from.ByteStream();
 
   std::unique_ptr<SVGPathByteStream> copy;
-  if (is_to_animation) {
+  if (parameters.is_to_animation) {
     copy = ByteStream().Clone();
     from_stream = copy.get();
   }
@@ -153,7 +151,7 @@
   // list length, fallback to a discrete animation.
   if (from_stream->size() != to_stream.size() && from_stream->size()) {
     if (percentage < 0.5) {
-      if (!is_to_animation) {
+      if (!parameters.is_to_animation) {
         path_value_ = from.PathValue();
         return;
       }
@@ -166,15 +164,15 @@
   std::unique_ptr<SVGPathByteStream> new_stream =
       BlendPathByteStreams(*from_stream, to_stream, percentage);
 
-  if (!is_to_animation) {
+  if (!parameters.is_to_animation) {
     // Handle additive='sum'.
-    if (animation_element.IsAdditive()) {
+    if (parameters.is_additive) {
       new_stream =
           ConditionallyAddPathByteStreams(std::move(new_stream), ByteStream());
     }
 
     // Handle accumulate='sum'.
-    if (repeat_count && animation_element.IsAccumulated()) {
+    if (repeat_count && parameters.is_cumulative) {
       new_stream = ConditionallyAddPathByteStreams(
           std::move(new_stream),
           To<SVGPath>(to_at_end_of_duration_value)->ByteStream(), repeat_count);
diff --git a/third_party/blink/renderer/core/svg/svg_path.h b/third_party/blink/renderer/core/svg/svg_path.h
index cbd3115..4d3711a 100644
--- a/third_party/blink/renderer/core/svg/svg_path.h
+++ b/third_party/blink/renderer/core/svg/svg_path.h
@@ -60,7 +60,7 @@
   SVGParsingError SetValueAsString(const String&);
 
   void Add(SVGPropertyBase*, SVGElement*) override;
-  void CalculateAnimatedValue(const SVGAnimateElement&,
+  void CalculateAnimatedValue(const SMILAnimationEffectParameters&,
                               float percentage,
                               unsigned repeat_count,
                               SVGPropertyBase* from_value,
diff --git a/third_party/blink/renderer/core/svg/svg_point.cc b/third_party/blink/renderer/core/svg/svg_point.cc
index ffcd7771..0bb5f58 100644
--- a/third_party/blink/renderer/core/svg/svg_point.cc
+++ b/third_party/blink/renderer/core/svg/svg_point.cc
@@ -30,7 +30,6 @@
 
 #include "third_party/blink/renderer/core/svg/svg_point.h"
 
-#include "third_party/blink/renderer/core/svg/svg_animate_element.h"
 #include "third_party/blink/renderer/core/svg/svg_parser_utilities.h"
 #include "third_party/blink/renderer/platform/heap/heap.h"
 #include "third_party/blink/renderer/platform/transforms/affine_transform.h"
@@ -101,7 +100,7 @@
 }
 
 void SVGPoint::CalculateAnimatedValue(
-    const SVGAnimateElement& animation_element,
+    const SMILAnimationEffectParameters&,
     float percentage,
     unsigned repeat_count,
     SVGPropertyBase* from_value,
diff --git a/third_party/blink/renderer/core/svg/svg_point.h b/third_party/blink/renderer/core/svg/svg_point.h
index 2a0a642..15f8a04 100644
--- a/third_party/blink/renderer/core/svg/svg_point.h
+++ b/third_party/blink/renderer/core/svg/svg_point.h
@@ -64,7 +64,7 @@
   SVGParsingError SetValueAsString(const String&);
 
   void Add(SVGPropertyBase*, SVGElement*) override;
-  void CalculateAnimatedValue(const SVGAnimateElement&,
+  void CalculateAnimatedValue(const SMILAnimationEffectParameters&,
                               float percentage,
                               unsigned repeat_count,
                               SVGPropertyBase* from,
diff --git a/third_party/blink/renderer/core/svg/svg_point_list.cc b/third_party/blink/renderer/core/svg/svg_point_list.cc
index b18f354c..642d8de 100644
--- a/third_party/blink/renderer/core/svg/svg_point_list.cc
+++ b/third_party/blink/renderer/core/svg/svg_point_list.cc
@@ -20,7 +20,7 @@
 
 #include "third_party/blink/renderer/core/svg/svg_point_list.h"
 
-#include "third_party/blink/renderer/core/svg/svg_animate_element.h"
+#include "third_party/blink/renderer/core/svg/animation/smil_animation_effect_parameters.h"
 #include "third_party/blink/renderer/core/svg/svg_parser_utilities.h"
 #include "third_party/blink/renderer/platform/geometry/float_point.h"
 #include "third_party/blink/renderer/platform/heap/heap.h"
@@ -88,7 +88,7 @@
 }
 
 void SVGPointList::CalculateAnimatedValue(
-    const SVGAnimateElement& animation_element,
+    const SMILAnimationEffectParameters& parameters,
     float percentage,
     unsigned repeat_count,
     SVGPropertyBase* from_value,
@@ -105,9 +105,8 @@
   uint32_t to_at_end_of_duration_list_size =
       to_at_end_of_duration_list->length();
 
-  const bool is_to_animation =
-      animation_element.GetAnimationMode() == kToAnimation;
-  if (!AdjustFromToListValues(from_list, to_list, percentage, is_to_animation))
+  if (!AdjustFromToListValues(from_list, to_list, percentage,
+                              parameters.is_to_animation))
     return;
 
   for (uint32_t i = 0; i < to_point_list_size; ++i) {
@@ -122,12 +121,12 @@
     if (i < to_at_end_of_duration_list_size)
       effective_to_at_end = to_at_end_of_duration_list->at(i)->Value();
 
-    animation_element.AnimateAdditiveNumber(
-        percentage, repeat_count, effective_from.X(), effective_to.X(),
-        effective_to_at_end.X(), animated_x);
-    animation_element.AnimateAdditiveNumber(
-        percentage, repeat_count, effective_from.Y(), effective_to.Y(),
-        effective_to_at_end.Y(), animated_y);
+    AnimateAdditiveNumber(parameters, percentage, repeat_count,
+                          effective_from.X(), effective_to.X(),
+                          effective_to_at_end.X(), animated_x);
+    AnimateAdditiveNumber(parameters, percentage, repeat_count,
+                          effective_from.Y(), effective_to.Y(),
+                          effective_to_at_end.Y(), animated_y);
     at(i)->SetValue(FloatPoint(animated_x, animated_y));
   }
 }
diff --git a/third_party/blink/renderer/core/svg/svg_point_list.h b/third_party/blink/renderer/core/svg/svg_point_list.h
index ffc80d5..2a06854 100644
--- a/third_party/blink/renderer/core/svg/svg_point_list.h
+++ b/third_party/blink/renderer/core/svg/svg_point_list.h
@@ -52,7 +52,7 @@
 
   // SVGPropertyBase:
   void Add(SVGPropertyBase*, SVGElement*) override;
-  void CalculateAnimatedValue(const SVGAnimateElement&,
+  void CalculateAnimatedValue(const SMILAnimationEffectParameters&,
                               float percentage,
                               unsigned repeat_count,
                               SVGPropertyBase* from_value,
diff --git a/third_party/blink/renderer/core/svg/svg_preserve_aspect_ratio.cc b/third_party/blink/renderer/core/svg/svg_preserve_aspect_ratio.cc
index dcad08c..534d0b2 100644
--- a/third_party/blink/renderer/core/svg/svg_preserve_aspect_ratio.cc
+++ b/third_party/blink/renderer/core/svg/svg_preserve_aspect_ratio.cc
@@ -439,7 +439,7 @@
 }
 
 void SVGPreserveAspectRatio::CalculateAnimatedValue(
-    const SVGAnimateElement& animation_element,
+    const SMILAnimationEffectParameters&,
     float percentage,
     unsigned repeat_count,
     SVGPropertyBase* from_value,
diff --git a/third_party/blink/renderer/core/svg/svg_preserve_aspect_ratio.h b/third_party/blink/renderer/core/svg/svg_preserve_aspect_ratio.h
index aa90dba2..422dca9 100644
--- a/third_party/blink/renderer/core/svg/svg_preserve_aspect_ratio.h
+++ b/third_party/blink/renderer/core/svg/svg_preserve_aspect_ratio.h
@@ -87,7 +87,7 @@
   bool Parse(const LChar*& ptr, const LChar* end, bool validate);
 
   void Add(SVGPropertyBase*, SVGElement*) override;
-  void CalculateAnimatedValue(const SVGAnimateElement&,
+  void CalculateAnimatedValue(const SMILAnimationEffectParameters&,
                               float percentage,
                               unsigned repeat_count,
                               SVGPropertyBase* from,
diff --git a/third_party/blink/renderer/core/svg/svg_rect.cc b/third_party/blink/renderer/core/svg/svg_rect.cc
index a1ca2ab1..7ed2f2d 100644
--- a/third_party/blink/renderer/core/svg/svg_rect.cc
+++ b/third_party/blink/renderer/core/svg/svg_rect.cc
@@ -21,7 +21,7 @@
 
 #include "third_party/blink/renderer/core/svg/svg_rect.h"
 
-#include "third_party/blink/renderer/core/svg/svg_animate_element.h"
+#include "third_party/blink/renderer/core/svg/animation/smil_animation_effect_parameters.h"
 #include "third_party/blink/renderer/core/svg/svg_parser_utilities.h"
 #include "third_party/blink/renderer/platform/heap/heap.h"
 #include "third_party/blink/renderer/platform/wtf/text/character_visitor.h"
@@ -91,7 +91,7 @@
 }
 
 void SVGRect::CalculateAnimatedValue(
-    const SVGAnimateElement& animation_element,
+    const SMILAnimationEffectParameters& parameters,
     float percentage,
     unsigned repeat_count,
     SVGPropertyBase* from_value,
@@ -106,18 +106,18 @@
   float animated_y = Y();
   float animated_width = Width();
   float animated_height = Height();
-  animation_element.AnimateAdditiveNumber(
-      percentage, repeat_count, from_rect->X(), to_rect->X(),
-      to_at_end_of_duration_rect->X(), animated_x);
-  animation_element.AnimateAdditiveNumber(
-      percentage, repeat_count, from_rect->Y(), to_rect->Y(),
-      to_at_end_of_duration_rect->Y(), animated_y);
-  animation_element.AnimateAdditiveNumber(
-      percentage, repeat_count, from_rect->Width(), to_rect->Width(),
-      to_at_end_of_duration_rect->Width(), animated_width);
-  animation_element.AnimateAdditiveNumber(
-      percentage, repeat_count, from_rect->Height(), to_rect->Height(),
-      to_at_end_of_duration_rect->Height(), animated_height);
+  AnimateAdditiveNumber(parameters, percentage, repeat_count, from_rect->X(),
+                        to_rect->X(), to_at_end_of_duration_rect->X(),
+                        animated_x);
+  AnimateAdditiveNumber(parameters, percentage, repeat_count, from_rect->Y(),
+                        to_rect->Y(), to_at_end_of_duration_rect->Y(),
+                        animated_y);
+  AnimateAdditiveNumber(parameters, percentage, repeat_count,
+                        from_rect->Width(), to_rect->Width(),
+                        to_at_end_of_duration_rect->Width(), animated_width);
+  AnimateAdditiveNumber(parameters, percentage, repeat_count,
+                        from_rect->Height(), to_rect->Height(),
+                        to_at_end_of_duration_rect->Height(), animated_height);
 
   value_ = FloatRect(animated_x, animated_y, animated_width, animated_height);
 }
diff --git a/third_party/blink/renderer/core/svg/svg_rect.h b/third_party/blink/renderer/core/svg/svg_rect.h
index e36f4c9..73793cb 100644
--- a/third_party/blink/renderer/core/svg/svg_rect.h
+++ b/third_party/blink/renderer/core/svg/svg_rect.h
@@ -61,7 +61,7 @@
   SVGParsingError SetValueAsString(const String&);
 
   void Add(SVGPropertyBase*, SVGElement*) override;
-  void CalculateAnimatedValue(const SVGAnimateElement&,
+  void CalculateAnimatedValue(const SMILAnimationEffectParameters&,
                               float percentage,
                               unsigned repeat_count,
                               SVGPropertyBase* from,
diff --git a/third_party/blink/renderer/core/svg/svg_string.cc b/third_party/blink/renderer/core/svg/svg_string.cc
index dc0dc5d..fa8affd 100644
--- a/third_party/blink/renderer/core/svg/svg_string.cc
+++ b/third_party/blink/renderer/core/svg/svg_string.cc
@@ -25,14 +25,13 @@
   NOTREACHED();
 }
 
-void SVGString::CalculateAnimatedValue(
-    const SVGAnimateElement& animation_element,
-    float percentage,
-    unsigned repeat_count,
-    SVGPropertyBase* from,
-    SVGPropertyBase* to,
-    SVGPropertyBase*,
-    SVGElement*) {
+void SVGString::CalculateAnimatedValue(const SMILAnimationEffectParameters&,
+                                       float percentage,
+                                       unsigned repeat_count,
+                                       SVGPropertyBase* from,
+                                       SVGPropertyBase* to,
+                                       SVGPropertyBase*,
+                                       SVGElement*) {
   NOTREACHED();
 }
 
diff --git a/third_party/blink/renderer/core/svg/svg_string.h b/third_party/blink/renderer/core/svg/svg_string.h
index 9125d80..c91e5db 100644
--- a/third_party/blink/renderer/core/svg/svg_string.h
+++ b/third_party/blink/renderer/core/svg/svg_string.h
@@ -58,7 +58,7 @@
   }
 
   void Add(SVGPropertyBase*, SVGElement*) override;
-  void CalculateAnimatedValue(const SVGAnimateElement&,
+  void CalculateAnimatedValue(const SMILAnimationEffectParameters&,
                               float percentage,
                               unsigned repeat_count,
                               SVGPropertyBase* from,
diff --git a/third_party/blink/renderer/core/svg/svg_string_list.cc b/third_party/blink/renderer/core/svg/svg_string_list.cc
index b7411e4..f2237ed 100644
--- a/third_party/blink/renderer/core/svg/svg_string_list.cc
+++ b/third_party/blink/renderer/core/svg/svg_string_list.cc
@@ -111,13 +111,14 @@
   NOTREACHED();
 }
 
-void SVGStringListBase::CalculateAnimatedValue(const SVGAnimateElement&,
-                                               float,
-                                               unsigned,
-                                               SVGPropertyBase*,
-                                               SVGPropertyBase*,
-                                               SVGPropertyBase*,
-                                               SVGElement*) {
+void SVGStringListBase::CalculateAnimatedValue(
+    const SMILAnimationEffectParameters&,
+    float,
+    unsigned,
+    SVGPropertyBase*,
+    SVGPropertyBase*,
+    SVGPropertyBase*,
+    SVGElement*) {
   // SVGStringList is never animated.
   NOTREACHED();
 }
diff --git a/third_party/blink/renderer/core/svg/svg_string_list.h b/third_party/blink/renderer/core/svg/svg_string_list.h
index f8f30ab..8c90901 100644
--- a/third_party/blink/renderer/core/svg/svg_string_list.h
+++ b/third_party/blink/renderer/core/svg/svg_string_list.h
@@ -71,7 +71,7 @@
 
   // SVGPropertyBase:
   void Add(SVGPropertyBase*, SVGElement*) override;
-  void CalculateAnimatedValue(const SVGAnimateElement&,
+  void CalculateAnimatedValue(const SMILAnimationEffectParameters&,
                               float percentage,
                               unsigned repeat_count,
                               SVGPropertyBase* from_value,
diff --git a/third_party/blink/renderer/core/svg/svg_transform.cc b/third_party/blink/renderer/core/svg/svg_transform.cc
index 350da932..b202f94 100644
--- a/third_party/blink/renderer/core/svg/svg_transform.cc
+++ b/third_party/blink/renderer/core/svg/svg_transform.cc
@@ -222,7 +222,7 @@
   NOTREACHED();
 }
 
-void SVGTransform::CalculateAnimatedValue(const SVGAnimateElement&,
+void SVGTransform::CalculateAnimatedValue(const SMILAnimationEffectParameters&,
                                           float,
                                           unsigned,
                                           SVGPropertyBase*,
diff --git a/third_party/blink/renderer/core/svg/svg_transform.h b/third_party/blink/renderer/core/svg/svg_transform.h
index 74d0336f..1e06482b 100644
--- a/third_party/blink/renderer/core/svg/svg_transform.h
+++ b/third_party/blink/renderer/core/svg/svg_transform.h
@@ -89,7 +89,7 @@
   String ValueAsString() const override;
 
   void Add(SVGPropertyBase*, SVGElement*) override;
-  void CalculateAnimatedValue(const SVGAnimateElement&,
+  void CalculateAnimatedValue(const SMILAnimationEffectParameters&,
                               float percentage,
                               unsigned repeat_count,
                               SVGPropertyBase* from,
diff --git a/third_party/blink/renderer/core/svg/svg_transform_list.cc b/third_party/blink/renderer/core/svg/svg_transform_list.cc
index 4e8fcf7..cfdcb9b2 100644
--- a/third_party/blink/renderer/core/svg/svg_transform_list.cc
+++ b/third_party/blink/renderer/core/svg/svg_transform_list.cc
@@ -29,10 +29,11 @@
 #include "third_party/blink/renderer/core/css/css_numeric_literal_value.h"
 #include "third_party/blink/renderer/core/css/css_primitive_value.h"
 #include "third_party/blink/renderer/core/css/css_value_list.h"
-#include "third_party/blink/renderer/core/svg/svg_animate_element.h"
+#include "third_party/blink/renderer/core/svg/animation/smil_animation_effect_parameters.h"
 #include "third_party/blink/renderer/core/svg/svg_parser_utilities.h"
 #include "third_party/blink/renderer/core/svg/svg_transform_distance.h"
 #include "third_party/blink/renderer/platform/heap/heap.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
 #include "third_party/blink/renderer/platform/wtf/text/parsing_utilities.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
 
@@ -428,7 +429,7 @@
 }
 
 void SVGTransformList::CalculateAnimatedValue(
-    const SVGAnimateElement& animation_element,
+    const SMILAnimationEffectParameters& parameters,
     float percentage,
     unsigned repeat_count,
     SVGPropertyBase* from_value,
@@ -467,17 +468,17 @@
       SVGTransformDistance(effective_from, to_transform)
           .ScaledDistance(percentage)
           .AddToSVGTransform(effective_from);
-  if (animation_element.GetAnimationMode() == kToAnimation) {
+  if (parameters.is_to_animation) {
     Clear();
     Append(current_transform);
     return;
   }
   // Never resize the animatedTransformList to the toList size, instead either
   // clear the list or append to it.
-  if (!IsEmpty() && !animation_element.IsAdditive())
+  if (!IsEmpty() && !parameters.is_additive)
     Clear();
 
-  if (repeat_count && animation_element.IsAccumulated()) {
+  if (repeat_count && parameters.is_cumulative) {
     SVGTransform* effective_to_at_end =
         !to_at_end_of_duration_list->IsEmpty()
             ? to_at_end_of_duration_list->at(0)
diff --git a/third_party/blink/renderer/core/svg/svg_transform_list.h b/third_party/blink/renderer/core/svg/svg_transform_list.h
index a699cb2..ccfe63a5 100644
--- a/third_party/blink/renderer/core/svg/svg_transform_list.h
+++ b/third_party/blink/renderer/core/svg/svg_transform_list.h
@@ -59,7 +59,7 @@
   bool Parse(const LChar*& ptr, const LChar* end);
 
   void Add(SVGPropertyBase*, SVGElement*) override;
-  void CalculateAnimatedValue(const SVGAnimateElement&,
+  void CalculateAnimatedValue(const SMILAnimationEffectParameters&,
                               float percentage,
                               unsigned repeat_count,
                               SVGPropertyBase* from_value,
diff --git a/third_party/blink/renderer/core/testing/mock_clipboard_host.cc b/third_party/blink/renderer/core/testing/mock_clipboard_host.cc
index c25fc22..120840e69 100644
--- a/third_party/blink/renderer/core/testing/mock_clipboard_host.cc
+++ b/third_party/blink/renderer/core/testing/mock_clipboard_host.cc
@@ -20,6 +20,7 @@
 void MockClipboardHost::Reset() {
   plain_text_ = g_empty_string;
   html_text_ = g_empty_string;
+  svg_text_ = g_empty_string;
   url_ = KURL();
   image_.reset();
   custom_data_.clear();
@@ -41,6 +42,8 @@
     types.push_back("text/plain");
   if (!html_text_.IsEmpty())
     types.push_back("text/html");
+  if (!svg_text_.IsEmpty())
+    types.push_back("image/svg+xml");
   if (!image_.isNull())
     types.push_back("image/png");
   for (auto& it : custom_data_) {
@@ -82,6 +85,11 @@
   std::move(callback).Run(html_text_, url_, 0, html_text_.length());
 }
 
+void MockClipboardHost::ReadSvg(mojom::ClipboardBuffer clipboard_buffer,
+                                ReadSvgCallback callback) {
+  std::move(callback).Run(svg_text_);
+}
+
 void MockClipboardHost::ReadRtf(mojom::ClipboardBuffer clipboard_buffer,
                                 ReadRtfCallback callback) {
   std::move(callback).Run(g_empty_string);
@@ -113,6 +121,12 @@
   url_ = url;
 }
 
+void MockClipboardHost::WriteSvg(const String& markup) {
+  if (needs_reset_)
+    Reset();
+  svg_text_ = markup;
+}
+
 void MockClipboardHost::WriteSmartPasteMarker() {
   if (needs_reset_)
     Reset();
diff --git a/third_party/blink/renderer/core/testing/mock_clipboard_host.h b/third_party/blink/renderer/core/testing/mock_clipboard_host.h
index e15f5e44..7df1da1 100644
--- a/third_party/blink/renderer/core/testing/mock_clipboard_host.h
+++ b/third_party/blink/renderer/core/testing/mock_clipboard_host.h
@@ -37,6 +37,8 @@
                 ReadTextCallback callback) override;
   void ReadHtml(mojom::ClipboardBuffer clipboard_buffer,
                 ReadHtmlCallback callback) override;
+  void ReadSvg(mojom::ClipboardBuffer clipboard_buffer,
+               ReadSvgCallback callback) override;
   void ReadRtf(mojom::ClipboardBuffer clipboard_buffer,
                ReadRtfCallback callback) override;
   void ReadImage(mojom::ClipboardBuffer clipboard_buffer,
@@ -46,6 +48,7 @@
                       ReadCustomDataCallback callback) override;
   void WriteText(const String& text) override;
   void WriteHtml(const String& markup, const KURL& url) override;
+  void WriteSvg(const String& markup) override;
   void WriteSmartPasteMarker() override;
   void WriteCustomData(const HashMap<String, String>& data) override;
   void WriteBookmark(const String& url, const String& title) override;
@@ -59,6 +62,7 @@
   uint64_t sequence_number_ = 0;
   String plain_text_ = g_empty_string;
   String html_text_ = g_empty_string;
+  String svg_text_ = g_empty_string;
   KURL url_;
   SkBitmap image_;
   HashMap<String, String> custom_data_;
diff --git a/third_party/blink/renderer/modules/canvas/offscreencanvas2d/offscreen_canvas_rendering_context_2d.cc b/third_party/blink/renderer/modules/canvas/offscreencanvas2d/offscreen_canvas_rendering_context_2d.cc
index ad943128..ce8921d 100644
--- a/third_party/blink/renderer/modules/canvas/offscreencanvas2d/offscreen_canvas_rendering_context_2d.cc
+++ b/third_party/blink/renderer/modules/canvas/offscreencanvas2d/offscreen_canvas_rendering_context_2d.cc
@@ -576,8 +576,14 @@
       [](const SkIRect& rect)  // overdraw test lambda
       { return false; },
       bounds, paint_type, CanvasRenderingContext2DState::kNoImage);
-  paint_canvas->restoreToCount(save_count);
-  ValidateStateStack();
+
+  // |paint_canvas| maybe rese during Draw. If that happens,
+  // GetOrCreatePaintCanvas will create a new |paint_canvas| and return a new
+  // address. In this case, there is no need to call |restoreToCount|.
+  if (paint_canvas == GetOrCreatePaintCanvas()) {
+    paint_canvas->restoreToCount(save_count);
+    ValidateStateStack();
+  }
 }
 
 TextMetrics* OffscreenCanvasRenderingContext2D::measureText(
diff --git a/third_party/blink/renderer/modules/mediastream/user_media_processor.cc b/third_party/blink/renderer/modules/mediastream/user_media_processor.cc
index 375e90f..b4361a6 100644
--- a/third_party/blink/renderer/modules/mediastream/user_media_processor.cc
+++ b/third_party/blink/renderer/modules/mediastream/user_media_processor.cc
@@ -720,13 +720,13 @@
   // Create a copy of the MediaStreamSource objects that are
   // associated to the same audio device capture based on its device ID.
   HeapVector<Member<MediaStreamSource>> matching_sources;
-  std::copy_if(local_sources_.begin(), local_sources_.end(),
-               std::back_inserter(matching_sources),
-               [&device_id](MediaStreamSource* source) {
-                 DCHECK(source);
-                 return source->GetType() == MediaStreamSource::kTypeAudio &&
-                        source->Id().Utf8() == device_id;
-               });
+  for (const auto& source : local_sources_) {
+    MediaStreamSource* source_copy = source;
+    if (source_copy->GetType() == MediaStreamSource::kTypeAudio &&
+        source_copy->Id().Utf8() == device_id) {
+      matching_sources.push_back(source_copy);
+    }
+  }
 
   // Return the session ID associated to the source that has the same settings
   // that have been previously selected, if one exists.
@@ -1722,6 +1722,7 @@
 }
 
 void UserMediaProcessor::StopAllProcessing() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   if (current_request_info_) {
     switch (current_request_info_->state()) {
       case RequestInfo::State::SENT_FOR_GENERATION:
@@ -1799,6 +1800,7 @@
 }
 
 bool UserMediaProcessor::HasActiveSources() const {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   return !local_sources_.IsEmpty();
 }
 
diff --git a/third_party/blink/renderer/modules/webcodecs/encoded_video_chunk.cc b/third_party/blink/renderer/modules/webcodecs/encoded_video_chunk.cc
index d88a46ff..86677a08b 100644
--- a/third_party/blink/renderer/modules/webcodecs/encoded_video_chunk.cc
+++ b/third_party/blink/renderer/modules/webcodecs/encoded_video_chunk.cc
@@ -17,8 +17,10 @@
   EncodedVideoMetadata metadata;
   metadata.timestamp = base::TimeDelta::FromMicroseconds(init->timestamp());
   metadata.key_frame = (init->type() == "key");
-  if (init->hasDuration())
-    metadata.duration = base::TimeDelta::FromMicroseconds(init->duration());
+  if (init->hasDurationNonNull()) {
+    metadata.duration =
+        base::TimeDelta::FromMicroseconds(init->durationNonNull());
+  }
   DOMArrayPiece piece(init->data());
 
   // A full copy of the data happens here.
diff --git a/third_party/blink/renderer/modules/webcodecs/encoded_video_chunk_test.cc b/third_party/blink/renderer/modules/webcodecs/encoded_video_chunk_test.cc
index 33c2646..ec4ad8d 100644
--- a/third_party/blink/renderer/modules/webcodecs/encoded_video_chunk_test.cc
+++ b/third_party/blink/renderer/modules/webcodecs/encoded_video_chunk_test.cc
@@ -5,7 +5,7 @@
 #include "third_party/blink/renderer/modules/webcodecs/encoded_video_chunk.h"
 
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/blink/renderer/modules/webcodecs/encoded_video_chunk_init.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_encoded_video_chunk_init.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
 
 namespace blink {
diff --git a/third_party/blink/renderer/platform/heap/heap_allocator.h b/third_party/blink/renderer/platform/heap/heap_allocator.h
index 603077fe..be0e583 100644
--- a/third_party/blink/renderer/platform/heap/heap_allocator.h
+++ b/third_party/blink/renderer/platform/heap/heap_allocator.h
@@ -108,14 +108,6 @@
     MarkingVisitor::WriteBarrier(slot);
   }
 
-  template <typename HashTable, typename T>
-  static void BackingWriteBarrierForHashTable(T** slot) {
-    if (MarkingVisitor::WriteBarrier(slot)) {
-      AddMovingCallback<HashTable>(
-          static_cast<typename HashTable::ValueType*>(*slot));
-    }
-  }
-
   template <typename Return, typename Metadata>
   static Return Malloc(size_t size, const char* type_name) {
     return reinterpret_cast<Return>(
@@ -272,22 +264,6 @@
     return address;
   }
 
-  template <
-      typename HashTable,
-      std::enable_if_t<HashTable::ValueTraits::kHasMovingCallback>* = nullptr>
-  static void AddMovingCallback(typename HashTable::ValueType* memory) {
-    ThreadState* thread_state = ThreadState::Current();
-    auto* visitor = thread_state->CurrentVisitor();
-    DCHECK(visitor);
-    HashTable::ValueTraits::template RegisterMovingCallback<HashTable>(visitor,
-                                                                       memory);
-  }
-
-  template <
-      typename HashTable,
-      std::enable_if_t<!HashTable::ValueTraits::kHasMovingCallback>* = nullptr>
-  static void AddMovingCallback(typename HashTable::ValueType*) {}
-
   static void BackingFree(void*);
   static bool BackingExpand(void*, size_t);
   static bool BackingShrink(void*,
@@ -474,48 +450,6 @@
 struct GCInfoTrait<HeapHashSet<T, U, V>>
     : public GCInfoTrait<HashSet<T, U, V, HeapAllocator>> {};
 
-// IMPORTANT! Do not use this class, unless you need to work around a
-// HeapLinkedHashSet issue. Contact chrome-memory-tok@ if you do.
-// TODO(bartekn): Remove once fully transitioned to LinkedHashSet.
-template <typename ValueArg,
-          typename HashArg = typename DefaultHash<ValueArg>::Hash,
-          typename TraitsArg = HashTraits<ValueArg>>
-class HeapLegacyLinkedHashSet
-    : public LegacyLinkedHashSet<ValueArg, HashArg, TraitsArg, HeapAllocator> {
-  IS_GARBAGE_COLLECTED_CONTAINER_TYPE();
-  DISALLOW_NEW();
-  // HeapLegacyLinkedHashSet is using custom callbacks for compaction that rely
-  // on the fact that the container itself does not move.
-  DISALLOW_IN_CONTAINER();
-
-  static void CheckType() {
-    static_assert(
-        internal::IsMemberOrWeakMemberType<ValueArg>,
-        "HeapLegacyLinkedHashSet supports only Member and WeakMember.");
-    static_assert(
-        IsAllowedInContainer<ValueArg>::value,
-        "Not allowed to directly nest type. Use Member<> indirection instead.");
-    static_assert(
-        WTF::IsTraceable<ValueArg>::value,
-        "For sets without traceable elements, use LegacyLinkedHashSet<> "
-        "instead of HeapLegacyLinkedHashSet<>.");
-  }
-
- public:
-  template <typename>
-  static void* AllocateObject(size_t size) {
-    return ThreadHeap::Allocate<
-        HeapLegacyLinkedHashSet<ValueArg, HashArg, TraitsArg>>(size);
-  }
-
-  HeapLegacyLinkedHashSet() { CheckType(); }
-};
-
-// TODO(bartekn): Remove once fully transitioned to LinkedHashSet.
-template <typename T, typename U, typename V>
-struct GCInfoTrait<HeapLegacyLinkedHashSet<T, U, V>>
-    : public GCInfoTrait<LegacyLinkedHashSet<T, U, V, HeapAllocator>> {};
-
 template <typename ValueArg, typename TraitsArg = HashTraits<ValueArg>>
 class HeapLinkedHashSet
     : public LinkedHashSet<ValueArg, TraitsArg, HeapAllocator> {
diff --git a/third_party/blink/renderer/platform/heap/marking_visitor.cc b/third_party/blink/renderer/platform/heap/marking_visitor.cc
index 8a7dc02..41907b6 100644
--- a/third_party/blink/renderer/platform/heap/marking_visitor.cc
+++ b/third_party/blink/renderer/platform/heap/marking_visitor.cc
@@ -45,16 +45,6 @@
   weak_callback_worklist_.Push({callback, object});
 }
 
-void MarkingVisitorBase::RegisterBackingStoreCallback(
-    const void* backing,
-    MovingObjectCallback callback) {
-  if (marking_mode_ != kGlobalMarkingWithCompaction)
-    return;
-  if (Heap().ShouldRegisterMovingAddress()) {
-    backing_store_callback_worklist_.Push({backing, callback});
-  }
-}
-
 void MarkingVisitorBase::RegisterMovableSlot(const void* const* slot) {
   if (marking_mode_ != kGlobalMarkingWithCompaction)
     return;
diff --git a/third_party/blink/renderer/platform/heap/marking_visitor.h b/third_party/blink/renderer/platform/heap/marking_visitor.h
index 667548d..146a473 100644
--- a/third_party/blink/renderer/platform/heap/marking_visitor.h
+++ b/third_party/blink/renderer/platform/heap/marking_visitor.h
@@ -49,15 +49,6 @@
   // to be in construction.
   void DynamicallyMarkAddress(ConstAddress);
 
-  // This callback mechanism is needed to account for backing store objects
-  // containing intra-object pointers, all of which must be relocated/rebased
-  // with respect to the moved-to location.
-  //
-  // |HeapLegacyLinkedHashSet<>| is currently the only abstraction which
-  // relies on this feature.
-  // TODO(bartekn): Remove once fully transitioned to LinkedHashSet.
-  void RegisterBackingStoreCallback(const void*, MovingObjectCallback) final;
-
   void RegisterMovableSlot(const void* const*) final;
 
   void RegisterWeakCallback(WeakCallback, const void*) final;
diff --git a/third_party/blink/renderer/platform/heap/test/concurrent_marking_test.cc b/third_party/blink/renderer/platform/heap/test/concurrent_marking_test.cc
index 4313337f..4f92244 100644
--- a/third_party/blink/renderer/platform/heap/test/concurrent_marking_test.cc
+++ b/third_party/blink/renderer/platform/heap/test/concurrent_marking_test.cc
@@ -192,37 +192,6 @@
   SwapCollections<HeapHashSet<Member<IntegerObject>>>();
 }
 
-// HeapLegacyLinkedHashSet
-template <typename T>
-class HeapLegacyLinkedHashSetAdapter : public HeapLegacyLinkedHashSet<T> {
- public:
-  ALWAYS_INLINE void swap(HeapLegacyLinkedHashSetAdapter<T>& other) {
-    HeapLegacyLinkedHashSet<T>::Swap(other);
-  }
-};
-
-TEST_F(ConcurrentMarkingTest, AddToLegacyLinkedHashSet) {
-  AddToCollection<HeapLegacyLinkedHashSetAdapter<Member<IntegerObject>>>();
-}
-TEST_F(ConcurrentMarkingTest, RemoveFromBeginningOfLegacyLinkedHashSet) {
-  RemoveFromBeginningOfCollection<
-      HeapLegacyLinkedHashSetAdapter<Member<IntegerObject>>>();
-}
-TEST_F(ConcurrentMarkingTest, RemoveFromMiddleOfLegacyLinkedHashSet) {
-  RemoveFromMiddleOfCollection<
-      HeapLegacyLinkedHashSetAdapter<Member<IntegerObject>>>();
-}
-TEST_F(ConcurrentMarkingTest, RemoveFromEndOfLegacyLinkedHashSet) {
-  RemoveFromEndOfCollection<
-      HeapLegacyLinkedHashSetAdapter<Member<IntegerObject>>>();
-}
-TEST_F(ConcurrentMarkingTest, ClearLegacyLinkedHashSet) {
-  ClearCollection<HeapLegacyLinkedHashSetAdapter<Member<IntegerObject>>>();
-}
-TEST_F(ConcurrentMarkingTest, SwapLegacyLinkedHashSet) {
-  SwapCollections<HeapLegacyLinkedHashSetAdapter<Member<IntegerObject>>>();
-}
-
 // HeapLinkedHashSet
 template <typename T>
 class HeapLinkedHashSetAdapter : public HeapLinkedHashSet<T> {
diff --git a/third_party/blink/renderer/platform/heap/test/heap_compact_test.cc b/third_party/blink/renderer/platform/heap/test/heap_compact_test.cc
index f562c63..7d9c1bc 100644
--- a/third_party/blink/renderer/platform/heap/test/heap_compact_test.cc
+++ b/third_party/blink/renderer/platform/heap/test/heap_compact_test.cc
@@ -224,119 +224,6 @@
     EXPECT_EQ(static_cast<int>(7 - i), deque->at(i)->Value());
 }
 
-TEST_F(HeapCompactTest, CompactLegacyLinkedHashSet) {
-  using OrderedHashSet = HeapLegacyLinkedHashSet<Member<IntWrapper>>;
-  Persistent<OrderedHashSet> set = MakeGarbageCollected<OrderedHashSet>();
-  for (int i = 0; i < 13; ++i) {
-    IntWrapper* value = IntWrapper::Create(i, HashTablesAreCompacted);
-    set->insert(value);
-  }
-  EXPECT_EQ(13u, set->size());
-
-  int expected = 0;
-  for (IntWrapper* v : *set) {
-    EXPECT_EQ(expected, v->Value());
-    expected++;
-  }
-
-  PerformHeapCompaction();
-  EXPECT_TRUE(IntWrapper::did_verify_at_least_once);
-
-  expected = 0;
-  for (IntWrapper* v : *set) {
-    EXPECT_EQ(expected, v->Value());
-    expected++;
-  }
-}
-
-TEST_F(HeapCompactTest, CompactLegacyLinkedHashSetVector) {
-  using OrderedHashSet = HeapLegacyLinkedHashSet<Member<IntVector>>;
-  Persistent<OrderedHashSet> set = MakeGarbageCollected<OrderedHashSet>();
-  for (int i = 0; i < 13; ++i) {
-    IntWrapper* value = IntWrapper::Create(i);
-    IntVector* vector = MakeGarbageCollected<IntVector>(19, value);
-    set->insert(vector);
-  }
-  EXPECT_EQ(13u, set->size());
-
-  int expected = 0;
-  for (IntVector* v : *set) {
-    EXPECT_EQ(expected, (*v)[0]->Value());
-    expected++;
-  }
-
-  PerformHeapCompaction();
-  EXPECT_TRUE(IntWrapper::did_verify_at_least_once);
-
-  expected = 0;
-  for (IntVector* v : *set) {
-    EXPECT_EQ(expected, (*v)[0]->Value());
-    expected++;
-  }
-}
-
-TEST_F(HeapCompactTest, CompactLegacyLinkedHashSetMap) {
-  using Inner = HeapHashSet<Member<IntWrapper>>;
-  using OrderedHashSet = HeapLegacyLinkedHashSet<Member<Inner>>;
-
-  Persistent<OrderedHashSet> set = MakeGarbageCollected<OrderedHashSet>();
-  for (int i = 0; i < 13; ++i) {
-    IntWrapper* value = IntWrapper::Create(i);
-    Inner* inner = MakeGarbageCollected<Inner>();
-    inner->insert(value);
-    set->insert(inner);
-  }
-  EXPECT_EQ(13u, set->size());
-
-  int expected = 0;
-  for (const Inner* v : *set) {
-    EXPECT_EQ(1u, v->size());
-    EXPECT_EQ(expected, (*v->begin())->Value());
-    expected++;
-  }
-
-  PerformHeapCompaction();
-  EXPECT_TRUE(IntWrapper::did_verify_at_least_once);
-
-  expected = 0;
-  for (const Inner* v : *set) {
-    EXPECT_EQ(1u, v->size());
-    EXPECT_EQ(expected, (*v->begin())->Value());
-    expected++;
-  }
-}
-
-TEST_F(HeapCompactTest, CompactLegacyLinkedHashSetNested) {
-  using Inner = HeapLegacyLinkedHashSet<Member<IntWrapper>>;
-  using OrderedHashSet = HeapLegacyLinkedHashSet<Member<Inner>>;
-
-  Persistent<OrderedHashSet> set = MakeGarbageCollected<OrderedHashSet>();
-  for (int i = 0; i < 13; ++i) {
-    IntWrapper* value = IntWrapper::Create(i);
-    Inner* inner = MakeGarbageCollected<Inner>();
-    inner->insert(value);
-    set->insert(inner);
-  }
-  EXPECT_EQ(13u, set->size());
-
-  int expected = 0;
-  for (const Inner* v : *set) {
-    EXPECT_EQ(1u, v->size());
-    EXPECT_EQ(expected, (*v->begin())->Value());
-    expected++;
-  }
-
-  PerformHeapCompaction();
-  EXPECT_TRUE(IntWrapper::did_verify_at_least_once);
-
-  expected = 0;
-  for (const Inner* v : *set) {
-    EXPECT_EQ(1u, v->size());
-    EXPECT_EQ(expected, (*v->begin())->Value());
-    expected++;
-  }
-}
-
 TEST_F(HeapCompactTest, CompactLinkedHashSet) {
   using OrderedHashSet = HeapLinkedHashSet<Member<IntWrapper>>;
   Persistent<OrderedHashSet> set = MakeGarbageCollected<OrderedHashSet>();
diff --git a/third_party/blink/renderer/platform/heap/test/heap_test.cc b/third_party/blink/renderer/platform/heap/test/heap_test.cc
index f0fb5cb..4d20708 100644
--- a/third_party/blink/renderer/platform/heap/test/heap_test.cc
+++ b/third_party/blink/renderer/platform/heap/test/heap_test.cc
@@ -3098,10 +3098,6 @@
 
 TEST_F(HeapTest, HeapWeakLinkedHashSet) {
   ClearOutOldGarbage();
-  OrderedSetHelper<HeapLegacyLinkedHashSet<Member<IntWrapper>>>(true);
-  ClearOutOldGarbage();
-  OrderedSetHelper<HeapLegacyLinkedHashSet<WeakMember<IntWrapper>>>(false);
-  ClearOutOldGarbage();
   OrderedSetHelper<HeapListHashSet<Member<IntWrapper>>>(true);
   ClearOutOldGarbage();
   OrderedSetHelper<HeapLinkedHashSet<Member<IntWrapper>>>(true);
@@ -3134,11 +3130,6 @@
 
 TEST_F(HeapTest, ClearInWeakProcessing) {
   ClearOutOldGarbage();
-  ClearInWeakProcessingHelper<HeapLegacyLinkedHashSet<Member<IntWrapper>>>();
-  ClearOutOldGarbage();
-  ClearInWeakProcessingHelper<
-      HeapLegacyLinkedHashSet<WeakMember<IntWrapper>>>();
-  ClearOutOldGarbage();
   ClearInWeakProcessingHelper<HeapLinkedHashSet<Member<IntWrapper>>>();
   ClearOutOldGarbage();
   ClearInWeakProcessingHelper<HeapLinkedHashSet<WeakMember<IntWrapper>>>();
@@ -3265,10 +3256,6 @@
 typedef HeapHashSet<PairWeakUnwrapped> WeakUnwrappedSet;
 typedef HeapHashSet<PairStrongWeak> StrongWeakSet;
 typedef HeapHashSet<PairUnwrappedWeak> UnwrappedWeakSet;
-typedef HeapLegacyLinkedHashSet<PairWeakStrong> WeakStrongLegacyLinkedSet;
-typedef HeapLegacyLinkedHashSet<PairWeakUnwrapped> WeakUnwrappedLegacyLinkedSet;
-typedef HeapLegacyLinkedHashSet<PairStrongWeak> StrongWeakLegacyLinkedSet;
-typedef HeapLegacyLinkedHashSet<PairUnwrappedWeak> UnwrappedWeakLegacyLinkedSet;
 typedef HeapLinkedHashSet<PairWeakStrong> WeakStrongLinkedSet;
 typedef HeapLinkedHashSet<PairWeakUnwrapped> WeakUnwrappedLinkedSet;
 typedef HeapLinkedHashSet<PairStrongWeak> StrongWeakLinkedSet;
@@ -3352,7 +3339,6 @@
   typedef HeapHashMap<Member<IntWrapper>, WeakMember<IntWrapper>> StrongWeak;
   typedef HeapHashMap<WeakMember<IntWrapper>, WeakMember<IntWrapper>> WeakWeak;
   typedef HeapHashSet<WeakMember<IntWrapper>> WeakSet;
-  typedef HeapLegacyLinkedHashSet<WeakMember<IntWrapper>> WeakOrderedLegacySet;
   typedef HeapLinkedHashSet<WeakMember<IntWrapper>> WeakOrderedSet;
 
   ClearOutOldGarbage();
@@ -3362,9 +3348,8 @@
   const int kWeakWeakIndex = 2;
   const int kNumberOfMapIndices = 3;
   const int kWeakSetIndex = 3;
-  const int kWeakOrderedLegacySetIndex = 4;
-  const int kWeakOrderedSetIndex = 5;
-  const int kNumberOfCollections = 6;
+  const int kWeakOrderedSetIndex = 4;
+  const int kNumberOfCollections = 5;
 
   for (int test_run = 0; test_run < 4; test_run++) {
     for (int collection_number = 0; collection_number < kNumberOfCollections;
@@ -3385,8 +3370,6 @@
       Persistent<WeakWeak> weak_weak = MakeGarbageCollected<WeakWeak>();
 
       Persistent<WeakSet> weak_set = MakeGarbageCollected<WeakSet>();
-      Persistent<WeakOrderedLegacySet> weak_ordered_legacy_set =
-          MakeGarbageCollected<WeakOrderedLegacySet>();
       Persistent<WeakOrderedSet> weak_ordered_set =
           MakeGarbageCollected<WeakOrderedSet>();
 
@@ -3401,7 +3384,6 @@
         strong_weak->insert(wrapped2, wrapped);
         weak_weak->insert(wrapped, wrapped2);
         weak_set->insert(wrapped);
-        weak_ordered_legacy_set->insert(wrapped);
         weak_ordered_set->insert(wrapped);
       }
 
@@ -3409,7 +3391,6 @@
       EXPECT_EQ(64u, strong_weak->size());
       EXPECT_EQ(64u, weak_weak->size());
       EXPECT_EQ(64u, weak_set->size());
-      EXPECT_EQ(64u, weak_ordered_legacy_set->size());
       EXPECT_EQ(64u, weak_ordered_set->size());
 
       // Collect garbage. This should change nothing since we are keeping
@@ -3420,7 +3401,6 @@
       EXPECT_EQ(64u, strong_weak->size());
       EXPECT_EQ(64u, weak_weak->size());
       EXPECT_EQ(64u, weak_set->size());
-      EXPECT_EQ(64u, weak_ordered_legacy_set->size());
       EXPECT_EQ(64u, weak_ordered_set->size());
 
       for (int i = 0; i < 128; i += 2) {
@@ -3430,7 +3410,6 @@
         EXPECT_EQ(wrapped, strong_weak->at(wrapped2));
         EXPECT_EQ(wrapped2, weak_weak->at(wrapped));
         EXPECT_TRUE(weak_set->Contains(wrapped));
-        EXPECT_TRUE(weak_ordered_legacy_set->Contains(wrapped));
         EXPECT_TRUE(weak_ordered_set->Contains(wrapped));
       }
 
@@ -3445,8 +3424,6 @@
         weak_weak->clear();
       if (collection_number != kWeakSetIndex)
         weak_set->clear();
-      if (collection_number != kWeakOrderedLegacySetIndex)
-        weak_ordered_legacy_set->clear();
       if (collection_number != kWeakOrderedSetIndex)
         weak_ordered_set->clear();
 
@@ -3455,8 +3432,7 @@
         StrongWeak::iterator it2 = strong_weak->begin();
         WeakWeak::iterator it3 = weak_weak->begin();
         WeakSet::iterator it4 = weak_set->begin();
-        WeakOrderedLegacySet::iterator it5 = weak_ordered_legacy_set->begin();
-        WeakOrderedSet::iterator it6 = weak_ordered_set->begin();
+        WeakOrderedSet::iterator it5 = weak_ordered_set->begin();
         // Collect garbage. This should change nothing since the
         // iterators make the collections strong.
         ConservativelyCollectGarbage();
@@ -3472,12 +3448,9 @@
         } else if (collection_number == kWeakSetIndex) {
           EXPECT_EQ(64u, weak_set->size());
           SetIteratorCheck(it4, weak_set->end(), 64);
-        } else if (collection_number == kWeakOrderedLegacySetIndex) {
-          EXPECT_EQ(64u, weak_ordered_legacy_set->size());
-          SetIteratorCheck(it5, weak_ordered_legacy_set->end(), 64);
         } else if (collection_number == kWeakOrderedSetIndex) {
           EXPECT_EQ(64u, weak_ordered_set->size());
-          SetIteratorCheck(it6, weak_ordered_set->end(), 64);
+          SetIteratorCheck(it5, weak_ordered_set->end(), 64);
         }
       } else {
         // Collect garbage. This causes weak processing to remove
@@ -3518,14 +3491,6 @@
               weak_set->erase(keep_numbers_alive->at(i));
             else
               count++;
-          } else if (collection_number == kWeakOrderedLegacySetIndex &&
-                     first_alive) {
-            ASSERT_TRUE(
-                weak_ordered_legacy_set->Contains(keep_numbers_alive->at(i)));
-            if (delete_afterwards)
-              weak_ordered_legacy_set->erase(keep_numbers_alive->at(i));
-            else
-              count++;
           } else if (collection_number == kWeakOrderedSetIndex && first_alive) {
             ASSERT_TRUE(weak_ordered_set->Contains(keep_numbers_alive->at(i)));
             if (delete_afterwards)
@@ -3542,7 +3507,6 @@
             strong_weak->insert(wrapped, wrapped);
             weak_weak->insert(wrapped, wrapped);
             weak_set->insert(wrapped);
-            weak_ordered_legacy_set->insert(wrapped);
             weak_ordered_set->insert(wrapped);
           }
         }
@@ -3554,16 +3518,13 @@
           EXPECT_EQ(count + added, weak_weak->size());
         else if (collection_number == kWeakSetIndex)
           EXPECT_EQ(count + added, weak_set->size());
-        else if (collection_number == kWeakOrderedLegacySetIndex)
-          EXPECT_EQ(count + added, weak_ordered_legacy_set->size());
         else if (collection_number == kWeakOrderedSetIndex)
           EXPECT_EQ(count + added, weak_ordered_set->size());
         WeakStrong::iterator it1 = weak_strong->begin();
         StrongWeak::iterator it2 = strong_weak->begin();
         WeakWeak::iterator it3 = weak_weak->begin();
         WeakSet::iterator it4 = weak_set->begin();
-        WeakOrderedLegacySet::iterator it5 = weak_ordered_legacy_set->begin();
-        WeakOrderedSet::iterator it6 = weak_ordered_set->begin();
+        WeakOrderedSet::iterator it5 = weak_ordered_set->begin();
         MapIteratorCheck(
             it1, weak_strong->end(),
             (collection_number == kWeakStrongIndex ? count : 0) + added);
@@ -3577,11 +3538,7 @@
             it4, weak_set->end(),
             (collection_number == kWeakSetIndex ? count : 0) + added);
         SetIteratorCheck(
-            it5, weak_ordered_legacy_set->end(),
-            (collection_number == kWeakOrderedLegacySetIndex ? count : 0) +
-                added);
-        SetIteratorCheck(
-            it6, weak_ordered_set->end(),
+            it5, weak_ordered_set->end(),
             (collection_number == kWeakOrderedSetIndex ? count : 0) + added);
       }
       for (unsigned i = 0; i < 128 + added; i++)
@@ -3591,7 +3548,6 @@
       EXPECT_EQ(0u, strong_weak->size());
       EXPECT_EQ(0u, weak_weak->size());
       EXPECT_EQ(0u, weak_set->size());
-      EXPECT_EQ(0u, weak_ordered_legacy_set->size());
       EXPECT_EQ(0u, weak_ordered_set->size());
     }
   }
@@ -5243,9 +5199,6 @@
   static_assert(
       WTF::IsGarbageCollectedType<HeapHashSet<Member<IntWrapper>>>::value,
       "HeapHashSet");
-  static_assert(WTF::IsGarbageCollectedType<
-                    HeapLegacyLinkedHashSet<Member<IntWrapper>>>::value,
-                "HeapLegacyLinkedHashSet");
   static_assert(
       WTF::IsGarbageCollectedType<HeapLinkedHashSet<Member<IntWrapper>>>::value,
       "HeapLinkedHashSet");
diff --git a/third_party/blink/renderer/platform/heap/test/incremental_marking_test.cc b/third_party/blink/renderer/platform/heap/test/incremental_marking_test.cc
index 41bed73..7bab2c4 100644
--- a/third_party/blink/renderer/platform/heap/test/incremental_marking_test.cc
+++ b/third_party/blink/renderer/platform/heap/test/incremental_marking_test.cc
@@ -904,34 +904,6 @@
 }
 
 // =============================================================================
-// HeapLegacyLinkedHashSet support. ============================================
-// =============================================================================
-
-TEST_F(IncrementalMarkingTest, HeapLegacyLinkedHashSetInsert) {
-  Insert<HeapLegacyLinkedHashSet<Member<Object>>>();
-  // Weak references are strongified for the current cycle.
-  Insert<HeapLegacyLinkedHashSet<WeakMember<Object>>>();
-}
-
-TEST_F(IncrementalMarkingTest, HeapLegacyLinkedHashSetCopy) {
-  Copy<HeapLegacyLinkedHashSet<Member<Object>>>();
-  // Weak references are strongified for the current cycle.
-  Copy<HeapLegacyLinkedHashSet<WeakMember<Object>>>();
-}
-
-TEST_F(IncrementalMarkingTest, HeapLegacyLinkedHashSetMove) {
-  Move<HeapLegacyLinkedHashSet<Member<Object>>>();
-  // Weak references are strongified for the current cycle.
-  Move<HeapLegacyLinkedHashSet<WeakMember<Object>>>();
-}
-
-TEST_F(IncrementalMarkingTest, HeapLegacyLinkedHashSetSwap) {
-  Swap<HeapLegacyLinkedHashSet<Member<Object>>>();
-  // Weak references are strongified for the current cycle.
-  Swap<HeapLegacyLinkedHashSet<WeakMember<Object>>>();
-}
-
-// =============================================================================
 // HeapLinkedHashSet support. ==================================================
 // =============================================================================
 
@@ -1809,53 +1781,6 @@
 
 size_t Destructed::n_destructed = 0;
 
-class LegacyLinkedHashSetWrapper final
-    : public GarbageCollected<LegacyLinkedHashSetWrapper> {
- public:
-  using HashType = HeapLegacyLinkedHashSet<Member<Destructed>>;
-
-  LegacyLinkedHashSetWrapper() {
-    for (size_t i = 0; i < 10; ++i) {
-      hash_set_.insert(MakeGarbageCollected<Destructed>());
-    }
-  }
-
-  void Trace(Visitor* v) const { v->Trace(hash_set_); }
-
-  void Swap() {
-    HashType hash_set;
-    hash_set_.Swap(hash_set);
-  }
-
-  HashType hash_set_;
-};
-
-TEST_F(IncrementalMarkingTest, LegacyLinkedHashSetMovingCallback) {
-  ClearOutOldGarbage();
-
-  Destructed::n_destructed = 0;
-  {
-    HeapHashSet<Member<Destructed>> to_be_destroyed;
-    to_be_destroyed.ReserveCapacityForSize(100);
-  }
-  Persistent<LegacyLinkedHashSetWrapper> wrapper =
-      MakeGarbageCollected<LegacyLinkedHashSetWrapper>();
-
-  IncrementalMarkingTestDriver driver(ThreadState::Current());
-  ThreadState::Current()->EnableCompactionForNextGCForTesting();
-  driver.Start();
-  driver.FinishSteps();
-
-  // Destroy the link between original HeapLegacyLinkedHashSet object and its
-  // backing store.
-  wrapper->Swap();
-  DCHECK(wrapper->hash_set_.IsEmpty());
-
-  PreciselyCollectGarbage();
-
-  EXPECT_EQ(10u, Destructed::n_destructed);
-}
-
 class LinkedHashSetWrapper final
     : public GarbageCollected<LinkedHashSetWrapper> {
  public:
diff --git a/third_party/blink/renderer/platform/heap/test/weakness_marking_test.cc b/third_party/blink/renderer/platform/heap/test/weakness_marking_test.cc
index b019b8f..008fecf 100644
--- a/third_party/blink/renderer/platform/heap/test/weakness_marking_test.cc
+++ b/third_party/blink/renderer/platform/heap/test/weakness_marking_test.cc
@@ -207,19 +207,6 @@
   // Test ensures that an empty weak set that has already been marked sets up
   // weakness callbacks. This is important as another backing may be swapped in
   // at some point after marking it initially.
-  using WeakLegacyLinkedSet =
-      HeapLegacyLinkedHashSet<WeakMember<IntegerObject>>;
-  Persistent<WeakLegacyLinkedSet> holder1(
-      MakeGarbageCollected<WeakLegacyLinkedSet>());
-  Persistent<WeakLegacyLinkedSet> holder2(
-      MakeGarbageCollected<WeakLegacyLinkedSet>());
-  holder1->insert(MakeGarbageCollected<IntegerObject>(1));
-  IncrementalMarkingTestDriver driver(ThreadState::Current());
-  driver.Start();
-  driver.FinishSteps();
-  holder1->Swap(*holder2.Get());
-  driver.FinishGC();
-
   using WeakLinkedSet = HeapLinkedHashSet<WeakMember<IntegerObject>>;
   Persistent<WeakLinkedSet> holder3(MakeGarbageCollected<WeakLinkedSet>());
   Persistent<WeakLinkedSet> holder4(MakeGarbageCollected<WeakLinkedSet>());
diff --git a/third_party/blink/renderer/platform/heap/trace_traits.h b/third_party/blink/renderer/platform/heap/trace_traits.h
index 1d640c7..59c97fe 100644
--- a/third_party/blink/renderer/platform/heap/trace_traits.h
+++ b/third_party/blink/renderer/platform/heap/trace_traits.h
@@ -376,50 +376,6 @@
   }
 };
 
-// Nodes used by LegacyLinkedHashSet.  Again we need two versions to
-// disambiguate the template.
-// TODO(bartekn): Remove once fully transitioned to LinkedHashSet.
-template <typename Value, typename Traits>
-struct TraceInCollectionTrait<kNoWeakHandling,
-                              LegacyLinkedHashSetNode<Value>,
-                              Traits> {
-  static bool IsAlive(const blink::LivenessBroker& info,
-                      const LegacyLinkedHashSetNode<Value>& self) {
-    return TraceInCollectionTrait<
-        kNoWeakHandling, Value,
-        typename Traits::ValueTraits>::IsAlive(info, self.value_);
-  }
-
-  static void Trace(blink::Visitor* visitor,
-                    const LegacyLinkedHashSetNode<Value>& self) {
-    static_assert(
-        IsTraceableInCollectionTrait<Traits>::value || IsWeak<Value>::value,
-        "T should be traceable (or weak)");
-    TraceInCollectionTrait<kNoWeakHandling, Value,
-                           typename Traits::ValueTraits>::Trace(visitor,
-                                                                self.value_);
-  }
-};
-
-template <typename Value, typename Traits>
-struct TraceInCollectionTrait<kWeakHandling,
-                              LegacyLinkedHashSetNode<Value>,
-                              Traits> {
-  static bool IsAlive(const blink::LivenessBroker& info,
-                      const LegacyLinkedHashSetNode<Value>& self) {
-    return TraceInCollectionTrait<
-        kWeakHandling, Value,
-        typename Traits::ValueTraits>::IsAlive(info, self.value_);
-  }
-
-  static void Trace(blink::Visitor* visitor,
-                    const LegacyLinkedHashSetNode<Value>& self) {
-    TraceInCollectionTrait<kWeakHandling, Value,
-                           typename Traits::ValueTraits>::Trace(visitor,
-                                                                self.value_);
-  }
-};
-
 // ListHashSetNode pointers (a ListHashSet is implemented as a hash table of
 // these pointers).
 template <typename Value, size_t inlineCapacity, typename Traits>
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index cd061b9..f67618d 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -2082,10 +2082,9 @@
       origin_trial_feature_name: "WebScheduler",
       status: "experimental",
     },
-    // WebShare is enabled by default on Android.
     {
       name: "WebShare",
-      status: "test",
+      status:  {"Android": "stable", "default": "test"},
     },
     {
       name: "WebSocketStream",
diff --git a/third_party/blink/renderer/platform/wtf/hash_table.h b/third_party/blink/renderer/platform/wtf/hash_table.h
index 5a596d0..9289763 100644
--- a/third_party/blink/renderer/platform/wtf/hash_table.h
+++ b/third_party/blink/renderer/platform/wtf/hash_table.h
@@ -219,12 +219,6 @@
           typename KeyTraits,
           typename Allocator>
 class HashTableConstIterator;
-// TODO(bartekn): Remove once fully transitioned to LinkedHashSet.
-template <typename Value,
-          typename HashFunctions,
-          typename HashTraits,
-          typename Allocator>
-class LegacyLinkedHashSet;
 template <WeakHandlingFlag x,
           typename T,
           typename U,
@@ -1019,9 +1013,6 @@
             typename Y,
             typename Z>
   friend struct WeakProcessingHashTableHelper;
-  // TODO(bartekn): Remove once fully transitioned to LinkedHashSet.
-  template <typename T, typename U, typename V, typename W>
-  friend class LegacyLinkedHashSet;
   template <typename T, size_t, typename U, typename V>
   friend class ListHashSet;
 };
@@ -1795,7 +1786,7 @@
     }
   }
   table_ = temporary_table;
-  Allocator::template BackingWriteBarrierForHashTable<HashTable>(&table_);
+  Allocator::template BackingWriteBarrier(&table_);
 
   if (Traits::kEmptyValueIsZero) {
     memset(original_table, 0, new_table_size * sizeof(ValueType));
@@ -1853,7 +1844,7 @@
   // This swaps the newly allocated buffer with the current one. The store to
   // the current table has to be atomic to prevent races with concurrent marker.
   AsAtomicPtr(&table_)->store(new_hash_table.table_, std::memory_order_relaxed);
-  Allocator::template BackingWriteBarrierForHashTable<HashTable>(&table_);
+  Allocator::template BackingWriteBarrier(&table_);
   table_size_ = new_table_size;
 
   new_hash_table.table_ = old_table;
@@ -2021,8 +2012,8 @@
   // on the mutator thread, which is also the only one that writes to them, so
   // there is *no* risk of data races when reading.
   AtomicWriteSwap(table_, other.table_);
-  Allocator::template BackingWriteBarrierForHashTable<HashTable>(&table_);
-  Allocator::template BackingWriteBarrierForHashTable<HashTable>(&other.table_);
+  Allocator::template BackingWriteBarrier(&table_);
+  Allocator::template BackingWriteBarrier(&other.table_);
   if (IsWeak<ValueType>::value) {
     // Weak processing is omitted when no backing store is present. In case such
     // an empty table is later on used it needs to be strongified.
diff --git a/third_party/blink/renderer/platform/wtf/hash_traits.h b/third_party/blink/renderer/platform/wtf/hash_traits.h
index 4a8a7a325..eb836152 100644
--- a/third_party/blink/renderer/platform/wtf/hash_traits.h
+++ b/third_party/blink/renderer/platform/wtf/hash_traits.h
@@ -98,13 +98,6 @@
 
   static constexpr bool kCanHaveDeletedValue = true;
 
-  // The kHasMovingCallback value is only used for HashTable backing stores.
-  // Currently it is needed for LegacyLinkedHashSet to register moving callback
-  // on write barrier. Users of this value have to provide
-  // RegisterMovingCallback function.
-  // TODO(bartekn): Remove once fully transitioned to LinkedHashSet.
-  static constexpr bool kHasMovingCallback = false;
-
   // The kCanTraceConcurrently value is used by Oilpan concurrent marking. Only
   // type for which HashTraits<T>::kCanTraceConcurrently is true can be traced
   // on a concurrent thread.
diff --git a/third_party/blink/renderer/platform/wtf/linked_hash_set.h b/third_party/blink/renderer/platform/wtf/linked_hash_set.h
index c45275a1..1ddcca6 100644
--- a/third_party/blink/renderer/platform/wtf/linked_hash_set.h
+++ b/third_party/blink/renderer/platform/wtf/linked_hash_set.h
@@ -32,911 +32,6 @@
 
 namespace WTF {
 
-// IMPORTANT! Do not use this class, unless you need to work around a
-// LinkedHashSet issue. Contact chrome-memory-tok@ if you do.
-// TODO(bartekn): Remove once fully transitioned to LinkedHashSet.
-//
-// LegacyLinkedHashSet provides a Set interface like HashSet, but also has a
-// predictable iteration order. It has O(1) insertion, removal, and test for
-// containership. It maintains a linked list through its contents such that
-// iterating it yields values in the order in which they were inserted.
-//
-// LegacyLinkedHashSet iterators are invalidated by mutation of the set. This
-// means, for example, that you cannot modify the container while iterating over
-// it (this will DCHECK in debug). Instead, you should either copy the entries
-// to a vector before iterating, or maintain a separate list of pending updates.
-//
-// Unlike ListHashSet, this container supports WeakMember<T>.
-template <typename Value,
-          typename HashFunctions,
-          typename HashTraits,
-          typename Allocator>
-class LegacyLinkedHashSet;
-
-template <typename LegacyLinkedHashSet>
-class LegacyLinkedHashSetConstIterator;
-template <typename LegacyLinkedHashSet>
-class LegacyLinkedHashSetConstReverseIterator;
-
-template <typename Value, typename HashFunctions, typename TraitsArg>
-struct LegacyLinkedHashSetTranslator;
-template <typename Value>
-struct LegacyLinkedHashSetExtractor;
-template <typename Value, typename ValueTraits, typename Allocator>
-struct LegacyLinkedHashSetTraits;
-class LegacyLinkedHashSetNodeBase;
-
-class LegacyLinkedHashSetNodeBasePointer {
- public:
-  LegacyLinkedHashSetNodeBasePointer(LegacyLinkedHashSetNodeBase* node)
-      : node_(node) {}
-
-  LegacyLinkedHashSetNodeBasePointer& operator=(
-      const LegacyLinkedHashSetNodeBasePointer& other) {
-    SetSafe(other);
-    return *this;
-  }
-
-  LegacyLinkedHashSetNodeBasePointer& operator=(
-      LegacyLinkedHashSetNodeBase* other) {
-    SetSafe(other);
-    return *this;
-  }
-
-  LegacyLinkedHashSetNodeBasePointer& operator=(std::nullptr_t) {
-    SetSafe(nullptr);
-    return *this;
-  }
-
-  LegacyLinkedHashSetNodeBase* Get() const { return node_; }
-  explicit operator bool() const { return Get(); }
-  operator LegacyLinkedHashSetNodeBase*() const { return Get(); }
-  LegacyLinkedHashSetNodeBase* operator->() const { return Get(); }
-  LegacyLinkedHashSetNodeBase& operator*() const { return *Get(); }
-
- private:
-  void SetSafe(LegacyLinkedHashSetNodeBase* node) {
-    AsAtomicPtr(&node_)->store(node, std::memory_order_relaxed);
-  }
-
-  LegacyLinkedHashSetNodeBase* node_ = nullptr;
-};
-
-class LegacyLinkedHashSetNodeBase {
-  DISALLOW_NEW();
-
- public:
-  LegacyLinkedHashSetNodeBase() : prev_(this), next_(this) {}
-
-  NO_SANITIZE_ADDRESS
-  void Unlink() {
-    if (!next_)
-      return;
-    DCHECK(prev_);
-    {
-      AsanUnpoisonScope unpoison_scope(next_,
-                                       sizeof(LegacyLinkedHashSetNodeBase));
-      DCHECK(next_->prev_ == this);
-      next_->prev_ = prev_;
-    }
-    {
-      AsanUnpoisonScope unpoison_scope(prev_,
-                                       sizeof(LegacyLinkedHashSetNodeBase));
-      DCHECK(prev_->next_ == this);
-      prev_->next_ = next_;
-    }
-  }
-
-  ~LegacyLinkedHashSetNodeBase() { Unlink(); }
-
-  void InsertBefore(LegacyLinkedHashSetNodeBase& other) {
-    other.next_ = this;
-    other.prev_ = prev_;
-    prev_->next_ = &other;
-    prev_ = &other;
-    DCHECK(other.next_);
-    DCHECK(other.prev_);
-    DCHECK(next_);
-    DCHECK(prev_);
-  }
-
-  void InsertAfter(LegacyLinkedHashSetNodeBase& other) {
-    other.prev_ = this;
-    other.next_ = next_;
-    next_->prev_ = &other;
-    next_ = &other;
-    DCHECK(other.next_);
-    DCHECK(other.prev_);
-    DCHECK(next_);
-    DCHECK(prev_);
-  }
-
-  LegacyLinkedHashSetNodeBase(LegacyLinkedHashSetNodeBase* prev,
-                              LegacyLinkedHashSetNodeBase* next)
-      : prev_(prev), next_(next) {
-    DCHECK((prev && next) || (!prev && !next));
-  }
-
-  LegacyLinkedHashSetNodeBasePointer prev_;
-  LegacyLinkedHashSetNodeBasePointer next_;
-
- protected:
-  // If we take a copy of a node we can't copy the next and prev pointers,
-  // since they point to something that does not point at us. This is used
-  // inside the shouldExpand() "if" in HashTable::add.
-  LegacyLinkedHashSetNodeBase(const LegacyLinkedHashSetNodeBase& other)
-      : prev_(nullptr), next_(nullptr) {}
-
-  LegacyLinkedHashSetNodeBase(LegacyLinkedHashSetNodeBase&& other)
-      : prev_(other.prev_), next_(other.next_) {
-    other.prev_ = nullptr;
-    other.next_ = nullptr;
-    if (next_) {
-      prev_->next_ = this;
-      next_->prev_ = this;
-    }
-  }
-
- private:
-  // Should not be used.
-  LegacyLinkedHashSetNodeBase& operator=(
-      const LegacyLinkedHashSetNodeBase& other) = delete;
-};
-
-template <typename ValueArg>
-class LegacyLinkedHashSetNode : public LegacyLinkedHashSetNodeBase {
-  DISALLOW_NEW();
-
- public:
-  LegacyLinkedHashSetNode(const ValueArg& value,
-                          LegacyLinkedHashSetNodeBase* prev,
-                          LegacyLinkedHashSetNodeBase* next)
-      : LegacyLinkedHashSetNodeBase(prev, next), value_(value) {}
-
-  LegacyLinkedHashSetNode(ValueArg&& value,
-                          LegacyLinkedHashSetNodeBase* prev,
-                          LegacyLinkedHashSetNodeBase* next)
-      : LegacyLinkedHashSetNodeBase(prev, next), value_(std::move(value)) {}
-
-  LegacyLinkedHashSetNode(LegacyLinkedHashSetNode&& other)
-      : LegacyLinkedHashSetNodeBase(std::move(other)),
-        value_(std::move(other.value_)) {}
-
-  ValueArg value_;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(LegacyLinkedHashSetNode);
-};
-
-template <typename T>
-struct IsWeak<LegacyLinkedHashSetNode<T>>
-    : std::integral_constant<bool, IsWeak<T>::value> {};
-
-template <typename ValueArg,
-          typename HashFunctions = typename DefaultHash<ValueArg>::Hash,
-          typename TraitsArg = HashTraits<ValueArg>,
-          typename Allocator = PartitionAllocator>
-class LegacyLinkedHashSet {
-  USE_ALLOCATOR(LegacyLinkedHashSet, Allocator);
-
- private:
-  typedef ValueArg Value;
-  typedef TraitsArg Traits;
-  typedef LegacyLinkedHashSetNode<Value> Node;
-  typedef LegacyLinkedHashSetNodeBase NodeBase;
-  typedef LegacyLinkedHashSetTranslator<Value, HashFunctions, Traits>
-      NodeHashFunctions;
-  typedef LegacyLinkedHashSetTraits<Value, Traits, Allocator> NodeHashTraits;
-
-  typedef HashTable<Node,
-                    Node,
-                    IdentityExtractor,
-                    NodeHashFunctions,
-                    NodeHashTraits,
-                    NodeHashTraits,
-                    Allocator>
-      ImplType;
-
- public:
-  typedef LegacyLinkedHashSetConstIterator<LegacyLinkedHashSet> iterator;
-  typedef LegacyLinkedHashSetConstIterator<LegacyLinkedHashSet> const_iterator;
-  friend class LegacyLinkedHashSetConstIterator<LegacyLinkedHashSet>;
-
-  typedef LegacyLinkedHashSetConstReverseIterator<LegacyLinkedHashSet>
-      reverse_iterator;
-  typedef LegacyLinkedHashSetConstReverseIterator<LegacyLinkedHashSet>
-      const_reverse_iterator;
-  friend class LegacyLinkedHashSetConstReverseIterator<LegacyLinkedHashSet>;
-
-  struct AddResult final {
-    STACK_ALLOCATED();
-
-   public:
-    AddResult(const typename ImplType::AddResult& hash_table_add_result)
-        : stored_value(&hash_table_add_result.stored_value->value_),
-          is_new_entry(hash_table_add_result.is_new_entry) {}
-
-    const Value* stored_value;
-    bool is_new_entry;
-  };
-
-  typedef typename TraitsArg::PeekInType ValuePeekInType;
-
-  LegacyLinkedHashSet();
-  LegacyLinkedHashSet(const LegacyLinkedHashSet&);
-  LegacyLinkedHashSet(LegacyLinkedHashSet&&);
-  LegacyLinkedHashSet& operator=(const LegacyLinkedHashSet&);
-  LegacyLinkedHashSet& operator=(LegacyLinkedHashSet&&);
-
-  // Needs finalization. The anchor needs to unlink itself from the chain.
-  ~LegacyLinkedHashSet();
-
-  void Swap(LegacyLinkedHashSet&);
-
-  unsigned size() const { return impl_.size(); }
-  unsigned Capacity() const { return impl_.Capacity(); }
-  bool IsEmpty() const { return impl_.IsEmpty(); }
-
-  iterator begin() { return MakeIterator(FirstNode()); }
-  iterator end() { return MakeIterator(Anchor()); }
-  const_iterator begin() const { return MakeConstIterator(FirstNode()); }
-  const_iterator end() const { return MakeConstIterator(Anchor()); }
-
-  reverse_iterator rbegin() { return MakeReverseIterator(LastNode()); }
-  reverse_iterator rend() { return MakeReverseIterator(Anchor()); }
-  const_reverse_iterator rbegin() const {
-    return MakeConstReverseIterator(LastNode());
-  }
-  const_reverse_iterator rend() const {
-    return MakeConstReverseIterator(Anchor());
-  }
-
-  Value& front();
-  const Value& front() const;
-  void RemoveFirst();
-
-  Value& back();
-  const Value& back() const;
-  void pop_back();
-
-  iterator find(ValuePeekInType);
-  const_iterator find(ValuePeekInType) const;
-  bool Contains(ValuePeekInType) const;
-
-  // An alternate version of find() that finds the object by hashing and
-  // comparing with some other type, to avoid the cost of type conversion.
-  // The HashTranslator interface is defined in HashSet.
-  template <typename HashTranslator, typename T>
-  iterator Find(const T&);
-  template <typename HashTranslator, typename T>
-  const_iterator Find(const T&) const;
-  template <typename HashTranslator, typename T>
-  bool Contains(const T&) const;
-
-  // The return value of insert is a pair of a pointer to the stored value,
-  // and a bool that is true if an new entry was added.
-  template <typename IncomingValueType>
-  AddResult insert(IncomingValueType&&);
-
-  // Add the value to the end of the collection. If the value was already in
-  // the list, it is moved to the end.
-  template <typename IncomingValueType>
-  AddResult AppendOrMoveToLast(IncomingValueType&&);
-
-  // Add the value to the beginning of the collection. If the value was already
-  // in the list, it is moved to the beginning.
-  template <typename IncomingValueType>
-  AddResult PrependOrMoveToFirst(IncomingValueType&&);
-
-  template <typename IncomingValueType>
-  AddResult InsertBefore(ValuePeekInType before_value,
-                         IncomingValueType&& new_value);
-  template <typename IncomingValueType>
-  AddResult InsertBefore(const_iterator it, IncomingValueType&& new_value) {
-    return impl_.template insert<NodeHashFunctions>(
-        std::forward<IncomingValueType>(new_value), it.GetNode());
-  }
-
-  void erase(ValuePeekInType);
-  void erase(const_iterator);
-  void clear() { impl_.clear(); }
-  template <typename Collection>
-  void RemoveAll(const Collection& other) {
-    WTF::RemoveAll(*this, other);
-  }
-
-  template <typename VisitorDispatcher>
-  void Trace(VisitorDispatcher visitor) const {
-    // Should the underlying table be moved by GC, register a callback
-    // that fixes up the interior pointers that the (Heap)LegacyLinkedHashSet
-    // keeps.
-    const auto* table =
-        AsAtomicPtr(&impl_.table_)->load(std::memory_order_relaxed);
-
-    impl_.TraceTable(visitor, table);
-    if (table) {
-      Allocator::RegisterBackingStoreCallback(
-          visitor, table,
-          NodeHashTraits::template MoveBackingCallback<ImplType>);
-    }
-  }
-
-  int64_t Modifications() const { return impl_.Modifications(); }
-  void CheckModifications(int64_t mods) const {
-    impl_.CheckModifications(mods);
-  }
-
- protected:
-  typename ImplType::ValueType** GetBufferSlot() {
-    return impl_.GetBufferSlot();
-  }
-
- private:
-  Node* Anchor() { return reinterpret_cast<Node*>(&anchor_); }
-  const Node* Anchor() const { return reinterpret_cast<const Node*>(&anchor_); }
-  Node* FirstNode() { return reinterpret_cast<Node*>(anchor_.next_.Get()); }
-  const Node* FirstNode() const {
-    return reinterpret_cast<const Node*>(anchor_.next_.Get());
-  }
-  Node* LastNode() { return reinterpret_cast<Node*>(anchor_.prev_.Get()); }
-  const Node* LastNode() const {
-    return reinterpret_cast<const Node*>(anchor_.prev_.Get());
-  }
-
-  iterator MakeIterator(const Node* position) {
-    return iterator(position, this);
-  }
-  const_iterator MakeConstIterator(const Node* position) const {
-    return const_iterator(position, this);
-  }
-  reverse_iterator MakeReverseIterator(const Node* position) {
-    return reverse_iterator(position, this);
-  }
-  const_reverse_iterator MakeConstReverseIterator(const Node* position) const {
-    return const_reverse_iterator(position, this);
-  }
-
-  ImplType impl_;
-  NodeBase anchor_;
-};
-
-template <typename Value, typename HashFunctions, typename TraitsArg>
-struct LegacyLinkedHashSetTranslator {
-  STATIC_ONLY(LegacyLinkedHashSetTranslator);
-  typedef LegacyLinkedHashSetNode<Value> Node;
-  typedef LegacyLinkedHashSetNodeBase NodeBase;
-  typedef typename TraitsArg::PeekInType ValuePeekInType;
-  static unsigned GetHash(const Node& node) {
-    return HashFunctions::GetHash(node.value_);
-  }
-  static unsigned GetHash(const ValuePeekInType& key) {
-    return HashFunctions::GetHash(key);
-  }
-  static bool Equal(const Node& a, const ValuePeekInType& b) {
-    return HashFunctions::Equal(a.value_, b);
-  }
-  static bool Equal(const Node& a, const Node& b) {
-    return HashFunctions::Equal(a.value_, b.value_);
-  }
-  template <typename IncomingValueType>
-  static void Translate(Node& location,
-                        IncomingValueType&& key,
-                        NodeBase* anchor) {
-    anchor->InsertBefore(location);
-    location.value_ = std::forward<IncomingValueType>(key);
-  }
-
-  // Empty (or deleted) slots have the next_ pointer set to null, but we
-  // don't do anything to the other fields, which may contain junk.
-  // Therefore you can't compare a newly constructed empty value with a
-  // slot and get the right answer.
-  static const bool safe_to_compare_to_empty_or_deleted = false;
-};
-
-template <typename Value>
-struct LegacyLinkedHashSetExtractor {
-  STATIC_ONLY(LegacyLinkedHashSetExtractor);
-  static const Value& Extract(const LegacyLinkedHashSetNode<Value>& node) {
-    return node.value_;
-  }
-};
-
-template <typename Value, typename ValueTraitsArg, typename Allocator>
-struct LegacyLinkedHashSetTraits
-    : public SimpleClassHashTraits<LegacyLinkedHashSetNode<Value>> {
-  STATIC_ONLY(LegacyLinkedHashSetTraits);
-  using Node = LegacyLinkedHashSetNode<Value>;
-  using NodeBase = LegacyLinkedHashSetNodeBase;
-  typedef ValueTraitsArg ValueTraits;
-
-  // The slot is empty when the next_ field is zero so it's safe to zero
-  // the backing.
-  static const bool kEmptyValueIsZero = ValueTraits::kEmptyValueIsZero;
-
-  static const bool kHasIsEmptyValueFunction = true;
-  static bool IsEmptyValue(const Node& node) { return !node.next_; }
-  static Node EmptyValue() {
-    return Node(ValueTraits::EmptyValue(), nullptr, nullptr);
-  }
-
-  static const int kDeletedValue = -1;
-
-  static void ConstructDeletedValue(Node& slot, bool) {
-    slot.next_ = reinterpret_cast<Node*>(kDeletedValue);
-  }
-  static bool IsDeletedValue(const Node& slot) {
-    return slot.next_ == reinterpret_cast<Node*>(kDeletedValue);
-  }
-
-  // Whether we need to trace and do weak processing depends on the traits of
-  // the type inside the node.
-  template <typename U = void>
-  struct IsTraceableInCollection {
-    STATIC_ONLY(IsTraceableInCollection);
-    static const bool value =
-        ValueTraits::template IsTraceableInCollection<>::value;
-  };
-
-  static constexpr bool kHasMovingCallback = true;
-
-  template <typename HashTable, typename Visitor>
-  static void RegisterMovingCallback(Visitor* visitor,
-                                     typename HashTable::ValueType* allocated) {
-    Allocator::RegisterBackingStoreCallback(visitor, allocated,
-                                            MoveBackingCallback<HashTable>);
-  }
-
-  template <typename HashTable>
-  static void MoveBackingCallback(const void* const_from,
-                                  const void* const_to,
-                                  size_t size) {
-    // Note: the hash table move may have been overlapping; linearly scan the
-    // entire table and fixup interior pointers into the old region with
-    // correspondingly offset ones into the new.
-    void* from = const_cast<void*>(const_from);
-    void* to = const_cast<void*>(const_to);
-    const size_t table_size = size / sizeof(Node);
-    Node* table = reinterpret_cast<Node*>(to);
-    NodeBase* from_start = reinterpret_cast<NodeBase*>(from);
-    NodeBase* from_end =
-        reinterpret_cast<NodeBase*>(reinterpret_cast<uintptr_t>(from) + size);
-    NodeBase* anchor_node = nullptr;
-    for (Node* element = table + table_size - 1; element >= table; element--) {
-      Node& node = *element;
-      if (HashTable::IsEmptyOrDeletedBucket(node))
-        continue;
-      if (node.next_ >= from_start && node.next_ < from_end) {
-        const size_t diff = reinterpret_cast<uintptr_t>(node.next_.Get()) -
-                            reinterpret_cast<uintptr_t>(from);
-        node.next_ =
-            reinterpret_cast<NodeBase*>(reinterpret_cast<uintptr_t>(to) + diff);
-      } else {
-        DCHECK(!anchor_node || node.next_ == anchor_node);
-        anchor_node = node.next_;
-      }
-      if (node.prev_ >= from_start && node.prev_ < from_end) {
-        const size_t diff = reinterpret_cast<uintptr_t>(node.prev_.Get()) -
-                            reinterpret_cast<uintptr_t>(from);
-        node.prev_ =
-            reinterpret_cast<NodeBase*>(reinterpret_cast<uintptr_t>(to) + diff);
-      } else {
-        DCHECK(!anchor_node || node.prev_ == anchor_node);
-        anchor_node = node.prev_;
-      }
-    }
-    // During incremental marking, HeapLegacyLinkedHashSet object may be marked,
-    // but later the mutator can destroy it. The compaction code will execute
-    // this callback, but the anchor will have already been unlinked.
-    if (!anchor_node) {
-      return;
-    }
-    {
-      DCHECK(anchor_node->prev_ >= from_start && anchor_node->prev_ < from_end);
-      const size_t diff =
-          reinterpret_cast<uintptr_t>(anchor_node->prev_.Get()) -
-          reinterpret_cast<uintptr_t>(from);
-      anchor_node->prev_ =
-          reinterpret_cast<NodeBase*>(reinterpret_cast<uintptr_t>(to) + diff);
-    }
-    {
-      DCHECK(anchor_node->next_ >= from_start && anchor_node->next_ < from_end);
-      const size_t diff =
-          reinterpret_cast<uintptr_t>(anchor_node->next_.Get()) -
-          reinterpret_cast<uintptr_t>(from);
-      anchor_node->next_ =
-          reinterpret_cast<NodeBase*>(reinterpret_cast<uintptr_t>(to) + diff);
-    }
-  }
-
-  static constexpr bool kCanTraceConcurrently =
-      ValueTraitsArg::kCanTraceConcurrently;
-};
-
-template <typename LegacyLinkedHashSetType>
-class LegacyLinkedHashSetConstIterator {
-  DISALLOW_NEW();
-
- private:
-  typedef typename LegacyLinkedHashSetType::Node Node;
-  typedef typename LegacyLinkedHashSetType::Traits Traits;
-
-  typedef const typename LegacyLinkedHashSetType::Value& ReferenceType;
-  typedef const typename LegacyLinkedHashSetType::Value* PointerType;
-
-  Node* GetNode() const {
-    return const_cast<Node*>(static_cast<const Node*>(position_));
-  }
-
- protected:
-  LegacyLinkedHashSetConstIterator(const LegacyLinkedHashSetNodeBase* position,
-                                   const LegacyLinkedHashSetType* container)
-      : position_(position)
-#if DCHECK_IS_ON()
-        ,
-        container_(container),
-        container_modifications_(container->Modifications())
-#endif
-  {
-  }
-
- public:
-  PointerType Get() const {
-    CheckModifications();
-    return &static_cast<const Node*>(position_)->value_;
-  }
-  ReferenceType operator*() const { return *Get(); }
-  PointerType operator->() const { return Get(); }
-
-  LegacyLinkedHashSetConstIterator& operator++() {
-    DCHECK(position_);
-    CheckModifications();
-    position_ = position_->next_;
-    return *this;
-  }
-
-  LegacyLinkedHashSetConstIterator& operator--() {
-    DCHECK(position_);
-    CheckModifications();
-    position_ = position_->prev_;
-    return *this;
-  }
-
-  // Postfix ++ and -- intentionally omitted.
-
-  // Comparison.
-  bool operator==(const LegacyLinkedHashSetConstIterator& other) const {
-    return position_ == other.position_;
-  }
-  bool operator!=(const LegacyLinkedHashSetConstIterator& other) const {
-    return position_ != other.position_;
-  }
-
- private:
-  const LegacyLinkedHashSetNodeBase* position_;
-#if DCHECK_IS_ON()
-  void CheckModifications() const {
-    container_->CheckModifications(container_modifications_);
-  }
-  const LegacyLinkedHashSetType* container_;
-  int64_t container_modifications_;
-#else
-  void CheckModifications() const {}
-#endif
-  template <typename T, typename U, typename V, typename W>
-  friend class LegacyLinkedHashSet;
-};
-
-template <typename LegacyLinkedHashSetType>
-class LegacyLinkedHashSetConstReverseIterator
-    : public LegacyLinkedHashSetConstIterator<LegacyLinkedHashSetType> {
-  typedef LegacyLinkedHashSetConstIterator<LegacyLinkedHashSetType> Superclass;
-  typedef typename LegacyLinkedHashSetType::Node Node;
-
- public:
-  LegacyLinkedHashSetConstReverseIterator(
-      const Node* position,
-      const LegacyLinkedHashSetType* container)
-      : Superclass(position, container) {}
-
-  LegacyLinkedHashSetConstReverseIterator& operator++() {
-    Superclass::operator--();
-    return *this;
-  }
-  LegacyLinkedHashSetConstReverseIterator& operator--() {
-    Superclass::operator++();
-    return *this;
-  }
-
-  // Postfix ++ and -- intentionally omitted.
-
-  template <typename T, typename U, typename V, typename W>
-  friend class LegacyLinkedHashSet;
-};
-
-inline void SwapAnchor(LegacyLinkedHashSetNodeBase& a,
-                       LegacyLinkedHashSetNodeBase& b) {
-  DCHECK(a.prev_);
-  DCHECK(a.next_);
-  DCHECK(b.prev_);
-  DCHECK(b.next_);
-  swap(a.prev_, b.prev_);
-  swap(a.next_, b.next_);
-  if (b.next_ == &a) {
-    DCHECK_EQ(b.prev_, &a);
-    b.next_ = &b;
-    b.prev_ = &b;
-  } else {
-    b.next_->prev_ = &b;
-    b.prev_->next_ = &b;
-  }
-  if (a.next_ == &b) {
-    DCHECK_EQ(a.prev_, &b);
-    a.next_ = &a;
-    a.prev_ = &a;
-  } else {
-    a.next_->prev_ = &a;
-    a.prev_->next_ = &a;
-  }
-}
-
-inline void swap(LegacyLinkedHashSetNodeBase& a,
-                 LegacyLinkedHashSetNodeBase& b) {
-  DCHECK_NE(a.next_, &a);
-  DCHECK_NE(b.next_, &b);
-  swap(a.prev_, b.prev_);
-  swap(a.next_, b.next_);
-  if (b.next_) {
-    b.next_->prev_ = &b;
-    b.prev_->next_ = &b;
-  }
-  if (a.next_) {
-    a.next_->prev_ = &a;
-    a.prev_->next_ = &a;
-  }
-}
-
-template <typename T, typename U, typename V, typename Allocator>
-inline LegacyLinkedHashSet<T, U, V, Allocator>::LegacyLinkedHashSet() {
-  static_assert(Allocator::kIsGarbageCollected ||
-                    !IsPointerToGarbageCollectedType<T>::value,
-                "Cannot put raw pointers to garbage-collected classes into "
-                "an off-heap LegacyLinkedHashSet. Use "
-                "HeapLegacyLinkedHashSet<Member<T>> instead.");
-}
-
-template <typename T, typename U, typename V, typename W>
-inline LegacyLinkedHashSet<T, U, V, W>::LegacyLinkedHashSet(
-    const LegacyLinkedHashSet& other)
-    : anchor_() {
-  const_iterator end = other.end();
-  for (const_iterator it = other.begin(); it != end; ++it)
-    insert(*it);
-}
-
-template <typename T, typename U, typename V, typename W>
-inline LegacyLinkedHashSet<T, U, V, W>::LegacyLinkedHashSet(
-    LegacyLinkedHashSet&& other)
-    : anchor_() {
-  Swap(other);
-}
-
-template <typename T, typename U, typename V, typename W>
-inline LegacyLinkedHashSet<T, U, V, W>&
-LegacyLinkedHashSet<T, U, V, W>::operator=(const LegacyLinkedHashSet& other) {
-  LegacyLinkedHashSet tmp(other);
-  Swap(tmp);
-  return *this;
-}
-
-template <typename T, typename U, typename V, typename W>
-inline LegacyLinkedHashSet<T, U, V, W>&
-LegacyLinkedHashSet<T, U, V, W>::operator=(LegacyLinkedHashSet&& other) {
-  Swap(other);
-  return *this;
-}
-
-template <typename T, typename U, typename V, typename W>
-inline void LegacyLinkedHashSet<T, U, V, W>::Swap(LegacyLinkedHashSet& other) {
-  impl_.swap(other.impl_);
-  SwapAnchor(anchor_, other.anchor_);
-}
-
-template <typename T, typename U, typename V, typename Allocator>
-inline LegacyLinkedHashSet<T, U, V, Allocator>::~LegacyLinkedHashSet() {
-  // The destructor of anchor_ will implicitly be called here, which will
-  // unlink the anchor from the collection.
-}
-
-template <typename T, typename U, typename V, typename W>
-inline T& LegacyLinkedHashSet<T, U, V, W>::front() {
-  DCHECK(!IsEmpty());
-  return FirstNode()->value_;
-}
-
-template <typename T, typename U, typename V, typename W>
-inline const T& LegacyLinkedHashSet<T, U, V, W>::front() const {
-  DCHECK(!IsEmpty());
-  return FirstNode()->value_;
-}
-
-template <typename T, typename U, typename V, typename W>
-inline void LegacyLinkedHashSet<T, U, V, W>::RemoveFirst() {
-  DCHECK(!IsEmpty());
-  impl_.erase(static_cast<Node*>(anchor_.next_.Get()));
-}
-
-template <typename T, typename U, typename V, typename W>
-inline T& LegacyLinkedHashSet<T, U, V, W>::back() {
-  DCHECK(!IsEmpty());
-  return LastNode()->value_;
-}
-
-template <typename T, typename U, typename V, typename W>
-inline const T& LegacyLinkedHashSet<T, U, V, W>::back() const {
-  DCHECK(!IsEmpty());
-  return LastNode()->value_;
-}
-
-template <typename T, typename U, typename V, typename W>
-inline void LegacyLinkedHashSet<T, U, V, W>::pop_back() {
-  DCHECK(!IsEmpty());
-  impl_.erase(static_cast<Node*>(anchor_.prev_.Get()));
-}
-
-template <typename T, typename U, typename V, typename W>
-inline typename LegacyLinkedHashSet<T, U, V, W>::iterator
-LegacyLinkedHashSet<T, U, V, W>::find(ValuePeekInType value) {
-  LegacyLinkedHashSet::Node* node =
-      impl_.template Lookup<LegacyLinkedHashSet::NodeHashFunctions,
-                            ValuePeekInType>(value);
-  if (!node)
-    return end();
-  return MakeIterator(node);
-}
-
-template <typename T, typename U, typename V, typename W>
-inline typename LegacyLinkedHashSet<T, U, V, W>::const_iterator
-LegacyLinkedHashSet<T, U, V, W>::find(ValuePeekInType value) const {
-  const LegacyLinkedHashSet::Node* node =
-      impl_.template Lookup<LegacyLinkedHashSet::NodeHashFunctions,
-                            ValuePeekInType>(value);
-  if (!node)
-    return end();
-  return MakeConstIterator(node);
-}
-
-template <typename Translator>
-struct LegacyLinkedHashSetTranslatorAdapter {
-  STATIC_ONLY(LegacyLinkedHashSetTranslatorAdapter);
-  template <typename T>
-  static unsigned GetHash(const T& key) {
-    return Translator::GetHash(key);
-  }
-  template <typename T, typename U>
-  static bool Equal(const T& a, const U& b) {
-    return Translator::Equal(a.value_, b);
-  }
-};
-
-template <typename Value, typename U, typename V, typename W>
-template <typename HashTranslator, typename T>
-inline typename LegacyLinkedHashSet<Value, U, V, W>::iterator
-LegacyLinkedHashSet<Value, U, V, W>::Find(const T& value) {
-  typedef LegacyLinkedHashSetTranslatorAdapter<HashTranslator>
-      TranslatedFunctions;
-  const LegacyLinkedHashSet::Node* node =
-      impl_.template Lookup<TranslatedFunctions, const T&>(value);
-  if (!node)
-    return end();
-  return MakeIterator(node);
-}
-
-template <typename Value, typename U, typename V, typename W>
-template <typename HashTranslator, typename T>
-inline typename LegacyLinkedHashSet<Value, U, V, W>::const_iterator
-LegacyLinkedHashSet<Value, U, V, W>::Find(const T& value) const {
-  typedef LegacyLinkedHashSetTranslatorAdapter<HashTranslator>
-      TranslatedFunctions;
-  const LegacyLinkedHashSet::Node* node =
-      impl_.template Lookup<TranslatedFunctions, const T&>(value);
-  if (!node)
-    return end();
-  return MakeConstIterator(node);
-}
-
-template <typename Value, typename U, typename V, typename W>
-template <typename HashTranslator, typename T>
-inline bool LegacyLinkedHashSet<Value, U, V, W>::Contains(
-    const T& value) const {
-  return impl_
-      .template Contains<LegacyLinkedHashSetTranslatorAdapter<HashTranslator>>(
-          value);
-}
-
-template <typename T, typename U, typename V, typename W>
-inline bool LegacyLinkedHashSet<T, U, V, W>::Contains(
-    ValuePeekInType value) const {
-  return impl_.template Contains<NodeHashFunctions>(value);
-}
-
-template <typename Value,
-          typename HashFunctions,
-          typename Traits,
-          typename Allocator>
-template <typename IncomingValueType>
-typename LegacyLinkedHashSet<Value, HashFunctions, Traits, Allocator>::AddResult
-LegacyLinkedHashSet<Value, HashFunctions, Traits, Allocator>::insert(
-    IncomingValueType&& value) {
-  return impl_.template insert<NodeHashFunctions>(
-      std::forward<IncomingValueType>(value), &anchor_);
-}
-
-template <typename T, typename U, typename V, typename W>
-template <typename IncomingValueType>
-typename LegacyLinkedHashSet<T, U, V, W>::AddResult
-LegacyLinkedHashSet<T, U, V, W>::AppendOrMoveToLast(IncomingValueType&& value) {
-  typename ImplType::AddResult result =
-      impl_.template insert<NodeHashFunctions>(
-          std::forward<IncomingValueType>(value), &anchor_);
-  Node* node = result.stored_value;
-  if (!result.is_new_entry) {
-    node->Unlink();
-    anchor_.InsertBefore(*node);
-  }
-  return result;
-}
-
-template <typename T, typename U, typename V, typename W>
-template <typename IncomingValueType>
-typename LegacyLinkedHashSet<T, U, V, W>::AddResult
-LegacyLinkedHashSet<T, U, V, W>::PrependOrMoveToFirst(
-    IncomingValueType&& value) {
-  typename ImplType::AddResult result =
-      impl_.template insert<NodeHashFunctions>(
-          std::forward<IncomingValueType>(value), anchor_.next_);
-  Node* node = result.stored_value;
-  if (!result.is_new_entry) {
-    node->Unlink();
-    anchor_.InsertAfter(*node);
-  }
-  return result;
-}
-
-template <typename T, typename U, typename V, typename W>
-template <typename IncomingValueType>
-typename LegacyLinkedHashSet<T, U, V, W>::AddResult
-LegacyLinkedHashSet<T, U, V, W>::InsertBefore(ValuePeekInType before_value,
-                                              IncomingValueType&& new_value) {
-  return InsertBefore(find(before_value),
-                      std::forward<IncomingValueType>(new_value));
-}
-
-template <typename T, typename U, typename V, typename W>
-inline void LegacyLinkedHashSet<T, U, V, W>::erase(const_iterator it) {
-  if (it == end())
-    return;
-  impl_.erase(it.GetNode());
-}
-
-template <typename T, typename U, typename V, typename W>
-inline void LegacyLinkedHashSet<T, U, V, W>::erase(ValuePeekInType value) {
-  erase(find(value));
-}
-
-template <typename T, typename U, typename V, typename Allocator>
-inline void swap(LegacyLinkedHashSetNode<T>& a, LegacyLinkedHashSetNode<T>& b) {
-  // The key and value cannot be swapped atomically, and it would be
-  // wrong to have a GC when only one was swapped and the other still
-  // contained garbage (eg. from a previous use of the same slot).
-  // Therefore we forbid a GC until both the key and the value are
-  // swapped.
-  Allocator::EnterGCForbiddenScope();
-  swap(static_cast<LegacyLinkedHashSetNodeBase&>(a),
-       static_cast<LegacyLinkedHashSetNodeBase&>(b));
-  swap(a.value_, b.value_);
-  Allocator::LeaveGCForbiddenScope();
-}
-
 // LinkedHashSet provides a Set interface like HashSet, but also has a
 // predictable iteration order. It has O(1) insertion, removal, and test for
 // containership. It maintains a linked list through its contents such that
@@ -1275,7 +370,6 @@
 
 }  // namespace WTF
 
-using WTF::LegacyLinkedHashSet;
 using WTF::LinkedHashSet;
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_LINKED_HASH_SET_H_
diff --git a/third_party/blink/renderer/platform/wtf/list_hash_set_test.cc b/third_party/blink/renderer/platform/wtf/list_hash_set_test.cc
index 048bcaf..b56f9f6 100644
--- a/third_party/blink/renderer/platform/wtf/list_hash_set_test.cc
+++ b/third_party/blink/renderer/platform/wtf/list_hash_set_test.cc
@@ -52,7 +52,6 @@
 
 using SetTypes = testing::Types<ListHashSet<int>,
                                 ListHashSet<int, 1>,
-                                LegacyLinkedHashSet<int>,
                                 LinkedHashSet<int, CustomHashTraitsForInt>>;
 TYPED_TEST_SUITE(ListOrLinkedHashSetTest, SetTypes);
 
@@ -240,7 +239,6 @@
 TYPED_TEST(ListOrLinkedHashSetTest, InsertBefore) {
   using Set = TypeParam;
   bool can_modify_while_iterating =
-      !std::is_same<Set, LegacyLinkedHashSet<int>>::value &&
       !std::is_same<Set, LinkedHashSet<int, CustomHashTraitsForInt>>::value;
   Set set;
   set.insert(-1);
@@ -373,7 +371,6 @@
 using RefPtrSetTypes =
     testing::Types<ListHashSet<scoped_refptr<DummyRefCounted>>,
                    ListHashSet<scoped_refptr<DummyRefCounted>, 1>,
-                   LegacyLinkedHashSet<scoped_refptr<DummyRefCounted>>,
                    LinkedHashSet<scoped_refptr<DummyRefCounted>>>;
 TYPED_TEST_SUITE(ListOrLinkedHashSetRefPtrTest, RefPtrSetTypes);
 
@@ -487,8 +484,7 @@
 // TODO(bartekn): Add LinkedHashSet once it supports custom hash function.
 using TranslatorSetTypes =
     testing::Types<ListHashSet<Complicated, 256, ComplicatedHashFunctions>,
-                   ListHashSet<Complicated, 1, ComplicatedHashFunctions>,
-                   LegacyLinkedHashSet<Complicated, ComplicatedHashFunctions>>;
+                   ListHashSet<Complicated, 1, ComplicatedHashFunctions>>;
 TYPED_TEST_SUITE(ListOrLinkedHashSetTranslatorTest, TranslatorSetTypes);
 
 TYPED_TEST(ListOrLinkedHashSetTranslatorTest, ComplexityTranslator) {
@@ -594,7 +590,6 @@
 
 using CountCopySetTypes = testing::Types<ListHashSet<CountCopy>,
                                          ListHashSet<CountCopy, 1>,
-                                         LegacyLinkedHashSet<CountCopy>,
                                          LinkedHashSet<CountCopy>>;
 TYPED_TEST_SUITE(ListOrLinkedHashSetCountCopyTest, CountCopySetTypes);
 
@@ -627,8 +622,7 @@
 
 // TODO(bartekn): Add LinkedHashSet once it supports move-only type.
 using MoveOnlySetTypes = testing::Types<ListHashSet<MoveOnlyHashValue>,
-                                        ListHashSet<MoveOnlyHashValue, 1>,
-                                        LegacyLinkedHashSet<MoveOnlyHashValue>>;
+                                        ListHashSet<MoveOnlyHashValue, 1>>;
 TYPED_TEST_SUITE(ListOrLinkedHashSetMoveOnlyTest, MoveOnlySetTypes);
 
 TYPED_TEST(ListOrLinkedHashSetMoveOnlyTest, MoveOnlyValue) {
@@ -724,8 +718,7 @@
 // LinkedHashSet is tested in LinkedHashSetEmptyTest.EmptyString
 using InvalidZeroValueSetTypes =
     testing::Types<ListHashSet<InvalidZeroValue>,
-                   ListHashSet<InvalidZeroValue, 1>,
-                   LegacyLinkedHashSet<InvalidZeroValue>>;
+                   ListHashSet<InvalidZeroValue, 1>>;
 TYPED_TEST_SUITE(ListOrLinkedHashSetInvalidZeroTest, InvalidZeroValueSetTypes);
 
 TYPED_TEST(ListOrLinkedHashSetInvalidZeroTest, InvalidZeroValue) {
diff --git a/third_party/blink/web_tests/FlagExpectations/disable-layout-ng b/third_party/blink/web_tests/FlagExpectations/disable-layout-ng
index a5f75b1e..85a8485 100644
--- a/third_party/blink/web_tests/FlagExpectations/disable-layout-ng
+++ b/third_party/blink/web_tests/FlagExpectations/disable-layout-ng
@@ -387,6 +387,8 @@
 crbug.com/591099 fast/writing-mode/flipped-blocks-inline-map-local-to-container.html [ Failure ]
 
 ### external/wpt/html/rendering
+crbug.com/591099 external/wpt/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-overflow-hidden.html [ Failure ]
+crbug.com/591099 external/wpt/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-overflow.html [ Crash Failure ]
 crbug.com/591099 external/wpt/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-vertical.html [ Failure ]
 crbug.com/875235 external/wpt/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-painting-order.html [ Failure ]
 
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 4b71364a..9c4ad93 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -827,8 +827,6 @@
 # Tentative mansonry tests
 crbug.com/1076027 external/wpt/css/css-grid/masonry/* [ Skip ]
 
-crbug.com/786475 external/wpt/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-overflow.html [ Failure Crash ]
-
 # ====== Layout team owned tests to here ======
 
 # ====== LayoutNG-only failures from here ======
@@ -2928,7 +2926,6 @@
 crbug.com/626703 external/wpt/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/reload.window.html [ Timeout ]
 crbug.com/626703 external/wpt/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-containing-block.html [ Failure ]
 crbug.com/626703 external/wpt/quirks/text-decoration-doesnt-propagate-into-tables/quirks.html [ Failure ]
-crbug.com/626703 external/wpt/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-overflow-hidden.html [ Failure ]
 crbug.com/626703 external/wpt/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-painting-order.html [ Failure ]
 crbug.com/626703 external/wpt/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/tasks.window.html [ Timeout ]
 crbug.com/875411 external/wpt/svg/text/reftests/text-complex-002.svg [ Failure ]
diff --git a/third_party/blink/web_tests/VirtualTestSuites b/third_party/blink/web_tests/VirtualTestSuites
index d9cf9b8..e0601eaf 100644
--- a/third_party/blink/web_tests/VirtualTestSuites
+++ b/third_party/blink/web_tests/VirtualTestSuites
@@ -111,8 +111,7 @@
               "compositing/filters"
               ],
     "args": ["--stable-release-mode",
-             "--disable-auto-wpt-origin-isolation",
-             "--enable-blink-features=CompositingOptimizations"]
+             "--disable-auto-wpt-origin-isolation"]
   },
   {
     "prefix": "feature-policy-permissions",
diff --git a/third_party/blink/web_tests/compositing/filters/sw-nested-shadow-overlaps-hw-nested-shadow-expected.txt b/third_party/blink/web_tests/compositing/filters/sw-nested-shadow-overlaps-hw-nested-shadow-expected.txt
index 16ea11b..ce951477 100644
--- a/third_party/blink/web_tests/compositing/filters/sw-nested-shadow-overlaps-hw-nested-shadow-expected.txt
+++ b/third_party/blink/web_tests/compositing/filters/sw-nested-shadow-overlaps-hw-nested-shadow-expected.txt
@@ -11,6 +11,19 @@
       "position": [-100, -100],
       "bounds": [200, 200],
       "transform": 1
+    },
+    {
+      "name": "LayoutNGBlockFlow (positioned) DIV id='software-parent'",
+      "bounds": [100, 100],
+      "contentsOpaque": true,
+      "backgroundColor": "#008000"
+    },
+    {
+      "name": "LayoutNGBlockFlow (positioned) DIV id='software-child'",
+      "position": [100, 100],
+      "bounds": [50, 50],
+      "contentsOpaque": true,
+      "backgroundColor": "#0000FF"
     }
   ],
   "transforms": [
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 a1394e1..07f3d04 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
@@ -79966,6 +79966,19 @@
        {}
       ]
      ],
+     "position-fixed-scroll-nested-fixed.html": [
+      "64586a207ed4b96c8e803f262e98e387f217f63a",
+      [
+       null,
+       [
+        [
+         "/css/css-position/position-fixed-scroll-nested-fixed-ref.html",
+         "=="
+        ]
+       ],
+       {}
+      ]
+     ],
      "position-relative-001.html": [
       "7ec9e4f767b4548e52e92b358b59e51376dea389",
       [
@@ -167320,7 +167333,7 @@
       []
      ],
      "event-dispatch.tentative-expected.txt": [
-      "0feefd2eedc72d4c5724d016e9ba3de6d51a4bb1",
+      "018e8365be3f8374e1311f627786b009193c120b",
       []
      ],
      "event-order.tentative-expected.txt": [
@@ -169722,7 +169735,7 @@
       []
      ],
      "inheritance-expected.txt": [
-      "a75fca4057ea2537d7cfdae56699339451ba0109",
+      "e271532d54d0af2336c53050bf95c93ca6e09089",
       []
      ],
      "pseudo-element-inline-box-ref.html": [
@@ -182897,6 +182910,10 @@
       "96dfe200fb0dbae31906305e1536cf32f4d96c09",
       []
      ],
+     "position-fixed-scroll-nested-fixed-ref.html": [
+      "024bf032d770c4c2a47b14715c279557dfaefefc",
+      []
+     ],
      "position-relative-table-left-ref.html": [
       "7c1193b80007d8e7f89b35400a6d2ea2266cb3ac",
       []
@@ -190437,10 +190454,6 @@
         "eabef3c49d4cdb31ca589fa67d358d8bdf5a1a7d",
         []
        ],
-       "quotes-expected.txt": [
-        "608583f4fae5a3256896769418c70dcaa5a70150",
-        []
-       ],
        "resources": {
         "testsuite.js": [
          "0ec25662c2392d4d80b1c67c19eacc3dc138bae0",
@@ -219531,7 +219544,7 @@
      []
     ],
     "web-share.idl": [
-     "b66bcd81fe9c951c2e70a58596380353b7046d97",
+     "acc54792fb453411018d8c2c5a72f4756577811c",
      []
     ],
     "webaudio.idl": [
@@ -219685,6 +219698,26 @@
      []
     ]
    },
+   "is-input-pending": {
+    "README.md": [
+     "7994b1a4ec36302942d1a6e6809e15505b727ed9",
+     []
+    ],
+    "resources": {
+     "blank.html": [
+      "f27d7148d3ff65b3c9f203c7c622156c1ea7547a",
+      []
+     ],
+     "input-onmessage.js": [
+      "eeaa82841edff0e62dd60a52b483c7bc4158c85b",
+      []
+     ],
+     "pending-input-utils.js": [
+      "489c16827a111a561eb1c2cccb72a4db526ca3e1",
+      []
+     ]
+    }
+   },
    "keyboard-lock": {
     "META.yml": [
      "e805a9d6658a2e731f625dc4567313aef7134643",
@@ -223761,6 +223794,20 @@
      }
     }
    },
+   "raw-sockets": {
+    "META.yml": [
+     "d424a0af6ecc77577524a4db9baf706ae4dc943c",
+     []
+    ],
+    "OWNERS": [
+     "66ede7333f1e030273c03c18b7a141fd2abe5c57",
+     []
+    ],
+    "README.md": [
+     "bcebe03c8f8ed9f758dca37debb3dc43a0057679",
+     []
+    ]
+   },
    "referrer-policy": {
     "4K": {
      "gen": {
@@ -229213,16 +229260,6 @@
       "0fd548724a2105f068448d8db07463bd95f71945",
       []
      ]
-    },
-    "subresource": {
-     "sxg-subresource-header-integrity-mismatch.tentative-expected.txt": [
-      "1cd5040229798c5f65dc9e6c4c116d29be22fba0",
-      []
-     ],
-     "sxg-subresource.tentative-expected.txt": [
-      "ee54a8ceeb96b741fd720f2304fc7909c2d41ec3",
-      []
-     ]
     }
    },
    "speech-api": {
@@ -270099,7 +270136,7 @@
       ]
      ],
      "event-dispatch.tentative.html": [
-      "d54031c2f4a6768e10303f3584096dad96676b0c",
+      "3e577d6ea6c67269766ed9ec38d13bd4cbda435c",
       [
        null,
        {
@@ -356879,7 +356916,110 @@
        ]
       }
      ]
-    ]
+    ],
+    "security": {
+     "cross-origin-subframe-complex-clip.sub.html": [
+      "0bbe9c078204b2448e9527dbd2a2afbfa6234c2f",
+      [
+       null,
+       {
+        "testdriver": true
+       }
+      ]
+     ],
+     "cross-origin-subframe-masked-complex-clip.sub.html": [
+      "e5a3d310a9aec3816e79374c302008527f388f2a",
+      [
+       null,
+       {
+        "testdriver": true
+       }
+      ]
+     ],
+     "cross-origin-subframe-masked-pointer-events-mixed-2.sub.html": [
+      "55cbc48b7d31cd84d768fd7e8f929a9779fe700d",
+      [
+       null,
+       {
+        "testdriver": true
+       }
+      ]
+     ],
+     "cross-origin-subframe-masked-pointer-events-mixed.sub.html": [
+      "b0363682ef4158e565fb8de3681ac1af7909e45f",
+      [
+       null,
+       {
+        "testdriver": true
+       }
+      ]
+     ],
+     "cross-origin-subframe-masked-pointer-events-none.sub.html": [
+      "a3f971cdfd2ef1d6e8d0318205124a41a4023104",
+      [
+       null,
+       {
+        "testdriver": true
+       }
+      ]
+     ],
+     "cross-origin-subframe-overlap.sub.html": [
+      "377eedbf35e2c2e81f61a65dd754f5fc08ab0a6f",
+      [
+       null,
+       {
+        "testdriver": true
+       }
+      ]
+     ],
+     "cross-origin-subframe-pointer-events-none.sub.html": [
+      "371a92866a6353c928cbf2a47c599ac3fec1fa86",
+      [
+       null,
+       {
+        "testdriver": true
+       }
+      ]
+     ],
+     "cross-origin-subframe-transformed.sub.html": [
+      "5dac5aa36b37c1bd893703662c55ee2879a87187",
+      [
+       null,
+       {
+        "testdriver": true
+       }
+      ]
+     ],
+     "cross-origin-subframe.sub.html": [
+      "d513fa94fc451861504700daa5dd177ae090b2ca",
+      [
+       null,
+       {
+        "testdriver": true
+       }
+      ]
+     ]
+    },
+    "tentative": {
+     "same-origin-subframe.sub.html": [
+      "ee85bc4d06ec1a45d672f4ea5a0ce6dff930b20a",
+      [
+       null,
+       {
+        "testdriver": true
+       }
+      ]
+     ],
+     "toplevel.html": [
+      "4b60571d53c4c0144799c84a1f4fe34b38eb471e",
+      [
+       null,
+       {
+        "testdriver": true
+       }
+      ]
+     ]
+    }
    },
    "js-self-profiling": {
     "idlharness.any.js": [
@@ -366863,7 +367003,7 @@
      ]
     ],
     "secure-payment-confirmation.https.html": [
-     "b8d8372d3cf70ccbe550566a1da0da0092466028",
+     "35de56ca0bf040daedc085aa5a44c23ab573de6f",
      [
       null,
       {}
@@ -369492,6 +369632,22 @@
      ]
     }
    },
+   "raw-sockets": {
+    "open-securecontext.http.html": [
+     "371f13781a4e2bdea9d9fa2545a13b8e4add9ba8",
+     [
+      null,
+      {}
+     ]
+    ],
+    "remotePort-required.html": [
+     "22bba2c1bd37fa3d79cd3da3f3d363dfe284e7ac",
+     [
+      null,
+      {}
+     ]
+    ]
+   },
    "referrer-policy": {
     "4K": {
      "gen": {
diff --git a/third_party/blink/web_tests/external/wpt/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-overflow-cssomview.html b/third_party/blink/web_tests/external/wpt/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-overflow-cssomview.html
new file mode 100644
index 0000000..c47c7cdd
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-overflow-cssomview.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+fieldset {
+  height: 200px;
+  overflow: scroll;
+  padding: 0;
+}
+
+.content {
+  height: 400px;
+}
+</style>
+
+<fieldset>
+  <legend>Legend</legend>
+  <div class="content"></div>
+</fieldset>
+
+<script>
+test(() => {
+  const fieldset = document.querySelector('fieldset');
+  assert_equals(getComputedStyle(fieldset)['overflow-x'], 'scroll');
+  assert_equals(getComputedStyle(fieldset)['overflow-y'], 'scroll');
+  assert_equals(fieldset.scrollHeight, 400);
+  fieldset.scrollTop = 500;
+  assert_greater_than_equal(fieldset.scrollTop, 200);
+}, 'Test cssom-view API for FIELDSET');
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/interfaces/web-share.idl b/third_party/blink/web_tests/external/wpt/interfaces/web-share.idl
index b66bcd81..acc54792 100644
--- a/third_party/blink/web_tests/external/wpt/interfaces/web-share.idl
+++ b/third_party/blink/web_tests/external/wpt/interfaces/web-share.idl
@@ -4,7 +4,7 @@
 // Source: Web Share API (https://w3c.github.io/web-share/)
 
 partial interface Navigator {
-  [SecureContext] Promise<void> share(optional ShareData data = {});
+  [SecureContext] Promise<undefined> share(optional ShareData data = {});
 };
 
 dictionary ShareData {
diff --git a/third_party/blink/web_tests/fast/canvas/OffscreenCanvas-drawText.html b/third_party/blink/web_tests/fast/canvas/OffscreenCanvas-drawText.html
new file mode 100644
index 0000000..6a2e1f8
--- /dev/null
+++ b/third_party/blink/web_tests/fast/canvas/OffscreenCanvas-drawText.html
@@ -0,0 +1,23 @@
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script type="text/javasctipt" id="worker">
+  var offscreenCanvas = new OffscreenCanvas(100, 100);
+  var ctx = offscreenCanvas.getContext("2d");
+  ctx.globalCompositeOperation = "copy";
+  ctx.rect(10, 10, 150, 100);
+  ctx.fill("evenodd");
+  ctx.lineTo(1,1);
+  ctx.fillText("", 1, 1);
+</script>
+<script>
+test(function() {
+    const worker = new Worker(
+        URL.createObjectURL(
+            new Blob(
+                [document.querySelector("#worker").textContent],
+                {type: 'text/javascript'}
+            )
+        )
+    );
+}, "crbug.com/1111737, pass by not crashing.");
+</script>
diff --git a/third_party/blink/web_tests/fast/forms/datetimelocal-multiple-fields/datetimelocal-multiple-fields-stepup-stepdown-from-renderer-expected.txt b/third_party/blink/web_tests/fast/forms/datetimelocal-multiple-fields/datetimelocal-multiple-fields-stepup-stepdown-from-renderer-expected.txt
index 11e22ff..a774a46 100644
--- a/third_party/blink/web_tests/fast/forms/datetimelocal-multiple-fields/datetimelocal-multiple-fields-stepup-stepdown-from-renderer-expected.txt
+++ b/third_party/blink/web_tests/fast/forms/datetimelocal-multiple-fields/datetimelocal-multiple-fields-stepup-stepdown-from-renderer-expected.txt
@@ -8,6 +8,11 @@
 PASS stepDown("2013-01-16T15:00", 1, "2013-01-16T15:00", "2013-01-16T17:00") is "2013-01-16T17:00"
 PASS stepUp("2013-01-16T17:00", 1, "2013-01-16T15:00", "2013-01-17T17:00") is "2013-01-16T18:00"
 PASS stepDown("2013-01-16T17:00", 1, "2013-01-16T17:00", "2013-01-17T20:00") is "2013-01-16T16:00"
+The hour field wraps am/pm.
+PASS stepUp("2013-01-16T11:00", 1, "2013-01-16T00:00", "2013-01-16T23:59") is "2013-01-16T12:00"
+PASS stepUp("2013-01-16T23:00", 1, "2013-01-16T00:00", "2013-01-16T23:59") is "2013-01-16T00:00"
+PASS stepDown("2013-01-16T12:00", 1, "2013-01-16T00:00", "2013-01-16T23:59") is "2013-01-16T11:00"
+PASS stepDown("2013-01-16T00:00", 1, "2013-01-16T00:00", "2013-01-16T23:59") is "2013-01-16T23:00"
 
 
 PASS successfullyParsed is true
diff --git a/third_party/blink/web_tests/fast/forms/datetimelocal-multiple-fields/datetimelocal-multiple-fields-stepup-stepdown-from-renderer.html b/third_party/blink/web_tests/fast/forms/datetimelocal-multiple-fields/datetimelocal-multiple-fields-stepup-stepdown-from-renderer.html
index d5d202c..fb3e7fe 100644
--- a/third_party/blink/web_tests/fast/forms/datetimelocal-multiple-fields/datetimelocal-multiple-fields-stepup-stepdown-from-renderer.html
+++ b/third_party/blink/web_tests/fast/forms/datetimelocal-multiple-fields/datetimelocal-multiple-fields-stepup-stepdown-from-renderer.html
@@ -53,6 +53,12 @@
 shouldBeEqualToString('stepUp("2013-01-16T17:00", 1, "2013-01-16T15:00", "2013-01-17T17:00")', '2013-01-16T18:00');
 shouldBeEqualToString('stepDown("2013-01-16T17:00", 1, "2013-01-16T17:00", "2013-01-17T20:00")', '2013-01-16T16:00');
 
+debug('The hour field wraps am/pm.');
+shouldBeEqualToString('stepUp("2013-01-16T11:00", 1, "2013-01-16T00:00", "2013-01-16T23:59")', '2013-01-16T12:00');
+shouldBeEqualToString('stepUp("2013-01-16T23:00", 1, "2013-01-16T00:00", "2013-01-16T23:59")', '2013-01-16T00:00');
+shouldBeEqualToString('stepDown("2013-01-16T12:00", 1, "2013-01-16T00:00", "2013-01-16T23:59")', '2013-01-16T11:00');
+shouldBeEqualToString('stepDown("2013-01-16T00:00", 1, "2013-01-16T00:00", "2013-01-16T23:59")', '2013-01-16T23:00');
+
 debug('');
 document.body.removeChild(input);
 </script>
diff --git a/third_party/blink/web_tests/fast/forms/datetimelocal-multiple-fields/datetimelocal-multiple-fields-value-set-24hr.html b/third_party/blink/web_tests/fast/forms/datetimelocal-multiple-fields/datetimelocal-multiple-fields-value-set-24hr.html
new file mode 100644
index 0000000..64afedf
--- /dev/null
+++ b/third_party/blink/web_tests/fast/forms/datetimelocal-multiple-fields/datetimelocal-multiple-fields-value-set-24hr.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<body>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../resources/common.js"></script>
+<script>
+
+input = document.createElement('input');
+input.type = 'datetime-local';
+input.step = '60';
+document.body.appendChild(input);
+
+function testTimeRollover(key1, key2, expected) {
+  test(() => {
+    input.value = "2020-08-21T00:00:00";
+    input.focus();
+    for (let i = 0; i < 3; i++)
+      eventSender.keyDown('ArrowRight');
+    eventSender.keyDown(key1);
+    eventSender.keyDown(key2);
+    assert_equals(input.value, '2020-08-21T' + expected);
+    input.blur();
+  }, `Putting ${key1} ${key2} to the hour field should set ${expected}`);
+}
+
+testTimeRollover('0', '1', '01:00');
+testTimeRollover('1', '2', '00:00');
+testTimeRollover('1', '3', '13:00');
+testTimeRollover('1', '9', '19:00');
+// 20+ is 2:--am
+testTimeRollover('2', '3', '02:03');
+
+</script>
+</body>
diff --git a/third_party/blink/web_tests/fast/forms/fieldset/overflow-scroll-interaction.html b/third_party/blink/web_tests/fast/forms/fieldset/overflow-scroll-interaction.html
new file mode 100644
index 0000000..5376862
--- /dev/null
+++ b/third_party/blink/web_tests/fast/forms/fieldset/overflow-scroll-interaction.html
@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/testdriver.js"></script>
+<script src="../../../resources/testdriver-vendor.js"></script>
+<script src="../../../resources/gesture-util.js"></script>
+<style>
+fieldset {
+  height: 200px;
+  overflow: scroll;
+  padding: 0;
+}
+
+.content {
+  height: 400px;
+}
+</style>
+
+<fieldset id="fs1">
+  <legend>Legend</legend>
+  <div class="content"></div>
+</fieldset>
+
+<fieldset id="fs2">
+  <legend>Legend</legend>
+  <div class="content"></div>
+</fieldset>
+
+<script>
+function eventPromise(target, type) {
+  return new Promise((resolve, reject) => {
+    target.addEventListener(type, resolve);
+  });
+}
+
+promise_test(async () => {
+  const fieldset = document.querySelector('#fs1');
+  assert_equals(fieldset.scrollHeight, 400);
+  await test_driver.click(fieldset);
+  assert_equals(fieldset.scrollTop, 0);
+  await test_driver.send_keys(fieldset, ' ');
+  await eventPromise(fieldset, 'scroll');
+  assert_greater_than(fieldset.scrollTop, 0);
+}, 'Pressing the space bar should scroll up');
+
+promise_test(async () => {
+  const fieldset = document.querySelector('#fs2');
+  assert_equals(fieldset.scrollHeight, 400);
+  assert_equals(fieldset.scrollTop, 0);
+  await wheelTick(0, 10, elementCenter(fieldset));
+  assert_greater_than(fieldset.scrollTop, 0);
+}, 'Wheel scroll should work');
+</script>
diff --git a/third_party/blink/web_tests/fast/forms/time-multiple-fields/time-multiple-fields-stepup-stepdown-from-renderer-hour.html b/third_party/blink/web_tests/fast/forms/time-multiple-fields/time-multiple-fields-stepup-stepdown-from-renderer-hour.html
index 907393e2..1385835 100644
--- a/third_party/blink/web_tests/fast/forms/time-multiple-fields/time-multiple-fields-stepup-stepdown-from-renderer-hour.html
+++ b/third_party/blink/web_tests/fast/forms/time-multiple-fields/time-multiple-fields-stepup-stepdown-from-renderer-hour.html
@@ -72,12 +72,12 @@
 
   assert_input_value_after_up('07:00', 1, '08:00');
   assert_input_value_after_down('07:00', 1, '06:00');
-  assert_input_value_after_up('11:00', 1, '00:00');
-  assert_input_value_after_down('00:00', 1, '11:00');
+  assert_input_value_after_up('11:00', 1, '12:00');
+  assert_input_value_after_down('00:00', 1, '23:00');
   assert_input_value_after_up('06:00', 7200, '08:00');
   assert_input_value_after_down('06:00', 7200, '04:00');
-  assert_input_value_after_up('10:00', 7200, '00:00');
-  assert_input_value_after_down('00:00', 7200, '10:00');
+  assert_input_value_after_up('10:00', 7200, '12:00');
+  assert_input_value_after_down('00:00', 7200, '22:00');
   assert_input_value_after_up('07:00', 7200, '08:00');
   assert_input_value_after_down('07:00', 7200, '06:00');
   assert_input_value_after_up('06:00', 3601, '07:00');
@@ -103,19 +103,37 @@
   assert_input_value_with_limits_after_up('15:00', 1, '13:00', '13:00', '13:00');
   assert_input_value_with_limits_after_down('15:00', 1, '13:00', '13:00', '13:00');
   assert_input_value_with_limits_after_up('12:00', 1, '12:00', '15:00', '13:00');
-  assert_input_value_with_limits_after_down('12:00', 1, '12:00', '15:00', '23:00');
+  assert_input_value_with_limits_after_down('12:00', 1, '12:00', '15:00', '11:00');
   assert_input_value_with_limits_after_up('15:00', 1, '12:00', '15:00', '16:00');
   assert_input_value_with_limits_after_down('15:00', 1, '12:00', '15:00', '14:00');
   assert_input_value_with_limits_after_up('12:00', 1, '10:00', '12:00', '13:00');
-  assert_input_value_with_limits_after_down('12:00', 1, '10:00', '12:00', '23:00');
+  assert_input_value_with_limits_after_down('12:00', 1, '10:00', '12:00', '11:00');
   assert_input_value_with_limits_after_up('00:00', 1, '00:00', '03:00', '01:00');
-  assert_input_value_with_limits_after_down('00:00', 1, '00:00', '03:00', '11:00');
+  assert_input_value_with_limits_after_down('00:00', 1, '00:00', '03:00', '23:00');
   assert_input_value_with_limits_after_up('15:00', 1, '10:00', '15:00', '16:00');
   assert_input_value_with_limits_after_down('10:00', 1, '10:00', '15:00', '09:00');
   assert_input_value_with_limits_after_up('17:00', 7200, '17:00', '20:00', '19:00');
   assert_input_value_with_limits_after_down('17:00', 7200, '17:00', '20:00', '19:00');
   assert_input_value_with_limits_after_up('17:00', 7200, '17:00', '18:00', '17:00');
   assert_input_value_with_limits_after_down('17:00', 7200, '17:00', '18:00', '17:00');
+  // Don't update am/pm if hours is initially blank
+  assert_input_value_with_limits_after_up('', 60, '', '', '', '01:-- --');
+  assert_input_value_with_limits_after_down('', 60, '', '', '', '12:-- --');
+  assert_input_value_with_limits_after_up('', 43200, '', '', '', '12:00 --');
+  assert_input_value_with_limits_after_down('', 43200, '', '', '', '12:00 --');
+  // Check rollover for different steps
+  assert_input_value_with_limits_after_up('12:00', 3600, '', '', '13:00');
+  assert_input_value_with_limits_after_down('00:00', 3600, '', '', '23:00');
+  assert_input_value_with_limits_after_up('12:00', 7200, '', '', '14:00');
+  assert_input_value_with_limits_after_down('00:00', 7200, '', '', '22:00');
+  assert_input_value_with_limits_after_up('12:00', 10800, '', '', '15:00');
+  assert_input_value_with_limits_after_down('00:00', 10800, '', '', '21:00');
+  assert_input_value_with_limits_after_up('12:00', 14400, '', '', '16:00');
+  assert_input_value_with_limits_after_down('00:00', 14400, '', '', '20:00');
+  assert_input_value_with_limits_after_up('12:00', 21600, '', '', '18:00');
+  assert_input_value_with_limits_after_down('00:00', 21600, '', '', '18:00');
+  assert_input_value_with_limits_after_up('12:00', 43200, '', '', '12:00');
+  assert_input_value_with_limits_after_down('00:00', 43200, '', '', '00:00');
 }, 'Hours, 1-12');
 
 // Hours, 0-11
@@ -152,6 +170,12 @@
   assert_input_value_with_limits_after_down('10:00', 1, '10:00', '15:00', '09:00');
   assert_input_value_with_limits_after_up('20:00', 7200, '17:00', '20:00', '17:00');
   assert_input_value_with_limits_after_down('20:00', 7200, '17:00', '20:00', '19:00');
+
+  // Don't update am/pm if hours is initially blank
+  assert_input_value_with_limits_after_up('', 60, '', '', '', '00:-- --');
+  assert_input_value_with_limits_after_down('', 60, '', '', '', '11:-- --');
+  assert_input_value_with_limits_after_up('', 43200, '', '', '', '00:00 --');
+  assert_input_value_with_limits_after_down('', 43200, '', '', '', '00:00 --');
 }, 'Hours, 0-11');
 
 // Hours, 0-23
diff --git a/third_party/blink/web_tests/fast/forms/time-multiple-fields/time-multiple-fields-value-set-24hr.html b/third_party/blink/web_tests/fast/forms/time-multiple-fields/time-multiple-fields-value-set-24hr.html
new file mode 100644
index 0000000..0fea876
--- /dev/null
+++ b/third_party/blink/web_tests/fast/forms/time-multiple-fields/time-multiple-fields-value-set-24hr.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<body>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../resources/common.js"></script>
+<script>
+
+input = document.createElement('input');
+input.type = 'time';
+input.step = '60';
+document.body.appendChild(input);
+
+function testTimeRollover(key1, key2, expected) {
+  test(() => {
+    input.value = "00:00:00";
+    input.focus();
+    eventSender.keyDown(key1);
+    eventSender.keyDown(key2);
+    assert_equals(input.value, expected);
+    input.blur();
+  }, `Putting ${key1} ${key2} to the hour field should set ${expected}`);
+}
+
+testTimeRollover('0', '1', '01:00');
+testTimeRollover('1', '2', '00:00');
+testTimeRollover('1', '3', '13:00');
+testTimeRollover('1', '9', '19:00');
+// 20+ is 2:--am
+testTimeRollover('2', '3', '02:03');
+
+</script>
+</body>
diff --git a/third_party/blink/web_tests/flag-specific/disable-layout-ng/external/wpt/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-overflow-cssomview-expected.txt b/third_party/blink/web_tests/flag-specific/disable-layout-ng/external/wpt/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-overflow-cssomview-expected.txt
new file mode 100644
index 0000000..318cc52
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/disable-layout-ng/external/wpt/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-overflow-cssomview-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL Test cssom-view API for FIELDSET assert_equals: expected 400 but got 418
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/flag-specific/disable-layout-ng/fast/forms/fieldset/overflow-scroll-interaction-expected.txt b/third_party/blink/web_tests/flag-specific/disable-layout-ng/fast/forms/fieldset/overflow-scroll-interaction-expected.txt
new file mode 100644
index 0000000..1e1975e0
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/disable-layout-ng/fast/forms/fieldset/overflow-scroll-interaction-expected.txt
@@ -0,0 +1,5 @@
+This is a testharness.js-based test.
+FAIL Pressing the space bar should scroll up assert_equals: expected 400 but got 418
+FAIL Wheel scroll should work assert_equals: expected 400 but got 418
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/platform/linux/fast/multicol/vertical-rl/composited-relpos-overlapping-will-change-expected.png b/third_party/blink/web_tests/platform/linux/fast/multicol/vertical-rl/composited-relpos-overlapping-will-change-expected.png
index 64bbd30..35b9d86 100644
--- a/third_party/blink/web_tests/platform/linux/fast/multicol/vertical-rl/composited-relpos-overlapping-will-change-expected.png
+++ b/third_party/blink/web_tests/platform/linux/fast/multicol/vertical-rl/composited-relpos-overlapping-will-change-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/multicol/vertical-rl/composited-relpos-overlapping-will-change-expected.png b/third_party/blink/web_tests/platform/mac/fast/multicol/vertical-rl/composited-relpos-overlapping-will-change-expected.png
index f7dd9a0e..e22a4f65 100644
--- a/third_party/blink/web_tests/platform/mac/fast/multicol/vertical-rl/composited-relpos-overlapping-will-change-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/multicol/vertical-rl/composited-relpos-overlapping-will-change-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/multicol/vertical-rl/composited-relpos-overlapping-will-change-expected.png b/third_party/blink/web_tests/platform/win/fast/multicol/vertical-rl/composited-relpos-overlapping-will-change-expected.png
index a8c7750..9224c4c 100644
--- a/third_party/blink/web_tests/platform/win/fast/multicol/vertical-rl/composited-relpos-overlapping-will-change-expected.png
+++ b/third_party/blink/web_tests/platform/win/fast/multicol/vertical-rl/composited-relpos-overlapping-will-change-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/stable/compositing/filters/sw-nested-shadow-overlaps-hw-nested-shadow-expected.txt b/third_party/blink/web_tests/virtual/stable/compositing/filters/sw-nested-shadow-overlaps-hw-nested-shadow-expected.txt
index ce951477..16ea11b 100644
--- a/third_party/blink/web_tests/virtual/stable/compositing/filters/sw-nested-shadow-overlaps-hw-nested-shadow-expected.txt
+++ b/third_party/blink/web_tests/virtual/stable/compositing/filters/sw-nested-shadow-overlaps-hw-nested-shadow-expected.txt
@@ -11,19 +11,6 @@
       "position": [-100, -100],
       "bounds": [200, 200],
       "transform": 1
-    },
-    {
-      "name": "LayoutNGBlockFlow (positioned) DIV id='software-parent'",
-      "bounds": [100, 100],
-      "contentsOpaque": true,
-      "backgroundColor": "#008000"
-    },
-    {
-      "name": "LayoutNGBlockFlow (positioned) DIV id='software-child'",
-      "position": [100, 100],
-      "bounds": [50, 50],
-      "contentsOpaque": true,
-      "backgroundColor": "#0000FF"
     }
   ],
   "transforms": [
diff --git a/third_party/closure_compiler/externs/file_manager_private.js b/third_party/closure_compiler/externs/file_manager_private.js
index 40a5906..6d5ec20 100644
--- a/third_party/closure_compiler/externs/file_manager_private.js
+++ b/third_party/closure_compiler/externs/file_manager_private.js
@@ -572,6 +572,13 @@
 chrome.fileManagerPrivate.MediaMetadata;
 
 /**
+ * @typedef {{
+ *   itemUrls: !Array<string>
+ * }}
+ */
+chrome.fileManagerPrivate.HoldingSpaceState;
+
+/**
  * Logout the current user for navigating to the re-authentication screen for
  * the Google account.
  */
@@ -1130,6 +1137,26 @@
  */
 chrome.fileManagerPrivate.invokeSharesheet = function(entries, callback) {};
 
+/**
+ * Adds or removes a list of entries to temporary holding space. Any entries
+ * whose current holding space state matches the intended state will be skipped.
+ * |entries| The list of entries whose holding space needs to be updated. |add|
+ * Whether items should be added or removed from the holding space. |callback|
+ * Completion callback.
+ * @param {!Array<!Entry>} entries
+ * @param {boolean} added
+ * @param {function(): void=} callback Callback that does not take arguments.
+ */
+chrome.fileManagerPrivate.toggleAddedToHoldingSpace = function(entries, added, callback) {};
+
+/**
+ * Retrieves the current holding space state, for example the list of items the
+ * holding space currently contains. |callback| The result callback.
+ * @param {function(!chrome.fileManagerPrivate.HoldingSpaceState): void}
+ *     callback |state| Describes the current holding space state.
+ */
+chrome.fileManagerPrivate.getHoldingSpaceState = function(callback) {};
+
 /** @type {!ChromeEvent} */
 chrome.fileManagerPrivate.onMountCompleted;
 
diff --git a/third_party/gvr-android-sdk/controller_test_api_java.info b/third_party/gvr-android-sdk/controller_test_api_java.info
index da8df19..02a8986 100644
--- a/third_party/gvr-android-sdk/controller_test_api_java.info
+++ b/third_party/gvr-android-sdk/controller_test_api_java.info
@@ -8,6 +8,7 @@
 has_proguard_flags = false
 has_r_text_file = false
 is_manifest_empty = true
+manifest_package = "com.google.vr.testframework.controller"
 resources = []
 subjar_tuples = []
 subjars = []
diff --git a/third_party/gvr-android-sdk/gvr_common_java.info b/third_party/gvr-android-sdk/gvr_common_java.info
index 3f2e07d..788e67c 100644
--- a/third_party/gvr-android-sdk/gvr_common_java.info
+++ b/third_party/gvr-android-sdk/gvr_common_java.info
@@ -8,6 +8,7 @@
 has_proguard_flags = false
 has_r_text_file = true
 is_manifest_empty = true
+manifest_package = "com.google.vr.cardboard"
 resources = [
   "res/drawable-hdpi-v4/quantum_ic_close_white_24.png",
   "res/drawable-hdpi-v4/quantum_ic_settings_white_24.png",
diff --git a/third_party/gvr-android-sdk/gvr_controller_java.info b/third_party/gvr-android-sdk/gvr_controller_java.info
index da8df19..f647256a 100644
--- a/third_party/gvr-android-sdk/gvr_controller_java.info
+++ b/third_party/gvr-android-sdk/gvr_controller_java.info
@@ -8,6 +8,7 @@
 has_proguard_flags = false
 has_r_text_file = false
 is_manifest_empty = true
+manifest_package = "com.google.vr.sdk.controller"
 resources = []
 subjar_tuples = []
 subjars = []
diff --git a/third_party/nearby/BUILD.gn b/third_party/nearby/BUILD.gn
index 06f8a30..de284bc 100644
--- a/third_party/nearby/BUILD.gn
+++ b/third_party/nearby/BUILD.gn
@@ -119,10 +119,12 @@
   public_configs = [ ":nearby_include_config" ]
   sources = [
     "src/cpp/platform_v2/base/base64_utils.cc",
+    "src/cpp/platform_v2/base/bluetooth_utils.cc",
     "src/cpp/platform_v2/base/prng.cc",
   ]
   public = [
     "src/cpp/platform_v2/base/base64_utils.h",
+    "src/cpp/platform_v2/base/bluetooth_utils.h",
     "src/cpp/platform_v2/base/byte_array.h",
     "src/cpp/platform_v2/base/callable.h",
     "src/cpp/platform_v2/base/exception.h",
@@ -213,10 +215,12 @@
 source_set("platform_v2_public_comm") {
   public_configs = [ ":nearby_include_config" ]
   sources = [
+    "src/cpp/platform_v2/public/ble.cc",
     "src/cpp/platform_v2/public/bluetooth_classic.cc",
     "src/cpp/platform_v2/public/wifi_lan.cc",
   ]
   public = [
+    "src/cpp/platform_v2/public/ble.h",
     "src/cpp/platform_v2/public/bluetooth_adapter.h",
     "src/cpp/platform_v2/public/bluetooth_classic.h",
     "src/cpp/platform_v2/public/webrtc.h",
@@ -250,6 +254,7 @@
   sources = [ "src/cpp/core_v2/core.cc" ]
   public = [ "src/cpp/core_v2/core.h" ]
   public_deps = [
+    ":connections_enums_proto",
     ":core_v2_internal",
     ":core_v2_types",
     ":platform_v2_public_comm",
@@ -273,6 +278,7 @@
     "src/cpp/core_v2/strategy.h",
   ]
   public_deps = [
+    ":connections_enums_proto",
     ":platform_v2_base",
     ":platform_v2_public_comm",
     ":platform_v2_public_logging",
@@ -291,6 +297,7 @@
     "src/cpp/core_v2/internal/base_endpoint_channel.cc",
     "src/cpp/core_v2/internal/base_pcp_handler.cc",
     "src/cpp/core_v2/internal/ble_advertisement.cc",
+    "src/cpp/core_v2/internal/ble_endpoint_channel.cc",
     "src/cpp/core_v2/internal/bluetooth_device_name.cc",
     "src/cpp/core_v2/internal/bluetooth_endpoint_channel.cc",
     "src/cpp/core_v2/internal/client_proxy.cc",
@@ -316,6 +323,7 @@
     "src/cpp/core_v2/internal/base_endpoint_channel.h",
     "src/cpp/core_v2/internal/base_pcp_handler.h",
     "src/cpp/core_v2/internal/ble_advertisement.h",
+    "src/cpp/core_v2/internal/ble_endpoint_channel.h",
     "src/cpp/core_v2/internal/bluetooth_device_name.h",
     "src/cpp/core_v2/internal/bluetooth_endpoint_channel.h",
     "src/cpp/core_v2/internal/client_proxy.h",
@@ -374,6 +382,7 @@
   public_configs = [ ":nearby_include_config" ]
   sources = [
     "src/cpp/core_v2/internal/mediums/advertisement_read_result.cc",
+    "src/cpp/core_v2/internal/mediums/ble.cc",
     "src/cpp/core_v2/internal/mediums/ble_advertisement.cc",
     "src/cpp/core_v2/internal/mediums/ble_advertisement_header.cc",
     "src/cpp/core_v2/internal/mediums/ble_packet.cc",
@@ -387,6 +396,7 @@
   ]
   public = [
     "src/cpp/core_v2/internal/mediums/advertisement_read_result.h",
+    "src/cpp/core_v2/internal/mediums/ble.h",
     "src/cpp/core_v2/internal/mediums/ble_advertisement.h",
     "src/cpp/core_v2/internal/mediums/ble_advertisement_header.h",
     "src/cpp/core_v2/internal/mediums/ble_packet.h",
diff --git a/tools/clang/blink_gc_plugin/tests/heap/stubs.h b/tools/clang/blink_gc_plugin/tests/heap/stubs.h
index 4ea8b61..392d7f4 100644
--- a/tools/clang/blink_gc_plugin/tests/heap/stubs.h
+++ b/tools/clang/blink_gc_plugin/tests/heap/stubs.h
@@ -102,7 +102,6 @@
 };
 
 template <typename ValueArg,
-          typename HashArg = void,
           typename TraitsArg = void,
           typename Allocator = DefaultAllocator>
 class LinkedHashSet {
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index a85d54d..04fb3e3 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -10664,6 +10664,7 @@
   <int value="5" label="Data"/>
   <int value="6" label="CustomData"/>
   <int value="7" label="WebSmartPaste"/>
+  <int value="8" label="Svg"/>
 </enum>
 
 <enum name="ClipboardFormatWrite">
@@ -10675,6 +10676,7 @@
   <int value="5" label="Data"/>
   <int value="6" label="CustomData"/>
   <int value="7" label="WebSmartPaste"/>
+  <int value="8" label="Svg"/>
 </enum>
 
 <enum name="ClockdriftLevel">
@@ -24418,6 +24420,12 @@
   <int value="1498" label="FILEMANAGERPRIVATEINTERNAL_GETCONTENTMETADATA"/>
   <int value="1499" label="SEARCH_QUERY"/>
   <int value="1500" label="FILEMANAGERPRIVATEINTERNAL_COPYIMAGETOCLIPBOARD"/>
+  <int value="1501"
+      label="AUTOTESTPRIVATE_WAITFORSYSTEMWEBAPPSINSTALLFUNCTION"/>
+  <int value="1502" label="AUTOTESTPRIVATE_GETREGISTEREDSYSTEMWEBAPPSFUNCTION"/>
+  <int value="1503"
+      label="FILEMANAGERPRIVATEINTERNAL_TOGGLEADDEDTOHOLDINGSPACE"/>
+  <int value="1504" label="FILEMANAGERPRIVATEINTERNAL_GETHOLDINGSPACESTATE"/>
 </enum>
 
 <enum name="ExtensionIconState">
@@ -59115,6 +59123,7 @@
   <int value="9" label="getSubscription() storage corrupt"/>
   <int value="10" label="Service Worker database got wiped"/>
   <int value="11" label="Subscription has expired"/>
+  <int value="12" label="Subscription has been refreshed"/>
 </enum>
 
 <enum name="PushUnregistrationStatus">
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json
index d24446c..66a6ee6 100644
--- a/tools/perf/core/perfetto_binary_roller/binary_deps.json
+++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -6,11 +6,11 @@
         },
         "mac": {
             "hash": "f946b367c73e6bcbcd585ba616226febf2836477",
-            "remote_path": "perfetto_binaries/trace_processor_shell/mac/ed2e4739280f8f0971c39966ac6319295b6598e4/trace_processor_shell"
+            "remote_path": "perfetto_binaries/trace_processor_shell/mac/33a6332a92323279d37beafa0ee8cf181a759672/trace_processor_shell"
         },
         "linux": {
             "hash": "a936e4844a2c42e3384d6e7fc96b00782ff6ef56",
-            "remote_path": "perfetto_binaries/trace_processor_shell/linux/ed2e4739280f8f0971c39966ac6319295b6598e4/trace_processor_shell"
+            "remote_path": "perfetto_binaries/trace_processor_shell/linux/33a6332a92323279d37beafa0ee8cf181a759672/trace_processor_shell"
         }
     },
     "power_profile.sql": {
diff --git a/ui/base/clipboard/clipboard.cc b/ui/base/clipboard/clipboard.cc
index a7722e26..b7445c0a 100644
--- a/ui/base/clipboard/clipboard.cc
+++ b/ui/base/clipboard/clipboard.cc
@@ -127,6 +127,10 @@
       }
       break;
 
+    case PortableFormat::kSvg:
+      WriteSvg(&(params[0].front()), params[0].size());
+      break;
+
     case PortableFormat::kRtf:
       WriteRTF(&(params[0].front()), params[0].size());
       break;
diff --git a/ui/base/clipboard/clipboard.h b/ui/base/clipboard/clipboard.h
index 62004e65..8055078 100644
--- a/ui/base/clipboard/clipboard.h
+++ b/ui/base/clipboard/clipboard.h
@@ -180,6 +180,12 @@
                         uint32_t* fragment_start,
                         uint32_t* fragment_end) const = 0;
 
+  // Reads an SVG image from the clipboard, if available.
+  // TODO(crbug.com/1103614): Update |data_dst| in all references to its
+  // appropriate ClipboardDataEndpoint for web-originates uses.
+  virtual void ReadSvg(ClipboardBuffer buffer,
+                       const ClipboardDataEndpoint* data_dst,
+                       base::string16* result) const = 0;
   // Reads RTF from the clipboard, if available. Stores the result as a byte
   // vector.
   // TODO(crbug.com/1103614): Update |data_dst| in all references to its
@@ -255,6 +261,7 @@
     kText,
     kWebkit,
     kData,  // Arbitrary block of bytes.
+    kSvg,
   };
 
   // TODO (https://crbug.com/994928): Rename ObjectMap-related types.
@@ -331,6 +338,8 @@
                          const char* url_data,
                          size_t url_len) = 0;
 
+  virtual void WriteSvg(const char* markup_data, size_t markup_len) = 0;
+
   virtual void WriteRTF(const char* rtf_data, size_t data_len) = 0;
 
   virtual void WriteBookmark(const char* title_data,
diff --git a/ui/base/clipboard/clipboard_android.cc b/ui/base/clipboard/clipboard_android.cc
index f8bcfc6..c1bafd4 100644
--- a/ui/base/clipboard/clipboard_android.cc
+++ b/ui/base/clipboard/clipboard_android.cc
@@ -485,6 +485,18 @@
 
 // |data_dst| is not used. It's only passed to be consistent with other
 // platforms.
+void ClipboardAndroid::ReadSvg(ClipboardBuffer buffer,
+                               const ClipboardDataEndpoint* data_dst,
+                               base::string16* result) const {
+  DCHECK(CalledOnValidThread());
+  DCHECK_EQ(buffer, ClipboardBuffer::kCopyPaste);
+  std::string utf8 =
+      g_map.Get().Get(ClipboardFormatType::GetSvgType().GetName());
+  *result = base::UTF8ToUTF16(utf8);
+}
+
+// |data_dst| is not used. It's only passed to be consistent with other
+// platforms.
 void ClipboardAndroid::ReadRTF(ClipboardBuffer buffer,
                                const ClipboardDataEndpoint* data_dst,
                                std::string* result) const {
@@ -587,6 +599,11 @@
                   std::string(markup_data, markup_len));
 }
 
+void ClipboardAndroid::WriteSvg(const char* markup_data, size_t markup_len) {
+  g_map.Get().Set(ClipboardFormatType::GetSvgType().GetName(),
+                  std::string(markup_data, markup_len));
+}
+
 void ClipboardAndroid::WriteRTF(const char* rtf_data, size_t data_len) {
   NOTIMPLEMENTED();
 }
diff --git a/ui/base/clipboard/clipboard_android.h b/ui/base/clipboard/clipboard_android.h
index 09258ce4..b12d551 100644
--- a/ui/base/clipboard/clipboard_android.h
+++ b/ui/base/clipboard/clipboard_android.h
@@ -83,6 +83,9 @@
                 std::string* src_url,
                 uint32_t* fragment_start,
                 uint32_t* fragment_end) const override;
+  void ReadSvg(ClipboardBuffer buffer,
+               const ClipboardDataEndpoint* data_dst,
+               base::string16* result) const override;
   void ReadRTF(ClipboardBuffer buffer,
                const ClipboardDataEndpoint* data_dst,
                std::string* result) const override;
@@ -115,6 +118,7 @@
                  size_t markup_len,
                  const char* url_data,
                  size_t url_len) override;
+  void WriteSvg(const char* markup_data, size_t markup_len) override;
   void WriteRTF(const char* rtf_data, size_t data_len) override;
   void WriteBookmark(const char* title_data,
                      size_t title_len,
diff --git a/ui/base/clipboard/clipboard_constants.cc b/ui/base/clipboard/clipboard_constants.cc
index dc9197c..9fafdfa 100644
--- a/ui/base/clipboard/clipboard_constants.cc
+++ b/ui/base/clipboard/clipboard_constants.cc
@@ -12,6 +12,7 @@
 const char kMimeTypeMozillaURL[] = "text/x-moz-url";
 const char kMimeTypeDownloadURL[] = "downloadurl";
 const char kMimeTypeHTML[] = "text/html";
+const char kMimeTypeSvg[] = "image/svg+xml";
 const char kMimeTypeRTF[] = "text/rtf";
 const char kMimeTypePNG[] = "image/png";
 
diff --git a/ui/base/clipboard/clipboard_constants.h b/ui/base/clipboard/clipboard_constants.h
index 8808a9917..aceeb8e 100644
--- a/ui/base/clipboard/clipboard_constants.h
+++ b/ui/base/clipboard/clipboard_constants.h
@@ -32,6 +32,7 @@
 COMPONENT_EXPORT(UI_BASE_CLIPBOARD_TYPES)
 extern const char kMimeTypeMozillaURL[];
 COMPONENT_EXPORT(UI_BASE_CLIPBOARD_TYPES) extern const char kMimeTypeHTML[];
+COMPONENT_EXPORT(UI_BASE_CLIPBOARD_TYPES) extern const char kMimeTypeSvg[];
 COMPONENT_EXPORT(UI_BASE_CLIPBOARD_TYPES) extern const char kMimeTypeRTF[];
 COMPONENT_EXPORT(UI_BASE_CLIPBOARD_TYPES) extern const char kMimeTypePNG[];
 
@@ -56,6 +57,10 @@
 #else
 // MacOS-specific Uniform Type Identifiers.
 
+// SVG images.
+COMPONENT_EXPORT(UI_BASE_CLIPBOARD_TYPES)
+extern NSString* const kImageSvg;
+
 // Pickled data.
 COMPONENT_EXPORT(UI_BASE_CLIPBOARD_TYPES)
 extern NSString* const kWebCustomDataPboardType;
diff --git a/ui/base/clipboard/clipboard_constants_mac.mm b/ui/base/clipboard/clipboard_constants_mac.mm
index 8633a20..a7bc458 100644
--- a/ui/base/clipboard/clipboard_constants_mac.mm
+++ b/ui/base/clipboard/clipboard_constants_mac.mm
@@ -8,6 +8,7 @@
 
 namespace ui {
 
+NSString* const kImageSvg = @"public.svg-image";
 // TODO(dcheng): This name is temporary. See crbug.com/106449.
 NSString* const kWebCustomDataPboardType = @"org.chromium.web-custom-data";
 NSString* const kWebSmartPastePboardType = @"NeXT smart paste pasteboard type";
diff --git a/ui/base/clipboard/clipboard_data.cc b/ui/base/clipboard/clipboard_data.cc
index d887c1ed..9c83e26 100644
--- a/ui/base/clipboard/clipboard_data.cc
+++ b/ui/base/clipboard/clipboard_data.cc
@@ -28,6 +28,7 @@
   custom_data_format_ = other.custom_data_format_;
   custom_data_data_ = other.custom_data_data_;
   web_smart_paste_ = other.web_smart_paste_;
+  svg_data_ = other.svg_data_;
   src_ = other.src_ ? std::make_unique<ClipboardDataEndpoint>(*other.src_.get())
                     : nullptr;
 }
@@ -45,6 +46,7 @@
          custom_data_format_ == that.custom_data_format() &&
          custom_data_data_ == that.custom_data_data() &&
          web_smart_paste_ == that.web_smart_paste() &&
+         svg_data_ == that.svg_data() &&
          gfx::BitmapsAreEqual(bitmap_, that.bitmap()) &&
          (src_.get() ? (that.source() && *src_.get() == *that.source())
                      : !that.source());
diff --git a/ui/base/clipboard/clipboard_data.h b/ui/base/clipboard/clipboard_data.h
index edbd558f..a9eec23 100644
--- a/ui/base/clipboard/clipboard_data.h
+++ b/ui/base/clipboard/clipboard_data.h
@@ -20,11 +20,12 @@
 enum class ClipboardInternalFormat {
   kText = 1 << 0,
   kHtml = 1 << 1,
-  kRtf = 1 << 2,
-  kBookmark = 1 << 3,
-  kBitmap = 1 << 4,
-  kCustom = 1 << 5,
-  kWeb = 1 << 6,
+  kSvg = 1 << 2,
+  kRtf = 1 << 3,
+  kBookmark = 1 << 4,
+  kBitmap = 1 << 5,
+  kCustom = 1 << 6,
+  kWeb = 1 << 7,
 };
 
 // ClipboardData contains data copied to the Clipboard for a variety of formats.
@@ -55,6 +56,12 @@
     format_ |= static_cast<int>(ClipboardInternalFormat::kHtml);
   }
 
+  const std::string& svg_data() const { return svg_data_; }
+  void set_svg_data(const std::string& svg_data) {
+    svg_data_ = svg_data;
+    format_ |= static_cast<int>(ClipboardInternalFormat::kSvg);
+  }
+
   const std::string& rtf_data() const { return rtf_data_; }
   void SetRTFData(const std::string& rtf_data) {
     rtf_data_ = rtf_data;
@@ -124,6 +131,9 @@
   // WebKit smart paste data.
   bool web_smart_paste_;
 
+  // Svg data.
+  std::string svg_data_;
+
   int format_;
 
   // The source of the data.
diff --git a/ui/base/clipboard/clipboard_format_type.h b/ui/base/clipboard/clipboard_format_type.h
index 17480a7d..297bff6 100644
--- a/ui/base/clipboard/clipboard_format_type.h
+++ b/ui/base/clipboard/clipboard_format_type.h
@@ -61,6 +61,7 @@
   static const ClipboardFormatType& GetWebKitSmartPasteType();
   // Win: MS HTML Format, Other: Generic HTML format
   static const ClipboardFormatType& GetHtmlType();
+  static const ClipboardFormatType& GetSvgType();
   static const ClipboardFormatType& GetRtfType();
   static const ClipboardFormatType& GetBitmapType();
   // TODO(raymes): Unify web custom data and pepper custom data:
diff --git a/ui/base/clipboard/clipboard_format_type_android.cc b/ui/base/clipboard/clipboard_format_type_android.cc
index 07564e07..12a132ed 100644
--- a/ui/base/clipboard/clipboard_format_type_android.cc
+++ b/ui/base/clipboard/clipboard_format_type_android.cc
@@ -72,6 +72,12 @@
 }
 
 // static
+const ClipboardFormatType& ClipboardFormatType::GetSvgType() {
+  static base::NoDestructor<ClipboardFormatType> type(kMimeTypeSvg);
+  return *type;
+}
+
+// static
 const ClipboardFormatType& ClipboardFormatType::GetRtfType() {
   static base::NoDestructor<ClipboardFormatType> type(kMimeTypeRTF);
   return *type;
diff --git a/ui/base/clipboard/clipboard_format_type_aura.cc b/ui/base/clipboard/clipboard_format_type_aura.cc
index 0ea472a7..024b20c 100644
--- a/ui/base/clipboard/clipboard_format_type_aura.cc
+++ b/ui/base/clipboard/clipboard_format_type_aura.cc
@@ -84,6 +84,12 @@
 }
 
 // static
+const ClipboardFormatType& ClipboardFormatType::GetSvgType() {
+  static base::NoDestructor<ClipboardFormatType> type(kMimeTypeSvg);
+  return *type;
+}
+
+// static
 const ClipboardFormatType& ClipboardFormatType::GetRtfType() {
   static base::NoDestructor<ClipboardFormatType> type(kMimeTypeRTF);
   return *type;
diff --git a/ui/base/clipboard/clipboard_format_type_mac.mm b/ui/base/clipboard/clipboard_format_type_mac.mm
index dce0ff7..7864dbf 100644
--- a/ui/base/clipboard/clipboard_format_type_mac.mm
+++ b/ui/base/clipboard/clipboard_format_type_mac.mm
@@ -87,6 +87,11 @@
   return *type;
 }
 
+const ClipboardFormatType& ClipboardFormatType::GetSvgType() {
+  static base::NoDestructor<ClipboardFormatType> type(kImageSvg);
+  return *type;
+}
+
 // static
 const ClipboardFormatType& ClipboardFormatType::GetRtfType() {
   static base::NoDestructor<ClipboardFormatType> type(NSRTFPboardType);
diff --git a/ui/base/clipboard/clipboard_format_type_win.cc b/ui/base/clipboard/clipboard_format_type_win.cc
index ef2e7f4c..71ca63cc 100644
--- a/ui/base/clipboard/clipboard_format_type_win.cc
+++ b/ui/base/clipboard/clipboard_format_type_win.cc
@@ -5,6 +5,7 @@
 #include "ui/base/clipboard/clipboard_format_type.h"
 
 #include <shlobj.h>
+#include <urlmon.h>
 
 #include "base/containers/flat_map.h"
 #include "base/memory/ptr_util.h"
@@ -201,6 +202,13 @@
   return *format;
 }
 
+// static
+const ClipboardFormatType& ClipboardFormatType::GetSvgType() {
+  static base::NoDestructor<ClipboardFormatType> format(
+      ::RegisterClipboardFormat(CFSTR_MIME_SVG_XML));
+  return *format;
+}
+
 // MS RTF Format
 
 // static
diff --git a/ui/base/clipboard/clipboard_mac.h b/ui/base/clipboard/clipboard_mac.h
index 1189e052..86ad0571 100644
--- a/ui/base/clipboard/clipboard_mac.h
+++ b/ui/base/clipboard/clipboard_mac.h
@@ -57,6 +57,9 @@
                 std::string* src_url,
                 uint32_t* fragment_start,
                 uint32_t* fragment_end) const override;
+  void ReadSvg(ClipboardBuffer buffer,
+               const ClipboardDataEndpoint* data_dst,
+               base::string16* result) const override;
   void ReadRTF(ClipboardBuffer buffer,
                const ClipboardDataEndpoint* data_dst,
                std::string* result) const override;
@@ -86,6 +89,7 @@
                  size_t markup_len,
                  const char* url_data,
                  size_t url_len) override;
+  void WriteSvg(const char* markup_data, size_t markup_len) override;
   void WriteRTF(const char* rtf_data, size_t data_len) override;
   void WriteBookmark(const char* title_data,
                      size_t title_len,
diff --git a/ui/base/clipboard/clipboard_mac.mm b/ui/base/clipboard/clipboard_mac.mm
index 3faea41c..8bb6c38 100644
--- a/ui/base/clipboard/clipboard_mac.mm
+++ b/ui/base/clipboard/clipboard_mac.mm
@@ -140,6 +140,8 @@
     types->push_back(base::UTF8ToUTF16(kMimeTypeText));
   if (IsFormatAvailable(ClipboardFormatType::GetHtmlType(), buffer, data_dst))
     types->push_back(base::UTF8ToUTF16(kMimeTypeHTML));
+  if (IsFormatAvailable(ClipboardFormatType::GetSvgType(), buffer, data_dst))
+    types->push_back(base::UTF8ToUTF16(kMimeTypeSvg));
   if (IsFormatAvailable(ClipboardFormatType::GetRtfType(), buffer, data_dst))
     types->push_back(base::UTF8ToUTF16(kMimeTypeRTF));
 
@@ -239,6 +241,18 @@
   *fragment_end = static_cast<uint32_t>(markup->length());
 }
 
+void ClipboardMac::ReadSvg(ClipboardBuffer buffer,
+                           const ClipboardDataEndpoint* data_dst,
+                           base::string16* result) const {
+  DCHECK(CalledOnValidThread());
+  DCHECK_EQ(buffer, ClipboardBuffer::kCopyPaste);
+  RecordRead(ClipboardFormatMetric::kSvg);
+  NSPasteboard* pb = GetPasteboard();
+  NSString* contents = [pb stringForType:kImageSvg];
+
+  *result = base::SysNSStringToUTF16(contents);
+}
+
 // |data_dst| is not used. It's only passed to be consistent with other
 // platforms.
 void ClipboardMac::ReadRTF(ClipboardBuffer buffer,
@@ -368,6 +382,14 @@
   [pb setString:html_fragment forType:NSHTMLPboardType];
 }
 
+void ClipboardMac::WriteSvg(const char* markup_data, size_t markup_len) {
+  std::string svg_str(markup_data, markup_len);
+  NSString* svg = base::SysUTF8ToNSString(svg_str);
+  NSPasteboard* pb = GetPasteboard();
+  [pb addTypes:@[ kImageSvg ] owner:nil];
+  [pb setString:svg forType:kImageSvg];
+}
+
 void ClipboardMac::WriteRTF(const char* rtf_data, size_t data_len) {
   WriteData(ClipboardFormatType::GetRtfType(), rtf_data, data_len);
 }
diff --git a/ui/base/clipboard/clipboard_metrics.h b/ui/base/clipboard/clipboard_metrics.h
index 5077043b..e719449 100644
--- a/ui/base/clipboard/clipboard_metrics.h
+++ b/ui/base/clipboard/clipboard_metrics.h
@@ -19,7 +19,8 @@
   kData = 5,
   kCustomData = 6,
   kWebSmartPaste = 7,  // Only used on write.
-  kMaxValue = kWebSmartPaste,
+  kSvg = 8,
+  kMaxValue = kSvg,
 };
 
 void RecordRead(ClipboardFormatMetric metric);
diff --git a/ui/base/clipboard/clipboard_non_backed.cc b/ui/base/clipboard/clipboard_non_backed.cc
index c8fc072..0ada3325 100644
--- a/ui/base/clipboard/clipboard_non_backed.cc
+++ b/ui/base/clipboard/clipboard_non_backed.cc
@@ -154,6 +154,19 @@
     *fragment_end = static_cast<uint32_t>(markup->length());
   }
 
+  // Reads SVG from the ClipboardData.
+  void ReadSvg(base::string16* markup) const {
+    markup->clear();
+
+    if (!HasFormat(ClipboardInternalFormat::kSvg))
+      return;
+
+    const ClipboardData* data = GetData();
+    *markup = base::UTF8ToUTF16(data->svg_data());
+
+    DCHECK_LE(markup->length(), std::numeric_limits<uint32_t>::max());
+  }
+
   // Reads RTF from the ClipboardData.
   void ReadRTF(std::string* result) const {
     result->clear();
@@ -284,6 +297,11 @@
     data->set_url(std::string(url_data, url_len));
   }
 
+  static void WriteSvg(const char* markup_data, size_t markup_len) {
+    ClipboardData* data = GetCurrentData();
+    data->set_svg_data(std::string(markup_data, markup_len));
+  }
+
   static void WriteRTF(const char* rtf_data, size_t rtf_len) {
     ClipboardData* data = GetCurrentData();
     data->SetRTFData(std::string(rtf_data, rtf_len));
@@ -407,6 +425,9 @@
   if (format == ClipboardFormatType::GetHtmlType())
     return clipboard_internal_->IsFormatAvailable(
         ClipboardInternalFormat::kHtml);
+  if (format == ClipboardFormatType::GetSvgType())
+    return clipboard_internal_->IsFormatAvailable(
+        ClipboardInternalFormat::kSvg);
   if (format == ClipboardFormatType::GetRtfType())
     return clipboard_internal_->IsFormatAvailable(
         ClipboardInternalFormat::kRtf);
@@ -543,6 +564,18 @@
 #endif
 }
 
+void ClipboardNonBacked::ReadSvg(ClipboardBuffer buffer,
+                                 const ClipboardDataEndpoint* data_dst,
+                                 base::string16* result) const {
+  DCHECK(CalledOnValidThread());
+
+  if (!clipboard_internal_->IsReadAllowed(data_dst))
+    return;
+
+  RecordRead(ClipboardFormatMetric::kSvg);
+  clipboard_internal_->ReadSvg(result);
+}
+
 void ClipboardNonBacked::ReadRTF(ClipboardBuffer buffer,
                                  const ClipboardDataEndpoint* data_dst,
                                  std::string* result) const {
@@ -664,6 +697,10 @@
   ClipboardDataBuilder::WriteHTML(markup_data, markup_len, url_data, url_len);
 }
 
+void ClipboardNonBacked::WriteSvg(const char* markup_data, size_t markup_len) {
+  ClipboardDataBuilder::WriteSvg(markup_data, markup_len);
+}
+
 void ClipboardNonBacked::WriteRTF(const char* rtf_data, size_t data_len) {
   ClipboardDataBuilder::WriteRTF(rtf_data, data_len);
 }
diff --git a/ui/base/clipboard/clipboard_non_backed.h b/ui/base/clipboard/clipboard_non_backed.h
index 6f68f53..876639d 100644
--- a/ui/base/clipboard/clipboard_non_backed.h
+++ b/ui/base/clipboard/clipboard_non_backed.h
@@ -72,6 +72,9 @@
                 std::string* src_url,
                 uint32_t* fragment_start,
                 uint32_t* fragment_end) const override;
+  void ReadSvg(ClipboardBuffer buffer,
+               const ClipboardDataEndpoint* data_dst,
+               base::string16* result) const override;
   void ReadRTF(ClipboardBuffer buffer,
                const ClipboardDataEndpoint* data_dst,
                std::string* result) const override;
@@ -102,6 +105,7 @@
                  size_t markup_len,
                  const char* url_data,
                  size_t url_len) override;
+  void WriteSvg(const char* markup_data, size_t markup_len) override;
   void WriteRTF(const char* rtf_data, size_t data_len) override;
   void WriteBookmark(const char* title_data,
                      size_t title_len,
diff --git a/ui/base/clipboard/clipboard_ozone.cc b/ui/base/clipboard/clipboard_ozone.cc
index 5f46a62..e972e8d 100644
--- a/ui/base/clipboard/clipboard_ozone.cc
+++ b/ui/base/clipboard/clipboard_ozone.cc
@@ -459,6 +459,19 @@
 }
 
 // TODO(crbug.com/1103194): |data_dst| should be supported.
+void ClipboardOzone::ReadSvg(ClipboardBuffer buffer,
+                             const ClipboardDataEndpoint* data_dst,
+                             base::string16* result) const {
+  DCHECK(CalledOnValidThread());
+  RecordRead(ClipboardFormatMetric::kSvg);
+
+  auto clipboard_data =
+      async_clipboard_ozone_->ReadClipboardDataAndWait(buffer, kMimeTypeSvg);
+  *result = base::UTF8ToUTF16(base::StringPiece(
+      reinterpret_cast<char*>(clipboard_data.data()), clipboard_data.size()));
+}
+
+// TODO(crbug.com/1103194): |data_dst| should be supported.
 void ClipboardOzone::ReadRTF(ClipboardBuffer buffer,
                              const ClipboardDataEndpoint* data_dst,
                              std::string* result) const {
@@ -570,6 +583,11 @@
   async_clipboard_ozone_->InsertData(std::move(data), {kMimeTypeHTML});
 }
 
+void ClipboardOzone::WriteSvg(const char* markup_data, size_t markup_len) {
+  std::vector<uint8_t> data(markup_data, markup_data + markup_len);
+  async_clipboard_ozone_->InsertData(std::move(data), {kMimeTypeSvg});
+}
+
 void ClipboardOzone::WriteRTF(const char* rtf_data, size_t data_len) {
   std::vector<uint8_t> data(rtf_data, rtf_data + data_len);
   async_clipboard_ozone_->InsertData(std::move(data), {kMimeTypeRTF});
diff --git a/ui/base/clipboard/clipboard_ozone.h b/ui/base/clipboard/clipboard_ozone.h
index c254b02..9e7d11174 100644
--- a/ui/base/clipboard/clipboard_ozone.h
+++ b/ui/base/clipboard/clipboard_ozone.h
@@ -50,6 +50,9 @@
                 std::string* src_url,
                 uint32_t* fragment_start,
                 uint32_t* fragment_end) const override;
+  void ReadSvg(ClipboardBuffer buffer,
+               const ClipboardDataEndpoint* data_dst,
+               base::string16* result) const override;
   void ReadRTF(ClipboardBuffer buffer,
                const ClipboardDataEndpoint* data_dst,
                std::string* result) const override;
@@ -80,6 +83,7 @@
                  size_t markup_len,
                  const char* url_data,
                  size_t url_len) override;
+  void WriteSvg(const char* markup_data, size_t markup_len) override;
   void WriteRTF(const char* rtf_data, size_t data_len) override;
   void WriteBookmark(const char* title_data,
                      size_t title_len,
diff --git a/ui/base/clipboard/clipboard_test_template.h b/ui/base/clipboard/clipboard_test_template.h
index 828b283a..e260372 100644
--- a/ui/base/clipboard/clipboard_test_template.h
+++ b/ui/base/clipboard/clipboard_test_template.h
@@ -228,6 +228,25 @@
 #endif  // defined(OS_WIN)
 }
 
+TYPED_TEST(ClipboardTest, SvgTest) {
+  base::string16 markup(ASCIIToUTF16("<svg> <circle r=\"40\" /> </svg>"));
+
+  {
+    ScopedClipboardWriter clipboard_writer(ClipboardBuffer::kCopyPaste);
+    clipboard_writer.WriteSvg(markup);
+  }
+
+  EXPECT_TRUE(this->clipboard().IsFormatAvailable(
+      ClipboardFormatType::GetSvgType(), ClipboardBuffer::kCopyPaste,
+      /* data_dst = */ nullptr));
+
+  base::string16 markup_result;
+  this->clipboard().ReadSvg(ClipboardBuffer::kCopyPaste,
+                            /* data_dst = */ nullptr, &markup_result);
+
+  EXPECT_EQ(markup, markup_result);
+}
+
 #if !defined(OS_ANDROID)
 // TODO(crbug/1064968): This test fails with ClipboardAndroid, but passes with
 // the TestClipboard as RTF isn't implemented in ClipboardAndroid.
@@ -1036,6 +1055,11 @@
   scw.WriteHTML(base::string16(), std::string());
 }
 
+TYPED_TEST(ClipboardTest, EmptySvgTest) {
+  ScopedClipboardWriter clipboard_writer(ClipboardBuffer::kCopyPaste);
+  clipboard_writer.WriteSvg(base::string16());
+}
+
 TYPED_TEST(ClipboardTest, WriteRTFEmptyParams) {
   ScopedClipboardWriter scw(ClipboardBuffer::kCopyPaste);
   scw.WriteRTF(std::string());
diff --git a/ui/base/clipboard/clipboard_win.cc b/ui/base/clipboard/clipboard_win.cc
index b422119..8ec47a1 100644
--- a/ui/base/clipboard/clipboard_win.cc
+++ b/ui/base/clipboard/clipboard_win.cc
@@ -454,6 +454,22 @@
 
 // |data_dst| is not used. It's only passed to be consistent with other
 // platforms.
+void ClipboardWin::ReadSvg(ClipboardBuffer buffer,
+                           const ClipboardDataEndpoint* data_dst,
+                           base::string16* result) const {
+  DCHECK_EQ(buffer, ClipboardBuffer::kCopyPaste);
+  RecordRead(ClipboardFormatMetric::kSvg);
+
+  std::string data;
+  ReadData(ClipboardFormatType::GetSvgType(), data_dst, &data);
+  result->assign(reinterpret_cast<const base::char16*>(data.data()),
+                 data.size() / sizeof(base::char16));
+
+  TrimAfterNull(result);
+}
+
+// |data_dst| is not used. It's only passed to be consistent with other
+// platforms.
 void ClipboardWin::ReadRTF(ClipboardBuffer buffer,
                            const ClipboardDataEndpoint* data_dst,
                            std::string* result) const {
@@ -609,6 +625,14 @@
   WriteToClipboard(ClipboardFormatType::GetHtmlType(), glob);
 }
 
+void ClipboardWin::WriteSvg(const char* markup_data, size_t markup_len) {
+  base::string16 markup;
+  base::UTF8ToUTF16(markup_data, markup_len, &markup);
+  HGLOBAL glob = CreateGlobalData(markup);
+
+  WriteToClipboard(ClipboardFormatType::GetSvgType(), glob);
+}
+
 void ClipboardWin::WriteRTF(const char* rtf_data, size_t data_len) {
   WriteData(ClipboardFormatType::GetRtfType(), rtf_data, data_len);
 }
diff --git a/ui/base/clipboard/clipboard_win.h b/ui/base/clipboard/clipboard_win.h
index 32e85b0..fee0206 100644
--- a/ui/base/clipboard/clipboard_win.h
+++ b/ui/base/clipboard/clipboard_win.h
@@ -60,6 +60,9 @@
                 std::string* src_url,
                 uint32_t* fragment_start,
                 uint32_t* fragment_end) const override;
+  void ReadSvg(ClipboardBuffer buffer,
+               const ClipboardDataEndpoint* data_dst,
+               base::string16* result) const override;
   void ReadRTF(ClipboardBuffer buffer,
                const ClipboardDataEndpoint* data_dst,
                std::string* result) const override;
@@ -89,6 +92,7 @@
                  size_t markup_len,
                  const char* url_data,
                  size_t url_len) override;
+  void WriteSvg(const char* markup_data, size_t markup_len) override;
   void WriteRTF(const char* rtf_data, size_t data_len) override;
   void WriteBookmark(const char* title_data,
                      size_t title_len,
diff --git a/ui/base/clipboard/clipboard_x11.cc b/ui/base/clipboard/clipboard_x11.cc
index 71b5489d..7032f2f8 100644
--- a/ui/base/clipboard/clipboard_x11.cc
+++ b/ui/base/clipboard/clipboard_x11.cc
@@ -644,6 +644,24 @@
 
 // |data_dst| is not used. It's only passed to be consistent with other
 // platforms.
+void ClipboardX11::ReadSvg(ClipboardBuffer buffer,
+                           const ClipboardDataEndpoint* data_dst,
+                           base::string16* result) const {
+  DCHECK(CalledOnValidThread());
+  RecordRead(ClipboardFormatMetric::kSvg);
+
+  SelectionData data(x11_details_->RequestAndWaitForTypes(
+      buffer,
+      x11_details_->GetAtomsForFormat(ClipboardFormatType::GetSvgType())));
+  if (data.IsValid()) {
+    std::string markup;
+    data.AssignTo(&markup);
+    *result = base::UTF8ToUTF16(markup);
+  }
+}
+
+// |data_dst| is not used. It's only passed to be consistent with other
+// platforms.
 void ClipboardX11::ReadRTF(ClipboardBuffer buffer,
                            const ClipboardDataEndpoint* data_dst,
                            std::string* result) const {
@@ -785,6 +803,14 @@
   x11_details_->InsertMapping(kMimeTypeHTML, mem);
 }
 
+void ClipboardX11::WriteSvg(const char* markup_data, size_t markup_len) {
+  std::string str(markup_data, markup_len);
+  scoped_refptr<base::RefCountedMemory> mem(
+      base::RefCountedString::TakeString(&str));
+
+  x11_details_->InsertMapping(kMimeTypeSvg, mem);
+}
+
 void ClipboardX11::WriteRTF(const char* rtf_data, size_t data_len) {
   WriteData(ClipboardFormatType::GetRtfType(), rtf_data, data_len);
 }
diff --git a/ui/base/clipboard/clipboard_x11.h b/ui/base/clipboard/clipboard_x11.h
index 9143b11..e849ec76 100644
--- a/ui/base/clipboard/clipboard_x11.h
+++ b/ui/base/clipboard/clipboard_x11.h
@@ -49,6 +49,9 @@
                 std::string* src_url,
                 uint32_t* fragment_start,
                 uint32_t* fragment_end) const override;
+  void ReadSvg(ClipboardBuffer buffer,
+               const ClipboardDataEndpoint* data_dst,
+               base::string16* result) const override;
   void ReadRTF(ClipboardBuffer buffer,
                const ClipboardDataEndpoint* data_dst,
                std::string* result) const override;
@@ -81,6 +84,7 @@
                  size_t markup_len,
                  const char* url_data,
                  size_t url_len) override;
+  void WriteSvg(const char* markup_data, size_t markup_len) override;
   void WriteRTF(const char* rtf_data, size_t data_len) override;
   void WriteBookmark(const char* title_data,
                      size_t title_len,
diff --git a/ui/base/clipboard/scoped_clipboard_writer.cc b/ui/base/clipboard/scoped_clipboard_writer.cc
index 2a806d3..b43743e 100644
--- a/ui/base/clipboard/scoped_clipboard_writer.cc
+++ b/ui/base/clipboard/scoped_clipboard_writer.cc
@@ -67,6 +67,16 @@
   objects_[Clipboard::PortableFormat::kHtml] = parameters;
 }
 
+void ScopedClipboardWriter::WriteSvg(const base::string16& markup) {
+  RecordWrite(ClipboardFormatMetric::kSvg);
+  std::string utf8_markup = base::UTF16ToUTF8(markup);
+
+  Clipboard::ObjectMapParams parameters;
+  parameters.push_back(
+      Clipboard::ObjectMapParam(utf8_markup.begin(), utf8_markup.end()));
+  objects_[Clipboard::PortableFormat::kSvg] = parameters;
+}
+
 void ScopedClipboardWriter::WriteRTF(const std::string& rtf_data) {
   RecordWrite(ClipboardFormatMetric::kRtf);
   Clipboard::ObjectMapParams parameters;
diff --git a/ui/base/clipboard/scoped_clipboard_writer.h b/ui/base/clipboard/scoped_clipboard_writer.h
index 82bf652..bac40ada 100644
--- a/ui/base/clipboard/scoped_clipboard_writer.h
+++ b/ui/base/clipboard/scoped_clipboard_writer.h
@@ -47,6 +47,9 @@
   // useful if the HTML fragment contains relative links.
   void WriteHTML(const base::string16& markup, const std::string& source_url);
 
+  // Adds SVG to the clipboard.
+  void WriteSvg(const base::string16& text);
+
   // Adds RTF to the clipboard.
   void WriteRTF(const std::string& rtf_data);
 
diff --git a/ui/base/clipboard/test/test_clipboard.cc b/ui/base/clipboard/test/test_clipboard.cc
index d8184f8..2c53f1d7 100644
--- a/ui/base/clipboard/test/test_clipboard.cc
+++ b/ui/base/clipboard/test/test_clipboard.cc
@@ -173,6 +173,20 @@
   *fragment_end = base::checked_cast<uint32_t>(markup->size());
 }
 
+void TestClipboard::ReadSvg(ClipboardBuffer buffer,
+                            const ClipboardDataEndpoint* data_dst,
+                            base::string16* result) const {
+  const DataStore& store = GetStore(buffer);
+  if (dlp_controller_ &&
+      !dlp_controller_->IsDataReadAllowed(store.data_src.get(), data_dst))
+    return;
+
+  result->clear();
+  auto it = store.data.find(ClipboardFormatType::GetSvgType());
+  if (it != store.data.end())
+    *result = base::UTF8ToUTF16(it->second);
+}
+
 void TestClipboard::ReadRTF(ClipboardBuffer buffer,
                             const ClipboardDataEndpoint* data_dst,
                             std::string* result) const {
@@ -292,6 +306,13 @@
   GetDefaultStore().html_src_url = std::string(url_data, url_len);
 }
 
+void TestClipboard::WriteSvg(const char* markup_data, size_t markup_len) {
+  base::string16 markup;
+  base::UTF8ToUTF16(markup_data, markup_len, &markup);
+  GetDefaultStore().data[ClipboardFormatType::GetSvgType()] =
+      base::UTF16ToUTF8(markup);
+}
+
 void TestClipboard::WriteRTF(const char* rtf_data, size_t data_len) {
   GetDefaultStore().data[ClipboardFormatType::GetRtfType()] =
       std::string(rtf_data, data_len);
diff --git a/ui/base/clipboard/test/test_clipboard.h b/ui/base/clipboard/test/test_clipboard.h
index 44f77a57..540044e 100644
--- a/ui/base/clipboard/test/test_clipboard.h
+++ b/ui/base/clipboard/test/test_clipboard.h
@@ -60,6 +60,9 @@
                 std::string* src_url,
                 uint32_t* fragment_start,
                 uint32_t* fragment_end) const override;
+  void ReadSvg(ClipboardBuffer buffer,
+               const ClipboardDataEndpoint* data_dst,
+               base::string16* result) const override;
   void ReadRTF(ClipboardBuffer buffer,
                const ClipboardDataEndpoint* data_dst,
                std::string* result) const override;
@@ -94,6 +97,7 @@
                  size_t markup_len,
                  const char* url_data,
                  size_t url_len) override;
+  void WriteSvg(const char* markup_data, size_t markup_len) override;
   void WriteRTF(const char* rtf_data, size_t data_len) override;
   void WriteBookmark(const char* title_data,
                      size_t title_len,
diff --git a/ui/base/dragdrop/os_exchange_data_provider_win.cc b/ui/base/dragdrop/os_exchange_data_provider_win.cc
index 82f17abd6..d13c09f4 100644
--- a/ui/base/dragdrop/os_exchange_data_provider_win.cc
+++ b/ui/base/dragdrop/os_exchange_data_provider_win.cc
@@ -55,9 +55,9 @@
 // Creates a new STGMEDIUM object to hold the specified text. The caller
 // owns the resulting object. The "Bytes" version does not NULL terminate, the
 // string version does.
-static STGMEDIUM GetStorageForBytes(const void* data, size_t bytes);
+static STGMEDIUM CreateStorageForBytes(const void* data, size_t bytes);
 template <typename T>
-static STGMEDIUM GetStorageForString(const std::basic_string<T>& data);
+static STGMEDIUM CreateStorageForString(const std::basic_string<T>& data);
 // Creates the contents of an Internet Shortcut file for the given URL.
 static void GetInternetShortcutFileContents(const GURL& url, std::string* data);
 // Creates a valid file name given a suggested title and URL.
@@ -65,11 +65,12 @@
                                          const base::string16& title,
                                          base::string16* validated);
 // Creates a new STGMEDIUM object to hold files.
-static STGMEDIUM GetStorageForFileNames(const std::vector<FileInfo>& filenames);
-static STGMEDIUM GetIDListStorageForFileName(const base::FilePath& path);
+static STGMEDIUM CreateStorageForFileNames(
+    const std::vector<FileInfo>& filenames);
+static STGMEDIUM CreateIdListStorageForFileName(const base::FilePath& path);
 // Creates a File Descriptor for the creation of a file to the given URL and
 // returns a handle to it.
-static STGMEDIUM GetStorageForFileDescriptor(const base::FilePath& path);
+static STGMEDIUM CreateStorageForFileDescriptor(const base::FilePath& path);
 
 ///////////////////////////////////////////////////////////////////////////////
 // FormatEtcEnumerator
@@ -294,7 +295,7 @@
 }
 
 void OSExchangeDataProviderWin::MarkOriginatedFromRenderer() {
-  STGMEDIUM storage = GetStorageForString(std::string());
+  STGMEDIUM storage = CreateStorageForString(std::string());
   data_->contents_.push_back(DataObjectImpl::StoredDataInfo::TakeStorageMedium(
       GetRendererTaintFormatType().ToFormatEtc(), storage));
 }
@@ -304,12 +305,12 @@
 }
 
 void OSExchangeDataProviderWin::SetString(const base::string16& data) {
-  STGMEDIUM storage = GetStorageForString(data);
+  STGMEDIUM storage = CreateStorageForString(data);
   data_->contents_.push_back(DataObjectImpl::StoredDataInfo::TakeStorageMedium(
       ClipboardFormatType::GetPlainTextType().ToFormatEtc(), storage));
 
   // Also add the UTF8-encoded version.
-  storage = GetStorageForString(base::UTF16ToUTF8(data));
+  storage = CreateStorageForString(base::UTF16ToUTF8(data));
   data_->contents_.push_back(DataObjectImpl::StoredDataInfo::TakeStorageMedium(
       ClipboardFormatType::GetPlainTextAType().ToFormatEtc(), storage));
 }
@@ -326,7 +327,7 @@
   base::string16 x_moz_url_str = base::UTF8ToUTF16(url.spec());
   x_moz_url_str += '\n';
   x_moz_url_str += title;
-  STGMEDIUM storage = GetStorageForString(x_moz_url_str);
+  STGMEDIUM storage = CreateStorageForString(x_moz_url_str);
   data_->contents_.push_back(DataObjectImpl::StoredDataInfo::TakeStorageMedium(
       ClipboardFormatType::GetMozUrlType().ToFormatEtc(), storage));
 
@@ -338,10 +339,10 @@
   SetFileContents(base::FilePath(valid_file_name), shortcut_url_file_contents);
 
   // Add a UniformResourceLocator link for apps like IE and Word.
-  storage = GetStorageForString(base::UTF8ToUTF16(url.spec()));
+  storage = CreateStorageForString(base::UTF8ToUTF16(url.spec()));
   data_->contents_.push_back(DataObjectImpl::StoredDataInfo::TakeStorageMedium(
       ClipboardFormatType::GetUrlType().ToFormatEtc(), storage));
-  storage = GetStorageForString(url.spec());
+  storage = CreateStorageForString(url.spec());
   data_->contents_.push_back(DataObjectImpl::StoredDataInfo::TakeStorageMedium(
       ClipboardFormatType::GetUrlAType().ToFormatEtc(), storage));
 
@@ -355,7 +356,7 @@
 void OSExchangeDataProviderWin::SetFilename(const base::FilePath& path) {
   SetFilenames({FileInfo(path, base::FilePath())});
 
-  STGMEDIUM storage = GetIDListStorageForFileName(path);
+  STGMEDIUM storage = CreateIdListStorageForFileName(path);
   if (storage.tymed == TYMED_NULL)
     return;
   data_->contents_.push_back(DataObjectImpl::StoredDataInfo::TakeStorageMedium(
@@ -364,7 +365,7 @@
 
 void OSExchangeDataProviderWin::SetFilenames(
     const std::vector<FileInfo>& filenames) {
-  STGMEDIUM storage = GetStorageForFileNames(filenames);
+  STGMEDIUM storage = CreateStorageForFileNames(filenames);
   if (storage.tymed == TYMED_NULL)
     return;
 
@@ -473,7 +474,7 @@
     }
   } else if (tymed == TYMED_HGLOBAL) {
     storage_for_contents =
-        GetStorageForBytes(data_buffer.data(), data_buffer.size_bytes());
+        CreateStorageForBytes(data_buffer.data(), data_buffer.size_bytes());
   }
   ClipboardFormatType type =
       ClipboardFormatType::GetFileContentAtIndexType(index);
@@ -485,7 +486,7 @@
 void OSExchangeDataProviderWin::SetPickledData(
     const ClipboardFormatType& format,
     const base::Pickle& data) {
-  STGMEDIUM storage = GetStorageForBytes(data.data(), data.size());
+  STGMEDIUM storage = CreateStorageForBytes(data.data(), data.size());
   data_->contents_.push_back(DataObjectImpl::StoredDataInfo::TakeStorageMedium(
       format.ToFormatEtc(), storage));
 }
@@ -494,13 +495,13 @@
     const base::FilePath& filename,
     const std::string& file_contents) {
   // Add CFSTR_FILEDESCRIPTORW.
-  STGMEDIUM storage = GetStorageForFileDescriptor(filename);
+  STGMEDIUM storage = CreateStorageForFileDescriptor(filename);
   data_->contents_.push_back(DataObjectImpl::StoredDataInfo::TakeStorageMedium(
       ClipboardFormatType::GetFileDescriptorType().ToFormatEtc(), storage));
 
   // Add CFSTR_FILECONTENTS.
   STGMEDIUM storage_contents =
-      GetStorageForBytes(file_contents.data(), file_contents.length());
+      CreateStorageForBytes(file_contents.data(), file_contents.length());
   data_->contents_.push_back(DataObjectImpl::StoredDataInfo::TakeStorageMedium(
       ClipboardFormatType::GetFileContentZeroType().ToFormatEtc(),
       storage_contents));
@@ -513,12 +514,12 @@
   std::string url = base_url.is_valid() ? base_url.spec() : std::string();
 
   std::string cf_html = ClipboardUtil::HtmlToCFHtml(utf8_html, url);
-  STGMEDIUM storage = GetStorageForBytes(cf_html.c_str(), cf_html.size());
+  STGMEDIUM storage = CreateStorageForBytes(cf_html.c_str(), cf_html.size());
   data_->contents_.push_back(DataObjectImpl::StoredDataInfo::TakeStorageMedium(
       ClipboardFormatType::GetHtmlType().ToFormatEtc(), storage));
 
   STGMEDIUM storage_plain =
-      GetStorageForBytes(utf8_html.c_str(), utf8_html.size());
+      CreateStorageForBytes(utf8_html.c_str(), utf8_html.size());
   data_->contents_.push_back(DataObjectImpl::StoredDataInfo::TakeStorageMedium(
       ClipboardFormatType::GetTextHtmlType().ToFormatEtc(), storage_plain));
 }
@@ -679,7 +680,7 @@
   // think we always synthesize one in WebContentsDragWin.
   STGMEDIUM storage = kNullStorageMedium;
   if (!download->filename.empty())
-    GetStorageForFileNames({FileInfo(download->filename, base::FilePath())});
+    CreateStorageForFileNames({FileInfo(download->filename, base::FilePath())});
 
   // Add CF_HDROP.
   auto info = DataObjectImpl::StoredDataInfo::TakeStorageMedium(
@@ -891,7 +892,7 @@
         downloader->Stop();
       // Replace stored data.
       STGMEDIUM storage =
-          GetStorageForFileNames({FileInfo(file_path, base::FilePath())});
+          CreateStorageForFileNames({FileInfo(file_path, base::FilePath())});
       content = StoredDataInfo::TakeStorageMedium(
           ClipboardFormatType::GetCFHDropType().ToFormatEtc(), storage);
       content->downloader = std::move(downloader);
@@ -1075,7 +1076,7 @@
 ///////////////////////////////////////////////////////////////////////////////
 // DataObjectImpl, private:
 
-static STGMEDIUM GetStorageForBytes(const void* data, size_t bytes) {
+static STGMEDIUM CreateStorageForBytes(const void* data, size_t bytes) {
   HANDLE handle = GlobalAlloc(GPTR, static_cast<int>(bytes));
   if (handle) {
     base::win::ScopedHGlobal<uint8_t*> scoped(handle);
@@ -1088,8 +1089,8 @@
 }
 
 template <typename T>
-static STGMEDIUM GetStorageForString(const std::basic_string<T>& data) {
-  return GetStorageForBytes(
+static STGMEDIUM CreateStorageForString(const std::basic_string<T>& data) {
+  return CreateStorageForBytes(
       data.c_str(),
       (data.size() + 1) * sizeof(typename std::basic_string<T>::value_type));
 }
@@ -1126,7 +1127,7 @@
   *validated += kExtension;
 }
 
-static STGMEDIUM GetStorageForFileNames(
+static STGMEDIUM CreateStorageForFileNames(
     const std::vector<FileInfo>& filenames) {
   // CF_HDROP clipboard format consists of DROPFILES structure, a series of file
   // names including the terminating null character and the additional null
@@ -1207,7 +1208,7 @@
   return SUCCEEDED(hr) ? pidl : NULL;
 }
 
-static STGMEDIUM GetIDListStorageForFileName(const base::FilePath& path) {
+static STGMEDIUM CreateIdListStorageForFileName(const base::FilePath& path) {
   LPITEMIDLIST pidl = GetPidlFromPath(path);
   if (!pidl)
     return kNullStorageMedium;
@@ -1248,7 +1249,7 @@
   return storage;
 }
 
-static STGMEDIUM GetStorageForFileDescriptor(const base::FilePath& path) {
+static STGMEDIUM CreateStorageForFileDescriptor(const base::FilePath& path) {
   base::string16 file_name = path.value();
   DCHECK(!file_name.empty());
   HANDLE hdata = GlobalAlloc(GPTR, sizeof(FILEGROUPDESCRIPTORW));
diff --git a/ui/chromeos/colors/cros_colors.json5 b/ui/chromeos/colors/cros_colors.json5
index 44171f27..af668de2 100644
--- a/ui/chromeos/colors/cros_colors.json5
+++ b/ui/chromeos/colors/cros_colors.json5
@@ -76,10 +76,20 @@
     toolbar_bg_color: "#ffffff",
     toolbar_search_bg_color: "$google_grey_100",
 
+    // TODO(crbug/1122767): Rename menu_button_* to app sidebar menu items.
     menu_button_bg_color_active: "$google_blue_50",
     menu_button_bg_color_hover: "$google_grey_100",
     menu_button_outline_color_focused: "$google_blue_600",
 
+    menu_item_bg_color_focus: {
+      light: "rgba(0, 0, 0, 0.06)",
+      dark: "rgba(255, 255, 255, 0.08)",
+    },
+    menu_item_ripple_color: {
+      light: "rgba(0, 0, 0, 0.06)",
+      dark: "rgba(255, 255, 255, 0.08)",
+    },
+
     toggle_color: "$icon_color_prominent",
     toggle_bg_color_inactive: "$google_grey_400",
     toggle_button_color_inactive: "#ffffff",
diff --git a/ui/file_manager/audio_player/manifest.json b/ui/file_manager/audio_player/manifest.json
index 0fd230e..0121af2 100644
--- a/ui/file_manager/audio_player/manifest.json
+++ b/ui/file_manager/audio_player/manifest.json
@@ -25,7 +25,6 @@
       "fileSystem": ["requestFileSystem", "write"]
     },
     "fullscreen",
-    "mediaGalleries",
     "mediaPlayerPrivate",
     "power",
     "storage",
diff --git a/ui/file_manager/file_manager/common/js/util.js b/ui/file_manager/file_manager/common/js/util.js
index 5d4a99c..61430f9 100644
--- a/ui/file_manager/file_manager/common/js/util.js
+++ b/ui/file_manager/file_manager/common/js/util.js
@@ -1728,3 +1728,9 @@
   return loadTimeData.valueExists('SHARESHEET_ENABLED') &&
       loadTimeData.getBoolean('SHARESHEET_ENABLED');
 };
+
+/** @return {boolean} */
+util.isHoldingSpaceEnabled = () => {
+  return loadTimeData.valueExists('HOLDING_SPACE_ENABLED') &&
+      loadTimeData.getBoolean('HOLDING_SPACE_ENABLED');
+};
diff --git a/ui/file_manager/file_manager/foreground/js/file_manager_commands.js b/ui/file_manager/file_manager/foreground/js/file_manager_commands.js
index 03991cb..c819499 100644
--- a/ui/file_manager/file_manager/foreground/js/file_manager_commands.js
+++ b/ui/file_manager/file_manager/foreground/js/file_manager_commands.js
@@ -1609,6 +1609,88 @@
   }
 };
 
+CommandHandler.COMMANDS_['toggle-holding-space'] = new class extends Command {
+  constructor() {
+    super();
+    /**
+     * Whether the command adds or removed items from holding space. The value
+     * is set in <code>canExecute()</code>. It will be true unless all selected
+     * items are already in the holding space.
+     * @private {boolean|undefined}
+     */
+    this.addsItems_;
+  }
+
+  execute(event, fileManager) {
+    if (this.addsItems_ === undefined) {
+      return;
+    }
+
+    const entries = fileManager.selectionHandler.selection.entries;
+    chrome.fileManagerPrivate.toggleAddedToHoldingSpace(
+        entries, this.addsItems_);
+  }
+
+  /** @override */
+  canExecute(event, fileManager) {
+    const command = event.command;
+
+    if (!util.isHoldingSpaceEnabled()) {
+      event.canExecute = false;
+      command.setHidden(true);
+      return;
+    }
+
+    const allowedVolumeTypes = [
+      VolumeManagerCommon.VolumeType.MY_FILES,
+      VolumeManagerCommon.VolumeType.DOWNLOADS,
+      VolumeManagerCommon.VolumeType.DRIVE,
+      VolumeManagerCommon.VolumeType.CROSTINI,
+      VolumeManagerCommon.VolumeType.ANDROID_FILES,
+    ];
+
+    const currentVolumeInfo = fileManager.directoryModel.getCurrentVolumeInfo();
+    if (!currentVolumeInfo ||
+        !allowedVolumeTypes.includes(currentVolumeInfo.volumeType)) {
+      event.canExecute = false;
+      command.setHidden(true);
+      return;
+    }
+
+    const entries = fileManager.selectionHandler.selection.entries;
+
+    if (!entries || entries.length === 0) {
+      event.canExecute = false;
+      command.setHidden(true);
+      return;
+    }
+
+    event.canExecute = true;
+    command.setHidden(false);
+
+    // Update the command to add or remove holding space items depending on the
+    // current holding space state - the command will remove items only if all
+    // currently selected items are already in the holding space.
+    chrome.fileManagerPrivate.getHoldingSpaceState((state) => {
+      if (!state) {
+        command.setHidden(true);
+        return;
+      }
+
+      const itemsSet = {};
+      state.itemUrls.forEach((item) => itemsSet[item] = true);
+
+      const selectedUrls = util.entriesToURLs(entries);
+      this.addsItems_ = selectedUrls.some(url => !itemsSet[url]);
+
+      // TODO(https://crbug.com/1122076): Update the labels when the wording
+      // gets finalized.
+      command.label = this.addsItems_ ? '[Needs l10n] Pin to shelf' :
+                                        '[Needs l10n] Unpin from shelf';
+    });
+  }
+};
+
 /**
  * Opens containing folder of the focused file.
  */
diff --git a/ui/file_manager/file_manager/foreground/js/metadata/content_metadata_provider.js b/ui/file_manager/file_manager/foreground/js/metadata/content_metadata_provider.js
index 991781af..b56c17f 100644
--- a/ui/file_manager/file_manager/foreground/js/metadata/content_metadata_provider.js
+++ b/ui/file_manager/file_manager/foreground/js/metadata/content_metadata_provider.js
@@ -67,12 +67,11 @@
 
     const promises = [];
     for (let i = 0; i < requests.length; i++) {
-      // TODO(crbug.com/1114622): is the .bind actually needed here?
-      promises.push(new Promise(((request, fulfill) => {
-                                  this.getImpl_(
-                                      request.entry, request.names, fulfill);
-                                }).bind(null, requests[i])));
+      promises.push(new Promise(function(request, fulfill) {
+        this.getImpl_(request.entry, request.names, fulfill);
+      }.bind(this, requests[i])));
     }
+
     return Promise.all(promises);
   }
 
@@ -94,10 +93,8 @@
 
     const type = FileType.getType(entry);
 
-    // TODO(ryoh): mediaGalleries API does not handle image metadata correctly.
-    // We parse it in our pure js parser.
-    // chrome/browser/media_galleries/fileapi/supported_image_type_validator.cc
     if (type && type.type === 'image') {
+      // Parse the image using the Worker image metadata parsers.
       const url = entry.toURL();
       if (this.callbacks_[url]) {
         this.callbacks_[url].push(callback);
@@ -159,51 +156,81 @@
       }
     }
 
-    this.getFromMediaGalleries_(entry, names).then(callback);
+    const fileEntry = /** @type {!FileEntry} */ (entry);
+    this.getContentMetadata_(fileEntry, names).then(callback);
   }
 
   /**
-   * Gets a metadata from mediaGalleries API
+   * Gets the content metadata for a file entry consisting of the content mime
+   * type. For audio and video file content mime types, additional metadata is
+   * extacted if requested, such as metadata tags and images.
    *
-   * @param {!Entry} entry File entry.
-   * @param {!Array<string>} names Requested metadata type.
-   * @return {!Promise<!MetadataItem>}  Promise that resolves with the metadata
-   *     of
-   *    the entry.
+   * @param {!FileEntry} entry File entry.
+   * @param {!Array<string>} names Requested metadata types.
+   * @return {!Promise<!MetadataItem>} Promise that resolves with the content
+   *     metadata of the file entry.
    * @private
    */
-  getFromMediaGalleries_(entry, names) {
-    const self = this;
-    return new Promise((resolve, reject) => {
-      entry.file(
-          blob => {
-            let metadataType = 'mimeTypeOnly';
-            if (names.indexOf('mediaArtist') !== -1 ||
-                names.indexOf('mediaTitle') !== -1 ||
-                names.indexOf('mediaTrack') !== -1 ||
-                names.indexOf('mediaYearRecorded') !== -1) {
-              metadataType = 'mimeTypeAndTags';
-            }
-            if (names.indexOf('contentThumbnailUrl') !== -1) {
-              metadataType = 'all';
-            }
-            chrome.mediaGalleries.getMetadata(
-                blob, {metadataType: metadataType}, metadata => {
-                  if (chrome.runtime.lastError) {
-                    resolve(self.createError_(
-                        entry.toURL(), 'resolving metadata',
-                        chrome.runtime.lastError.toString()));
-                  } else {
-                    self.convertMediaMetadataToMetadataItem_(entry, metadata)
-                        .then(resolve, reject);
-                  }
-                });
-          },
-          err => {
-            resolve(self.createError_(
-                entry.toURL(), 'loading file entry',
-                'failed to open file entry'));
-          });
+  getContentMetadata_(entry, names) {
+    /**
+     * First step is to determine the sniffed content mime type of |entry|.
+     * @const {!Promise<!MetadataItem>}
+     */
+    const getContentMimeType = new Promise((resolve, reject) => {
+      chrome.fileManagerPrivate.getContentMimeType(entry, resolve);
+    }).then(mimeType => {
+      if (chrome.runtime.lastError) {
+        const error = chrome.runtime.lastError.toString();
+        return this.createError_(entry.toURL(), 'sniff mime type', error);
+      }
+      const item = new MetadataItem();
+      item.contentMimeType = mimeType;
+      item.mediaMimeType = mimeType;
+      return item;
+    });
+
+    /**
+     * Once the content mime type sniff step is done, search |names| for any
+     * remaining media metadata to extract from the file. Note mediaMimeType
+     * is excluded since it is used for the sniff step.
+     * @param {!Array<string>} names Requested metadata types.
+     * @param {(string|undefined)} type File entry content mime type.
+     * @return {?boolean} Media metadata type: false for metadata tags, true
+     *    for metadata tags and images. A null return means there is no more
+     *    media metadata that needs to be extracted.
+     */
+    function getMediaMetadataType(names, type) {
+      if (!type || !names.length) {
+        return null;
+      } else if (!type.startsWith('audio/') && !type.startsWith('video/')) {
+        return null;  // Only audio and video are supported.
+      } else if (names.includes('contentThumbnailUrl')) {
+        return true;  // Metadata tags and images.
+      } else if (names.find((name) =>
+          name.startsWith('media') && name !== 'mediaMimeType')) {
+        return false;  // Metadata tags only.
+      }
+      return null;
+    }
+
+    return getContentMimeType.then(item => {
+      const extract = getMediaMetadataType(names, item.contentMimeType);
+      if (extract === null) {
+        return item;  // done: no more media metadata to extract.
+      }
+      return new Promise((resolve, reject) => {
+        const contentMimeType = assert(item.contentMimeType);
+        chrome.fileManagerPrivate.getContentMetadata(
+            entry, contentMimeType, !!extract, resolve);
+      }).then(metadata => {
+        if (chrome.runtime.lastError) {
+          const error = chrome.runtime.lastError.toString();
+          return this.createError_(entry.toURL(), 'content metadata', error);
+        }
+        return this.convertMediaMetadataToMetadataItem_(entry, metadata);
+      }).catch((_, error = 'Conversion failed') => {
+        return this.createError_(entry.toURL(), 'convert metadata', error);
+      });
     });
   }
 
@@ -282,9 +309,9 @@
   }
 
   /**
-   * Dispatches a message from MediaGalleries API to the appropriate on* method.
-   * @param {!Entry} entry File entry.
-   * @param {!Object} metadata The metadata from MediaGalleries API.
+   * Converts fileManagerPrivate.MediaMetadata |metadata| to a MetadataItem.
+   * @param {!FileEntry} entry File entry.
+   * @param {!chrome.fileManagerPrivate.MediaMetadata} metadata The metadata.
    * @return {!Promise<!MetadataItem>} Promise that resolves with the
    *    converted metadata item.
    * @private
@@ -293,14 +320,15 @@
     return new Promise((resolve, reject) => {
       if (!metadata) {
         resolve(this.createError_(
-            entry.toURL(), 'Reading a thumbnail image',
-            'Failed to parse metadata'));
+            entry.toURL(), 'metadata result', 'Failed to parse metadata'));
         return;
       }
+
       const item = new MetadataItem();
       const mimeType = metadata['mimeType'];
       item.contentMimeType = mimeType;
       item.mediaMimeType = mimeType;
+
       const trans = {scaleX: 1, scaleY: 1, rotate90: 0};
       if (metadata.rotation) {
         switch (metadata.rotation) {
@@ -325,6 +353,7 @@
       if (metadata.rotation) {
         item.contentImageTransform = item.contentThumbnailTransform = trans;
       }
+
       item.imageHeight = metadata['height'];
       item.imageWidth = metadata['width'];
       item.mediaAlbum = metadata['album'];
@@ -332,6 +361,7 @@
       item.mediaDuration = metadata['duration'];
       item.mediaGenre = metadata['genre'];
       item.mediaTitle = metadata['title'];
+
       if (metadata['track']) {
         item.mediaTrack = '' + metadata['track'];
       }
@@ -349,21 +379,12 @@
           }
         });
       }
+
       if (metadata.attachedImages && metadata.attachedImages.length > 0) {
-        const reader = new FileReader();
-        reader.onload = e => {
-          item.contentThumbnailUrl = e.target.result;
-          resolve(item);
-        };
-        reader.onerror = e => {
-          resolve(this.createError_(
-              entry.toURL(), 'Reading a thumbnail image',
-              reader.error.toString()));
-        };
-        reader.readAsDataURL(metadata.attachedImages[0]);
-      } else {
-        resolve(item);
+        item.contentThumbnailUrl = metadata.attachedImages[0].data;
       }
+
+      resolve(item);
     });
   }
 
diff --git a/ui/file_manager/file_manager/foreground/js/ui/file_table.js b/ui/file_manager/file_manager/foreground/js/ui/file_table.js
index 8c29d08..6ade9aad 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/file_table.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/file_table.js
@@ -281,6 +281,7 @@
     const iconName = sortOrder === 'desc' ? 'up' : 'down';
     icon.setAttribute('iron-icon', `files16:arrow_${iconName}_small`);
     icon.setAttribute('tabindex', '-1');
+    icon.setAttribute('aria-hidden', 'true');
     icon.classList.add('sort-icon', 'no-overlap');
 
     container.classList.toggle('not-sorted', !isSorted);
diff --git a/ui/file_manager/file_manager/foreground/js/ui/table/table_splitter.js b/ui/file_manager/file_manager/foreground/js/ui/table/table_splitter.js
index ea78c33..0062edb 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/table/table_splitter.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/table/table_splitter.js
@@ -48,6 +48,7 @@
       const icon = document.createElement('cr-icon-button');
       icon.setAttribute('iron-icon', 'files32:small-dragger');
       icon.setAttribute('tabindex', '-1');
+      icon.setAttribute('aria-hidden', 'true');
       icon.classList.add('splitter-icon');
       this.appendChild(icon);
     }
diff --git a/ui/file_manager/file_manager/main.html b/ui/file_manager/file_manager/main.html
index 01e6acd8..02192cf 100644
--- a/ui/file_manager/file_manager/main.html
+++ b/ui/file_manager/file_manager/main.html
@@ -133,6 +133,7 @@
       <command id="more-actions" label="$i18n{MORE_ACTIONS_BUTTON_LABEL}">
       <command id="show-submenu" label="$i18n{MORE_ACTIONS_BUTTON_LABEL}">
       <command id="invoke-sharesheet" label="$i18n{MORE_ACTIONS_BUTTON_LABEL}">
+      <command id="toggle-holding-space">
       <command id="go-to-file-location"
                label="$i18n{GO_TO_FILE_LOCATION_BUTTON_LABEL}">
       <command id="zip-selection"
@@ -204,6 +205,8 @@
         <cr-menu-item command="#pin-folder"></cr-menu-item>
         <cr-menu-item command="#delete" class="hide-on-toolbar">$i18n{DELETE_BUTTON_LABEL}</cr-menu-item>
         <cr-menu-item command="#zip-selection"></cr-menu-item>
+        <cr-menu-item command="#toggle-holding-space" visibleif="full-page">
+            </cr-menu-item>
         <cr-menu-item command="#share-with-linux"></cr-menu-item>
         <cr-menu-item command="#manage-linux-sharing"></cr-menu-item>
         <cr-menu-item command="#share-with-plugin-vm"></cr-menu-item>
@@ -516,7 +519,7 @@
         </div>
       </div>
       <div class="splitter" id="navigation-list-splitter">
-        <cr-button class="splitter-button" tabindex="-1">
+        <cr-button aria-hidden="true" class="splitter-button" tabindex="-1">
           <div class="icon"></div>
         </cr-button>
       </div>
diff --git a/ui/file_manager/file_manager/manifest.json b/ui/file_manager/file_manager/manifest.json
index a68a235..eb8809f 100644
--- a/ui/file_manager/file_manager/manifest.json
+++ b/ui/file_manager/file_manager/manifest.json
@@ -36,7 +36,6 @@
     "https://drive.google.com/",
     "https://www.google-analytics.com/",
     "launcherSearchProvider",
-    "mediaGalleries",
     "mediaPlayerPrivate",
     "metricsPrivate",
     "notifications",
diff --git a/ui/file_manager/gallery/manifest.json b/ui/file_manager/gallery/manifest.json
index 975afec..49c40e7 100644
--- a/ui/file_manager/gallery/manifest.json
+++ b/ui/file_manager/gallery/manifest.json
@@ -30,7 +30,6 @@
       "fileSystem": ["requestFileSystem", "write"]
     },
     "fullscreen",
-    "mediaGalleries",
     "metricsPrivate",
     "storage",
     "webview"
diff --git a/ui/file_manager/integration_tests/file_manager/open_image_backlight.js b/ui/file_manager/integration_tests/file_manager/open_image_backlight.js
new file mode 100644
index 0000000..6d8fd93
--- /dev/null
+++ b/ui/file_manager/integration_tests/file_manager/open_image_backlight.js
@@ -0,0 +1,39 @@
+// 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.
+
+'use strict';
+
+(() => {
+  const BACKLIGHT_APP_ID = 'jhdjimmaggjajfjphpljagpgkidjilnj';
+
+  /** Tests opening an image opens Backlight. */
+  testcase.imageOpenBacklight = async () => {
+    await sendTestMessage({
+      name: 'expectFileTask',
+      fileNames: [ENTRIES.image3.targetPath],
+      openType: 'launch'
+    });
+
+    // Open Files.App on Downloads, add image3 to Downloads.
+    const filesAppId =
+        await setupAndWaitUntilReady(RootPath.DOWNLOADS, [ENTRIES.image3], []);
+
+    // Open the image file in Files app.
+    chrome.test.assertTrue(await remoteCall.callRemoteTestUtil(
+        'openFile', filesAppId, [ENTRIES.image3.targetPath]));
+
+    // Wait for Backlight to open.
+    const caller = getCaller();
+    await repeatUntil(async () => {
+      const result = await sendTestMessage({
+        name: 'hasSwaStarted',
+        swaAppId: BACKLIGHT_APP_ID,
+      });
+
+      if (result !== 'true') {
+        return pending(caller, 'Waiting for Backlight to open');
+      }
+    });
+  };
+})();
diff --git a/ui/file_manager/integration_tests/file_manager_test_manifest.json b/ui/file_manager/integration_tests/file_manager_test_manifest.json
index bb64600..f1641675 100644
--- a/ui/file_manager/integration_tests/file_manager_test_manifest.json
+++ b/ui/file_manager/integration_tests/file_manager_test_manifest.json
@@ -36,6 +36,7 @@
       "file_manager/metrics.js",
       "file_manager/my_files.js",
       "file_manager/open_audio_files.js",
+      "file_manager/open_image_backlight.js",
       "file_manager/open_image_files.js",
       "file_manager/open_sniffed_files.js",
       "file_manager/open_video_files.js",
diff --git a/ui/gfx/x/x11_atom_cache.cc b/ui/gfx/x/x11_atom_cache.cc
index 70be123..cf727cd2 100644
--- a/ui/gfx/x/x11_atom_cache.cc
+++ b/ui/gfx/x/x11_atom_cache.cc
@@ -225,6 +225,7 @@
     "chromium/x-web-custom-data",
     "chromium/x-webkit-paste",
     "image/png",
+    "image/svg+xml",
     "marker_event",
     "scaling mode",
     "text/html",
diff --git a/ui/views/controls/webview/web_dialog_view.cc b/ui/views/controls/webview/web_dialog_view.cc
index 9dd5dbb..2dd0b8b5 100644
--- a/ui/views/controls/webview/web_dialog_view.cc
+++ b/ui/views/controls/webview/web_dialog_view.cc
@@ -185,6 +185,12 @@
   return true;
 }
 
+bool WebDialogView::CanMaximize() const {
+  if (delegate_)
+    return delegate_->CanMaximizeDialog();
+  return false;
+}
+
 base::string16 WebDialogView::GetWindowTitle() const {
   if (delegate_)
     return delegate_->GetDialogTitle();
@@ -424,6 +430,27 @@
   return false;
 }
 
+void WebDialogView::RequestMediaAccessPermission(
+    content::WebContents* web_contents,
+    const content::MediaStreamRequest& request,
+    content::MediaResponseCallback callback) {
+  if (delegate_) {
+    delegate_->RequestMediaAccessPermission(web_contents, request,
+                                            std::move(callback));
+  }
+}
+
+bool WebDialogView::CheckMediaAccessPermission(
+    content::RenderFrameHost* render_frame_host,
+    const GURL& security_origin,
+    blink::mojom::MediaStreamType type) {
+  if (delegate_) {
+    return delegate_->CheckMediaAccessPermission(render_frame_host,
+                                                 security_origin, type);
+  }
+  return false;
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // WebDialogView, private:
 
diff --git a/ui/views/controls/webview/web_dialog_view.h b/ui/views/controls/webview/web_dialog_view.h
index 1e519df..99ca532 100644
--- a/ui/views/controls/webview/web_dialog_view.h
+++ b/ui/views/controls/webview/web_dialog_view.h
@@ -95,6 +95,7 @@
   // WidgetDelegate:
   bool OnCloseRequested(Widget::ClosedReason close_reason) override;
   bool CanResize() const override;
+  bool CanMaximize() const override;
   base::string16 GetWindowTitle() const override;
   base::string16 GetAccessibleWindowTitle() const override;
   std::string GetWindowName() const override;
@@ -156,6 +157,13 @@
       const GURL& opener_url,
       const std::string& frame_name,
       const GURL& target_url) override;
+  void RequestMediaAccessPermission(
+      content::WebContents* web_contents,
+      const content::MediaStreamRequest& request,
+      content::MediaResponseCallback callback) override;
+  bool CheckMediaAccessPermission(content::RenderFrameHost* render_frame_host,
+                                  const GURL& security_origin,
+                                  blink::mojom::MediaStreamType type) override;
 
  private:
   friend class WebDialogViewUnitTest;
diff --git a/ui/web_dialogs/web_dialog_delegate.cc b/ui/web_dialogs/web_dialog_delegate.cc
index 4d8fa5d..85199a9f8c 100644
--- a/ui/web_dialogs/web_dialog_delegate.cc
+++ b/ui/web_dialogs/web_dialog_delegate.cc
@@ -24,6 +24,10 @@
   return true;
 }
 
+bool WebDialogDelegate::CanMaximizeDialog() const {
+  return false;
+}
+
 bool WebDialogDelegate::OnDialogCloseRequested() {
   return true;
 }
@@ -74,4 +78,11 @@
   return false;
 }
 
+bool WebDialogDelegate::CheckMediaAccessPermission(
+    content::RenderFrameHost* render_frame_host,
+    const GURL& security_origin,
+    blink::mojom::MediaStreamType type) {
+  return false;
+}
+
 }  // namespace ui
diff --git a/ui/web_dialogs/web_dialog_delegate.h b/ui/web_dialogs/web_dialog_delegate.h
index c5012a7..05833b79 100644
--- a/ui/web_dialogs/web_dialog_delegate.h
+++ b/ui/web_dialogs/web_dialog_delegate.h
@@ -76,6 +76,10 @@
   // returns true.
   virtual bool CanResizeDialog() const;
 
+  // Returns true if the dialog can ever be maximized. Default implementation
+  // returns false.
+  virtual bool CanMaximizeDialog() const;
+
   // A callback to notify the delegate that |source|'s loading state has
   // changed.
   virtual void OnLoadingStateChanged(content::WebContents* source) {}
@@ -165,7 +169,17 @@
   virtual void OnMainFrameResourceLoadComplete(
       const blink::mojom::ResourceLoadInfo& resource_load_info) {}
 
-  virtual ~WebDialogDelegate() {}
+  virtual void RequestMediaAccessPermission(
+      content::WebContents* web_contents,
+      const content::MediaStreamRequest& request,
+      content::MediaResponseCallback callback) {}
+
+  virtual bool CheckMediaAccessPermission(
+      content::RenderFrameHost* render_frame_host,
+      const GURL& security_origin,
+      blink::mojom::MediaStreamType type);
+
+  virtual ~WebDialogDelegate() = default;
 };
 
 }  // namespace ui
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 cf20133..3f73e99 100644
--- a/ui/webui/resources/cr_components/customize_themes/customize_themes.html
+++ b/ui/webui/resources/cr_components/customize_themes/customize_themes.html
@@ -1,4 +1,9 @@
 <style include="cr-hidden-style cr-icons">
+  :host {
+    --cr-customize-themes-grid-gap: 20px;
+    --cr-customize-themes-icon-size: 72px;
+  }
+
   #thirdPartyThemeContainer {
     max-width: 544px;
     width: 100%;
@@ -50,7 +55,7 @@
   }
 
   #themesContainer {
-    --cr-grid-gap: 20px;
+    --cr-grid-gap: var(--cr-customize-themes-grid-gap);
   }
 
   #themesContainer > * {
@@ -78,6 +83,10 @@
     width: 20px;
   }
 
+  cr-theme-icon {
+    --cr-theme-icon-size: var(--cr-customize-themes-icon-size);
+  }
+
   #autogeneratedTheme {
     --cr-theme-icon-frame-color: var(--google-grey-refresh-100);
     --cr-theme-icon-active-tab-color: white;
@@ -119,9 +128,9 @@
     hidden>
 </input>
 <cr-grid id="themesContainer" columns="6">
-  <div id="autogeneratedThemeContainer" tabindex="0"
-      on-click="onAutogeneratedThemeClick_">
-    <cr-theme-icon id="autogeneratedTheme" title="[[i18n('colorPickerLabel')]]"
+  <div id="autogeneratedThemeContainer" title="[[i18n('colorPickerLabel')]]"
+      tabindex="0" on-click="onAutogeneratedThemeClick_">
+    <cr-theme-icon id="autogeneratedTheme"
         selected$="[[isThemeIconSelected_('autogenerated', selectedTheme)]]">
     </cr-theme-icon>
     <div id="colorPickerIcon"></div>
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 ffa4c64..4862ee2 100644
--- a/ui/webui/resources/cr_components/customize_themes/customize_themes.js
+++ b/ui/webui/resources/cr_components/customize_themes/customize_themes.js
@@ -41,6 +41,7 @@
       selectedTheme: {
         type: Object,
         observer: 'onThemeChange_',
+        notify: true,
       },
 
       /** @private {!Array<!customizeThemes.mojom.ChromeTheme>} */
diff --git a/ui/webui/resources/cr_components/customize_themes/theme_icon.html b/ui/webui/resources/cr_components/customize_themes/theme_icon.html
index a695f6b..7a569406 100644
--- a/ui/webui/resources/cr_components/customize_themes/theme_icon.html
+++ b/ui/webui/resources/cr_components/customize_themes/theme_icon.html
@@ -1,8 +1,12 @@
 <style>
+  :host {
+    --cr-theme-icon-size: 72px;
+  }
+
   :host,
   svg {
-    height: 72px;
-    width: 72px;
+    height: var(--cr-theme-icon-size);
+    width: var(--cr-theme-icon-size);
   }
 
   #ring {
@@ -52,7 +56,7 @@
     }
   }
 </style>
-<svg xmlns="http://www.w3.org/2000/svg"
+<svg viewBox="0 0 72 72" xmlns="http://www.w3.org/2000/svg"
     xmlns:xlink="http://www.w3.org/1999/xlink">
   <defs>
     <linearGradient id="gradient" gradientUnits="objectBoundingBox"