diff --git a/BUILD.gn b/BUILD.gn
index 46d499a..38885bd 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -1316,7 +1316,7 @@
                                   "*\bposix/*",
                                   "*\bwin/*",
                                 ])
-  assert(target_name != "")  # Mark as used.
+  not_needed([ "target_name" ])
   sources = invoker.actual_sources
   assert(
       sources == invoker.actual_sources,
diff --git a/DEPS b/DEPS
index 2e02be7..7246adf 100644
--- a/DEPS
+++ b/DEPS
@@ -179,7 +179,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'v8_revision': '506142ad90f3a4d686c6367f4a720f53eeae38af',
+  'v8_revision': '866726ef76c0d7d7ee239bfc7be732da0a7f6a59',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling swarming_client
   # and whatever else without interference from each other.
@@ -187,11 +187,11 @@
   # 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': 'd0748eb038be66dfd05c05773d1edbfd3bdacbc8',
+  'angle_revision': '90a8af383cf9173b36ed17ca5a2ec6df9fd0dbc7',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
-  'swiftshader_revision': '710f8c3e8de2a237b0cb0151316695e657a92c6b',
+  'swiftshader_revision': '7e857092052e74bdba8017e211b4f91442be29e2',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
@@ -238,7 +238,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': '7004f998c7ba0e88c27d6f294666dd67b0916ec8',
+  'catapult_revision': '786ed18d9d43482b5e76fd201776565bf54affea',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -246,7 +246,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': 'ba26b2b503a64abab01cc9bb1356e0f8a0e0ec5d',
+  'devtools_frontend_revision': '21996bda4c5234860015cf22fa6a49567b6394bb',
   # 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.
@@ -306,7 +306,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.
-  'quiche_revision': 'ee0079aeee37c6f2df155b62679a572fda9b2c50',
+  'quiche_revision': '1c2d1ab18e85ca256ff767a74e583b910396202c',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ios_webkit
   # and whatever else without interference from each other.
@@ -899,7 +899,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '53effe844c9a900105898c109ad9762d5fab9abb',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '4c7c4b4bc6e8f294d611679364a821508e21b6bd',
 
   'src/third_party/devtools-frontend/src':
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
@@ -1233,7 +1233,7 @@
     Var('chromium_git') + '/external/github.com/cisco/openh264' + '@' + '6f26bce0b1c4e8ce0e13332f7c0083788def5fdf',
 
   'src/third_party/openscreen/src':
-    Var('chromium_git') + '/openscreen' + '@' + 'cbf7f5178da67099ecc2ead3a88101188a3292f0',
+    Var('chromium_git') + '/openscreen' + '@' + '84bd9713ed32230a226cb400c486564db69eadd5',
 
   'src/third_party/openxr/src': {
     'url': Var('chromium_git') + '/external/github.com/KhronosGroup/OpenXR-SDK' + '@' + '9e97b73e7dd2bfc07745489d728f6a36665c648f',
@@ -1250,7 +1250,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + '79bf811d23f51207d6ba22539036008af0c2824a',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + 'de29e8343a24507ae8012895a4ff9d44bf57e10d',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1454,7 +1454,7 @@
     Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '84ee597cdeae08bb26e578fc66a35bcf35f633f4',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '6c08e4b57d48f498f61409b58b6bf9493fec74ce',
+    Var('webrtc_git') + '/src.git' + '@' + 'd8d15937da6d6bc76c5b8411e08cb2b18ca0b07b',
 
   'src/third_party/libgifcodec':
      Var('skia_git') + '/libgifcodec' + '@'+  Var('libgifcodec_revision'),
@@ -1529,7 +1529,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@72abb04e33faa6156bc5a447aee3f76c7dbeec3c',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@06c36b3c16208d873528ba6ac0f3b8a999427096',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/android_webview/browser/aw_web_contents_view_delegate.cc b/android_webview/browser/aw_web_contents_view_delegate.cc
index 74e0eeb..2f735b2d 100644
--- a/android_webview/browser/aw_web_contents_view_delegate.cc
+++ b/android_webview/browser/aw_web_contents_view_delegate.cc
@@ -5,7 +5,6 @@
 #include "android_webview/browser/aw_web_contents_view_delegate.h"
 
 #include "content/public/browser/web_contents.h"
-#include "content/public/common/context_menu_params.h"
 #include "ui/gfx/color_space.h"
 
 namespace android_webview {
diff --git a/android_webview/variables.gni b/android_webview/variables.gni
index 4b54471..aaa4d94 100644
--- a/android_webview/variables.gni
+++ b/android_webview/variables.gni
@@ -7,13 +7,10 @@
 
 declare_args() {
   # Show a launcher icon to open WebView developer UI. This is enabled by
-  # default for local builds as well as dev and canary builds. The icon for
-  # Monochrome is shown dynamically at runtime if Monochrome is the current
-  # selected system WebView implementation or hidden otherwise.
-  # TODO(https://crbug.com/1002589): add beta channel when approved.
-  webview_devui_show_icon =
-      android_channel == "default" || android_channel == "canary" ||
-      android_channel == "dev"
+  # default for all prestable builds. The icon for Monochrome is shown
+  # dynamically at runtime if Monochrome is the current selected system WebView
+  # implementation or hidden otherwise.
+  webview_devui_show_icon = android_channel != "stable"
 }
 
 system_webview_android_manifest =
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index e5b1923f..657ba65 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -1658,8 +1658,8 @@
 
   deps = [
     ":test_support",
-    "//ash/components/shortcut_viewer",
     "//ash/public/cpp",
+    "//ash/shortcut_viewer",
     "//base:i18n",
     "//chrome:packed_resources",
 
@@ -2102,7 +2102,6 @@
     "//ash/assistant/ui",
     "//ash/assistant/ui:constants",
     "//ash/assistant/util",
-    "//ash/components/shortcut_viewer:unit_tests",
     "//ash/keyboard/arc",
     "//ash/keyboard/ui",
     "//ash/keyboard/ui:test_support",
@@ -2111,6 +2110,7 @@
     "//ash/public/cpp:unit_tests",
     "//ash/public/cpp/vector_icons",
     "//ash/resources/vector_icons",
+    "//ash/shortcut_viewer:unit_tests",
     "//ash/strings",
     "//ash/system/machine_learning:user_settings_event_proto",
     "//ash/system/message_center/arc",
@@ -2299,7 +2299,7 @@
   testonly = true
   friend = [
     ":*",
-    "//ash/components/shortcut_viewer:*",
+    "//ash/shortcut_viewer:*",
     "//components/exo:*",
     "//components/exo/wayland:*",
   ]
diff --git a/ash/ash_strings.grd b/ash/ash_strings.grd
index 46e1a018..941239e 100644
--- a/ash/ash_strings.grd
+++ b/ash/ash_strings.grd
@@ -447,10 +447,10 @@
         Device will be rolled back
       </message>
       <message name="IDS_GESTURE_NOTIFICATION_TITLE" desc="The title of the notification to show off new gestures.">
-        Try out new gesture navigation
+        Try new ways to navigate Chrome OS
       </message>
       <message name="IDS_GESTURE_NOTIFICATION_MESSAGE_LEARN_MORE" desc="The body of the notification to show off new gestures. This notification body links to the help page for the gestures.">
-        Use gestures to quickly switch between apps and interact with your chromebook in tablet mode.
+        Use gestures to quickly switch between apps and interact with your Chromebook in tablet mode.
       </message>
       <message name="IDS_UPDATE_NOTIFICATION_MESSAGE_LEARN_MORE" desc="The body of the notification to notify that the user should restart to get system updates. This notification body links to the info page on the update.">
         Learn more about the latest <ph name="SYSTEM_APP_NAME">$1<ex>Chromium OS</ex></ph> update
diff --git a/ash/components/DEPS b/ash/components/DEPS
deleted file mode 100644
index f7b9713..0000000
--- a/ash/components/DEPS
+++ /dev/null
@@ -1,33 +0,0 @@
-# Components must declare all dependencies explicitly. They do not inherit
-# from //ash/DEPS.
-noparent = True
-
-include_rules = [
-  # Components can use some common things.
-  "+base",
-  "+build",
-  "+testing",
-
-  # Components can be mojo services.
-  "+mojo/public",
-
-  # Components support views UI with aura.
-  "+cc/paint",
-  "+ui/aura",
-  "+ui/base",
-  "+ui/display",
-  "+ui/events",
-  "+ui/gfx",
-  "+ui/views",
-
-  # Components may directly access ash.
-  "+ash",
-
-  # Individual components must explicitly declare their dependencies
-  # on other components. Cycles in the dependency graph within
-  # components/ are not allowed.
-  "-ash/components",
-
-  # Components sit below chrome.
-  "-chrome",
-]
diff --git a/ash/components/OWNERS b/ash/components/OWNERS
deleted file mode 100644
index ecf1a5e..0000000
--- a/ash/components/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-# grdp files
-per-file shortcut_viewer_strings.grdp=file://ash/components/shortcut_viewer/OWNERS
diff --git a/ash/components/README.md b/ash/components/README.md
deleted file mode 100644
index 81d4533..0000000
--- a/ash/components/README.md
+++ /dev/null
@@ -1,21 +0,0 @@
-//ash/components
-----------------
-This directory is for medium-sized UI "components" or "modules" or "mini-apps"
-that run on Chrome OS. In the long term, some of these mini-apps may become
-their own mojo applications and run independently, instead of as part of the
-browser or as part of ash.
-
-Generally these mini-apps depend on //base, //chromeos, //ui and low-level
-components like //components/prefs.
-
-Code here should not depend on //ash, except for the public mojo IPC interfaces
-in //ash/public. Likewise, //ash should not depend on these components, except
-for possibly for a header that allows launching the mini-app.
-
-If the mini-app contains webui it might depend on //content, but in general
-//content dependencies should be avoided.
-
-Code in //ash/components/foo should be in "namespace foo" not "namespace ash".
-
-//ash is only used on Chrome OS. If a component is used on other platforms the
-code should move to //components.
diff --git a/ash/components/shortcut_viewer/DEPS b/ash/components/shortcut_viewer/DEPS
deleted file mode 100644
index d708fed1..0000000
--- a/ash/components/shortcut_viewer/DEPS
+++ /dev/null
@@ -1,14 +0,0 @@
-include_rules = [
-  "+ash/components/strings",
-  "+ui/accessibility",
-  "+ui/chromeos/events",
-  "+ui/chromeos/search_box",
-]
-
-specific_include_rules = {
-  "keyboard_shortcut_view_unittest\.cc": [
-    "+ash/shell.h",
-    "+ash/test/ash_test_base.h",
-    "+ui/compositor",
-  ],
-}
diff --git a/ash/keyboard/keyboard_controller_impl.cc b/ash/keyboard/keyboard_controller_impl.cc
index 31a5cca2..3d2a2c5 100644
--- a/ash/keyboard/keyboard_controller_impl.cc
+++ b/ash/keyboard/keyboard_controller_impl.cc
@@ -13,11 +13,16 @@
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/root_window_controller.h"
 #include "ash/session/session_controller_impl.h"
+#include "ash/shelf/shelf.h"
 #include "ash/shell.h"
 #include "ash/shell_delegate.h"
 #include "ash/wm/window_util.h"
 #include "base/command_line.h"
+#include "ui/aura/env.h"
+#include "ui/aura/window_delegate.h"
 #include "ui/base/ui_base_features.h"
+#include "ui/events/base_event_utils.h"
+#include "ui/events/gestures/gesture_recognizer.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/wm/core/coordinate_conversion.h"
 
@@ -252,6 +257,19 @@
       has_touch_display ? *first_touch_display : screen->GetPrimaryDisplay());
 }
 
+void KeyboardControllerImpl::TransferGestureEventToShelf(
+    const ui::GestureEvent& e) {
+  ash::Shelf* shelf =
+      ash::Shelf::ForWindow(keyboard_ui_controller_->GetKeyboardWindow());
+  if (shelf) {
+    shelf->ProcessGestureEvent(e);
+    aura::Env::GetInstance()->gesture_recognizer()->TransferEventsTo(
+        keyboard_ui_controller_->GetGestureConsumer(), shelf->GetWindow(),
+        ui::TransferTouchesBehavior::kCancel);
+    HideKeyboard(HideReason::kUser);
+  }
+}
+
 void KeyboardControllerImpl::OnKeyboardConfigChanged(
     const keyboard::KeyboardConfig& config) {
   for (auto& observer : observers_)
diff --git a/ash/keyboard/keyboard_controller_impl.h b/ash/keyboard/keyboard_controller_impl.h
index 8367760..cca487c8 100644
--- a/ash/keyboard/keyboard_controller_impl.h
+++ b/ash/keyboard/keyboard_controller_impl.h
@@ -84,6 +84,7 @@
   aura::Window* GetContainerForDefaultDisplay() override;
   aura::Window* GetContainerForDisplay(
       const display::Display& display) override;
+  void TransferGestureEventToShelf(const ui::GestureEvent& e) override;
 
   // SessionObserver:
   void OnSessionStateChanged(session_manager::SessionState state) override;
diff --git a/ash/keyboard/keyboard_controller_impl_unittest.cc b/ash/keyboard/keyboard_controller_impl_unittest.cc
index 806a5db..6ad1cbf 100644
--- a/ash/keyboard/keyboard_controller_impl_unittest.cc
+++ b/ash/keyboard/keyboard_controller_impl_unittest.cc
@@ -13,18 +13,23 @@
 #include "ash/keyboard/ui/test/keyboard_test_util.h"
 #include "ash/public/cpp/keyboard/keyboard_controller.h"
 #include "ash/public/cpp/test/test_keyboard_controller_observer.h"
+#include "ash/shelf/shelf_layout_manager.h"
 #include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
 #include "ash/test/ash_test_helper.h"
+#include "ash/wm/tablet_mode/tablet_mode_controller_test_api.h"
 #include "base/bind.h"
 #include "base/run_loop.h"
 #include "base/stl_util.h"
 #include "base/test/bind_test_util.h"
+#include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
+#include "chromeos/constants/chromeos_features.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/aura/window.h"
 #include "ui/display/manager/display_manager.h"
 #include "ui/display/test/display_manager_test_api.h"
+#include "ui/wm/core/window_util.h"
 
 using keyboard::KeyboardConfig;
 using keyboard::KeyboardEnableFlag;
@@ -33,6 +38,10 @@
 
 namespace {
 
+ShelfLayoutManager* GetShelfLayoutManager() {
+  return AshTestBase::GetPrimaryShelf()->shelf_layout_manager();
+}
+
 class TestContainerBehavior : public keyboard::ContainerBehavior {
  public:
   TestContainerBehavior() : keyboard::ContainerBehavior(nullptr) {}
@@ -67,6 +76,10 @@
                           const display::Display& current_display) override {
     return false;
   }
+  bool HandleGestureEvent(const ui::GestureEvent& event,
+                          const gfx::Rect& bounds_in_screen) override {
+    return false;
+  }
 
   keyboard::ContainerType GetType() const override { return type_; }
 
@@ -110,6 +123,9 @@
   ~KeyboardControllerImplTest() override = default;
 
   void SetUp() override {
+    base::test::ScopedFeatureList feature_list;
+    feature_list.InitAndEnableFeature(chromeos::features::kShelfHotseat);
+
     AshTestBase::SetUp();
 
     // Set the initial observer config to the default config.
@@ -552,4 +568,61 @@
       !keyboard_ui_controller()->GetKeyboardWindow()->bounds().IsEmpty());
 }
 
+TEST_F(KeyboardControllerImplTest, SwipeUpToShowHotSeat) {
+  TabletModeControllerTestApi().EnterTabletMode();
+
+  std::unique_ptr<aura::Window> window =
+      AshTestBase::CreateTestWindow(gfx::Rect(0, 0, 400, 400));
+  wm::ActivateWindow(window.get());
+
+  keyboard_controller()->SetEnableFlag(KeyboardEnableFlag::kExtensionEnabled);
+
+  keyboard_ui_controller()->ShowKeyboard(/* lock */ false);
+  ASSERT_TRUE(keyboard::WaitUntilShown());
+
+  gfx::Rect display_bounds =
+      display::Screen::GetScreen()->GetPrimaryDisplay().bounds();
+  const gfx::Point start(display_bounds.bottom_center());
+  const gfx::Point end(start + gfx::Vector2d(0, -80));
+  const base::TimeDelta time_delta = base::TimeDelta::FromMilliseconds(100);
+  const int num_scroll_steps = 4;
+  GetEventGenerator()->GestureScrollSequence(start, end, time_delta,
+                                             num_scroll_steps);
+
+  // Keyboard should hide and gesture should forward to the shelf.
+  ASSERT_TRUE(keyboard::WaitUntilHidden());
+  EXPECT_EQ(HotseatState::kExtended, GetShelfLayoutManager()->hotseat_state());
+}
+
+TEST_F(KeyboardControllerImplTest, FlingUpToShowOverviewMode) {
+  TabletModeControllerTestApi().EnterTabletMode();
+
+  std::unique_ptr<aura::Window> window =
+      AshTestBase::CreateTestWindow(gfx::Rect(0, 0, 400, 400));
+  wm::ActivateWindow(window.get());
+
+  keyboard_controller()->SetEnableFlag(KeyboardEnableFlag::kExtensionEnabled);
+
+  keyboard_ui_controller()->ShowKeyboard(/* lock */ false);
+  ASSERT_TRUE(keyboard::WaitUntilShown());
+
+  gfx::Rect display_bounds =
+      display::Screen::GetScreen()->GetPrimaryDisplay().bounds();
+  const gfx::Point start(display_bounds.bottom_center());
+  const gfx::Point end(start + gfx::Vector2d(0, -200));
+  const int fling_speed =
+      DragWindowFromShelfController::kVelocityToHomeScreenThreshold + 1;
+  const int scroll_steps = 20;
+  base::TimeDelta scroll_time =
+      GetEventGenerator()->CalculateScrollDurationForFlingVelocity(
+          start, end, fling_speed, scroll_steps);
+  GetEventGenerator()->GestureScrollSequence(start, end, scroll_time,
+                                             scroll_steps);
+
+  // Keyboard should hide and gesture should forward to the shelf.
+  ASSERT_TRUE(keyboard::WaitUntilHidden());
+  EXPECT_EQ(HotseatState::kShownHomeLauncher,
+            GetShelfLayoutManager()->hotseat_state());
+}
+
 }  // namespace ash
diff --git a/ash/keyboard/test_keyboard_ui.cc b/ash/keyboard/test_keyboard_ui.cc
index 272513f0..b6be41f 100644
--- a/ash/keyboard/test_keyboard_ui.cc
+++ b/ash/keyboard/test_keyboard_ui.cc
@@ -40,6 +40,10 @@
   return keyboard_window_.get();
 }
 
+ui::GestureConsumer* TestKeyboardUI::GetGestureConsumer() const {
+  return GetKeyboardWindow();
+}
+
 ui::InputMethod* TestKeyboardUI::GetInputMethod() {
   aura::Window* active_window = window_util::GetActiveWindow();
   aura::Window* root_window = active_window ? active_window->GetRootWindow()
diff --git a/ash/keyboard/test_keyboard_ui.h b/ash/keyboard/test_keyboard_ui.h
index 953db84..9b528a6 100644
--- a/ash/keyboard/test_keyboard_ui.h
+++ b/ash/keyboard/test_keyboard_ui.h
@@ -27,6 +27,7 @@
   // Overridden from KeyboardUI:
   aura::Window* LoadKeyboardWindow(LoadCallback callback) override;
   aura::Window* GetKeyboardWindow() const override;
+  ui::GestureConsumer* GetGestureConsumer() const override;
 
  private:
   // Overridden from keyboard::KeyboardUI:
diff --git a/ash/keyboard/ui/container_behavior.h b/ash/keyboard/ui/container_behavior.h
index b16c6a2..990d03e 100644
--- a/ash/keyboard/ui/container_behavior.h
+++ b/ash/keyboard/ui/container_behavior.h
@@ -8,6 +8,7 @@
 #include "ash/keyboard/ui/keyboard_export.h"
 #include "ash/public/cpp/keyboard/keyboard_types.h"
 #include "ui/display/display.h"
+#include "ui/events/event.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/vector2d.h"
 
@@ -38,6 +39,8 @@
     virtual void MoveKeyboardWindow(const gfx::Rect& new_bounds) = 0;
     virtual void MoveKeyboardWindowToDisplay(const display::Display& display,
                                              const gfx::Rect& new_bounds) = 0;
+    // Needed for ContainerFullWidthBehavior to forward gestures to the shelf.
+    virtual void TransferGestureEventToShelf(const ui::GestureEvent& event) = 0;
   };
 
   explicit ContainerBehavior(Delegate* delegate);
@@ -85,6 +88,8 @@
 
   virtual bool HandlePointerEvent(const ui::LocatedEvent& event,
                                   const display::Display& current_display) = 0;
+  virtual bool HandleGestureEvent(const ui::GestureEvent& event,
+                                  const gfx::Rect& bounds_in_screen) = 0;
 
   virtual ContainerType GetType() const = 0;
 
diff --git a/ash/keyboard/ui/container_floating_behavior.cc b/ash/keyboard/ui/container_floating_behavior.cc
index 417dae2..04d5c9a 100644
--- a/ash/keyboard/ui/container_floating_behavior.cc
+++ b/ash/keyboard/ui/container_floating_behavior.cc
@@ -304,6 +304,12 @@
   return false;
 }
 
+bool ContainerFloatingBehavior::HandleGestureEvent(
+    const ui::GestureEvent& event,
+    const gfx::Rect& bounds_in_screen) {
+  return false;
+}
+
 void ContainerFloatingBehavior::SetCanonicalBounds(
     aura::Window* container,
     const gfx::Rect& display_bounds) {
diff --git a/ash/keyboard/ui/container_floating_behavior.h b/ash/keyboard/ui/container_floating_behavior.h
index 265b0a53..655fe8ccc 100644
--- a/ash/keyboard/ui/container_floating_behavior.h
+++ b/ash/keyboard/ui/container_floating_behavior.h
@@ -42,6 +42,9 @@
                     const gfx::Size& screen_size) override;
   bool HandlePointerEvent(const ui::LocatedEvent& event,
                           const display::Display& current_display) override;
+  bool HandleGestureEvent(const ui::GestureEvent& event,
+                          const gfx::Rect& bounds_in_screen) override;
+
   void SetCanonicalBounds(aura::Window* container,
                           const gfx::Rect& display_bounds) override;
   ContainerType GetType() const override;
diff --git a/ash/keyboard/ui/container_full_width_behavior.cc b/ash/keyboard/ui/container_full_width_behavior.cc
index b5e404f8c..30948440 100644
--- a/ash/keyboard/ui/container_full_width_behavior.cc
+++ b/ash/keyboard/ui/container_full_width_behavior.cc
@@ -14,6 +14,11 @@
 // The virtual keyboard show/hide animation duration.
 constexpr int kFullWidthKeyboardAnimationDurationMs = 100;
 
+// The height of the area from the bottom of the keyboard where the user can
+// swipe up to access the shelf. Manually calculated to be slightly below
+// the virtual keyboard's space bar.
+constexpr int kSwipeUpGestureAreaHeight = 25;
+
 ContainerFullWidthBehavior::ContainerFullWidthBehavior(Delegate* delegate)
     : ContainerBehavior(delegate) {}
 
@@ -92,6 +97,23 @@
   return false;
 }
 
+bool ContainerFullWidthBehavior::HandleGestureEvent(
+    const ui::GestureEvent& event,
+    const gfx::Rect& bounds_in_screen) {
+  if (event.type() == ui::ET_GESTURE_SCROLL_BEGIN) {
+    // Check that the user is swiping upwards near the bottom of the keyboard.
+    // The coordinates of the |event| is relative to the window.
+    const auto details = event.details();
+    if (std::abs(details.scroll_y_hint()) > std::abs(details.scroll_x_hint()) &&
+        details.scroll_y_hint() < 0 &&
+        event.y() > bounds_in_screen.height() - kSwipeUpGestureAreaHeight) {
+      delegate_->TransferGestureEventToShelf(event);
+      return true;
+    }
+  }
+  return false;
+}
+
 void ContainerFullWidthBehavior::SetCanonicalBounds(
     aura::Window* container,
     const gfx::Rect& display_bounds) {
diff --git a/ash/keyboard/ui/container_full_width_behavior.h b/ash/keyboard/ui/container_full_width_behavior.h
index 5a45549f..ebba5c3 100644
--- a/ash/keyboard/ui/container_full_width_behavior.h
+++ b/ash/keyboard/ui/container_full_width_behavior.h
@@ -35,6 +35,8 @@
                     const gfx::Size& screen_size) override;
   bool HandlePointerEvent(const ui::LocatedEvent& event,
                           const display::Display& current_display) override;
+  bool HandleGestureEvent(const ui::GestureEvent& event,
+                          const gfx::Rect& bounds_in_screen) override;
   void SetCanonicalBounds(aura::Window* container,
                           const gfx::Rect& display_bounds) override;
   ContainerType GetType() const override;
diff --git a/ash/keyboard/ui/keyboard_event_handler.cc b/ash/keyboard/ui/keyboard_event_handler.cc
index 661c1c0a..794aac0 100644
--- a/ash/keyboard/ui/keyboard_event_handler.cc
+++ b/ash/keyboard/ui/keyboard_event_handler.cc
@@ -18,6 +18,9 @@
       event->StopPropagation();
       break;
     default:
+      auto* controller = KeyboardUIController::Get();
+      if (controller->IsEnabled() && controller->HandleGestureEvent(*event))
+        event->SetHandled();
       break;
   }
 }
diff --git a/ash/keyboard/ui/keyboard_event_handler_unittest.cc b/ash/keyboard/ui/keyboard_event_handler_unittest.cc
index 11223e41e..e19fe76 100644
--- a/ash/keyboard/ui/keyboard_event_handler_unittest.cc
+++ b/ash/keyboard/ui/keyboard_event_handler_unittest.cc
@@ -4,12 +4,17 @@
 
 #include "ash/keyboard/ui/keyboard_event_handler.h"
 
+#include "ash/keyboard/ui/keyboard_ui_controller.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/events/event.h"
 
 namespace keyboard {
 
 TEST(KeyboardEventHandlerTest, HandleGestureEvents) {
+  // KeyboardEventHandler needs a KeyboardUIController to be present, otherwise
+  // handling gesture events will crash.
+  KeyboardUIController controller;
+
   KeyboardEventHandler filter;
   ui::GestureEvent pinch_begin(
       15, 15, 0, base::TimeTicks(),
diff --git a/ash/keyboard/ui/keyboard_layout_delegate.h b/ash/keyboard/ui/keyboard_layout_delegate.h
index 97b5ac8..c2c3766 100644
--- a/ash/keyboard/ui/keyboard_layout_delegate.h
+++ b/ash/keyboard/ui/keyboard_layout_delegate.h
@@ -8,6 +8,7 @@
 #include <stdint.h>
 
 #include "ash/keyboard/ui/keyboard_export.h"
+#include "ui/events/event.h"
 
 namespace display {
 class Display;
@@ -32,6 +33,11 @@
   // Get the container window for a particular display. |display| must be valid.
   virtual aura::Window* GetContainerForDisplay(
       const display::Display& display) = 0;
+
+  // Transfer a gesture event to the Ash shelf. Any remaining gestures will be
+  // sent directly to the shelf. Used for accessing the shelf and the home
+  // screen even when the virtual keyboard is blocking the shelf.
+  virtual void TransferGestureEventToShelf(const ui::GestureEvent& e) = 0;
 };
 
 }  // namespace keyboard
diff --git a/ash/keyboard/ui/keyboard_ui.h b/ash/keyboard/ui/keyboard_ui.h
index 0b9902ae..8131982 100644
--- a/ash/keyboard/ui/keyboard_ui.h
+++ b/ash/keyboard/ui/keyboard_ui.h
@@ -9,6 +9,7 @@
 #include "base/callback_forward.h"
 #include "base/macros.h"
 #include "ui/base/ime/text_input_type.h"
+#include "ui/events/gestures/gesture_types.h"
 
 namespace aura {
 class Window;
@@ -41,6 +42,10 @@
   // loading.
   virtual aura::Window* GetKeyboardWindow() const = 0;
 
+  // Get the gesture consumer for the keyboard, which may be different to the
+  // window itself if there are nested windows.
+  virtual ui::GestureConsumer* GetGestureConsumer() const = 0;
+
   // Gets the InputMethod that will provide notifications about changes in the
   // text input context.
   virtual ui::InputMethod* GetInputMethod() = 0;
diff --git a/ash/keyboard/ui/keyboard_ui_controller.cc b/ash/keyboard/ui/keyboard_ui_controller.cc
index 16deb27c..0faf0dd 100644
--- a/ash/keyboard/ui/keyboard_ui_controller.cc
+++ b/ash/keyboard/ui/keyboard_ui_controller.cc
@@ -28,6 +28,7 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/time.h"
 #include "ui/aura/client/aura_constants.h"
+#include "ui/aura/env.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_delegate.h"
 #include "ui/aura/window_observer.h"
@@ -39,6 +40,7 @@
 #include "ui/compositor/scoped_layer_animation_settings.h"
 #include "ui/display/types/display_constants.h"
 #include "ui/events/base_event_utils.h"
+#include "ui/events/gestures/gesture_recognizer.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/vector2d.h"
 #include "ui/ozone/public/input_controller.h"
@@ -302,6 +304,10 @@
   return ui_ ? ui_->GetKeyboardWindow() : nullptr;
 }
 
+ui::GestureConsumer* KeyboardUIController::GetGestureConsumer() const {
+  return ui_ ? ui_->GetGestureConsumer() : nullptr;
+}
+
 aura::Window* KeyboardUIController::GetRootWindow() const {
   return parent_container_ ? parent_container_->GetRootWindow() : nullptr;
 }
@@ -747,6 +753,11 @@
   HideKeyboardTemporarilyForTransition();
 }
 
+void KeyboardUIController::TransferGestureEventToShelf(
+    const ui::GestureEvent& e) {
+  layout_delegate_->TransferGestureEventToShelf(e);
+}
+
 // aura::WindowObserver overrides
 
 void KeyboardUIController::OnWindowAddedToRootWindow(aura::Window* window) {
@@ -1026,6 +1037,10 @@
   return container_behavior_->HandlePointerEvent(event, current_display);
 }
 
+bool KeyboardUIController::HandleGestureEvent(const ui::GestureEvent& event) {
+  return container_behavior_->HandleGestureEvent(event, GetBoundsInScreen());
+}
+
 void KeyboardUIController::SetContainerType(
     ContainerType type,
     const base::Optional<gfx::Rect>& target_bounds_in_root,
diff --git a/ash/keyboard/ui/keyboard_ui_controller.h b/ash/keyboard/ui/keyboard_ui_controller.h
index 1ac79d48..979a384 100644
--- a/ash/keyboard/ui/keyboard_ui_controller.h
+++ b/ash/keyboard/ui/keyboard_ui_controller.h
@@ -32,6 +32,7 @@
 #include "ui/base/ime/input_method_observer.h"
 #include "ui/base/ime/text_input_type.h"
 #include "ui/events/event.h"
+#include "ui/events/gestures/gesture_types.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/vector2d.h"
 
@@ -86,6 +87,10 @@
   // created yet.
   aura::Window* GetKeyboardWindow() const;
 
+  // Returns the gesture consumer for the keyboard, or null if the keyboard
+  // window has not been created yet.
+  ui::GestureConsumer* GetGestureConsumer() const;
+
   // Returns the root window that this keyboard controller is attached to, or
   // null if the keyboard has not been attached to any root window.
   aura::Window* GetRootWindow() const;
@@ -215,6 +220,7 @@
   // Handle mouse and touch events on the keyboard. The effects of this method
   // will not stop propagation to the keyboard extension.
   bool HandlePointerEvent(const ui::LocatedEvent& event);
+  bool HandleGestureEvent(const ui::GestureEvent& event);
 
   // Sets the active container type. If the keyboard is currently shown, this
   // will trigger a hide animation and a subsequent show animation. Otherwise
@@ -287,6 +293,7 @@
   void MoveKeyboardWindow(const gfx::Rect& new_bounds) override;
   void MoveKeyboardWindowToDisplay(const display::Display& display,
                                    const gfx::Rect& new_bounds) override;
+  void TransferGestureEventToShelf(const ui::GestureEvent& e) override;
 
   // aura::WindowObserver overrides
   void OnWindowAddedToRootWindow(aura::Window* window) override;
diff --git a/ash/keyboard/ui/test/test_keyboard_layout_delegate.cc b/ash/keyboard/ui/test/test_keyboard_layout_delegate.cc
index 5ebd73f..7f2a7fb 100644
--- a/ash/keyboard/ui/test/test_keyboard_layout_delegate.cc
+++ b/ash/keyboard/ui/test/test_keyboard_layout_delegate.cc
@@ -21,4 +21,7 @@
   return root_window_;
 }
 
+void TestKeyboardLayoutDelegate::TransferGestureEventToShelf(
+    const ui::GestureEvent& e) {}
+
 }  // namespace keyboard
diff --git a/ash/keyboard/ui/test/test_keyboard_layout_delegate.h b/ash/keyboard/ui/test/test_keyboard_layout_delegate.h
index 3064ac1..2991236 100644
--- a/ash/keyboard/ui/test/test_keyboard_layout_delegate.h
+++ b/ash/keyboard/ui/test/test_keyboard_layout_delegate.h
@@ -25,6 +25,7 @@
   aura::Window* GetContainerForDefaultDisplay() override;
   aura::Window* GetContainerForDisplay(
       const display::Display& display) override;
+  void TransferGestureEventToShelf(const ui::GestureEvent& e) override;
 
  private:
   aura::Window* root_window_;
diff --git a/ash/keyboard/ui/test/test_keyboard_ui_factory.cc b/ash/keyboard/ui/test/test_keyboard_ui_factory.cc
index c58b1e6..1d7db413 100644
--- a/ash/keyboard/ui/test/test_keyboard_ui_factory.cc
+++ b/ash/keyboard/ui/test/test_keyboard_ui_factory.cc
@@ -56,6 +56,11 @@
   return window_.get();
 }
 
+ui::GestureConsumer* TestKeyboardUIFactory::TestKeyboardUI::GetGestureConsumer()
+    const {
+  return GetKeyboardWindow();
+}
+
 ui::InputMethod* TestKeyboardUIFactory::TestKeyboardUI::GetInputMethod() {
   return input_method_;
 }
diff --git a/ash/keyboard/ui/test/test_keyboard_ui_factory.h b/ash/keyboard/ui/test/test_keyboard_ui_factory.h
index cd3a8f3b..5cc1ffc 100644
--- a/ash/keyboard/ui/test/test_keyboard_ui_factory.h
+++ b/ash/keyboard/ui/test/test_keyboard_ui_factory.h
@@ -31,6 +31,7 @@
     // Overridden from KeyboardUI:
     aura::Window* LoadKeyboardWindow(LoadCallback callback) override;
     aura::Window* GetKeyboardWindow() const override;
+    ui::GestureConsumer* GetGestureConsumer() const override;
     ui::InputMethod* GetInputMethod() override;
     void ReloadKeyboardIfNeeded() override {}
 
diff --git a/ash/components/shortcut_viewer/BUILD.gn b/ash/shortcut_viewer/BUILD.gn
similarity index 94%
rename from ash/components/shortcut_viewer/BUILD.gn
rename to ash/shortcut_viewer/BUILD.gn
index 7fab5f9..83ff5e94 100644
--- a/ash/components/shortcut_viewer/BUILD.gn
+++ b/ash/shortcut_viewer/BUILD.gn
@@ -26,10 +26,10 @@
 
   deps = [
     "//ash",
-    "//ash/components/shortcut_viewer/vector_icons",
-    "//ash/components/strings",
     "//ash/public/cpp",
     "//ash/public/cpp/resources:ash_public_unscaled_resources",
+    "//ash/shortcut_viewer/strings",
+    "//ash/shortcut_viewer/vector_icons",
     "//cc/paint",
     "//ui/accessibility",
     "//ui/aura",
diff --git a/ash/components/shortcut_viewer/OWNERS b/ash/shortcut_viewer/OWNERS
similarity index 100%
rename from ash/components/shortcut_viewer/OWNERS
rename to ash/shortcut_viewer/OWNERS
diff --git a/ash/components/shortcut_viewer/README.md b/ash/shortcut_viewer/README.md
similarity index 100%
rename from ash/components/shortcut_viewer/README.md
rename to ash/shortcut_viewer/README.md
diff --git a/ash/components/ash_components_strings.grd b/ash/shortcut_viewer/ash_components_strings.grd
similarity index 100%
rename from ash/components/ash_components_strings.grd
rename to ash/shortcut_viewer/ash_components_strings.grd
diff --git a/ash/components/shortcut_viewer/keyboard_shortcut_item.cc b/ash/shortcut_viewer/keyboard_shortcut_item.cc
similarity index 94%
rename from ash/components/shortcut_viewer/keyboard_shortcut_item.cc
rename to ash/shortcut_viewer/keyboard_shortcut_item.cc
index 97b4eb1..31af089 100644
--- a/ash/components/shortcut_viewer/keyboard_shortcut_item.cc
+++ b/ash/shortcut_viewer/keyboard_shortcut_item.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ash/components/shortcut_viewer/keyboard_shortcut_item.h"
+#include "ash/shortcut_viewer/keyboard_shortcut_item.h"
 
 #include <tuple>
 
diff --git a/ash/components/shortcut_viewer/keyboard_shortcut_item.h b/ash/shortcut_viewer/keyboard_shortcut_item.h
similarity index 94%
rename from ash/components/shortcut_viewer/keyboard_shortcut_item.h
rename to ash/shortcut_viewer/keyboard_shortcut_item.h
index bd5f831..be708cd 100644
--- a/ash/components/shortcut_viewer/keyboard_shortcut_item.h
+++ b/ash/shortcut_viewer/keyboard_shortcut_item.h
@@ -2,12 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef ASH_COMPONENTS_SHORTCUT_VIEWER_KEYBOARD_SHORTCUT_ITEM_H_
-#define ASH_COMPONENTS_SHORTCUT_VIEWER_KEYBOARD_SHORTCUT_ITEM_H_
+#ifndef ASH_SHORTCUT_VIEWER_KEYBOARD_SHORTCUT_ITEM_H_
+#define ASH_SHORTCUT_VIEWER_KEYBOARD_SHORTCUT_ITEM_H_
 
 #include <vector>
 
-#include "ash/components/shortcut_viewer/ksv_export.h"
+#include "ash/shortcut_viewer/ksv_export.h"
 #include "base/macros.h"
 #include "ui/events/keycodes/keyboard_codes.h"
 
@@ -103,4 +103,4 @@
 
 }  // namespace keyboard_shortcut_viewer
 
-#endif  // ASH_COMPONENTS_SHORTCUT_VIEWER_KEYBOARD_SHORTCUT_ITEM_H_
+#endif  // ASH_SHORTCUT_VIEWER_KEYBOARD_SHORTCUT_ITEM_H_
diff --git a/ash/components/shortcut_viewer/keyboard_shortcut_viewer_metadata.cc b/ash/shortcut_viewer/keyboard_shortcut_viewer_metadata.cc
similarity index 99%
rename from ash/components/shortcut_viewer/keyboard_shortcut_viewer_metadata.cc
rename to ash/shortcut_viewer/keyboard_shortcut_viewer_metadata.cc
index 7c21cf3..f8cb2be1 100644
--- a/ash/components/shortcut_viewer/keyboard_shortcut_viewer_metadata.cc
+++ b/ash/shortcut_viewer/keyboard_shortcut_viewer_metadata.cc
@@ -2,11 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ash/components/shortcut_viewer/keyboard_shortcut_viewer_metadata.h"
+#include "ash/shortcut_viewer/keyboard_shortcut_viewer_metadata.h"
 
-#include "ash/components/shortcut_viewer/keyboard_shortcut_item.h"
-#include "ash/components/shortcut_viewer/vector_icons/vector_icons.h"
-#include "ash/components/strings/grit/ash_components_strings.h"
+#include "ash/shortcut_viewer/keyboard_shortcut_item.h"
+#include "ash/shortcut_viewer/strings/grit/ash_components_strings.h"
+#include "ash/shortcut_viewer/vector_icons/vector_icons.h"
 #include "base/logging.h"
 #include "base/macros.h"
 #include "base/no_destructor.h"
diff --git a/ash/components/shortcut_viewer/keyboard_shortcut_viewer_metadata.h b/ash/shortcut_viewer/keyboard_shortcut_viewer_metadata.h
similarity index 85%
rename from ash/components/shortcut_viewer/keyboard_shortcut_viewer_metadata.h
rename to ash/shortcut_viewer/keyboard_shortcut_viewer_metadata.h
index 865e268..8f0a75e 100644
--- a/ash/components/shortcut_viewer/keyboard_shortcut_viewer_metadata.h
+++ b/ash/shortcut_viewer/keyboard_shortcut_viewer_metadata.h
@@ -2,12 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef ASH_COMPONENTS_SHORTCUT_VIEWER_KEYBOARD_SHORTCUT_VIEWER_METADATA_H_
-#define ASH_COMPONENTS_SHORTCUT_VIEWER_KEYBOARD_SHORTCUT_VIEWER_METADATA_H_
+#ifndef ASH_SHORTCUT_VIEWER_KEYBOARD_SHORTCUT_VIEWER_METADATA_H_
+#define ASH_SHORTCUT_VIEWER_KEYBOARD_SHORTCUT_VIEWER_METADATA_H_
 
 #include <vector>
 
-#include "ash/components/shortcut_viewer/ksv_export.h"
+#include "ash/shortcut_viewer/ksv_export.h"
 #include "base/containers/span.h"
 #include "base/optional.h"
 #include "base/strings/string16.h"
@@ -48,4 +48,4 @@
 
 }  // namespace keyboard_shortcut_viewer
 
-#endif  // ASH_COMPONENTS_SHORTCUT_VIEWER_KEYBOARD_SHORTCUT_VIEWER_METADATA_H_
+#endif  // ASH_SHORTCUT_VIEWER_KEYBOARD_SHORTCUT_VIEWER_METADATA_H_
diff --git a/ash/components/shortcut_viewer/ksv_export.h b/ash/shortcut_viewer/ksv_export.h
similarity index 75%
rename from ash/components/shortcut_viewer/ksv_export.h
rename to ash/shortcut_viewer/ksv_export.h
index 3b5b59b..e2c749b 100644
--- a/ash/components/shortcut_viewer/ksv_export.h
+++ b/ash/shortcut_viewer/ksv_export.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef ASH_COMPONENTS_SHORTCUT_VIEWER_KSV_EXPORT_H_
-#define ASH_COMPONENTS_SHORTCUT_VIEWER_KSV_EXPORT_H_
+#ifndef ASH_SHORTCUT_VIEWER_KSV_EXPORT_H_
+#define ASH_SHORTCUT_VIEWER_KSV_EXPORT_H_
 
 // Defines KSV_EXPORT so that functionality implemented by
 // the keyboard shortcut viewer module can be exported to consumers.
@@ -20,4 +20,4 @@
 #define KSV_EXPORT
 #endif
 
-#endif  // ASH_COMPONENTS_SHORTCUT_VIEWER_KSV_EXPORT_H_
+#endif  // ASH_SHORTCUT_VIEWER_KSV_EXPORT_H_
diff --git a/ash/components/resources/default_100_percent/shortcut_viewer/ksv_app_icon.png b/ash/shortcut_viewer/resources/default_100_percent/shortcut_viewer/ksv_app_icon.png
similarity index 100%
rename from ash/components/resources/default_100_percent/shortcut_viewer/ksv_app_icon.png
rename to ash/shortcut_viewer/resources/default_100_percent/shortcut_viewer/ksv_app_icon.png
Binary files differ
diff --git a/ash/components/resources/default_200_percent/shortcut_viewer/ksv_app_icon.png b/ash/shortcut_viewer/resources/default_200_percent/shortcut_viewer/ksv_app_icon.png
similarity index 100%
rename from ash/components/resources/default_200_percent/shortcut_viewer/ksv_app_icon.png
rename to ash/shortcut_viewer/resources/default_200_percent/shortcut_viewer/ksv_app_icon.png
Binary files differ
diff --git a/ash/components/shortcut_viewer_strings.grdp b/ash/shortcut_viewer/shortcut_viewer_strings.grdp
similarity index 100%
rename from ash/components/shortcut_viewer_strings.grdp
rename to ash/shortcut_viewer/shortcut_viewer_strings.grdp
diff --git a/ash/components/strings/BUILD.gn b/ash/shortcut_viewer/strings/BUILD.gn
similarity index 100%
rename from ash/components/strings/BUILD.gn
rename to ash/shortcut_viewer/strings/BUILD.gn
diff --git a/ash/components/strings/ash_components_strings_af.xtb b/ash/shortcut_viewer/strings/ash_components_strings_af.xtb
similarity index 100%
rename from ash/components/strings/ash_components_strings_af.xtb
rename to ash/shortcut_viewer/strings/ash_components_strings_af.xtb
diff --git a/ash/components/strings/ash_components_strings_am.xtb b/ash/shortcut_viewer/strings/ash_components_strings_am.xtb
similarity index 100%
rename from ash/components/strings/ash_components_strings_am.xtb
rename to ash/shortcut_viewer/strings/ash_components_strings_am.xtb
diff --git a/ash/components/strings/ash_components_strings_ar.xtb b/ash/shortcut_viewer/strings/ash_components_strings_ar.xtb
similarity index 100%
rename from ash/components/strings/ash_components_strings_ar.xtb
rename to ash/shortcut_viewer/strings/ash_components_strings_ar.xtb
diff --git a/ash/components/strings/ash_components_strings_as.xtb b/ash/shortcut_viewer/strings/ash_components_strings_as.xtb
similarity index 100%
rename from ash/components/strings/ash_components_strings_as.xtb
rename to ash/shortcut_viewer/strings/ash_components_strings_as.xtb
diff --git a/ash/components/strings/ash_components_strings_az.xtb b/ash/shortcut_viewer/strings/ash_components_strings_az.xtb
similarity index 100%
rename from ash/components/strings/ash_components_strings_az.xtb
rename to ash/shortcut_viewer/strings/ash_components_strings_az.xtb
diff --git a/ash/components/strings/ash_components_strings_be.xtb b/ash/shortcut_viewer/strings/ash_components_strings_be.xtb
similarity index 100%
rename from ash/components/strings/ash_components_strings_be.xtb
rename to ash/shortcut_viewer/strings/ash_components_strings_be.xtb
diff --git a/ash/components/strings/ash_components_strings_bg.xtb b/ash/shortcut_viewer/strings/ash_components_strings_bg.xtb
similarity index 100%
rename from ash/components/strings/ash_components_strings_bg.xtb
rename to ash/shortcut_viewer/strings/ash_components_strings_bg.xtb
diff --git a/ash/components/strings/ash_components_strings_bn.xtb b/ash/shortcut_viewer/strings/ash_components_strings_bn.xtb
similarity index 100%
rename from ash/components/strings/ash_components_strings_bn.xtb
rename to ash/shortcut_viewer/strings/ash_components_strings_bn.xtb
diff --git a/ash/components/strings/ash_components_strings_bs.xtb b/ash/shortcut_viewer/strings/ash_components_strings_bs.xtb
similarity index 100%
rename from ash/components/strings/ash_components_strings_bs.xtb
rename to ash/shortcut_viewer/strings/ash_components_strings_bs.xtb
diff --git a/ash/components/strings/ash_components_strings_ca.xtb b/ash/shortcut_viewer/strings/ash_components_strings_ca.xtb
similarity index 100%
rename from ash/components/strings/ash_components_strings_ca.xtb
rename to ash/shortcut_viewer/strings/ash_components_strings_ca.xtb
diff --git a/ash/components/strings/ash_components_strings_cs.xtb b/ash/shortcut_viewer/strings/ash_components_strings_cs.xtb
similarity index 100%
rename from ash/components/strings/ash_components_strings_cs.xtb
rename to ash/shortcut_viewer/strings/ash_components_strings_cs.xtb
diff --git a/ash/components/strings/ash_components_strings_da.xtb b/ash/shortcut_viewer/strings/ash_components_strings_da.xtb
similarity index 100%
rename from ash/components/strings/ash_components_strings_da.xtb
rename to ash/shortcut_viewer/strings/ash_components_strings_da.xtb
diff --git a/ash/components/strings/ash_components_strings_de.xtb b/ash/shortcut_viewer/strings/ash_components_strings_de.xtb
similarity index 100%
rename from ash/components/strings/ash_components_strings_de.xtb
rename to ash/shortcut_viewer/strings/ash_components_strings_de.xtb
diff --git a/ash/components/strings/ash_components_strings_el.xtb b/ash/shortcut_viewer/strings/ash_components_strings_el.xtb
similarity index 100%
rename from ash/components/strings/ash_components_strings_el.xtb
rename to ash/shortcut_viewer/strings/ash_components_strings_el.xtb
diff --git a/ash/components/strings/ash_components_strings_en-GB.xtb b/ash/shortcut_viewer/strings/ash_components_strings_en-GB.xtb
similarity index 100%
rename from ash/components/strings/ash_components_strings_en-GB.xtb
rename to ash/shortcut_viewer/strings/ash_components_strings_en-GB.xtb
diff --git a/ash/components/strings/ash_components_strings_es-419.xtb b/ash/shortcut_viewer/strings/ash_components_strings_es-419.xtb
similarity index 100%
rename from ash/components/strings/ash_components_strings_es-419.xtb
rename to ash/shortcut_viewer/strings/ash_components_strings_es-419.xtb
diff --git a/ash/components/strings/ash_components_strings_es.xtb b/ash/shortcut_viewer/strings/ash_components_strings_es.xtb
similarity index 100%
rename from ash/components/strings/ash_components_strings_es.xtb
rename to ash/shortcut_viewer/strings/ash_components_strings_es.xtb
diff --git a/ash/components/strings/ash_components_strings_et.xtb b/ash/shortcut_viewer/strings/ash_components_strings_et.xtb
similarity index 100%
rename from ash/components/strings/ash_components_strings_et.xtb
rename to ash/shortcut_viewer/strings/ash_components_strings_et.xtb
diff --git a/ash/components/strings/ash_components_strings_eu.xtb b/ash/shortcut_viewer/strings/ash_components_strings_eu.xtb
similarity index 100%
rename from ash/components/strings/ash_components_strings_eu.xtb
rename to ash/shortcut_viewer/strings/ash_components_strings_eu.xtb
diff --git a/ash/components/strings/ash_components_strings_fa.xtb b/ash/shortcut_viewer/strings/ash_components_strings_fa.xtb
similarity index 100%
rename from ash/components/strings/ash_components_strings_fa.xtb
rename to ash/shortcut_viewer/strings/ash_components_strings_fa.xtb
diff --git a/ash/components/strings/ash_components_strings_fi.xtb b/ash/shortcut_viewer/strings/ash_components_strings_fi.xtb
similarity index 100%
rename from ash/components/strings/ash_components_strings_fi.xtb
rename to ash/shortcut_viewer/strings/ash_components_strings_fi.xtb
diff --git a/ash/components/strings/ash_components_strings_fil.xtb b/ash/shortcut_viewer/strings/ash_components_strings_fil.xtb
similarity index 100%
rename from ash/components/strings/ash_components_strings_fil.xtb
rename to ash/shortcut_viewer/strings/ash_components_strings_fil.xtb
diff --git a/ash/components/strings/ash_components_strings_fr-CA.xtb b/ash/shortcut_viewer/strings/ash_components_strings_fr-CA.xtb
similarity index 100%
rename from ash/components/strings/ash_components_strings_fr-CA.xtb
rename to ash/shortcut_viewer/strings/ash_components_strings_fr-CA.xtb
diff --git a/ash/components/strings/ash_components_strings_fr.xtb b/ash/shortcut_viewer/strings/ash_components_strings_fr.xtb
similarity index 100%
rename from ash/components/strings/ash_components_strings_fr.xtb
rename to ash/shortcut_viewer/strings/ash_components_strings_fr.xtb
diff --git a/ash/components/strings/ash_components_strings_gl.xtb b/ash/shortcut_viewer/strings/ash_components_strings_gl.xtb
similarity index 100%
rename from ash/components/strings/ash_components_strings_gl.xtb
rename to ash/shortcut_viewer/strings/ash_components_strings_gl.xtb
diff --git a/ash/components/strings/ash_components_strings_gu.xtb b/ash/shortcut_viewer/strings/ash_components_strings_gu.xtb
similarity index 100%
rename from ash/components/strings/ash_components_strings_gu.xtb
rename to ash/shortcut_viewer/strings/ash_components_strings_gu.xtb
diff --git a/ash/components/strings/ash_components_strings_hi.xtb b/ash/shortcut_viewer/strings/ash_components_strings_hi.xtb
similarity index 100%
rename from ash/components/strings/ash_components_strings_hi.xtb
rename to ash/shortcut_viewer/strings/ash_components_strings_hi.xtb
diff --git a/ash/components/strings/ash_components_strings_hr.xtb b/ash/shortcut_viewer/strings/ash_components_strings_hr.xtb
similarity index 100%
rename from ash/components/strings/ash_components_strings_hr.xtb
rename to ash/shortcut_viewer/strings/ash_components_strings_hr.xtb
diff --git a/ash/components/strings/ash_components_strings_hu.xtb b/ash/shortcut_viewer/strings/ash_components_strings_hu.xtb
similarity index 100%
rename from ash/components/strings/ash_components_strings_hu.xtb
rename to ash/shortcut_viewer/strings/ash_components_strings_hu.xtb
diff --git a/ash/components/strings/ash_components_strings_hy.xtb b/ash/shortcut_viewer/strings/ash_components_strings_hy.xtb
similarity index 100%
rename from ash/components/strings/ash_components_strings_hy.xtb
rename to ash/shortcut_viewer/strings/ash_components_strings_hy.xtb
diff --git a/ash/components/strings/ash_components_strings_id.xtb b/ash/shortcut_viewer/strings/ash_components_strings_id.xtb
similarity index 100%
rename from ash/components/strings/ash_components_strings_id.xtb
rename to ash/shortcut_viewer/strings/ash_components_strings_id.xtb
diff --git a/ash/components/strings/ash_components_strings_is.xtb b/ash/shortcut_viewer/strings/ash_components_strings_is.xtb
similarity index 100%
rename from ash/components/strings/ash_components_strings_is.xtb
rename to ash/shortcut_viewer/strings/ash_components_strings_is.xtb
diff --git a/ash/components/strings/ash_components_strings_it.xtb b/ash/shortcut_viewer/strings/ash_components_strings_it.xtb
similarity index 100%
rename from ash/components/strings/ash_components_strings_it.xtb
rename to ash/shortcut_viewer/strings/ash_components_strings_it.xtb
diff --git a/ash/components/strings/ash_components_strings_iw.xtb b/ash/shortcut_viewer/strings/ash_components_strings_iw.xtb
similarity index 100%
rename from ash/components/strings/ash_components_strings_iw.xtb
rename to ash/shortcut_viewer/strings/ash_components_strings_iw.xtb
diff --git a/ash/components/strings/ash_components_strings_ja.xtb b/ash/shortcut_viewer/strings/ash_components_strings_ja.xtb
similarity index 100%
rename from ash/components/strings/ash_components_strings_ja.xtb
rename to ash/shortcut_viewer/strings/ash_components_strings_ja.xtb
diff --git a/ash/components/strings/ash_components_strings_ka.xtb b/ash/shortcut_viewer/strings/ash_components_strings_ka.xtb
similarity index 100%
rename from ash/components/strings/ash_components_strings_ka.xtb
rename to ash/shortcut_viewer/strings/ash_components_strings_ka.xtb
diff --git a/ash/components/strings/ash_components_strings_kk.xtb b/ash/shortcut_viewer/strings/ash_components_strings_kk.xtb
similarity index 100%
rename from ash/components/strings/ash_components_strings_kk.xtb
rename to ash/shortcut_viewer/strings/ash_components_strings_kk.xtb
diff --git a/ash/components/strings/ash_components_strings_km.xtb b/ash/shortcut_viewer/strings/ash_components_strings_km.xtb
similarity index 100%
rename from ash/components/strings/ash_components_strings_km.xtb
rename to ash/shortcut_viewer/strings/ash_components_strings_km.xtb
diff --git a/ash/components/strings/ash_components_strings_kn.xtb b/ash/shortcut_viewer/strings/ash_components_strings_kn.xtb
similarity index 100%
rename from ash/components/strings/ash_components_strings_kn.xtb
rename to ash/shortcut_viewer/strings/ash_components_strings_kn.xtb
diff --git a/ash/components/strings/ash_components_strings_ko.xtb b/ash/shortcut_viewer/strings/ash_components_strings_ko.xtb
similarity index 100%
rename from ash/components/strings/ash_components_strings_ko.xtb
rename to ash/shortcut_viewer/strings/ash_components_strings_ko.xtb
diff --git a/ash/components/strings/ash_components_strings_ky.xtb b/ash/shortcut_viewer/strings/ash_components_strings_ky.xtb
similarity index 100%
rename from ash/components/strings/ash_components_strings_ky.xtb
rename to ash/shortcut_viewer/strings/ash_components_strings_ky.xtb
diff --git a/ash/components/strings/ash_components_strings_lo.xtb b/ash/shortcut_viewer/strings/ash_components_strings_lo.xtb
similarity index 100%
rename from ash/components/strings/ash_components_strings_lo.xtb
rename to ash/shortcut_viewer/strings/ash_components_strings_lo.xtb
diff --git a/ash/components/strings/ash_components_strings_lt.xtb b/ash/shortcut_viewer/strings/ash_components_strings_lt.xtb
similarity index 100%
rename from ash/components/strings/ash_components_strings_lt.xtb
rename to ash/shortcut_viewer/strings/ash_components_strings_lt.xtb
diff --git a/ash/components/strings/ash_components_strings_lv.xtb b/ash/shortcut_viewer/strings/ash_components_strings_lv.xtb
similarity index 100%
rename from ash/components/strings/ash_components_strings_lv.xtb
rename to ash/shortcut_viewer/strings/ash_components_strings_lv.xtb
diff --git a/ash/components/strings/ash_components_strings_mk.xtb b/ash/shortcut_viewer/strings/ash_components_strings_mk.xtb
similarity index 100%
rename from ash/components/strings/ash_components_strings_mk.xtb
rename to ash/shortcut_viewer/strings/ash_components_strings_mk.xtb
diff --git a/ash/components/strings/ash_components_strings_ml.xtb b/ash/shortcut_viewer/strings/ash_components_strings_ml.xtb
similarity index 100%
rename from ash/components/strings/ash_components_strings_ml.xtb
rename to ash/shortcut_viewer/strings/ash_components_strings_ml.xtb
diff --git a/ash/components/strings/ash_components_strings_mn.xtb b/ash/shortcut_viewer/strings/ash_components_strings_mn.xtb
similarity index 100%
rename from ash/components/strings/ash_components_strings_mn.xtb
rename to ash/shortcut_viewer/strings/ash_components_strings_mn.xtb
diff --git a/ash/components/strings/ash_components_strings_mr.xtb b/ash/shortcut_viewer/strings/ash_components_strings_mr.xtb
similarity index 100%
rename from ash/components/strings/ash_components_strings_mr.xtb
rename to ash/shortcut_viewer/strings/ash_components_strings_mr.xtb
diff --git a/ash/components/strings/ash_components_strings_ms.xtb b/ash/shortcut_viewer/strings/ash_components_strings_ms.xtb
similarity index 100%
rename from ash/components/strings/ash_components_strings_ms.xtb
rename to ash/shortcut_viewer/strings/ash_components_strings_ms.xtb
diff --git a/ash/components/strings/ash_components_strings_my.xtb b/ash/shortcut_viewer/strings/ash_components_strings_my.xtb
similarity index 100%
rename from ash/components/strings/ash_components_strings_my.xtb
rename to ash/shortcut_viewer/strings/ash_components_strings_my.xtb
diff --git a/ash/components/strings/ash_components_strings_ne.xtb b/ash/shortcut_viewer/strings/ash_components_strings_ne.xtb
similarity index 100%
rename from ash/components/strings/ash_components_strings_ne.xtb
rename to ash/shortcut_viewer/strings/ash_components_strings_ne.xtb
diff --git a/ash/components/strings/ash_components_strings_nl.xtb b/ash/shortcut_viewer/strings/ash_components_strings_nl.xtb
similarity index 100%
rename from ash/components/strings/ash_components_strings_nl.xtb
rename to ash/shortcut_viewer/strings/ash_components_strings_nl.xtb
diff --git a/ash/components/strings/ash_components_strings_no.xtb b/ash/shortcut_viewer/strings/ash_components_strings_no.xtb
similarity index 100%
rename from ash/components/strings/ash_components_strings_no.xtb
rename to ash/shortcut_viewer/strings/ash_components_strings_no.xtb
diff --git a/ash/components/strings/ash_components_strings_or.xtb b/ash/shortcut_viewer/strings/ash_components_strings_or.xtb
similarity index 100%
rename from ash/components/strings/ash_components_strings_or.xtb
rename to ash/shortcut_viewer/strings/ash_components_strings_or.xtb
diff --git a/ash/components/strings/ash_components_strings_pa.xtb b/ash/shortcut_viewer/strings/ash_components_strings_pa.xtb
similarity index 100%
rename from ash/components/strings/ash_components_strings_pa.xtb
rename to ash/shortcut_viewer/strings/ash_components_strings_pa.xtb
diff --git a/ash/components/strings/ash_components_strings_pl.xtb b/ash/shortcut_viewer/strings/ash_components_strings_pl.xtb
similarity index 100%
rename from ash/components/strings/ash_components_strings_pl.xtb
rename to ash/shortcut_viewer/strings/ash_components_strings_pl.xtb
diff --git a/ash/components/strings/ash_components_strings_pt-BR.xtb b/ash/shortcut_viewer/strings/ash_components_strings_pt-BR.xtb
similarity index 100%
rename from ash/components/strings/ash_components_strings_pt-BR.xtb
rename to ash/shortcut_viewer/strings/ash_components_strings_pt-BR.xtb
diff --git a/ash/components/strings/ash_components_strings_pt-PT.xtb b/ash/shortcut_viewer/strings/ash_components_strings_pt-PT.xtb
similarity index 100%
rename from ash/components/strings/ash_components_strings_pt-PT.xtb
rename to ash/shortcut_viewer/strings/ash_components_strings_pt-PT.xtb
diff --git a/ash/components/strings/ash_components_strings_ro.xtb b/ash/shortcut_viewer/strings/ash_components_strings_ro.xtb
similarity index 100%
rename from ash/components/strings/ash_components_strings_ro.xtb
rename to ash/shortcut_viewer/strings/ash_components_strings_ro.xtb
diff --git a/ash/components/strings/ash_components_strings_ru.xtb b/ash/shortcut_viewer/strings/ash_components_strings_ru.xtb
similarity index 100%
rename from ash/components/strings/ash_components_strings_ru.xtb
rename to ash/shortcut_viewer/strings/ash_components_strings_ru.xtb
diff --git a/ash/components/strings/ash_components_strings_si.xtb b/ash/shortcut_viewer/strings/ash_components_strings_si.xtb
similarity index 100%
rename from ash/components/strings/ash_components_strings_si.xtb
rename to ash/shortcut_viewer/strings/ash_components_strings_si.xtb
diff --git a/ash/components/strings/ash_components_strings_sk.xtb b/ash/shortcut_viewer/strings/ash_components_strings_sk.xtb
similarity index 100%
rename from ash/components/strings/ash_components_strings_sk.xtb
rename to ash/shortcut_viewer/strings/ash_components_strings_sk.xtb
diff --git a/ash/components/strings/ash_components_strings_sl.xtb b/ash/shortcut_viewer/strings/ash_components_strings_sl.xtb
similarity index 100%
rename from ash/components/strings/ash_components_strings_sl.xtb
rename to ash/shortcut_viewer/strings/ash_components_strings_sl.xtb
diff --git a/ash/components/strings/ash_components_strings_sq.xtb b/ash/shortcut_viewer/strings/ash_components_strings_sq.xtb
similarity index 100%
rename from ash/components/strings/ash_components_strings_sq.xtb
rename to ash/shortcut_viewer/strings/ash_components_strings_sq.xtb
diff --git a/ash/components/strings/ash_components_strings_sr.xtb b/ash/shortcut_viewer/strings/ash_components_strings_sr.xtb
similarity index 100%
rename from ash/components/strings/ash_components_strings_sr.xtb
rename to ash/shortcut_viewer/strings/ash_components_strings_sr.xtb
diff --git a/ash/components/strings/ash_components_strings_sv.xtb b/ash/shortcut_viewer/strings/ash_components_strings_sv.xtb
similarity index 100%
rename from ash/components/strings/ash_components_strings_sv.xtb
rename to ash/shortcut_viewer/strings/ash_components_strings_sv.xtb
diff --git a/ash/components/strings/ash_components_strings_sw.xtb b/ash/shortcut_viewer/strings/ash_components_strings_sw.xtb
similarity index 100%
rename from ash/components/strings/ash_components_strings_sw.xtb
rename to ash/shortcut_viewer/strings/ash_components_strings_sw.xtb
diff --git a/ash/components/strings/ash_components_strings_ta.xtb b/ash/shortcut_viewer/strings/ash_components_strings_ta.xtb
similarity index 100%
rename from ash/components/strings/ash_components_strings_ta.xtb
rename to ash/shortcut_viewer/strings/ash_components_strings_ta.xtb
diff --git a/ash/components/strings/ash_components_strings_te.xtb b/ash/shortcut_viewer/strings/ash_components_strings_te.xtb
similarity index 100%
rename from ash/components/strings/ash_components_strings_te.xtb
rename to ash/shortcut_viewer/strings/ash_components_strings_te.xtb
diff --git a/ash/components/strings/ash_components_strings_th.xtb b/ash/shortcut_viewer/strings/ash_components_strings_th.xtb
similarity index 100%
rename from ash/components/strings/ash_components_strings_th.xtb
rename to ash/shortcut_viewer/strings/ash_components_strings_th.xtb
diff --git a/ash/components/strings/ash_components_strings_tr.xtb b/ash/shortcut_viewer/strings/ash_components_strings_tr.xtb
similarity index 100%
rename from ash/components/strings/ash_components_strings_tr.xtb
rename to ash/shortcut_viewer/strings/ash_components_strings_tr.xtb
diff --git a/ash/components/strings/ash_components_strings_uk.xtb b/ash/shortcut_viewer/strings/ash_components_strings_uk.xtb
similarity index 100%
rename from ash/components/strings/ash_components_strings_uk.xtb
rename to ash/shortcut_viewer/strings/ash_components_strings_uk.xtb
diff --git a/ash/components/strings/ash_components_strings_ur.xtb b/ash/shortcut_viewer/strings/ash_components_strings_ur.xtb
similarity index 100%
rename from ash/components/strings/ash_components_strings_ur.xtb
rename to ash/shortcut_viewer/strings/ash_components_strings_ur.xtb
diff --git a/ash/components/strings/ash_components_strings_uz.xtb b/ash/shortcut_viewer/strings/ash_components_strings_uz.xtb
similarity index 100%
rename from ash/components/strings/ash_components_strings_uz.xtb
rename to ash/shortcut_viewer/strings/ash_components_strings_uz.xtb
diff --git a/ash/components/strings/ash_components_strings_vi.xtb b/ash/shortcut_viewer/strings/ash_components_strings_vi.xtb
similarity index 100%
rename from ash/components/strings/ash_components_strings_vi.xtb
rename to ash/shortcut_viewer/strings/ash_components_strings_vi.xtb
diff --git a/ash/components/strings/ash_components_strings_zh-CN.xtb b/ash/shortcut_viewer/strings/ash_components_strings_zh-CN.xtb
similarity index 100%
rename from ash/components/strings/ash_components_strings_zh-CN.xtb
rename to ash/shortcut_viewer/strings/ash_components_strings_zh-CN.xtb
diff --git a/ash/components/strings/ash_components_strings_zh-HK.xtb b/ash/shortcut_viewer/strings/ash_components_strings_zh-HK.xtb
similarity index 100%
rename from ash/components/strings/ash_components_strings_zh-HK.xtb
rename to ash/shortcut_viewer/strings/ash_components_strings_zh-HK.xtb
diff --git a/ash/components/strings/ash_components_strings_zh-TW.xtb b/ash/shortcut_viewer/strings/ash_components_strings_zh-TW.xtb
similarity index 100%
rename from ash/components/strings/ash_components_strings_zh-TW.xtb
rename to ash/shortcut_viewer/strings/ash_components_strings_zh-TW.xtb
diff --git a/ash/components/strings/ash_components_strings_zu.xtb b/ash/shortcut_viewer/strings/ash_components_strings_zu.xtb
similarity index 100%
rename from ash/components/strings/ash_components_strings_zu.xtb
rename to ash/shortcut_viewer/strings/ash_components_strings_zu.xtb
diff --git a/ash/components/shortcut_viewer/vector_icons/BUILD.gn b/ash/shortcut_viewer/vector_icons/BUILD.gn
similarity index 100%
rename from ash/components/shortcut_viewer/vector_icons/BUILD.gn
rename to ash/shortcut_viewer/vector_icons/BUILD.gn
diff --git a/ash/components/shortcut_viewer/vector_icons/OWNERS b/ash/shortcut_viewer/vector_icons/OWNERS
similarity index 100%
rename from ash/components/shortcut_viewer/vector_icons/OWNERS
rename to ash/shortcut_viewer/vector_icons/OWNERS
diff --git a/ash/components/shortcut_viewer/vector_icons/ksv_arrow_down.icon b/ash/shortcut_viewer/vector_icons/ksv_arrow_down.icon
similarity index 100%
rename from ash/components/shortcut_viewer/vector_icons/ksv_arrow_down.icon
rename to ash/shortcut_viewer/vector_icons/ksv_arrow_down.icon
diff --git a/ash/components/shortcut_viewer/vector_icons/ksv_arrow_left.icon b/ash/shortcut_viewer/vector_icons/ksv_arrow_left.icon
similarity index 100%
rename from ash/components/shortcut_viewer/vector_icons/ksv_arrow_left.icon
rename to ash/shortcut_viewer/vector_icons/ksv_arrow_left.icon
diff --git a/ash/components/shortcut_viewer/vector_icons/ksv_arrow_right.icon b/ash/shortcut_viewer/vector_icons/ksv_arrow_right.icon
similarity index 100%
rename from ash/components/shortcut_viewer/vector_icons/ksv_arrow_right.icon
rename to ash/shortcut_viewer/vector_icons/ksv_arrow_right.icon
diff --git a/ash/components/shortcut_viewer/vector_icons/ksv_arrow_up.icon b/ash/shortcut_viewer/vector_icons/ksv_arrow_up.icon
similarity index 100%
rename from ash/components/shortcut_viewer/vector_icons/ksv_arrow_up.icon
rename to ash/shortcut_viewer/vector_icons/ksv_arrow_up.icon
diff --git a/ash/components/shortcut_viewer/vector_icons/ksv_brightness_down.icon b/ash/shortcut_viewer/vector_icons/ksv_brightness_down.icon
similarity index 100%
rename from ash/components/shortcut_viewer/vector_icons/ksv_brightness_down.icon
rename to ash/shortcut_viewer/vector_icons/ksv_brightness_down.icon
diff --git a/ash/components/shortcut_viewer/vector_icons/ksv_brightness_up.icon b/ash/shortcut_viewer/vector_icons/ksv_brightness_up.icon
similarity index 100%
rename from ash/components/shortcut_viewer/vector_icons/ksv_brightness_up.icon
rename to ash/shortcut_viewer/vector_icons/ksv_brightness_up.icon
diff --git a/ash/components/shortcut_viewer/vector_icons/ksv_browser_back.icon b/ash/shortcut_viewer/vector_icons/ksv_browser_back.icon
similarity index 100%
rename from ash/components/shortcut_viewer/vector_icons/ksv_browser_back.icon
rename to ash/shortcut_viewer/vector_icons/ksv_browser_back.icon
diff --git a/ash/components/shortcut_viewer/vector_icons/ksv_browser_forward.icon b/ash/shortcut_viewer/vector_icons/ksv_browser_forward.icon
similarity index 100%
rename from ash/components/shortcut_viewer/vector_icons/ksv_browser_forward.icon
rename to ash/shortcut_viewer/vector_icons/ksv_browser_forward.icon
diff --git a/ash/components/shortcut_viewer/vector_icons/ksv_fullscreen.icon b/ash/shortcut_viewer/vector_icons/ksv_fullscreen.icon
similarity index 100%
rename from ash/components/shortcut_viewer/vector_icons/ksv_fullscreen.icon
rename to ash/shortcut_viewer/vector_icons/ksv_fullscreen.icon
diff --git a/ash/components/shortcut_viewer/vector_icons/ksv_mute.icon b/ash/shortcut_viewer/vector_icons/ksv_mute.icon
similarity index 100%
rename from ash/components/shortcut_viewer/vector_icons/ksv_mute.icon
rename to ash/shortcut_viewer/vector_icons/ksv_mute.icon
diff --git a/ash/components/shortcut_viewer/vector_icons/ksv_overview.icon b/ash/shortcut_viewer/vector_icons/ksv_overview.icon
similarity index 100%
rename from ash/components/shortcut_viewer/vector_icons/ksv_overview.icon
rename to ash/shortcut_viewer/vector_icons/ksv_overview.icon
diff --git a/ash/components/shortcut_viewer/vector_icons/ksv_privacy_screen_toggle.icon b/ash/shortcut_viewer/vector_icons/ksv_privacy_screen_toggle.icon
similarity index 100%
rename from ash/components/shortcut_viewer/vector_icons/ksv_privacy_screen_toggle.icon
rename to ash/shortcut_viewer/vector_icons/ksv_privacy_screen_toggle.icon
diff --git a/ash/components/shortcut_viewer/vector_icons/ksv_reload.icon b/ash/shortcut_viewer/vector_icons/ksv_reload.icon
similarity index 100%
rename from ash/components/shortcut_viewer/vector_icons/ksv_reload.icon
rename to ash/shortcut_viewer/vector_icons/ksv_reload.icon
diff --git a/ash/components/shortcut_viewer/vector_icons/ksv_search_back.icon b/ash/shortcut_viewer/vector_icons/ksv_search_back.icon
similarity index 100%
rename from ash/components/shortcut_viewer/vector_icons/ksv_search_back.icon
rename to ash/shortcut_viewer/vector_icons/ksv_search_back.icon
diff --git a/ash/components/shortcut_viewer/vector_icons/ksv_search_bar.icon b/ash/shortcut_viewer/vector_icons/ksv_search_bar.icon
similarity index 100%
rename from ash/components/shortcut_viewer/vector_icons/ksv_search_bar.icon
rename to ash/shortcut_viewer/vector_icons/ksv_search_bar.icon
diff --git a/ash/components/shortcut_viewer/vector_icons/ksv_search_close.icon b/ash/shortcut_viewer/vector_icons/ksv_search_close.icon
similarity index 100%
rename from ash/components/shortcut_viewer/vector_icons/ksv_search_close.icon
rename to ash/shortcut_viewer/vector_icons/ksv_search_close.icon
diff --git a/ash/components/shortcut_viewer/vector_icons/ksv_search_no_result.icon b/ash/shortcut_viewer/vector_icons/ksv_search_no_result.icon
similarity index 100%
rename from ash/components/shortcut_viewer/vector_icons/ksv_search_no_result.icon
rename to ash/shortcut_viewer/vector_icons/ksv_search_no_result.icon
diff --git a/ash/components/shortcut_viewer/vector_icons/ksv_separator_plus.icon b/ash/shortcut_viewer/vector_icons/ksv_separator_plus.icon
similarity index 100%
rename from ash/components/shortcut_viewer/vector_icons/ksv_separator_plus.icon
rename to ash/shortcut_viewer/vector_icons/ksv_separator_plus.icon
diff --git a/ash/components/shortcut_viewer/vector_icons/ksv_volume_down.icon b/ash/shortcut_viewer/vector_icons/ksv_volume_down.icon
similarity index 100%
rename from ash/components/shortcut_viewer/vector_icons/ksv_volume_down.icon
rename to ash/shortcut_viewer/vector_icons/ksv_volume_down.icon
diff --git a/ash/components/shortcut_viewer/vector_icons/ksv_volume_up.icon b/ash/shortcut_viewer/vector_icons/ksv_volume_up.icon
similarity index 100%
rename from ash/components/shortcut_viewer/vector_icons/ksv_volume_up.icon
rename to ash/shortcut_viewer/vector_icons/ksv_volume_up.icon
diff --git a/ash/components/shortcut_viewer/vector_icons/vector_icons.cc.template b/ash/shortcut_viewer/vector_icons/vector_icons.cc.template
similarity index 88%
rename from ash/components/shortcut_viewer/vector_icons/vector_icons.cc.template
rename to ash/shortcut_viewer/vector_icons/vector_icons.cc.template
index dca06e2..77cd582 100644
--- a/ash/components/shortcut_viewer/vector_icons/vector_icons.cc.template
+++ b/ash/shortcut_viewer/vector_icons/vector_icons.cc.template
@@ -5,7 +5,7 @@
 // vector_icons.cc.template is used to generate vector_icons.cc. Edit the former
 // rather than the latter.
 
-#include "ash/components/shortcut_viewer/vector_icons/vector_icons.h"
+#include "ash/shortcut_viewer/vector_icons/vector_icons.h"
 
 #include "base/logging.h"
 #include "components/vector_icons/cc_macros.h"
diff --git a/ash/components/shortcut_viewer/vector_icons/vector_icons.h.template b/ash/shortcut_viewer/vector_icons/vector_icons.h.template
similarity index 71%
rename from ash/components/shortcut_viewer/vector_icons/vector_icons.h.template
rename to ash/shortcut_viewer/vector_icons/vector_icons.h.template
index fac37ae..6e25027b 100644
--- a/ash/components/shortcut_viewer/vector_icons/vector_icons.h.template
+++ b/ash/shortcut_viewer/vector_icons/vector_icons.h.template
@@ -5,8 +5,8 @@
 // vector_icons.h.template is used to generate vector_icons.h. Edit the former
 // rather than the latter.
 
-#ifndef ASH_COMPONENTS_SHORTCUT_VIEWER_VECTOR_ICONS_VECTOR_ICONS_H_
-#define ASH_COMPONENTS_SHORTCUT_VIEWER_VECTOR_ICONS_VECTOR_ICONS_H_
+#ifndef ASH_SHORTCUT_VIEWER_VECTOR_ICONS_VECTOR_ICONS_H_
+#define ASH_SHORTCUT_VIEWER_VECTOR_ICONS_VECTOR_ICONS_H_
 
 namespace gfx {
 struct VectorIcon;
@@ -23,4 +23,4 @@
 
 #undef VECTOR_ICON_TEMPLATE_H
 
-#endif  // ASH_COMPONENTS_SHORTCUT_VIEWER_VECTOR_ICONS_VECTOR_ICONS_H_
+#endif  // ASH_SHORTCUT_VIEWER_VECTOR_ICONS_VECTOR_ICONS_H_
diff --git a/ash/components/shortcut_viewer/views/bubble_view.cc b/ash/shortcut_viewer/views/bubble_view.cc
similarity index 98%
rename from ash/components/shortcut_viewer/views/bubble_view.cc
rename to ash/shortcut_viewer/views/bubble_view.cc
index 9c7281f..dedaba9 100644
--- a/ash/components/shortcut_viewer/views/bubble_view.cc
+++ b/ash/shortcut_viewer/views/bubble_view.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ash/components/shortcut_viewer/views/bubble_view.h"
+#include "ash/shortcut_viewer/views/bubble_view.h"
 
 #include <memory>
 
diff --git a/ash/components/shortcut_viewer/views/bubble_view.h b/ash/shortcut_viewer/views/bubble_view.h
similarity index 85%
rename from ash/components/shortcut_viewer/views/bubble_view.h
rename to ash/shortcut_viewer/views/bubble_view.h
index 1768ce9..223dea0 100644
--- a/ash/components/shortcut_viewer/views/bubble_view.h
+++ b/ash/shortcut_viewer/views/bubble_view.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef ASH_COMPONENTS_SHORTCUT_VIEWER_VIEWS_BUBBLE_VIEW_H_
-#define ASH_COMPONENTS_SHORTCUT_VIEWER_VIEWS_BUBBLE_VIEW_H_
+#ifndef ASH_SHORTCUT_VIEWER_VIEWS_BUBBLE_VIEW_H_
+#define ASH_SHORTCUT_VIEWER_VIEWS_BUBBLE_VIEW_H_
 
 #include <vector>
 
@@ -50,4 +50,4 @@
 
 }  // namespace keyboard_shortcut_viewer
 
-#endif  // ASH_COMPONENTS_SHORTCUT_VIEWER_VIEWS_BUBBLE_VIEW_H_
+#endif  // ASH_SHORTCUT_VIEWER_VIEWS_BUBBLE_VIEW_H_
diff --git a/ash/components/shortcut_viewer/views/keyboard_shortcut_item_list_view.cc b/ash/shortcut_viewer/views/keyboard_shortcut_item_list_view.cc
similarity index 94%
rename from ash/components/shortcut_viewer/views/keyboard_shortcut_item_list_view.cc
rename to ash/shortcut_viewer/views/keyboard_shortcut_item_list_view.cc
index 2866f76..1952ed5 100644
--- a/ash/components/shortcut_viewer/views/keyboard_shortcut_item_list_view.cc
+++ b/ash/shortcut_viewer/views/keyboard_shortcut_item_list_view.cc
@@ -2,9 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ash/components/shortcut_viewer/views/keyboard_shortcut_item_list_view.h"
+#include "ash/shortcut_viewer/views/keyboard_shortcut_item_list_view.h"
 
-#include "ash/components/shortcut_viewer/views/keyboard_shortcut_item_view.h"
+#include "ash/shortcut_viewer/views/keyboard_shortcut_item_view.h"
 #include "ui/base/default_style.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/gfx/canvas.h"
diff --git a/ash/components/shortcut_viewer/views/keyboard_shortcut_item_list_view.h b/ash/shortcut_viewer/views/keyboard_shortcut_item_list_view.h
similarity index 82%
rename from ash/components/shortcut_viewer/views/keyboard_shortcut_item_list_view.h
rename to ash/shortcut_viewer/views/keyboard_shortcut_item_list_view.h
index 4891a40..1f9fd60 100644
--- a/ash/components/shortcut_viewer/views/keyboard_shortcut_item_list_view.h
+++ b/ash/shortcut_viewer/views/keyboard_shortcut_item_list_view.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef ASH_COMPONENTS_SHORTCUT_VIEWER_VIEWS_KEYBOARD_SHORTCUT_ITEM_LIST_VIEW_H_
-#define ASH_COMPONENTS_SHORTCUT_VIEWER_VIEWS_KEYBOARD_SHORTCUT_ITEM_LIST_VIEW_H_
+#ifndef ASH_SHORTCUT_VIEWER_VIEWS_KEYBOARD_SHORTCUT_ITEM_LIST_VIEW_H_
+#define ASH_SHORTCUT_VIEWER_VIEWS_KEYBOARD_SHORTCUT_ITEM_LIST_VIEW_H_
 
 #include "ui/views/view.h"
 
@@ -32,4 +32,4 @@
 
 }  // namespace keyboard_shortcut_viewer
 
-#endif  // ASH_COMPONENTS_SHORTCUT_VIEWER_VIEWS_KEYBOARD_SHORTCUT_ITEM_LIST_VIEW_H_
+#endif  // ASH_SHORTCUT_VIEWER_VIEWS_KEYBOARD_SHORTCUT_ITEM_LIST_VIEW_H_
diff --git a/ash/components/shortcut_viewer/views/keyboard_shortcut_item_view.cc b/ash/shortcut_viewer/views/keyboard_shortcut_item_view.cc
similarity index 95%
rename from ash/components/shortcut_viewer/views/keyboard_shortcut_item_view.cc
rename to ash/shortcut_viewer/views/keyboard_shortcut_item_view.cc
index a268b5d..57ca318 100644
--- a/ash/components/shortcut_viewer/views/keyboard_shortcut_item_view.cc
+++ b/ash/shortcut_viewer/views/keyboard_shortcut_item_view.cc
@@ -2,16 +2,16 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ash/components/shortcut_viewer/views/keyboard_shortcut_item_view.h"
+#include "ash/shortcut_viewer/views/keyboard_shortcut_item_view.h"
 
 #include <memory>
 #include <vector>
 
-#include "ash/components/shortcut_viewer/keyboard_shortcut_item.h"
-#include "ash/components/shortcut_viewer/keyboard_shortcut_viewer_metadata.h"
-#include "ash/components/shortcut_viewer/vector_icons/vector_icons.h"
-#include "ash/components/shortcut_viewer/views/bubble_view.h"
-#include "ash/components/strings/grit/ash_components_strings.h"
+#include "ash/shortcut_viewer/keyboard_shortcut_item.h"
+#include "ash/shortcut_viewer/keyboard_shortcut_viewer_metadata.h"
+#include "ash/shortcut_viewer/strings/grit/ash_components_strings.h"
+#include "ash/shortcut_viewer/vector_icons/vector_icons.h"
+#include "ash/shortcut_viewer/views/bubble_view.h"
 #include "base/i18n/rtl.h"
 #include "base/no_destructor.h"
 #include "base/strings/utf_string_conversions.h"
diff --git a/ash/components/shortcut_viewer/views/keyboard_shortcut_item_view.h b/ash/shortcut_viewer/views/keyboard_shortcut_item_view.h
similarity index 89%
rename from ash/components/shortcut_viewer/views/keyboard_shortcut_item_view.h
rename to ash/shortcut_viewer/views/keyboard_shortcut_item_view.h
index d1bad4f..1e0d0f9 100644
--- a/ash/components/shortcut_viewer/views/keyboard_shortcut_item_view.h
+++ b/ash/shortcut_viewer/views/keyboard_shortcut_item_view.h
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef ASH_COMPONENTS_SHORTCUT_VIEWER_VIEWS_KEYBOARD_SHORTCUT_ITEM_VIEW_H_
-#define ASH_COMPONENTS_SHORTCUT_VIEWER_VIEWS_KEYBOARD_SHORTCUT_ITEM_VIEW_H_
+#ifndef ASH_SHORTCUT_VIEWER_VIEWS_KEYBOARD_SHORTCUT_ITEM_VIEW_H_
+#define ASH_SHORTCUT_VIEWER_VIEWS_KEYBOARD_SHORTCUT_ITEM_VIEW_H_
 
-#include "ash/components/shortcut_viewer/keyboard_shortcut_item.h"
+#include "ash/shortcut_viewer/keyboard_shortcut_item.h"
 #include "ui/views/view.h"
 
 namespace views {
@@ -80,4 +80,4 @@
 
 }  // namespace keyboard_shortcut_viewer
 
-#endif  // ASH_COMPONENTS_SHORTCUT_VIEWER_VIEWS_KEYBOARD_SHORTCUT_ITEM_VIEW_H_
+#endif  // ASH_SHORTCUT_VIEWER_VIEWS_KEYBOARD_SHORTCUT_ITEM_VIEW_H_
diff --git a/ash/components/shortcut_viewer/views/keyboard_shortcut_view.cc b/ash/shortcut_viewer/views/keyboard_shortcut_view.cc
similarity index 97%
rename from ash/components/shortcut_viewer/views/keyboard_shortcut_view.cc
rename to ash/shortcut_viewer/views/keyboard_shortcut_view.cc
index 2d3c0b0c..bb4073d 100644
--- a/ash/components/shortcut_viewer/views/keyboard_shortcut_view.cc
+++ b/ash/shortcut_viewer/views/keyboard_shortcut_view.cc
@@ -2,19 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ash/components/shortcut_viewer/views/keyboard_shortcut_view.h"
+#include "ash/shortcut_viewer/views/keyboard_shortcut_view.h"
 
 #include <algorithm>
 #include <memory>
 #include <string>
 #include <utility>
 
-#include "ash/components/shortcut_viewer/keyboard_shortcut_viewer_metadata.h"
-#include "ash/components/shortcut_viewer/vector_icons/vector_icons.h"
-#include "ash/components/shortcut_viewer/views/keyboard_shortcut_item_list_view.h"
-#include "ash/components/shortcut_viewer/views/keyboard_shortcut_item_view.h"
-#include "ash/components/shortcut_viewer/views/ksv_search_box_view.h"
-#include "ash/components/strings/grit/ash_components_strings.h"
 #include "ash/display/privacy_screen_controller.h"
 #include "ash/public/cpp/app_list/internal_app_id_constants.h"
 #include "ash/public/cpp/app_types.h"
@@ -22,6 +16,12 @@
 #include "ash/public/cpp/shelf_item.h"
 #include "ash/public/cpp/window_properties.h"
 #include "ash/shell.h"
+#include "ash/shortcut_viewer/keyboard_shortcut_viewer_metadata.h"
+#include "ash/shortcut_viewer/strings/grit/ash_components_strings.h"
+#include "ash/shortcut_viewer/vector_icons/vector_icons.h"
+#include "ash/shortcut_viewer/views/keyboard_shortcut_item_list_view.h"
+#include "ash/shortcut_viewer/views/keyboard_shortcut_item_view.h"
+#include "ash/shortcut_viewer/views/ksv_search_box_view.h"
 #include "base/bind.h"
 #include "base/i18n/string_search.h"
 #include "base/metrics/histogram_macros.h"
diff --git a/ash/components/shortcut_viewer/views/keyboard_shortcut_view.h b/ash/shortcut_viewer/views/keyboard_shortcut_view.h
similarity index 95%
rename from ash/components/shortcut_viewer/views/keyboard_shortcut_view.h
rename to ash/shortcut_viewer/views/keyboard_shortcut_view.h
index 78adcf0..6ffbea2 100644
--- a/ash/components/shortcut_viewer/views/keyboard_shortcut_view.h
+++ b/ash/shortcut_viewer/views/keyboard_shortcut_view.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef ASH_COMPONENTS_SHORTCUT_VIEWER_VIEWS_KEYBOARD_SHORTCUT_VIEW_H_
-#define ASH_COMPONENTS_SHORTCUT_VIEWER_VIEWS_KEYBOARD_SHORTCUT_VIEW_H_
+#ifndef ASH_SHORTCUT_VIEWER_VIEWS_KEYBOARD_SHORTCUT_VIEW_H_
+#define ASH_SHORTCUT_VIEWER_VIEWS_KEYBOARD_SHORTCUT_VIEW_H_
 
 #include <map>
 #include <memory>
@@ -136,4 +136,4 @@
 
 }  // namespace keyboard_shortcut_viewer
 
-#endif  // ASH_COMPONENTS_SHORTCUT_VIEWER_VIEWS_KEYBOARD_SHORTCUT_VIEW_H_
+#endif  // ASH_SHORTCUT_VIEWER_VIEWS_KEYBOARD_SHORTCUT_VIEW_H_
diff --git a/ash/components/shortcut_viewer/views/keyboard_shortcut_view_unittest.cc b/ash/shortcut_viewer/views/keyboard_shortcut_view_unittest.cc
similarity index 95%
rename from ash/components/shortcut_viewer/views/keyboard_shortcut_view_unittest.cc
rename to ash/shortcut_viewer/views/keyboard_shortcut_view_unittest.cc
index 8744e71..7bf89e4 100644
--- a/ash/components/shortcut_viewer/views/keyboard_shortcut_view_unittest.cc
+++ b/ash/shortcut_viewer/views/keyboard_shortcut_view_unittest.cc
@@ -2,13 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ash/components/shortcut_viewer/views/keyboard_shortcut_view.h"
+#include "ash/shortcut_viewer/views/keyboard_shortcut_view.h"
 
 #include <set>
 
-#include "ash/components/shortcut_viewer/keyboard_shortcut_viewer_metadata.h"
-#include "ash/components/shortcut_viewer/views/keyboard_shortcut_item_view.h"
-#include "ash/components/shortcut_viewer/views/ksv_search_box_view.h"
+#include "ash/shortcut_viewer/keyboard_shortcut_viewer_metadata.h"
+#include "ash/shortcut_viewer/views/keyboard_shortcut_item_view.h"
+#include "ash/shortcut_viewer/views/ksv_search_box_view.h"
 #include "ash/test/ash_test_base.h"
 #include "base/bind.h"
 #include "base/test/metrics/histogram_tester.h"
diff --git a/ash/components/shortcut_viewer/views/ksv_search_box_view.cc b/ash/shortcut_viewer/views/ksv_search_box_view.cc
similarity index 95%
rename from ash/components/shortcut_viewer/views/ksv_search_box_view.cc
rename to ash/shortcut_viewer/views/ksv_search_box_view.cc
index e071e33..8d6b74d 100644
--- a/ash/components/shortcut_viewer/views/ksv_search_box_view.cc
+++ b/ash/shortcut_viewer/views/ksv_search_box_view.cc
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ash/components/shortcut_viewer/views/ksv_search_box_view.h"
+#include "ash/shortcut_viewer/views/ksv_search_box_view.h"
 
-#include "ash/components/shortcut_viewer/vector_icons/vector_icons.h"
-#include "ash/components/strings/grit/ash_components_strings.h"
+#include "ash/shortcut_viewer/strings/grit/ash_components_strings.h"
+#include "ash/shortcut_viewer/vector_icons/vector_icons.h"
 #include "ui/accessibility/ax_enums.mojom.h"
 #include "ui/accessibility/ax_node_data.h"
 #include "ui/base/l10n/l10n_util.h"
diff --git a/ash/components/shortcut_viewer/views/ksv_search_box_view.h b/ash/shortcut_viewer/views/ksv_search_box_view.h
similarity index 87%
rename from ash/components/shortcut_viewer/views/ksv_search_box_view.h
rename to ash/shortcut_viewer/views/ksv_search_box_view.h
index cdf406ab..a7c6c69 100644
--- a/ash/components/shortcut_viewer/views/ksv_search_box_view.h
+++ b/ash/shortcut_viewer/views/ksv_search_box_view.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef ASH_COMPONENTS_SHORTCUT_VIEWER_VIEWS_KSV_SEARCH_BOX_VIEW_H_
-#define ASH_COMPONENTS_SHORTCUT_VIEWER_VIEWS_KSV_SEARCH_BOX_VIEW_H_
+#ifndef ASH_SHORTCUT_VIEWER_VIEWS_KSV_SEARCH_BOX_VIEW_H_
+#define ASH_SHORTCUT_VIEWER_VIEWS_KSV_SEARCH_BOX_VIEW_H_
 
 #include "base/macros.h"
 #include "base/strings/string16.h"
@@ -46,4 +46,4 @@
 
 }  // namespace keyboard_shortcut_viewer
 
-#endif  // ASH_COMPONENTS_SHORTCUT_VIEWER_VIEWS_KSV_SEARCH_BOX_VIEW_H_
+#endif  // ASH_SHORTCUT_VIEWER_VIEWS_KSV_SEARCH_BOX_VIEW_H_
diff --git a/ash/strings/BUILD.gn b/ash/strings/BUILD.gn
index ea3e60d0..c637fc4 100644
--- a/ash/strings/BUILD.gn
+++ b/ash/strings/BUILD.gn
@@ -79,7 +79,7 @@
   repack(target_name) {
     # Each input pak file should also have a deps line for completeness.
     sources = [
-      "$root_gen_dir/ash/components/strings/ash_components_strings_${locale}.pak",
+      "$root_gen_dir/ash/shortcut_viewer/strings/ash_components_strings_${locale}.pak",
       "$root_gen_dir/ash/strings/ash_strings_${locale}.pak",
       "$root_gen_dir/chromeos/strings/chromeos_strings_${locale}.pak",
       "$root_gen_dir/components/strings/components_strings_${locale}.pak",
@@ -90,7 +90,7 @@
     ]
 
     deps = [
-      "//ash/components/strings",
+      "//ash/shortcut_viewer/strings",
       "//ash/strings",
       "//chromeos/strings",
       "//components/strings",
diff --git a/ash/wm/overview/overview_controller_unittest.cc b/ash/wm/overview/overview_controller_unittest.cc
index caf1924..4b7a509 100644
--- a/ash/wm/overview/overview_controller_unittest.cc
+++ b/ash/wm/overview/overview_controller_unittest.cc
@@ -10,6 +10,8 @@
 #include "ash/keyboard/ui/test/keyboard_test_util.h"
 #include "ash/public/cpp/ash_features.h"
 #include "ash/public/cpp/keyboard/keyboard_switches.h"
+#include "ash/public/cpp/overview_test_api.h"
+#include "ash/public/cpp/test/shell_test_api.h"
 #include "ash/root_window_controller.h"
 #include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
@@ -57,14 +59,14 @@
   }
 
   // OverviewObserver:
+  void OnOverviewModeWillStart() override { ++observer_counts_.will_start; }
   void OnOverviewModeStarting() override {
+    ++observer_counts_.starting;
     UpdateLastAnimationStates(
         Shell::Get()->overview_controller()->overview_session());
   }
-  void OnOverviewModeEnding(OverviewSession* overview_session) override {
-    UpdateLastAnimationStates(overview_session);
-  }
   void OnOverviewModeStartingAnimationComplete(bool canceled) override {
+    ++observer_counts_.starting_animation_complete;
     if (!should_monitor_animation_state_)
       return;
 
@@ -73,7 +75,13 @@
     if (run_loop_)
       run_loop_->Quit();
   }
+  void OnOverviewModeEnding(OverviewSession* overview_session) override {
+    ++observer_counts_.ending;
+    UpdateLastAnimationStates(overview_session);
+  }
+  void OnOverviewModeEnded() override { ++observer_counts_.ended; }
   void OnOverviewModeEndingAnimationComplete(bool canceled) override {
+    ++observer_counts_.ending_animation_complete;
     if (!should_monitor_animation_state_)
       return;
 
@@ -102,6 +110,23 @@
     }
   }
 
+  // Checks if all the observed methods have fired the same amount of times.
+  bool ObserverCountsEqual() {
+    const int expected_count = observer_counts_.will_start;
+    DCHECK_GT(expected_count, 0);
+    if (observer_counts_.starting != expected_count)
+      return false;
+    if (observer_counts_.starting_animation_complete != expected_count)
+      return false;
+    if (observer_counts_.ending != expected_count)
+      return false;
+    if (observer_counts_.ended != expected_count)
+      return false;
+    if (observer_counts_.ending_animation_complete != expected_count)
+      return false;
+    return true;
+  }
+
   bool is_ended() const { return ending_animation_state_ != UNKNOWN; }
   bool is_started() const { return starting_animation_state_ != UNKNOWN; }
   AnimationState starting_animation_state() const {
@@ -131,6 +156,17 @@
         enter_exit_type == OverviewSession::EnterExitOverviewType::kFadeOutExit;
   }
 
+  // Struct which keeps track of the counts a OverviewObserver method has fired.
+  // These are used to verify that certain methods have a one to one ratio.
+  struct ObserverCounts {
+    int will_start;
+    int starting;
+    int starting_animation_complete;
+    int ending;
+    int ended;
+    int ending_animation_complete;
+  } observer_counts_ = {0};
+
   AnimationState starting_animation_state_ = UNKNOWN;
   AnimationState ending_animation_state_ = UNKNOWN;
   bool last_animation_was_slide_ = false;
@@ -345,6 +381,79 @@
   GetAppListTestHelper()->CheckVisibility(false);
 }
 
+// Some ash codes are reliant on some OverviewObserver calls matching (i.e. the
+// amount of starts should match the amount of ends). This test verifies that
+// behavior. Tests for both tablet and clamshell mode.
+TEST_F(OverviewControllerTest, ObserverCallsMatch) {
+  ui::ScopedAnimationDurationScaleMode non_zero(
+      ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
+  TestOverviewObserver observer(/*should_monitor_animation_state=*/false);
+
+  // Helper which waits for an overview animation to finish.
+  auto wait_for_animation = [](bool enter) {
+    ShellTestApi().WaitForOverviewAnimationState(
+        enter ? OverviewAnimationState::kEnterAnimationComplete
+              : OverviewAnimationState::kExitAnimationComplete);
+  };
+
+  auto set_tablet_mode_enabled = [](bool enabled) {
+    TabletMode::Waiter waiter(enabled);
+    if (enabled)
+      TabletModeControllerTestApi().EnterTabletMode();
+    else
+      TabletModeControllerTestApi().LeaveTabletMode();
+    waiter.Wait();
+  };
+
+  // Tests the case where we enter without windows and do regular enter/exit
+  // (wait for enter animation to finish before exiting).
+  auto* overview_controller = Shell::Get()->overview_controller();
+
+  for (bool is_tablet_mode : {false, true}) {
+    SCOPED_TRACE(is_tablet_mode ? "Tablet Mode" : "Clamshell Mode");
+    set_tablet_mode_enabled(is_tablet_mode);
+
+    overview_controller->StartOverview();
+    wait_for_animation(/*enter=*/true);
+    overview_controller->EndOverview();
+    wait_for_animation(/*enter=*/false);
+    EXPECT_TRUE(observer.ObserverCountsEqual());
+  }
+
+  // Create one window for the next set of tests.
+  std::unique_ptr<aura::Window> window(CreateTestWindow());
+
+  for (bool is_tablet_mode : {false, true}) {
+    SCOPED_TRACE(is_tablet_mode ? "Tablet Mode" : "Clamshell Mode");
+    set_tablet_mode_enabled(is_tablet_mode);
+
+    // Tests the case where we enter with windows and do regular enter/exit
+    // (wait for enter animation to finish before exiting).
+    overview_controller->StartOverview();
+    wait_for_animation(/*enter=*/true);
+    overview_controller->EndOverview();
+    wait_for_animation(/*enter=*/false);
+    EXPECT_TRUE(observer.ObserverCountsEqual());
+
+    // Tests the case where we exit overview before the start animation has
+    // completed.
+    overview_controller->StartOverview();
+    overview_controller->EndOverview();
+    wait_for_animation(/*enter=*/false);
+    EXPECT_TRUE(observer.ObserverCountsEqual());
+
+    // Tests the case where we enter overview before the exit animation has
+    // completed.
+    overview_controller->StartOverview();
+    wait_for_animation(/*enter=*/true);
+    overview_controller->EndOverview();
+    overview_controller->StartOverview();
+    overview_controller->EndOverview();
+    wait_for_animation(/*enter=*/false);
+    EXPECT_TRUE(observer.ObserverCountsEqual());
+  }
+}
+
 // Parameterized test depending on whether kDragFromShelfToHomeOrOverview is
 // enabled.
 class OverviewControllerTestWithDragFromShelfToHomeOrOverview
diff --git a/base/BUILD.gn b/base/BUILD.gn
index 391f8e1..b47b0ec 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -3360,6 +3360,7 @@
       "android/java/src/org/chromium/base/metrics/ScopedSysTraceEvent.java",
       "android/java/src/org/chromium/base/metrics/StatisticsRecorderAndroid.java",
       "android/java/src/org/chromium/base/metrics/UmaRecorder.java",
+      "android/java/src/org/chromium/base/metrics/UmaRecorderHolder.java",
       "android/java/src/org/chromium/base/multidex/ChromiumMultiDexInstaller.java",
       "android/java/src/org/chromium/base/process_launcher/BindService.java",
       "android/java/src/org/chromium/base/process_launcher/ChildConnectionAllocator.java",
diff --git a/base/android/java/src/org/chromium/base/AnimationFrameTimeHistogram.java b/base/android/java/src/org/chromium/base/AnimationFrameTimeHistogram.java
index c902a1d..3920f7ac 100644
--- a/base/android/java/src/org/chromium/base/AnimationFrameTimeHistogram.java
+++ b/base/android/java/src/org/chromium/base/AnimationFrameTimeHistogram.java
@@ -12,7 +12,7 @@
 import android.util.Log;
 
 import org.chromium.base.annotations.NativeMethods;
-import org.chromium.base.metrics.RecordHistogram;
+import org.chromium.base.metrics.UmaRecorderHolder;
 
 /**
  * Record Android animation frame rate and save it to UMA histogram. This is mainly for monitoring
@@ -73,7 +73,7 @@
      * successful.
      */
     public void endRecording() {
-        if (mRecorder.endRecording() && RecordHistogram.sDisabledBy == null) {
+        if (mRecorder.endRecording() && UmaRecorderHolder.sDisabledBy == null) {
             AnimationFrameTimeHistogramJni.get().saveHistogram(
                     mHistogramName, mRecorder.getFrameTimesMs(), mRecorder.getFrameTimesCount());
         }
diff --git a/base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java b/base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java
index 1286947..55a6c60 100644
--- a/base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java
+++ b/base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java
@@ -38,6 +38,7 @@
 import org.chromium.base.compat.ApiHelperForM;
 import org.chromium.base.metrics.CachedMetrics;
 import org.chromium.base.metrics.RecordHistogram;
+import org.chromium.base.metrics.UmaRecorderHolder;
 
 import java.io.File;
 import java.io.IOException;
@@ -661,7 +662,7 @@
             Log.i(TAG, "Loaded native library version number \"%s\"",
                     NativeLibraries.sVersionNumber);
         }
-        RecordHistogram.onLibraryLoaded();
+        UmaRecorderHolder.onLibraryLoaded();
 
         // From now on, keep tracing in sync with native.
         TraceEvent.registerNativeEnabledObserver();
diff --git a/base/android/java/src/org/chromium/base/metrics/RecordHistogram.java b/base/android/java/src/org/chromium/base/metrics/RecordHistogram.java
index 40842bb..1f78fb9 100644
--- a/base/android/java/src/org/chromium/base/metrics/RecordHistogram.java
+++ b/base/android/java/src/org/chromium/base/metrics/RecordHistogram.java
@@ -6,76 +6,24 @@
 
 import android.text.format.DateUtils;
 
-import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 
 import org.chromium.base.annotations.JNINamespace;
 import org.chromium.base.annotations.MainDex;
 import org.chromium.base.annotations.NativeMethods;
 
-import javax.annotation.concurrent.GuardedBy;
-
 /** Java API for recording UMA histograms. */
 @JNINamespace("base::android")
 @MainDex
 public class RecordHistogram {
-    /** Underlying {@link UmaRecorder} used by methods in this class. */
-    private static final CachingUmaRecorder sRecorder = new CachingUmaRecorder();
-
     /**
-     * {@code null}, unless recording of histograms is currently disabled for testing. Exposed for
-     * use in peer classes {e.g. AnimationFrameTimeHistogram}.
-     * <p>
-     * Use {@link #setDisabledForTests(boolean)} to set this value.
-     * <p>
-     * TODO(bttk@chromium.org): Rename to sDisabledForTestBy
-     */
-    @VisibleForTesting
-    @Nullable
-    public static Throwable sDisabledBy;
-
-    /**
-     * The delegate disabled by {@link #setDisabledForTests(boolean)}.
-     */
-    @GuardedBy("sRecorder")
-    @Nullable
-    private static UmaRecorder sDisabledDelegateForTest;
-
-    /**
-     * Tests may not have native initialized, so they may need to disable metrics. The value should
-     * be reset after the test done, to avoid carrying over state to unrelated tests.
-     * <p>
-     * In JUnit tests this can be done automatically using
-     * {@link org.chromium.base.metrics.test.DisableHistogramsRule}.
+     * Tests may need to disable metrics. The value should be reset after the test done, to avoid
+     * carrying over state to unrelated tests. <p> In JUnit tests this can be done automatically
+     * using {@link org.chromium.base.metrics.test.DisableHistogramsRule}.
      */
     @VisibleForTesting
     public static void setDisabledForTests(boolean disabled) {
-        synchronized (sRecorder) {
-            if (disabled) {
-                if (sDisabledBy != null) {
-                    throw new IllegalStateException(
-                            "Histograms are already disabled.", sDisabledBy);
-                }
-                sDisabledBy = new Throwable();
-                sDisabledDelegateForTest = sRecorder.setDelegate(new NoopUmaRecorder());
-            } else {
-                sDisabledBy = null;
-                sRecorder.setDelegate(sDisabledDelegateForTest);
-                sDisabledDelegateForTest = null;
-            }
-        }
-    }
-
-    /** Starts forwarding metrics to the native code. Returns after the cache has been flushed. */
-    public static void onLibraryLoaded() {
-        synchronized (sRecorder) {
-            if (sDisabledBy == null) {
-                sRecorder.setDelegate(new NativeUmaRecorder());
-            } else {
-                // If metrics are disabled for test, use native when metrics get restored.
-                sDisabledDelegateForTest = new NativeUmaRecorder();
-            }
-        }
+        UmaRecorderHolder.setDisabledForTests(disabled);
     }
 
     /**
@@ -87,7 +35,7 @@
      * @param sample sample to be recorded, either true or false
      */
     public static void recordBooleanHistogram(String name, boolean sample) {
-        sRecorder.recordBooleanHistogram(name, sample);
+        UmaRecorderHolder.get().recordBooleanHistogram(name, sample);
     }
 
     /**
@@ -112,7 +60,7 @@
      * @param sample sample to be recorded, at least 1 and at most 999999
      */
     public static void recordCountHistogram(String name, int sample) {
-        sRecorder.recordExponentialHistogram(name, sample, 1, 1_000_000, 50);
+        UmaRecorderHolder.get().recordExponentialHistogram(name, sample, 1, 1_000_000, 50);
     }
 
     /**
@@ -123,7 +71,7 @@
      * @param sample sample to be recorded, at least 1 and at most 99
      */
     public static void recordCount100Histogram(String name, int sample) {
-        sRecorder.recordExponentialHistogram(name, sample, 1, 100, 50);
+        UmaRecorderHolder.get().recordExponentialHistogram(name, sample, 1, 100, 50);
     }
 
     /**
@@ -134,7 +82,7 @@
      * @param sample sample to be recorded, at least 1 and at most 999
      */
     public static void recordCount1000Histogram(String name, int sample) {
-        sRecorder.recordExponentialHistogram(name, sample, 1, 1_000, 50);
+        UmaRecorderHolder.get().recordExponentialHistogram(name, sample, 1, 1_000, 50);
     }
 
     /**
@@ -145,7 +93,7 @@
      * @param sample sample to be recorded, at least 1 and at most 99999
      */
     public static void recordCount100000Histogram(String name, int sample) {
-        sRecorder.recordExponentialHistogram(name, sample, 1, 100_000, 50);
+        UmaRecorderHolder.get().recordExponentialHistogram(name, sample, 1, 100_000, 50);
     }
 
     /**
@@ -161,7 +109,7 @@
      */
     public static void recordCustomCountHistogram(
             String name, int sample, int min, int max, int numBuckets) {
-        sRecorder.recordExponentialHistogram(name, sample, min, max, numBuckets);
+        UmaRecorderHolder.get().recordExponentialHistogram(name, sample, min, max, numBuckets);
     }
 
     /**
@@ -177,7 +125,7 @@
      */
     public static void recordLinearCountHistogram(
             String name, int sample, int min, int max, int numBuckets) {
-        sRecorder.recordLinearHistogram(name, sample, min, max, numBuckets);
+        UmaRecorderHolder.get().recordLinearHistogram(name, sample, min, max, numBuckets);
     }
 
     /**
@@ -200,7 +148,7 @@
      *               {@code <= 100} ideally, definitely {@code <= 1000}.
      */
     public static void recordSparseHistogram(String name, int sample) {
-        sRecorder.recordSparseHistogram(name, sample);
+        UmaRecorderHolder.get().recordSparseHistogram(name, sample);
     }
 
     /**
@@ -286,7 +234,7 @@
      * @param sizeInkB Sample to record in KB
      */
     public static void recordMemoryKBHistogram(String name, int sizeInKB) {
-        sRecorder.recordExponentialHistogram(name, sizeInKB, 1000, 500000, 50);
+        UmaRecorderHolder.get().recordExponentialHistogram(name, sizeInKB, 1000, 500000, 50);
     }
 
     /**
@@ -302,7 +250,7 @@
         final int min = 1;
         // One extra is added for the overflow bucket.
         final int numBuckets = max + 1;
-        sRecorder.recordLinearHistogram(name, sample, min, max, numBuckets);
+        UmaRecorderHolder.get().recordLinearHistogram(name, sample, min, max, numBuckets);
     }
 
     private static int clampToInt(long value) {
@@ -315,7 +263,7 @@
 
     private static void recordCustomTimesHistogramMilliseconds(
             String name, long duration, long min, long max, int numBuckets) {
-        sRecorder.recordExponentialHistogram(
+        UmaRecorderHolder.get().recordExponentialHistogram(
                 name, clampToInt(duration), clampToInt(min), clampToInt(max), numBuckets);
     }
 
diff --git a/base/android/java/src/org/chromium/base/metrics/UmaRecorderHolder.java b/base/android/java/src/org/chromium/base/metrics/UmaRecorderHolder.java
new file mode 100644
index 0000000..daaac27
--- /dev/null
+++ b/base/android/java/src/org/chromium/base/metrics/UmaRecorderHolder.java
@@ -0,0 +1,77 @@
+// 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.base.metrics;
+
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+
+import javax.annotation.concurrent.GuardedBy;
+
+/** Holds the {@link CachingUmaRecorder} used by {@link RecordHistogram}. */
+public class UmaRecorderHolder {
+    /** The instance held by this class. */
+    private static CachingUmaRecorder sRecorder = new CachingUmaRecorder();
+
+    /**
+     * {@code null}, unless recording is currently disabled for testing. Exposed for use in peer
+     * classes {e.g. AnimationFrameTimeHistogram}.
+     * <p>
+     * Use {@link #setDisabledForTests(boolean)} to set this value.
+     * <p>
+     * TODO(bttk@chromium.org): Fix dependency in AnimationFrameTimeHistogram, make this field
+     *     private and rename to sDisabledForTestBy
+     */
+    @VisibleForTesting
+    @Nullable
+    public static Throwable sDisabledBy;
+
+    /** Lock for {@code sDisabledDelegateForTest}. */
+    private static final Object sLock = new Object();
+
+    /** The delegate disabled by {@link #setDisabledForTests(boolean)}. */
+    @GuardedBy("sLock")
+    @Nullable
+    private static UmaRecorder sDisabledDelegateForTest;
+
+    /** Returns the {@link CachingUmaRecorder}. */
+    /* package */ static CachingUmaRecorder get() {
+        return sRecorder;
+    }
+
+    /** Starts forwarding metrics to the native code. Returns after the cache has been flushed. */
+    public static void onLibraryLoaded() {
+        synchronized (sLock) {
+            if (sDisabledBy == null) {
+                sRecorder.setDelegate(new NativeUmaRecorder());
+            } else {
+                // If metrics are disabled for test, use native when metrics get restored.
+                sDisabledDelegateForTest = new NativeUmaRecorder();
+            }
+        }
+    }
+
+    /**
+     * Tests may need to disable metrics. The value should be reset after the test done, to avoid
+     * carrying over state to unrelated tests. <p> In JUnit tests this can be done automatically
+     * using {@link org.chromium.base.metrics.test.DisableHistogramsRule}.
+     */
+    @VisibleForTesting
+    public static void setDisabledForTests(boolean disabled) {
+        synchronized (sLock) {
+            if (disabled) {
+                if (sDisabledBy != null) {
+                    throw new IllegalStateException(
+                            "Histograms are already disabled.", sDisabledBy);
+                }
+                sDisabledBy = new Throwable();
+                sDisabledDelegateForTest = sRecorder.setDelegate(new NoopUmaRecorder());
+            } else {
+                sDisabledBy = null;
+                sRecorder.setDelegate(sDisabledDelegateForTest);
+                sDisabledDelegateForTest = null;
+            }
+        }
+    }
+}
diff --git a/base/android/junit/src/org/chromium/base/metrics/test/DisableHistogramsRule.java b/base/android/junit/src/org/chromium/base/metrics/test/DisableHistogramsRule.java
index 5c82da20..de23281c 100644
--- a/base/android/junit/src/org/chromium/base/metrics/test/DisableHistogramsRule.java
+++ b/base/android/junit/src/org/chromium/base/metrics/test/DisableHistogramsRule.java
@@ -6,7 +6,7 @@
 
 import org.junit.rules.ExternalResource;
 
-import org.chromium.base.metrics.RecordHistogram;
+import org.chromium.base.metrics.UmaRecorderHolder;
 
 /**
  * Disables histogram recording for the duration of the tests.
@@ -14,11 +14,11 @@
 public class DisableHistogramsRule extends ExternalResource {
     @Override
     protected void before() {
-        RecordHistogram.setDisabledForTests(true);
+        UmaRecorderHolder.setDisabledForTests(true);
     }
 
     @Override
     protected void after() {
-        RecordHistogram.setDisabledForTests(false);
+        UmaRecorderHolder.setDisabledForTests(false);
     }
 }
diff --git a/base/test/android/javatests/src/org/chromium/base/test/DisableNativeTestRule.java b/base/test/android/javatests/src/org/chromium/base/test/DisableNativeTestRule.java
index 7875bc8d..6ccf21c 100644
--- a/base/test/android/javatests/src/org/chromium/base/test/DisableNativeTestRule.java
+++ b/base/test/android/javatests/src/org/chromium/base/test/DisableNativeTestRule.java
@@ -9,8 +9,8 @@
 import org.junit.runners.model.Statement;
 
 import org.chromium.base.Log;
-import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.metrics.RecordUserAction;
+import org.chromium.base.metrics.UmaRecorderHolder;
 
 /**
  * {@link TestRule} to disable native access for testing.
@@ -26,7 +26,7 @@
                 LoadNative loadNative = description.getAnnotation(LoadNative.class);
                 if (loadNative == null) {
                     Log.i(TAG, "Disable RecordHistogram and RecordUserAction for testing");
-                    RecordHistogram.setDisabledForTests(true);
+                    UmaRecorderHolder.setDisabledForTests(true);
                     RecordUserAction.setDisabledForTests(true);
                 } else {
                     Log.i(TAG, "Test will run with native libraries");
@@ -36,7 +36,7 @@
                 } finally {
                     if (loadNative == null) {
                         Log.i(TAG, "Re-enable RecordHistogram and RecordUserAction after test");
-                        RecordHistogram.setDisabledForTests(false);
+                        UmaRecorderHolder.setDisabledForTests(false);
                         RecordUserAction.setDisabledForTests(false);
                     }
                 }
diff --git a/build/android/lint/suppressions.xml b/build/android/lint/suppressions.xml
index d127bf8..8a5b90a1 100644
--- a/build/android/lint/suppressions.xml
+++ b/build/android/lint/suppressions.xml
@@ -424,11 +424,12 @@
     <ignore regexp="android_webview/test/shell/res/raw/resource_file.html"/>
     <ignore regexp="android_webview/test/shell/res/raw/resource_icon.png"/>
     <ignore regexp="android_webview/tools/automated_ui_tests/java/res/layout/"/>
-    <!-- TODO(crbug.com/1017190): Remove the following 6 suppressions once we lint entire app rather
+    <!-- TODO(crbug.com/1017190): Remove the following 7 suppressions once we lint entire app rather
          than each individual target -->
     <ignore regexp="components/browser_ui/strings/android/browser_ui_strings_grd"/>
     <ignore regexp="chrome/browser/ui/android/strings/ui_strings_grd"/>
     <ignore regexp="The resource `R.drawable.*_expand_.*` appears to be unused"/>
+    <ignore regexp="The resource `R.drawable.btn_close` appears to be unused"/>
     <ignore regexp="The resource `R.drawable.ic_more_vert_24dp_on_dark_bg` appears to be unused"/>
     <ignore regexp="The resource `R.layout.permission_dialog` appears to be unused"/>
     <ignore regexp="components/permissions/android/res/drawable"/>
diff --git a/build/config/android/internal_rules.gni b/build/config/android/internal_rules.gni
index 2a20a3e9..6c65ee0 100644
--- a/build/config/android/internal_rules.gni
+++ b/build/config/android/internal_rules.gni
@@ -652,7 +652,7 @@
           get_label_info(invoker.apk_target, "target_gen_dir") + "/" +
           get_label_info(invoker.apk_target, "name") + ".build_config"
       _rebased_apk_build_config = rebase_path(_apk_build_config, root_build_dir)
-      assert(_rebased_apk_build_config != "")  # Mark as used.
+      not_needed([ "_rebased_apk_build_config" ])
     } else if (_test_type == "gtest") {
       assert(
           defined(invoker.executable_dist_dir),
@@ -2797,6 +2797,7 @@
         # Override the default action_pool when goma is enabled.
         pool = "//build/config/android:goma_javac_pool"
       }
+
       # Flag enable_kythe_annotations requires
       # checkout_android_prebuilts_build_tools=True in .gclient.
       if (enable_kythe_annotations && !invoker.enable_errorprone) {
@@ -3388,7 +3389,7 @@
       }
       if (defined(invoker.android_manifest_for_lint)) {
         _android_manifest_for_lint = invoker.android_manifest_for_lint
-        assert(_android_manifest_for_lint != "")  # Mark as used.
+        not_needed([ "_android_manifest_for_lint" ])
       }
       if (_lint_enabled) {
         _android_lint_target = "${_main_target_name}__lint"
diff --git a/build/config/android/rules.gni b/build/config/android/rules.gni
index 3c644b6..f14a0e0 100644
--- a/build/config/android/rules.gni
+++ b/build/config/android/rules.gni
@@ -2174,7 +2174,7 @@
           process_file_template([ _final_apk_path ],
                                 "{{source_dir}}/{{source_name_part}}")
       _final_apk_path_no_ext = _final_apk_path_no_ext_list[0]
-      assert(_final_apk_path_no_ext != "")  # Mark as used.
+      not_needed([ "_final_apk_path_no_ext" ])
     }
 
     # Non-base bundle modules create only proto resources.
@@ -2255,8 +2255,10 @@
     _load_library_from_apk =
         defined(invoker.load_library_from_apk) && invoker.load_library_from_apk
 
-    assert(_use_chromium_linker || true)  # Mark as used.
-    assert(_use_modern_linker || true)  # Mark as used.
+    not_needed([
+                 "_use_chromium_linker",
+                 "_use_modern_linker",
+               ])
     assert(!_load_library_from_apk || _use_chromium_linker,
            "load_library_from_apk requires use_chromium_linker")
 
@@ -2295,7 +2297,6 @@
     }
 
     _secondary_abi_native_libs_deps = []
-    assert(_secondary_abi_native_libs_deps == [])  # mark as used.
 
     if (defined(invoker.secondary_abi_shared_libraries) &&
         invoker.secondary_abi_shared_libraries != []) {
diff --git a/cc/animation/animation.cc b/cc/animation/animation.cc
index 61b6ace8..dec58b94 100644
--- a/cc/animation/animation.cc
+++ b/cc/animation/animation.cc
@@ -261,13 +261,6 @@
   DispatchAndDelegateAnimationEvent(event);
 }
 
-void Animation::PromoteScrollTimelinePendingToActive() {
-  if (!animation_timeline_)
-    return;
-
-  animation_timeline_->ActivateTimeline();
-}
-
 void Animation::UpdateScrollTimeline(base::Optional<ElementId> scroller_id,
                                      base::Optional<double> start_scroll_offset,
                                      base::Optional<double> end_scroll_offset) {
diff --git a/cc/animation/animation.h b/cc/animation/animation.h
index 994df565..003215c 100644
--- a/cc/animation/animation.h
+++ b/cc/animation/animation.h
@@ -66,20 +66,15 @@
   void SetAnimationTimeline(AnimationTimeline* timeline);
 
   // TODO(yigu): There is a reverse dependency between AnimationTimeline and
-  // Animation. ScrollTimeline promotion and update should be handled by
-  // AnimationHost instead of Animation. https://crbug.com/1023508.
-  //
-  // Should be called when the pending tree is promoted to active, as this may
-  // require updating the ElementId for the ScrollTimeline scroll source.
-  virtual void PromoteScrollTimelinePendingToActive();
+  // Animation. ScrollTimeline update should be handled by AnimationHost instead
+  // of Animation. This could be fixed once the snapshotting in blink is
+  // implemented. https://crbug.com/1023508.
+
   // Should be called when the ScrollTimeline attached to this animation has a
   // change, such as when the scroll source changes ElementId.
-  // TODO(yigu): This is currently virtual because WorkletAnimation has a
-  // separate ScrollTimeline member and the update should be applied to it
-  // instead of the AnimationTimeline object in this class.
-  virtual void UpdateScrollTimeline(base::Optional<ElementId> scroller_id,
-                                    base::Optional<double> start_scroll_offset,
-                                    base::Optional<double> end_scroll_offset);
+  void UpdateScrollTimeline(base::Optional<ElementId> scroller_id,
+                            base::Optional<double> start_scroll_offset,
+                            base::Optional<double> end_scroll_offset);
 
   scoped_refptr<ElementAnimations> element_animations() const;
 
diff --git a/cc/animation/animation_host.cc b/cc/animation/animation_host.cc
index 9866529..75cc47a 100644
--- a/cc/animation/animation_host.cc
+++ b/cc/animation/animation_host.cc
@@ -484,8 +484,9 @@
 }
 
 void AnimationHost::PromoteScrollTimelinesPendingToActive() {
-  for (auto& animation : ticking_animations_) {
-    animation->PromoteScrollTimelinePendingToActive();
+  for (auto& kv : id_to_timeline_map_) {
+    auto& timeline = kv.second;
+    timeline->ActivateTimeline();
   }
 }
 
diff --git a/cc/animation/animation_host.h b/cc/animation/animation_host.h
index 59446c3b..28d9f7b 100644
--- a/cc/animation/animation_host.h
+++ b/cc/animation/animation_host.h
@@ -120,6 +120,8 @@
   bool UpdateAnimationState(bool start_ready_animations,
                             MutatorEvents* events) override;
   void TakeTimeUpdatedEvents(MutatorEvents* events) override;
+  // Should be called when the pending tree is promoted to active, as this may
+  // require updating the ElementId for the ScrollTimeline scroll source.
   void PromoteScrollTimelinesPendingToActive() override;
 
   std::unique_ptr<MutatorEvents> CreateEvents() override;
diff --git a/cc/animation/animation_host_unittest.cc b/cc/animation/animation_host_unittest.cc
index bcdec53..e6d00aa 100644
--- a/cc/animation/animation_host_unittest.cc
+++ b/cc/animation/animation_host_unittest.cc
@@ -37,7 +37,7 @@
     client_impl_.RegisterElementId(element_id_, ElementListType::ACTIVE);
 
     worklet_animation_ = WorkletAnimation::Create(
-        worklet_animation_id_, "test_name", 1, nullptr, nullptr, nullptr);
+        worklet_animation_id_, "test_name", 1, nullptr, nullptr);
     int cc_id = worklet_animation_->id();
     worklet_animation_->AttachElement(element_id_);
     host_->AddAnimationTimeline(timeline_);
@@ -339,9 +339,11 @@
   // Create a worklet animation that is bound to the scroll timeline.
   scoped_refptr<WorkletAnimation> worklet_animation(
       new WorkletAnimation(animation_id2, worklet_animation_id, "test_name", 1,
-                           std::move(scroll_timeline), nullptr, nullptr, true));
+                           nullptr, nullptr, true));
+  host_impl_->AddAnimationTimeline(scroll_timeline);
+  scroll_timeline->AttachAnimation(worklet_animation);
+
   worklet_animation->AttachElement(element_id);
-  timeline_->AttachAnimation(worklet_animation);
 
   AddOpacityTransitionToAnimation(worklet_animation.get(), 1, .7f, .3f, true);
 
diff --git a/cc/animation/animation_timeline.cc b/cc/animation/animation_timeline.cc
index 4f06245..766257ac 100644
--- a/cc/animation/animation_timeline.cc
+++ b/cc/animation/animation_timeline.cc
@@ -65,6 +65,15 @@
   return f == id_to_animation_map_.end() ? nullptr : f->second.get();
 }
 
+std::vector<Animation*> AnimationTimeline::GetAnimations() const {
+  std::vector<Animation*> animations;
+  animations.reserve(id_to_animation_map_.size());
+
+  for (auto& kv : id_to_animation_map_)
+    animations.push_back(kv.second.get());
+  return animations;
+}
+
 void AnimationTimeline::ClearAnimations() {
   for (auto& kv : id_to_animation_map_)
     EraseAnimation(kv.second);
diff --git a/cc/animation/animation_timeline.h b/cc/animation/animation_timeline.h
index 34f0cf7..9c266e1 100644
--- a/cc/animation/animation_timeline.h
+++ b/cc/animation/animation_timeline.h
@@ -24,7 +24,7 @@
     : public base::RefCounted<AnimationTimeline> {
  public:
   static scoped_refptr<AnimationTimeline> Create(int id);
-  scoped_refptr<AnimationTimeline> CreateImplInstance() const;
+  virtual scoped_refptr<AnimationTimeline> CreateImplInstance() const;
 
   AnimationTimeline(const AnimationTimeline&) = delete;
   AnimationTimeline& operator=(const AnimationTimeline&) = delete;
@@ -42,7 +42,9 @@
   void AttachAnimation(scoped_refptr<Animation> animation);
   void DetachAnimation(scoped_refptr<Animation> animation);
 
+  std::vector<Animation*> GetAnimations() const;
   void ClearAnimations();
+  bool HasAnimation() const { return !id_to_animation_map_.empty(); }
 
   virtual void PushPropertiesTo(AnimationTimeline* timeline_impl);
   virtual void ActivateTimeline() {}
@@ -58,6 +60,10 @@
   explicit AnimationTimeline(int id);
   virtual ~AnimationTimeline();
 
+  // A list of all animations which this timeline owns.
+  using IdToAnimationMap = std::unordered_map<int, scoped_refptr<Animation>>;
+  IdToAnimationMap id_to_animation_map_;
+
  private:
   friend class base::RefCounted<AnimationTimeline>;
 
@@ -68,10 +74,6 @@
 
   void EraseAnimation(scoped_refptr<Animation> animation);
 
-  // A list of all animations which this timeline owns.
-  using IdToAnimationMap = std::unordered_map<int, scoped_refptr<Animation>>;
-  IdToAnimationMap id_to_animation_map_;
-
   int id_;
   AnimationHost* animation_host_;
   bool needs_push_properties_;
diff --git a/cc/animation/scroll_timeline.cc b/cc/animation/scroll_timeline.cc
index e95489071..8471efb 100644
--- a/cc/animation/scroll_timeline.cc
+++ b/cc/animation/scroll_timeline.cc
@@ -5,6 +5,7 @@
 #include "cc/animation/scroll_timeline.h"
 
 #include "cc/animation/animation_id_provider.h"
+#include "cc/animation/worklet_animation.h"
 #include "cc/trees/property_tree.h"
 #include "cc/trees/scroll_node.h"
 #include "ui/gfx/geometry/scroll_offset.h"
@@ -31,8 +32,9 @@
                                base::Optional<double> start_scroll_offset,
                                base::Optional<double> end_scroll_offset,
                                double time_range,
-                               KeyframeModel::FillMode fill)
-    : AnimationTimeline(AnimationIdProvider::NextTimelineId()),
+                               KeyframeModel::FillMode fill,
+                               int animation_timeline_id)
+    : AnimationTimeline(animation_timeline_id),
       pending_id_(scroller_id),
       direction_(direction),
       start_scroll_offset_(start_scroll_offset),
@@ -49,15 +51,15 @@
     base::Optional<double> end_scroll_offset,
     double time_range,
     KeyframeModel::FillMode fill) {
-  return base::WrapRefCounted(
-      new ScrollTimeline(scroller_id, direction, start_scroll_offset,
-                         end_scroll_offset, time_range, fill));
+  return base::WrapRefCounted(new ScrollTimeline(
+      scroller_id, direction, start_scroll_offset, end_scroll_offset,
+      time_range, fill, AnimationIdProvider::NextTimelineId()));
 }
 
-scoped_refptr<ScrollTimeline> ScrollTimeline::CreateImplInstance() const {
+scoped_refptr<AnimationTimeline> ScrollTimeline::CreateImplInstance() const {
   return base::WrapRefCounted(
       new ScrollTimeline(pending_id_, direction_, start_scroll_offset_,
-                         end_scroll_offset_, time_range_, fill_));
+                         end_scroll_offset_, time_range_, fill_, id()));
 }
 
 bool ScrollTimeline::IsActive(const ScrollTree& scroll_tree,
@@ -153,6 +155,7 @@
 }
 
 void ScrollTimeline::PushPropertiesTo(AnimationTimeline* impl_timeline) {
+  AnimationTimeline::PushPropertiesTo(impl_timeline);
   DCHECK(impl_timeline);
   ScrollTimeline* scroll_timeline = ToScrollTimeline(impl_timeline);
   scroll_timeline->pending_id_ = pending_id_;
@@ -166,12 +169,23 @@
 
 void ScrollTimeline::ActivateTimeline() {
   active_id_ = pending_id_;
+  for (auto& kv : id_to_animation_map_) {
+    auto& animation = kv.second;
+    if (animation->IsWorkletAnimation())
+      ToWorkletAnimation(animation.get())->ReleasePendingTreeLock();
+  }
 }
 
 void ScrollTimeline::UpdateScrollerIdAndScrollOffsets(
     base::Optional<ElementId> pending_id,
     base::Optional<double> start_scroll_offset,
     base::Optional<double> end_scroll_offset) {
+  if (pending_id_ == pending_id &&
+      start_scroll_offset_ == start_scroll_offset &&
+      end_scroll_offset_ == end_scroll_offset) {
+    return;
+  }
+
   // When the scroller id changes it will first be modified in the pending tree.
   // Then later (when the pending tree is promoted to active)
   // |ActivateTimeline| will be called and will set the |active_id_|.
diff --git a/cc/animation/scroll_timeline.h b/cc/animation/scroll_timeline.h
index b8be066..9c07b06 100644
--- a/cc/animation/scroll_timeline.h
+++ b/cc/animation/scroll_timeline.h
@@ -39,7 +39,8 @@
                  base::Optional<double> start_scroll_offset,
                  base::Optional<double> end_scroll_offset,
                  double time_range,
-                 KeyframeModel::FillMode fill);
+                 KeyframeModel::FillMode fill,
+                 int animation_timeline_id);
 
   static scoped_refptr<ScrollTimeline> Create(
       base::Optional<ElementId> scroller_id,
@@ -51,7 +52,7 @@
 
   // Create a copy of this ScrollTimeline intended for the impl thread in the
   // compositor.
-  scoped_refptr<ScrollTimeline> CreateImplInstance() const;
+  scoped_refptr<AnimationTimeline> CreateImplInstance() const override;
 
   // ScrollTimeline is active if the scroll node exists in active or pending
   // scroll tree.
diff --git a/cc/animation/scroll_timeline_unittest.cc b/cc/animation/scroll_timeline_unittest.cc
index 998ea9f..934550e 100644
--- a/cc/animation/scroll_timeline_unittest.cc
+++ b/cc/animation/scroll_timeline_unittest.cc
@@ -183,8 +183,8 @@
   // Now create an impl version of the ScrollTimeline. Initially this should
   // only have a pending scroller id, as the active tree may not yet have the
   // scroller in it (as in this case).
-  scoped_refptr<ScrollTimeline> impl_timeline =
-      main_timeline->CreateImplInstance();
+  scoped_refptr<ScrollTimeline> impl_timeline = base::WrapRefCounted(
+      ToScrollTimeline(main_timeline->CreateImplInstance().get()));
 
   EXPECT_TRUE(std::isnan(
       ToDouble(impl_timeline->CurrentTime(active_tree.scroll_tree, true))));
diff --git a/cc/animation/worklet_animation.cc b/cc/animation/worklet_animation.cc
index 473dc1f4..54bb27f6 100644
--- a/cc/animation/worklet_animation.cc
+++ b/cc/animation/worklet_animation.cc
@@ -20,7 +20,6 @@
     WorkletAnimationId worklet_animation_id,
     const std::string& name,
     double playback_rate,
-    scoped_refptr<ScrollTimeline> scroll_timeline,
     std::unique_ptr<AnimationOptions> options,
     std::unique_ptr<AnimationEffectTimings> effect_timings,
     bool is_controlling_instance)
@@ -28,7 +27,6 @@
                        worklet_animation_id,
                        name,
                        playback_rate,
-                       std::move(scroll_timeline),
                        std::move(options),
                        std::move(effect_timings),
                        is_controlling_instance,
@@ -39,7 +37,6 @@
     WorkletAnimationId worklet_animation_id,
     const std::string& name,
     double playback_rate,
-    scoped_refptr<ScrollTimeline> scroll_timeline,
     std::unique_ptr<AnimationOptions> options,
     std::unique_ptr<AnimationEffectTimings> effect_timings,
     bool is_controlling_instance,
@@ -47,7 +44,6 @@
     : Animation(cc_animation_id, std::move(effect)),
       worklet_animation_id_(worklet_animation_id),
       name_(name),
-      scroll_timeline_(std::move(scroll_timeline)),
       playback_rate_(playback_rate),
       options_(std::move(options)),
       effect_timings_(std::move(effect_timings)),
@@ -65,32 +61,22 @@
     WorkletAnimationId worklet_animation_id,
     const std::string& name,
     double playback_rate,
-    scoped_refptr<ScrollTimeline> scroll_timeline,
     std::unique_ptr<AnimationOptions> options,
     std::unique_ptr<AnimationEffectTimings> effect_timings) {
   return WrapRefCounted(new WorkletAnimation(
       AnimationIdProvider::NextAnimationId(), worklet_animation_id, name,
-      playback_rate, std::move(scroll_timeline), std::move(options),
-      std::move(effect_timings), false));
+      playback_rate, std::move(options), std::move(effect_timings), false));
 }
 
 scoped_refptr<Animation> WorkletAnimation::CreateImplInstance() const {
-  scoped_refptr<ScrollTimeline> impl_timeline;
-  if (scroll_timeline_)
-    impl_timeline = scroll_timeline_->CreateImplInstance();
-
-  return WrapRefCounted(new WorkletAnimation(
-      id(), worklet_animation_id_, name(), playback_rate_,
-      std::move(impl_timeline), CloneOptions(), CloneEffectTimings(), true));
+  return WrapRefCounted(
+      new WorkletAnimation(id(), worklet_animation_id_, name(), playback_rate_,
+                           CloneOptions(), CloneEffectTimings(), true));
 }
 
 void WorkletAnimation::PushPropertiesTo(Animation* animation_impl) {
   Animation::PushPropertiesTo(animation_impl);
   WorkletAnimation* worklet_animation_impl = ToWorkletAnimation(animation_impl);
-  if (scroll_timeline_) {
-    scroll_timeline_->PushPropertiesTo(
-        worklet_animation_impl->scroll_timeline_.get());
-  }
   worklet_animation_impl->SetPlaybackRate(playback_rate_);
 }
 
@@ -138,7 +124,8 @@
   // To stay consistent with blink::WorkletAnimation, record start time only
   // when the timeline becomes active.
   if (!start_time_.has_value() && is_timeline_active)
-    start_time_ = scroll_timeline_ ? base::TimeTicks() : monotonic_time;
+    start_time_ = animation_timeline_->IsScrollTimeline() ? base::TimeTicks()
+                                                          : monotonic_time;
 
   if (is_active_tree && has_pending_tree_lock_)
     return;
@@ -171,7 +158,8 @@
 
   // Prevent active tree mutations from queuing up until pending tree is
   // activated to preserve flow of time for scroll timelines.
-  has_pending_tree_lock_ = !is_active_tree && scroll_timeline_;
+  has_pending_tree_lock_ =
+      !is_active_tree && animation_timeline_->IsScrollTimeline();
 
   switch (state_) {
     case State::PENDING:
@@ -236,9 +224,10 @@
     bool is_active_tree) {
   DCHECK(IsTimelineActive(scroll_tree, is_active_tree));
   base::TimeTicks timeline_time;
-  if (scroll_timeline_) {
+  if (animation_timeline_->IsScrollTimeline()) {
     base::Optional<base::TimeTicks> scroll_monotonic_time =
-        scroll_timeline_->CurrentTime(scroll_tree, is_active_tree);
+        ToScrollTimeline(animation_timeline_)
+            ->CurrentTime(scroll_tree, is_active_tree);
     if (!scroll_monotonic_time)
       return base::nullopt;
     timeline_time = scroll_monotonic_time.value();
@@ -267,21 +256,11 @@
 
 bool WorkletAnimation::IsTimelineActive(const ScrollTree& scroll_tree,
                                         bool is_active_tree) const {
-  return !scroll_timeline_ ||
-         scroll_timeline_->IsActive(scroll_tree, is_active_tree);
-}
+  if (!animation_timeline_->IsScrollTimeline())
+    return true;
 
-void WorkletAnimation::PromoteScrollTimelinePendingToActive() {
-  Animation::PromoteScrollTimelinePendingToActive();
-  ReleasePendingTreeLock();
-}
-
-void WorkletAnimation::UpdateScrollTimeline(
-    base::Optional<ElementId> scroller_id,
-    base::Optional<double> start_scroll_offset,
-    base::Optional<double> end_scroll_offset) {
-  scroll_timeline_->UpdateScrollerIdAndScrollOffsets(
-      scroller_id, start_scroll_offset, end_scroll_offset);
+  return ToScrollTimeline(animation_timeline_)
+      ->IsActive(scroll_tree, is_active_tree);
 }
 
 void WorkletAnimation::RemoveKeyframeModel(int keyframe_model_id) {
diff --git a/cc/animation/worklet_animation.h b/cc/animation/worklet_animation.h
index 63268956..46dd8b0 100644
--- a/cc/animation/worklet_animation.h
+++ b/cc/animation/worklet_animation.h
@@ -21,7 +21,6 @@
 
 class AnimationOptions;
 class AnimationEffectTimings;
-class ScrollTimeline;
 
 // A WorkletAnimation is an animation that allows its animation
 // timing to be controlled by an animator instance that is running in a
@@ -40,7 +39,6 @@
                    WorkletAnimationId worklet_animation_id,
                    const std::string& name,
                    double playback_rate,
-                   scoped_refptr<ScrollTimeline> scroll_timeline,
                    std::unique_ptr<AnimationOptions> options,
                    std::unique_ptr<AnimationEffectTimings> effect_timings,
                    bool is_controlling_instance);
@@ -48,16 +46,12 @@
       WorkletAnimationId worklet_animation_id,
       const std::string& name,
       double playback_rate,
-      scoped_refptr<ScrollTimeline> scroll_timeline,
       std::unique_ptr<AnimationOptions> options,
       std::unique_ptr<AnimationEffectTimings> effect_timings);
   scoped_refptr<Animation> CreateImplInstance() const override;
 
   WorkletAnimationId worklet_animation_id() { return worklet_animation_id_; }
   const std::string& name() const { return name_; }
-  const ScrollTimeline* scroll_timeline() const {
-    return scroll_timeline_.get();
-  }
 
   bool IsWorkletAnimation() const override;
 
@@ -75,11 +69,6 @@
 
   void PushPropertiesTo(Animation* animation_impl) override;
 
-  void PromoteScrollTimelinePendingToActive() override;
-  void UpdateScrollTimeline(base::Optional<ElementId> scroller_id,
-                            base::Optional<double> start_scroll_offset,
-                            base::Optional<double> end_scroll_offset) override;
-
   // Called by Blink WorkletAnimation when its playback rate is updated.
   void UpdatePlaybackRate(double playback_rate);
   void SetPlaybackRateForTesting(double playback_rate) {
@@ -96,7 +85,6 @@
                    WorkletAnimationId worklet_animation_id,
                    const std::string& name,
                    double playback_rate,
-                   scoped_refptr<ScrollTimeline> scroll_timeline,
                    std::unique_ptr<AnimationOptions> options,
                    std::unique_ptr<AnimationEffectTimings> effect_timings,
                    bool is_controlling_instance,
@@ -136,13 +124,6 @@
   WorkletAnimationId worklet_animation_id_;
   std::string name_;
 
-  // The ScrollTimeline associated with the underlying animation. If null, the
-  // animation is based on a DocumentTimeline.
-  // TODO(crbug.com/1023508): Remove scroll_timeline_. For scroll-linked
-  // animations, construct animation_timeline_ with ScrollTimeline directly via
-  // blink::Animation::AttachCompositorTimeline.
-  scoped_refptr<ScrollTimeline> scroll_timeline_;
-
   // Controls speed of the animation.
   // https://drafts.csswg.org/web-animations-2/#animation-effect-playback-rate
 
diff --git a/cc/animation/worklet_animation_unittest.cc b/cc/animation/worklet_animation_unittest.cc
index 361fa6ef..51d8c51 100644
--- a/cc/animation/worklet_animation_unittest.cc
+++ b/cc/animation/worklet_animation_unittest.cc
@@ -6,6 +6,7 @@
 
 #include <utility>
 #include "base/memory/ptr_util.h"
+#include "cc/animation/animation_id_provider.h"
 #include "cc/animation/keyframe_effect.h"
 #include "cc/animation/scroll_timeline.h"
 #include "cc/test/animation_test_common.h"
@@ -41,7 +42,7 @@
 
     worklet_animation_ = WrapRefCounted(
         new WorkletAnimation(1, worklet_animation_id_, "test_name", 1, nullptr,
-                             nullptr, nullptr, true /* controlling instance*/));
+                             nullptr, true /* controlling instance*/));
     worklet_animation_->AttachElement(element_id_);
     host_->AddAnimationTimeline(timeline_);
     timeline_->AttachAnimation(worklet_animation_);
@@ -59,7 +60,8 @@
                        base::nullopt,
                        base::nullopt,
                        0,
-                       KeyframeModel::FillMode::NONE) {}
+                       KeyframeModel::FillMode::NONE,
+                       AnimationIdProvider::NextTimelineId()) {}
   MOCK_CONST_METHOD2(CurrentTime,
                      base::Optional<base::TimeTicks>(const ScrollTree&, bool));
   MOCK_CONST_METHOD2(IsActive, bool(const ScrollTree&, bool));
@@ -75,7 +77,7 @@
 
   scoped_refptr<WorkletAnimation> worklet_animation =
       WrapRefCounted(new WorkletAnimation(
-          1, worklet_animation_id_, "test_name", 1, nullptr, nullptr, nullptr,
+          1, worklet_animation_id_, "test_name", 1, nullptr, nullptr,
           false /* not impl instance*/, std::move(effect)));
 
   EXPECT_CALL(*mock_effect, Tick(_)).Times(0);
@@ -173,9 +175,10 @@
   EXPECT_CALL(*scroll_timeline, CurrentTime(_, _))
       .WillRepeatedly(Return(
           (base::TimeTicks() + base::TimeDelta::FromMilliseconds(1234))));
-  scoped_refptr<WorkletAnimation> worklet_animation =
-      WorkletAnimation::Create(worklet_animation_id_, "test_name", 1,
-                               std::move(scroll_timeline), nullptr, nullptr);
+  scoped_refptr<WorkletAnimation> worklet_animation = WorkletAnimation::Create(
+      worklet_animation_id_, "test_name", 1, nullptr, nullptr);
+  host_->AddAnimationTimeline(scroll_timeline);
+  scroll_timeline->AttachAnimation(worklet_animation);
 
   ScrollTree scroll_tree;
   std::unique_ptr<MutatorInputState> state =
@@ -190,7 +193,11 @@
 TEST_F(WorkletAnimationTest,
        CurrentTimeFromRegularTimelineIsOffsetByStartTime) {
   scoped_refptr<WorkletAnimation> worklet_animation = WorkletAnimation::Create(
-      worklet_animation_id_, "test_name", 1, nullptr, nullptr, nullptr);
+      worklet_animation_id_, "test_name", 1, nullptr, nullptr);
+
+  worklet_animation->AttachElement(element_id_);
+  host_->AddAnimationTimeline(timeline_);
+  timeline_->AttachAnimation(worklet_animation);
 
   base::TimeTicks first_ticks =
       base::TimeTicks() + base::TimeDelta::FromMillisecondsD(111);
@@ -228,7 +235,11 @@
   const double playback_rate_half = 0.5;
   scoped_refptr<WorkletAnimation> worklet_animation =
       WorkletAnimation::Create(worklet_animation_id_, "test_name",
-                               playback_rate_double, nullptr, nullptr, nullptr);
+                               playback_rate_double, nullptr, nullptr);
+
+  worklet_animation->AttachElement(element_id_);
+  host_->AddAnimationTimeline(timeline_);
+  timeline_->AttachAnimation(worklet_animation);
 
   base::TimeTicks first_ticks =
       base::TimeTicks() + base::TimeDelta::FromMillisecondsD(111);
@@ -278,12 +289,14 @@
   const double playback_rate_half = 0.5;
   auto scroll_timeline = base::WrapRefCounted(new MockScrollTimeline());
 
-  scoped_refptr<WorkletAnimation> worklet_animation = WorkletAnimation::Create(
-      worklet_animation_id_, "test_name", playback_rate_double,
-      std::move(scroll_timeline), nullptr, nullptr);
+  scoped_refptr<WorkletAnimation> worklet_animation =
+      WorkletAnimation::Create(worklet_animation_id_, "test_name",
+                               playback_rate_double, nullptr, nullptr);
+  host_->AddAnimationTimeline(scroll_timeline);
+  scroll_timeline->AttachAnimation(worklet_animation);
   const MockScrollTimeline* mock_timeline =
       static_cast<const MockScrollTimeline*>(
-          worklet_animation->scroll_timeline());
+          worklet_animation->animation_timeline());
 
   ScrollTree scroll_tree;
   std::unique_ptr<MutatorInputState> state =
@@ -328,13 +341,15 @@
 TEST_F(WorkletAnimationTest, InactiveScrollTimeline) {
   auto scroll_timeline = base::WrapRefCounted(new MockScrollTimeline());
 
-  scoped_refptr<WorkletAnimation> worklet_animation = WorkletAnimation::Create(
-      worklet_animation_id_, "test_name", /*playback_rate*/ 1,
-      std::move(scroll_timeline), nullptr, nullptr);
+  scoped_refptr<WorkletAnimation> worklet_animation =
+      WorkletAnimation::Create(worklet_animation_id_, "test_name",
+                               /*playback_rate*/ 1, nullptr, nullptr);
 
+  host_->AddAnimationTimeline(scroll_timeline);
+  scroll_timeline->AttachAnimation(worklet_animation);
   const MockScrollTimeline* mock_timeline =
       static_cast<const MockScrollTimeline*>(
-          worklet_animation->scroll_timeline());
+          worklet_animation->animation_timeline());
   ScrollTree scroll_tree;
   std::unique_ptr<MutatorInputState> state =
       std::make_unique<MutatorInputState>();
@@ -497,9 +512,10 @@
   EXPECT_CALL(*scroll_timeline, IsActive(_, _)).WillRepeatedly(Return(true));
   EXPECT_CALL(*scroll_timeline, CurrentTime(_, _))
       .WillRepeatedly(Invoke(FakeIncreasingScrollTimelineTime));
-  scoped_refptr<WorkletAnimation> worklet_animation =
-      WorkletAnimation::Create(worklet_animation_id_, "test_name", 1,
-                               std::move(scroll_timeline), nullptr, nullptr);
+  scoped_refptr<WorkletAnimation> worklet_animation = WorkletAnimation::Create(
+      worklet_animation_id_, "test_name", 1, nullptr, nullptr);
+  host_->AddAnimationTimeline(scroll_timeline);
+  scroll_timeline->AttachAnimation(worklet_animation);
 
   ScrollTree scroll_tree;
   std::unique_ptr<MutatorInputState> state =
@@ -545,7 +561,9 @@
   EXPECT_EQ(scroll_timeline->GetPendingIdForTest(), ElementId());
 
   scoped_refptr<WorkletAnimation> worklet_animation = WorkletAnimation::Create(
-      worklet_animation_id_, "test_name", 1, scroll_timeline, nullptr, nullptr);
+      worklet_animation_id_, "test_name", 1, nullptr, nullptr);
+  host_->AddAnimationTimeline(scroll_timeline);
+  scroll_timeline->AttachAnimation(worklet_animation);
   ElementId scroller_id = ElementId(1);
   worklet_animation->UpdateScrollTimeline(scroller_id, base::nullopt,
                                           base::nullopt);
diff --git a/cc/input/input_handler.h b/cc/input/input_handler.h
index d3366ca4..40fc85d3 100644
--- a/cc/input/input_handler.h
+++ b/cc/input/input_handler.h
@@ -40,6 +40,15 @@
 
 enum PointerResultType { kUnhandled = 0, kScrollbarScroll };
 
+// These enum values are reported in UMA. So these values should never be
+// removed or changed.
+enum class ScrollBeginThreadState {
+  kScrollingOnCompositor = 0,
+  kScrollingOnCompositorBlockedOnMain = 1,
+  kScrollingOnMain = 2,
+  kMaxValue = kScrollingOnMain,
+};
+
 struct CC_EXPORT InputHandlerPointerResult {
   InputHandlerPointerResult();
   // Tells what type of processing occurred in the input handler as a result of
@@ -192,6 +201,12 @@
   // |should_snap| is true.
   virtual void ScrollEnd(bool should_snap) = 0;
 
+  // Called to notify every time scroll-begin/end is attempted by an input
+  // event.
+  virtual void RecordScrollBegin(ScrollInputType input_type,
+                                 ScrollBeginThreadState scroll_start_state) = 0;
+  virtual void RecordScrollEnd(ScrollInputType input_type) = 0;
+
   virtual InputHandlerPointerResult MouseMoveAt(
       const gfx::Point& mouse_position) = 0;
   // TODO(arakeri): Pass in the modifier instead of a bool once the refactor
diff --git a/cc/layers/painted_overlay_scrollbar_layer.h b/cc/layers/painted_overlay_scrollbar_layer.h
index 6b5e928..cdb192d 100644
--- a/cc/layers/painted_overlay_scrollbar_layer.h
+++ b/cc/layers/painted_overlay_scrollbar_layer.h
@@ -14,7 +14,9 @@
 namespace cc {
 
 // For composited overlay scrollbars with nine-patch thumb. For overlay
-// scrollbars whose thumb is not nine-patch, use PaintedScrollbarLayer.
+// scrollbars whose thumb is not nine-patch, use PaintedScrollbarLayer or
+// SolidColorScrollbarLayer. In practice, this is used for non-custom
+// overlay scrollbars on Win/Linux.
 class CC_EXPORT PaintedOverlayScrollbarLayer : public ScrollbarLayerBase {
  public:
   std::unique_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl) override;
diff --git a/cc/layers/painted_scrollbar_layer.h b/cc/layers/painted_scrollbar_layer.h
index 2e171c0..e910a00 100644
--- a/cc/layers/painted_scrollbar_layer.h
+++ b/cc/layers/painted_scrollbar_layer.h
@@ -13,6 +13,10 @@
 
 namespace cc {
 
+// Generic scrollbar layer for cases not covered by PaintedOverlayScrollbarLayer
+// or SolidColorScrollbarLayer. This is not used for CSS-styled scrollbars. In
+// practice, this is used for overlay and non-overlay scrollbars on MacOS, as
+// well as non-overlay scrollbars on Win/Linux.
 class CC_EXPORT PaintedScrollbarLayer : public ScrollbarLayerBase {
  public:
   std::unique_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl) override;
diff --git a/cc/layers/solid_color_scrollbar_layer.h b/cc/layers/solid_color_scrollbar_layer.h
index ff09a53..8931b9c9 100644
--- a/cc/layers/solid_color_scrollbar_layer.h
+++ b/cc/layers/solid_color_scrollbar_layer.h
@@ -11,6 +11,8 @@
 
 namespace cc {
 
+// A solid color scrollbar that can be fully drawn on the impl thread. In
+// practice, this is used for overlay scrollbars on Android.
 class CC_EXPORT SolidColorScrollbarLayer : public ScrollbarLayerBase {
  public:
   std::unique_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl) override;
diff --git a/cc/metrics/frame_sequence_tracker.cc b/cc/metrics/frame_sequence_tracker.cc
index d01869e..fcc15047 100644
--- a/cc/metrics/frame_sequence_tracker.cc
+++ b/cc/metrics/frame_sequence_tracker.cc
@@ -51,7 +51,9 @@
       return "Video";
     case FrameSequenceTrackerType::kWheelScroll:
       return "WheelScroll";
-    default:
+    case FrameSequenceTrackerType::kScrollbarScroll:
+      return "ScrollbarScroll";
+    case FrameSequenceTrackerType::kMaxType:
       return "";
   }
 }
@@ -62,7 +64,8 @@
 // sufficient number of frames.
 constexpr int kMinFramesForThroughputMetric = 100;
 
-constexpr int kBuiltinSequenceNum = FrameSequenceTrackerType::kMaxType + 1;
+constexpr int kBuiltinSequenceNum =
+    static_cast<int>(FrameSequenceTrackerType::kMaxType) + 1;
 constexpr int kMaximumHistogramIndex = 3 * kBuiltinSequenceNum;
 
 int GetIndexForMetric(FrameSequenceMetrics::ThreadType thread_type,
@@ -70,8 +73,8 @@
   if (thread_type == FrameSequenceMetrics::ThreadType::kMain)
     return static_cast<int>(type);
   if (thread_type == FrameSequenceMetrics::ThreadType::kCompositor)
-    return static_cast<int>(type + kBuiltinSequenceNum);
-  return static_cast<int>(type + 2 * kBuiltinSequenceNum);
+    return static_cast<int>(type) + kBuiltinSequenceNum;
+  return static_cast<int>(type) + 2 * kBuiltinSequenceNum;
 }
 
 std::string GetCheckerboardingHistogramName(FrameSequenceTrackerType type) {
@@ -144,6 +147,14 @@
     ReportMetrics();
 }
 
+void FrameSequenceMetrics::SetScrollingThread(ThreadType scrolling_thread) {
+  DCHECK(type_ == FrameSequenceTrackerType::kTouchScroll ||
+         type_ == FrameSequenceTrackerType::kWheelScroll ||
+         type_ == FrameSequenceTrackerType::kScrollbarScroll);
+  DCHECK_EQ(scrolling_thread_, ThreadType::kUnknown);
+  scrolling_thread_ = scrolling_thread;
+}
+
 void FrameSequenceMetrics::Merge(
     std::unique_ptr<FrameSequenceMetrics> metrics) {
   DCHECK_EQ(type_, metrics->type_);
@@ -186,6 +197,7 @@
       GetIndexForMetric(FrameSequenceMetrics::ThreadType::kMain, type_),
       main_throughput_);
 
+  // Report for the 'slower thread' for the metrics where it makes sense.
   bool should_report_slower_thread =
       IsInteractionType(type_) || type_ == FrameSequenceTrackerType::kUniversal;
   if (should_report_slower_thread) {
@@ -218,13 +230,53 @@
     }
   }
 
+  // Report for the 'scrolling thread' for the scrolling interactions.
+  if (scrolling_thread_ != ThreadType::kUnknown) {
+    base::Optional<int> scrolling_thread_throughput;
+    switch (scrolling_thread_) {
+      case ThreadType::kCompositor:
+        scrolling_thread_throughput = impl_throughput_percent;
+        break;
+      case ThreadType::kMain:
+        scrolling_thread_throughput = main_throughput_percent;
+        break;
+      case ThreadType::kSlower:
+      case ThreadType::kUnknown:
+        NOTREACHED();
+        break;
+    }
+    if (scrolling_thread_throughput.has_value()) {
+      // It's OK to use the UMA histogram in the following code while still
+      // using |GetThroughputHistogramName()| to get the name of the metric,
+      // since the input-params to the function never change at runtime.
+      if (type_ == FrameSequenceTrackerType::kWheelScroll) {
+        UMA_HISTOGRAM_PERCENTAGE(
+            GetThroughputHistogramName(FrameSequenceTrackerType::kWheelScroll,
+                                       "ScrollingThread"),
+            scrolling_thread_throughput.value());
+      } else if (type_ == FrameSequenceTrackerType::kTouchScroll) {
+        UMA_HISTOGRAM_PERCENTAGE(
+            GetThroughputHistogramName(FrameSequenceTrackerType::kTouchScroll,
+                                       "ScrollingThread"),
+            scrolling_thread_throughput.value());
+      } else {
+        DCHECK_EQ(type_, FrameSequenceTrackerType::kScrollbarScroll);
+        UMA_HISTOGRAM_PERCENTAGE(
+            GetThroughputHistogramName(
+                FrameSequenceTrackerType::kScrollbarScroll, "ScrollingThread"),
+            scrolling_thread_throughput.value());
+      }
+    }
+  }
+
   // Report the checkerboarding metrics.
   if (impl_throughput_.frames_expected >= kMinFramesForThroughputMetric) {
     const int checkerboarding_percent = static_cast<int>(
         100 * frames_checkerboarded_ / impl_throughput_.frames_expected);
     STATIC_HISTOGRAM_POINTER_GROUP(
-        GetCheckerboardingHistogramName(type_), type_,
-        FrameSequenceTrackerType::kMaxType, Add(checkerboarding_percent),
+        GetCheckerboardingHistogramName(type_), static_cast<int>(type_),
+        static_cast<int>(FrameSequenceTrackerType::kMaxType),
+        Add(checkerboarding_percent),
         base::LinearHistogram::FactoryGet(
             GetCheckerboardingHistogramName(type_), 1, 100, 101,
             base::HistogramBase::kUmaTargetedHistogramFlag));
@@ -253,18 +305,19 @@
   removal_trackers_.clear();
 }
 
-void FrameSequenceTrackerCollection::StartSequence(
+FrameSequenceMetrics* FrameSequenceTrackerCollection::StartSequence(
     FrameSequenceTrackerType type) {
   if (is_single_threaded_)
-    return;
+    return nullptr;
   if (frame_trackers_.contains(type))
-    return;
+    return frame_trackers_[type]->metrics();
   auto tracker = base::WrapUnique(
       new FrameSequenceTracker(type, throughput_ukm_reporter_.get()));
   frame_trackers_[type] = std::move(tracker);
 
   if (compositor_frame_reporting_controller_)
     compositor_frame_reporting_controller_->AddActiveTracker(type);
+  return frame_trackers_[type]->metrics();
 }
 
 void FrameSequenceTrackerCollection::StopSequence(
@@ -965,8 +1018,10 @@
   DCHECK_LT(sequence_type, FrameSequenceTrackerType::kMaxType);
 
   STATIC_HISTOGRAM_POINTER_GROUP(
-      GetFrameSequenceLengthHistogramName(sequence_type), sequence_type,
-      FrameSequenceTrackerType::kMaxType, Add(data.frames_expected),
+      GetFrameSequenceLengthHistogramName(sequence_type),
+      static_cast<int>(sequence_type),
+      static_cast<int>(FrameSequenceTrackerType::kMaxType),
+      Add(data.frames_expected),
       base::Histogram::FactoryGet(
           GetFrameSequenceLengthHistogramName(sequence_type), 1, 1000, 50,
           base::HistogramBase::kUmaTargetedHistogramFlag));
diff --git a/cc/metrics/frame_sequence_tracker.h b/cc/metrics/frame_sequence_tracker.h
index c8ba7410..7828201f 100644
--- a/cc/metrics/frame_sequence_tracker.h
+++ b/cc/metrics/frame_sequence_tracker.h
@@ -36,7 +36,7 @@
 class ThroughputUkmReporter;
 class UkmManager;
 
-enum FrameSequenceTrackerType {
+enum class FrameSequenceTrackerType {
   // Used as an enum for metrics. DO NOT reorder or delete values. Rather,
   // add them at the end and increment kMaxType.
   kCompositorAnimation = 0,
@@ -47,6 +47,7 @@
   kUniversal = 5,
   kVideo = 6,
   kWheelScroll = 7,
+  kScrollbarScroll = 8,
   kMaxType
 };
 
@@ -61,11 +62,7 @@
   FrameSequenceMetrics(const FrameSequenceMetrics&) = delete;
   FrameSequenceMetrics& operator=(const FrameSequenceMetrics&) = delete;
 
-  enum class ThreadType {
-    kMain,
-    kCompositor,
-    kSlower,
-  };
+  enum class ThreadType { kMain, kCompositor, kSlower, kUnknown };
 
   struct ThroughputData {
     static std::unique_ptr<base::trace_event::TracedValue> ToTracedValue(
@@ -108,6 +105,8 @@
 #endif
   };
 
+  void SetScrollingThread(ThreadType thread);
+
   void Merge(std::unique_ptr<FrameSequenceMetrics> metrics);
   bool HasEnoughDataForReporting() const;
   bool HasDataLeftForReporting() const;
@@ -130,6 +129,8 @@
   ThroughputData impl_throughput_;
   ThroughputData main_throughput_;
 
+  ThreadType scrolling_thread_ = ThreadType::kUnknown;
+
   // Tracks the number of produced frames that had some amount of
   // checkerboarding, and how many frames showed such checkerboarded frames.
   uint32_t frames_checkerboarded_ = 0;
@@ -150,7 +151,7 @@
       const FrameSequenceTrackerCollection&) = delete;
 
   // Creates a tracker for the specified sequence-type.
-  void StartSequence(FrameSequenceTrackerType type);
+  FrameSequenceMetrics* StartSequence(FrameSequenceTrackerType type);
 
   // Schedules |tracker| for destruction. This is preferred instead of outright
   // desrtruction of the tracker, since this ensures that the actual tracker
diff --git a/cc/metrics/frame_sequence_tracker_unittest.cc b/cc/metrics/frame_sequence_tracker_unittest.cc
index 7d8a4c28..f9ba723 100644
--- a/cc/metrics/frame_sequence_tracker_unittest.cc
+++ b/cc/metrics/frame_sequence_tracker_unittest.cc
@@ -113,7 +113,7 @@
     collection_.StartSequence(FrameSequenceTrackerType::kUniversal);
     EXPECT_EQ(collection_.frame_trackers_.size(), 4u);
 
-    collection_.StopSequence(kCompositorAnimation);
+    collection_.StopSequence(FrameSequenceTrackerType::kCompositorAnimation);
     EXPECT_EQ(collection_.frame_trackers_.size(), 3u);
     EXPECT_TRUE(collection_.frame_trackers_.contains(
         FrameSequenceTrackerType::kMainThreadAnimation));
@@ -637,6 +637,48 @@
               testing::ElementsAre(base::Bucket(1, 1)));
 }
 
+TEST_F(FrameSequenceTrackerTest, ScrollingThreadMetricCompositorThread) {
+  tracker_->metrics()->SetScrollingThread(
+      FrameSequenceMetrics::ThreadType::kCompositor);
+
+  // Start with a bunch of frames so that the metric does get reported at the
+  // end of the test.
+  ImplThroughput().frames_expected = 100u;
+  ImplThroughput().frames_produced = 100u;
+  MainThroughput().frames_expected = 100u;
+  MainThroughput().frames_produced = 90u;
+
+  base::HistogramTester histogram_tester;
+  ReportMetrics();
+
+  const char metric[] =
+      "Graphics.Smoothness.PercentDroppedFrames.ScrollingThread.TouchScroll";
+  histogram_tester.ExpectTotalCount(metric, 1u);
+  EXPECT_THAT(histogram_tester.GetAllSamples(metric),
+              testing::ElementsAre(base::Bucket(0, 1)));
+}
+
+TEST_F(FrameSequenceTrackerTest, ScrollingThreadMetricMainThread) {
+  tracker_->metrics()->SetScrollingThread(
+      FrameSequenceMetrics::ThreadType::kMain);
+
+  // Start with a bunch of frames so that the metric does get reported at the
+  // end of the test.
+  ImplThroughput().frames_expected = 100u;
+  ImplThroughput().frames_produced = 100u;
+  MainThroughput().frames_expected = 100u;
+  MainThroughput().frames_produced = 90u;
+
+  base::HistogramTester histogram_tester;
+  ReportMetrics();
+
+  const char metric[] =
+      "Graphics.Smoothness.PercentDroppedFrames.ScrollingThread.TouchScroll";
+  histogram_tester.ExpectTotalCount(metric, 1u);
+  EXPECT_THAT(histogram_tester.GetAllSamples(metric),
+              testing::ElementsAre(base::Bucket(10, 1)));
+}
+
 TEST_F(FrameSequenceTrackerTest, SimpleSequenceOneFrame) {
   const char sequence[] = "b(1)B(0,1)s(1)S(1)e(1,0)P(1)";
   GenerateSequence(sequence);
diff --git a/cc/trees/damage_tracker.cc b/cc/trees/damage_tracker.cc
index 341d027c..ec33bf0 100644
--- a/cc/trees/damage_tracker.cc
+++ b/cc/trees/damage_tracker.cc
@@ -332,26 +332,6 @@
   return damage;
 }
 
-void DamageTracker::ExpandDamageInsideRectWithFilters(
-    const gfx::Rect& pre_filter_rect,
-    const FilterOperations& filters) {
-  gfx::Rect damage_rect;
-  bool is_valid_rect = damage_for_this_update_.GetAsRect(&damage_rect);
-  // If the damage accumulated so far isn't a valid rect or empty, then there is
-  // no point in trying to make it bigger.
-  if (!is_valid_rect || damage_rect.IsEmpty())
-    return;
-
-  // Compute the pixels in the backdrop of the surface that could be affected
-  // by the damage in the content below.
-  gfx::Rect expanded_damage_rect = filters.MapRect(damage_rect, SkMatrix::I());
-
-  // Restrict it to the rectangle in which the backdrop filter is shown.
-  expanded_damage_rect.Intersect(pre_filter_rect);
-
-  damage_for_this_update_.Union(expanded_damage_rect);
-}
-
 void DamageTracker::AccumulateDamageFromLayer(LayerImpl* layer) {
   // There are two ways that a layer can damage a region of the target surface:
   //   1. Property change (e.g. opacity, position, transforms):
@@ -470,24 +450,15 @@
       const gfx::Transform& draw_transform = render_surface->draw_transform();
       gfx::Rect damage_rect_in_target_space = MathUtil::MapEnclosingClippedRect(
           draw_transform, damage_rect_in_local_space);
+      damage_rect_in_target_space.Intersect(
+          gfx::ToEnclosingRect(render_surface->DrawableContentRect()));
       damage_for_this_update_.Union(damage_rect_in_target_space);
     } else if (!is_valid_rect) {
       damage_for_this_update_.Union(surface_rect_in_target_space);
     }
   }
 
-  // If the layer has a backdrop filter, this may cause pixels in our surface
-  // to be expanded, so we will need to expand any damage at or below this
-  // layer. We expand the damage from this layer too, as we need to readback
-  // those pixels from the surface with only the contents of layers below this
-  // one in them. This means we need to redraw any pixels in the surface being
-  // used for the blur in this layer this frame.
   const FilterOperations& backdrop_filters = render_surface->BackdropFilters();
-  if (backdrop_filters.HasFilterThatMovesPixels()) {
-    ExpandDamageInsideRectWithFilters(surface_rect_in_target_space,
-                                      backdrop_filters);
-  }
-
   if (!surface_is_new &&
       backdrop_filters.HasFilterOfType(FilterOperation::BLUR)) {
     gfx::Rect damage_on_target;
diff --git a/cc/trees/damage_tracker_unittest.cc b/cc/trees/damage_tracker_unittest.cc
index 86d46336..cc784ab 100644
--- a/cc/trees/damage_tracker_unittest.cc
+++ b/cc/trees/damage_tracker_unittest.cc
@@ -1072,148 +1072,6 @@
   EXPECT_EQ(expected_child_damage_rect, child_damage_rect);
 }
 
-TEST_F(DamageTrackerTest, VerifyDamageForBackdropBlurredChild) {
-  LayerImpl* root = CreateAndSetUpTestTreeWithTwoSurfaces();
-
-  // Allow us to set damage on child1_ too.
-  child1_->SetDrawsContent(true);
-
-  FilterOperations filters;
-  filters.Append(FilterOperation::CreateBlurFilter(2.f));
-
-  // Setting the filter will damage the whole surface.
-  ClearDamageForAllSurfaces(root);
-  SetBackdropFilter(child1_, filters);
-  child1_->NoteLayerPropertyChanged();
-  EmulateDrawingOneFrame(root);
-
-  // CASE 1: Setting the update rect should cause the corresponding damage to
-  //         the surface, blurred based on the size of the child's backdrop
-  //         blur filter. Note that child1_'s render surface has a size of
-  //         206x208 due to contributions from grand_child1_ and grand_child2_.
-  ClearDamageForAllSurfaces(root);
-  root->UnionUpdateRect(gfx::Rect(297, 297, 2, 2));
-  EmulateDrawingOneFrame(root);
-
-  gfx::Rect root_damage_rect;
-  EXPECT_TRUE(GetRenderSurface(root)->damage_tracker()->GetDamageRectIfValid(
-      &root_damage_rect));
-  // Damage position on the surface should be a composition of the damage on
-  // the root and on child2_.  Damage on the root should be: position of
-  // update_rect (297, 297), but expanded by the blur outsets.
-  gfx::Rect expected_damage_rect = gfx::Rect(297, 297, 2, 2);
-
-  // 6px spread for a 2px blur.
-  expected_damage_rect.Inset(-6, -6, -6, -6);
-  EXPECT_EQ(expected_damage_rect.ToString(), root_damage_rect.ToString());
-
-  // CASE 2: Setting the update rect should cause the corresponding damage to
-  //         the surface, blurred based on the size of the child's backdrop
-  //         blur filter. Since the damage extends to the right/bottom outside
-  //         of the blurred layer, only the left/top should end up expanded.
-  ClearDamageForAllSurfaces(root);
-  root->UnionUpdateRect(gfx::Rect(297, 297, 30, 30));
-  EmulateDrawingOneFrame(root);
-
-  EXPECT_TRUE(GetRenderSurface(root)->damage_tracker()->GetDamageRectIfValid(
-      &root_damage_rect));
-  // Damage position on the surface should be a composition of the damage on
-  // the root and on child2_.  Damage on the root should be: position of
-  // update_rect (297, 297), but expanded on the left/top by the blur outsets.
-  expected_damage_rect = gfx::Rect(297, 297, 30, 30);
-
-  // 6px spread for a 2px blur.
-  expected_damage_rect.Inset(-6, -6, 0, 0);
-  EXPECT_EQ(expected_damage_rect.ToString(), root_damage_rect.ToString());
-
-  // CASE 3: Setting this update rect outside the blurred content_bounds of the
-  //         blurred child1_ will not cause it to be expanded.
-  ClearDamageForAllSurfaces(root);
-  root->UnionUpdateRect(gfx::Rect(30, 30, 2, 2));
-  EmulateDrawingOneFrame(root);
-
-  EXPECT_TRUE(GetRenderSurface(root)->damage_tracker()->GetDamageRectIfValid(
-      &root_damage_rect));
-  // Damage on the root should be: position of update_rect (30, 30), not
-  // expanded.
-  expected_damage_rect = gfx::Rect(30, 30, 2, 2);
-
-  EXPECT_EQ(expected_damage_rect.ToString(), root_damage_rect.ToString());
-
-  // CASE 4: Setting this update rect inside the blurred content_bounds but
-  //         outside the original content_bounds of the blurred child1_ will
-  //         cause it to be expanded.
-  ClearDamageForAllSurfaces(root);
-  root->UnionUpdateRect(gfx::Rect(99, 99, 1, 1));
-  EmulateDrawingOneFrame(root);
-
-  EXPECT_TRUE(GetRenderSurface(root)->damage_tracker()->GetDamageRectIfValid(
-      &root_damage_rect));
-  // Damage on the root should be: the originally damaged rect (99,99 1x1)
-  // plus the rect that can influence with a 2px blur (93,93 13x13) intersected
-  // with the surface rect (100,100 206x208). So no additional damage occurs
-  // above or to the left, but there is additional damage within the blurred
-  // area.
-  expected_damage_rect = gfx::Rect(99, 99, 7, 7);
-  EXPECT_EQ(expected_damage_rect.ToString(), root_damage_rect.ToString());
-
-  // CASE 5: Setting the update rect on child2_, which is above child1_, will
-  // not get blurred by child1_, so it does not need to get expanded.
-  ClearDamageForAllSurfaces(root);
-  child2_->UnionUpdateRect(gfx::Rect(1, 1));
-  EmulateDrawingOneFrame(root);
-
-  EXPECT_TRUE(GetRenderSurface(root)->damage_tracker()->GetDamageRectIfValid(
-      &root_damage_rect));
-  // Damage on child2_ should be: position of update_rect offset by the child's
-  // position (11, 11), and not expanded by anything.
-  expected_damage_rect = gfx::Rect(11, 11, 1, 1);
-
-  EXPECT_EQ(expected_damage_rect.ToString(), root_damage_rect.ToString());
-
-  // CASE 6: Setting the update rect on child1_ will also blur the damage, so
-  //         that any pixels needed for the blur are redrawn in the current
-  //         frame.
-  ClearDamageForAllSurfaces(root);
-  child1_->UnionUpdateRect(gfx::Rect(1, 1));
-  EmulateDrawingOneFrame(root);
-
-  EXPECT_TRUE(GetRenderSurface(root)->damage_tracker()->GetDamageRectIfValid(
-      &root_damage_rect));
-  // Damage on child1_ should be: position of update_rect offset by the child's
-  // position (100, 100), and expanded by the damage.
-
-  // Damage should be (0,0 1x1), offset by the 100,100 offset of child1_ in
-  // root, and expanded 6px for the 2px blur (i.e., 94,94 13x13), but there
-  // should be no damage outside child1_ (i.e. none above or to the left of
-  // 100,100.
-  expected_damage_rect = gfx::Rect(100, 100, 7, 7);
-  EXPECT_EQ(expected_damage_rect.ToString(), root_damage_rect.ToString());
-
-  // CASE 7: No changes, so should not damage the surface.
-  ClearDamageForAllSurfaces(root);
-  // We want to make sure that the backdrop filter doesn't cause empty damage
-  // to get expanded. We position child1_ so that an expansion of the empty rect
-  // would have non-empty intersection with child1_ in its target space (root
-  // space).
-  SetPostTranslation(child1_, gfx::Vector2dF());
-  child1_->NoteLayerPropertyChanged();
-  // The first call clears the damage caused by the movement.
-  EmulateDrawingOneFrame(root);
-  ClearDamageForAllSurfaces(root);
-  EmulateDrawingOneFrame(root);
-
-  gfx::Rect child_damage_rect;
-  EXPECT_TRUE(GetRenderSurface(root)->damage_tracker()->GetDamageRectIfValid(
-      &root_damage_rect));
-  EXPECT_TRUE(GetRenderSurface(child1_)->damage_tracker()->GetDamageRectIfValid(
-      &child_damage_rect));
-
-  // Should not be expanded by the blur filter.
-  EXPECT_EQ(gfx::Rect(), root_damage_rect);
-  EXPECT_EQ(gfx::Rect(), child_damage_rect);
-}
-
 TEST_F(DamageTrackerTest, VerifyDamageForAddingAndRemovingLayer) {
   LayerImpl* root = CreateAndSetUpTestTreeWithOneSurface();
   LayerImpl* child1 = child_layers_[0];
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc
index 25d99438..3ce66da 100644
--- a/cc/trees/layer_tree_host_impl.cc
+++ b/cc/trees/layer_tree_host_impl.cc
@@ -138,6 +138,20 @@
 // kHitTestAsk after the threshold is reached.
 const size_t kAssumeOverlapThreshold = 100;
 
+FrameSequenceTrackerType GetTrackerTypeForScroll(ScrollInputType input_type) {
+  switch (input_type) {
+    case ScrollInputType::kWheel:
+      return FrameSequenceTrackerType::kWheelScroll;
+    case ScrollInputType::kTouchscreen:
+      return FrameSequenceTrackerType::kTouchScroll;
+    case ScrollInputType::kScrollbar:
+      return FrameSequenceTrackerType::kScrollbarScroll;
+    case ScrollInputType::kAutoscroll:
+    case ScrollInputType::kUnknown:
+      return FrameSequenceTrackerType::kMaxType;
+  }
+}
+
 bool HasFixedPageScale(LayerTreeImpl* active_tree) {
   return active_tree->min_page_scale_factor() ==
          active_tree->max_page_scale_factor();
@@ -4537,9 +4551,6 @@
   last_latched_scroller_ = CurrentlyScrollingNode()->element_id;
   latched_scroll_type_ = type;
 
-  frame_trackers_.StartSequence(latched_scroll_type_ == ScrollInputType::kWheel
-                                    ? FrameSequenceTrackerType::kWheelScroll
-                                    : FrameSequenceTrackerType::kTouchScroll);
   client_->RenewTreePriority();
   RecordCompositorSlowScrollMetric(type, CC_THREAD);
 
@@ -4937,9 +4948,6 @@
   DCHECK(latched_scroll_type_.has_value());
 
   browser_controls_offset_manager_->ScrollEnd();
-  frame_trackers_.StopSequence(latched_scroll_type_ == ScrollInputType::kWheel
-                                   ? FrameSequenceTrackerType::kWheelScroll
-                                   : FrameSequenceTrackerType::kTouchScroll);
 
   ClearCurrentlyScrollingNode();
   deferred_scroll_end_ = false;
@@ -4947,6 +4955,39 @@
   client_->SetNeedsCommitOnImplThread();
 }
 
+void LayerTreeHostImpl::RecordScrollBegin(
+    ScrollInputType input_type,
+    ScrollBeginThreadState scroll_start_state) {
+  auto tracker_type = GetTrackerTypeForScroll(input_type);
+  DCHECK_NE(tracker_type, FrameSequenceTrackerType::kMaxType);
+  auto* metrics = frame_trackers_.StartSequence(tracker_type);
+  if (!metrics)
+    return;
+
+  // The main-thread is the 'scrolling thread' if:
+  //   (1) the scroll is driven by the main thread, or
+  //   (2) the scroll is driven by the compositor, but blocked on the main
+  //       thread.
+  // Otherwise, the compositor-thread is the 'scrolling thread'.
+  // TODO(crbug.com/1060712): We should also count 'main thread' as the
+  // 'scrolling thread' if the layer being scrolled has scroll-event handlers.
+  FrameSequenceMetrics::ThreadType scrolling_thread;
+  switch (scroll_start_state) {
+    case ScrollBeginThreadState::kScrollingOnCompositor:
+      scrolling_thread = FrameSequenceMetrics::ThreadType::kCompositor;
+      break;
+    case ScrollBeginThreadState::kScrollingOnMain:
+    case ScrollBeginThreadState::kScrollingOnCompositorBlockedOnMain:
+      scrolling_thread = FrameSequenceMetrics::ThreadType::kMain;
+      break;
+  }
+  metrics->SetScrollingThread(scrolling_thread);
+}
+
+void LayerTreeHostImpl::RecordScrollEnd(ScrollInputType input_type) {
+  frame_trackers_.StopSequence(GetTrackerTypeForScroll(input_type));
+}
+
 InputHandlerPointerResult LayerTreeHostImpl::MouseDown(
     const gfx::PointF& viewport_point,
     bool shift_modifier) {
diff --git a/cc/trees/layer_tree_host_impl.h b/cc/trees/layer_tree_host_impl.h
index 4489ebb6..eaeeec4e 100644
--- a/cc/trees/layer_tree_host_impl.h
+++ b/cc/trees/layer_tree_host_impl.h
@@ -272,6 +272,9 @@
   void SetSynchronousInputHandlerRootScrollOffset(
       const gfx::ScrollOffset& root_content_offset) override;
   void ScrollEnd(bool should_snap = false) override;
+  void RecordScrollBegin(ScrollInputType input_type,
+                         ScrollBeginThreadState scroll_start_state) override;
+  void RecordScrollEnd(ScrollInputType input_type) override;
 
   InputHandlerPointerResult MouseDown(const gfx::PointF& viewport_point,
                                       bool shift_modifier) override;
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index 81a7600..d12cfb40 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -219,19 +219,11 @@
   }
 }
 
-# This prevents a cyclic dependency for features depending on the compositor.
-# TODO(crbug.com/846440): Move this to features/compositor when ready.
-android_library("chrome_public_java") {
-  sources = [ "//chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorViewResizer.java" ]
-  deps = [ "//third_party/android_deps:androidx_annotation_annotation_java" ]
-}
-
 android_library("chrome_java") {
   deps = [
     ":chrome_app_java_resources",
     ":chrome_public_android_manifest",
     ":chrome_public_apk_template_resources",
-    ":chrome_public_java",
     ":chrome_version_constants",
     ":partner_location_descriptor_proto_java",
     ":update_proto_java",
@@ -339,6 +331,7 @@
     "//components/signin/core/browser/android:java",
     "//components/signin/public/android:java",
     "//components/spellcheck/browser/android:java",
+    "//components/subresource_filter/android:java",
     "//components/sync/android:sync_java",
     "//components/sync/protocol:protocol_java",
     "//components/url_formatter/android:url_formatter_java",
@@ -2057,6 +2050,7 @@
     "//chrome/android/webapk/libs/runtime_library:runtime_library_javatests",
     "//chrome/android/webapk/shell_apk:shell_apk_javatests",
     "//chrome/browser/download/android:download_java_tests",
+    "//chrome/browser/flags:javatests",
     "//chrome/browser/subresource_filter:subresource_filter_javatests",
     "//chrome/browser/touch_to_fill/android:test_java",
     "//chrome/browser/ui/android/appmenu/internal:javatests",
@@ -2810,6 +2804,7 @@
     "java/src/org/chromium/chrome/browser/prerender/ExternalPrerenderHandler.java",
     "java/src/org/chromium/chrome/browser/previews/PreviewsAndroidBridge.java",
     "java/src/org/chromium/chrome/browser/printing/TabPrinter.java",
+    "java/src/org/chromium/chrome/browser/privacy/settings/PrivacyPreferencesManager.java",
     "java/src/org/chromium/chrome/browser/provider/ChromeBrowserProvider.java",
     "java/src/org/chromium/chrome/browser/push_messaging/PushMessagingServiceObserver.java",
     "java/src/org/chromium/chrome/browser/rappor/RapporServiceBridge.java",
@@ -2827,7 +2822,6 @@
     "java/src/org/chromium/chrome/browser/send_tab_to_self/TargetDeviceInfo.java",
     "java/src/org/chromium/chrome/browser/settings/password/PasswordEditingBridge.java",
     "java/src/org/chromium/chrome/browser/settings/password/PasswordUIView.java",
-    "java/src/org/chromium/chrome/browser/settings/privacy/PrivacyPreferencesManager.java",
     "java/src/org/chromium/chrome/browser/sharing/SharingJNIBridge.java",
     "java/src/org/chromium/chrome/browser/sharing/SharingServiceProxy.java",
     "java/src/org/chromium/chrome/browser/sharing/click_to_call/ClickToCallMessageHandler.java",
diff --git a/chrome/android/DEPS b/chrome/android/DEPS
index 3bd08745..901adc4f 100644
--- a/chrome/android/DEPS
+++ b/chrome/android/DEPS
@@ -24,6 +24,7 @@
   "+components/signin/core/browser/android",
   "+components/signin/public/android",
   "+components/spellcheck/browser",
+  "+components/subresource_filter/android",
   "+components/viz/common/java",
   "+jni",
   "+media/base/android/java",
diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni
index 3cae2a1..d14a008 100644
--- a/chrome/android/chrome_java_sources.gni
+++ b/chrome/android/chrome_java_sources.gni
@@ -66,6 +66,8 @@
   "java/src/org/chromium/chrome/browser/about_settings/AboutSettingsBridge.java",
   "java/src/org/chromium/chrome/browser/about_settings/LegalInformationSettings.java",
   "java/src/org/chromium/chrome/browser/accessibility/FontSizePrefs.java",
+  "java/src/org/chromium/chrome/browser/accessibility/settings/AccessibilitySettings.java",
+  "java/src/org/chromium/chrome/browser/accessibility/settings/TextScalePreference.java",
   "java/src/org/chromium/chrome/browser/accessibility_tab_switcher/AccessibilityTabModelAdapter.java",
   "java/src/org/chromium/chrome/browser/accessibility_tab_switcher/AccessibilityTabModelListItem.java",
   "java/src/org/chromium/chrome/browser/accessibility_tab_switcher/AccessibilityTabModelListView.java",
@@ -664,6 +666,11 @@
   "java/src/org/chromium/chrome/browser/download/items/OfflineContentAggregatorNotificationBridgeUiFactory.java",
   "java/src/org/chromium/chrome/browser/download/service/DownloadBackgroundTask.java",
   "java/src/org/chromium/chrome/browser/download/service/DownloadTaskScheduler.java",
+  "java/src/org/chromium/chrome/browser/download/settings/DownloadDirectoryAdapter.java",
+  "java/src/org/chromium/chrome/browser/download/settings/DownloadLocationPreference.java",
+  "java/src/org/chromium/chrome/browser/download/settings/DownloadLocationPreferenceAdapter.java",
+  "java/src/org/chromium/chrome/browser/download/settings/DownloadLocationPreferenceDialog.java",
+  "java/src/org/chromium/chrome/browser/download/settings/DownloadSettings.java",
   "java/src/org/chromium/chrome/browser/engagement/SiteEngagementService.java",
   "java/src/org/chromium/chrome/browser/explore_sites/CategoryCardAdapter.java",
   "java/src/org/chromium/chrome/browser/explore_sites/CategoryCardViewHolderFactory.java",
@@ -829,12 +836,9 @@
   "java/src/org/chromium/chrome/browser/infobar/IPHBubbleDelegateImpl.java",
   "java/src/org/chromium/chrome/browser/infobar/IPHInfoBarSupport.java",
   "java/src/org/chromium/chrome/browser/infobar/InfoBar.java",
-  "java/src/org/chromium/chrome/browser/infobar/InfoBarCompactLayout.java",
   "java/src/org/chromium/chrome/browser/infobar/InfoBarContainer.java",
   "java/src/org/chromium/chrome/browser/infobar/InfoBarContainerLayout.java",
   "java/src/org/chromium/chrome/browser/infobar/InfoBarContainerView.java",
-  "java/src/org/chromium/chrome/browser/infobar/InfoBarInteractionHandler.java",
-  "java/src/org/chromium/chrome/browser/infobar/InfoBarLayout.java",
   "java/src/org/chromium/chrome/browser/infobar/InfoBarWrapper.java",
   "java/src/org/chromium/chrome/browser/infobar/InstallableAmbientBadgeInfoBar.java",
   "java/src/org/chromium/chrome/browser/infobar/InstantAppsInfoBar.java",
@@ -1339,6 +1343,10 @@
   "java/src/org/chromium/chrome/browser/previews/PreviewsUma.java",
   "java/src/org/chromium/chrome/browser/printing/PrintShareActivity.java",
   "java/src/org/chromium/chrome/browser/printing/TabPrinter.java",
+  "java/src/org/chromium/chrome/browser/privacy/settings/BandwidthType.java",
+  "java/src/org/chromium/chrome/browser/privacy/settings/DoNotTrackSettings.java",
+  "java/src/org/chromium/chrome/browser/privacy/settings/PrivacyPreferencesManager.java",
+  "java/src/org/chromium/chrome/browser/privacy/settings/PrivacySettings.java",
   "java/src/org/chromium/chrome/browser/profile_card/NavigationDelegateImpl.java",
   "java/src/org/chromium/chrome/browser/provider/BaseColumns.java",
   "java/src/org/chromium/chrome/browser/provider/BookmarkColumns.java",
@@ -1387,13 +1395,6 @@
   "java/src/org/chromium/chrome/browser/settings/MainSettings.java",
   "java/src/org/chromium/chrome/browser/settings/SettingsActivity.java",
   "java/src/org/chromium/chrome/browser/settings/SettingsLauncher.java",
-  "java/src/org/chromium/chrome/browser/settings/accessibility/AccessibilitySettings.java",
-  "java/src/org/chromium/chrome/browser/settings/accessibility/TextScalePreference.java",
-  "java/src/org/chromium/chrome/browser/settings/download/DownloadDirectoryAdapter.java",
-  "java/src/org/chromium/chrome/browser/settings/download/DownloadLocationPreference.java",
-  "java/src/org/chromium/chrome/browser/settings/download/DownloadLocationPreferenceAdapter.java",
-  "java/src/org/chromium/chrome/browser/settings/download/DownloadLocationPreferenceDialog.java",
-  "java/src/org/chromium/chrome/browser/settings/download/DownloadSettings.java",
   "java/src/org/chromium/chrome/browser/settings/homepage/HomepageEditor.java",
   "java/src/org/chromium/chrome/browser/settings/homepage/HomepageMetricsEnums.java",
   "java/src/org/chromium/chrome/browser/settings/homepage/HomepageSettings.java",
@@ -1419,10 +1420,7 @@
   "java/src/org/chromium/chrome/browser/settings/password/SavedPasswordEntry.java",
   "java/src/org/chromium/chrome/browser/settings/password/SingleThreadBarrierClosure.java",
   "java/src/org/chromium/chrome/browser/settings/password/TimedCallbackDelayer.java",
-  "java/src/org/chromium/chrome/browser/settings/privacy/BandwidthType.java",
-  "java/src/org/chromium/chrome/browser/settings/privacy/DoNotTrackSettings.java",
   "java/src/org/chromium/chrome/browser/settings/privacy/PrivacyPreferencesManager.java",
-  "java/src/org/chromium/chrome/browser/settings/privacy/PrivacySettings.java",
   "java/src/org/chromium/chrome/browser/share/LensUtils.java",
   "java/src/org/chromium/chrome/browser/share/OptionalShareTargetsManager.java",
   "java/src/org/chromium/chrome/browser/share/ShareActivity.java",
diff --git a/chrome/android/chrome_junit_test_java_sources.gni b/chrome/android/chrome_junit_test_java_sources.gni
index a43a95d0..2063f299 100644
--- a/chrome/android/chrome_junit_test_java_sources.gni
+++ b/chrome/android/chrome_junit_test_java_sources.gni
@@ -172,6 +172,7 @@
   "junit/src/org/chromium/chrome/browser/payments/AutofillContactTest.java",
   "junit/src/org/chromium/chrome/browser/payments/AutofillContactUnitTest.java",
   "junit/src/org/chromium/chrome/browser/preferences/PrefServiceBridgeTest.java",
+  "junit/src/org/chromium/chrome/browser/privacy/settings/PrivacyPreferencesManagerTest.java",
   "junit/src/org/chromium/chrome/browser/search_engines/SearchEngineChoiceMetricsTest.java",
   "junit/src/org/chromium/chrome/browser/search_engines/SearchEngineChoiceNotificationTest.java",
   "junit/src/org/chromium/chrome/browser/search_engines/settings/SearchEngineAdapterTest.java",
@@ -185,7 +186,6 @@
   "junit/src/org/chromium/chrome/browser/settings/password/ReauthenticationManagerTest.java",
   "junit/src/org/chromium/chrome/browser/settings/password/SingleThreadBarrierClosureTest.java",
   "junit/src/org/chromium/chrome/browser/settings/password/TimedCallbackDelayerTest.java",
-  "junit/src/org/chromium/chrome/browser/settings/privacy/PrivacyPreferencesManagerTest.java",
   "junit/src/org/chromium/chrome/browser/sharing/click_to_call/ClickToCallMessageHandlerTest.java",
   "junit/src/org/chromium/chrome/browser/signin/ConfirmImportSyncDataDialogTest.java",
   "junit/src/org/chromium/chrome/browser/signin/ConfirmManagedSyncDataDialogTest.java",
diff --git a/chrome/android/chrome_public_apk_tmpl.gni b/chrome/android/chrome_public_apk_tmpl.gni
index 0d0de5ba..3e947a731 100644
--- a/chrome/android/chrome_public_apk_tmpl.gni
+++ b/chrome/android/chrome_public_apk_tmpl.gni
@@ -73,8 +73,10 @@
   _is_trichrome = defined(invoker.is_trichrome) && invoker.is_trichrome
   _is_64_bit_browser =
       defined(invoker.is_64_bit_browser) && invoker.is_64_bit_browser
-  assert(_is_modern || !_is_modern)  # Mark as used.
-  assert(_is_64_bit_browser || !_is_64_bit_browser)  # Mark as used.
+  not_needed([
+               "_is_modern",
+               "_is_64_bit_browser",
+             ])
   assert(!(_is_monochrome && _is_trichrome),
          "Cannot be both trichrome and monochrome!")
   assert(_is_trichrome == defined(invoker.static_library_provider),
diff --git a/chrome/android/chrome_test_java_sources.gni b/chrome/android/chrome_test_java_sources.gni
index 2c43352..381620b 100644
--- a/chrome/android/chrome_test_java_sources.gni
+++ b/chrome/android/chrome_test_java_sources.gni
@@ -47,6 +47,7 @@
   "javatests/src/org/chromium/chrome/browser/VideoFullscreenOrientationLockChromeTest.java",
   "javatests/src/org/chromium/chrome/browser/WarmupManagerTest.java",
   "javatests/src/org/chromium/chrome/browser/accessibility/FontSizePrefsTest.java",
+  "javatests/src/org/chromium/chrome/browser/accessibility/settings/AccessibilitySettingsTest.java",
   "javatests/src/org/chromium/chrome/browser/accessibility_tab_switcher/OverviewListLayoutTest.java",
   "javatests/src/org/chromium/chrome/browser/appmenu/DataSaverAppMenuTest.java",
   "javatests/src/org/chromium/chrome/browser/appmenu/OverviewAppMenuTest.java",
@@ -407,6 +408,7 @@
   "javatests/src/org/chromium/chrome/browser/portals/PortalsTest.java",
   "javatests/src/org/chromium/chrome/browser/prerender/ExternalPrerenderHandlerTest.java",
   "javatests/src/org/chromium/chrome/browser/printing/PrintingControllerTest.java",
+  "javatests/src/org/chromium/chrome/browser/privacy/settings/PrivacyPreferencesManagerNativeTest.java",
   "javatests/src/org/chromium/chrome/browser/provider/ProviderBookmarkNodeUnitTest.java",
   "javatests/src/org/chromium/chrome/browser/provider/ProviderBookmarksUriTest.java",
   "javatests/src/org/chromium/chrome/browser/provider/ProviderSearchesUriTest.java",
@@ -419,11 +421,9 @@
   "javatests/src/org/chromium/chrome/browser/services/GoogleServicesManagerIntegrationTest.java",
   "javatests/src/org/chromium/chrome/browser/settings/PasswordViewingTypeTest.java",
   "javatests/src/org/chromium/chrome/browser/settings/SettingsActivityTest.java",
-  "javatests/src/org/chromium/chrome/browser/settings/accessibility/AccessibilitySettingsTest.java",
   "javatests/src/org/chromium/chrome/browser/settings/homepage/HomepageSettingsFragmentTest.java",
   "javatests/src/org/chromium/chrome/browser/settings/homepage/HomepageSettingsFragmentWithEditorTest.java",
   "javatests/src/org/chromium/chrome/browser/settings/password/PasswordSettingsTest.java",
-  "javatests/src/org/chromium/chrome/browser/settings/privacy/PrivacyPreferencesManagerNativeTest.java",
   "javatests/src/org/chromium/chrome/browser/shape_detection/ShapeDetectionTest.java",
   "javatests/src/org/chromium/chrome/browser/share/LensUtilsTest.java",
   "javatests/src/org/chromium/chrome/browser/share/ShareButtonControllerTest.java",
diff --git a/chrome/android/features/autofill_assistant/BUILD.gn b/chrome/android/features/autofill_assistant/BUILD.gn
index 09235e6..4f4bf65 100644
--- a/chrome/android/features/autofill_assistant/BUILD.gn
+++ b/chrome/android/features/autofill_assistant/BUILD.gn
@@ -33,7 +33,6 @@
     "//base:base_java",
     "//base:jni_java",
     "//chrome/android:chrome_java",
-    "//chrome/android:chrome_public_java",
     "//chrome/android/public/profiles:java",
     "//chrome/browser/image_fetcher:java",
     "//chrome/browser/ui/messages/android:java",
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantBottomBarCoordinator.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantBottomBarCoordinator.java
index 9e0cb8f..35b93b6 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantBottomBarCoordinator.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantBottomBarCoordinator.java
@@ -15,7 +15,6 @@
 import android.widget.LinearLayout;
 import android.widget.ScrollView;
 
-import org.chromium.base.ObserverList;
 import org.chromium.base.supplier.ObservableSupplierImpl;
 import org.chromium.chrome.autofill_assistant.R;
 import org.chromium.chrome.browser.ChromeActivity;
@@ -30,7 +29,6 @@
 import org.chromium.chrome.browser.autofill_assistant.infobox.AssistantInfoBoxCoordinator;
 import org.chromium.chrome.browser.autofill_assistant.user_data.AssistantCollectUserDataCoordinator;
 import org.chromium.chrome.browser.autofill_assistant.user_data.AssistantCollectUserDataModel;
-import org.chromium.chrome.browser.compositor.CompositorViewResizer;
 import org.chromium.chrome.browser.ui.TabObscuringHandler;
 import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetContent;
 import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetController;
@@ -41,8 +39,7 @@
 /**
  * Coordinator responsible for the Autofill Assistant bottom bar.
  */
-class AssistantBottomBarCoordinator
-        implements CompositorViewResizer, AssistantPeekHeightCoordinator.Delegate {
+class AssistantBottomBarCoordinator implements AssistantPeekHeightCoordinator.Delegate {
     private static final int FADE_OUT_TRANSITION_TIME_MS = 150;
     private static final int FADE_IN_TRANSITION_TIME_MS = 150;
     private static final int CHANGE_BOUNDS_TRANSITION_TIME_MS = 250;
@@ -75,12 +72,8 @@
                     .addTransition(new ChangeBounds().setDuration(CHANGE_BOUNDS_TRANSITION_TIME_MS))
                     .addTransition(new Fade(Fade.IN).setDuration(FADE_IN_TRANSITION_TIME_MS));
 
-    private final ObserverList<CompositorViewResizer.Observer> mLayoutViewportSizeObservers =
-            new ObserverList<>();
     @AssistantViewportMode
     private int mViewportMode = AssistantViewportMode.NO_RESIZE;
-    private int mLastLayoutViewportResizing;
-    private int mLastVisualViewportResizing;
 
     AssistantBottomBarCoordinator(ChromeActivity activity, AssistantModel model,
             BottomSheetController controller, TabObscuringHandler tabObscuringHandler) {
@@ -178,7 +171,6 @@
             public void onSheetContentChanged(@Nullable BottomSheetContent newContent) {
                 // TODO(crbug.com/806868): Make sure this works and does not interfere with Duet
                 // once we are in ChromeTabbedActivity.
-                updateLayoutViewportHeight();
                 updateVisualViewportHeight();
             }
 
@@ -295,7 +287,6 @@
 
         mViewportMode = mode;
         updateVisualViewportHeight();
-        updateLayoutViewportHeight();
     }
 
     /** Set the peek mode. */
@@ -321,7 +312,7 @@
 
     @Override
     public void onPeekHeightChanged() {
-        updateLayoutViewportHeight();
+        updateVisualViewportHeight();
     }
 
     private void setChildMarginTop(View child, int marginTop) {
@@ -360,25 +351,8 @@
         view.setLayoutParams(layoutParams);
     }
 
-    private void updateLayoutViewportHeight() {
-        setLayoutViewportResizing(getHeight());
-    }
-
-    /**
-     * Shrink the layout viewport by {@code resizing} pixels. This is an expensive operation that
-     * should be used with care.
-     */
-    private void setLayoutViewportResizing(int resizing) {
-        if (resizing == mLastLayoutViewportResizing) return;
-        mLastLayoutViewportResizing = resizing;
-
-        for (Observer observer : mLayoutViewportSizeObservers) {
-            observer.onHeightChanged(resizing);
-        }
-    }
-
     private void updateVisualViewportHeight() {
-        if (mViewportMode != AssistantViewportMode.RESIZE_VISUAL_VIEWPORT
+        if (mViewportMode == AssistantViewportMode.NO_RESIZE
                 || mBottomSheetController.getCurrentSheetContent() != mContent) {
             resetVisualViewportHeight();
             return;
@@ -393,39 +367,24 @@
     }
 
     /**
-     * Shrink the visual viewport by {@code resizing} pixels. This operation is cheaper than calling
-     * {@link #setLayoutViewportResizing} and can therefore be often called (e.g. during
-     * animations).
+     * Shrink the visual viewport by {@code resizing} pixels.
      */
     private void setVisualViewportResizing(int resizing) {
-        if (resizing == mLastVisualViewportResizing || mWebContents == null
+        int currentInset = mInsetSupplier.get() != null ? mInsetSupplier.get() : 0;
+        if (resizing == currentInset || mWebContents == null
                 || mWebContents.getRenderWidgetHostView() == null) {
             return;
         }
 
-        mLastVisualViewportResizing = resizing;
         mInsetSupplier.set(resizing);
     }
 
-    // Implementation of methods from AutofillAssistantSizeManager.
-
-    @Override
-    public int getHeight() {
-        if (mViewportMode == AssistantViewportMode.RESIZE_LAYOUT_VIEWPORT
-                && mBottomSheetController.getCurrentSheetContent() == mContent) {
+    /** @return The peeking height of the bottom sheet if our content is showing, otherwise 0. */
+    private int getHeight() {
+        if (mBottomSheetController.getCurrentSheetContent() == mContent) {
             return mPeekHeightCoordinator.getPeekHeight();
         }
 
         return 0;
     }
-
-    @Override
-    public void addObserver(Observer observer) {
-        mLayoutViewportSizeObservers.addObserver(observer);
-    }
-
-    @Override
-    public void removeObserver(Observer observer) {
-        mLayoutViewportSizeObservers.removeObserver(observer);
-    }
 }
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantCoordinator.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantCoordinator.java
index 481ac6c8..176e589 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantCoordinator.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantCoordinator.java
@@ -49,16 +49,11 @@
         mKeyboardCoordinator =
                 new AssistantKeyboardCoordinator(activity, mModel, keyboardCoordinatorDelegate);
 
-        activity.getCompositorViewHolder().addCompositorViewResizer(mBottomBarCoordinator);
         mModel.setVisible(true);
     }
 
     /** Detaches and destroys the view. */
     public void destroy() {
-        if (mActivity.getCompositorViewHolder() != null) {
-            mActivity.getCompositorViewHolder().removeCompositorViewResizer(mBottomBarCoordinator);
-        }
-
         mModel.setVisible(false);
         mOverlayCoordinator.destroy();
         mBottomBarCoordinator.destroy();
diff --git a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantBottomsheetTest.java b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantBottomsheetTest.java
index 463c3db..ee17064 100644
--- a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantBottomsheetTest.java
+++ b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantBottomsheetTest.java
@@ -196,7 +196,9 @@
 
         waitUntilViewMatchesCondition(withText("Focus element"), isCompletelyDisplayed());
         onView(withText("Focus element")).perform(click());
-        checkElementIsCoveredByBottomsheet("bottom", true);
+        // The viewport should be resized so that the bottom element is not covered by the bottom
+        // sheet.
+        checkElementIsCoveredByBottomsheet("bottom", false);
         onView(withId(R.id.swipe_indicator)).perform(swipeDownToMinimize());
         // Minimizing the bottomsheet should completely uncover the bottom element.
         waitUntilViewMatchesCondition(withText("Hello world!"), not(isDisplayed()));
diff --git a/chrome/android/features/cablev2_authenticator/internal/java/src/org/chromium/chrome/browser/webauth/authenticator/BLEHandler.java b/chrome/android/features/cablev2_authenticator/internal/java/src/org/chromium/chrome/browser/webauth/authenticator/BLEHandler.java
index afe66fe..25a0488 100644
--- a/chrome/android/features/cablev2_authenticator/internal/java/src/org/chromium/chrome/browser/webauth/authenticator/BLEHandler.java
+++ b/chrome/android/features/cablev2_authenticator/internal/java/src/org/chromium/chrome/browser/webauth/authenticator/BLEHandler.java
@@ -43,7 +43,7 @@
 class BLEHandler extends BluetoothGattServerCallback implements Closeable {
     // TODO: remove @TargetApi once 21 is the minimum, Clank-wide.
 
-    private static final String TAG = "AuthenticatorBLEHandler";
+    private static final String TAG = "CableBLEHandler";
     // These UUIDs are taken from the FIDO spec[1], save for the caBLE UUID
     // which is allocated to Google. See
     // https://fidoalliance.org/specs/fido-v2.0-ps-20190130/fido-client-to-authenticator-protocol-v2.0-ps-20190130.html#ble
diff --git a/chrome/android/features/cablev2_authenticator/internal/java/src/org/chromium/chrome/browser/webauth/authenticator/CameraView.java b/chrome/android/features/cablev2_authenticator/internal/java/src/org/chromium/chrome/browser/webauth/authenticator/CameraView.java
index 26b3a9a..191bd5f 100644
--- a/chrome/android/features/cablev2_authenticator/internal/java/src/org/chromium/chrome/browser/webauth/authenticator/CameraView.java
+++ b/chrome/android/features/cablev2_authenticator/internal/java/src/org/chromium/chrome/browser/webauth/authenticator/CameraView.java
@@ -6,28 +6,64 @@
 
 import android.content.Context;
 import android.hardware.Camera;
+import android.view.Display;
+import android.view.Surface;
 import android.view.SurfaceHolder;
 import android.view.SurfaceView;
 
 import org.chromium.base.Log;
+import org.chromium.base.ThreadUtils;
+import org.chromium.base.task.PostTask;
+import org.chromium.base.task.TaskTraits;
+import org.chromium.content_public.browser.UiThreadTaskTraits;
 
 import java.io.IOException;
 
 /**
  * Provides a SurfaceView and adapts it for use as a camera preview target
  * so that the current camera image can be displayed.
+ *
+ * TODO: locking and unlocking the screen seems to stop the camera preview because, on unlock,
+ * multiple of these Views end up getting created and only one wins the race to the camera.
  */
 class CameraView extends SurfaceView implements SurfaceHolder.Callback {
     private static final String TAG = "CameraView";
     private final Camera.PreviewCallback mCallback;
-    private Camera mCamera;
+    private final Display mDisplay;
 
-    public CameraView(Context context, Camera.PreviewCallback callback) {
+    /**
+     * Holds a reference to the camera. Only referenced from the UI thread.
+     */
+    private Camera mCamera;
+    /**
+     * Contains the number of degrees that the image from the selected camera
+     * will be rotated. Only referenced from the UI thread.
+     */
+    private int mCameraRotation;
+    /**
+     * True if a background thread is trying to open the camera. Only referenced from the UI thread.
+     */
+    private boolean mAmOpeningCamera;
+    /**
+      True if this View is currently detached. If this occurs while the camera is being opened
+      then it needs to immediately be closed again. Only referenced from the UI thread.
+    */
+    private boolean mDetached;
+    private SurfaceHolder mHolder;
+
+    public CameraView(Context context, Display display, Camera.PreviewCallback callback) {
         super(context);
         mCallback = callback;
+        mDisplay = display;
     }
 
+    /**
+     * Called to indicate that the callback that was passed to the constructor has finished
+     * processing and thus is free to receive another camera frame.
+     */
     public void rearmCallback() {
+        ThreadUtils.assertOnUiThread();
+
         if (mCamera != null) {
             mCamera.setOneShotPreviewCallback(mCallback);
         }
@@ -35,16 +71,19 @@
 
     @Override
     protected void onAttachedToWindow() {
+        ThreadUtils.assertOnUiThread();
+
         super.onAttachedToWindow();
+        mDetached = false;
         getHolder().addCallback(this);
-        // TODO: Camera.open is slow and shouldn't be called on the UI
-        // thread.
-        mCamera = Camera.open();
     }
 
     @Override
     protected void onDetachedFromWindow() {
+        ThreadUtils.assertOnUiThread();
+
         super.onDetachedFromWindow();
+        mDetached = true;
         getHolder().removeCallback(this);
         if (mCamera != null) {
             mCamera.release();
@@ -52,47 +91,143 @@
         }
     }
 
-    private void startCamera() {
+    private void openCamera() {
+        // We want to find the first, rear-facing camera. This is what
+        // Camera.open() gives us, but then we don't get the camera ID and we
+        // need that to get the rotation amount. Thus the need to iterate
+        // over the cameras to find the right one.
+        final int numCameras = Camera.getNumberOfCameras();
+        if (numCameras == 0) {
+            // TODO: indicate in UI when QR scanning fails.
+            return;
+        }
+
+        Camera.CameraInfo info = new Camera.CameraInfo();
+        boolean found = false;
+        int cameraId;
+        for (cameraId = 0; cameraId < numCameras; cameraId++) {
+            Camera.getCameraInfo(cameraId, info);
+            if (info.facing == Camera.CameraInfo.CAMERA_FACING_BACK) {
+                found = true;
+                break;
+            }
+        }
+
+        if (!found) {
+            // No rear facing cameras available. Just use the first camera.
+            cameraId = 0;
+            Camera.getCameraInfo(cameraId, info);
+        }
+
+        Camera camera;
         try {
-            mCamera.setPreviewDisplay(getHolder());
+            camera = Camera.open(cameraId);
+        } catch (RuntimeException e) {
+            Log.w(TAG, "Failed to open camera", e);
+            // TODO: indicate in UI when QR scanning fails.
+            return;
+        }
+
+        // This logic is based on
+        // https://developer.android.com/reference/android/hardware/Camera.html#setDisplayOrientation(int)
+        // But the sample code there appears to be wrong in practice.
+        int rotation;
+        if (info.facing == Camera.CameraInfo.CAMERA_FACING_BACK) {
+            rotation = info.orientation;
+        } else {
+            rotation = (360 - info.orientation) % 360;
+        }
+
+        PostTask.postTask(UiThreadTaskTraits.DEFAULT, () -> startCamera(camera, rotation));
+    }
+
+    private void startCamera(Camera camera, int cameraRotation) {
+        ThreadUtils.assertOnUiThread();
+
+        mAmOpeningCamera = false;
+
+        if (mDetached) {
+            // View was detached while the camera was being opened.
+            camera.release();
+            return;
+        }
+
+        mCamera = camera;
+        mCameraRotation = cameraRotation;
+
+        if (mHolder == null) {
+            // Surface was lost while the camera was being opened.
+            return;
+        }
+
+        try {
+            mCamera.setPreviewDisplay(mHolder);
             // Use a one-shot callback so that callbacks don't happen faster
             // they're processed.
             mCamera.setOneShotPreviewCallback(mCallback);
             Camera.Parameters parameters = mCamera.getParameters();
             parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
             mCamera.setParameters(parameters);
-            // TODO: the display orientation should be configured as
-            // described in
-            // https://developer.android.com/reference/android/hardware/Camera.html#setDisplayOrientation(int)
+
+            int displayRotation = 0;
+            // getRotation returns the opposite of the rotation of the physical
+            // display. (I.e. it returns the rotation that needs to be applied
+            // in order to correct for the rotation of the screen.) Thus 90/270
+            // are swapped.
+            switch (mDisplay.getRotation()) {
+                case Surface.ROTATION_0:
+                    displayRotation = 0;
+                    break;
+                case Surface.ROTATION_90:
+                    displayRotation = 270;
+                    break;
+                case Surface.ROTATION_180:
+                    displayRotation = 180;
+                    break;
+                case Surface.ROTATION_270:
+                    displayRotation = 90;
+                    break;
+            }
+            mCamera.setDisplayOrientation((mCameraRotation + displayRotation) % 360);
+
             mCamera.startPreview();
         } catch (IOException e) {
             Log.w(TAG, "Exception while starting camera", e);
         }
     }
 
-    private void stopCamera() {
-        if (mCamera == null) {
-            return;
-        }
-
-        mCamera.setOneShotPreviewCallback(null);
-        mCamera.stopPreview();
-    }
-
     /** SurfaceHolder.Callback implementation. */
     @Override
     public void surfaceCreated(SurfaceHolder holder) {
-        startCamera();
+        ThreadUtils.assertOnUiThread();
+
+        mHolder = holder;
+        if (mAmOpeningCamera) {
+            return;
+        }
+
+        if (mCamera == null) {
+            mAmOpeningCamera = true;
+            PostTask.postTask(TaskTraits.USER_VISIBLE_MAY_BLOCK, this::openCamera);
+        } else {
+            startCamera(mCamera, mCameraRotation);
+        }
     }
 
     @Override
     public void surfaceDestroyed(SurfaceHolder holder) {
-        stopCamera();
+        ThreadUtils.assertOnUiThread();
+
+        mHolder = null;
+        if (mCamera != null) {
+            mCamera.setOneShotPreviewCallback(null);
+            mCamera.stopPreview();
+        }
     }
 
     @Override
     public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
-        stopCamera();
-        startCamera();
+        surfaceDestroyed(holder);
+        surfaceCreated(holder);
     }
 }
diff --git a/chrome/android/features/cablev2_authenticator/internal/java/src/org/chromium/chrome/browser/webauth/authenticator/QRScanDialog.java b/chrome/android/features/cablev2_authenticator/internal/java/src/org/chromium/chrome/browser/webauth/authenticator/QRScanDialog.java
index b842c6e..973dc07 100644
--- a/chrome/android/features/cablev2_authenticator/internal/java/src/org/chromium/chrome/browser/webauth/authenticator/QRScanDialog.java
+++ b/chrome/android/features/cablev2_authenticator/internal/java/src/org/chromium/chrome/browser/webauth/authenticator/QRScanDialog.java
@@ -57,7 +57,8 @@
     @Override
     public View onCreateView(
             LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
-        mCameraView = new CameraView(getContext(), this);
+        mCameraView = new CameraView(
+                getContext(), getActivity().getWindowManager().getDefaultDisplay(), this);
         return mCameraView;
     }
 
diff --git a/chrome/android/features/keyboard_accessory/internal/BUILD.gn b/chrome/android/features/keyboard_accessory/internal/BUILD.gn
index 4ca6eab..5cac0f4 100644
--- a/chrome/android/features/keyboard_accessory/internal/BUILD.gn
+++ b/chrome/android/features/keyboard_accessory/internal/BUILD.gn
@@ -14,7 +14,6 @@
 
     # TODO(crbug/951695): Cyclic dependency. Depend on public only when ready.
     "//chrome/android:chrome_java",
-    "//chrome/android:chrome_public_java",
     "//chrome/android/features/keyboard_accessory/public:public_java",
     "//chrome/android/public/profiles:java",
     "//chrome/browser/flags:java",
@@ -36,7 +35,6 @@
   ]
   sources = [
     "java/src/org/chromium/chrome/browser/keyboard_accessory/AutofillKeyboardAccessoryViewBridge.java",
-    "java/src/org/chromium/chrome/browser/keyboard_accessory/KeyboardExtensionViewResizer.java",
     "java/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingComponentBridge.java",
     "java/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingCoordinator.java",
     "java/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingMediator.java",
diff --git a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/KeyboardExtensionViewResizer.java b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/KeyboardExtensionViewResizer.java
deleted file mode 100644
index 61f1f0dc..0000000
--- a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/KeyboardExtensionViewResizer.java
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.keyboard_accessory;
-
-import androidx.annotation.Px;
-
-import org.chromium.base.ObserverList;
-import org.chromium.chrome.browser.compositor.CompositorViewResizer;
-
-/**
- * This class holds the size of any extension to or even replacement for a keyboard. The height can
- * be used to either compute an offset for bottom bars (e.g. CCTs or PWAs) or to push up the content
- * area.
- */
-class KeyboardExtensionViewResizer implements CompositorViewResizer {
-    private int mHeight;
-    private final ObserverList<Observer> mObservers = new ObserverList<>();
-
-    @Override
-    public @Px int getHeight() {
-        return mHeight;
-    }
-
-    @Override
-    public void addObserver(Observer observer) {
-        mObservers.addObserver(observer);
-    }
-
-    @Override
-    public void removeObserver(Observer observer) {
-        mObservers.removeObserver(observer);
-    }
-
-    /**
-     * Sets a new extension height and notifies observers if its value changed.
-     * @param newKeyboardExtensionHeight The height in pixels.
-     */
-    void setKeyboardExtensionHeight(@Px int newKeyboardExtensionHeight) {
-        if (mHeight == newKeyboardExtensionHeight) return;
-        mHeight = newKeyboardExtensionHeight;
-        for (Observer observer : mObservers) observer.onHeightChanged(mHeight);
-    }
-}
diff --git a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingCoordinator.java b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingCoordinator.java
index 5651db4..1f4a476 100644
--- a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingCoordinator.java
+++ b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingCoordinator.java
@@ -9,7 +9,6 @@
 
 import androidx.annotation.VisibleForTesting;
 
-import org.chromium.chrome.browser.compositor.CompositorViewResizer;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.keyboard_accessory.bar_component.KeyboardAccessoryCoordinator;
 import org.chromium.chrome.browser.keyboard_accessory.data.KeyboardAccessoryData;
@@ -121,11 +120,6 @@
     }
 
     @Override
-    public CompositorViewResizer getKeyboardExtensionViewResizer() {
-        return mMediator.getKeyboardExtensionViewResizer();
-    }
-
-    @Override
     public boolean isFillingViewShown(View view) {
         return mMediator.isFillingViewShown(view);
     }
diff --git a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingMediator.java b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingMediator.java
index a28d38e..5f3b730 100644
--- a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingMediator.java
+++ b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingMediator.java
@@ -26,12 +26,12 @@
 import androidx.annotation.Px;
 import androidx.annotation.VisibleForTesting;
 
+import org.chromium.base.supplier.ObservableSupplierImpl;
 import org.chromium.base.supplier.Supplier;
 import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.ChromeKeyboardVisibilityDelegate;
 import org.chromium.chrome.browser.ChromeWindow;
 import org.chromium.chrome.browser.compositor.CompositorViewHolder;
-import org.chromium.chrome.browser.compositor.CompositorViewResizer;
 import org.chromium.chrome.browser.compositor.layouts.Layout;
 import org.chromium.chrome.browser.compositor.layouts.LayoutManager;
 import org.chromium.chrome.browser.compositor.layouts.SceneChangeObserver;
@@ -85,8 +85,8 @@
     private PropertyModel mModel = ManualFillingProperties.createFillingModel();
     private WindowAndroid mWindowAndroid;
     private Supplier<InsetObserverView> mInsetObserverViewSupplier;
-    private final KeyboardExtensionViewResizer mKeyboardExtensionViewResizer =
-            new KeyboardExtensionViewResizer();
+    private final ObservableSupplierImpl<Integer> mViewportInsetSupplier =
+            new ObservableSupplierImpl<>();
     private final ManualFillingStateCache mStateCache = new ManualFillingStateCache();
     private final HashSet<Tab> mObservedTabs = new HashSet<>();
     private KeyboardAccessoryCoordinator mKeyboardAccessory;
@@ -137,11 +137,17 @@
         }
     };
 
+    /** Default constructor */
+    ManualFillingMediator() {
+        mViewportInsetSupplier.set(0);
+    }
+
     void initialize(KeyboardAccessoryCoordinator keyboardAccessory,
             AccessorySheetCoordinator accessorySheet, WindowAndroid windowAndroid) {
         mActivity = (ChromeActivity) windowAndroid.getActivity().get();
         assert mActivity != null;
         mWindowAndroid = windowAndroid;
+        mWindowAndroid.getApplicationBottomInsetProvider().addSupplier(mViewportInsetSupplier);
         mKeyboardAccessory = keyboardAccessory;
         mModel.set(PORTRAIT_ORIENTATION, hasPortraitOrientation());
         mModel.addObserver(this::onPropertyChanged);
@@ -243,6 +249,7 @@
     void destroy() {
         if (!isInitialized()) return;
         pause();
+        mWindowAndroid.getApplicationBottomInsetProvider().removeSupplier(mViewportInsetSupplier);
         mActivity.findViewById(android.R.id.content).removeOnLayoutChangeListener(this);
         mTabModelObserver.destroy();
         mStateCache.destroy();
@@ -320,16 +327,11 @@
         WebContents webContents = mActivity.getCurrentWebContents();
         if (webContents == null) return false;
         float height = webContents.getHeight(); // getHeight actually returns dip, not Px!
-        height += mKeyboardExtensionViewResizer.getHeight()
-                / mWindowAndroid.getDisplay().getDipScale();
+        height += mViewportInsetSupplier.get() / mWindowAndroid.getDisplay().getDipScale();
         return height >= MINIMAL_AVAILABLE_VERTICAL_SPACE
                 && webContents.getWidth() >= MINIMAL_AVAILABLE_HORIZONTAL_SPACE;
     }
 
-    CompositorViewResizer getKeyboardExtensionViewResizer() {
-        return mKeyboardExtensionViewResizer;
-    }
-
     private void onPropertyChanged(PropertyObservable<PropertyKey> source, PropertyKey property) {
         assert source == mModel;
         if (property == SHOW_WHEN_VISIBLE) {
@@ -514,7 +516,7 @@
             newControlsOffset += mAccessorySheet.getHeight();
         }
         mKeyboardAccessory.setBottomOffset(newControlsOffset);
-        mKeyboardExtensionViewResizer.setKeyboardExtensionHeight(newControlsHeight);
+        mViewportInsetSupplier.set(newControlsHeight);
         mActivity.getFullscreenManager().updateViewportSize();
     }
 
@@ -603,7 +605,7 @@
         float density = mWindowAndroid.getDisplay().getDipScale();
         // The maximal height for the sheet ensures a minimal amount of WebContents space.
         @Px
-        int maxHeight = mKeyboardExtensionViewResizer.getHeight();
+        int maxHeight = mViewportInsetSupplier.get();
         maxHeight += Math.round(density * webContents.getHeight());
         maxHeight -= Math.round(density * MINIMAL_AVAILABLE_VERTICAL_SPACE);
         if (mAccessorySheet.getHeight() <= maxHeight) return; // Sheet height needs no adjustment!
diff --git a/chrome/android/features/keyboard_accessory/junit/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingControllerTest.java b/chrome/android/features/keyboard_accessory/junit/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingControllerTest.java
index 7ed96211..a3b9fae 100644
--- a/chrome/android/features/keyboard_accessory/junit/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingControllerTest.java
+++ b/chrome/android/features/keyboard_accessory/junit/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingControllerTest.java
@@ -82,6 +82,7 @@
 import org.chromium.components.embedder_support.view.ContentView;
 import org.chromium.content_public.browser.WebContents;
 import org.chromium.ui.KeyboardVisibilityDelegate;
+import org.chromium.ui.base.ApplicationViewportInsetSupplier;
 import org.chromium.ui.display.DisplayAndroid;
 import org.chromium.ui.modelutil.PropertyModel;
 
@@ -119,6 +120,8 @@
     private CompositorViewHolder mMockCompositorViewHolder;
     @Mock
     private BottomSheetController mMockBottomSheetController;
+    @Mock
+    private ApplicationViewportInsetSupplier mApplicationViewportInsetSupplier;
 
     @Rule
     public Features.JUnitProcessor mFeaturesProcessor = new Features.JUnitProcessor();
@@ -285,6 +288,8 @@
         when(mMockActivity.getBottomSheetController()).thenReturn(mMockBottomSheetController);
         when(mMockWindow.getKeyboardDelegate()).thenReturn(mMockKeyboard);
         when(mMockWindow.getActivity()).thenReturn(new WeakReference<>(mMockActivity));
+        when(mMockWindow.getApplicationBottomInsetProvider())
+                .thenReturn(mApplicationViewportInsetSupplier);
         when(mMockKeyboard.calculateKeyboardHeight(any())).thenReturn(0);
         when(mMockActivity.getTabModelSelector()).thenReturn(mMockTabModelSelector);
         when(mMockActivity.getActivityTabProvider()).thenReturn(mActivityTabProvider);
diff --git a/chrome/android/features/keyboard_accessory/public/BUILD.gn b/chrome/android/features/keyboard_accessory/public/BUILD.gn
index 9a6954a..839d0492a 100644
--- a/chrome/android/features/keyboard_accessory/public/BUILD.gn
+++ b/chrome/android/features/keyboard_accessory/public/BUILD.gn
@@ -7,7 +7,6 @@
 android_library("public_java") {
   deps = [
     "//base:base_java",
-    "//chrome/android:chrome_public_java",
     "//components/autofill/android:autofill_java",
     "//third_party/android_deps:androidx_annotation_annotation_java",
   ]
diff --git a/chrome/android/features/keyboard_accessory/public/java/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingComponent.java b/chrome/android/features/keyboard_accessory/public/java/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingComponent.java
index 17d65fa..06a7f08 100644
--- a/chrome/android/features/keyboard_accessory/public/java/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingComponent.java
+++ b/chrome/android/features/keyboard_accessory/public/java/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingComponent.java
@@ -7,7 +7,6 @@
 import android.view.View;
 import android.view.ViewStub;
 
-import org.chromium.chrome.browser.compositor.CompositorViewResizer;
 import org.chromium.chrome.browser.keyboard_accessory.data.KeyboardAccessoryData;
 import org.chromium.chrome.browser.keyboard_accessory.data.PropertyProvider;
 import org.chromium.components.autofill.AutofillDelegate;
@@ -109,14 +108,6 @@
     void onPause();
 
     /**
-     * Returns a {@link CompositorViewResizer} that allows to access the combined height of
-     * KeyboardAccessoryCoordinator and AccessorySheetCoordinator, and to be
-     * notified when it changes.
-     * @return A {@link CompositorViewResizer}.
-     */
-    CompositorViewResizer getKeyboardExtensionViewResizer();
-
-    /**
      * Returns whether the Keyboard is replaced by an accessory sheet or is about to do so.
      * @return True if an accessory sheet is (being) opened and replacing the keyboard.
      * @param view A {@link View} that is used to find the window root.
diff --git a/chrome/android/features/start_surface/internal/java/src/org/chromium/chrome/features/start_surface/StartSurfaceLayout.java b/chrome/android/features/start_surface/internal/java/src/org/chromium/chrome/features/start_surface/StartSurfaceLayout.java
index 220ce61..ef9c209 100644
--- a/chrome/android/features/start_surface/internal/java/src/org/chromium/chrome/features/start_surface/StartSurfaceLayout.java
+++ b/chrome/android/features/start_surface/internal/java/src/org/chromium/chrome/features/start_surface/StartSurfaceLayout.java
@@ -12,7 +12,6 @@
 import android.graphics.RectF;
 import android.os.Handler;
 import android.os.SystemClock;
-import android.text.TextUtils;
 
 import androidx.annotation.VisibleForTesting;
 
@@ -33,6 +32,7 @@
 import org.chromium.chrome.browser.compositor.layouts.eventfilter.EventFilter;
 import org.chromium.chrome.browser.compositor.scene_layer.SceneLayer;
 import org.chromium.chrome.browser.compositor.scene_layer.TabListSceneLayer;
+import org.chromium.chrome.browser.flags.CachedFeatureFlags;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager;
 import org.chromium.chrome.browser.tab.Tab;
@@ -60,10 +60,6 @@
     public static final long ZOOMING_DURATION = 300;
     private static final int BACKGROUND_FADING_DURATION_MS = 150;
 
-    // Field trial parameter for whether skipping slow zooming animation.
-    private static final String SKIP_SLOW_ZOOMING_PARAM = "skip-slow-zooming";
-    private static final boolean DEFAULT_SKIP_SLOW_ZOOMING = true;
-
     // The transition animation from a tab to the tab switcher.
     private AnimatorSet mTabToSwitcherAnimation;
     private boolean mIsAnimating;
@@ -176,8 +172,10 @@
         boolean showShrinkingAnimation = animate && TabFeatureUtilities.isTabToGtsAnimationEnabled()
                 && !isCurrentTabModelEmpty;
         boolean quick = mTabListDelegate.prepareOverview();
-        Log.d(TAG, "SkipSlowZooming = " + getSkipSlowZooming());
-        if (getSkipSlowZooming()) {
+        boolean skipSlowZooming =
+                CachedFeatureFlags.getValue(TabUiFeatureUtilities.SKIP_SLOW_ZOOMING);
+        Log.d(TAG, "SkipSlowZooming = " + skipSlowZooming);
+        if (skipSlowZooming) {
             showShrinkingAnimation &= quick;
         }
 
@@ -448,13 +446,6 @@
         }
     }
 
-    private boolean getSkipSlowZooming() {
-        String skip = ChromeFeatureList.getFieldTrialParamByFeature(
-                ChromeFeatureList.TAB_TO_GTS_ANIMATION, SKIP_SLOW_ZOOMING_PARAM);
-        if (TextUtils.equals(skip, "")) return DEFAULT_SKIP_SLOW_ZOOMING;
-        return Boolean.valueOf(skip);
-    }
-
     @Override
     protected void updateSceneLayer(RectF viewport, RectF contentViewport,
             LayerTitleCache layerTitleCache, TabContentManager tabContentManager,
diff --git a/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/StartSurfaceLayoutTest.java b/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/StartSurfaceLayoutTest.java
index 645d40a..e945e55 100644
--- a/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/StartSurfaceLayoutTest.java
+++ b/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/StartSurfaceLayoutTest.java
@@ -26,8 +26,10 @@
 
 import static org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper.areAnimatorsEnabled;
 import static org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper.closeFirstTabInTabSwitcher;
+import static org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper.createTabGroup;
 import static org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper.createTabs;
 import static org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper.enterTabSwitcher;
+import static org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper.getSwipeToDismissAction;
 import static org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper.rotateDeviceToOrientation;
 import static org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper.verifyTabModelTabCount;
 import static org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper.verifyTabSwitcherCardCount;
@@ -113,6 +115,8 @@
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Locale;
@@ -1401,6 +1405,39 @@
                 .perform(click());
     }
 
+    @Test
+    @MediumTest
+    // clang-format off
+    @CommandLineFlags.Add({BASE_PARAMS})
+    @Features.EnableFeatures({ChromeFeatureList.TAB_GROUPS_ANDROID})
+    public void testSwipeToDismiss_GTS() throws Exception {
+        // clang-format on
+        ChromeTabbedActivity cta = mActivityTestRule.getActivity();
+        // Create 3 tabs and merge the first two tabs into one group.
+        createTabs(cta, false, 3);
+        enterTabSwitcher(cta);
+        TabModel normalTabModel = cta.getTabModelSelector().getModel(false);
+        List<Tab> tabGroup = new ArrayList<>(
+                Arrays.asList(normalTabModel.getTabAt(0), normalTabModel.getTabAt(1)));
+        createTabGroup(cta, false, tabGroup);
+        verifyTabSwitcherCardCount(cta, 2);
+        verifyTabModelTabCount(cta, 3, 0);
+
+        // Swipe to dismiss a single tab card.
+        onView(allOf(withParent(withId(R.id.compositor_view_holder)), withId(R.id.tab_list_view)))
+                .perform(RecyclerViewActions.actionOnItemAtPosition(
+                        1, getSwipeToDismissAction(false)));
+        verifyTabSwitcherCardCount(cta, 1);
+        verifyTabModelTabCount(cta, 2, 0);
+
+        // Swipe to dismiss a tab group card.
+        onView(allOf(withParent(withId(R.id.compositor_view_holder)), withId(R.id.tab_list_view)))
+                .perform(RecyclerViewActions.actionOnItemAtPosition(
+                        0, getSwipeToDismissAction(true)));
+        verifyTabSwitcherCardCount(cta, 0);
+        verifyTabModelTabCount(cta, 0, 0);
+    }
+
     private static class TabCountAssertion implements ViewAssertion {
         private int mExpectedCount;
 
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMediator.java
index fb0bc16..a14709a 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMediator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMediator.java
@@ -16,6 +16,7 @@
 import android.graphics.Bitmap;
 import android.os.Handler;
 import android.os.SystemClock;
+import android.text.TextUtils;
 import android.view.ViewGroup;
 
 import androidx.annotation.Nullable;
@@ -30,6 +31,7 @@
 import org.chromium.base.task.PostTask;
 import org.chromium.chrome.browser.compositor.layouts.LayoutManager;
 import org.chromium.chrome.browser.compositor.layouts.content.TabContentManager;
+import org.chromium.chrome.browser.flags.CachedFeatureFlags;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager;
 import org.chromium.chrome.browser.fullscreen.FullscreenManager;
@@ -614,9 +616,9 @@
      *  @return whether tabs should show in MRU order
      */
     static boolean isShowingTabsInMRUOrder() {
-        String feature = ChromeFeatureList.getFieldTrialParamByFeature(
-                ChromeFeatureList.START_SURFACE_ANDROID, "start_surface_variation");
-        return feature.equals("twopanes");
+        String feature =
+                CachedFeatureFlags.getValue(StartSurfaceConfiguration.START_SURFACE_VARIATION);
+        return TextUtils.equals(feature, "twopanes");
     }
 
     /**
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiFeatureUtilities.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiFeatureUtilities.java
index 1fe8d81..5ea482c 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiFeatureUtilities.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiFeatureUtilities.java
@@ -4,13 +4,18 @@
 
 package org.chromium.chrome.browser.tasks.tab_management;
 
+import android.text.TextUtils;
+
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 
 import org.chromium.base.ContextUtils;
 import org.chromium.chrome.browser.device.DeviceClassManager;
+import org.chromium.chrome.browser.flags.BooleanCachedFieldTrialParameter;
 import org.chromium.chrome.browser.flags.CachedFeatureFlags;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
+import org.chromium.chrome.browser.flags.DoubleCachedFieldTrialParameter;
+import org.chromium.chrome.browser.flags.StringCachedFieldTrialParameter;
 import org.chromium.chrome.features.start_surface.StartSurfaceConfiguration;
 import org.chromium.ui.base.DeviceFormFactor;
 
@@ -22,6 +27,23 @@
  * A class to handle the state of flags for tab_management.
  */
 public class TabUiFeatureUtilities {
+    // Field trial parameters:
+    public static final String SKIP_SLOW_ZOOMING_PARAM = "skip-slow-zooming";
+    public static final BooleanCachedFieldTrialParameter SKIP_SLOW_ZOOMING =
+            new BooleanCachedFieldTrialParameter(
+                    ChromeFeatureList.TAB_TO_GTS_ANIMATION, SKIP_SLOW_ZOOMING_PARAM, true);
+
+    public static final String TAB_GRID_LAYOUT_ANDROID_NEW_TAB_TILE_PARAM =
+            "tab_grid_layout_android_new_tab_tile";
+    public static final StringCachedFieldTrialParameter TAB_GRID_LAYOUT_ANDROID_NEW_TAB_TILE =
+            new StringCachedFieldTrialParameter(ChromeFeatureList.TAB_GRID_LAYOUT_ANDROID,
+                    TAB_GRID_LAYOUT_ANDROID_NEW_TAB_TILE_PARAM, "");
+
+    public static final String THUMBNAIL_ASPECT_RATIO_PARAM = "thumbnail_aspect_ratio";
+    public static final DoubleCachedFieldTrialParameter THUMBNAIL_ASPECT_RATIO =
+            new DoubleCachedFieldTrialParameter(
+                    ChromeFeatureList.TAB_GRID_LAYOUT_ANDROID, THUMBNAIL_ASPECT_RATIO_PARAM, 1.0);
+
     private static Boolean sSearchTermChipEnabledForTesting;
     private static Boolean sTabManagementModuleSupportedForTesting;
     private static Double sTabThumbnailAspectRatioForTesting;
@@ -123,13 +145,17 @@
         if (sTabThumbnailAspectRatioForTesting != null) {
             aspectRatio = sTabThumbnailAspectRatioForTesting;
         } else {
-            aspectRatio = ChromeFeatureList.getFieldTrialParamByFeatureAsDouble(
-                    ChromeFeatureList.TAB_GRID_LAYOUT_ANDROID, "thumbnail_aspect_ratio", 1.0);
+            aspectRatio = CachedFeatureFlags.getValue(THUMBNAIL_ASPECT_RATIO);
         }
 
         return Double.compare(1.0, aspectRatio) != 0;
     }
 
+    public static boolean isTabGridLayoutAndroidNewTabTileEnabled() {
+        return TextUtils.equals(
+                CachedFeatureFlags.getValue(TAB_GRID_LAYOUT_ANDROID_NEW_TAB_TILE), "NewTabTile");
+    }
+
     @VisibleForTesting
     public static void setTabThumbnailAspectRatioForTesting(Double value) {
         sTabThumbnailAspectRatioForTesting = value;
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogTest.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogTest.java
index 82bcb602..5e48fe4d 100644
--- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogTest.java
+++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogTest.java
@@ -32,6 +32,7 @@
 import static org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper.closeFirstTabInDialog;
 import static org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper.createTabs;
 import static org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper.enterTabSwitcher;
+import static org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper.getSwipeToDismissAction;
 import static org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper.isShowingPopupTabList;
 import static org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper.mergeAllNormalTabsToAGroup;
 import static org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper.verifyShowingPopupTabList;
@@ -41,6 +42,7 @@
 import android.content.Intent;
 import android.graphics.Rect;
 import android.support.test.espresso.Espresso;
+import android.support.test.espresso.contrib.RecyclerViewActions;
 import android.support.test.espresso.intent.Intents;
 import android.support.test.espresso.intent.rule.IntentsTestRule;
 import android.support.test.filters.MediumTest;
@@ -425,6 +427,31 @@
         assertEquals(3, filter.getCount());
     }
 
+    @Test
+    @MediumTest
+    public void testSwipeToDismiss_Dialog() throws InterruptedException {
+        ChromeTabbedActivity cta = mActivityTestRule.getActivity();
+        // Create 2 tabs and merge them into one group.
+        createTabs(cta, false, 2);
+        enterTabSwitcher(cta);
+        mergeAllNormalTabsToAGroup(cta);
+        verifyTabSwitcherCardCount(cta, 1);
+        openDialogFromTabSwitcherAndVerify(cta, 2);
+
+        // Swipe to dismiss two tabs in dialog.
+        onView((withId(R.id.tab_list_view)))
+                .inRoot(withDecorView(not(cta.getWindow().getDecorView())))
+                .perform(RecyclerViewActions.actionOnItemAtPosition(
+                        1, getSwipeToDismissAction(true)));
+        verifyShowingDialog(cta, 1);
+        onView((withId(R.id.tab_list_view)))
+                .inRoot(withDecorView(not(cta.getWindow().getDecorView())))
+                .perform(RecyclerViewActions.actionOnItemAtPosition(
+                        0, getSwipeToDismissAction(false)));
+        waitForDialogHidingAnimation(cta);
+        verifyTabSwitcherCardCount(cta, 0);
+    }
+
     private void openDialogFromTabSwitcherAndVerify(ChromeTabbedActivity cta, int tabCount) {
         clickFirstCardFromTabSwitcher(cta);
         CriteriaHelper.pollInstrumentationThread(() -> isDialogShowing(cta));
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridIphTest.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridIphTest.java
index 70959cdc..b50ca17c 100644
--- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridIphTest.java
+++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridIphTest.java
@@ -22,13 +22,16 @@
 import static org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper.clickScrimToExitDialog;
 import static org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper.closeFirstTabInTabSwitcher;
 import static org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper.enterTabSwitcher;
+import static org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper.getSwipeToDismissAction;
 import static org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper.rotateDeviceToOrientation;
 import static org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper.verifyTabSwitcherCardCount;
 
 import android.content.res.Configuration;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.espresso.NoMatchingRootException;
+import android.support.test.espresso.contrib.RecyclerViewActions;
 import android.support.test.filters.MediumTest;
+import android.support.v7.widget.RecyclerView;
 import android.widget.TextView;
 
 import org.junit.After;
@@ -221,6 +224,23 @@
         onView(withId(R.id.tab_grid_message_item)).check(matches(isDisplayed()));
     }
 
+    @Test
+    @MediumTest
+    public void testSwipeToDismiss_IPH() throws InterruptedException {
+        ChromeTabbedActivity cta = mActivityTestRule.getActivity();
+        enterTabSwitcher(cta);
+        onView(withId(R.id.tab_grid_message_item)).check(matches(isDisplayed()));
+        RecyclerView.ViewHolder viewHolder = ((RecyclerView) cta.findViewById(R.id.tab_list_view))
+                                                     .findViewHolderForAdapterPosition(1);
+        assertEquals(TabProperties.UiType.MESSAGE, viewHolder.getItemViewType());
+
+        onView(allOf(withParent(withId(R.id.compositor_view_holder)), withId(R.id.tab_list_view)))
+                .perform(RecyclerViewActions.actionOnItemAtPosition(
+                        1, getSwipeToDismissAction(true)));
+
+        onView(withId(R.id.tab_grid_message_item)).check(doesNotExist());
+    }
+
     private void verifyIphDialogShowing(ChromeTabbedActivity cta) {
         onView(withId(R.id.iph_dialog))
                 .inRoot(withDecorView(not(cta.getWindow().getDecorView())))
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabUiTestHelper.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabUiTestHelper.java
index ba8132b0..fbb44219 100644
--- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabUiTestHelper.java
+++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabUiTestHelper.java
@@ -36,6 +36,10 @@
 import android.support.test.espresso.UiController;
 import android.support.test.espresso.ViewAction;
 import android.support.test.espresso.ViewAssertion;
+import android.support.test.espresso.action.GeneralLocation;
+import android.support.test.espresso.action.GeneralSwipeAction;
+import android.support.test.espresso.action.Press;
+import android.support.test.espresso.action.Swipe;
 import android.support.test.espresso.contrib.RecyclerViewActions;
 import android.view.View;
 
@@ -308,7 +312,8 @@
      * @param isIncognito     Whether the group is in normal model or incognito model.
      * @param tabs            A list of {@link Tab} to create group.
      */
-    static void createTabGroup(ChromeTabbedActivity cta, boolean isIncognito, List<Tab> tabs) {
+    public static void createTabGroup(
+            ChromeTabbedActivity cta, boolean isIncognito, List<Tab> tabs) {
         if (tabs.size() == 0) return;
         assert cta.getTabModelSelector().getTabModelFilterProvider().getCurrentTabModelFilter()
                         instanceof TabGroupModelFilter;
@@ -559,6 +564,22 @@
     }
 
     /**
+     * Get the {@link GeneralSwipeAction} used to perform a swipe-to-dismiss action in tab grid
+     * layout.
+     * @param isLeftToRight  decides whether the swipe is from left to right or from right to left.
+     * @return {@link GeneralSwipeAction} to perform swipe-to-dismiss.
+     */
+    public static GeneralSwipeAction getSwipeToDismissAction(boolean isLeftToRight) {
+        if (isLeftToRight) {
+            return new GeneralSwipeAction(Swipe.FAST, GeneralLocation.CENTER_LEFT,
+                    GeneralLocation.CENTER_RIGHT, Press.FINGER);
+        } else {
+            return new GeneralSwipeAction(Swipe.FAST, GeneralLocation.CENTER_RIGHT,
+                    GeneralLocation.CENTER_LEFT, Press.FINGER);
+        }
+    }
+
+    /**
      * Implementation of {@link ViewAssertion} to verify the {@link RecyclerView} has correct number
      * of children.
      */
diff --git a/chrome/android/feed/feed_java_sources.gni b/chrome/android/feed/feed_java_sources.gni
index 598ba5d..ab4e18bc 100644
--- a/chrome/android/feed/feed_java_sources.gni
+++ b/chrome/android/feed/feed_java_sources.gni
@@ -386,8 +386,6 @@
     "//chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/library/api/internal/common/testing/ContentIdGenerators.java",
     "//chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/library/api/internal/common/testing/InternalProtocolBuilder.java",
     "//chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/library/basicstream/internal/drivers/testing/FakeFeatureDriver.java",
-    "//chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/library/basicstream/internal/drivers/testing/FakeFeatureDriver.java",
-    "//chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/library/basicstream/internal/drivers/testing/FakeLeafFeatureDriver.java",
     "//chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/library/basicstream/internal/drivers/testing/FakeLeafFeatureDriver.java",
     "//chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/library/common/concurrent/testing/FakeDirectExecutor.java",
     "//chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/library/common/concurrent/testing/FakeMainThreadRunner.java",
@@ -402,7 +400,6 @@
     "//chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/library/common/testing/SessionTestUtils.java",
     "//chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/library/common/time/testing/FakeClock.java",
     "//chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/library/feedsessionmanager/internal/testing/AbstractSessionImplTest.java",
-    "//chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/library/feedsessionmanager/internal/testing/AbstractSessionImplTest.java",
     "//chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/library/feedstore/testing/AbstractClearableFeedStoreTest.java",
     "//chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/library/feedstore/testing/AbstractFeedStoreTest.java",
     "//chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/library/feedstore/testing/DelegatingContentStorage.java",
@@ -496,29 +493,18 @@
     "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/common/testing/RunnableSubjectTest.java",
     "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/common/ui/LayoutUtilsTest.java",
     "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedactionmanager/FeedActionManagerImplTest.java",
-    "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedactionmanager/FeedActionManagerImplTest.java",
     "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedactionparser/FeedActionParserTest.java",
-    "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedactionparser/FeedActionParserTest.java",
-    "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedactionreader/FeedActionReaderTest.java",
     "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedactionreader/FeedActionReaderTest.java",
     "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedapplifecyclelistener/FeedAppLifecycleListenerTest.java",
     "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedknowncontent/FeedKnownContentImplTest.java",
     "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedmodelprovider/FeedModelProviderFactoryTest.java",
-    "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedmodelprovider/FeedModelProviderFactoryTest.java",
-    "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedmodelprovider/FeedModelProviderTest.java",
     "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedmodelprovider/FeedModelProviderTest.java",
     "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedmodelprovider/internal/FeatureChangeImplTest.java",
-    "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedmodelprovider/internal/FeatureChangeImplTest.java",
-    "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedmodelprovider/internal/ModelCursorImplTest.java",
     "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedmodelprovider/internal/ModelCursorImplTest.java",
     "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedmodelprovider/internal/ModelFeatureImplTest.java",
-    "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedmodelprovider/internal/ModelFeatureImplTest.java",
     "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedmodelprovider/internal/ModelMutationImplTest.java",
-    "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedmodelprovider/internal/ModelMutationImplTest.java",
-    "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedmodelprovider/internal/ModelTokenImplTest.java",
     "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedmodelprovider/internal/ModelTokenImplTest.java",
     "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedprotocoladapter/FeedProtocolAdapterTest.java",
-    "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedprotocoladapter/internal/transformers/ContentDataOperationTransformerTest.java",
     "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedprotocoladapter/internal/transformers/FeatureDataOperationTransformerTest.java",
     "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedrequestmanager/FeedActionUploadRequestManagerTest.java",
     "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedrequestmanager/FeedRequestManagerImplTest.java",
@@ -526,22 +512,13 @@
     "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedrequestmanager/UploadableActionsRequestBuilderTest.java",
     "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedrequestmanager/internal/UtilsTest.java",
     "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedsessionmanager/FeedSessionManagerImplTest.java",
-    "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedsessionmanager/FeedSessionManagerImplTest.java",
-    "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedsessionmanager/internal/ContentCacheTest.java",
     "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedsessionmanager/internal/ContentCacheTest.java",
     "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedsessionmanager/internal/HeadAsStructureTest.java",
-    "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedsessionmanager/internal/HeadAsStructureTest.java",
-    "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedsessionmanager/internal/HeadSessionImplTest.java",
     "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedsessionmanager/internal/HeadSessionImplTest.java",
     "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedsessionmanager/internal/SessionCacheTest.java",
-    "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedsessionmanager/internal/SessionCacheTest.java",
-    "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedsessionmanager/internal/SessionContentTrackerTest.java",
     "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedsessionmanager/internal/SessionContentTrackerTest.java",
     "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedsessionmanager/internal/SessionImplTest.java",
-    "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedsessionmanager/internal/SessionImplTest.java",
     "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedsessionmanager/internal/SessionManagerMutationTest.java",
-    "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedsessionmanager/internal/SessionManagerMutationTest.java",
-    "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedsessionmanager/internal/TimeoutSessionImplTest.java",
     "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedsessionmanager/internal/TimeoutSessionImplTest.java",
     "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedstore/FeedStoreEphemeralModeTest.java",
     "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedstore/FeedStoreTest.java",
@@ -591,76 +568,40 @@
     "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/infraintegration/ViewDepthProviderTest.java",
     "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/mocknetworkclient/MockServerNetworkClientTest.java",
     "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ChunkedTextElementAdapterTest.java",
-    "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ChunkedTextElementAdapterTest.java",
-    "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/CustomElementAdapterTest.java",
     "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/CustomElementAdapterTest.java",
     "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/DebugLoggerTest.java",
-    "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/DebugLoggerTest.java",
-    "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ElementAdapterFactoryTest.java",
     "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ElementAdapterFactoryTest.java",
     "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ElementAdapterTest.java",
-    "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ElementAdapterTest.java",
-    "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ElementContainerAdapterTest.java",
     "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ElementContainerAdapterTest.java",
     "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ElementListAdapterTest.java",
-    "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ElementListAdapterTest.java",
-    "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ElementStackAdapterTest.java",
     "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ElementStackAdapterTest.java",
     "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/FrameAdapterImplTest.java",
-    "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/FrameAdapterImplTest.java",
-    "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/FrameContextTest.java",
     "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/FrameContextTest.java",
     "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/GridRowAdapterTest.java",
-    "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/GridRowAdapterTest.java",
-    "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ImageElementAdapterTest.java",
     "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ImageElementAdapterTest.java",
     "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/KeyedRecyclerPoolTest.java",
-    "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/KeyedRecyclerPoolTest.java",
-    "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/LoadImageCallbackTest.java",
     "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/LoadImageCallbackTest.java",
     "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/MediaQueryHelperTest.java",
-    "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/MediaQueryHelperTest.java",
-    "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/NoKeyOverwriteHashMapTest.java",
     "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/NoKeyOverwriteHashMapTest.java",
     "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ParameterizedTextElementAdapterFactoryTest.java",
-    "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ParameterizedTextElementAdapterFactoryTest.java",
-    "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ParameterizedTextElementAdapterTest.java",
     "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ParameterizedTextElementAdapterTest.java",
     "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ParameterizedTextEvaluatorTest.java",
-    "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ParameterizedTextEvaluatorTest.java",
-    "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/PietManagerImplTest.java",
     "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/PietManagerImplTest.java",
     "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/PietStylesHelperTest.java",
-    "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/PietStylesHelperTest.java",
-    "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/SingleKeyRecyclerPoolTest.java",
     "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/SingleKeyRecyclerPoolTest.java",
     "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/StyleProviderTest.java",
-    "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/StyleProviderTest.java",
-    "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/TemplateBinderTest.java",
     "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/TemplateBinderTest.java",
     "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/TextElementAdapterTest.java",
-    "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/TextElementAdapterTest.java",
-    "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ViewUtilsTest.java",
     "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ViewUtilsTest.java",
     "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/host/AssetProviderTest.java",
-    "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/host/AssetProviderTest.java",
-    "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ui/AspectRatioScalingImageViewTest.java",
     "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ui/AspectRatioScalingImageViewTest.java",
     "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ui/BitmapMaskingRoundedCornerDelegateTest.java",
-    "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ui/BitmapMaskingRoundedCornerDelegateTest.java",
-    "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ui/BorderDrawableTest.java",
     "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ui/BorderDrawableTest.java",
     "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ui/GradientDrawableTest.java",
-    "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ui/GradientDrawableTest.java",
-    "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ui/GradientShaderTest.java",
     "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ui/GradientShaderTest.java",
     "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ui/GridRowViewTest.java",
-    "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ui/GridRowViewTest.java",
-    "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ui/RoundedCornerMaskCacheTest.java",
     "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ui/RoundedCornerMaskCacheTest.java",
     "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ui/RoundedCornerViewHelperTest.java",
-    "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ui/RoundedCornerViewHelperTest.java",
-    "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ui/RoundedCornerWrapperViewTest.java",
     "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/piet/ui/RoundedCornerWrapperViewTest.java",
     "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/sharedstream/contentchanged/StreamContentChangedListenerTest.java",
     "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/sharedstream/contextmenumanager/ContextMenuManagerImplTest.java",
diff --git a/chrome/android/java/res/values/dimens.xml b/chrome/android/java/res/values/dimens.xml
index 7f64519..03f19657 100644
--- a/chrome/android/java/res/values/dimens.xml
+++ b/chrome/android/java/res/values/dimens.xml
@@ -59,16 +59,8 @@
     <dimen name="menu_badge_translation_y">6dp</dimen>
 
     <!-- Infobar dimensions -->
-    <!-- Text size of the infobar message when a big icon is shown. -->
-    <dimen name="infobar_big_icon_message_size">20sp</dimen>
     <!-- Text size of descriptive controls on an infobar. -->
     <dimen name="infobar_descriptive_text_size">14sp</dimen>
-    <!-- Padding surrounding the infobar. -->
-    <dimen name="infobar_padding">16dp</dimen>
-    <!-- Margin between stacked buttons in an infobar. -->
-    <dimen name="infobar_margin_between_stacked_buttons">24dp</dimen>
-    <!-- Minimum width of an infobar. -->
-    <dimen name="infobar_min_width">220dp</dimen>
     <!-- Maximum width of an infobar. -->
     <dimen name="infobar_max_width">600dp</dimen>
     <!-- Width of side shadows on floating infobars. -->
@@ -78,22 +70,9 @@
     <!-- Distance that a back infobar peeks out above the front infobar. -->
     <dimen name="infobar_peeking_height">10dp</dimen>
 
-    <!-- Vertical margin applied between groups of controls. -->
-    <dimen name="infobar_margin_above_control_groups">24dp</dimen>
-    <dimen name="infobar_margin_above_button_row">32dp</dimen>
-
-    <!-- Dimensions applied to InfoBars with differently sized icons. -->
-    <dimen name="infobar_small_icon_size">24dp</dimen>
-    <dimen name="infobar_small_icon_margin">8dp</dimen>
-    <dimen name="infobar_big_icon_size">48dp</dimen>
-    <dimen name="infobar_big_icon_margin">16dp</dimen>
-
     <!-- Dimension applied to SyncErrorInfoBar icon. -->
     <dimen name="sync_error_infobar_icon_size">48dp</dimen>
 
-    <!-- Dimensions for compact infobars are a little shorter. -->
-    <dimen name="infobar_compact_size">56dp</dimen>
-
     <!-- Dimensions for compact translate infobar. -->
     <dimen name="infobar_translate_fade_edge_length">18dp</dimen>
     <dimen name="infobar_translate_menu_width">260dp</dimen>
@@ -594,9 +573,6 @@
     <dimen name="revamped_context_menu_header_circle_bg_vertical_margin">22dp</dimen>
     <dimen name="revamped_context_menu_header_monogram_text_size">16dp</dimen>
     <dimen name="revamped_context_menu_header_monogram_size">26dp</dimen>
-    <!-- Reader Mode dimensions -->
-    <!-- Padding surrounding the message. -->
-    <dimen name="reader_mode_infobar_text_padding">8dp</dimen>
 
     <!-- Defaults for TabbedModeFirstRunActivity values. -->
     <item type="dimen" name="dialog_fixed_width_major">100%</item>
diff --git a/chrome/android/java/res/values/ids.xml b/chrome/android/java/res/values/ids.xml
index 6bec84457..df6623a 100644
--- a/chrome/android/java/res/values/ids.xml
+++ b/chrome/android/java/res/values/ids.xml
@@ -23,8 +23,6 @@
     <item type="id" name="share_tab_group" />
 
     <!-- InfoBar constants -->
-    <item type="id" name="infobar_icon" />
-    <item type="id" name="infobar_close_button" />
     <item type="id" name="infobar_extra_check" />
     <item type="id" name="infobar_message" />
     <item type="id" name="permission_infobar_persist_toggle" />
diff --git a/chrome/android/java/res/xml/accessibility_preferences.xml b/chrome/android/java/res/xml/accessibility_preferences.xml
index c75bb0e..54d6d560 100644
--- a/chrome/android/java/res/xml/accessibility_preferences.xml
+++ b/chrome/android/java/res/xml/accessibility_preferences.xml
@@ -5,7 +5,7 @@
 
 <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
 
-    <org.chromium.chrome.browser.settings.accessibility.TextScalePreference
+    <org.chromium.chrome.browser.accessibility.settings.TextScalePreference
         android:key="text_scale"
         android:title="@string/font_size"
         android:selectable="false" />
diff --git a/chrome/android/java/res/xml/download_preferences.xml b/chrome/android/java/res/xml/download_preferences.xml
index 516ece6..a721add 100644
--- a/chrome/android/java/res/xml/download_preferences.xml
+++ b/chrome/android/java/res/xml/download_preferences.xml
@@ -4,7 +4,7 @@
      found in the LICENSE file. -->
 
 <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
-    <org.chromium.chrome.browser.settings.download.DownloadLocationPreference
+    <org.chromium.chrome.browser.download.settings.DownloadLocationPreference
         android:key="location_change"
         android:title="@string/downloads_location_selector_title"
         android:positiveButtonText="@string/done"
diff --git a/chrome/android/java/res/xml/main_preferences.xml b/chrome/android/java/res/xml/main_preferences.xml
index ca85481..b792065e 100644
--- a/chrome/android/java/res/xml/main_preferences.xml
+++ b/chrome/android/java/res/xml/main_preferences.xml
@@ -66,12 +66,12 @@
         android:order="11"
         android:title="@string/prefs_section_advanced"/>
     <Preference
-        android:fragment="org.chromium.chrome.browser.settings.privacy.PrivacySettings"
+        android:fragment="org.chromium.chrome.browser.privacy.settings.PrivacySettings"
         android:key="privacy"
         android:order="12"
         android:title="@string/prefs_privacy"/>
     <Preference
-        android:fragment="org.chromium.chrome.browser.settings.accessibility.AccessibilitySettings"
+        android:fragment="org.chromium.chrome.browser.accessibility.settings.AccessibilitySettings"
         android:key="accessibility"
         android:order="13"
         android:title="@string/prefs_accessibility"/>
@@ -91,7 +91,7 @@
         android:order="16"
         android:title="@string/data_reduction_title_lite_mode"/>
     <org.chromium.chrome.browser.settings.ChromeBasePreference
-        android:fragment="org.chromium.chrome.browser.settings.download.DownloadSettings"
+        android:fragment="org.chromium.chrome.browser.download.settings.DownloadSettings"
         android:key="downloads"
         android:order="17"
         android:title="@string/menu_downloads"/>
diff --git a/chrome/android/java/res/xml/privacy_preferences.xml b/chrome/android/java/res/xml/privacy_preferences.xml
index 975fa6d..27b29bc 100644
--- a/chrome/android/java/res/xml/privacy_preferences.xml
+++ b/chrome/android/java/res/xml/privacy_preferences.xml
@@ -20,7 +20,7 @@
         android:title="@string/usage_stats_setting_title"
         android:persistent="false" />
     <Preference
-        android:fragment="org.chromium.chrome.browser.settings.privacy.DoNotTrackSettings"
+        android:fragment="org.chromium.chrome.browser.privacy.settings.DoNotTrackSettings"
         android:key="do_not_track"
         android:title="@string/do_not_track_title" />
     <Preference
diff --git a/chrome/android/java/res/xml/website_preferences.xml b/chrome/android/java/res/xml/website_preferences.xml
index 5c06cc6..a94b2d7 100644
--- a/chrome/android/java/res/xml/website_preferences.xml
+++ b/chrome/android/java/res/xml/website_preferences.xml
@@ -8,6 +8,11 @@
     xmlns:app="http://schemas.android.com/apk/res-auto"
     android:title="@string/all_sites">
 
+    <!-- A text message describing cookie settings. -->
+    <org.chromium.chrome.browser.settings.TextMessagePreference
+        android:key="cookie_info_text"
+        android:summary="@string/website_settings_cookie_info"/>
+
     <!-- A common binary toggle, only shown for specific categories that allow
          turning default values for that category on/off.-->
     <org.chromium.chrome.browser.settings.ChromeSwitchPreference
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
index fbc42e6b..c3fc447 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
@@ -437,9 +437,10 @@
                         getTabModelSelector(), getControlContainerHeightResource());
             }
 
-            ((BottomContainer) findViewById(R.id.bottom_container))
-                    .initialize(mFullscreenManager,
-                            mManualFillingComponent.getKeyboardExtensionViewResizer());
+            BottomContainer bottomContainer = (BottomContainer) findViewById(R.id.bottom_container);
+            bottomContainer.initialize(
+                    mFullscreenManager, getWindowAndroid().getApplicationBottomInsetProvider());
+            getLifecycleDispatcher().register(bottomContainer);
 
             // Should be called after TabModels are initialized.
             ShareDelegate shareDelegate =
@@ -1338,8 +1339,6 @@
         mManualFillingComponent.initialize(getWindowAndroid(),
                 findViewById(R.id.keyboard_accessory_stub),
                 findViewById(R.id.keyboard_accessory_sheet_stub));
-        getCompositorViewHolder().addCompositorViewResizer(
-                mManualFillingComponent.getKeyboardExtensionViewResizer());
 
         if (EphemeralTabCoordinator.isSupported()) {
             mEphemeralTabCoordinator = new EphemeralTabCoordinator(this, getWindowAndroid(),
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeCachedFlags.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeCachedFlags.java
index d371dfd..09e8bd4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeCachedFlags.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeCachedFlags.java
@@ -9,6 +9,7 @@
 import org.chromium.chrome.browser.flags.CachedFeatureFlags;
 import org.chromium.chrome.browser.flags.CachedFieldTrialParameter;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
+import org.chromium.chrome.browser.tab.TabFeatureUtilities;
 import org.chromium.chrome.browser.tasks.tab_management.TabUiFeatureUtilities;
 import org.chromium.chrome.browser.toolbar.bottom.BottomToolbarVariationManager;
 import org.chromium.chrome.features.start_surface.StartSurfaceConfiguration;
@@ -40,24 +41,42 @@
         if (mIsFinishedCachingNativeFlags) return;
         FirstRunUtils.cacheFirstRunPrefs();
 
-        List<String> featuresToCache = Arrays.asList(ChromeFeatureList.HOMEPAGE_LOCATION_POLICY,
-                ChromeFeatureList.COMMAND_LINE_ON_NON_ROOTED, ChromeFeatureList.CHROME_DUET,
-                ChromeFeatureList.CHROME_DUET_ADAPTIVE, ChromeFeatureList.CHROME_DUET_LABELED,
+        // clang-format off
+        List<String> featuresToCache = Arrays.asList(
+
+                ChromeFeatureList.COMMAND_LINE_ON_NON_ROOTED,
+                ChromeFeatureList.CHROME_DUET,
+                ChromeFeatureList.CHROME_DUET_ADAPTIVE,
+                ChromeFeatureList.CHROME_DUET_LABELED,
+                ChromeFeatureList.CLOSE_TAB_SUGGESTIONS,
                 ChromeFeatureList.DOWNLOADS_AUTO_RESUMPTION_NATIVE,
-                ChromeFeatureList.PRIORITIZE_BOOTSTRAP_TASKS,
+                ChromeFeatureList.HOMEPAGE_LOCATION_POLICY,
                 ChromeFeatureList.INTEREST_FEED_CONTENT_SUGGESTIONS,
                 ChromeFeatureList.IMMERSIVE_UI_MODE,
+                ChromeFeatureList.PAINT_PREVIEW_TEST,
+                ChromeFeatureList.PRIORITIZE_BOOTSTRAP_TASKS,
+                ChromeFeatureList.INSTANT_START,
+                ChromeFeatureList.SHARE_BUTTON_IN_TOP_TOOLBAR,
+                ChromeFeatureList.START_SURFACE_ANDROID,
                 ChromeFeatureList.SWAP_PIXEL_FORMAT_TO_FIX_CONVERT_FROM_TRANSLUCENT,
-                ChromeFeatureList.START_SURFACE_ANDROID, ChromeFeatureList.PAINT_PREVIEW_TEST,
-                ChromeFeatureList.SHARE_BUTTON_IN_TOP_TOOLBAR);
+                ChromeFeatureList.TAB_GROUPS_CONTINUATION_ANDROID,
+                ChromeFeatureList.TAB_TO_GTS_ANIMATION);
+        // clang-format on
         CachedFeatureFlags.cacheNativeFlags(featuresToCache);
         CachedFeatureFlags.cacheNativeFlags(TabUiFeatureUtilities.getFeaturesToCache());
         CachedFeatureFlags.cacheAdditionalNativeFlags();
 
-        List<CachedFieldTrialParameter> fieldTrialsToCache =
-                Arrays.asList(BottomToolbarVariationManager.BOTTOM_TOOLBAR_VARIATION,
-                        StartSurfaceConfiguration.START_SURFACE_VARIATION,
-                        TabContentManager.ALLOW_TO_REFETCH_TAB_THUMBNAIL_VARIATION);
+        // clang-format off
+        List<CachedFieldTrialParameter> fieldTrialsToCache = Arrays.asList(
+                BottomToolbarVariationManager.BOTTOM_TOOLBAR_VARIATION,
+                StartSurfaceConfiguration.START_SURFACE_VARIATION,
+                TabContentManager.ALLOW_TO_REFETCH_TAB_THUMBNAIL_VARIATION,
+                TabFeatureUtilities.ZOOMING_MIN_MEMORY,
+                TabFeatureUtilities.ZOOMING_MIN_SDK,
+                TabUiFeatureUtilities.SKIP_SLOW_ZOOMING,
+                TabUiFeatureUtilities.TAB_GRID_LAYOUT_ANDROID_NEW_TAB_TILE,
+                TabUiFeatureUtilities.THUMBNAIL_ASPECT_RATIO);
+        // clang-format on
         CachedFeatureFlags.cacheFieldTrialParameters(fieldTrialsToCache);
 
         mIsFinishedCachingNativeFlags = true;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/MonochromeApplication.java b/chrome/android/java/src/org/chromium/chrome/browser/MonochromeApplication.java
index 47df55f..6271ae3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/MonochromeApplication.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/MonochromeApplication.java
@@ -49,8 +49,7 @@
     @Override
     public void onCreate() {
         super.onCreate();
-        // TODO(https://crbug.com/1002589): remove beta channel check when approved
-        if (!ChromeVersionInfo.isStableBuild() && !ChromeVersionInfo.isBetaBuild()) {
+        if (!ChromeVersionInfo.isStableBuild()) {
             // Performing Monochrome WebView DevTools Launcher icon showing/hiding logic in onCreate
             // rather than in attachBaseContext() because it depends on application context being
             // initiatied.
@@ -63,7 +62,7 @@
                 // accordingly by listening to Monochrome browser Activities status (whenever a
                 // browser activity comes to the foreground).
                 ApplicationStatus.registerStateListenerForAllActivities((activity, state) -> {
-                    if (state == ActivityState.RESUMED) {
+                    if (state == ActivityState.STARTED) {
                         WebViewApkApplication.postDeveloperUiLauncherIconTask();
                     }
                 });
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/settings/accessibility/AccessibilitySettings.java b/chrome/android/java/src/org/chromium/chrome/browser/accessibility/settings/AccessibilitySettings.java
similarity index 98%
rename from chrome/android/java/src/org/chromium/chrome/browser/settings/accessibility/AccessibilitySettings.java
rename to chrome/android/java/src/org/chromium/chrome/browser/accessibility/settings/AccessibilitySettings.java
index fd479e8..b14b42b2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/settings/accessibility/AccessibilitySettings.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/accessibility/settings/AccessibilitySettings.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.chrome.browser.settings.accessibility;
+package org.chromium.chrome.browser.accessibility.settings;
 
 import android.content.Intent;
 import android.os.Bundle;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/accessibility/settings/OWNERS b/chrome/android/java/src/org/chromium/chrome/browser/accessibility/settings/OWNERS
new file mode 100644
index 0000000..623ac6f
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/accessibility/settings/OWNERS
@@ -0,0 +1,4 @@
+chouinard@chromium.org
+twellington@chromium.org
+
+# COMPONENT: UI>Browser>Mobile>Settings
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/settings/accessibility/TextScalePreference.java b/chrome/android/java/src/org/chromium/chrome/browser/accessibility/settings/TextScalePreference.java
similarity index 98%
rename from chrome/android/java/src/org/chromium/chrome/browser/settings/accessibility/TextScalePreference.java
rename to chrome/android/java/src/org/chromium/chrome/browser/accessibility/settings/TextScalePreference.java
index d13e930..cd25ede 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/settings/accessibility/TextScalePreference.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/accessibility/settings/TextScalePreference.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.chrome.browser.settings.accessibility;
+package org.chromium.chrome.browser.accessibility.settings;
 
 import android.content.Context;
 import android.support.v7.preference.Preference;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorViewHolder.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorViewHolder.java
index e089a0d..590dfa4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorViewHolder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorViewHolder.java
@@ -74,9 +74,7 @@
 import org.chromium.ui.resources.dynamics.DynamicResourceLoader;
 
 import java.util.ArrayList;
-import java.util.HashSet;
 import java.util.List;
-import java.util.Set;
 
 /**
  * This class holds a {@link CompositorView}. This level of indirection is needed to benefit from
@@ -88,8 +86,7 @@
 public class CompositorViewHolder extends FrameLayout
         implements ContentOffsetProvider, LayoutManagerHost, LayoutRenderHost, Invalidator.Host,
                    FullscreenListener, InsetObserverView.WindowInsetObserver,
-                   CompositorViewResizer.Observer, AccessibilityUtil.Observer,
-                   TabObscuringHandler.Observer {
+                   AccessibilityUtil.Observer, TabObscuringHandler.Observer {
     private static final long SYSTEM_UI_VIEWPORT_UPDATE_DELAY_MS = 500;
 
     /**
@@ -143,7 +140,6 @@
     /** The toolbar control container. **/
     private ControlContainer mControlContainer;
 
-    private Set<CompositorViewResizer> mViewResizers = new HashSet<>();
     private InsetObserverView mInsetObserverView;
     private boolean mShowingFullscreen;
     private Runnable mSystemUiFullscreenResizeRunnable;
@@ -486,33 +482,6 @@
     public void onSafeAreaChanged(Rect area) {}
 
     /**
-     * Add a {@link CompositorViewResizer} whose height should be taken into account when computing
-     * the size of the content area. Registers an observer to react to size changes immediately.
-     * @param viewResizer A {@link CompositorViewResizer}.
-     */
-    public void addCompositorViewResizer(CompositorViewResizer viewResizer) {
-        mViewResizers.add(viewResizer);
-        viewResizer.addObserver(this);
-        onViewportChanged();
-    }
-
-    /**
-     * Remove a {@link CompositorViewResizer} that was previously added via {@link
-     * #addCompositorViewResizer(CompositorViewResizer)}.
-     * @param viewResizer A {@link CompositorViewResizer}.
-     */
-    public void removeCompositorViewResizer(CompositorViewResizer viewResizer) {
-        viewResizer.removeObserver(this);
-        mViewResizers.remove(viewResizer);
-        onViewportChanged();
-    }
-
-    @Override
-    public void onHeightChanged(int height) {
-        onUpdateViewportSize();
-    }
-
-    /**
      * Should be called for cleanup when the CompositorView instance is no longer used.
      */
     public void shutDown() {
@@ -743,19 +712,6 @@
                 ? getTopControlsHeightPixels() + getBottomControlsHeightPixels()
                 : 0;
 
-        int resizerHeight = 0;
-        for (CompositorViewResizer viewResizer : mViewResizers) {
-            int resizerImplHeight = viewResizer.getHeight();
-            if (resizerImplHeight != 0) {
-                if (resizerHeight != 0) {
-                    throw new IllegalStateException(
-                            "Multiple CompositorViewResizer with height > 0 are not supported.");
-                }
-                resizerHeight = resizerImplHeight;
-            }
-        }
-        controlsHeight += resizerHeight;
-
         if (isAttachedToWindow(view)) {
             webContents.setSize(w, h - controlsHeight);
         } else {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorViewResizer.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorViewResizer.java
deleted file mode 100644
index e7cc23f..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorViewResizer.java
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.compositor;
-
-import androidx.annotation.Px;
-
-/**
- * This class holds the size of any component shown at the bottom of the screen. The height can be
- * used to either compute an offset for bottom bars (e.g. CCTs or PWAs) or to push up the content
- * area.
- */
-public interface CompositorViewResizer {
-    /**
-     * Observers are notified when the size of the component changes.
-     */
-    interface Observer {
-        /**
-         * Called when the component height changes.
-         * @param height The new height of the component.
-         */
-        void onHeightChanged(@Px int height);
-    }
-
-    /**
-     * Returns the height of the component.
-     * @return A height in pixels.
-     */
-    @Px
-    int getHeight();
-
-    /**
-     * Registered observers are called whenever the components size changes until unregistered.
-     * Does not guarantee order.
-     * @param observer a {@link CompositorViewResizer.Observer}.
-     */
-    void addObserver(Observer observer);
-
-    /**
-     * Removes a registered observer if present.
-     * @param observer a registered {@link CompositorViewResizer.Observer}.
-     */
-    void removeObserver(Observer observer);
-}
\ No newline at end of file
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchPolicy.java b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchPolicy.java
index 9e3ad366..4edc339 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchPolicy.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchPolicy.java
@@ -23,7 +23,7 @@
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
 import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
-import org.chromium.chrome.browser.settings.privacy.PrivacyPreferencesManager;
+import org.chromium.chrome.browser.privacy.settings.PrivacyPreferencesManager;
 import org.chromium.components.embedder_support.util.UrlConstants;
 
 import java.net.URL;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/crash/ChromeMinidumpUploaderDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/crash/ChromeMinidumpUploaderDelegate.java
index a2bae4d..ee612a9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/crash/ChromeMinidumpUploaderDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/crash/ChromeMinidumpUploaderDelegate.java
@@ -9,7 +9,7 @@
 import android.os.Build;
 import android.os.PersistableBundle;
 
-import org.chromium.chrome.browser.settings.privacy.PrivacyPreferencesManager;
+import org.chromium.chrome.browser.privacy.settings.PrivacyPreferencesManager;
 import org.chromium.components.minidump_uploader.MinidumpUploaderDelegate;
 import org.chromium.components.minidump_uploader.util.CrashReportingPermissionManager;
 import org.chromium.components.minidump_uploader.util.NetworkPermissionUtil;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/crash/MinidumpUploadService.java b/chrome/android/java/src/org/chromium/chrome/browser/crash/MinidumpUploadService.java
index 3302eba..db9a0a2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/crash/MinidumpUploadService.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/crash/MinidumpUploadService.java
@@ -20,7 +20,7 @@
 import org.chromium.base.StreamUtil;
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.metrics.RecordHistogram;
-import org.chromium.chrome.browser.settings.privacy.PrivacyPreferencesManager;
+import org.chromium.chrome.browser.privacy.settings.PrivacyPreferencesManager;
 import org.chromium.components.background_task_scheduler.TaskIds;
 import org.chromium.components.minidump_uploader.CrashFileManager;
 import org.chromium.components.minidump_uploader.MinidumpUploadCallable;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabBottomBarDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabBottomBarDelegate.java
index 660e2d92..47ce61f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabBottomBarDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabBottomBarDelegate.java
@@ -355,12 +355,10 @@
     @Override
     public void onUpdateViewportSize() {
         if (mBottomBarView == null) return; // Check bottom bar view but don't inflate it.
-        // Hide the container of the bottom bar while the extension is showing. This doesn't
-        // affect the content.
-        boolean keyboardExtensionHidesBottomBar =
-                mActivity.getManualFillingComponent().getKeyboardExtensionViewResizer().getHeight()
-                > 0;
-        hideBottomBar(keyboardExtensionHidesBottomBar);
+
+        // Hide the container of the bottom bar while there is another feature shrinking the
+        // viewport of the web content.
+        hideBottomBar(mActivity.getWindowAndroid().getApplicationBottomInsetProvider().get() > 0);
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsConnection.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsConnection.java
index 14ca94a..5bfac73 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsConnection.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsConnection.java
@@ -63,8 +63,8 @@
 import org.chromium.chrome.browser.net.spdyproxy.DataReductionProxySettings;
 import org.chromium.chrome.browser.preferences.Pref;
 import org.chromium.chrome.browser.preferences.PrefServiceBridge;
+import org.chromium.chrome.browser.privacy.settings.PrivacyPreferencesManager;
 import org.chromium.chrome.browser.profiles.Profile;
-import org.chromium.chrome.browser.settings.privacy.PrivacyPreferencesManager;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.components.embedder_support.util.UrlConstants;
 import org.chromium.content_public.browser.BrowserStartupController;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadLocationCustomView.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadLocationCustomView.java
index 13b6e1fc..7d73ea9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadLocationCustomView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadLocationCustomView.java
@@ -4,7 +4,7 @@
 
 package org.chromium.chrome.browser.download;
 
-import static org.chromium.chrome.browser.settings.download.DownloadDirectoryAdapter.NO_SELECTED_ITEM_ID;
+import static org.chromium.chrome.browser.download.settings.DownloadDirectoryAdapter.NO_SELECTED_ITEM_ID;
 
 import android.content.Context;
 import android.util.AttributeSet;
@@ -18,7 +18,7 @@
 
 import androidx.annotation.Nullable;
 
-import org.chromium.chrome.browser.settings.download.DownloadDirectoryAdapter;
+import org.chromium.chrome.browser.download.settings.DownloadDirectoryAdapter;
 import org.chromium.chrome.download.R;
 import org.chromium.components.browser_ui.widget.text.AlertDialogEditText;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/DownloadManagerCoordinatorFactory.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/DownloadManagerCoordinatorFactory.java
index 39d9a5c..1bff4db 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/DownloadManagerCoordinatorFactory.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/DownloadManagerCoordinatorFactory.java
@@ -9,10 +9,10 @@
 
 import org.chromium.chrome.browser.GlobalDiscardableReferencePool;
 import org.chromium.chrome.browser.download.items.OfflineContentAggregatorFactory;
+import org.chromium.chrome.browser.download.settings.DownloadSettings;
 import org.chromium.chrome.browser.feature_engagement.TrackerFactory;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.settings.SettingsLauncher;
-import org.chromium.chrome.browser.settings.download.DownloadSettings;
 import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager;
 import org.chromium.ui.modaldialog.ModalDialogManager;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/settings/download/DownloadDirectoryAdapter.java b/chrome/android/java/src/org/chromium/chrome/browser/download/settings/DownloadDirectoryAdapter.java
similarity index 95%
rename from chrome/android/java/src/org/chromium/chrome/browser/settings/download/DownloadDirectoryAdapter.java
rename to chrome/android/java/src/org/chromium/chrome/browser/download/settings/DownloadDirectoryAdapter.java
index 9d9a7ad..ad4b723 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/settings/download/DownloadDirectoryAdapter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/settings/DownloadDirectoryAdapter.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.chrome.browser.settings.download;
+package org.chromium.chrome.browser.download.settings;
 
 import android.content.Context;
 import android.view.LayoutInflater;
@@ -233,11 +233,11 @@
                     break;
                 case DirectoryOption.DownloadLocationDirectoryType.ADDITIONAL:
                     String directoryName = (numOtherAdditionalDirectories > 0)
-                            ? mContext.getString(org.chromium.chrome.R.string
-                                                         .downloads_location_sd_card_number,
-                                      numOtherAdditionalDirectories + 1)
+                            ? mContext.getString(
+                                    org.chromium.chrome.R.string.downloads_location_sd_card_number,
+                                    numOtherAdditionalDirectories + 1)
                             : mContext.getString(
-                                      org.chromium.chrome.R.string.downloads_location_sd_card);
+                                    org.chromium.chrome.R.string.downloads_location_sd_card);
                     directory.name = directoryName;
                     mAdditionalOptions.add(directory);
                     numOtherAdditionalDirectories++;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/settings/download/DownloadLocationPreference.java b/chrome/android/java/src/org/chromium/chrome/browser/download/settings/DownloadLocationPreference.java
similarity index 97%
rename from chrome/android/java/src/org/chromium/chrome/browser/settings/download/DownloadLocationPreference.java
rename to chrome/android/java/src/org/chromium/chrome/browser/download/settings/DownloadLocationPreference.java
index 2cb83d95..a976875 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/settings/download/DownloadLocationPreference.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/settings/DownloadLocationPreference.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.chrome.browser.settings.download;
+package org.chromium.chrome.browser.download.settings;
 
 import android.content.Context;
 import android.support.v7.preference.DialogPreference;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/settings/download/DownloadLocationPreferenceAdapter.java b/chrome/android/java/src/org/chromium/chrome/browser/download/settings/DownloadLocationPreferenceAdapter.java
similarity index 98%
rename from chrome/android/java/src/org/chromium/chrome/browser/settings/download/DownloadLocationPreferenceAdapter.java
rename to chrome/android/java/src/org/chromium/chrome/browser/download/settings/DownloadLocationPreferenceAdapter.java
index 1aa8279..4195bea 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/settings/download/DownloadLocationPreferenceAdapter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/settings/DownloadLocationPreferenceAdapter.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.chrome.browser.settings.download;
+package org.chromium.chrome.browser.download.settings;
 
 import android.content.Context;
 import android.view.LayoutInflater;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/settings/download/DownloadLocationPreferenceDialog.java b/chrome/android/java/src/org/chromium/chrome/browser/download/settings/DownloadLocationPreferenceDialog.java
similarity index 96%
rename from chrome/android/java/src/org/chromium/chrome/browser/settings/download/DownloadLocationPreferenceDialog.java
rename to chrome/android/java/src/org/chromium/chrome/browser/download/settings/DownloadLocationPreferenceDialog.java
index 93a6571..fa045df 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/settings/download/DownloadLocationPreferenceDialog.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/settings/DownloadLocationPreferenceDialog.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.chrome.browser.settings.download;
+package org.chromium.chrome.browser.download.settings;
 
 import android.os.Bundle;
 import android.support.v7.preference.PreferenceDialogFragmentCompat;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/settings/download/DownloadSettings.java b/chrome/android/java/src/org/chromium/chrome/browser/download/settings/DownloadSettings.java
similarity index 98%
rename from chrome/android/java/src/org/chromium/chrome/browser/settings/download/DownloadSettings.java
rename to chrome/android/java/src/org/chromium/chrome/browser/download/settings/DownloadSettings.java
index 26209a52..68a936d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/settings/download/DownloadSettings.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/settings/DownloadSettings.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.chrome.browser.settings.download;
+package org.chromium.chrome.browser.download.settings;
 
 import android.os.Bundle;
 import android.support.v7.preference.Preference;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunFlowSequencer.java b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunFlowSequencer.java
index 27eed4f..d96afd8fa 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunFlowSequencer.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunFlowSequencer.java
@@ -24,8 +24,8 @@
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.browser.locale.LocaleManager;
 import org.chromium.chrome.browser.net.spdyproxy.DataReductionProxySettings;
+import org.chromium.chrome.browser.privacy.settings.PrivacyPreferencesManager;
 import org.chromium.chrome.browser.services.AndroidChildAccountHelper;
-import org.chromium.chrome.browser.settings.privacy.PrivacyPreferencesManager;
 import org.chromium.chrome.browser.signin.IdentityServicesProvider;
 import org.chromium.chrome.browser.signin.SigninManager;
 import org.chromium.chrome.browser.toolbar.bottom.BottomToolbarVariationManager;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/infobar/AdsBlockedInfoBar.java b/chrome/android/java/src/org/chromium/chrome/browser/infobar/AdsBlockedInfoBar.java
index 9eaf36b..6562f93 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/infobar/AdsBlockedInfoBar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/infobar/AdsBlockedInfoBar.java
@@ -15,6 +15,7 @@
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ui.messages.infobar.InfoBarControlLayout;
+import org.chromium.chrome.browser.ui.messages.infobar.InfoBarLayout;
 import org.chromium.ui.text.NoUnderlineClickableSpan;
 import org.chromium.ui.widget.ButtonCompat;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/infobar/AutofillCreditCardFillingInfoBar.java b/chrome/android/java/src/org/chromium/chrome/browser/infobar/AutofillCreditCardFillingInfoBar.java
index 8a6b5d22..4d9137a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/infobar/AutofillCreditCardFillingInfoBar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/infobar/AutofillCreditCardFillingInfoBar.java
@@ -9,6 +9,7 @@
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ui.messages.infobar.InfoBarControlLayout;
+import org.chromium.chrome.browser.ui.messages.infobar.InfoBarLayout;
 
 import java.util.ArrayList;
 import java.util.List;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/infobar/AutofillSaveCardInfoBar.java b/chrome/android/java/src/org/chromium/chrome/browser/infobar/AutofillSaveCardInfoBar.java
index 1227436..b73603a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/infobar/AutofillSaveCardInfoBar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/infobar/AutofillSaveCardInfoBar.java
@@ -15,6 +15,7 @@
 import org.chromium.base.annotations.NativeMethods;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ui.messages.infobar.InfoBarControlLayout;
+import org.chromium.chrome.browser.ui.messages.infobar.InfoBarLayout;
 import org.chromium.ui.UiUtils;
 
 import java.util.ArrayList;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/infobar/ConfirmInfoBar.java b/chrome/android/java/src/org/chromium/chrome/browser/infobar/ConfirmInfoBar.java
index cd185d5..b231105 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/infobar/ConfirmInfoBar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/infobar/ConfirmInfoBar.java
@@ -9,6 +9,7 @@
 import androidx.annotation.ColorRes;
 
 import org.chromium.base.annotations.CalledByNative;
+import org.chromium.chrome.browser.ui.messages.infobar.InfoBarLayout;
 
 /**
  * An infobar that presents the user with several buttons.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/infobar/DataReductionPromoInfoBar.java b/chrome/android/java/src/org/chromium/chrome/browser/infobar/DataReductionPromoInfoBar.java
index 1243a5d..2893b88 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/infobar/DataReductionPromoInfoBar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/infobar/DataReductionPromoInfoBar.java
@@ -18,6 +18,7 @@
 import org.chromium.chrome.browser.datareduction.DataReductionPromoUtils;
 import org.chromium.chrome.browser.omaha.VersionNumberGetter;
 import org.chromium.chrome.browser.ui.messages.infobar.InfoBarControlLayout;
+import org.chromium.chrome.browser.ui.messages.infobar.InfoBarLayout;
 import org.chromium.components.embedder_support.util.UrlConstants;
 import org.chromium.content_public.browser.WebContents;
 import org.chromium.net.GURLUtils;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/infobar/DownloadProgressInfoBar.java b/chrome/android/java/src/org/chromium/chrome/browser/infobar/DownloadProgressInfoBar.java
index d3fb9c2..080898e7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/infobar/DownloadProgressInfoBar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/infobar/DownloadProgressInfoBar.java
@@ -20,6 +20,7 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.download.DownloadInfoBarController;
 import org.chromium.chrome.browser.tab.Tab;
+import org.chromium.chrome.browser.ui.messages.infobar.InfoBarLayout;
 import org.chromium.components.offline_items_collection.ContentId;
 import org.chromium.content_public.browser.UiThreadTaskTraits;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/infobar/DuplicateDownloadInfoBar.java b/chrome/android/java/src/org/chromium/chrome/browser/infobar/DuplicateDownloadInfoBar.java
index 2c5e6cc7..84610c7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/infobar/DuplicateDownloadInfoBar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/infobar/DuplicateDownloadInfoBar.java
@@ -23,6 +23,7 @@
 import org.chromium.chrome.browser.download.DownloadManagerService;
 import org.chromium.chrome.browser.download.DownloadOpenSource;
 import org.chromium.chrome.browser.download.DownloadUtils;
+import org.chromium.chrome.browser.ui.messages.infobar.InfoBarLayout;
 import org.chromium.components.download.DownloadCollectionBridge;
 
 import java.io.File;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/infobar/FramebustBlockInfoBar.java b/chrome/android/java/src/org/chromium/chrome/browser/infobar/FramebustBlockInfoBar.java
index bb60181..036c250 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/infobar/FramebustBlockInfoBar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/infobar/FramebustBlockInfoBar.java
@@ -14,7 +14,9 @@
 
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.chrome.R;
+import org.chromium.chrome.browser.ui.messages.infobar.InfoBarCompactLayout;
 import org.chromium.chrome.browser.ui.messages.infobar.InfoBarControlLayout;
+import org.chromium.chrome.browser.ui.messages.infobar.InfoBarLayout;
 import org.chromium.components.url_formatter.UrlFormatter;
 
 /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/infobar/GeneratedPasswordSavedInfoBar.java b/chrome/android/java/src/org/chromium/chrome/browser/infobar/GeneratedPasswordSavedInfoBar.java
index 9f6f7e6..b7d322f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/infobar/GeneratedPasswordSavedInfoBar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/infobar/GeneratedPasswordSavedInfoBar.java
@@ -9,6 +9,7 @@
 
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ui.messages.infobar.InfoBarControlLayout;
+import org.chromium.chrome.browser.ui.messages.infobar.InfoBarLayout;
 import org.chromium.ui.text.NoUnderlineClickableSpan;
 
 /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/infobar/InfoBar.java b/chrome/android/java/src/org/chromium/chrome/browser/infobar/InfoBar.java
index 8fa112d..9ea8a61a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/infobar/InfoBar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/infobar/InfoBar.java
@@ -15,6 +15,9 @@
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.NativeMethods;
 import org.chromium.chrome.R;
+import org.chromium.chrome.browser.ui.messages.infobar.InfoBarCompactLayout;
+import org.chromium.chrome.browser.ui.messages.infobar.InfoBarInteractionHandler;
+import org.chromium.chrome.browser.ui.messages.infobar.InfoBarLayout;
 import org.chromium.chrome.browser.ui.messages.infobar.InfoBarUiItem;
 import org.chromium.ui.modelutil.PropertyModel;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/infobar/InfoBarContainerLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/infobar/InfoBarContainerLayout.java
index f48e744..601aabc 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/infobar/InfoBarContainerLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/infobar/InfoBarContainerLayout.java
@@ -110,9 +110,9 @@
     }
 
     /**
-     * Notifies that an infobar's View ({@link InfoBarUiItem#getView}) has changed. If
-     * the infobar is visible in the front of the stack, the infobar will fade out the old contents,
-     * resize, then fade in the new contents.
+     * Notifies that an infobar's View ({@link InfoBarUiItem#getView}) has changed. If the infobar
+     * is visible in the front of the stack, the infobar will fade out the old contents, resize,
+     * then fade in the new contents.
      */
     void notifyInfoBarViewChanged() {
         processPendingAnimations();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/infobar/InstallableAmbientBadgeInfoBar.java b/chrome/android/java/src/org/chromium/chrome/browser/infobar/InstallableAmbientBadgeInfoBar.java
index ac21445..382444a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/infobar/InstallableAmbientBadgeInfoBar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/infobar/InstallableAmbientBadgeInfoBar.java
@@ -18,6 +18,7 @@
 import org.chromium.base.annotations.NativeMethods;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ShortcutHelper;
+import org.chromium.chrome.browser.ui.messages.infobar.InfoBarCompactLayout;
 import org.chromium.components.browser_ui.widget.text.AccessibleTextView;
 
 /**
@@ -66,7 +67,7 @@
         iconView.setOnClickListener(this);
         iconView.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
         final int messagePadding =
-                res.getDimensionPixelOffset(R.dimen.reader_mode_infobar_text_padding);
+                res.getDimensionPixelOffset(R.dimen.infobar_compact_message_vertical_padding);
         prompt.setPadding(0, messagePadding, 0, messagePadding);
         layout.addContent(prompt, 1f);
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/infobar/InstantAppsInfoBar.java b/chrome/android/java/src/org/chromium/chrome/browser/infobar/InstantAppsInfoBar.java
index 6469160..ee7d2d9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/infobar/InstantAppsInfoBar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/infobar/InstantAppsInfoBar.java
@@ -10,6 +10,7 @@
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.instantapps.InstantAppsBannerData;
+import org.chromium.chrome.browser.ui.messages.infobar.InfoBarLayout;
 import org.chromium.components.browser_ui.widget.DualControlLayout;
 import org.chromium.components.url_formatter.SchemeDisplay;
 import org.chromium.components.url_formatter.UrlFormatter;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/infobar/KnownInterceptionDisclosureInfoBar.java b/chrome/android/java/src/org/chromium/chrome/browser/infobar/KnownInterceptionDisclosureInfoBar.java
index 8d59fc8d97..8b43a1b3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/infobar/KnownInterceptionDisclosureInfoBar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/infobar/KnownInterceptionDisclosureInfoBar.java
@@ -9,6 +9,7 @@
 import androidx.annotation.ColorRes;
 
 import org.chromium.base.annotations.CalledByNative;
+import org.chromium.chrome.browser.ui.messages.infobar.InfoBarLayout;
 
 /**
  * An infobar to disclose known monitoring to the user. This is a thin veneer over
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/infobar/NearOomInfoBar.java b/chrome/android/java/src/org/chromium/chrome/browser/infobar/NearOomInfoBar.java
index 2ced019..55565ae 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/infobar/NearOomInfoBar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/infobar/NearOomInfoBar.java
@@ -8,6 +8,7 @@
 
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.chrome.R;
+import org.chromium.chrome.browser.ui.messages.infobar.InfoBarCompactLayout;
 
 /**
  * This InfoBar is shown to let the user know when the browser took action to stop a page from using
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/infobar/NearOomReductionInfoBar.java b/chrome/android/java/src/org/chromium/chrome/browser/infobar/NearOomReductionInfoBar.java
index 502fdac..0383c7f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/infobar/NearOomReductionInfoBar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/infobar/NearOomReductionInfoBar.java
@@ -8,6 +8,7 @@
 
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.chrome.R;
+import org.chromium.chrome.browser.ui.messages.infobar.InfoBarCompactLayout;
 
 /**
  * This InfoBar is shown to let the user know when the browser took action to
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/infobar/PermissionInfoBar.java b/chrome/android/java/src/org/chromium/chrome/browser/infobar/PermissionInfoBar.java
index 3823241d..5f20ccf3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/infobar/PermissionInfoBar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/infobar/PermissionInfoBar.java
@@ -12,6 +12,8 @@
 import org.chromium.chrome.browser.settings.SettingsLauncher;
 import org.chromium.chrome.browser.site_settings.SingleCategorySettings;
 import org.chromium.chrome.browser.site_settings.SiteSettingsCategory;
+import org.chromium.chrome.browser.ui.messages.infobar.InfoBarCompactLayout;
+import org.chromium.chrome.browser.ui.messages.infobar.InfoBarLayout;
 import org.chromium.components.permissions.AndroidPermissionRequester;
 import org.chromium.ui.base.WindowAndroid;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/infobar/PreviewsInfoBar.java b/chrome/android/java/src/org/chromium/chrome/browser/infobar/PreviewsInfoBar.java
index 189f90ba..19c2bab 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/infobar/PreviewsInfoBar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/infobar/PreviewsInfoBar.java
@@ -6,6 +6,7 @@
 
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.chrome.R;
+import org.chromium.chrome.browser.ui.messages.infobar.InfoBarLayout;
 
 /**
  * An InfoBar that lets the user know that a Preview page has been loaded, and gives the user
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/infobar/ReaderModeInfoBar.java b/chrome/android/java/src/org/chromium/chrome/browser/infobar/ReaderModeInfoBar.java
index ad8c500..2fcc78a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/infobar/ReaderModeInfoBar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/infobar/ReaderModeInfoBar.java
@@ -21,6 +21,7 @@
 import org.chromium.chrome.browser.dom_distiller.ReaderModeManager;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TabImpl;
+import org.chromium.chrome.browser.ui.messages.infobar.InfoBarCompactLayout;
 import org.chromium.components.browser_ui.widget.text.AccessibleTextView;
 
 /**
@@ -74,7 +75,7 @@
         iconView.setOnClickListener(mNavigateListener);
         iconView.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
         final int messagePadding = getContext().getResources().getDimensionPixelOffset(
-                R.dimen.reader_mode_infobar_text_padding);
+                R.dimen.infobar_compact_message_vertical_padding);
         prompt.setPadding(0, messagePadding, 0, messagePadding);
         layout.addContent(prompt, 1f);
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/infobar/SafetyTipInfoBar.java b/chrome/android/java/src/org/chromium/chrome/browser/infobar/SafetyTipInfoBar.java
index 19c9e1ee..2aa69c6 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/infobar/SafetyTipInfoBar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/infobar/SafetyTipInfoBar.java
@@ -9,6 +9,7 @@
 import androidx.annotation.ColorRes;
 
 import org.chromium.base.annotations.CalledByNative;
+import org.chromium.chrome.browser.ui.messages.infobar.InfoBarLayout;
 
 /**
  * An infobar to present a Safety Tip. This is a thin vineer over standard ConfirmInfoBar to provide
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/infobar/SavePasswordInfoBar.java b/chrome/android/java/src/org/chromium/chrome/browser/infobar/SavePasswordInfoBar.java
index 5a2b01f1..b29cc5b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/infobar/SavePasswordInfoBar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/infobar/SavePasswordInfoBar.java
@@ -9,6 +9,7 @@
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ui.messages.infobar.InfoBarControlLayout;
+import org.chromium.chrome.browser.ui.messages.infobar.InfoBarLayout;
 
 /**
  * The Save Password infobar asks the user whether they want to save the password for the site.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/infobar/SearchGeolocationDisclosureInfoBar.java b/chrome/android/java/src/org/chromium/chrome/browser/infobar/SearchGeolocationDisclosureInfoBar.java
index a091d48..9ca2e33 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/infobar/SearchGeolocationDisclosureInfoBar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/infobar/SearchGeolocationDisclosureInfoBar.java
@@ -11,6 +11,7 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.settings.SettingsLauncher;
 import org.chromium.chrome.browser.site_settings.SingleWebsiteSettings;
+import org.chromium.chrome.browser.ui.messages.infobar.InfoBarLayout;
 
 /**
  * An infobar to disclose to the user that the default search engine has geolocation access by
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/infobar/SurveyInfoBar.java b/chrome/android/java/src/org/chromium/chrome/browser/infobar/SurveyInfoBar.java
index 0b665ae..6670c09 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/infobar/SurveyInfoBar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/infobar/SurveyInfoBar.java
@@ -19,6 +19,7 @@
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TabHidingType;
 import org.chromium.chrome.browser.tab.TabImpl;
+import org.chromium.chrome.browser.ui.messages.infobar.InfoBarCompactLayout;
 import org.chromium.chrome.browser.util.AccessibilityUtil;
 import org.chromium.content_public.browser.WebContents;
 import org.chromium.ui.text.NoUnderlineClickableSpan;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/infobar/SyncErrorInfoBar.java b/chrome/android/java/src/org/chromium/chrome/browser/infobar/SyncErrorInfoBar.java
index 0e9b57e..70a153b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/infobar/SyncErrorInfoBar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/infobar/SyncErrorInfoBar.java
@@ -24,6 +24,7 @@
 import org.chromium.chrome.browser.sync.settings.SyncAndServicesSettings;
 import org.chromium.chrome.browser.sync.settings.SyncSettingsUtils;
 import org.chromium.chrome.browser.sync.settings.SyncSettingsUtils.SyncError;
+import org.chromium.chrome.browser.ui.messages.infobar.InfoBarLayout;
 import org.chromium.content_public.browser.WebContents;
 
 import java.lang.annotation.Retention;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/infobar/TranslateCompactInfoBar.java b/chrome/android/java/src/org/chromium/chrome/browser/infobar/TranslateCompactInfoBar.java
index ab420eba..fd1a1ad 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/infobar/TranslateCompactInfoBar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/infobar/TranslateCompactInfoBar.java
@@ -24,6 +24,7 @@
 import org.chromium.chrome.browser.infobar.translate.TranslateTabLayout;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TabImpl;
+import org.chromium.chrome.browser.ui.messages.infobar.InfoBarCompactLayout;
 import org.chromium.chrome.browser.ui.messages.snackbar.Snackbar;
 import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager;
 import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager.SnackbarController;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/infobar/UpdatePasswordInfoBar.java b/chrome/android/java/src/org/chromium/chrome/browser/infobar/UpdatePasswordInfoBar.java
index 2a0b69c7..8d00956 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/infobar/UpdatePasswordInfoBar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/infobar/UpdatePasswordInfoBar.java
@@ -11,6 +11,7 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ui.messages.infobar.InfoBarControlLayout;
 import org.chromium.chrome.browser.ui.messages.infobar.InfoBarControlLayout.InfoBarArrayAdapter;
+import org.chromium.chrome.browser.ui.messages.infobar.InfoBarLayout;
 
 /**
  * The Update Password infobar offers the user the ability to update a password for the site.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/metrics/UmaSessionStats.java b/chrome/android/java/src/org/chromium/chrome/browser/metrics/UmaSessionStats.java
index 67f46b02..812fcf51 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/metrics/UmaSessionStats.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/metrics/UmaSessionStats.java
@@ -19,7 +19,7 @@
 import org.chromium.chrome.browser.DefaultBrowserInfo;
 import org.chromium.chrome.browser.instantapps.InstantAppsHandler;
 import org.chromium.chrome.browser.policy.EnterpriseInfo;
-import org.chromium.chrome.browser.settings.privacy.PrivacyPreferencesManager;
+import org.chromium.chrome.browser.privacy.settings.PrivacyPreferencesManager;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tabmodel.TabModel;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java
index b5b83b81..d8053ee 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java
@@ -55,9 +55,9 @@
 import org.chromium.chrome.browser.omnibox.suggestions.OmniboxSuggestionListEmbedder;
 import org.chromium.chrome.browser.omnibox.voice.AssistantVoiceSearchService;
 import org.chromium.chrome.browser.omnibox.voice.VoiceRecognitionHandler;
+import org.chromium.chrome.browser.privacy.settings.PrivacyPreferencesManager;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.search_engines.TemplateUrlServiceFactory;
-import org.chromium.chrome.browser.settings.privacy.PrivacyPreferencesManager;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tasks.ReturnToChromeExperimentsUtil;
 import org.chromium.chrome.browser.toolbar.ToolbarDataProvider;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/settings/privacy/BandwidthType.java b/chrome/android/java/src/org/chromium/chrome/browser/privacy/settings/BandwidthType.java
similarity index 96%
rename from chrome/android/java/src/org/chromium/chrome/browser/settings/privacy/BandwidthType.java
rename to chrome/android/java/src/org/chromium/chrome/browser/privacy/settings/BandwidthType.java
index 8afd2658..8237c6c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/settings/privacy/BandwidthType.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/privacy/settings/BandwidthType.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.chrome.browser.settings.privacy;
+package org.chromium.chrome.browser.privacy.settings;
 
 import androidx.annotation.IntDef;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/settings/privacy/DoNotTrackSettings.java b/chrome/android/java/src/org/chromium/chrome/browser/privacy/settings/DoNotTrackSettings.java
similarity index 96%
rename from chrome/android/java/src/org/chromium/chrome/browser/settings/privacy/DoNotTrackSettings.java
rename to chrome/android/java/src/org/chromium/chrome/browser/privacy/settings/DoNotTrackSettings.java
index 940f203..67098b4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/settings/privacy/DoNotTrackSettings.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/privacy/settings/DoNotTrackSettings.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.chrome.browser.settings.privacy;
+package org.chromium.chrome.browser.privacy.settings;
 
 import android.os.Bundle;
 import android.support.v7.preference.PreferenceFragmentCompat;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/privacy/settings/OWNERS b/chrome/android/java/src/org/chromium/chrome/browser/privacy/settings/OWNERS
new file mode 100644
index 0000000..f152acf
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/privacy/settings/OWNERS
@@ -0,0 +1,5 @@
+chouinard@chromium.org
+dullweber@chromium.org
+twellington@chromium.org
+
+# COMPONENT: UI>Browser>Mobile>Settings
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/privacy/settings/PrivacyPreferencesManager.java b/chrome/android/java/src/org/chromium/chrome/browser/privacy/settings/PrivacyPreferencesManager.java
new file mode 100644
index 0000000..807a032
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/privacy/settings/PrivacyPreferencesManager.java
@@ -0,0 +1,329 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.privacy.settings;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+
+import androidx.annotation.VisibleForTesting;
+
+import org.chromium.base.CommandLine;
+import org.chromium.base.ContextUtils;
+import org.chromium.base.annotations.NativeMethods;
+import org.chromium.chrome.browser.device.DeviceClassManager;
+import org.chromium.chrome.browser.flags.ChromeSwitches;
+import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
+import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
+import org.chromium.chrome.browser.survey.SurveyController;
+import org.chromium.components.minidump_uploader.util.CrashReportingPermissionManager;
+import org.chromium.components.minidump_uploader.util.NetworkPermissionUtil;
+import org.chromium.content_public.browser.BrowserStartupController;
+
+/**
+ * Reads, writes, and migrates preferences related to network usage and privacy.
+ */
+public class PrivacyPreferencesManager implements CrashReportingPermissionManager {
+    @SuppressLint("StaticFieldLeak")
+    private static PrivacyPreferencesManager sInstance;
+
+    private final Context mContext;
+    private final SharedPreferencesManager mPrefs;
+
+    @VisibleForTesting
+    PrivacyPreferencesManager(Context context) {
+        mContext = context;
+        mPrefs = SharedPreferencesManager.getInstance();
+    }
+
+    public static PrivacyPreferencesManager getInstance() {
+        if (sInstance == null) {
+            sInstance = new PrivacyPreferencesManager(ContextUtils.getApplicationContext());
+        }
+        return sInstance;
+    }
+
+    /**
+     * Migrate and delete old preferences.  Note that migration has to happen in Android-specific
+     * code because we need to access ALLOW_PRERENDER sharedPreference.
+     * TODO(bnc) https://crbug.com/394845. This change is planned for M38. After a year or so, it
+     * would be worth considering removing this migration code and reverting to default for users
+     * who had set preferences but have not used Chrome for a year. This change would be subject to
+     * privacy review.
+     */
+    public void migrateNetworkPredictionPreferences() {
+        // See if PREF_NETWORK_PREDICTIONS is an old boolean value.
+        boolean predictionOptionIsBoolean = false;
+        try {
+            mPrefs.readString(ChromePreferenceKeys.PRIVACY_NETWORK_PREDICTIONS, "");
+        } catch (ClassCastException ex) {
+            predictionOptionIsBoolean = true;
+        }
+
+        // Nothing to do if the user or this migration code has already set the new
+        // preference.
+        if (!predictionOptionIsBoolean && obsoleteNetworkPredictionOptionsHasUserSetting()) {
+            return;
+        }
+
+        // Nothing to do if the old preferences are unset.
+        if (!predictionOptionIsBoolean
+                && !mPrefs.contains(ChromePreferenceKeys.PRIVACY_BANDWIDTH_OLD)
+                && !mPrefs.contains(ChromePreferenceKeys.PRIVACY_BANDWIDTH_NO_CELLULAR_OLD)) {
+            return;
+        }
+
+        // Migrate if the old preferences are at their default values.
+        // (Note that for PREF_BANDWIDTH*, if the setting is default, then there is no way to tell
+        // whether the user has set it.)
+        final String prefBandwidthDefault =
+                BandwidthType.title(BandwidthType.Type.PRERENDER_ON_WIFI);
+        final String prefBandwidth =
+                mPrefs.readString(ChromePreferenceKeys.PRIVACY_BANDWIDTH_OLD, prefBandwidthDefault);
+        boolean prefBandwidthNoCellularDefault = true;
+        boolean prefBandwidthNoCellular =
+                mPrefs.readBoolean(ChromePreferenceKeys.PRIVACY_BANDWIDTH_NO_CELLULAR_OLD,
+                        prefBandwidthNoCellularDefault);
+
+        if (!(prefBandwidthDefault.equals(prefBandwidth))
+                || (prefBandwidthNoCellular != prefBandwidthNoCellularDefault)) {
+            boolean newValue = true;
+            // Observe PREF_BANDWIDTH on mobile network capable devices.
+            if (isMobileNetworkCapable()) {
+                if (mPrefs.contains(ChromePreferenceKeys.PRIVACY_BANDWIDTH_OLD)) {
+                    @BandwidthType.Type
+                    int prefetchBandwidthTypePref =
+                            BandwidthType.getBandwidthFromTitle(prefBandwidth);
+                    if (BandwidthType.Type.NEVER_PRERENDER == prefetchBandwidthTypePref) {
+                        newValue = false;
+                    } else if (BandwidthType.Type.PRERENDER_ON_WIFI == prefetchBandwidthTypePref
+                            || BandwidthType.Type.ALWAYS_PRERENDER == prefetchBandwidthTypePref) {
+                        newValue = true;
+                    }
+                }
+            }
+            // Observe PREF_BANDWIDTH_NO_CELLULAR on devices without mobile network.
+            else {
+                if (mPrefs.contains(ChromePreferenceKeys.PRIVACY_BANDWIDTH_NO_CELLULAR_OLD)) {
+                    newValue = prefBandwidthNoCellular;
+                }
+            }
+            // Save new value in Chrome PrefService.
+            setNetworkPredictionEnabled(newValue);
+        }
+
+        // Delete old sharedPreferences.
+
+        // Delete PREF_BANDWIDTH and PREF_BANDWIDTH_NO_CELLULAR: just migrated these options.
+        if (mPrefs.contains(ChromePreferenceKeys.PRIVACY_BANDWIDTH_OLD)) {
+            mPrefs.removeKey(ChromePreferenceKeys.PRIVACY_BANDWIDTH_OLD);
+        }
+        if (mPrefs.contains(ChromePreferenceKeys.PRIVACY_BANDWIDTH_NO_CELLULAR_OLD)) {
+            mPrefs.removeKey(ChromePreferenceKeys.PRIVACY_BANDWIDTH_NO_CELLULAR_OLD);
+        }
+        // Also delete ALLOW_PRERENDER, which was updated based on PREF_BANDWIDTH[_NO_CELLULAR] and
+        // network connectivity type, therefore does not carry additional information.
+        if (mPrefs.contains(ChromePreferenceKeys.PRIVACY_ALLOW_PRERENDER_OLD)) {
+            mPrefs.removeKey(ChromePreferenceKeys.PRIVACY_ALLOW_PRERENDER_OLD);
+        }
+        // Delete bool PREF_NETWORK_PREDICTIONS so that string values can be stored. Note that this
+        // SharedPreference carries no information, because it used to be overwritten by
+        // kNetworkPredictionEnabled on startup, and now it is overwritten by
+        // kNetworkPredictionOptions on startup.
+        if (mPrefs.contains(ChromePreferenceKeys.PRIVACY_NETWORK_PREDICTIONS)) {
+            mPrefs.removeKey(ChromePreferenceKeys.PRIVACY_NETWORK_PREDICTIONS);
+        }
+    }
+
+    protected boolean isNetworkAvailable() {
+        ConnectivityManager connectivityManager =
+                (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
+        NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
+        return (networkInfo != null && networkInfo.isConnected());
+    }
+
+    protected boolean isMobileNetworkCapable() {
+        ConnectivityManager connectivityManager =
+                (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
+        // Android telephony team said it is OK to continue using getNetworkInfo() for our purposes.
+        // We cannot use ConnectivityManager#getAllNetworks() because that one only reports enabled
+        // networks. See crbug.com/532455.
+        @SuppressWarnings("deprecation")
+        NetworkInfo networkInfo =
+                connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
+        return networkInfo != null;
+    }
+
+    /**
+     * Checks whether prerender should be allowed and updates the preference if it is not set yet.
+     * @return Whether prerendering should be allowed.
+     */
+    public boolean shouldPrerender() {
+        if (!DeviceClassManager.enablePrerendering()) return false;
+        migrateNetworkPredictionPreferences();
+        return canPrefetchAndPrerender();
+    }
+
+    /**
+     * Sets the usage and crash reporting preference ON or OFF.
+     *
+     * @param enabled A boolean corresponding whether usage and crash reports uploads are allowed.
+     */
+    public void setUsageAndCrashReporting(boolean enabled) {
+        mPrefs.writeBoolean(ChromePreferenceKeys.PRIVACY_METRICS_REPORTING, enabled);
+        syncUsageAndCrashReportingPrefs();
+        if (!enabled) {
+            SurveyController.getInstance().clearCache(ContextUtils.getApplicationContext());
+        }
+    }
+
+    /**
+     * Update usage and crash preferences based on Android preferences if possible in case they are
+     * out of sync.
+     */
+    public void syncUsageAndCrashReportingPrefs() {
+        // Skip if native browser process is not yet fully initialized.
+        if (!BrowserStartupController.getInstance().isNativeStarted()) return;
+
+        setMetricsReportingEnabled(isUsageAndCrashReportingPermittedByUser());
+    }
+
+    /**
+     * Sets whether this client is in-sample for usage metrics and crash reporting. See
+     * {@link org.chromium.chrome.browser.metrics.UmaUtils#isClientInMetricsSample} for details.
+     */
+    public void setClientInMetricsSample(boolean inSample) {
+        mPrefs.writeBoolean(ChromePreferenceKeys.PRIVACY_METRICS_IN_SAMPLE, inSample);
+    }
+
+    /**
+     * Checks whether this client is in-sample for usage metrics and crash reporting. See
+     * {@link org.chromium.chrome.browser.metrics.UmaUtils#isClientInMetricsSample} for details.
+     *
+     * @returns boolean Whether client is in-sample.
+     */
+    @Override
+    public boolean isClientInMetricsSample() {
+        // The default value is true to avoid sampling out crashes that occur before native code has
+        // been initialized on first run. We'd rather have some extra crashes than none from that
+        // time.
+        return mPrefs.readBoolean(ChromePreferenceKeys.PRIVACY_METRICS_IN_SAMPLE, true);
+    }
+
+    /**
+     * Checks whether uploading of crash dumps is permitted for the available network(s).
+     *
+     * @return whether uploading crash dumps is permitted.
+     */
+    @Override
+    public boolean isNetworkAvailableForCrashUploads() {
+        ConnectivityManager connectivityManager =
+                (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
+        return NetworkPermissionUtil.isNetworkUnmetered(connectivityManager);
+    }
+
+    /**
+     * Checks whether uploading of usage metrics and crash dumps is currently permitted, based on
+     * user consent only. This doesn't take network condition or experimental state (i.e. disabling
+     * upload) into consideration. A crash dump may be retried if this check passes.
+     *
+     * @return whether the user has consented to reporting usage metrics and crash dumps.
+     */
+    @Override
+    public boolean isUsageAndCrashReportingPermittedByUser() {
+        return mPrefs.readBoolean(ChromePreferenceKeys.PRIVACY_METRICS_REPORTING, false);
+    }
+
+    /**
+     * Check whether the command line switch is used to force uploading if at all possible. Used by
+     * test devices to avoid UI manipulation.
+     *
+     * @return whether uploading should be enabled if at all possible.
+     */
+    @Override
+    public boolean isUploadEnabledForTests() {
+        return CommandLine.getInstance().hasSwitch(ChromeSwitches.FORCE_CRASH_DUMP_UPLOAD);
+    }
+
+    /**
+     * @return Whether uploading usage metrics is currently permitted.
+     */
+    public boolean isMetricsUploadPermitted() {
+        return isNetworkAvailable()
+                && (isUsageAndCrashReportingPermittedByUser() || isUploadEnabledForTests());
+    }
+
+    /**
+     * @return Whether there is a user set value for kNetworkPredictionOptions.  This should only be
+     * used for preference migration. See http://crbug.com/334602
+     */
+    private boolean obsoleteNetworkPredictionOptionsHasUserSetting() {
+        return PrivacyPreferencesManagerJni.get().obsoleteNetworkPredictionOptionsHasUserSetting();
+    }
+
+    /**
+     * @return Network predictions preference.
+     */
+    public boolean getNetworkPredictionEnabled() {
+        return PrivacyPreferencesManagerJni.get().getNetworkPredictionEnabled();
+    }
+
+    /**
+     * Sets network predictions preference.
+     */
+    public void setNetworkPredictionEnabled(boolean enabled) {
+        PrivacyPreferencesManagerJni.get().setNetworkPredictionEnabled(enabled);
+    }
+
+    /**
+     * @return Whether Network Predictions is configured by policy.
+     */
+    public boolean isNetworkPredictionManaged() {
+        return PrivacyPreferencesManagerJni.get().getNetworkPredictionManaged();
+    }
+
+    /**
+     * Checks whether network predictions are allowed given preferences and current network
+     * connection type.
+     * @return Whether network predictions are allowed.
+     */
+    private boolean canPrefetchAndPrerender() {
+        return PrivacyPreferencesManagerJni.get().canPrefetchAndPrerender();
+    }
+
+    /**
+     * @return Whether usage and crash reporting pref is enabled.
+     */
+    public boolean isMetricsReportingEnabled() {
+        return PrivacyPreferencesManagerJni.get().isMetricsReportingEnabled();
+    }
+
+    /**
+     * Sets whether the usage and crash reporting pref should be enabled.
+     */
+    public void setMetricsReportingEnabled(boolean enabled) {
+        PrivacyPreferencesManagerJni.get().setMetricsReportingEnabled(enabled);
+    }
+
+    /**
+     * @return Whether usage and crash report pref is managed.
+     */
+    public boolean isMetricsReportingManaged() {
+        return PrivacyPreferencesManagerJni.get().isMetricsReportingManaged();
+    }
+
+    @NativeMethods
+    public interface Natives {
+        boolean canPrefetchAndPrerender();
+        boolean getNetworkPredictionManaged();
+        boolean obsoleteNetworkPredictionOptionsHasUserSetting();
+        boolean getNetworkPredictionEnabled();
+        void setNetworkPredictionEnabled(boolean enabled);
+        boolean isMetricsReportingEnabled();
+        void setMetricsReportingEnabled(boolean enabled);
+        boolean isMetricsReportingManaged();
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/settings/privacy/PrivacySettings.java b/chrome/android/java/src/org/chromium/chrome/browser/privacy/settings/PrivacySettings.java
similarity index 98%
rename from chrome/android/java/src/org/chromium/chrome/browser/settings/privacy/PrivacySettings.java
rename to chrome/android/java/src/org/chromium/chrome/browser/privacy/settings/PrivacySettings.java
index 4ab04d8..a2d559d7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/settings/privacy/PrivacySettings.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/privacy/settings/PrivacySettings.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.chrome.browser.settings.privacy;
+package org.chromium.chrome.browser.privacy.settings;
 
 import android.os.Bundle;
 import android.support.v7.preference.CheckBoxPreference;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfInfoBar.java b/chrome/android/java/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfInfoBar.java
index f216ad1..b650540b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfInfoBar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfInfoBar.java
@@ -7,7 +7,7 @@
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.infobar.InfoBar;
-import org.chromium.chrome.browser.infobar.InfoBarCompactLayout;
+import org.chromium.chrome.browser.ui.messages.infobar.InfoBarCompactLayout;
 
 /**
  * This infobar is shown to let users know they have a shared tab from another
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/settings/privacy/PrivacyPreferencesManager.java b/chrome/android/java/src/org/chromium/chrome/browser/settings/privacy/PrivacyPreferencesManager.java
index a7723d7..607d560 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/settings/privacy/PrivacyPreferencesManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/settings/privacy/PrivacyPreferencesManager.java
@@ -1,328 +1,15 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
+// 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.chrome.browser.settings.privacy;
 
-import android.annotation.SuppressLint;
-import android.content.Context;
-import android.net.ConnectivityManager;
-import android.net.NetworkInfo;
-
-import androidx.annotation.VisibleForTesting;
-
-import org.chromium.base.CommandLine;
-import org.chromium.base.ContextUtils;
-import org.chromium.base.annotations.NativeMethods;
-import org.chromium.chrome.browser.device.DeviceClassManager;
-import org.chromium.chrome.browser.flags.ChromeSwitches;
-import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
-import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
-import org.chromium.chrome.browser.survey.SurveyController;
-import org.chromium.components.minidump_uploader.util.CrashReportingPermissionManager;
-import org.chromium.components.minidump_uploader.util.NetworkPermissionUtil;
-import org.chromium.content_public.browser.BrowserStartupController;
-
 /**
- * Reads, writes, and migrates preferences related to network usage and privacy.
+ * TODO(chouinard): Remove this temporary wrapper class after downstream references are migrated.
  */
-public class PrivacyPreferencesManager implements CrashReportingPermissionManager{
-    @SuppressLint("StaticFieldLeak")
-    private static PrivacyPreferencesManager sInstance;
-
-    private final Context mContext;
-    private final SharedPreferencesManager mPrefs;
-
-    @VisibleForTesting
-    PrivacyPreferencesManager(Context context) {
-        mContext = context;
-        mPrefs = SharedPreferencesManager.getInstance();
-    }
-
-    public static PrivacyPreferencesManager getInstance() {
-        if (sInstance == null) {
-            sInstance = new PrivacyPreferencesManager(ContextUtils.getApplicationContext());
-        }
-        return sInstance;
-    }
-
-    /**
-     * Migrate and delete old preferences.  Note that migration has to happen in Android-specific
-     * code because we need to access ALLOW_PRERENDER sharedPreference.
-     * TODO(bnc) https://crbug.com/394845. This change is planned for M38. After a year or so, it
-     * would be worth considering removing this migration code and reverting to default for users
-     * who had set preferences but have not used Chrome for a year. This change would be subject to
-     * privacy review.
-     */
-    public void migrateNetworkPredictionPreferences() {
-        // See if PREF_NETWORK_PREDICTIONS is an old boolean value.
-        boolean predictionOptionIsBoolean = false;
-        try {
-            mPrefs.readString(ChromePreferenceKeys.PRIVACY_NETWORK_PREDICTIONS, "");
-        } catch (ClassCastException ex) {
-            predictionOptionIsBoolean = true;
-        }
-
-        // Nothing to do if the user or this migration code has already set the new
-        // preference.
-        if (!predictionOptionIsBoolean && obsoleteNetworkPredictionOptionsHasUserSetting()) {
-            return;
-        }
-
-        // Nothing to do if the old preferences are unset.
-        if (!predictionOptionIsBoolean
-                && !mPrefs.contains(ChromePreferenceKeys.PRIVACY_BANDWIDTH_OLD)
-                && !mPrefs.contains(ChromePreferenceKeys.PRIVACY_BANDWIDTH_NO_CELLULAR_OLD)) {
-            return;
-        }
-
-        // Migrate if the old preferences are at their default values.
-        // (Note that for PREF_BANDWIDTH*, if the setting is default, then there is no way to tell
-        // whether the user has set it.)
-        final String prefBandwidthDefault =
-                BandwidthType.title(BandwidthType.Type.PRERENDER_ON_WIFI);
-        final String prefBandwidth =
-                mPrefs.readString(ChromePreferenceKeys.PRIVACY_BANDWIDTH_OLD, prefBandwidthDefault);
-        boolean prefBandwidthNoCellularDefault = true;
-        boolean prefBandwidthNoCellular =
-                mPrefs.readBoolean(ChromePreferenceKeys.PRIVACY_BANDWIDTH_NO_CELLULAR_OLD,
-                        prefBandwidthNoCellularDefault);
-
-        if (!(prefBandwidthDefault.equals(prefBandwidth))
-                || (prefBandwidthNoCellular != prefBandwidthNoCellularDefault)) {
-            boolean newValue = true;
-            // Observe PREF_BANDWIDTH on mobile network capable devices.
-            if (isMobileNetworkCapable()) {
-                if (mPrefs.contains(ChromePreferenceKeys.PRIVACY_BANDWIDTH_OLD)) {
-                    @BandwidthType.Type
-                    int prefetchBandwidthTypePref =
-                            BandwidthType.getBandwidthFromTitle(prefBandwidth);
-                    if (BandwidthType.Type.NEVER_PRERENDER == prefetchBandwidthTypePref) {
-                        newValue = false;
-                    } else if (BandwidthType.Type.PRERENDER_ON_WIFI == prefetchBandwidthTypePref
-                            || BandwidthType.Type.ALWAYS_PRERENDER == prefetchBandwidthTypePref) {
-                        newValue = true;
-                    }
-                }
-            // Observe PREF_BANDWIDTH_NO_CELLULAR on devices without mobile network.
-            } else {
-                if (mPrefs.contains(ChromePreferenceKeys.PRIVACY_BANDWIDTH_NO_CELLULAR_OLD)) {
-                    newValue = prefBandwidthNoCellular;
-                }
-            }
-            // Save new value in Chrome PrefService.
-            setNetworkPredictionEnabled(newValue);
-        }
-
-        // Delete old sharedPreferences.
-
-        // Delete PREF_BANDWIDTH and PREF_BANDWIDTH_NO_CELLULAR: just migrated these options.
-        if (mPrefs.contains(ChromePreferenceKeys.PRIVACY_BANDWIDTH_OLD)) {
-            mPrefs.removeKey(ChromePreferenceKeys.PRIVACY_BANDWIDTH_OLD);
-        }
-        if (mPrefs.contains(ChromePreferenceKeys.PRIVACY_BANDWIDTH_NO_CELLULAR_OLD)) {
-            mPrefs.removeKey(ChromePreferenceKeys.PRIVACY_BANDWIDTH_NO_CELLULAR_OLD);
-        }
-        // Also delete ALLOW_PRERENDER, which was updated based on PREF_BANDWIDTH[_NO_CELLULAR] and
-        // network connectivity type, therefore does not carry additional information.
-        if (mPrefs.contains(ChromePreferenceKeys.PRIVACY_ALLOW_PRERENDER_OLD)) {
-            mPrefs.removeKey(ChromePreferenceKeys.PRIVACY_ALLOW_PRERENDER_OLD);
-        }
-        // Delete bool PREF_NETWORK_PREDICTIONS so that string values can be stored. Note that this
-        // SharedPreference carries no information, because it used to be overwritten by
-        // kNetworkPredictionEnabled on startup, and now it is overwritten by
-        // kNetworkPredictionOptions on startup.
-        if (mPrefs.contains(ChromePreferenceKeys.PRIVACY_NETWORK_PREDICTIONS)) {
-            mPrefs.removeKey(ChromePreferenceKeys.PRIVACY_NETWORK_PREDICTIONS);
-        }
-    }
-
-    protected boolean isNetworkAvailable() {
-        ConnectivityManager connectivityManager =
-                (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
-        NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
-        return (networkInfo != null && networkInfo.isConnected());
-    }
-
-    protected boolean isMobileNetworkCapable() {
-        ConnectivityManager connectivityManager =
-                (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
-        // Android telephony team said it is OK to continue using getNetworkInfo() for our purposes.
-        // We cannot use ConnectivityManager#getAllNetworks() because that one only reports enabled
-        // networks. See crbug.com/532455.
-        @SuppressWarnings("deprecation")
-        NetworkInfo networkInfo =
-                connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
-        return networkInfo != null;
-    }
-
-    /**
-     * Checks whether prerender should be allowed and updates the preference if it is not set yet.
-     * @return Whether prerendering should be allowed.
-     */
-    public boolean shouldPrerender() {
-        if (!DeviceClassManager.enablePrerendering()) return false;
-        migrateNetworkPredictionPreferences();
-        return canPrefetchAndPrerender();
-    }
-
-    /**
-     * Sets the usage and crash reporting preference ON or OFF.
-     *
-     * @param enabled A boolean corresponding whether usage and crash reports uploads are allowed.
-     */
-    public void setUsageAndCrashReporting(boolean enabled) {
-        mPrefs.writeBoolean(ChromePreferenceKeys.PRIVACY_METRICS_REPORTING, enabled);
-        syncUsageAndCrashReportingPrefs();
-        if (!enabled) {
-            SurveyController.getInstance().clearCache(ContextUtils.getApplicationContext());
-        }
-    }
-
-    /**
-     * Update usage and crash preferences based on Android preferences if possible in case they are
-     * out of sync.
-     */
-    public void syncUsageAndCrashReportingPrefs() {
-        // Skip if native browser process is not yet fully initialized.
-        if (!BrowserStartupController.getInstance().isNativeStarted()) return;
-
-        setMetricsReportingEnabled(isUsageAndCrashReportingPermittedByUser());
-    }
-
-    /**
-     * Sets whether this client is in-sample for usage metrics and crash reporting. See
-     * {@link org.chromium.chrome.browser.metrics.UmaUtils#isClientInMetricsSample} for details.
-     */
-    public void setClientInMetricsSample(boolean inSample) {
-        mPrefs.writeBoolean(ChromePreferenceKeys.PRIVACY_METRICS_IN_SAMPLE, inSample);
-    }
-
-    /**
-     * Checks whether this client is in-sample for usage metrics and crash reporting. See
-     * {@link org.chromium.chrome.browser.metrics.UmaUtils#isClientInMetricsSample} for details.
-     *
-     * @returns boolean Whether client is in-sample.
-     */
-    @Override
-    public boolean isClientInMetricsSample() {
-        // The default value is true to avoid sampling out crashes that occur before native code has
-        // been initialized on first run. We'd rather have some extra crashes than none from that
-        // time.
-        return mPrefs.readBoolean(ChromePreferenceKeys.PRIVACY_METRICS_IN_SAMPLE, true);
-    }
-
-    /**
-     * Checks whether uploading of crash dumps is permitted for the available network(s).
-     *
-     * @return whether uploading crash dumps is permitted.
-     */
-    @Override
-    public boolean isNetworkAvailableForCrashUploads() {
-        ConnectivityManager connectivityManager =
-                (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
-        return NetworkPermissionUtil.isNetworkUnmetered(connectivityManager);
-    }
-
-    /**
-     * Checks whether uploading of usage metrics and crash dumps is currently permitted, based on
-     * user consent only. This doesn't take network condition or experimental state (i.e. disabling
-     * upload) into consideration. A crash dump may be retried if this check passes.
-     *
-     * @return whether the user has consented to reporting usage metrics and crash dumps.
-     */
-    @Override
-    public boolean isUsageAndCrashReportingPermittedByUser() {
-        return mPrefs.readBoolean(ChromePreferenceKeys.PRIVACY_METRICS_REPORTING, false);
-    }
-
-    /**
-     * Check whether the command line switch is used to force uploading if at all possible. Used by
-     * test devices to avoid UI manipulation.
-     *
-     * @return whether uploading should be enabled if at all possible.
-     */
-    @Override
-    public boolean isUploadEnabledForTests() {
-        return CommandLine.getInstance().hasSwitch(ChromeSwitches.FORCE_CRASH_DUMP_UPLOAD);
-    }
-
-    /**
-     * @return Whether uploading usage metrics is currently permitted.
-     */
-    public boolean isMetricsUploadPermitted() {
-        return isNetworkAvailable()
-                && (isUsageAndCrashReportingPermittedByUser() || isUploadEnabledForTests());
-    }
-
-    /**
-     * @return Whether there is a user set value for kNetworkPredictionOptions.  This should only be
-     * used for preference migration. See http://crbug.com/334602
-     */
-    private boolean obsoleteNetworkPredictionOptionsHasUserSetting() {
-        return PrivacyPreferencesManagerJni.get().obsoleteNetworkPredictionOptionsHasUserSetting();
-    }
-
-    /**
-     * @return Network predictions preference.
-     */
-    public boolean getNetworkPredictionEnabled() {
-        return PrivacyPreferencesManagerJni.get().getNetworkPredictionEnabled();
-    }
-
-    /**
-     * Sets network predictions preference.
-     */
-    public void setNetworkPredictionEnabled(boolean enabled) {
-        PrivacyPreferencesManagerJni.get().setNetworkPredictionEnabled(enabled);
-    }
-
-    /**
-     * @return Whether Network Predictions is configured by policy.
-     */
-    public boolean isNetworkPredictionManaged() {
-        return PrivacyPreferencesManagerJni.get().getNetworkPredictionManaged();
-    }
-
-    /**
-     * Checks whether network predictions are allowed given preferences and current network
-     * connection type.
-     * @return Whether network predictions are allowed.
-     */
-    private boolean canPrefetchAndPrerender() {
-        return PrivacyPreferencesManagerJni.get().canPrefetchAndPrerender();
-    }
-
-    /**
-     * @return Whether usage and crash reporting pref is enabled.
-     */
-    public boolean isMetricsReportingEnabled() {
-        return PrivacyPreferencesManagerJni.get().isMetricsReportingEnabled();
-    }
-
-    /**
-     * Sets whether the usage and crash reporting pref should be enabled.
-     */
-    public void setMetricsReportingEnabled(boolean enabled) {
-        PrivacyPreferencesManagerJni.get().setMetricsReportingEnabled(enabled);
-    }
-
-    /**
-     * @return Whether usage and crash report pref is managed.
-     */
-    public boolean isMetricsReportingManaged() {
-        return PrivacyPreferencesManagerJni.get().isMetricsReportingManaged();
-    }
-
-    @NativeMethods
-    public interface Natives {
-        boolean canPrefetchAndPrerender();
-        boolean getNetworkPredictionManaged();
-        boolean obsoleteNetworkPredictionOptionsHasUserSetting();
-        boolean getNetworkPredictionEnabled();
-        void setNetworkPredictionEnabled(boolean enabled);
-        boolean isMetricsReportingEnabled();
-        void setMetricsReportingEnabled(boolean enabled);
-        boolean isMetricsReportingManaged();
+public class PrivacyPreferencesManager {
+    public static org.chromium.chrome.browser.privacy.settings.PrivacyPreferencesManager
+    getInstance() {
+        return org.chromium.chrome.browser.privacy.settings.PrivacyPreferencesManager.getInstance();
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/site_settings/SingleCategorySettings.java b/chrome/android/java/src/org/chromium/chrome/browser/site_settings/SingleCategorySettings.java
index 697772b1..bdebf6c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/site_settings/SingleCategorySettings.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/site_settings/SingleCategorySettings.java
@@ -134,6 +134,7 @@
     public static final String NOTIFICATIONS_QUIET_UI_TOGGLE_KEY = "notifications_quiet_ui";
     public static final String EXPLAIN_PROTECTED_MEDIA_KEY = "protected_content_learn_more";
     private static final String ADD_EXCEPTION_KEY = "add_exception";
+    public static final String COOKIE_INFO_TEXT_KEY = "cookie_info_text";
 
     // Keys for Allowed/Blocked preference groups/headers.
     private static final String ALLOWED_GROUP = "allowed_group";
@@ -859,6 +860,11 @@
             maybeShowOsWarning(screen);
         }
 
+        if (!(mCategory.showSites(SiteSettingsCategory.Type.COOKIES)
+                    && ChromeFeatureList.isEnabled(ChromeFeatureList.IMPROVED_COOKIE_CONTROLS))) {
+            screen.removePreference(screen.findPreference(COOKIE_INFO_TEXT_KEY));
+        }
+
         if (hideSecondaryToggles) {
             screen.removePreference(thirdPartyCookies);
             screen.removePreference(notificationsVibrate);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/site_settings/SiteSettingsCategory.java b/chrome/android/java/src/org/chromium/chrome/browser/site_settings/SiteSettingsCategory.java
index 9c965996..d1083a7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/site_settings/SiteSettingsCategory.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/site_settings/SiteSettingsCategory.java
@@ -28,6 +28,7 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.components.content_settings.ContentSettingsType;
+import org.chromium.components.subresource_filter.SubresourceFilterFeatureList;
 import org.chromium.ui.text.SpanApplier;
 import org.chromium.ui.text.SpanApplier.SpanInfo;
 
@@ -274,7 +275,8 @@
      * Returns whether the Ads category is enabled via an experiment flag.
      */
     public static boolean adsCategoryEnabled() {
-        return ChromeFeatureList.isEnabled(ChromeFeatureList.SUBRESOURCE_FILTER);
+        return SubresourceFilterFeatureList.isEnabled(
+                SubresourceFilterFeatureList.SUBRESOURCE_FILTER);
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/sms/SmsReceiverInfoBar.java b/chrome/android/java/src/org/chromium/chrome/browser/sms/SmsReceiverInfoBar.java
index a8ebe63f..0065ed9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/sms/SmsReceiverInfoBar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/sms/SmsReceiverInfoBar.java
@@ -15,8 +15,8 @@
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.infobar.ConfirmInfoBar;
-import org.chromium.chrome.browser.infobar.InfoBarLayout;
 import org.chromium.chrome.browser.ui.messages.infobar.InfoBarControlLayout;
+import org.chromium.chrome.browser.ui.messages.infobar.InfoBarLayout;
 import org.chromium.ui.KeyboardVisibilityDelegate;
 import org.chromium.ui.base.WindowAndroid;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/survey/ChromeSurveyController.java b/chrome/android/java/src/org/chromium/chrome/browser/survey/ChromeSurveyController.java
index 819f8b7..98b9b10c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/survey/ChromeSurveyController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/survey/ChromeSurveyController.java
@@ -27,7 +27,7 @@
 import org.chromium.chrome.browser.infobar.SurveyInfoBarDelegate;
 import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
 import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
-import org.chromium.chrome.browser.settings.privacy.PrivacyPreferencesManager;
+import org.chromium.chrome.browser.privacy.settings.PrivacyPreferencesManager;
 import org.chromium.chrome.browser.tab.EmptyTabObserver;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TabHidingType;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/SyncAndServicesSettings.java b/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/SyncAndServicesSettings.java
index e8e0db9e..114532a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/SyncAndServicesSettings.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/SyncAndServicesSettings.java
@@ -45,6 +45,7 @@
 import org.chromium.chrome.browser.preferences.Pref;
 import org.chromium.chrome.browser.preferences.PrefServiceBridge;
 import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
+import org.chromium.chrome.browser.privacy.settings.PrivacyPreferencesManager;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.safe_browsing.SafeBrowsingBridge;
 import org.chromium.chrome.browser.settings.ChromeBasePreference;
@@ -54,7 +55,6 @@
 import org.chromium.chrome.browser.settings.SettingsActivity;
 import org.chromium.chrome.browser.settings.SettingsUtils;
 import org.chromium.chrome.browser.settings.password.PasswordUIView;
-import org.chromium.chrome.browser.settings.privacy.PrivacyPreferencesManager;
 import org.chromium.chrome.browser.signin.IdentityServicesProvider;
 import org.chromium.chrome.browser.signin.UnifiedConsentServiceBridge;
 import org.chromium.chrome.browser.sync.ProfileSyncService;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/DEPS b/chrome/android/java/src/org/chromium/chrome/browser/tab/DEPS
index 796d5933..d3f4836 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/DEPS
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/DEPS
@@ -3,7 +3,6 @@
   "+chrome/android/java/src/org/chromium/chrome/browser/ActivityTabProvider.java",
   "+chrome/android/java/src/org/chromium/chrome/browser/AppHooks.java",
   "+chrome/android/java/src/org/chromium/chrome/browser/TabHidingType.java",
-  "+chrome/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java",
   "+chrome/android/java/src/org/chromium/chrome/browser/incognito/IncognitoUtils.java",
   "+chrome/android/java/src/org/chromium/chrome/browser/paint_preview/PaintPreviewTabHelper.java",
   "+chrome/android/java/src/org/chromium/chrome/browser/previews/Previews.java",
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabFeatureUtilities.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabFeatureUtilities.java
index 8707c1bc..e2f63de 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabFeatureUtilities.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabFeatureUtilities.java
@@ -11,13 +11,29 @@
 
 import org.chromium.base.Log;
 import org.chromium.base.SysUtils;
+import org.chromium.chrome.browser.flags.CachedFeatureFlags;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
+import org.chromium.chrome.browser.flags.IntCachedFieldTrialParameter;
 
 /**
  * Contains logic that decides whether to enable features related to tabs.
  */
 public class TabFeatureUtilities {
     private static final String TAG = "TabFeatureUtilities";
+    private static final int DEFAULT_MIN_SDK = Build.VERSION_CODES.O;
+    private static final int DEFAULT_MIN_MEMORY_MB = 2048;
+
+    // Field trial parameter for the minimum Android SDK version to enable zooming animation.
+    public static final String MIN_SDK_PARAM = "zooming-min-sdk-version";
+    public static final IntCachedFieldTrialParameter ZOOMING_MIN_SDK =
+            new IntCachedFieldTrialParameter(
+                    ChromeFeatureList.TAB_TO_GTS_ANIMATION, MIN_SDK_PARAM, DEFAULT_MIN_SDK);
+
+    // Field trial parameter for the minimum physical memory size to enable zooming animation.
+    public static final String MIN_MEMORY_MB_PARAM = "zooming-min-memory-mb";
+    public static final IntCachedFieldTrialParameter ZOOMING_MIN_MEMORY =
+            new IntCachedFieldTrialParameter(ChromeFeatureList.TAB_TO_GTS_ANIMATION,
+                    MIN_MEMORY_MB_PARAM, DEFAULT_MIN_MEMORY_MB);
 
     private static Boolean sIsTabToGtsAnimationEnabled;
 
@@ -40,39 +56,19 @@
         }
         Log.d(TAG, "GTS.MinSdkVersion = " + GridTabSwitcherUtil.getMinSdkVersion());
         Log.d(TAG, "GTS.MinMemoryMB = " + GridTabSwitcherUtil.getMinMemoryMB());
-        return ChromeFeatureList.isEnabled(ChromeFeatureList.TAB_TO_GTS_ANIMATION)
+        return CachedFeatureFlags.isEnabled(ChromeFeatureList.TAB_TO_GTS_ANIMATION)
                 && Build.VERSION.SDK_INT >= GridTabSwitcherUtil.getMinSdkVersion()
                 && SysUtils.amountOfPhysicalMemoryKB() / 1024
                 >= GridTabSwitcherUtil.getMinMemoryMB();
     }
 
     private static class GridTabSwitcherUtil {
-        // Field trial parameter for the minimum Android SDK version to enable zooming animation.
-        private static final String MIN_SDK_PARAM = "zooming-min-sdk-version";
-        private static final int DEFAULT_MIN_SDK = Build.VERSION_CODES.O;
-
-        // Field trial parameter for the minimum physical memory size to enable zooming animation.
-        private static final String MIN_MEMORY_MB_PARAM = "zooming-min-memory-mb";
-        private static final int DEFAULT_MIN_MEMORY_MB = 2048;
-
         private static int getMinSdkVersion() {
-            String sdkVersion = ChromeFeatureList.getFieldTrialParamByFeature(
-                    ChromeFeatureList.TAB_TO_GTS_ANIMATION, MIN_SDK_PARAM);
-            try {
-                return Integer.valueOf(sdkVersion);
-            } catch (NumberFormatException e) {
-                return DEFAULT_MIN_SDK;
-            }
+            return CachedFeatureFlags.getValue(TabFeatureUtilities.ZOOMING_MIN_SDK);
         }
 
         private static int getMinMemoryMB() {
-            String sdkVersion = ChromeFeatureList.getFieldTrialParamByFeature(
-                    ChromeFeatureList.TAB_TO_GTS_ANIMATION, MIN_MEMORY_MB_PARAM);
-            try {
-                return Integer.valueOf(sdkVersion);
-            } catch (NumberFormatException e) {
-                return DEFAULT_MIN_MEMORY_MB;
-            }
+            return CachedFeatureFlags.getValue(TabFeatureUtilities.ZOOMING_MIN_MEMORY);
         }
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ui/BottomContainer.java b/chrome/android/java/src/org/chromium/chrome/browser/ui/BottomContainer.java
index d9eb655..a702893 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ui/BottomContainer.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ui/BottomContainer.java
@@ -8,21 +8,25 @@
 import android.util.AttributeSet;
 import android.widget.FrameLayout;
 
-import org.chromium.chrome.browser.compositor.CompositorViewResizer;
+import org.chromium.base.Callback;
 import org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager;
 import org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager.FullscreenListener;
+import org.chromium.chrome.browser.lifecycle.Destroyable;
+import org.chromium.ui.base.ApplicationViewportInsetSupplier;
 
 /**
  * The container that holds both infobars and snackbars. It will be translated up and down when the
  * bottom controls' offset changes.
  */
-public class BottomContainer
-        extends FrameLayout implements FullscreenListener, CompositorViewResizer.Observer {
+public class BottomContainer extends FrameLayout implements Destroyable, FullscreenListener {
+    /** An observer of the viewport insets to change this container's position. */
+    private final Callback<Integer> mViewportInsetObserver;
+
     /** The {@link ChromeFullscreenManager} to listen for controls offset changes. */
     private ChromeFullscreenManager mFullscreenManager;
 
-    /** A {@link CompositorViewResizer} to listen to for keyboard extension size changes. */
-    private CompositorViewResizer mKeyboardExtensionSizeManager;
+    /** A {@link ApplicationViewportInsetSupplier} to listen for viewport-shrinking features. */
+    private ApplicationViewportInsetSupplier mViewportInsetSupplier;
 
     /** The desired Y offset if unaffected by other UI. */
     private float mBaseYOffset;
@@ -32,23 +36,18 @@
      */
     public BottomContainer(Context context, AttributeSet attrs) {
         super(context, attrs);
+        mViewportInsetObserver = (inset) -> setTranslationY(mBaseYOffset);
     }
 
     /**
      * Initializes this container.
      */
     public void initialize(ChromeFullscreenManager fullscreenManager,
-            CompositorViewResizer keyboardExtensionSizeManager) {
+            ApplicationViewportInsetSupplier viewportInsetSupplier) {
         mFullscreenManager = fullscreenManager;
         mFullscreenManager.addListener(this);
-        mKeyboardExtensionSizeManager = keyboardExtensionSizeManager;
-        mKeyboardExtensionSizeManager.addObserver(this);
-        setTranslationY(mBaseYOffset);
-    }
-
-    // CompositorViewResizer methods
-    @Override
-    public void onHeightChanged(int keyboardHeight) {
+        mViewportInsetSupplier = viewportInsetSupplier;
+        mViewportInsetSupplier.addObserver(mViewportInsetObserver);
         setTranslationY(mBaseYOffset);
     }
 
@@ -65,7 +64,7 @@
 
         float offsetFromControls = mFullscreenManager.getBottomControlOffset()
                 - mFullscreenManager.getBottomControlsHeight();
-        offsetFromControls -= mKeyboardExtensionSizeManager.getHeight();
+        offsetFromControls -= mViewportInsetSupplier.get();
 
         // Sit on top of either the bottom sheet or the bottom toolbar depending on which is larger
         // (offsets are negative).
@@ -80,4 +79,10 @@
 
     @Override
     public void onContentOffsetChanged(int offset) {}
+
+    @Override
+    public void destroy() {
+        mFullscreenManager.removeListener(this);
+        mViewportInsetSupplier.removeObserver(mViewportInsetObserver);
+    }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/settings/accessibility/AccessibilitySettingsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/accessibility/settings/AccessibilitySettingsTest.java
similarity index 98%
rename from chrome/android/javatests/src/org/chromium/chrome/browser/settings/accessibility/AccessibilitySettingsTest.java
rename to chrome/android/javatests/src/org/chromium/chrome/browser/accessibility/settings/AccessibilitySettingsTest.java
index f41cbc6..8db94377 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/settings/accessibility/AccessibilitySettingsTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/accessibility/settings/AccessibilitySettingsTest.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.chrome.browser.settings.accessibility;
+package org.chromium.chrome.browser.accessibility.settings;
 
 import static android.support.test.espresso.Espresso.onView;
 import static android.support.test.espresso.action.ViewActions.click;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/AutofillUpstreamTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/AutofillUpstreamTest.java
index 6ddf428..29e0c62 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/AutofillUpstreamTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/AutofillUpstreamTest.java
@@ -22,8 +22,8 @@
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.browser.infobar.AutofillSaveCardInfoBar;
 import org.chromium.chrome.browser.infobar.InfoBar;
-import org.chromium.chrome.browser.infobar.InfoBarLayout;
 import org.chromium.chrome.browser.sync.SyncTestRule;
+import org.chromium.chrome.browser.ui.messages.infobar.InfoBarLayout;
 import org.chromium.chrome.test.ChromeActivityTestRule;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.content_public.browser.WebContents;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabsConnectionTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabsConnectionTest.java
index 821b6ee..644ae671 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabsConnectionTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabsConnectionTest.java
@@ -39,7 +39,7 @@
 import org.chromium.chrome.browser.WarmupManager;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.browser.preferences.PrefServiceBridge;
-import org.chromium.chrome.browser.settings.privacy.PrivacyPreferencesManager;
+import org.chromium.chrome.browser.privacy.settings.PrivacyPreferencesManager;
 import org.chromium.chrome.browser.tab.EmptyTabObserver;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadLocationChangeTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadLocationChangeTest.java
index 2f554c17..29c4770f 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadLocationChangeTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadLocationChangeTest.java
@@ -29,9 +29,9 @@
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.Feature;
 import org.chromium.chrome.browser.download.DownloadTestRule.CustomMainActivityStart;
+import org.chromium.chrome.browser.download.settings.DownloadDirectoryAdapter;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
-import org.chromium.chrome.browser.settings.download.DownloadDirectoryAdapter;
 import org.chromium.chrome.download.R;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.util.browser.Features;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/infobar/InfoBarContainerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/infobar/InfoBarContainerTest.java
index 322d83ca..306d4c2 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/infobar/InfoBarContainerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/infobar/InfoBarContainerTest.java
@@ -27,7 +27,7 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
-import org.chromium.chrome.browser.settings.privacy.PrivacyPreferencesManager;
+import org.chromium.chrome.browser.privacy.settings.PrivacyPreferencesManager;
 import org.chromium.chrome.test.ChromeActivityTestRule;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.util.InfoBarTestAnimationListener;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/privacy/settings/OWNERS b/chrome/android/javatests/src/org/chromium/chrome/browser/privacy/settings/OWNERS
new file mode 100644
index 0000000..b0a71ba
--- /dev/null
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/privacy/settings/OWNERS
@@ -0,0 +1 @@
+file://chrome/android/java/src/org/chromium/chrome/browser/privacy/settings/OWNERS
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/settings/privacy/PrivacyPreferencesManagerNativeTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/privacy/settings/PrivacyPreferencesManagerNativeTest.java
similarity index 98%
rename from chrome/android/javatests/src/org/chromium/chrome/browser/settings/privacy/PrivacyPreferencesManagerNativeTest.java
rename to chrome/android/javatests/src/org/chromium/chrome/browser/privacy/settings/PrivacyPreferencesManagerNativeTest.java
index 7ef585e..394f484 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/settings/privacy/PrivacyPreferencesManagerNativeTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/privacy/settings/PrivacyPreferencesManagerNativeTest.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.chrome.browser.settings.privacy;
+package org.chromium.chrome.browser.privacy.settings;
 
 import android.content.Context;
 import android.content.SharedPreferences;
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/privacy/settings/OWNERS b/chrome/android/junit/src/org/chromium/chrome/browser/privacy/settings/OWNERS
new file mode 100644
index 0000000..b0a71ba
--- /dev/null
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/privacy/settings/OWNERS
@@ -0,0 +1 @@
+file://chrome/android/java/src/org/chromium/chrome/browser/privacy/settings/OWNERS
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/settings/privacy/PrivacyPreferencesManagerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/privacy/settings/PrivacyPreferencesManagerTest.java
similarity index 98%
rename from chrome/android/junit/src/org/chromium/chrome/browser/settings/privacy/PrivacyPreferencesManagerTest.java
rename to chrome/android/junit/src/org/chromium/chrome/browser/privacy/settings/PrivacyPreferencesManagerTest.java
index fbb9e95..2b27d9b 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/settings/privacy/PrivacyPreferencesManagerTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/privacy/settings/PrivacyPreferencesManagerTest.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.chrome.browser.settings.privacy;
+package org.chromium.chrome.browser.privacy.settings;
 
 import static org.junit.Assert.assertEquals;
 import static org.mockito.Mockito.mock;
diff --git a/chrome/app/chromeos_strings.grdp b/chrome/app/chromeos_strings.grdp
index 1cb37e4..7eecfe6 100644
--- a/chrome/app/chromeos_strings.grdp
+++ b/chrome/app/chromeos_strings.grdp
@@ -4292,8 +4292,8 @@
   <message name="IDS_OOBE_GESTURE_NAVIGATION_BACK_TITLE" desc="The title in the gesture navigation education back OOBE screen shown on the first user login.">
     Go Back
   </message>
-  <message name="IDS_OOBE_GESTURE_NAVIGATION_BACK_DESCRIPTION" desc="The description for the gesture navigation education back OOBE screen shown on the first user login.">
-    To return to the previous screen, swipe from the side.
+  <message name="IDS_OOBE_GESTURE_NAVIGATION_BACK_DESCRIPTION" desc="The description for the gesture navigation education back OOBE screen shown on the first user login. This string should say to swipe from the right side in RTL languages.">
+    To return to the previous screen, swipe from the left side.
   </message>
   <message name="IDS_OOBE_GESTURE_NAVIGATION_OVERVIEW_TITLE" desc="The title in the gesture navigation education overview OOBE screen shown on the first user login.">
     Switch to open apps
@@ -4498,6 +4498,12 @@
   <message name="IDS_EDU_LOGIN_NEXT" desc="The name of the button that goes to the next step in EDU login flow.">
     Next
   </message>
+  <message name="IDS_EDU_LOGIN_WELCOME_TITLE" desc="Title for the welcome screen in EDU account addition flow.">
+    Get started with a school account
+  </message>
+  <message name="IDS_EDU_LOGIN_WELCOME_BODY" desc="Text on the welcome screen in EDU account addition flow informing user to ask their parent for permission to add the account.">
+    To add an account for access to educational resources, ask a parent to give you permission
+  </message>
 
   <!-- TPM firmware auto-update notifications -->
   <message name="IDS_TPM_AUTO_UPDATE_PLANNED_NOTIFICATION_TITLE" desc="Title for the notification informing the user that local data will be deleted.">
diff --git a/chrome/app/chromeos_strings_grdp/IDS_EDU_LOGIN_WELCOME_BODY.png.sha1 b/chrome/app/chromeos_strings_grdp/IDS_EDU_LOGIN_WELCOME_BODY.png.sha1
new file mode 100644
index 0000000..d7c5a55
--- /dev/null
+++ b/chrome/app/chromeos_strings_grdp/IDS_EDU_LOGIN_WELCOME_BODY.png.sha1
@@ -0,0 +1 @@
+968315109ba1b751a980433e4d782492110c77f1
\ No newline at end of file
diff --git a/chrome/app/chromeos_strings_grdp/IDS_EDU_LOGIN_WELCOME_TITLE.png.sha1 b/chrome/app/chromeos_strings_grdp/IDS_EDU_LOGIN_WELCOME_TITLE.png.sha1
new file mode 100644
index 0000000..d7c5a55
--- /dev/null
+++ b/chrome/app/chromeos_strings_grdp/IDS_EDU_LOGIN_WELCOME_TITLE.png.sha1
@@ -0,0 +1 @@
+968315109ba1b751a980433e4d782492110c77f1
\ No newline at end of file
diff --git a/chrome/app/chromeos_strings_grdp/IDS_OOBE_GESTURE_NAVIGATION_BACK_DESCRIPTION.png.sha1 b/chrome/app/chromeos_strings_grdp/IDS_OOBE_GESTURE_NAVIGATION_BACK_DESCRIPTION.png.sha1
index e33f9e8..594678f 100644
--- a/chrome/app/chromeos_strings_grdp/IDS_OOBE_GESTURE_NAVIGATION_BACK_DESCRIPTION.png.sha1
+++ b/chrome/app/chromeos_strings_grdp/IDS_OOBE_GESTURE_NAVIGATION_BACK_DESCRIPTION.png.sha1
@@ -1 +1 @@
-ffbdfe9e84332b56945bbf9b7c792a66cd9a68f6
\ No newline at end of file
+2be29446bff7f7d6ff0687fa604a474cd5a76b3e
\ No newline at end of file
diff --git a/chrome/app/settings_strings.grdp b/chrome/app/settings_strings.grdp
index 76a655bb..73246953 100644
--- a/chrome/app/settings_strings.grdp
+++ b/chrome/app/settings_strings.grdp
@@ -1690,7 +1690,7 @@
     Enhanced Protection
   </message>
   <message name="IDS_SETTINGS_SAFEBROWSING_ENHANCED_DESC" desc="Description of safe browsing enhanced protection mode">
-    Faster, proactive protection against dangerous websites, downloads, and extensions. Warns you about password breaches. Requires browsing data to be sent to Google. 
+    Faster, proactive protection against dangerous websites, downloads, and extensions. Warns you about password breaches. Requires browsing data to be sent to Google.
   </message>
   <message name="IDS_SETTINGS_SAFEBROWSING_ENHANCED_BULLET_ONE" desc="First bullet point under the safe browsing enhanced protection mode">
     Predicts and warns you about dangerous events before they happen.
@@ -1778,7 +1778,16 @@
     Determines how to connect to websites over a secure connection
   </message>
   <message name="IDS_SETTINGS_AUTOMATIC_MODE_DESCRIPTION" desc="Text of the radio button that puts secure DNS in auto-upgrade mode">
-    With your current service provider. Secure DNS may not be available all the time.
+    With your current service provider
+  </message>
+  <message name="IDS_SETTINGS_AUTOMATIC_MODE_DESCRIPTION_SECONDARY" desc="Secondary, continued explanation of the radio button that puts secure DNS in auto-upgrade mode">
+    Secure DNS may not be available all the time
+  </message>
+  <message name="IDS_SETTINGS_SECURE_MODE_DESCRIPTION_ACCESSIBILITY_LABEL" desc="Accessibility label for the radio button that puts secure DNS in secure mode">
+    With a provider of your choice
+  </message>
+  <message name="IDS_SETTINGS_SECURE_DNS_DROPDOWN_ACCESSIBILITY_LABEL" desc="Accessibility label for the dropdown menu containing secure resolvers">
+    Provider options
   </message>
   <message name="IDS_SETTINGS_SECURE_DROPDOWN_MODE_DESCRIPTION" desc="Text of the radio button that allows a secure resolver to be selected from a dropdown menu">
     With
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index f3ab075..e0ecc7d7 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -3014,6 +3014,7 @@
       "//components/security_state/content/android",
       "//components/send_tab_to_self",
       "//components/signin/internal/identity_manager",  # cf android/signin/DEPS
+      "//components/subresource_filter/android",
       "//components/viz/common",
       "//media/mojo/clients",
       "//rlz:rlz_utils",
diff --git a/chrome/browser/DEPS b/chrome/browser/DEPS
index 44c8186..669edaf 100644
--- a/chrome/browser/DEPS
+++ b/chrome/browser/DEPS
@@ -430,6 +430,10 @@
 ]
 
 specific_include_rules = {
+  '.*InstrumentationTest\.java': [
+    # Android instrumentation tests depend on the whole app.
+    "+chrome/android",
+  ],
   "platform_util_linux.cc": [
     # The following is used to call the org.freedesktop.FileManager1
     # DBus interface to highlight a file within its parent folder
diff --git a/chrome/browser/android/contextualsearch/contextual_search_delegate.cc b/chrome/browser/android/contextualsearch/contextual_search_delegate.cc
index e256946..4b64d6b 100644
--- a/chrome/browser/android/contextualsearch/contextual_search_delegate.cc
+++ b/chrome/browser/android/contextualsearch/contextual_search_delegate.cc
@@ -285,11 +285,18 @@
     contextual_cards_version =
         contextual_search::kContextualCardsTranslationsIntegration;
   }
-  // TODO(donnd): figure out if this is really needed or if the communication
-  // can be done automatically through a FieldTrial config setting.
+  // Mixin the debug setting.
   if (base::FeatureList::IsEnabled(chrome::android::kContextualSearchDebug)) {
-    contextual_cards_version =
-        contextual_search::kContextualCardsDebugIntegration;
+    contextual_cards_version +=
+        contextual_search::kContextualCardsServerDebugMixin;
+  }
+  // Mixin the exact-search setting.
+  // TODO(donnd): remove or merge-away this ugly implementation if we get
+  // approval for the additional parameter used below (in
+  // ContextualSearchParams). It would be better to only have to support
+  // one of these communication methods server-side.
+  if (context->GetExactResolve()) {
+    contextual_cards_version += contextual_search::kExactSearchMixin;
   }
   // Let the field-trial override.
   if (field_trial_->GetContextualCardsVersion() != 0) {
diff --git a/chrome/browser/browser_resources.grd b/chrome/browser/browser_resources.grd
index cbba616c..f5e6f1df 100644
--- a/chrome/browser/browser_resources.grd
+++ b/chrome/browser/browser_resources.grd
@@ -215,14 +215,16 @@
           </else>
         </if>
         <!-- Edu account login resources -->
-        <include name="IDU_EDU_LOGIN_EDU_LOGIN_HTML" file="resources\chromeos\edu_login\edu_login.html" allowexternalscript="true" type="BINDATA" compress="gzip" />
-        <include name="IDU_EDU_LOGIN_EDU_LOGIN_JS" file="${root_gen_dir}\chrome\browser\resources\chromeos\edu_login\app.js" use_base_dir="false" type="BINDATA" compress="gzip" />
+        <include name="IDR_EDU_LOGIN_EDU_LOGIN_HTML" file="resources\chromeos\edu_login\edu_login.html" allowexternalscript="true" type="BINDATA" compress="gzip" />
+        <include name="IDR_EDU_LOGIN_EDU_LOGIN_JS" file="${root_gen_dir}\chrome\browser\resources\chromeos\edu_login\app.js" use_base_dir="false" type="BINDATA" compress="gzip" />
         <include name="IDR_EDU_LOGIN_EDU_LOGIN_UTIL_JS" file="resources\chromeos\edu_login\edu_login_util.js" type="BINDATA" compress="gzip" />
         <include name="IDR_EDU_LOGIN_BROWSER_PROXY_JS" file="resources\chromeos\edu_login\browser_proxy.js" type="BINDATA" compress="gzip" />
         <include name="IDR_EDU_LOGIN_EDU_LOGIN_BUTTON_JS" file="${root_gen_dir}\chrome\browser\resources\chromeos\edu_login\edu_login_button.js" use_base_dir="false" type="BINDATA" compress="gzip" />
         <include name="IDR_EDU_LOGIN_EDU_LOGIN_TEMPLATE_JS" file="${root_gen_dir}\chrome\browser\resources\chromeos\edu_login\edu_login_template.js" use_base_dir="false" type="BINDATA" compress="gzip" />
         <include name="IDR_EDU_LOGIN_EDU_LOGIN_CSS_JS" file="${root_gen_dir}\chrome\browser\resources\chromeos\edu_login\edu_login_css.js" use_base_dir="false" type ="BINDATA" />
+        <include name="IDR_EDU_LOGIN_EDU_LOGIN_WELCOME_JS" file="${root_gen_dir}\chrome\browser\resources\chromeos\edu_login\edu_login_welcome.js" use_base_dir="false" type ="BINDATA" compress="gzip" preprocess="true" />
         <include name="IDR_FAMILY_LINK_LOGO_SVG" file="resources\chromeos\family_link_logo.svg" type="BINDATA" compress="gzip" />
+
         <!-- Chrome OS Account Manager welcome screen resources -->
         <include name="IDR_ACCOUNT_MANAGER_SHARED_CSS" file="resources\chromeos\account_manager_shared.css" type="BINDATA" compress="gzip" />
         <include name="IDR_ACCOUNT_MANAGER_WELCOME_HTML" file="resources\chromeos\account_manager_welcome.html" allowexternalscript="true" type="BINDATA" compress="gzip" preprocess="true"/>
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 57fdf83..f1f4f71 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -2087,7 +2087,7 @@
 
       // Disable client-side phishing detection in the renderer if it is
       // disabled in the Profile preferences or the browser process.
-      if (!prefs->GetBoolean(prefs::kSafeBrowsingEnabled) ||
+      if (!safe_browsing::IsSafeBrowsingEnabled(*prefs) ||
           !g_browser_process->safe_browsing_detection_service()) {
         command_line->AppendSwitch(
             switches::kDisableClientSidePhishingDetection);
@@ -4248,7 +4248,7 @@
         base::BindOnce(
             &ChromeContentBrowserClient::GetSafeBrowsingUrlCheckerDelegate,
             base::Unretained(this),
-            profile->GetPrefs()->GetBoolean(prefs::kSafeBrowsingEnabled)),
+            safe_browsing::IsSafeBrowsingEnabled(*profile->GetPrefs())),
         wc_getter, frame_tree_node_id,
         url_lookup_service ? url_lookup_service->GetWeakPtr() : nullptr));
   }
diff --git a/chrome/browser/chrome_content_browser_client_receiver_bindings.cc b/chrome/browser/chrome_content_browser_client_receiver_bindings.cc
index b323a49..8ae9486 100644
--- a/chrome/browser/chrome_content_browser_client_receiver_bindings.cc
+++ b/chrome/browser/chrome_content_browser_client_receiver_bindings.cc
@@ -85,10 +85,9 @@
   if (!render_process_host)
     return;
 
-  bool safe_browsing_enabled =
-      Profile::FromBrowserContext(render_process_host->GetBrowserContext())
-          ->GetPrefs()
-          ->GetBoolean(prefs::kSafeBrowsingEnabled);
+  bool safe_browsing_enabled = safe_browsing::IsSafeBrowsingEnabled(
+      *Profile::FromBrowserContext(render_process_host->GetBrowserContext())
+           ->GetPrefs());
   base::CreateSingleThreadTaskRunner({content::BrowserThread::IO})
       ->PostTask(
           FROM_HERE,
diff --git a/chrome/browser/chrome_navigation_browsertest.cc b/chrome/browser/chrome_navigation_browsertest.cc
index 2f3d849e..d88587ba 100644
--- a/chrome/browser/chrome_navigation_browsertest.cc
+++ b/chrome/browser/chrome_navigation_browsertest.cc
@@ -35,6 +35,7 @@
 #include "components/variations/active_field_trials.h"
 #include "components/variations/hashing.h"
 #include "components/version_info/channel.h"
+#include "content/public/browser/context_menu_params.h"
 #include "content/public/browser/download_manager_delegate.h"
 #include "content/public/browser/navigation_entry.h"
 #include "content/public/browser/navigation_handle.h"
@@ -48,7 +49,6 @@
 #include "content/public/browser/web_contents_observer.h"
 #include "content/public/common/content_features.h"
 #include "content/public/common/content_switches.h"
-#include "content/public/common/context_menu_params.h"
 #include "content/public/common/url_constants.h"
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/download_test_observer.h"
diff --git a/chrome/browser/chromeos/arc/accessibility/accessibility_node_info_data_wrapper.cc b/chrome/browser/chromeos/arc/accessibility/accessibility_node_info_data_wrapper.cc
index e20594b..5656239 100644
--- a/chrome/browser/chromeos/arc/accessibility/accessibility_node_info_data_wrapper.cc
+++ b/chrome/browser/chromeos/arc/accessibility/accessibility_node_info_data_wrapper.cc
@@ -331,7 +331,11 @@
     // This ensures that the edited text will be read out appropriately.
     if (!text.empty()) {
       if (out_data->role == ax::mojom::Role::kTextField) {
-        out_data->SetValue(text);
+        // When the edited text is empty, Android framework shows |hint_text| in
+        // the text field and |text| is also populated with |hint_text|.
+        // Prevent the duplicated output of |hint_text|.
+        if (!GetProperty(AXBooleanProperty::SHOWING_HINT_TEXT))
+          out_data->SetValue(text);
       } else {
         names.push_back(text);
       }
diff --git a/chrome/browser/chromeos/arc/accessibility/ax_tree_source_arc_unittest.cc b/chrome/browser/chromeos/arc/accessibility/ax_tree_source_arc_unittest.cc
index bd5d409..89799ee 100644
--- a/chrome/browser/chromeos/arc/accessibility/ax_tree_source_arc_unittest.cc
+++ b/chrome/browser/chromeos/arc/accessibility/ax_tree_source_arc_unittest.cc
@@ -540,60 +540,85 @@
   root_window->root_node_id = 1;
 
   std::unique_ptr<ui::AXNodeData> data;
-  SetProperty(root, AXStringProperty::CLASS_NAME, "");
+  SetProperty(root, AXBooleanProperty::EDITABLE, true);
 
   // Populate the tree source with the data.
   CallNotifyAccessibilityEvent(event.get());
 
-  // Case for when both text property and content_description is non-empty.
-  SetProperty(root, AXBooleanProperty::EDITABLE, true);
-  SetProperty(root, AXStringProperty::TEXT, "foo@example.com");
-  SetProperty(root, AXStringProperty::CONTENT_DESCRIPTION,
-              "Type your email here.");
+  struct AndroidState {
+    std::string content_description, text, hint_text;
+    bool showingHint = false;
+  };
+  struct ChromeState {
+    std::string name, value;
+  };
 
-  CallSerializeNode(root, &data);
+  std::vector<std::pair<AndroidState, ChromeState>> test_cases = {
+      {
+          {"email", "editing_text", "", false},
+          {"email", "editing_text"},
+      },
+      {
+          {"email", "", "", false},
+          {"email", ""},
+      },
+      {
+          {"", "editing_text", "", false},
+          {"", "editing_text"},
+      },
+      {
+          // User input and hint text.
+          {"", "editing_text", "hint@example.com", false},
+          {"hint@example.com", "editing_text"},
+      },
+      {
+          // No user input. Hint text is non-empty.
+          {"", "hint@example.com", "hint@example.com", true},
+          {"hint@example.com", ""},
+      },
+      {
+          // User input is the same as hint text.
+          {"", "example@example.com", "example@example.com", false},
+          {"example@example.com", "example@example.com"},
+      },
+      {
+          // No user input. Content description and hint tex are non-empty.
+          {"email", "hint@example.com", "hint@example.com", true},
+          {"email hint@example.com", ""},
+      },
+      {
+          {"email", "editing_text", "hint@example.com", false},
+          {"email hint@example.com", "editing_text"},
+      },
+      {
+          {"", "", "", false},
+          {"", ""},
+      },
+  };
 
-  std::string prop;
-  ASSERT_TRUE(
-      data->GetStringAttribute(ax::mojom::StringAttribute::kName, &prop));
-  EXPECT_EQ("Type your email here.", prop);
+  for (const auto& test_case : test_cases) {
+    SetProperty(root, AXStringProperty::CONTENT_DESCRIPTION,
+                test_case.first.content_description);
+    SetProperty(root, AXStringProperty::TEXT, test_case.first.text);
+    SetProperty(root, AXStringProperty::HINT_TEXT, test_case.first.hint_text);
+    SetProperty(root, AXBooleanProperty::SHOWING_HINT_TEXT,
+                test_case.first.showingHint);
 
-  ASSERT_TRUE(
-      data->GetStringAttribute(ax::mojom::StringAttribute::kValue, &prop));
-  EXPECT_EQ("foo@example.com", prop);
+    CallSerializeNode(root, &data);
 
-  // Case for when text property is empty.
-  SetProperty(root, AXStringProperty::TEXT, "");
-  SetProperty(root, AXStringProperty::CONTENT_DESCRIPTION,
-              "Type your email here.");
+    std::string prop;
+    ASSERT_EQ(
+        !test_case.second.name.empty(),
+        data->GetStringAttribute(ax::mojom::StringAttribute::kName, &prop));
+    if (!test_case.second.name.empty())
+      EXPECT_EQ(test_case.second.name, prop);
 
-  CallSerializeNode(root, &data);
-
-  ASSERT_TRUE(
-      data->GetStringAttribute(ax::mojom::StringAttribute::kName, &prop));
-  EXPECT_EQ("Type your email here.", prop);
-  ASSERT_FALSE(
-      data->GetStringAttribute(ax::mojom::StringAttribute::kValue, &prop));
-
-  // Case for when only text property is non-empty.
-  SetProperty(root, AXStringProperty::TEXT, "foo@example.com");
-  SetProperty(root, AXStringProperty::CONTENT_DESCRIPTION, "");
-
-  CallSerializeNode(root, &data);
-
-  ASSERT_FALSE(
-      data->GetStringAttribute(ax::mojom::StringAttribute::kName, &prop));
-  ASSERT_TRUE(
-      data->GetStringAttribute(ax::mojom::StringAttribute::kValue, &prop));
-  EXPECT_EQ("foo@example.com", prop);
-
-  // Clearing string properties, the name and the value should not be populated.
-  root->string_properties->clear();
-  CallSerializeNode(root, &data);
-  ASSERT_FALSE(
-      data->GetStringAttribute(ax::mojom::StringAttribute::kName, &prop));
-  ASSERT_FALSE(
-      data->GetStringAttribute(ax::mojom::StringAttribute::kValue, &prop));
+    ASSERT_EQ(
+        !test_case.second.value.empty(),
+        data->GetStringAttribute(ax::mojom::StringAttribute::kValue, &prop));
+    if (!test_case.second.value.empty())
+      EXPECT_EQ(test_case.second.value, prop);
+  }
 }
 
 TEST_F(AXTreeSourceArcTest, AccessibleNameComputationWindow) {
diff --git a/chrome/browser/chromeos/arc/intent_helper/open_with_menu.cc b/chrome/browser/chromeos/arc/intent_helper/open_with_menu.cc
index d67fdb0..36fc749 100644
--- a/chrome/browser/chromeos/arc/intent_helper/open_with_menu.cc
+++ b/chrome/browser/chromeos/arc/intent_helper/open_with_menu.cc
@@ -11,7 +11,7 @@
 #include "chrome/grit/generated_resources.h"
 #include "components/renderer_context_menu/render_view_context_menu_proxy.h"
 #include "content/public/browser/browser_context.h"
-#include "content/public/common/context_menu_params.h"
+#include "content/public/browser/context_menu_params.h"
 #include "ui/base/l10n/l10n_util.h"
 
 namespace arc {
diff --git a/chrome/browser/chromeos/arc/intent_helper/start_smart_selection_action_menu.cc b/chrome/browser/chromeos/arc/intent_helper/start_smart_selection_action_menu.cc
index 5bc6b8d0..de24572 100644
--- a/chrome/browser/chromeos/arc/intent_helper/start_smart_selection_action_menu.cc
+++ b/chrome/browser/chromeos/arc/intent_helper/start_smart_selection_action_menu.cc
@@ -22,7 +22,7 @@
 #include "components/arc/mojom/intent_helper.mojom.h"
 #include "components/arc/session/arc_bridge_service.h"
 #include "components/renderer_context_menu/render_view_context_menu_proxy.h"
-#include "content/public/common/context_menu_params.h"
+#include "content/public/browser/context_menu_params.h"
 #include "ui/gfx/image/image_skia.h"
 #include "ui/gfx/image/image_skia_operations.h"
 
diff --git a/chrome/browser/chromeos/assistant/assistant_util.cc b/chrome/browser/chromeos/assistant/assistant_util.cc
index 8dc8a67e..6f158e4e 100644
--- a/chrome/browser/chromeos/assistant/assistant_util.cc
+++ b/chrome/browser/chromeos/assistant/assistant_util.cc
@@ -6,32 +6,127 @@
 
 #include <string>
 
-#include "base/strings/string_util.h"
-#include "base/system/sys_info.h"
 #include "chrome/browser/chromeos/login/demo_mode/demo_session.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
-#include "chromeos/constants/chromeos_features.h"
-#include "chromeos/constants/chromeos_switches.h"
 #include "chromeos/constants/devicetype.h"
 #include "chromeos/services/assistant/public/cpp/assistant_prefs.h"
 #include "components/language/core/browser/pref_names.h"
 #include "components/prefs/pref_service.h"
-#include "components/signin/public/identity_manager/account_info.h"
-#include "components/signin/public/identity_manager/consent_level.h"
 #include "components/signin/public/identity_manager/identity_manager.h"
 #include "components/user_manager/user_manager.h"
 #include "google_apis/gaia/gaia_auth_util.h"
 #include "third_party/icu/source/common/unicode/locid.h"
-#include "ui/chromeos/events/keyboard_layout_util.h"
 
 namespace {
 
-bool IsActiveDirectoryUser(const Profile* profile) {
-  const user_manager::User* user =
-      chromeos::ProfileHelper::Get()->GetUserByProfile(profile);
-  return user->IsActiveDirectoryUser();
+using ash::mojom::AssistantAllowedState;
+
+bool g_override_is_google_device = false;
+
+bool HasPrimaryAccount(const Profile* profile) {
+  auto* identity_manager =
+      IdentityManagerFactory::GetForProfileIfExists(profile);
+  if (!identity_manager)
+    return false;
+
+  return identity_manager->HasPrimaryAccount(
+      signin::ConsentLevel::kNotRequired);
+}
+
+bool IsGoogleDevice() {
+  return g_override_is_google_device || chromeos::IsGoogleBrandedDevice();
+}
+
+const user_manager::User* GetUser(const Profile* profile) {
+  return chromeos::ProfileHelper::Get()->GetUserByProfile(profile);
+}
+
+bool IsAssistantAllowedForUserType(const Profile* profile) {
+  return GetUser(profile)->HasGaiaAccount();
+}
+
+// Get the actual reason why the user type is not allowed.
+AssistantAllowedState GetErrorForUserType(const Profile* profile) {
+  DCHECK(!IsAssistantAllowedForUserType(profile));
+  switch (GetUser(profile)->GetType()) {
+    case user_manager::USER_TYPE_PUBLIC_ACCOUNT:
+      return AssistantAllowedState::DISALLOWED_BY_PUBLIC_SESSION;
+
+    case user_manager::USER_TYPE_SUPERVISED:
+      return AssistantAllowedState::DISALLOWED_BY_SUPERVISED_USER;
+
+    case user_manager::USER_TYPE_KIOSK_APP:
+    case user_manager::USER_TYPE_ARC_KIOSK_APP:
+    case user_manager::USER_TYPE_WEB_KIOSK_APP:
+      return AssistantAllowedState::DISALLOWED_BY_KIOSK_MODE;
+
+    case user_manager::USER_TYPE_ACTIVE_DIRECTORY:
+      return AssistantAllowedState::DISALLOWED_BY_ACCOUNT_TYPE;
+
+    case user_manager::USER_TYPE_GUEST:
+      return AssistantAllowedState::DISALLOWED_BY_ACCOUNT_TYPE;
+
+    case user_manager::USER_TYPE_REGULAR:
+    case user_manager::USER_TYPE_CHILD:
+      // This method should only be called for disallowed user types.
+      NOTREACHED();
+      return AssistantAllowedState::DISALLOWED_BY_ACCOUNT_TYPE;
+
+    case user_manager::NUM_USER_TYPES:
+      NOTREACHED();
+      return AssistantAllowedState::DISALLOWED_BY_ACCOUNT_TYPE;
+  }
+}
+
+bool IsAssistantAllowedForLocale(const Profile* profile) {
+  // String literals used in some cases in the array because their
+  // constant equivalents don't exist in:
+  // third_party/icu/source/common/unicode/uloc.h
+  const std::string kAllowedLocales[] = {ULOC_CANADA,  ULOC_CANADA_FRENCH,
+                                         ULOC_FRANCE,  ULOC_FRENCH,
+                                         ULOC_GERMANY, ULOC_ITALY,
+                                         ULOC_JAPAN,   ULOC_JAPANESE,
+                                         ULOC_UK,      ULOC_US,
+                                         "da",         "en_AU",
+                                         "en_IN",      "en_NZ",
+                                         "es_CO",      "es_ES",
+                                         "es_MX",      "fr_BE",
+                                         "it",         "nb",
+                                         "nl",         "nn",
+                                         "no",         "sv"};
+
+  const PrefService* prefs = profile->GetPrefs();
+  std::string pref_locale =
+      prefs->GetString(language::prefs::kApplicationLocale);
+  // Also accept runtime locale which maybe an approximation of user's pref
+  // locale.
+  const std::string kRuntimeLocale = icu::Locale::getDefault().getName();
+
+  base::ReplaceChars(pref_locale, "-", "_", &pref_locale);
+  bool allowed = base::Contains(kAllowedLocales, pref_locale) ||
+                 base::Contains(kAllowedLocales, kRuntimeLocale);
+
+  return allowed;
+}
+
+bool IsAssistantDisabledByPolicy(const Profile* profile) {
+  return profile->GetPrefs()->GetBoolean(
+      chromeos::assistant::prefs::kAssistantDisabledByPolicy);
+}
+
+bool IsEmailDomainSupported(const Profile* profile) {
+  const std::string email = GetUser(profile)->GetAccountId().GetUserEmail();
+  DCHECK(!email.empty());
+
+  return (gaia::ExtractDomainName(email) == "gmail.com" ||
+          gaia::ExtractDomainName(email) == "googlemail.com" ||
+          gaia::IsGoogleInternalAccountEmail(email));
+}
+
+bool HasDedicatedAssistantKey() {
+  return IsGoogleDevice();
 }
 
 }  // namespace
@@ -40,105 +135,38 @@
 
 ash::mojom::AssistantAllowedState IsAssistantAllowedForProfile(
     const Profile* profile) {
+  // Primary account might be missing during unittests.
+  if (!HasPrimaryAccount(profile))
+    return AssistantAllowedState::DISALLOWED_BY_NONPRIMARY_USER;
+
   if (!chromeos::ProfileHelper::IsPrimaryProfile(profile))
-    return ash::mojom::AssistantAllowedState::DISALLOWED_BY_NONPRIMARY_USER;
+    return AssistantAllowedState::DISALLOWED_BY_NONPRIMARY_USER;
 
   if (profile->IsOffTheRecord())
-    return ash::mojom::AssistantAllowedState::DISALLOWED_BY_INCOGNITO;
-
-  if (profile->IsLegacySupervised())
-    return ash::mojom::AssistantAllowedState::DISALLOWED_BY_SUPERVISED_USER;
+    return AssistantAllowedState::DISALLOWED_BY_INCOGNITO;
 
   if (chromeos::DemoSession::IsDeviceInDemoMode())
-    return ash::mojom::AssistantAllowedState::DISALLOWED_BY_DEMO_MODE;
+    return AssistantAllowedState::DISALLOWED_BY_DEMO_MODE;
 
-  if (user_manager::UserManager::Get()->IsLoggedInAsPublicAccount())
-    return ash::mojom::AssistantAllowedState::DISALLOWED_BY_PUBLIC_SESSION;
+  if (!IsAssistantAllowedForUserType(profile))
+    return GetErrorForUserType(profile);
 
-  if (IsActiveDirectoryUser(profile))
-    return ash::mojom::AssistantAllowedState::DISALLOWED_BY_ACCOUNT_TYPE;
+  if (!IsAssistantAllowedForLocale(profile))
+    return AssistantAllowedState::DISALLOWED_BY_LOCALE;
 
-  if (user_manager::UserManager::Get()->IsLoggedInAsAnyKioskApp()) {
-    return ash::mojom::AssistantAllowedState::DISALLOWED_BY_KIOSK_MODE;
-  }
+  if (IsAssistantDisabledByPolicy(profile))
+    return AssistantAllowedState::DISALLOWED_BY_POLICY;
 
-  // String literals used in some cases in the array because their
-  // constant equivalents don't exist in:
-  // third_party/icu/source/common/unicode/uloc.h
-  const std::string kAllowedLocales[] = {ULOC_CANADA,
-                                         ULOC_CANADA_FRENCH,
-                                         ULOC_FRANCE,
-                                         ULOC_FRENCH,
-                                         ULOC_GERMANY,
-                                         ULOC_ITALY,
-                                         ULOC_JAPAN,
-                                         ULOC_JAPANESE,
-                                         ULOC_UK,
-                                         ULOC_US,
-                                         "da",
-                                         "en_AU",
-                                         "en_IN",
-                                         "en_NZ",
-                                         "es_CO",
-                                         "es_ES",
-                                         "es_MX",
-                                         "fr_BE",
-                                         "it",
-                                         "nb",
-                                         "nl",
-                                         "nn",
-                                         "no",
-                                         "sv"};
+  // Bypass the email domain check when the account is logged in a device with
+  // dedicated Assistant key.
+  if (!HasDedicatedAssistantKey() && !IsEmailDomainSupported(profile))
+    return AssistantAllowedState::DISALLOWED_BY_ACCOUNT_TYPE;
 
-  const PrefService* prefs = profile->GetPrefs();
-  std::string pref_locale =
-      prefs->GetString(language::prefs::kApplicationLocale);
-  // Also accept runtime locale which maybe an approximation of user's pref
-  // locale.
-  const std::string kRuntimeLocale = icu::Locale::getDefault().getName();
-  // Bypass locale check when using fake gaia login. There is no need to enforce
-  // in these test environments.
-  if (!chromeos::switches::IsGaiaServicesDisabled() && !pref_locale.empty()) {
-    base::ReplaceChars(pref_locale, "-", "_", &pref_locale);
-    bool disallowed = !base::Contains(kAllowedLocales, pref_locale) &&
-                      !base::Contains(kAllowedLocales, kRuntimeLocale);
+  return AssistantAllowedState::ALLOWED;
+}
 
-    if (disallowed)
-      return ash::mojom::AssistantAllowedState::DISALLOWED_BY_LOCALE;
-  }
-
-  if (prefs->GetBoolean(chromeos::assistant::prefs::kAssistantDisabledByPolicy))
-    return ash::mojom::AssistantAllowedState::DISALLOWED_BY_POLICY;
-
-  // Bypass the account type check when using fake gaia login, e.g. in Tast
-  // tests, or the account is logged in a device with dedicated Assistant key.
-  if (!chromeos::switches::IsGaiaServicesDisabled() &&
-      !chromeos::IsGoogleBrandedDevice()) {
-    // Only enable non-dasher accounts for devices without physical key.
-    bool account_supported = false;
-    auto* identity_manager =
-        IdentityManagerFactory::GetForProfileIfExists(profile);
-
-    if (identity_manager) {
-      // This function doesn't care about browser sync consent. We don't
-      // DCHECK that an account exists because some tests don't have one.
-      const std::string email =
-          identity_manager
-              ->GetPrimaryAccountInfo(signin::ConsentLevel::kNotRequired)
-              .email;
-      if (!email.empty() &&
-          (gaia::ExtractDomainName(email) == "gmail.com" ||
-           gaia::ExtractDomainName(email) == "googlemail.com" ||
-           gaia::IsGoogleInternalAccountEmail(email))) {
-        account_supported = true;
-      }
-    }
-
-    if (!account_supported)
-      return ash::mojom::AssistantAllowedState::DISALLOWED_BY_ACCOUNT_TYPE;
-  }
-
-  return ash::mojom::AssistantAllowedState::ALLOWED;
+void OverrideIsGoogleDeviceForTesting(bool is_google_device) {
+  g_override_is_google_device = is_google_device;
 }
 
 }  // namespace assistant
diff --git a/chrome/browser/chromeos/assistant/assistant_util.h b/chrome/browser/chromeos/assistant/assistant_util.h
index cb4ec59..96339b5 100644
--- a/chrome/browser/chromeos/assistant/assistant_util.h
+++ b/chrome/browser/chromeos/assistant/assistant_util.h
@@ -15,6 +15,8 @@
 ash::mojom::AssistantAllowedState IsAssistantAllowedForProfile(
     const Profile* profile);
 
+void OverrideIsGoogleDeviceForTesting(bool is_google_device);
+
 }  // namespace assistant
 
 #endif  // CHROME_BROWSER_CHROMEOS_ASSISTANT_ASSISTANT_UTIL_H_
diff --git a/chrome/browser/chromeos/assistant/assistant_util_unittest.cc b/chrome/browser/chromeos/assistant/assistant_util_unittest.cc
index 0f3c38d..c8258e78 100644
--- a/chrome/browser/chromeos/assistant/assistant_util_unittest.cc
+++ b/chrome/browser/chromeos/assistant/assistant_util_unittest.cc
@@ -6,25 +6,19 @@
 
 #include <memory>
 
-#include "base/files/scoped_temp_dir.h"
 #include "base/strings/utf_string_conversions.h"
-#include "base/test/scoped_command_line.h"
 #include "chrome/browser/chromeos/login/demo_mode/demo_session.h"
 #include "chrome/browser/chromeos/login/users/fake_chrome_user_manager.h"
-#include "chrome/browser/signin/identity_manager_factory.h"
 #include "chrome/browser/signin/identity_test_environment_profile_adaptor.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile.h"
 #include "chrome/test/base/testing_profile_manager.h"
-#include "chromeos/constants/chromeos_features.h"
 #include "components/account_id/account_id.h"
 #include "components/language/core/browser/pref_names.h"
 #include "components/prefs/testing_pref_service.h"
 #include "components/signin/public/identity_manager/consent_level.h"
 #include "components/signin/public/identity_manager/identity_manager.h"
 #include "components/signin/public/identity_manager/identity_test_environment.h"
-#include "components/signin/public/identity_manager/identity_test_utils.h"
-#include "components/sync_preferences/testing_pref_service_syncable.h"
 #include "components/user_manager/scoped_user_manager.h"
 #include "components/user_manager/user_manager.h"
 #include "components/user_manager/user_type.h"
@@ -40,6 +34,12 @@
 constexpr char kTestProfileName[] = "user@gmail.com";
 constexpr char kTestGaiaId[] = "1234567890";
 
+class ScopedSpoofGoogleBrandedDevice {
+ public:
+  ScopedSpoofGoogleBrandedDevice() { OverrideIsGoogleDeviceForTesting(true); }
+  ~ScopedSpoofGoogleBrandedDevice() { OverrideIsGoogleDeviceForTesting(false); }
+};
+
 class FakeUserManagerWithLocalState : public chromeos::FakeChromeUserManager {
  public:
   explicit FakeUserManagerWithLocalState(
@@ -75,39 +75,32 @@
       user_manager::UserType user_type = user_manager::USER_TYPE_REGULAR)
       : fake_user_manager_(fake_user_manager),
         identity_test_env_(identity_test_env),
-
         account_id_(account_id) {
-    // Prevent access to DBus. This switch is reset in case set from test SetUp
-    // due massive usage of InitFromArgv.
-    base::CommandLine& command_line = *base::CommandLine::ForCurrentProcess();
-    if (!command_line.HasSwitch(switches::kTestType))
-      command_line.AppendSwitch(switches::kTestType);
+    PreventAccessToDBus();
+    RunSanityChecks(user_type);
+    AddUser(user_type);
 
-    switch (user_type) {
-      case user_manager::USER_TYPE_REGULAR:  // fallthrough
-      case user_manager::USER_TYPE_ACTIVE_DIRECTORY:
-        LogIn();
-        break;
-      case user_manager::USER_TYPE_PUBLIC_ACCOUNT:
-        LogInAsPublicAccount();
-        break;
-      case user_manager::USER_TYPE_KIOSK_APP:
-        LogInKioskApp();
-        break;
-      case user_manager::USER_TYPE_ARC_KIOSK_APP:
-        LogInArcKioskApp();
-        break;
-      default:
-        NOTREACHED();
-    }
+    fake_user_manager_->LoginUser(account_id_);
+
+    MakeAccountAvailableAsPrimaryAccount(user_type);
   }
 
   ~ScopedLogIn() { fake_user_manager_->RemoveUserFromList(account_id_); }
 
  private:
-  void LogIn() {
-    fake_user_manager_->AddUser(account_id_);
-    fake_user_manager_->LoginUser(account_id_);
+  // Prevent access to DBus. This switch is reset in case set from test SetUp
+  // due massive usage of InitFromArgv.
+  void PreventAccessToDBus() {
+    base::CommandLine& command_line = *base::CommandLine::ForCurrentProcess();
+    if (!command_line.HasSwitch(switches::kTestType))
+      command_line.AppendSwitch(switches::kTestType);
+  }
+
+  void MakeAccountAvailableAsPrimaryAccount(user_manager::UserType user_type) {
+    // Guest user can never be a primary account.
+    if (user_type == user_manager::USER_TYPE_GUEST)
+      return;
+
     if (!identity_test_env_->identity_manager()->HasPrimaryAccount(
             signin::ConsentLevel::kNotRequired)) {
       identity_test_env_->MakeUnconsentedPrimaryAccountAvailable(
@@ -115,19 +108,67 @@
     }
   }
 
-  void LogInAsPublicAccount() {
-    fake_user_manager_->AddPublicAccountUser(account_id_);
-    fake_user_manager_->LoginUser(account_id_);
+  // Run sanity checks ensuring the account id is valid for the given user type.
+  // If these checks go off your test is testing something that can not happen.
+  void RunSanityChecks(user_manager::UserType user_type) const {
+    switch (user_type) {
+      case user_manager::USER_TYPE_REGULAR:
+      case user_manager::USER_TYPE_CHILD:
+        EXPECT_TRUE(IsGaiaAccount());
+        return;
+      case user_manager::USER_TYPE_SUPERVISED:
+      case user_manager::USER_TYPE_ACTIVE_DIRECTORY:
+      case user_manager::USER_TYPE_PUBLIC_ACCOUNT:
+      case user_manager::USER_TYPE_KIOSK_APP:
+      case user_manager::USER_TYPE_ARC_KIOSK_APP:
+      case user_manager::USER_TYPE_WEB_KIOSK_APP:
+        EXPECT_FALSE(IsGaiaAccount());
+        return;
+      case user_manager::USER_TYPE_GUEST:
+        // Guest user must use the guest user account id.
+        EXPECT_EQ(account_id_, fake_user_manager_->GetGuestAccountId());
+        return;
+      case user_manager::NUM_USER_TYPES:
+        NOTREACHED();
+    }
   }
 
-  void LogInKioskApp() {
-    fake_user_manager_->AddKioskAppUser(account_id_);
-    fake_user_manager_->LoginUser(account_id_);
+  void AddUser(user_manager::UserType user_type) {
+    switch (user_type) {
+      case user_manager::USER_TYPE_REGULAR:
+        fake_user_manager_->AddUser(account_id_);
+        return;
+      case user_manager::USER_TYPE_ACTIVE_DIRECTORY:
+        fake_user_manager_->AddActiveDirectoryUser(account_id_);
+        return;
+      case user_manager::USER_TYPE_SUPERVISED:
+        fake_user_manager_->AddSupervisedUser(account_id_);
+        return;
+      case user_manager::USER_TYPE_PUBLIC_ACCOUNT:
+        fake_user_manager_->AddPublicAccountUser(account_id_);
+        return;
+      case user_manager::USER_TYPE_KIOSK_APP:
+        fake_user_manager_->AddKioskAppUser(account_id_);
+        return;
+      case user_manager::USER_TYPE_ARC_KIOSK_APP:
+        fake_user_manager_->AddArcKioskAppUser(account_id_);
+        return;
+      case user_manager::USER_TYPE_WEB_KIOSK_APP:
+        fake_user_manager_->AddWebKioskAppUser(account_id_);
+        return;
+      case user_manager::USER_TYPE_CHILD:
+        fake_user_manager_->AddChildUser(account_id_);
+        return;
+      case user_manager::USER_TYPE_GUEST:
+        fake_user_manager_->AddGuestUser();
+        return;
+      case user_manager::NUM_USER_TYPES:
+        NOTREACHED();
+    }
   }
 
-  void LogInArcKioskApp() {
-    fake_user_manager_->AddArcKioskAppUser(account_id_);
-    fake_user_manager_->LoginUser(account_id_);
+  bool IsGaiaAccount() const {
+    return account_id_.GetAccountType() == AccountType::GOOGLE;
   }
 
   FakeUserManagerWithLocalState* fake_user_manager_;
@@ -137,10 +178,6 @@
   DISALLOW_COPY_AND_ASSIGN(ScopedLogIn);
 };
 
-AccountId GetActiveDirectoryUserAccountId(const std::string& email) {
-  return AccountId::AdFromUserEmailObjGuid(email, "<obj_guid>");
-}
-
 }  // namespace
 
 class ChromeAssistantUtilTest : public testing::Test {
@@ -149,8 +186,6 @@
   ~ChromeAssistantUtilTest() override = default;
 
   void SetUp() override {
-    command_line_ = std::make_unique<base::test::ScopedCommandLine>();
-
     ASSERT_TRUE(data_dir_.CreateUniqueTempDir());
     profile_manager_ = std::make_unique<TestingProfileManager>(
         TestingBrowserProcess::GetGlobal());
@@ -177,7 +212,6 @@
     profile_manager_->DeleteTestingProfile(kTestProfileName);
     profile_ = nullptr;
     profile_manager_.reset();
-    command_line_.reset();
   }
 
   TestingProfile* profile() { return profile_; }
@@ -191,8 +225,30 @@
         user_manager::UserManager::Get());
   }
 
+  AccountId GetActiveDirectoryUserAccountId(const TestingProfile* profile) {
+    return AccountId::AdFromUserEmailObjGuid(profile->GetProfileUserName(),
+                                             "<obj_guid>");
+  }
+
+  AccountId GetNonGaiaUserAccountId(const TestingProfile* profile) {
+    return AccountId::FromUserEmail(profile->GetProfileUserName());
+  }
+
+  AccountId GetGaiaUserAccountId(const TestingProfile* profile) {
+    return AccountId::FromUserEmailGaiaId(profile->GetProfileUserName(),
+                                          kTestGaiaId);
+  }
+
+  AccountId GetGaiaUserAccountId(const std::string& user_name,
+                                 const std::string& gaia_id) {
+    return AccountId::FromUserEmailGaiaId(user_name, gaia_id);
+  }
+
+  AccountId GetGuestAccountId() {
+    return GetFakeUserManager()->GetGuestAccountId();
+  }
+
  private:
-  std::unique_ptr<base::test::ScopedCommandLine> command_line_;
   content::BrowserTaskEnvironment task_environment_;
   base::ScopedTempDir data_dir_;
   std::unique_ptr<IdentityTestEnvironmentProfileAdaptor>
@@ -205,13 +261,20 @@
   DISALLOW_COPY_AND_ASSIGN(ChromeAssistantUtilTest);
 };
 
-TEST_F(ChromeAssistantUtilTest, IsAssistantAllowedForProfile_SecondaryUser) {
-  ScopedLogIn login2(
-      GetFakeUserManager(), identity_test_env(),
-      AccountId::FromUserEmailGaiaId("user2@gmail.com", "0123456789"));
+TEST_F(ChromeAssistantUtilTest, IsAssistantAllowedForProfile_PrimaryUser) {
   ScopedLogIn login(GetFakeUserManager(), identity_test_env(),
-                    AccountId::FromUserEmailGaiaId(
-                        profile()->GetProfileUserName(), kTestGaiaId));
+                    GetGaiaUserAccountId(profile()));
+
+  EXPECT_EQ(ash::mojom::AssistantAllowedState::ALLOWED,
+            IsAssistantAllowedForProfile(profile()));
+}
+
+TEST_F(ChromeAssistantUtilTest, IsAssistantAllowedForProfile_SecondaryUser) {
+  ScopedLogIn secondary_user_login(
+      GetFakeUserManager(), identity_test_env(),
+      GetGaiaUserAccountId("user2@gmail.com", "0123456789"));
+  ScopedLogIn primary_user_login(GetFakeUserManager(), identity_test_env(),
+                                 GetGaiaUserAccountId(profile()));
 
   EXPECT_EQ(ash::mojom::AssistantAllowedState::DISALLOWED_BY_NONPRIMARY_USER,
             IsAssistantAllowedForProfile(profile()));
@@ -219,13 +282,32 @@
 
 TEST_F(ChromeAssistantUtilTest, IsAssistantAllowedForProfile_SupervisedUser) {
   ScopedLogIn login(GetFakeUserManager(), identity_test_env(),
-                    AccountId::FromUserEmailGaiaId(
-                        profile()->GetProfileUserName(), kTestGaiaId));
+                    GetNonGaiaUserAccountId(profile()),
+                    user_manager::USER_TYPE_SUPERVISED);
+
   profile()->SetSupervisedUserId("foo");
+
   EXPECT_EQ(ash::mojom::AssistantAllowedState::DISALLOWED_BY_SUPERVISED_USER,
             IsAssistantAllowedForProfile(profile()));
 }
 
+TEST_F(ChromeAssistantUtilTest, IsAssistantAllowedForProfile_ChildUser) {
+  ScopedLogIn login(GetFakeUserManager(), identity_test_env(),
+                    GetGaiaUserAccountId(profile()),
+                    user_manager::USER_TYPE_CHILD);
+
+  EXPECT_EQ(ash::mojom::AssistantAllowedState::ALLOWED,
+            IsAssistantAllowedForProfile(profile()));
+}
+
+TEST_F(ChromeAssistantUtilTest, IsAssistantAllowedForProfile_GuestUser) {
+  ScopedLogIn login(GetFakeUserManager(), identity_test_env(),
+                    GetGuestAccountId(), user_manager::USER_TYPE_GUEST);
+
+  EXPECT_EQ(ash::mojom::AssistantAllowedState::DISALLOWED_BY_NONPRIMARY_USER,
+            IsAssistantAllowedForProfile(profile()));
+}
+
 TEST_F(ChromeAssistantUtilTest, IsAssistantAllowedForProfile_Locale) {
   profile()->GetTestingPrefService()->SetString(
       language::prefs::kApplicationLocale, "he");
@@ -233,8 +315,7 @@
   const icu::Locale& old_locale = icu::Locale::getDefault();
   icu::Locale::setDefault(icu::Locale("he"), error_code);
   ScopedLogIn login(GetFakeUserManager(), identity_test_env(),
-                    AccountId::FromUserEmailGaiaId(
-                        profile()->GetProfileUserName(), kTestGaiaId));
+                    GetGaiaUserAccountId(profile()));
 
   EXPECT_EQ(ash::mojom::AssistantAllowedState::DISALLOWED_BY_LOCALE,
             IsAssistantAllowedForProfile(profile()));
@@ -245,7 +326,7 @@
   chromeos::DemoSession::SetDemoConfigForTesting(
       chromeos::DemoSession::DemoModeConfig::kOnline);
   ScopedLogIn login(GetFakeUserManager(), identity_test_env(),
-                    AccountId::FromUserEmail(profile()->GetProfileUserName()),
+                    GetNonGaiaUserAccountId(profile()),
                     user_manager::USER_TYPE_PUBLIC_ACCOUNT);
   EXPECT_EQ(ash::mojom::AssistantAllowedState::DISALLOWED_BY_DEMO_MODE,
             IsAssistantAllowedForProfile(profile()));
@@ -256,36 +337,45 @@
 
 TEST_F(ChromeAssistantUtilTest, IsAssistantAllowedForProfile_PublicSession) {
   ScopedLogIn login(GetFakeUserManager(), identity_test_env(),
-                    AccountId::FromUserEmail(profile()->GetProfileUserName()),
+                    GetNonGaiaUserAccountId(profile()),
                     user_manager::USER_TYPE_PUBLIC_ACCOUNT);
   EXPECT_EQ(ash::mojom::AssistantAllowedState::DISALLOWED_BY_PUBLIC_SESSION,
             IsAssistantAllowedForProfile(profile()));
 }
 
 TEST_F(ChromeAssistantUtilTest, IsAssistantAllowedForProfile_NonGmail) {
-  ScopedLogIn login(GetFakeUserManager(), identity_test_env(),
-                    AccountId::FromUserEmailGaiaId("user2@someotherdomain.com",
-                                                   "0123456789"));
+  ScopedLogIn login(
+      GetFakeUserManager(), identity_test_env(),
+      GetGaiaUserAccountId("user2@someotherdomain.com", "0123456789"));
 
   EXPECT_EQ(ash::mojom::AssistantAllowedState::DISALLOWED_BY_ACCOUNT_TYPE,
             IsAssistantAllowedForProfile(profile()));
 }
 
 TEST_F(ChromeAssistantUtilTest, IsAssistantAllowedForProfile_GoogleMail) {
-  ScopedLogIn login(
-      GetFakeUserManager(), identity_test_env(),
-      AccountId::FromUserEmailGaiaId("user2@googlemail.com", "0123456789"));
+  ScopedLogIn login(GetFakeUserManager(), identity_test_env(),
+                    GetGaiaUserAccountId("user2@googlemail.com", "0123456789"));
 
   EXPECT_EQ(ash::mojom::AssistantAllowedState::ALLOWED,
             IsAssistantAllowedForProfile(profile()));
 }
 
 TEST_F(ChromeAssistantUtilTest,
-       IsAssistantAllowedForProfile_ActiveDirectoryUser) {
+       IsAssistantAllowed_AllowsNonGmailOnGoogleBrandedDevices) {
   ScopedLogIn login(
       GetFakeUserManager(), identity_test_env(),
-      GetActiveDirectoryUserAccountId(profile()->GetProfileUserName()),
-      user_manager::USER_TYPE_ACTIVE_DIRECTORY);
+      GetGaiaUserAccountId("user2@someotherdomain.com", "0123456789"));
+
+  ScopedSpoofGoogleBrandedDevice make_google_branded_device;
+  EXPECT_EQ(ash::mojom::AssistantAllowedState::ALLOWED,
+            IsAssistantAllowedForProfile(profile()));
+}
+
+TEST_F(ChromeAssistantUtilTest,
+       IsAssistantAllowedForProfile_ActiveDirectoryUser) {
+  ScopedLogIn login(GetFakeUserManager(), identity_test_env(),
+                    GetActiveDirectoryUserAccountId(profile()),
+                    user_manager::USER_TYPE_ACTIVE_DIRECTORY);
 
   EXPECT_EQ(ash::mojom::AssistantAllowedState::DISALLOWED_BY_ACCOUNT_TYPE,
             IsAssistantAllowedForProfile(profile()));
@@ -293,7 +383,7 @@
 
 TEST_F(ChromeAssistantUtilTest, IsAssistantAllowedForKiosk_KioskApp) {
   ScopedLogIn login(GetFakeUserManager(), identity_test_env(),
-                    AccountId::FromUserEmail(profile()->GetProfileUserName()),
+                    GetNonGaiaUserAccountId(profile()),
                     user_manager::USER_TYPE_KIOSK_APP);
 
   EXPECT_EQ(ash::mojom::AssistantAllowedState::DISALLOWED_BY_KIOSK_MODE,
@@ -302,11 +392,20 @@
 
 TEST_F(ChromeAssistantUtilTest, IsAssistantAllowedForKiosk_ArcKioskApp) {
   ScopedLogIn login(GetFakeUserManager(), identity_test_env(),
-                    AccountId::FromUserEmail(profile()->GetProfileUserName()),
+                    GetNonGaiaUserAccountId(profile()),
                     user_manager::USER_TYPE_ARC_KIOSK_APP);
 
   EXPECT_EQ(ash::mojom::AssistantAllowedState::DISALLOWED_BY_KIOSK_MODE,
             IsAssistantAllowedForProfile(profile()));
 }
 
+TEST_F(ChromeAssistantUtilTest, IsAssistantAllowedForKiosk_WebKioskApp) {
+  ScopedLogIn login(GetFakeUserManager(), identity_test_env(),
+                    GetNonGaiaUserAccountId(profile()),
+                    user_manager::USER_TYPE_WEB_KIOSK_APP);
+
+  EXPECT_EQ(ash::mojom::AssistantAllowedState::DISALLOWED_BY_KIOSK_MODE,
+            IsAssistantAllowedForProfile(profile()));
+}
+
 }  // namespace assistant
diff --git a/chrome/browser/chromeos/lock_screen_apps/lock_screen_profile_creator_impl.cc b/chrome/browser/chromeos/lock_screen_apps/lock_screen_profile_creator_impl.cc
index 89e499b..125e853 100644
--- a/chrome/browser/chromeos/lock_screen_apps/lock_screen_profile_creator_impl.cc
+++ b/chrome/browser/chromeos/lock_screen_apps/lock_screen_profile_creator_impl.cc
@@ -93,6 +93,7 @@
     // similar to http://crbug.com/461493).
     // TODO(tbarzic): Revisit this if webviews get enabled for lock screen apps.
     profile->GetPrefs()->SetBoolean(prefs::kSafeBrowsingEnabled, false);
+    profile->GetPrefs()->SetBoolean(prefs::kSafeBrowsingEnhanced, false);
     return;
   }
 
diff --git a/chrome/browser/chromeos/login/help_app_launcher.h b/chrome/browser/chromeos/login/help_app_launcher.h
index b21de3e..40c2d1c 100644
--- a/chrome/browser/chromeos/login/help_app_launcher.h
+++ b/chrome/browser/chromeos/login/help_app_launcher.h
@@ -42,6 +42,9 @@
     HELP_WILCO_DOCK = 9385025,
     // Shown under "Need help?" button on parent access dialog.
     HELP_PARENT_ACCESS_CODE = 7307262,
+    // Shown as "Learn more" for ADB sideloading dialog. The original URL is
+    // https://support.google.com/chromebook/?p=develop_android_apps
+    HELP_ADB_SIDELOADING = 9770692,
   };
 
   // The dialog is shown as a child of |parent_window|. If |parent_window| is
diff --git a/chrome/browser/chromeos/login/screens/enable_adb_sideloading_screen.cc b/chrome/browser/chromeos/login/screens/enable_adb_sideloading_screen.cc
index 37ef30d7..3e20072 100644
--- a/chrome/browser/chromeos/login/screens/enable_adb_sideloading_screen.cc
+++ b/chrome/browser/chromeos/login/screens/enable_adb_sideloading_screen.cc
@@ -156,8 +156,7 @@
 }
 
 void EnableAdbSideloadingScreen::OnLearnMore() {
-  // TODO(victorhsieh): replace the help center link
-  HelpAppLauncher::HelpTopic topic = HelpAppLauncher::HELP_POWERWASH;
+  HelpAppLauncher::HelpTopic topic = HelpAppLauncher::HELP_ADB_SIDELOADING;
   VLOG(1) << "Trying to view help article " << topic;
   if (!help_app_.get()) {
     help_app_ = new HelpAppLauncher(
diff --git a/chrome/browser/chromeos/login/users/fake_chrome_user_manager.cc b/chrome/browser/chromeos/login/users/fake_chrome_user_manager.cc
index 9acafcf6..b1151f880 100644
--- a/chrome/browser/chromeos/login/users/fake_chrome_user_manager.cc
+++ b/chrome/browser/chromeos/login/users/fake_chrome_user_manager.cc
@@ -182,6 +182,15 @@
   return user;
 }
 
+user_manager::User* FakeChromeUserManager::AddActiveDirectoryUser(
+    const AccountId& account_id) {
+  DCHECK(account_id.GetAccountType() == AccountType::ACTIVE_DIRECTORY);
+  return AddUserWithAffiliationAndTypeAndProfile(
+      account_id, /*is_affiliated=*/false,
+      user_manager::USER_TYPE_ACTIVE_DIRECTORY,
+      /*profile=*/nullptr);
+}
+
 bool FakeChromeUserManager::AreEphemeralUsersEnabled() const {
   return fake_ephemeral_users_enabled_;
 }
diff --git a/chrome/browser/chromeos/login/users/fake_chrome_user_manager.h b/chrome/browser/chromeos/login/users/fake_chrome_user_manager.h
index ee2a9211..05339d3 100644
--- a/chrome/browser/chromeos/login/users/fake_chrome_user_manager.h
+++ b/chrome/browser/chromeos/login/users/fake_chrome_user_manager.h
@@ -36,6 +36,7 @@
   user_manager::User* AddWebKioskAppUser(const AccountId& account_id);
   user_manager::User* AddSupervisedUser(const AccountId& account_id);
   user_manager::User* AddPublicAccountUser(const AccountId& account_id);
+  user_manager::User* AddActiveDirectoryUser(const AccountId& account_id);
 
   // Calculates the user name hash and calls UserLoggedIn to login a user.
   // Sets the user as having its profile created if |set_profile_created_flag|
diff --git a/chrome/browser/download/android/download_controller_base.h b/chrome/browser/download/android/download_controller_base.h
index 478ffa2..e80b9e52 100644
--- a/chrome/browser/download/android/download_controller_base.h
+++ b/chrome/browser/download/android/download_controller_base.h
@@ -10,8 +10,8 @@
 #include "base/callback.h"
 #include "components/download/public/common/download_item.h"
 #include "components/download/public/common/download_start_observer.h"
+#include "content/public/browser/context_menu_params.h"
 #include "content/public/browser/web_contents.h"
-#include "content/public/common/context_menu_params.h"
 #include "net/http/http_content_disposition.h"
 #include "net/http/http_request_headers.h"
 #include "net/http/http_response_headers.h"
diff --git a/chrome/browser/download/download_browsertest.cc b/chrome/browser/download/download_browsertest.cc
index 24e1ab9c..4fc4704 100644
--- a/chrome/browser/download/download_browsertest.cc
+++ b/chrome/browser/download/download_browsertest.cc
@@ -105,6 +105,7 @@
 #include "components/services/quarantine/test_support.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
+#include "content/public/browser/context_menu_params.h"
 #include "content/public/browser/device_service.h"
 #include "content/public/browser/download_manager.h"
 #include "content/public/browser/download_request_utils.h"
@@ -117,7 +118,6 @@
 #include "content/public/common/content_client.h"
 #include "content/public/common/content_features.h"
 #include "content/public/common/content_switches.h"
-#include "content/public/common/context_menu_params.h"
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/download_test_observer.h"
 #include "content/public/test/slow_download_http_response.h"
diff --git a/chrome/browser/extensions/api/context_menus/context_menu_apitest.cc b/chrome/browser/extensions/api/context_menus/context_menu_apitest.cc
index 66dd875..52acc9c1 100644
--- a/chrome/browser/extensions/api/context_menus/context_menu_apitest.cc
+++ b/chrome/browser/extensions/api/context_menus/context_menu_apitest.cc
@@ -16,8 +16,8 @@
 #include "chrome/common/extensions/api/context_menus.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "components/version_info/channel.h"
+#include "content/public/browser/context_menu_params.h"
 #include "content/public/browser/render_frame_host.h"
-#include "content/public/common/context_menu_params.h"
 #include "content/public/test/browser_test_utils.h"
 #include "extensions/common/scoped_worker_based_extensions_channel.h"
 #include "extensions/test/result_catcher.h"
diff --git a/chrome/browser/extensions/api/tabs/tabs_api.cc b/chrome/browser/extensions/api/tabs/tabs_api.cc
index 9bae87b..a4575e7 100644
--- a/chrome/browser/extensions/api/tabs/tabs_api.cc
+++ b/chrome/browser/extensions/api/tabs/tabs_api.cc
@@ -547,9 +547,8 @@
 
     // Initialize default window bounds according to window type.
     ui::WindowShowState ignored_show_state = ui::SHOW_STATE_DEFAULT;
-    WindowSizer::GetBrowserWindowBoundsAndShowState(std::string(), gfx::Rect(),
-                                                    nullptr, &window_bounds,
-                                                    &ignored_show_state);
+    WindowSizer::GetBrowserWindowBoundsAndShowState(
+        gfx::Rect(), nullptr, &window_bounds, &ignored_show_state);
 
     // Any part of the bounds can optionally be set by the caller.
     if (create_data->left)
diff --git a/chrome/browser/extensions/api/web_navigation/web_navigation_apitest.cc b/chrome/browser/extensions/api/web_navigation/web_navigation_apitest.cc
index cdb2b255..1d9592e 100644
--- a/chrome/browser/extensions/api/web_navigation/web_navigation_apitest.cc
+++ b/chrome/browser/extensions/api/web_navigation/web_navigation_apitest.cc
@@ -30,13 +30,13 @@
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "content/public/browser/browser_thread.h"
+#include "content/public/browser/context_menu_params.h"
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/render_view_host.h"
 #include "content/public/browser/render_widget_host.h"
 #include "content/public/browser/web_contents.h"
-#include "content/public/common/context_menu_params.h"
 #include "content/public/common/url_constants.h"
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/download_test_observer.h"
diff --git a/chrome/browser/extensions/api/webstore_private/webstore_private_api.cc b/chrome/browser/extensions/api/webstore_private/webstore_private_api.cc
index 1442287..ba2f59be 100644
--- a/chrome/browser/extensions/api/webstore_private/webstore_private_api.cc
+++ b/chrome/browser/extensions/api/webstore_private/webstore_private_api.cc
@@ -52,9 +52,11 @@
 #include "url/gurl.h"
 
 #if BUILDFLAG(ENABLE_SUPERVISED_USERS)
+// TODO(https://crbug.com/1060801): Here and elsewhere, possibly switch build
+// flag to #if defined(OS_CHROMEOS)
 #include "chrome/browser/supervised_user/supervised_user_service.h"
 #include "chrome/browser/supervised_user/supervised_user_service_factory.h"
-#endif
+#endif  // BUILDFLAG(ENABLE_SUPERVISED_USERS)
 
 using safe_browsing::SafeBrowsingNavigationObserverManager;
 
@@ -155,6 +157,16 @@
 const char kEphemeralAppLaunchingNotSupported[] =
     "Ephemeral launching of apps is no longer supported.";
 
+#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
+// Note that the following error doesn't mean an incorrect password was entered,
+// nor that the parent permisison request was canceled by the user, but rather
+// that the Parent permission request after credential entry and acceptance
+// failed due to a either network connection error or some unsatisfied invariant
+// that prevented the request from completing.
+const char kWebstoreParentPermissionFailedError[] =
+    "Parent permission request failed";
+#endif  // BUILDFLAG(ENABLE_SUPERVISED_USERS)
+
 // The number of user gestures to trace back for the referrer chain.
 const int kExtensionReferrerUserGestureLimit = 2;
 
@@ -274,12 +286,11 @@
 }
 
 WebstorePrivateBeginInstallWithManifest3Function::
-    WebstorePrivateBeginInstallWithManifest3Function() : chrome_details_(this) {
-}
+    WebstorePrivateBeginInstallWithManifest3Function()
+    : chrome_details_(this) {}
 
 WebstorePrivateBeginInstallWithManifest3Function::
-    ~WebstorePrivateBeginInstallWithManifest3Function() {
-}
+    ~WebstorePrivateBeginInstallWithManifest3Function() = default;
 
 ExtensionFunction::ResponseAction
 WebstorePrivateBeginInstallWithManifest3Function::Run() {
@@ -422,11 +433,18 @@
                 : ExtensionInstallPrompt::EXTENSION_PENDING_REQUEST_PROMPT),
         ExtensionInstallPrompt::GetDefaultShowDialogCallback());
   } else {
+    auto prompt = std::make_unique<ExtensionInstallPrompt::Prompt>(
+        ExtensionInstallPrompt::INSTALL_PROMPT);
+
+#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
+    prompt->set_user_is_child(profile->IsChild());
+#endif  // BUILDFLAG(ENABLE_SUPERVISED_USERS)
+
     install_prompt_->ShowDialog(
         base::BindRepeating(&WebstorePrivateBeginInstallWithManifest3Function::
                                 OnInstallPromptDone,
                             this),
-        dummy_extension_.get(), &icon_,
+        dummy_extension_.get(), &icon_, std::move(prompt),
         ExtensionInstallPrompt::GetDefaultShowDialogCallback());
   }
   // Control flow finishes up in OnInstallPromptDone or OnRequestPromptDone.
@@ -445,11 +463,120 @@
   Release();
 }
 
+#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
+
+void WebstorePrivateBeginInstallWithManifest3Function::OnParentPermissionDone(
+    std::unique_ptr<ParentPermissionDialog> dialog,
+    ParentPermissionDialog::Result result) {
+  switch (result) {
+    case ParentPermissionDialog::Result::kParentPermissionReceived:
+      OnParentPermissionReceived();
+      break;
+    case ParentPermissionDialog::Result::kParentPermissionCanceled:
+      OnParentPermissionCanceled();
+      break;
+    case ParentPermissionDialog::Result::kParentPermissionFailed:
+      OnParentPermissionFailed();
+      break;
+  }
+}
+
+void WebstorePrivateBeginInstallWithManifest3Function::
+    OnParentPermissionReceived() {
+  SupervisedUserService* service =
+      SupervisedUserServiceFactory::GetForProfile(chrome_details_.GetProfile());
+  service->AddExtensionApproval(*dummy_extension_);
+
+  HandleInstallProceed();
+  Release();  // Matches the AddRef in Run().
+}
+
+void WebstorePrivateBeginInstallWithManifest3Function::
+    OnParentPermissionCanceled() {
+  if (test_webstore_installer_delegate) {
+    test_webstore_installer_delegate->OnExtensionInstallFailure(
+        dummy_extension_->id(), kWebstoreParentPermissionFailedError,
+        WebstoreInstaller::FailureReason::FAILURE_REASON_CANCELLED);
+  }
+
+  HandleInstallAbort(true /* user_initiated */);
+  Release();  // Matches the AddRef in Run().
+}
+
+void WebstorePrivateBeginInstallWithManifest3Function::
+    OnParentPermissionFailed() {
+  if (test_webstore_installer_delegate) {
+    test_webstore_installer_delegate->OnExtensionInstallFailure(
+        dummy_extension_->id(), kWebstoreParentPermissionFailedError,
+        WebstoreInstaller::FailureReason::FAILURE_REASON_OTHER);
+  }
+
+  Respond(BuildResponse(api::webstore_private::RESULT_UNKNOWN_ERROR,
+                        kWebstoreParentPermissionFailedError));
+
+  Release();  // Matches the AddRef in Run().
+}
+
+bool WebstorePrivateBeginInstallWithManifest3Function::
+    PromptForParentApproval() {
+  Profile* profile = chrome_details_.GetProfile();
+  DCHECK(profile->IsChild());
+  content::WebContents* web_contents = GetSenderWebContents();
+  if (!web_contents) {
+    // The browser window has gone away.
+    Respond(BuildResponse(api::webstore_private::RESULT_USER_CANCELLED,
+                          kWebstoreUserCancelledError));
+    return false;
+  }
+
+  bool reprompt_after_incorrect_credential = true;
+
+  // If we are in a test, then we don't reprompt after an incorrect
+  // credential because that, combined with the auto-confirm, would
+  // result in an infinite invalid password / reprompt loop, and
+  // eventually a test timeout.
+  if (test_webstore_installer_delegate)
+    reprompt_after_incorrect_credential = false;
+
+  auto parent_permission_dialog =
+      std::make_unique<ParentPermissionDialog>(profile);
+  parent_permission_dialog->set_reprompt_after_incorrect_credential(
+      reprompt_after_incorrect_credential);
+
+  // Cache a pointer to the dialog so we can show it after we transfer
+  // ownership to the callback.
+  ParentPermissionDialog* dialog_ptr = parent_permission_dialog.get();
+
+  ParentPermissionDialog::DoneCallback done_callback = base::BindOnce(
+      &WebstorePrivateBeginInstallWithManifest3Function::OnParentPermissionDone,
+      this, std::move(parent_permission_dialog));
+
+  dialog_ptr->ShowPromptForExtensionInstallation(
+      web_contents, dummy_extension_.get(), icon_, std::move(done_callback));
+  return true;
+}
+#endif  // BUILDFLAG(ENABLE_SUPERVISED_USERS)
+
 void WebstorePrivateBeginInstallWithManifest3Function::OnInstallPromptDone(
     ExtensionInstallPrompt::Result result) {
   switch (result) {
     case ExtensionInstallPrompt::Result::ACCEPTED:
     case ExtensionInstallPrompt::Result::ACCEPTED_AND_OPTION_CHECKED: {
+#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
+      // Handle parent permission for child accounts on ChromeOS.
+      Profile* profile = chrome_details_.GetProfile();
+      if (profile->IsChild()) {
+        if (PromptForParentApproval()) {
+          // If are showing parent permission dialog, return instead of
+          // break, so that we don't release the ref below.
+          return;
+        } else {
+          // An error occurred in the dialog setup, so release and abort.
+          HandleInstallAbort(false /* user_initiated */);
+          break;
+        }
+      }
+#endif  // BUILDFLAG(ENABLE_SUPERVISED_USERS)
       HandleInstallProceed();
       break;
     }
@@ -533,7 +660,8 @@
 
 ExtensionFunction::ResponseValue
 WebstorePrivateBeginInstallWithManifest3Function::BuildResponse(
-    api::webstore_private::Result result, const std::string& error) {
+    api::webstore_private::Result result,
+    const std::string& error) {
   if (result != api::webstore_private::RESULT_SUCCESS)
     return ErrorWithArguments(CreateResults(result), error);
 
diff --git a/chrome/browser/extensions/api/webstore_private/webstore_private_api.h b/chrome/browser/extensions/api/webstore_private/webstore_private_api.h
index cda0733..cc069ca 100644
--- a/chrome/browser/extensions/api/webstore_private/webstore_private_api.h
+++ b/chrome/browser/extensions/api/webstore_private/webstore_private_api.h
@@ -14,11 +14,18 @@
 #include "chrome/browser/extensions/extension_install_prompt.h"
 #include "chrome/browser/extensions/webstore_install_helper.h"
 #include "chrome/browser/extensions/webstore_installer.h"
+#include "chrome/common/buildflags.h"
 #include "chrome/common/extensions/api/webstore_private.h"
 #include "chrome/common/extensions/webstore_install_result.h"
 #include "extensions/browser/extension_function.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 
+#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
+// TODO(https://crbug.com/1060801): Here and elsewhere, possibly switch build
+// flag to #if defined(OS_CHROMEOS)
+#include "chrome/browser/ui/supervised_user/parent_permission_dialog.h"
+#endif  // BUILDFLAG(ENABLE_SUPERVISED_USERS)
+
 namespace content {
 class GpuFeatureChecker;
 }
@@ -68,6 +75,16 @@
                               InstallHelperResultCode result,
                               const std::string& error_message) override;
 
+#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
+  void OnParentPermissionDone(std::unique_ptr<ParentPermissionDialog> dialog,
+                              ParentPermissionDialog::Result result);
+  void OnParentPermissionReceived();
+  void OnParentPermissionCanceled();
+  void OnParentPermissionFailed();
+  // Returns true if the parental approval prompt was shown, false if there was
+  // an error showing it.
+  bool PromptForParentApproval();
+#endif  // BUILDFLAG(ENABLE_SUPERVISED_USERS)
   void OnInstallPromptDone(ExtensionInstallPrompt::Result result);
   void OnRequestPromptDone(ExtensionInstallPrompt::Result result);
 
diff --git a/chrome/browser/extensions/api/webstore_private/webstore_private_apitest.cc b/chrome/browser/extensions/api/webstore_private/webstore_private_apitest.cc
index 8e10ae9e..037397f 100644
--- a/chrome/browser/extensions/api/webstore_private/webstore_private_apitest.cc
+++ b/chrome/browser/extensions/api/webstore_private/webstore_private_apitest.cc
@@ -42,8 +42,18 @@
 #include "ui/gl/gl_switches.h"
 
 #if BUILDFLAG(ENABLE_SUPERVISED_USERS)
+// TODO(https://crbug.com/1060801): Here and elsewhere, possibly switch build
+// flag to #if defined(OS_CHROMEOS)
 #include "chrome/browser/supervised_user/logged_in_user_mixin.h"
 #include "chrome/browser/supervised_user/supervised_user_constants.h"
+#include "chrome/browser/supervised_user/supervised_user_features.h"
+#include "chrome/browser/supervised_user/supervised_user_service.h"
+#include "chrome/browser/supervised_user/supervised_user_service_factory.h"
+#include "chrome/browser/ui/supervised_user/parent_permission_dialog.h"
+#include "chrome/common/pref_names.h"
+#include "components/account_id/account_id.h"
+#include "components/signin/public/identity_manager/identity_test_environment.h"
+#include "extensions/common/extension_builder.h"
 
 #if defined(OS_CHROMEOS)
 #include "chromeos/constants/chromeos_switches.h"
@@ -79,6 +89,7 @@
     received_failure_ = true;
     id_ = id;
     error_ = error;
+    last_failure_reason_ = reason;
 
     if (waiting_) {
       waiting_ = false;
@@ -94,12 +105,17 @@
     content::RunMessageLoop();
   }
   bool received_success() const { return received_success_; }
+  bool received_failure() const { return received_failure_; }
   const std::string& id() const { return id_; }
+  WebstoreInstaller::FailureReason last_failure_reason() {
+    return last_failure_reason_;
+  }
 
  private:
   bool received_failure_;
   bool received_success_;
   bool waiting_;
+  WebstoreInstaller::FailureReason last_failure_reason_;
   std::string id_;
   std::string error_;
 };
@@ -372,15 +388,23 @@
 }
 
 #if BUILDFLAG(ENABLE_SUPERVISED_USERS)
+static constexpr char kTestChildEmail[] = "test_child_user@google.com";
+static constexpr char kTestChildGaiaId[] = "8u8tuw09sufncmnaos";
+
 class ExtensionWebstorePrivateApiTestChild
     : public ExtensionWebstorePrivateApiTest {
  public:
   ExtensionWebstorePrivateApiTestChild()
       : embedded_test_server_(std::make_unique<net::EmbeddedTestServer>()),
-        logged_in_user_mixin_(&mixin_host_,
-                              chromeos::LoggedInUserMixin::LogInType::kChild,
-                              embedded_test_server_.get(),
-                              this) {
+        logged_in_user_mixin_(
+            &mixin_host_,
+            chromeos::LoggedInUserMixin::LogInType::kChild,
+            embedded_test_server_.get(),
+            this,
+            true /* should_launch_browser */,
+            base::Optional<AccountId>(
+                AccountId::FromUserEmailGaiaId(kTestChildEmail,
+                                               kTestChildGaiaId))) {
     // Suppress regular user login to enable child user login.
     set_chromeos_user_ = false;
   }
@@ -422,10 +446,40 @@
         browser_main_parts);
   }
 
+  void InitializeFamilyData() {
+    // Set up the child user's custodians (i.e. parents).
+    ASSERT_TRUE(browser());
+    PrefService* prefs = browser()->profile()->GetPrefs();
+    prefs->SetString(prefs::kSupervisedUserCustodianEmail,
+                     "test_parent_0@google.com");
+    prefs->SetString(prefs::kSupervisedUserCustodianObfuscatedGaiaId,
+                     "239029320");
+
+    prefs->SetString(prefs::kSupervisedUserSecondCustodianEmail,
+                     "test_parent_1@google.com");
+    prefs->SetString(prefs::kSupervisedUserSecondCustodianObfuscatedGaiaId,
+                     "85948533");
+
+    // Set up the identity test environment, which provides fake
+    // OAuth refresh tokens.
+    identity_test_env_ = std::make_unique<signin::IdentityTestEnvironment>();
+    identity_test_env_->MakeAccountAvailable(kTestChildEmail);
+    identity_test_env_->SetPrimaryAccount(kTestChildEmail);
+    identity_test_env_->SetRefreshTokenForPrimaryAccount();
+    identity_test_env_->SetAutomaticIssueOfAccessTokens(true);
+    ParentPermissionDialog::SetFakeIdentityManagerForTesting(
+        identity_test_env_->identity_manager());
+  }
+
   void SetUpOnMainThread() override {
     mixin_host_.SetUpOnMainThread();
+    logged_in_user_mixin_.LogInUser(true /* issue_any_scope_token */);
     ExtensionWebstorePrivateApiTest::SetUpOnMainThread();
-    logged_in_user_mixin_.LogInUser(true /*issue_any_scope_token*/);
+    InitializeFamilyData();
+    SupervisedUserService* service =
+        SupervisedUserServiceFactory::GetForProfile(profile());
+    service->SetSupervisedUserExtensionsMayRequestPermissionsPrefForTesting(
+        true);
   }
 
   void TearDownOnMainThread() override {
@@ -443,22 +497,114 @@
     ExtensionWebstorePrivateApiTest::TearDown();
   }
 
+  chromeos::LoggedInUserMixin* GetLoggedInUserMixin() {
+    return &logged_in_user_mixin_;
+  }
+
+  void SetNextReAuthStatus(
+      const GaiaAuthConsumer::ReAuthProofTokenStatus next_status) {
+    GetLoggedInUserMixin()
+        ->GetFakeGaiaMixin()
+        ->fake_gaia()
+        ->SetNextReAuthStatus(next_status);
+  }
+
  private:
   // Replicate what MixinBasedInProcessBrowserTest does since inheriting from
   // that class is inconvenient here.
   InProcessBrowserTestMixinHost mixin_host_;
   // Create another embedded test server to avoid starting the same one twice.
   std::unique_ptr<net::EmbeddedTestServer> embedded_test_server_;
-
+  std::unique_ptr<signin::IdentityTestEnvironment> identity_test_env_;
   chromeos::LoggedInUserMixin logged_in_user_mixin_;
 };
 
-// Tests that extension installation is blocked for child accounts, and
-// attempting to do so produces a special error code.
-// Note: This will have to be updated when we enable child-initiated installs.
-IN_PROC_BROWSER_TEST_F(ExtensionWebstorePrivateApiTestChild, InstallBlocked) {
-  ASSERT_TRUE(browser());
-  ASSERT_TRUE(RunInstallTest("begin_install_fail_child.html", "extension.crx"));
+class ExtensionWebstorePrivateApiTestChildInstallDisabled
+    : public ExtensionWebstorePrivateApiTestChild {
+ public:
+  ExtensionWebstorePrivateApiTestChildInstallDisabled() {
+    feature_list_.InitWithFeatures(
+        {}, {supervised_users::kSupervisedUserInitiatedExtensionInstall});
+  }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+};
+
+// Tests that extension installation is blocked for child accounts when
+// the feature flag is disabled.
+IN_PROC_BROWSER_TEST_F(ExtensionWebstorePrivateApiTestChildInstallDisabled,
+                       InstallBlocked) {
+  ASSERT_TRUE(RunInstallTest("install_blocked_child.html", "app.crx"));
+}
+
+static constexpr char kTestAppId[] = "iladmdjkfniedhfhcfoefgojhgaiaccc";
+static constexpr char kTestAppVersion[] = "0.1";
+
+// Tests fixture for various cases of installation for child accounts
+// when the feature flag is enabled.
+class ExtensionWebstorePrivateApiTestChildInstallEnabled
+    : public ExtensionWebstorePrivateApiTestChild {
+ public:
+  ExtensionWebstorePrivateApiTestChildInstallEnabled() {
+    feature_list_.InitWithFeatures(
+        {supervised_users::kSupervisedUserInitiatedExtensionInstall}, {});
+  }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+};
+
+// Tests install for a child when parent permission is granted.
+IN_PROC_BROWSER_TEST_F(ExtensionWebstorePrivateApiTestChildInstallEnabled,
+                       ParentPermissionGranted) {
+  WebstoreInstallListener listener;
+  WebstorePrivateApi::SetWebstoreInstallerDelegateForTesting(&listener);
+
+  // Tell the Reauth API client to return a success for the next reauth
+  // request.
+  SetNextReAuthStatus(GaiaAuthConsumer::ReAuthProofTokenStatus::kSuccess);
+  SetAutoConfirmParentPermissionDialogForTest(
+      ::internal::ParentPermissionDialogViewResult::Status::kAccepted);
+
+  ASSERT_TRUE(RunInstallTest("install_child.html", "app.crx"));
+  listener.Wait();
+  ASSERT_TRUE(listener.received_success());
+  ASSERT_EQ(kTestAppId, listener.id());
+
+  scoped_refptr<const Extension> extension =
+      extensions::ExtensionBuilder("test extension")
+          .SetID(kTestAppId)
+          .SetVersion(kTestAppVersion)
+          .Build();
+  SupervisedUserService* service =
+      SupervisedUserServiceFactory::GetForProfile(profile());
+  ASSERT_TRUE(service->IsExtensionAllowed(*extension));
+}
+
+// Tests no install occurs for a child when the parent permission
+// dialog is canceled.
+IN_PROC_BROWSER_TEST_F(ExtensionWebstorePrivateApiTestChildInstallEnabled,
+                       ParentPermissionCanceled) {
+  WebstoreInstallListener listener;
+  WebstorePrivateApi::SetWebstoreInstallerDelegateForTesting(&listener);
+  SetAutoConfirmParentPermissionDialogForTest(
+      ::internal::ParentPermissionDialogViewResult::Status::kCanceled);
+
+  ASSERT_TRUE(RunInstallTest("install_cancel_child.html", "app.crx"));
+  listener.Wait();
+  ASSERT_TRUE(listener.received_failure());
+  ASSERT_EQ(kTestAppId, listener.id());
+  ASSERT_EQ(listener.last_failure_reason(),
+            WebstoreInstaller::FailureReason::FAILURE_REASON_CANCELLED);
+  scoped_refptr<const Extension> extension =
+      extensions::ExtensionBuilder("test extension")
+          .SetID(kTestAppId)
+          .SetVersion(kTestAppVersion)
+          .Build();
+  SupervisedUserService* service =
+      SupervisedUserServiceFactory::GetForProfile(profile());
+  ASSERT_FALSE(service->IsExtensionAllowed(*extension));
 }
 
 #endif  // BUILDFLAG(ENABLE_SUPERVISED_USERS)
diff --git a/chrome/browser/extensions/context_menu_matcher.cc b/chrome/browser/extensions/context_menu_matcher.cc
index ab803af..4989ad4 100644
--- a/chrome/browser/extensions/context_menu_matcher.cc
+++ b/chrome/browser/extensions/context_menu_matcher.cc
@@ -11,7 +11,7 @@
 #include "chrome/app/chrome_command_ids.h"
 #include "chrome/common/extensions/api/context_menus.h"
 #include "content/public/browser/browser_context.h"
-#include "content/public/common/context_menu_params.h"
+#include "content/public/browser/context_menu_params.h"
 #include "extensions/browser/extension_registry.h"
 #include "extensions/browser/extension_util.h"
 #include "ui/gfx/favicon_size.h"
diff --git a/chrome/browser/extensions/extension_context_menu_browsertest.cc b/chrome/browser/extensions/extension_context_menu_browsertest.cc
index c510dfe4..35d889e 100644
--- a/chrome/browser/extensions/extension_context_menu_browsertest.cc
+++ b/chrome/browser/extensions/extension_context_menu_browsertest.cc
@@ -21,8 +21,8 @@
 #include "chrome/common/channel_info.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "components/version_info/channel.h"
+#include "content/public/browser/context_menu_params.h"
 #include "content/public/browser/render_frame_host.h"
-#include "content/public/common/context_menu_params.h"
 #include "content/public/test/browser_test_utils.h"
 #include "extensions/browser/extension_api_frame_id_map.h"
 #include "extensions/browser/extension_registry.h"
diff --git a/chrome/browser/extensions/extension_context_menu_model.cc b/chrome/browser/extensions/extension_context_menu_model.cc
index 2f251377..c6c3673 100644
--- a/chrome/browser/extensions/extension_context_menu_model.cc
+++ b/chrome/browser/extensions/extension_context_menu_model.cc
@@ -35,8 +35,8 @@
 #include "components/sessions/content/session_tab_helper.h"
 #include "components/url_formatter/url_formatter.h"
 #include "components/vector_icons/vector_icons.h"
+#include "content/public/browser/context_menu_params.h"
 #include "content/public/browser/web_contents.h"
-#include "content/public/common/context_menu_params.h"
 #include "extensions/browser/extension_registry.h"
 #include "extensions/browser/extension_system.h"
 #include "extensions/browser/management_policy.h"
diff --git a/chrome/browser/extensions/menu_manager.cc b/chrome/browser/extensions/menu_manager.cc
index 43efc3b..65f5c2ae 100644
--- a/chrome/browser/extensions/menu_manager.cc
+++ b/chrome/browser/extensions/menu_manager.cc
@@ -23,9 +23,9 @@
 #include "chrome/common/extensions/api/chrome_web_view_internal.h"
 #include "chrome/common/extensions/api/context_menus.h"
 #include "chrome/common/extensions/api/url_handlers/url_handlers_parser.h"
+#include "content/public/browser/context_menu_params.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/child_process_host.h"
-#include "content/public/common/context_menu_params.h"
 #include "extensions/browser/event_router.h"
 #include "extensions/browser/extension_api_frame_id_map.h"
 #include "extensions/browser/guest_view/web_view/web_view_guest.h"
diff --git a/chrome/browser/extensions/menu_manager_unittest.cc b/chrome/browser/extensions/menu_manager_unittest.cc
index 97987e9..3d47a2bd 100644
--- a/chrome/browser/extensions/menu_manager_unittest.cc
+++ b/chrome/browser/extensions/menu_manager_unittest.cc
@@ -25,8 +25,8 @@
 #include "chrome/test/base/testing_profile.h"
 #include "components/prefs/pref_service.h"
 #include "components/sync_preferences/pref_service_syncable.h"
+#include "content/public/browser/context_menu_params.h"
 #include "content/public/browser/notification_service.h"
-#include "content/public/common/context_menu_params.h"
 #include "content/public/test/browser_task_environment.h"
 #include "extensions/browser/event_router.h"
 #include "extensions/browser/event_router_factory.h"
diff --git a/chrome/browser/flags/BUILD.gn b/chrome/browser/flags/BUILD.gn
index 82776c8..a1b938c 100644
--- a/chrome/browser/flags/BUILD.gn
+++ b/chrome/browser/flags/BUILD.gn
@@ -65,3 +65,20 @@
     "//chrome/test/android:chrome_java_test_support",
   ]
 }
+
+android_library("javatests") {
+  testonly = true
+  sources = [ "android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureListInstrumentationTest.java" ]
+  deps = [
+    ":java",
+    "//base:base_java",
+    "//base:base_java_test_support",
+    "//base/test:test_support_java",
+    "//chrome/android:chrome_java",
+    "//chrome/test/android:chrome_java_test_support",
+    "//third_party/android_sdk:android_test_mock_java",
+    "//third_party/android_support_test_runner:runner_java",
+    "//third_party/junit",
+    "//third_party/mockito:mockito_java",
+  ]
+}
diff --git a/chrome/browser/flags/android/chrome_feature_list.cc b/chrome/browser/flags/android/chrome_feature_list.cc
index 67ec332..4c00d02 100644
--- a/chrome/browser/flags/android/chrome_feature_list.cc
+++ b/chrome/browser/flags/android/chrome_feature_list.cc
@@ -157,6 +157,7 @@
     &kHorizontalTabSwitcherAndroid,
     &kImmersiveUiMode,
     &kInlineUpdateFlow,
+    &kInstantStart,
     &kKitKatSupported,
     &kNewPhotoPicker,
     &kNotificationSuspender,
@@ -457,6 +458,9 @@
 const base::Feature kInlineUpdateFlow{"InlineUpdateFlow",
                                       base::FEATURE_DISABLED_BY_DEFAULT};
 
+const base::Feature kInstantStart{"InstantStart",
+                                  base::FEATURE_DISABLED_BY_DEFAULT};
+
 const base::Feature kKitKatSupported{"KitKatSupported",
                                      base::FEATURE_DISABLED_BY_DEFAULT};
 
diff --git a/chrome/browser/flags/android/chrome_feature_list.h b/chrome/browser/flags/android/chrome_feature_list.h
index 21815f7..c53269f 100644
--- a/chrome/browser/flags/android/chrome_feature_list.h
+++ b/chrome/browser/flags/android/chrome_feature_list.h
@@ -78,6 +78,7 @@
 extern const base::Feature kImmersiveUiMode;
 extern const base::Feature kImprovedA2HS;
 extern const base::Feature kInlineUpdateFlow;
+extern const base::Feature kInstantStart;
 extern const base::Feature kKitKatSupported;
 extern const base::Feature kLanguagesPreference;
 extern const base::Feature kNewPhotoPicker;
diff --git a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/CachedFeatureFlags.java b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/CachedFeatureFlags.java
index ec0a5c5b..f545be2 100644
--- a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/CachedFeatureFlags.java
+++ b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/CachedFeatureFlags.java
@@ -66,6 +66,10 @@
             put(ChromeFeatureList.TAB_GROUPS_CONTINUATION_ANDROID, false);
             put(ChromeFeatureList.DUET_TABSTRIP_INTEGRATION_ANDROID, false);
             put(ChromeFeatureList.SHARE_BUTTON_IN_TOP_TOOLBAR, false);
+            put(ChromeFeatureList.CLOSE_TAB_SUGGESTIONS, false);
+            put(ChromeFeatureList.INSTANT_START, false);
+            put(ChromeFeatureList.TAB_GROUPS_CONTINUATION_ANDROID, false);
+            put(ChromeFeatureList.TAB_TO_GTS_ANIMATION, false);
             put(ChromeFeatureList.TEST_DEFAULT_DISABLED, false);
             put(ChromeFeatureList.TEST_DEFAULT_ENABLED, true);
         }
@@ -348,6 +352,7 @@
             if (value != null) {
                 return Boolean.valueOf(value);
             }
+            return defaultValue;
         }
 
         Boolean flag = sBoolValuesReturned.get(preferenceName);
@@ -364,6 +369,7 @@
             if (stringValue != null) {
                 return stringValue;
             }
+            return defaultValue;
         }
 
         String value = sStringValuesReturned.get(preferenceName);
@@ -380,6 +386,7 @@
             if (stringValue != null) {
                 return Integer.valueOf(stringValue);
             }
+            return defaultValue;
         }
 
         Integer value = sIntValuesReturned.get(preferenceName);
@@ -396,6 +403,7 @@
             if (stringValue != null) {
                 return Double.valueOf(stringValue);
             }
+            return defaultValue;
         }
 
         Double value = sDoubleValuesReturned.get(preferenceName);
diff --git a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
index b14928cb5..b6dcde2 100644
--- a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
+++ b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
@@ -310,6 +310,7 @@
             "ImprovedCookieControlsForThirdPartyCookieBlocking";
     public static final String INLINE_UPDATE_FLOW = "InlineUpdateFlow";
     public static final String INSTALLABLE_AMBIENT_BADGE_INFOBAR = "InstallableAmbientBadgeInfoBar";
+    public static final String INSTANT_START = "InstantStart";
     public static final String INTEREST_FEED_CONTENT_SUGGESTIONS = "InterestFeedContentSuggestions";
     public static final String INTEREST_FEED_FEEDBACK = "InterestFeedFeedback";
     public static final String KITKAT_SUPPORTED = "KitKatSupported";
@@ -389,7 +390,6 @@
     public static final String START_SURFACE_ANDROID = "StartSurfaceAndroid";
     public static final String STRICT_HAS_ENROLLED_AUTOFILL_INSTRUMENT =
             "StrictHasEnrolledAutofillInstrument";
-    public static final String SUBRESOURCE_FILTER = "SubresourceFilter";
     public static final String SWAP_PIXEL_FORMAT_TO_FIX_CONVERT_FROM_TRANSLUCENT =
             "SwapPixelFormatToFixConvertFromTranslucent";
     public static final String SYNC_USE_SESSIONS_UNREGISTER_DELAY =
diff --git a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureListInstrumentationTest.java b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureListInstrumentationTest.java
new file mode 100644
index 0000000..d926afd1
--- /dev/null
+++ b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureListInstrumentationTest.java
@@ -0,0 +1,73 @@
+// 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.chrome.browser.flags;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.util.ArrayMap;
+
+import androidx.test.filters.MediumTest;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.chrome.browser.ChromeActivity;
+import org.chromium.chrome.browser.ChromeSwitches;
+import org.chromium.chrome.test.ChromeActivityTestRule;
+import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.chrome.test.util.browser.Features.DisableFeatures;
+import org.chromium.chrome.test.util.browser.Features.EnableFeatures;
+
+import java.util.Map;
+
+/**
+ * Tests the behavior of {@link ChromeFeatureList} in instrumentation tests.
+ */
+@RunWith(ChromeJUnit4ClassRunner.class)
+@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
+public class ChromeFeatureListInstrumentationTest {
+    @Rule
+    public ChromeActivityTestRule<ChromeActivity> mActivityTestRule =
+            new ChromeActivityTestRule<>(ChromeActivity.class);
+
+    @Before
+    public void setUp() throws Exception {
+        mActivityTestRule.startMainActivityOnBlankPage();
+    }
+
+    @Test
+    @MediumTest
+    public void testNoOverridesDefault() {
+        assertFalse(ChromeFeatureList.isEnabled(ChromeFeatureList.TEST_DEFAULT_DISABLED));
+        assertTrue(ChromeFeatureList.isEnabled(ChromeFeatureList.TEST_DEFAULT_ENABLED));
+    }
+
+    @Test
+    @MediumTest
+    @EnableFeatures(ChromeFeatureList.TEST_DEFAULT_DISABLED)
+    @DisableFeatures(ChromeFeatureList.TEST_DEFAULT_ENABLED)
+    public void testAnnotations() {
+        assertTrue(ChromeFeatureList.isEnabled(ChromeFeatureList.TEST_DEFAULT_DISABLED));
+        assertFalse(ChromeFeatureList.isEnabled(ChromeFeatureList.TEST_DEFAULT_ENABLED));
+    }
+
+    @Test
+    @MediumTest
+    public void testSetTestFeatures() {
+        Map<String, Boolean> overrides = new ArrayMap<>();
+        overrides.put(ChromeFeatureList.TEST_DEFAULT_DISABLED, true);
+        overrides.put(ChromeFeatureList.TEST_DEFAULT_ENABLED, false);
+        ChromeFeatureList.setTestFeatures(overrides);
+
+        assertTrue(ChromeFeatureList.isEnabled(ChromeFeatureList.TEST_DEFAULT_DISABLED));
+        assertFalse(ChromeFeatureList.isEnabled(ChromeFeatureList.TEST_DEFAULT_ENABLED));
+
+        ChromeFeatureList.setTestFeatures(null);
+    }
+}
diff --git a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureListWithProcessorUnitTest.java b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureListWithProcessorUnitTest.java
index 7b10311..8af1c538 100644
--- a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureListWithProcessorUnitTest.java
+++ b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureListWithProcessorUnitTest.java
@@ -21,7 +21,7 @@
 import java.util.Collections;
 
 /**
- * Shows the behavior of {@link ChromeFeatureList} in Robolectric unit tests when the rule
+ * Tests the behavior of {@link ChromeFeatureList} in Robolectric unit tests when the rule
  * Features.JUnitProcessor is present.
  */
 @RunWith(BaseRobolectricTestRunner.class)
@@ -59,9 +59,9 @@
      * In unit tests, flags may have their value specified by the DisableFeatures annotation.
      */
     @Test
-    @DisableFeatures(ChromeFeatureList.TEST_DEFAULT_DISABLED)
+    @DisableFeatures(ChromeFeatureList.TEST_DEFAULT_ENABLED)
     public void testAnnotationDisabled_returnsDisabled() {
-        assertFalse(ChromeFeatureList.isEnabled(ChromeFeatureList.TEST_DEFAULT_DISABLED));
+        assertFalse(ChromeFeatureList.isEnabled(ChromeFeatureList.TEST_DEFAULT_ENABLED));
     }
 
     /**
@@ -83,8 +83,8 @@
     @Test
     public void testSetTestFeaturesDisabled_returnsDisabled() {
         ChromeFeatureList.setTestFeatures(
-                Collections.singletonMap(ChromeFeatureList.TEST_DEFAULT_DISABLED, false));
-        assertFalse(ChromeFeatureList.isEnabled(ChromeFeatureList.TEST_DEFAULT_DISABLED));
+                Collections.singletonMap(ChromeFeatureList.TEST_DEFAULT_ENABLED, false));
+        assertFalse(ChromeFeatureList.isEnabled(ChromeFeatureList.TEST_DEFAULT_ENABLED));
         ChromeFeatureList.setTestFeatures(null);
     }
 }
diff --git a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureListWithoutProcessorUnitTest.java b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureListWithoutProcessorUnitTest.java
index 8b900e7..d39bf903c 100644
--- a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureListWithoutProcessorUnitTest.java
+++ b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureListWithoutProcessorUnitTest.java
@@ -18,7 +18,7 @@
 import java.util.Collections;
 
 /**
- * Shows the behavior of {@link ChromeFeatureList} in Robolectric unit tests when the rule
+ * Tests the behavior of {@link ChromeFeatureList} in Robolectric unit tests when the rule
  * Features.JUnitProcessor is NOT present.
  */
 @RunWith(BaseRobolectricTestRunner.class)
@@ -55,9 +55,9 @@
      * work.
      */
     @Test(expected = AssertionError.class)
-    @DisableFeatures(ChromeFeatureList.TEST_DEFAULT_DISABLED)
+    @DisableFeatures(ChromeFeatureList.TEST_DEFAULT_ENABLED)
     public void testAnnotationDisabled_asserts() {
-        ChromeFeatureList.isEnabled(ChromeFeatureList.TEST_DEFAULT_DISABLED);
+        ChromeFeatureList.isEnabled(ChromeFeatureList.TEST_DEFAULT_ENABLED);
     }
 
     /**
@@ -79,8 +79,8 @@
     @Test
     public void testSetTestFeaturesDisabled_returnsDisabled() {
         ChromeFeatureList.setTestFeatures(
-                Collections.singletonMap(ChromeFeatureList.TEST_DEFAULT_DISABLED, false));
-        assertFalse(ChromeFeatureList.isEnabled(ChromeFeatureList.TEST_DEFAULT_DISABLED));
+                Collections.singletonMap(ChromeFeatureList.TEST_DEFAULT_ENABLED, false));
+        assertFalse(ChromeFeatureList.isEnabled(ChromeFeatureList.TEST_DEFAULT_ENABLED));
         ChromeFeatureList.setTestFeatures(null);
     }
 }
diff --git a/chrome/browser/guest_view/app_view/chrome_app_view_guest_delegate.h b/chrome/browser/guest_view/app_view/chrome_app_view_guest_delegate.h
index 148e32a..cc81a21 100644
--- a/chrome/browser/guest_view/app_view/chrome_app_view_guest_delegate.h
+++ b/chrome/browser/guest_view/app_view/chrome_app_view_guest_delegate.h
@@ -6,7 +6,7 @@
 #define CHROME_BROWSER_GUEST_VIEW_APP_VIEW_CHROME_APP_VIEW_GUEST_DELEGATE_H_
 
 #include "base/macros.h"
-#include "content/public/common/context_menu_params.h"
+#include "content/public/browser/context_menu_params.h"
 #include "extensions/browser/guest_view/app_view/app_view_guest_delegate.h"
 
 namespace extensions {
diff --git a/chrome/browser/net/dns_util.h b/chrome/browser/net/dns_util.h
index 3bdff114..af7fb17 100644
--- a/chrome/browser/net/dns_util.h
+++ b/chrome/browser/net/dns_util.h
@@ -34,6 +34,31 @@
   kDisabledParentalControls,
 };
 
+// Detailed descriptions of the secure DNS mode. These values are logged to UMA.
+// Entries should not be renumbered and numeric values should never be reused.
+// Please keep in sync with "SecureDnsModeDetails" in
+// src/tools/metrics/histograms/enums.xml.
+enum class SecureDnsModeDetailsForHistogram {
+  // The mode is controlled by the user and is set to 'off'.
+  kOffByUser = 0,
+  // The mode is controlled via enterprise policy and is set to 'off'.
+  kOffByEnterprisePolicy = 1,
+  // Chrome detected a managed environment and forced the mode to 'off'.
+  kOffByDetectedManagedEnvironment = 2,
+  // Chrome detected parental controls and forced the mode to 'off'.
+  kOffByDetectedParentalControls = 3,
+  // The mode is controlled by the user and is set to 'automatic' (the default
+  // mode).
+  kAutomaticByUser = 4,
+  // The mode is controlled via enterprise policy and is set to 'automatic'.
+  kAutomaticByEnterprisePolicy = 5,
+  // The mode is controlled by the user and is set to 'secure'.
+  kSecureByUser = 6,
+  // The mode is controlled via enterprise policy and is set to 'secure'.
+  kSecureByEnterprisePolicy = 7,
+  kMaxValue = kSecureByEnterprisePolicy,
+};
+
 }  // namespace chrome_browser_net
 
 #endif  // CHROME_BROWSER_NET_DNS_UTIL_H_
diff --git a/chrome/browser/net/system_network_context_manager.cc b/chrome/browser/net/system_network_context_manager.cc
index 55c435c..0ce4445b 100644
--- a/chrome/browser/net/system_network_context_manager.cc
+++ b/chrome/browser/net/system_network_context_manager.cc
@@ -15,6 +15,7 @@
 #include "base/feature_list.h"
 #include "base/logging.h"
 #include "base/metrics/field_trial_params.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/process/process_handle.h"
 #include "base/sequence_checker.h"
 #include "base/strings/string_split.h"
@@ -92,6 +93,9 @@
 #include "extensions/common/constants.h"
 #endif  // BUILDFLAG(ENABLE_EXTENSIONS)
 
+using chrome_browser_net::SecureDnsModeDetailsForHistogram;
+using chrome_browser_net::SecureDnsUiManagementMode;
+
 namespace {
 
 constexpr bool kCertificateTransparencyEnabled =
@@ -528,39 +532,71 @@
     net::DnsConfig::SecureDnsMode* secure_dns_mode,
     base::Optional<std::vector<network::mojom::DnsOverHttpsServerPtr>>*
         dns_over_https_servers,
-    chrome_browser_net::SecureDnsUiManagementMode* forced_management_mode) {
+    bool record_metrics,
+    SecureDnsUiManagementMode* forced_management_mode) {
   DCHECK(!dns_over_https_servers->has_value());
 
   *insecure_stub_resolver_enabled =
       local_state->GetBoolean(prefs::kBuiltInDnsClientEnabled);
 
   std::string doh_mode;
-  if (!local_state->FindPreference(prefs::kDnsOverHttpsMode)->IsManaged() &&
-      chrome_browser_net::ShouldDisableDohForManaged()) {
+  SecureDnsModeDetailsForHistogram mode_details;
+  SecureDnsUiManagementMode forced_management_mode_internal =
+      SecureDnsUiManagementMode::kNoOverride;
+  bool is_managed =
+      local_state->FindPreference(prefs::kDnsOverHttpsMode)->IsManaged();
+  if (!is_managed && chrome_browser_net::ShouldDisableDohForManaged()) {
     doh_mode = chrome_browser_net::kDnsOverHttpsModeOff;
-    if (forced_management_mode)
-      *forced_management_mode =
-          chrome_browser_net::SecureDnsUiManagementMode::kDisabledManaged;
-  } else if (!local_state->FindPreference(prefs::kDnsOverHttpsMode)
-                  ->IsManaged() &&
+    forced_management_mode_internal =
+        SecureDnsUiManagementMode::kDisabledManaged;
+  } else if (!is_managed &&
              chrome_browser_net::ShouldDisableDohForParentalControls()) {
     doh_mode = chrome_browser_net::kDnsOverHttpsModeOff;
-    if (forced_management_mode)
-      *forced_management_mode = chrome_browser_net::SecureDnsUiManagementMode::
-          kDisabledParentalControls;
+    forced_management_mode_internal =
+        SecureDnsUiManagementMode::kDisabledParentalControls;
   } else {
     doh_mode = local_state->GetString(prefs::kDnsOverHttpsMode);
-    if (forced_management_mode)
-      *forced_management_mode =
-          chrome_browser_net::SecureDnsUiManagementMode::kNoOverride;
   }
 
-  if (doh_mode == chrome_browser_net::kDnsOverHttpsModeSecure)
+  if (doh_mode == chrome_browser_net::kDnsOverHttpsModeSecure) {
     *secure_dns_mode = net::DnsConfig::SecureDnsMode::SECURE;
-  else if (doh_mode == chrome_browser_net::kDnsOverHttpsModeAutomatic)
+    mode_details =
+        is_managed ? SecureDnsModeDetailsForHistogram::kSecureByEnterprisePolicy
+                   : SecureDnsModeDetailsForHistogram::kSecureByUser;
+  } else if (doh_mode == chrome_browser_net::kDnsOverHttpsModeAutomatic) {
     *secure_dns_mode = net::DnsConfig::SecureDnsMode::AUTOMATIC;
-  else
+    mode_details =
+        is_managed
+            ? SecureDnsModeDetailsForHistogram::kAutomaticByEnterprisePolicy
+            : SecureDnsModeDetailsForHistogram::kAutomaticByUser;
+  } else {
     *secure_dns_mode = net::DnsConfig::SecureDnsMode::OFF;
+    switch (forced_management_mode_internal) {
+      case SecureDnsUiManagementMode::kNoOverride:
+        mode_details =
+            is_managed
+                ? SecureDnsModeDetailsForHistogram::kOffByEnterprisePolicy
+                : SecureDnsModeDetailsForHistogram::kOffByUser;
+        break;
+      case SecureDnsUiManagementMode::kDisabledManaged:
+        mode_details =
+            SecureDnsModeDetailsForHistogram::kOffByDetectedManagedEnvironment;
+        break;
+      case SecureDnsUiManagementMode::kDisabledParentalControls:
+        mode_details =
+            SecureDnsModeDetailsForHistogram::kOffByDetectedParentalControls;
+        break;
+      default:
+        NOTREACHED();
+    }
+  }
+
+  if (forced_management_mode)
+    *forced_management_mode = forced_management_mode_internal;
+
+  if (record_metrics) {
+    UMA_HISTOGRAM_ENUMERATION("Net.DNS.DnsConfig.SecureDnsMode", mode_details);
+  }
 
   std::string doh_templates =
       local_state->GetString(prefs::kDnsOverHttpsTemplates);
@@ -638,7 +674,8 @@
   base::Optional<std::vector<network::mojom::DnsOverHttpsServerPtr>>
       dns_over_https_servers;
   GetStubResolverConfig(local_state_, &insecure_stub_resolver_enabled,
-                        &secure_dns_mode, &dns_over_https_servers);
+                        &secure_dns_mode, &dns_over_https_servers,
+                        true /* record_metrics */);
   content::GetNetworkService()->ConfigureStubHostResolver(
       insecure_stub_resolver_enabled, secure_dns_mode,
       std::move(dns_over_https_servers));
diff --git a/chrome/browser/net/system_network_context_manager.h b/chrome/browser/net/system_network_context_manager.h
index 0c8ec00..f916082 100644
--- a/chrome/browser/net/system_network_context_manager.h
+++ b/chrome/browser/net/system_network_context_manager.h
@@ -89,6 +89,7 @@
       net::DnsConfig::SecureDnsMode* secure_dns_mode,
       base::Optional<std::vector<network::mojom::DnsOverHttpsServerPtr>>*
           dns_over_https_servers,
+      bool record_metrics = false,
       chrome_browser_net::SecureDnsUiManagementMode* forced_management_mode =
           nullptr);
 
diff --git a/chrome/browser/pdf/pdf_extension_test.cc b/chrome/browser/pdf/pdf_extension_test.cc
index 35e234a..6a3973b 100644
--- a/chrome/browser/pdf/pdf_extension_test.cc
+++ b/chrome/browser/pdf/pdf_extension_test.cc
@@ -69,7 +69,7 @@
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/content_features.h"
 #include "content/public/common/content_switches.h"
-#include "content/public/common/context_menu_params.h"
+#include "content/public/common/untrustworthy_context_menu_params.h"
 #include "content/public/common/url_constants.h"
 #include "content/public/test/accessibility_notification_waiter.h"
 #include "content/public/test/browser_test_utils.h"
diff --git a/chrome/browser/performance_manager/chrome_browser_main_extra_parts_performance_manager.cc b/chrome/browser/performance_manager/chrome_browser_main_extra_parts_performance_manager.cc
index 827d83a5..ca81f89 100644
--- a/chrome/browser/performance_manager/chrome_browser_main_extra_parts_performance_manager.cc
+++ b/chrome/browser/performance_manager/chrome_browser_main_extra_parts_performance_manager.cc
@@ -157,11 +157,10 @@
   page_load_tracker_decorator_helper_.reset();
   page_live_state_data_helper_.reset();
 
-  // There may still be WebContents and RenderProcessHosts with attached user
-  // data, retaining PageNodes, FrameNodes and ProcessNodes. Tear down the
-  // registry to release these nodes. There is no convenient later call-out to
-  // destroy the performance manager after all WebContents and
-  // RenderProcessHosts have been destroyed.
+  // There may still be worker hosts, WebContents and RenderProcessHosts with
+  // attached user data, retaining WorkerNodes, PageNodes, FrameNodes and
+  // ProcessNodes. Tear down the registry to release these nodes. After this,
+  // there is no convenient call-out to destroy the performance manager.
   registry_->TearDown();
   registry_.reset();
 
diff --git a/chrome/browser/renderer_context_menu/accessibility_labels_menu_observer.cc b/chrome/browser/renderer_context_menu/accessibility_labels_menu_observer.cc
index 828ec9a4..23a281c 100644
--- a/chrome/browser/renderer_context_menu/accessibility_labels_menu_observer.cc
+++ b/chrome/browser/renderer_context_menu/accessibility_labels_menu_observer.cc
@@ -21,12 +21,12 @@
 #include "chrome/grit/generated_resources.h"
 #include "components/prefs/pref_service.h"
 #include "content/public/browser/browser_thread.h"
+#include "content/public/browser/context_menu_params.h"
 #include "content/public/browser/render_view_host.h"
 #include "content/public/browser/render_widget_host.h"
 #include "content/public/browser/render_widget_host_view.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/content_features.h"
-#include "content/public/common/context_menu_params.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/gfx/geometry/rect.h"
 
diff --git a/chrome/browser/renderer_context_menu/accessibility_labels_menu_observer_browsertest.cc b/chrome/browser/renderer_context_menu/accessibility_labels_menu_observer_browsertest.cc
index 0642cac..8e2d40a 100644
--- a/chrome/browser/renderer_context_menu/accessibility_labels_menu_observer_browsertest.cc
+++ b/chrome/browser/renderer_context_menu/accessibility_labels_menu_observer_browsertest.cc
@@ -12,8 +12,8 @@
 #include "chrome/common/pref_names.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "components/prefs/pref_service.h"
+#include "content/public/browser/context_menu_params.h"
 #include "content/public/common/content_features.h"
-#include "content/public/common/context_menu_params.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #if defined(OS_CHROMEOS)
 #include "chrome/browser/chromeos/accessibility/accessibility_manager.h"
diff --git a/chrome/browser/renderer_context_menu/context_menu_content_type_factory.h b/chrome/browser/renderer_context_menu/context_menu_content_type_factory.h
index 32445b0c..76e5940 100644
--- a/chrome/browser/renderer_context_menu/context_menu_content_type_factory.h
+++ b/chrome/browser/renderer_context_menu/context_menu_content_type_factory.h
@@ -8,7 +8,7 @@
 #include <memory>
 
 #include "base/macros.h"
-#include "content/public/common/context_menu_params.h"
+#include "content/public/browser/context_menu_params.h"
 
 class ContextMenuContentType;
 
diff --git a/chrome/browser/renderer_context_menu/quick_answers_menu_observer.cc b/chrome/browser/renderer_context_menu/quick_answers_menu_observer.cc
index b9db1a11..9f56e34c 100644
--- a/chrome/browser/renderer_context_menu/quick_answers_menu_observer.cc
+++ b/chrome/browser/renderer_context_menu/quick_answers_menu_observer.cc
@@ -20,8 +20,8 @@
 #include "chromeos/constants/chromeos_features.h"
 #include "chromeos/services/assistant/public/mojom/assistant.mojom.h"
 #include "components/renderer_context_menu/render_view_context_menu_proxy.h"
+#include "content/public/browser/context_menu_params.h"
 #include "content/public/browser/storage_partition.h"
-#include "content/public/common/context_menu_params.h"
 #include "ui/gfx/text_constants.h"
 #include "ui/gfx/text_elider.h"
 
diff --git a/chrome/browser/renderer_context_menu/quick_answers_menu_observer_browsertest.cc b/chrome/browser/renderer_context_menu/quick_answers_menu_observer_browsertest.cc
index c133a5ae..d9ab2714 100644
--- a/chrome/browser/renderer_context_menu/quick_answers_menu_observer_browsertest.cc
+++ b/chrome/browser/renderer_context_menu/quick_answers_menu_observer_browsertest.cc
@@ -13,6 +13,7 @@
 #include "chromeos/components/quick_answers/quick_answers_client.h"
 #include "chromeos/components/quick_answers/quick_answers_model.h"
 #include "chromeos/components/quick_answers/test/test_helpers.h"
+#include "content/public/browser/context_menu_params.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu.h b/chrome/browser/renderer_context_menu/render_view_context_menu.h
index 900b3bb..299d5f6e 100644
--- a/chrome/browser/renderer_context_menu/render_view_context_menu.h
+++ b/chrome/browser/renderer_context_menu/render_view_context_menu.h
@@ -20,7 +20,7 @@
 #include "components/renderer_context_menu/render_view_context_menu_base.h"
 #include "components/renderer_context_menu/render_view_context_menu_observer.h"
 #include "components/renderer_context_menu/render_view_context_menu_proxy.h"
-#include "content/public/common/context_menu_params.h"
+#include "content/public/browser/context_menu_params.h"
 #include "extensions/buildflags/buildflags.h"
 #include "ppapi/buildflags/buildflags.h"
 #include "printing/buildflags/buildflags.h"
diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu_browsertest_util.h b/chrome/browser/renderer_context_menu/render_view_context_menu_browsertest_util.h
index 2580b47d..6e131fe 100644
--- a/chrome/browser/renderer_context_menu/render_view_context_menu_browsertest_util.h
+++ b/chrome/browser/renderer_context_menu/render_view_context_menu_browsertest_util.h
@@ -9,7 +9,7 @@
 #include "base/run_loop.h"
 #include "base/strings/string16.h"
 #include "chrome/browser/renderer_context_menu/render_view_context_menu.h"
-#include "content/public/common/context_menu_params.h"
+#include "content/public/browser/context_menu_params.h"
 
 class RenderViewContextMenu;
 
diff --git a/chrome/browser/renderer_context_menu/spelling_menu_observer.cc b/chrome/browser/renderer_context_menu/spelling_menu_observer.cc
index cb5c7c5..cf25433f 100644
--- a/chrome/browser/renderer_context_menu/spelling_menu_observer.cc
+++ b/chrome/browser/renderer_context_menu/spelling_menu_observer.cc
@@ -31,11 +31,11 @@
 #include "components/spellcheck/common/spellcheck_result.h"
 #include "components/spellcheck/spellcheck_buildflags.h"
 #include "content/public/browser/browser_thread.h"
+#include "content/public/browser/context_menu_params.h"
 #include "content/public/browser/render_view_host.h"
 #include "content/public/browser/render_widget_host.h"
 #include "content/public/browser/render_widget_host_view.h"
 #include "content/public/browser/web_contents.h"
-#include "content/public/common/context_menu_params.h"
 #include "extensions/browser/view_type_utils.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/gfx/geometry/rect.h"
diff --git a/chrome/browser/renderer_context_menu/spelling_menu_observer_browsertest.cc b/chrome/browser/renderer_context_menu/spelling_menu_observer_browsertest.cc
index 23f2a2bc..605b05c 100644
--- a/chrome/browser/renderer_context_menu/spelling_menu_observer_browsertest.cc
+++ b/chrome/browser/renderer_context_menu/spelling_menu_observer_browsertest.cc
@@ -13,7 +13,7 @@
 #include "components/prefs/pref_service.h"
 #include "components/spellcheck/browser/pref_names.h"
 #include "components/spellcheck/browser/spelling_service_client.h"
-#include "content/public/common/context_menu_params.h"
+#include "content/public/browser/context_menu_params.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace {
diff --git a/chrome/browser/renderer_context_menu/spelling_options_submenu_observer_browsertest.cc b/chrome/browser/renderer_context_menu/spelling_options_submenu_observer_browsertest.cc
index 40f1ab1..f6cf0d8 100644
--- a/chrome/browser/renderer_context_menu/spelling_options_submenu_observer_browsertest.cc
+++ b/chrome/browser/renderer_context_menu/spelling_options_submenu_observer_browsertest.cc
@@ -13,7 +13,7 @@
 #include "components/language/core/browser/pref_names.h"
 #include "components/prefs/pref_service.h"
 #include "components/spellcheck/browser/pref_names.h"
-#include "content/public/common/context_menu_params.h"
+#include "content/public/browser/context_menu_params.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace {
diff --git a/chrome/browser/resources/chromeos/edu_login/BUILD.gn b/chrome/browser/resources/chromeos/edu_login/BUILD.gn
index 187499e..5f0e417 100644
--- a/chrome/browser/resources/chromeos/edu_login/BUILD.gn
+++ b/chrome/browser/resources/chromeos/edu_login/BUILD.gn
@@ -13,12 +13,15 @@
     ":edu_login_button",
     ":edu_login_template",
     ":edu_login_util",
+    ":edu_login_welcome",
   ]
 }
 
 js_library("app") {
   deps = [
+    ":edu_login_welcome",
     "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
+    "//ui/webui/resources/cr_elements/cr_view_manager:cr_view_manager.m",
   ]
 }
 
@@ -47,6 +50,14 @@
 js_library("edu_login_util") {
 }
 
+js_library("edu_login_welcome") {
+  deps = [
+    ":edu_login_button",
+    ":edu_login_template",
+    "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
+  ]
+}
+
 polymer_modulizer("app") {
   js_file = "app.js"
   html_file = "app.html"
@@ -71,11 +82,18 @@
   html_type = "v3-ready"
 }
 
+polymer_modulizer("edu_login_welcome") {
+  js_file = "edu_login_welcome.js"
+  html_file = "edu_login_welcome.html"
+  html_type = "v3-ready"
+}
+
 group("polymer3_elements") {
   deps = [
     ":app_module",
     ":edu_login_button_module",
     ":edu_login_css_module",
     ":edu_login_template_module",
+    ":edu_login_welcome_module",
   ]
 }
diff --git a/chrome/browser/resources/chromeos/edu_login/app.html b/chrome/browser/resources/chromeos/edu_login/app.html
index 0c0ba73..c37a6a5e 100644
--- a/chrome/browser/resources/chromeos/edu_login/app.html
+++ b/chrome/browser/resources/chromeos/edu_login/app.html
@@ -1 +1,4 @@
-<!-- TODO(anastasiian) -->
+<cr-view-manager id="viewManager">
+  <edu-login-welcome id="[[Steps.WELCOME]]" slot="view">
+  </edu-login-welcome>
+</cr-view-manager>
diff --git a/chrome/browser/resources/chromeos/edu_login/app.js b/chrome/browser/resources/chromeos/edu_login/app.js
index a32e1a5..7056d1a 100644
--- a/chrome/browser/resources/chromeos/edu_login/app.js
+++ b/chrome/browser/resources/chromeos/edu_login/app.js
@@ -2,10 +2,68 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import './edu_login_welcome.js';
+import 'chrome://resources/cr_elements/cr_view_manager/cr_view_manager.m.js';
+
 import {html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
+/** @enum {string} */
+const Steps = {
+  WELCOME: 'welcome',
+  PARENTS: 'parents',
+  PARENT_SIGNIN: 'parent-signin',
+  PARENT_INFO: 'parent-info',
+  EDU_LOGIN: 'edu-login'
+};
+
+/** @type {!Array<Steps>} */
+const stepsArray = Object.values(Steps);
+
 Polymer({
   is: 'edu-login-app',
 
   _template: html`{__html_template__}`,
+
+  properties: {
+    /** Mirroring the enum so that it can be used from HTML bindings. */
+    Steps: {
+      type: Object,
+      value: Steps,
+    },
+
+    /**
+     * Index of the current step displayed.
+     * @private {number}
+     */
+    stepIndex_: {
+      type: Number,
+      value: 0,
+    },
+  },
+
+  listeners: {
+    'go-next': 'onGoNext_',
+  },
+
+  /** @override */
+  ready() {
+    this.switchViewAtIndex_(this.stepIndex_);
+  },
+
+  /** Switches to the next view. */
+  onGoNext_() {
+    if (this.stepIndex_ < stepsArray.length - 1) {
+      ++this.stepIndex_;
+    }
+    this.switchViewAtIndex_(this.stepIndex_);
+  },
+
+  /**
+   * Switches to the specified step.
+   * @param {number} index of the step to be shown.
+   */
+  switchViewAtIndex_(index) {
+    /** @type {CrViewManagerElement} */ (this.$.viewManager)
+        .switchView(stepsArray[index]);
+  },
 });
diff --git a/chrome/browser/resources/chromeos/edu_login/edu_login.html b/chrome/browser/resources/chromeos/edu_login/edu_login.html
index 711ac1c..4ba7025e 100644
--- a/chrome/browser/resources/chromeos/edu_login/edu_login.html
+++ b/chrome/browser/resources/chromeos/edu_login/edu_login.html
@@ -2,6 +2,7 @@
 <html dir="$i18n{textdirection}" lang="$i18n{language}">
 <head>
   <meta charset="utf-8">
+  <title>$i18n{welcomeTitle}</title>
   <link rel="stylesheet" href="chrome://resources/css/text_defaults_md.css">
   <style>
     html,
diff --git a/chrome/browser/resources/chromeos/edu_login/edu_login_button.js b/chrome/browser/resources/chromeos/edu_login/edu_login_button.js
index c78b3b1..cf7f9c9 100644
--- a/chrome/browser/resources/chromeos/edu_login/edu_login_button.js
+++ b/chrome/browser/resources/chromeos/edu_login/edu_login_button.js
@@ -116,7 +116,9 @@
   onTap_(e) {
     if (this.disabled) {
       e.stopPropagation();
+      return;
     }
+    this.fire(this.buttonType === ButtonTypes.BACK ? 'go-back' : 'go-next');
   }
 
 });
diff --git a/chrome/browser/resources/chromeos/edu_login/edu_login_css.html b/chrome/browser/resources/chromeos/edu_login/edu_login_css.html
index e2eb0297e..4e741aa 100644
--- a/chrome/browser/resources/chromeos/edu_login/edu_login_css.html
+++ b/chrome/browser/resources/chromeos/edu_login/edu_login_css.html
@@ -1,5 +1,22 @@
 <template>
   <style include="cr-shared-style cros-color-overrides">
+    h1 {
+      font-size: 2em;
+      font-weight: normal;
+      margin-bottom: 16px;
+      margin-top: 28px;
+    }
+
+    p {
+      line-height: 1.5;
+      white-space: pre-line;
+    }
+
+    a {
+      color: var(--cr-link-color);
+      text-decoration: none;
+    }
+
     .main-padding {
       padding-inline-end: 64px;
       padding-inline-start: 64px;
@@ -14,7 +31,13 @@
       @apply --cr-secondary-text;
     }
 
-    #google-full-logo {
+    .google-logo {
+      height: 32px;
+      margin-top: 10px;
+      width: 32px;
+    }
+
+    .google-full-logo {
       width: 74px;
     }
   </style>
diff --git a/chrome/browser/resources/chromeos/edu_login/edu_login_welcome.html b/chrome/browser/resources/chromeos/edu_login/edu_login_welcome.html
new file mode 100644
index 0000000..b2635cd
--- /dev/null
+++ b/chrome/browser/resources/chromeos/edu_login/edu_login_welcome.html
@@ -0,0 +1,17 @@
+<style include="edu-login-css"></style>
+<edu-login-template>
+  <span slot="main">
+    <div class="main-padding">
+      <if expr="_google_chrome">
+        <img class="google-logo"
+            src="chrome://chrome-signin/googleg.svg" alt="">
+      </if>
+      <h1>$i18n{welcomeTitle}</h1>
+      <p>$i18n{welcomeBody}</p>
+    </div>
+  </span>
+  <span slot="buttons">
+    <edu-login-button button-type="ok">
+    </edu-login-button>
+  </span>
+</edu-login-template>
diff --git a/chrome/browser/resources/chromeos/edu_login/edu_login_welcome.js b/chrome/browser/resources/chromeos/edu_login/edu_login_welcome.js
new file mode 100644
index 0000000..798cbfc
--- /dev/null
+++ b/chrome/browser/resources/chromeos/edu_login/edu_login_welcome.js
@@ -0,0 +1,15 @@
+// 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.
+
+import './edu_login_css.js';
+import './edu_login_template.js';
+import './edu_login_button.js';
+
+import {html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+Polymer({
+  is: 'edu-login-welcome',
+
+  _template: html`{__html_template__}`,
+});
diff --git a/chrome/browser/resources/local_ntp/externs.js b/chrome/browser/resources/local_ntp/externs.js
index 175b4778..51b85d3 100644
--- a/chrome/browser/resources/local_ntp/externs.js
+++ b/chrome/browser/resources/local_ntp/externs.js
@@ -330,13 +330,17 @@
  *   searchBox: (!{
  *     bg: !Array<number>,
  *     icon: !Array<number>,
+ *     iconSelected: !Array<number>,
  *     placeholder: !Array<number>,
  *     resultsBg: !Array<number>,
  *     resultsBgHovered: !Array<number>,
  *     resultsBgSelected: !Array<number>,
  *     resultsDim: !Array<number>,
+ *     resultsDimSelected: !Array<number>,
  *     resultsText: !Array<number>,
+ *     resultsTextSelected: !Array<number>,
  *     resultsUrl: !Array<number>,
+ *     resultsUrlSelected: !Array<number>,
  *     text: !Array<number>,
  *   }|undefined),
  *   textColorLightRgba: !Array<number>,
diff --git a/chrome/browser/resources/local_ntp/local_ntp.css b/chrome/browser/resources/local_ntp/local_ntp.css
index e3d9220..22e06db 100644
--- a/chrome/browser/resources/local_ntp/local_ntp.css
+++ b/chrome/browser/resources/local_ntp/local_ntp.css
@@ -335,6 +335,11 @@
   width: 24px;
 }
 
+#realbox-matches a:-webkit-any(:focus-within, .selected)
+    :-webkit-any(.clock-icon, .search-icon) {
+  background-color: var(--search-box-icon-selected, rgb(117, 117, 117));
+}
+
 .clock-icon {
   -webkit-mask-image: url(icons/clock.svg);
 }
@@ -380,6 +385,7 @@
 
 #realbox-matches a:-webkit-any(:focus-within, .selected) {
   background-color: var(--search-box-results-bg-selected, rgb(219, 219, 220));
+  color: var(--search-box-results-text-selected, rgb(var(--GG900-rgb)));
 }
 
 #realbox-matches .match {
@@ -390,10 +396,18 @@
   color: var(--search-box-results-dim, rgb(var(--GG600-rgb)));
 }
 
+#realbox-matches a:-webkit-any(:focus-within, .selected) .dim {
+  color: var(--search-box-results-dim-selected, rgb(var(--GG600-rgb)));
+}
+
 #realbox-matches .url {
   color: var(--search-box-results-url, rgb(var(--GB600-rgb)));
 }
 
+#realbox-matches  a:-webkit-any(:focus-within, .selected) .url {
+  color: var(--search-box-results-url-selected, rgb(var(--GB600-rgb)));
+}
+
 #realbox-matches .remove-match {
   border-radius: 50%;
   height: 24px;
@@ -409,11 +423,17 @@
 }
 
 #realbox-matches .remove-match:hover {
-  background-color: var(--remove-match-hovered, rgba(var(--GG900-rgb), .08));
+  background-color: var(--remove-match-hovered, rgba(var(--GG900-rgb), .16));
 }
 
-#realbox-matches .remove-match:focus-within {
-  background-color: var(--remove-match-focused, rgba(var(--GG900-rgb), .16));
+#realbox-matches a:-webkit-any(:focus-within, .selected) .remove-match:hover {
+  background-color:
+      var(--remove-match-selected-hovered, rgba(var(--GG900-rgb), .16));
+}
+
+#realbox-matches a:-webkit-any(:focus-within, .selected)
+    .remove-match:focus-within {
+  background-color: var(--remove-match-focused, rgba(var(--GG900-rgb), .32));
 }
 
 #realbox-matches .remove-icon {
@@ -421,14 +441,21 @@
   width: 100%;
 }
 
-#realbox-matches a:-webkit-any(:hover, :focus-within, .selected) .remove-icon {
+#realbox-matches .remove-icon {
   -webkit-mask-image: url(../../../../ui/webui/resources/images/icon_clear.svg);
   -webkit-mask-position: center;
   -webkit-mask-repeat: no-repeat;
   -webkit-mask-size: 16px;
+}
+
+#realbox-matches a:-webkit-any(:hover) .remove-icon {
   background-color: var(--search-box-icon, rgb(var(--GG900-rgb)));
 }
 
+#realbox-matches a:-webkit-any(:focus-within, .selected) .remove-icon {
+  background-color: var(--search-box-icon-selected, rgb(var(--GG900-rgb)));
+}
+
 #fakebox > input {
   bottom: 0;
   box-sizing: border-box;
diff --git a/chrome/browser/resources/local_ntp/local_ntp.js b/chrome/browser/resources/local_ntp/local_ntp.js
index 0e9acb05..42dd763 100644
--- a/chrome/browser/resources/local_ntp/local_ntp.js
+++ b/chrome/browser/resources/local_ntp/local_ntp.js
@@ -1951,10 +1951,14 @@
   if (configData.realboxMatchOmniboxTheme) {
     // TODO(dbeam): actually get these from theme service.
     const removeMatchHovered = assert(theme.searchBox.icon).slice();
-    removeMatchHovered[3] = .08 * 255;
+    removeMatchHovered[3] = .16 * 255;
 
-    const removeMatchFocused = theme.searchBox.icon.slice();
-    removeMatchFocused[3] = .16 * 255;
+    const removeMatchSelectedHovered =
+        assert(theme.searchBox.iconSelected).slice();
+    removeMatchSelectedHovered[3] = .16 * 255;
+
+    const removeMatchFocused = theme.searchBox.iconSelected.slice();
+    removeMatchFocused[3] = .32 * 255;
 
     /**
      * @param {string} varName
@@ -1967,6 +1971,7 @@
 
     setCssVar('search-box-bg', theme.searchBox.bg);
     setCssVar('search-box-icon', theme.searchBox.icon);
+    setCssVar('search-box-icon-selected', theme.searchBox.iconSelected);
     setCssVar('search-box-placeholder', theme.searchBox.placeholder);
     setCssVar('search-box-results-bg', theme.searchBox.resultsBg);
     setCssVar(
@@ -1974,10 +1979,18 @@
     setCssVar(
         'search-box-results-bg-selected', theme.searchBox.resultsBgSelected);
     setCssVar('search-box-results-dim', theme.searchBox.resultsDim);
+    setCssVar(
+        'search-box-results-dim-selected', theme.searchBox.resultsDimSelected);
     setCssVar('search-box-results-text', theme.searchBox.resultsText);
+    setCssVar(
+        'search-box-results-text-selected',
+        theme.searchBox.resultsTextSelected);
     setCssVar('search-box-results-url', theme.searchBox.resultsUrl);
+    setCssVar(
+        'search-box-results-url-selected', theme.searchBox.resultsUrlSelected);
     setCssVar('search-box-text', theme.searchBox.text);
     setCssVar('remove-match-hovered', removeMatchHovered);
+    setCssVar('remove-match-selected-hovered', removeMatchSelectedHovered);
     setCssVar('remove-match-focused', removeMatchFocused);
   }
 }
diff --git a/chrome/browser/resources/settings/BUILD.gn b/chrome/browser/resources/settings/BUILD.gn
index b51e7505..91e06b51 100644
--- a/chrome/browser/resources/settings/BUILD.gn
+++ b/chrome/browser/resources/settings/BUILD.gn
@@ -239,6 +239,7 @@
     "settings_main:closure_compile_module",
     "settings_menu:closure_compile_module",
     "settings_page:closure_compile_module",
+    "settings_ui:closure_compile_module",
   ]
   if (!is_chromeos) {
     deps += [
@@ -259,15 +260,13 @@
   is_polymer3 = true
   deps = [
     ":extension_control_browser_proxy.m",
+    ":global_scroll_target_behavior.m",
     ":i18n_setup.m",
 
     # TODO(crbug.com/1026426): Fix and enable.
-    ":global_scroll_target_behavior.m",
-
     #":icons.m",
     ":lifetime_browser_proxy.m",
-
-    #":metrics_browser_proxy.m",
+    ":metrics_browser_proxy.m",
     ":open_window_proxy.m",
     ":page_visibility.m",
     ":plural_string_proxy.m",
@@ -315,9 +314,7 @@
 
 js_library("metrics_browser_proxy.m") {
   sources = [ "$root_gen_dir/chrome/browser/resources/settings/metrics_browser_proxy.m.js" ]
-  deps = [
-    # TODO: Fill those in.
-  ]
+  deps = [ "//ui/webui/resources/js:cr.m" ]
   extra_deps = [ ":modulize" ]
 }
 
diff --git a/chrome/browser/resources/settings/global_scroll_target_behavior.js b/chrome/browser/resources/settings/global_scroll_target_behavior.js
index a9deb2fd5..29ec449 100644
--- a/chrome/browser/resources/settings/global_scroll_target_behavior.js
+++ b/chrome/browser/resources/settings/global_scroll_target_behavior.js
@@ -98,11 +98,11 @@
    * This should only be called once.
    * @param {HTMLElement} scrollTarget
    */
-  function setGlobalScrollTarget(scrollTarget) {
+  /* #export */ function setGlobalScrollTarget(scrollTarget) {
     scrollTargetResolver.resolve(scrollTarget);
   }
 
-  function resetGlobalScrollTargetForTesting() {
+  /* #export */ function resetGlobalScrollTargetForTesting() {
     scrollTargetResolver = new PromiseResolver();
   }
 
diff --git a/chrome/browser/resources/settings/metrics_browser_proxy.js b/chrome/browser/resources/settings/metrics_browser_proxy.js
index 9fe2095..4d2b63d 100644
--- a/chrome/browser/resources/settings/metrics_browser_proxy.js
+++ b/chrome/browser/resources/settings/metrics_browser_proxy.js
@@ -4,6 +4,10 @@
 
 /** @fileoverview Handles metrics for the settings pages. */
 
+// clang-format off
+// #import {addSingletonGetter} from 'chrome://resources/js/cr.m.js';
+// clang-format on
+
 cr.define('settings', function() {
   /**
    * Contains all possible recorded interactions across privacy settings pages.
@@ -15,7 +19,7 @@
    * histograms/enums.xml
    * @enum {number}
    */
-  const PrivacyElementInteractions = {
+  /* #export */ const PrivacyElementInteractions = {
     SYNC_AND_GOOGLE_SERVICES: 0,
     CHROME_SIGN_IN: 1,
     DO_NOT_TRACK: 2,
@@ -43,7 +47,7 @@
   /**
    * @implements {settings.MetricsBrowserProxy}
    */
-  class MetricsBrowserProxyImpl {
+  /* #export */ class MetricsBrowserProxyImpl {
     /** @override*/
     recordSettingsPageHistogram(interaction) {
       chrome.send('metricsHandler:recordInHistogram', [
diff --git a/chrome/browser/resources/settings/privacy_page/secure_dns.html b/chrome/browser/resources/settings/privacy_page/secure_dns.html
index 65fcabe..c1988edc 100644
--- a/chrome/browser/resources/settings/privacy_page/secure_dns.html
+++ b/chrome/browser/resources/settings/privacy_page/secure_dns.html
@@ -17,13 +17,36 @@
 <dom-module id="settings-secure-dns">
   <template>
     <style include="settings-shared md-select">
-      #secureResolverSelect {
-        margin-inline-start: 12px;
+      #automaticRadioButton {
+        align-items: flex-start;
+        padding: 6px 0;
+        --cr-radio-button-disc-margin-block-start: calc(
+            (1.54em /* line-height */ - var(--cr-radio-button-size)) / 2);
       }
 
       #secureResolverSelectRadioButton {
         align-items: flex-start;
-        --cr-radio-button-disc-margin-block-start: 1.25em;
+        --cr-radio-button-disc-margin-block-start: calc(
+            (1.54em /* line-height */ + 12px /* md-select padding */ -
+             var(--cr-radio-button-size)) / 2);
+      }
+
+      #secureRadioButtonItem {
+        align-items: baseline;
+      }
+
+      #secureRadioButtonItemInner {
+        margin-inline-start: 0.5em;
+        width: 80%;
+      }
+
+      #privacyPolicy {
+        display: none;
+        padding: 8px /* md-select left padding */;
+      }
+
+      #secureDnsInput {
+        margin-top: 6px;
       }
     </style>
     <settings-toggle-button
@@ -37,28 +60,34 @@
         selected="{{secureDnsRadio_}}"
         on-selected-changed="onRadioSelectionChanged_"
         hidden="[[!showRadioGroup_]]">
-      <cr-radio-button class="list-item" name="[[secureDnsModeEnum_.AUTOMATIC]]"
+      <cr-radio-button id="automaticRadioButton" class="list-item"
+          name="[[secureDnsModeEnum_.AUTOMATIC]]"
           label="$i18n{secureDnsAutomaticModeDescription}">
+        <div class="secondary">
+          $i18n{secureDnsAutomaticModeDescriptionSecondary}
+        </div>
       </cr-radio-button>
       <cr-radio-button id="secureResolverSelectRadioButton" class="list-item"
-          name="[[secureDnsModeEnum_.SECURE]]">
-        <div class="list-item">
-          <div>$i18n{secureDnsSecureDropdownModeDescription}</div>
-          <select id="secureResolverSelect" class="md-select"
-              on-click="stopEventPropagation_"
-              on-change="onDropdownSelectionChanged_">
-            <template is="dom-repeat" items="[[resolverOptions_]]">
-              <option value="[[item.value]]">[[item.name]]</option>
-            </template>
-          </select>
-        </div>
-        <div id="privacyPolicy" class="secondary"
-            inner-h-t-m-l="[[privacyPolicyString_]]"
-            hidden="[[!showPrivacyPolicyLine_]]"></div>
-        <secure-dns-input id="secureDnsInput" value="[[secureDnsInputValue_]]"
-            hidden="[[showPrivacyPolicyLine_]]"
-            on-value-update="onSecureDnsInputEvaluated_"
-            on-click="stopEventPropagation_">
+          name="[[secureDnsModeEnum_.SECURE]]"
+          aria-label="$i18n{secureDnsSecureModeA11yLabel}">
+        <div id="secureRadioButtonItem" class="list-item">
+          $i18n{secureDnsSecureDropdownModeDescription}
+          <div id="secureRadioButtonItemInner">
+            <select id="secureResolverSelect" class="md-select"
+                aria-label="$i18n{secureDnsDropdownA11yLabel}"
+                on-click="stopEventPropagation_"
+                on-change="onDropdownSelectionChanged_">
+              <template is="dom-repeat" items="[[resolverOptions_]]">
+                <option value="[[item.value]]">[[item.name]]</option>
+              </template>
+            </select>
+            <div id="privacyPolicy" class="secondary"
+                inner-h-t-m-l="[[privacyPolicyString_]]"></div>
+            <secure-dns-input id="secureDnsInput"
+                value="[[secureDnsInputValue_]]"
+                on-value-update="onSecureDnsInputEvaluated_"
+                on-click="stopEventPropagation_">
+          </div>
         </secure-dns-input>
       </cr-radio-button>
     </cr-radio-group>
diff --git a/chrome/browser/resources/settings/privacy_page/secure_dns.js b/chrome/browser/resources/settings/privacy_page/secure_dns.js
index 1b5bdcee..c0a84cf 100644
--- a/chrome/browser/resources/settings/privacy_page/secure_dns.js
+++ b/chrome/browser/resources/settings/privacy_page/secure_dns.js
@@ -82,12 +82,6 @@
     resolverOptions_: Array,
 
     /**
-     * Whether the privacy policy line should be displayed.
-     * @private
-     */
-    showPrivacyPolicyLine_: Boolean,
-
-    /**
      * String displaying the privacy policy of the resolver selected in the
      * dropdown menu.
      * @private
@@ -353,12 +347,14 @@
     // If the selected item is the custom provider option, hide the privacy
     // policy line.
     if (this.$.secureResolverSelect.value === 'custom') {
-      this.showPrivacyPolicyLine_ = false;
+      this.$.privacyPolicy.style.display = 'none';
+      this.$.secureDnsInput.style.display = 'block';
       return;
     }
 
     // Otherwise, display the corresponding privacy policy.
-    this.showPrivacyPolicyLine_ = true;
+    this.$.privacyPolicy.style.display = 'block';
+    this.$.secureDnsInput.style.display = 'none';
     const resolver = this.resolverOptions_.find(
         r => r.value === this.$.secureResolverSelect.value);
     if (!resolver) {
diff --git a/chrome/browser/resources/settings/settings.gni b/chrome/browser/resources/settings/settings.gni
index c82834d5..2040b7d 100644
--- a/chrome/browser/resources/settings/settings.gni
+++ b/chrome/browser/resources/settings/settings.gni
@@ -28,6 +28,7 @@
   "settings.LanguagesBrowserProxy|LanguagesBrowserProxy",
   "settings.LifetimeBrowserProxy|LifetimeBrowserProxy",
   "settings.MainPageBehavior|MainPageBehavior",
+  "settings.MetricsBrowserProxy|MetricsBrowserProxy",
   "settings.MinimumRoutes|MinimumRoutes",
   "settings.OnStartupBrowserProxy|OnStartupBrowserProxy",
   "settings.OpenWindowProxy|OpenWindowProxy",
@@ -38,6 +39,7 @@
   "Settings.PrefUtil.prefToString|prefToString",
   "Settings.PrefUtil.stringToPrefValue|stringToPrefValue",
   "settings.PrintingBrowserProxy|PrintingBrowserProxy",
+  "settings.PrivacyElementInteractions|PrivacyElementInteractions",
   "settings.ResetBrowserProxy|ResetBrowserProxy",
   "settings.Route|Route",
   "settings.routes|routes",
diff --git a/chrome/browser/resources/settings/settings.js b/chrome/browser/resources/settings/settings.js
index b876ae5..a212852 100644
--- a/chrome/browser/resources/settings/settings.js
+++ b/chrome/browser/resources/settings/settings.js
@@ -36,6 +36,7 @@
 import './settings_menu/settings_menu.m.js';
 import './settings_page/settings_subpage.m.js';
 import './settings_page/settings_animated_pages.m.js';
+import './settings_ui/settings_ui.m.js';
 
 // <if expr="_google_chrome and is_win">
 import './incompatible_applications_page/incompatible_applications_page.m.js';
@@ -81,6 +82,7 @@
 export {kMenuCloseDelay} from './languages_page/languages_page.m.js';
 export {LanguagesBrowserProxyImpl} from './languages_page/languages_browser_proxy.m.js';
 export {LifetimeBrowserProxyImpl} from './lifetime_browser_proxy.m.js';
+export {MetricsBrowserProxyImpl, PrivacyElementInteractions} from './metrics_browser_proxy.m.js';
 export {OnStartupBrowserProxy, OnStartupBrowserProxyImpl} from './on_startup_page/on_startup_browser_proxy.m.js';
 export {EDIT_STARTUP_URL_EVENT} from './on_startup_page/startup_url_entry.m.js';
 export {StartupUrlsPageBrowserProxy, StartupUrlsPageBrowserProxyImpl} from './on_startup_page/startup_urls_page_browser_proxy.m.js';
diff --git a/chrome/browser/resources/settings/settings_resources_v3.grdp b/chrome/browser/resources/settings/settings_resources_v3.grdp
index e9fe0ee6..64030e9 100644
--- a/chrome/browser/resources/settings/settings_resources_v3.grdp
+++ b/chrome/browser/resources/settings/settings_resources_v3.grdp
@@ -302,6 +302,10 @@
            use_base_dir="false"
            type="BINDATA"
            preprocess="true" />
+  <include name="IDR_SETTINGS_METRICS_BROWSER_PROXY_M_JS"
+           file="${root_gen_dir}/chrome/browser/resources/settings/metrics_browser_proxy.m.js"
+           use_base_dir="false"
+           type="BINDATA" />
   <include name="IDR_SETTINGS_ON_STARTUP_PAGE_ON_STARTUP_BROWSER_PROXY_M_JS"
            file="${root_gen_dir}/chrome/browser/resources/settings/on_startup_page/on_startup_browser_proxy.m.js"
            use_base_dir="false"
@@ -448,6 +452,11 @@
            file="${root_gen_dir}/chrome/browser/resources/settings/settings_page/settings_subpage.m.js"
            use_base_dir="false"
            type="BINDATA" />
+  <include name="IDR_SETTINGS_SETTINGS_UI_SETTINGS_UI_M_JS"
+           file="${root_gen_dir}/chrome/browser/resources/settings/settings_ui/settings_ui.m.js"
+           use_base_dir="false"
+           type="BINDATA"
+           preprocess="true" />
   <include name="IDR_SETTINGS_SETTINGS_ROUTES_M_JS"
            file="${root_gen_dir}/chrome/browser/resources/settings/settings_routes.m.js"
            use_base_dir="false"
diff --git a/chrome/browser/resources/settings/settings_ui/BUILD.gn b/chrome/browser/resources/settings/settings_ui/BUILD.gn
index 0b15c3d..92a8310 100644
--- a/chrome/browser/resources/settings/settings_ui/BUILD.gn
+++ b/chrome/browser/resources/settings/settings_ui/BUILD.gn
@@ -3,6 +3,8 @@
 # found in the LICENSE file.
 
 import("//third_party/closure_compiler/compile_js.gni")
+import("//tools/polymer/polymer.gni")
+import("../settings.gni")
 
 js_type_check("closure_compile") {
   deps = [ ":settings_ui" ]
@@ -25,28 +27,54 @@
   ]
 }
 
-# TODO(crbug.com/1026426): Fix and enable.
-#js_type_check("closure_compile_module") {
-#  is_polymer3 = true
-#  deps = [ ":settings_ui.m" ]
-#}
+js_type_check("closure_compile_module") {
+  is_polymer3 = true
+  deps = [ ":settings_ui.m" ]
+}
 
 js_library("settings_ui.m") {
   sources = [ "$root_gen_dir/chrome/browser/resources/settings/settings_ui/settings_ui.m.js" ]
   deps = [
-    # TODO: Fill those in.
+    "..:global_scroll_target_behavior.m",
+    "..:page_visibility.m",
+    "..:route.m",
+    "..:router.m",
+    "../prefs:prefs.m",
+    "../settings_main:settings_main.m",
+    "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
+    "//ui/webui/resources/cr_elements:cr_container_shadow_behavior.m",
+    "//ui/webui/resources/cr_elements/cr_drawer:cr_drawer.m",
+    "//ui/webui/resources/cr_elements/cr_toolbar:cr_toolbar.m",
+    "//ui/webui/resources/cr_elements/cr_toolbar:cr_toolbar_search_field.m",
+    "//ui/webui/resources/cr_elements/policy:cr_policy_indicator_behavior.m",
+    "//ui/webui/resources/js:assert.m",
+    "//ui/webui/resources/js:cr.m",
+    "//ui/webui/resources/js:find_shortcut_behavior.m",
+    "//ui/webui/resources/js:util.m",
   ]
   extra_deps = [ ":settings_ui_module" ]
 }
 
-import("//tools/polymer/polymer.gni")
-
 group("polymer3_elements") {
-  deps = [ ":settings_ui_module" ]
+  public_deps = [ ":settings_ui_module" ]
 }
 
 polymer_modulizer("settings_ui") {
   js_file = "settings_ui.js"
   html_file = "settings_ui.html"
   html_type = "dom-module"
+  auto_imports = settings_auto_imports + [
+                   "chrome/browser/resources/settings/global_scroll_target_behavior.html|setGlobalScrollTarget,resetGlobalScrollTargetForTesting",
+                   "chrome/browser/resources/settings/page_visibility.html|PageVisibility,pageVisibility",
+                   "chrome/browser/resources/settings/route.html|routes",
+                   "chrome/browser/resources/settings/router.html|Route,Router,RouteObserverBehavior",
+                   "ui/webui/resources/html/assert.html|assert",
+                   "ui/webui/resources/html/cr.html|isChromeOS",
+                   "ui/webui/resources/html/util.html|listenOnce",
+                   "ui/webui/resources/cr_elements/cr_container_shadow_behavior.html|CrContainerShadowBehavior",
+                 ]
+  namespace_rewrites = settings_namespace_rewrites + [
+                         "settings.setGlobalScrollTarget|setGlobalScrollTarget",
+                         "settings.resetGlobalScrollTargetForTesting|resetGlobalScrollTargetForTesting",
+                       ]
 }
diff --git a/chrome/browser/resources/settings/settings_ui/settings_ui.html b/chrome/browser/resources/settings/settings_ui/settings_ui.html
index 72edacc6..039fbac 100644
--- a/chrome/browser/resources/settings/settings_ui/settings_ui.html
+++ b/chrome/browser/resources/settings/settings_ui/settings_ui.html
@@ -6,6 +6,8 @@
 <link rel="import" href="chrome://resources/cr_elements/cr_toolbar/cr_toolbar.html">
 <link rel="import" href="chrome://resources/cr_elements/icons.html">
 <link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
+<link rel="import" href="chrome://resources/html/assert.html">
+<link rel="import" href="chrome://resources/html/cr.html">
 <link rel="import" href="chrome://resources/html/find_shortcut_behavior.html">
 <link rel="import" href="chrome://resources/html/util.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-styles/color.html">
diff --git a/chrome/browser/resources/settings/settings_ui/settings_ui.js b/chrome/browser/resources/settings/settings_ui/settings_ui.js
index 1330bb0..2942e2b0 100644
--- a/chrome/browser/resources/settings/settings_ui/settings_ui.js
+++ b/chrome/browser/resources/settings/settings_ui/settings_ui.js
@@ -14,9 +14,10 @@
   /** Defined when the main Settings script runs. */
   let defaultResourceLoaded = true;  // eslint-disable-line prefer-const
 
-  assert(
-      !window.settings || !window.settings.defaultResourceLoaded,
-      'settings_ui.js run twice. You probably have an invalid import.');
+  /* #ignore */ assert(
+      /* #ignore */ !window.settings || !window.settings.defaultResourceLoaded,
+      /* #ignore */ 'settings_ui.js run twice. ' +
+          /* #ignore */ 'You probably have an invalid import.');
 
   Polymer({
     is: 'settings-ui',
@@ -97,7 +98,7 @@
         this.$.drawer.cancel();
       });
 
-      CrPolicyStrings = {
+      window.CrPolicyStrings = {
         controlledSettingExtension:
             loadTimeData.getString('controlledSettingExtension'),
         controlledSettingExtensionWithoutName:
@@ -143,7 +144,8 @@
 
       // Preload bold Roboto so it doesn't load and flicker the first time used.
       document.fonts.load('bold 12px Roboto');
-      settings.setGlobalScrollTarget(this.$.container);
+      settings.setGlobalScrollTarget(
+          /** @type {HTMLElement} */ (this.$.container));
 
       const scrollToTop = top => new Promise(resolve => {
         if (this.$.container.scrollTop === top) {
diff --git a/chrome/browser/resources/tab_strip/tab_group.html b/chrome/browser/resources/tab_strip/tab_group.html
index 7784dd8b..802d3ea 100644
--- a/chrome/browser/resources/tab_strip/tab_group.html
+++ b/chrome/browser/resources/tab_strip/tab_group.html
@@ -15,10 +15,16 @@
   padding: var(--tabstrip-tab-spacing);
 }
 
+#chipContainer {
+  min-width: 100%;
+  width: 0;
+}
+
 #chip {
   display: inline-block;
   height: var(--tabstrip-tab-group-title-height);
   margin-bottom: var(--tabstrip-tab-group-title-margin);
+  max-width: 100%;
 }
 
 #chip:focus {
@@ -33,7 +39,11 @@
   display: inline-block;
   height: var(--tabstrip-tab-group-title-height);
   line-height: var(--tabstrip-tab-group-title-height);
+  max-width: 100%;
+  overflow: hidden;
   padding: 0 6px;
+  text-overflow: ellipsis;
+  white-space: nowrap;
 }
 
 #title:empty {
@@ -157,8 +167,10 @@
 
 <div id="dragImage">
   <div id="tabGroup">
-    <div id="chip" draggable="true" tabindex="0" role="button">
-      <div id="title"></div>
+    <div id="chipContainer">
+      <div id="chip" draggable="true" tabindex="0" role="button">
+        <div id="title"></div>
+      </div>
     </div>
     <div id="tabs">
       <slot></slot>
diff --git a/chrome/browser/safe_browsing/chrome_password_protection_service.cc b/chrome/browser/safe_browsing/chrome_password_protection_service.cc
index c1a8c1b..05e0a4f2 100644
--- a/chrome/browser/safe_browsing/chrome_password_protection_service.cc
+++ b/chrome/browser/safe_browsing/chrome_password_protection_service.cc
@@ -1387,7 +1387,7 @@
 }
 
 bool ChromePasswordProtectionService::IsSafeBrowsingEnabled() {
-  return GetPrefs()->GetBoolean(prefs::kSafeBrowsingEnabled);
+  return ::safe_browsing::IsSafeBrowsingEnabled(*GetPrefs());
 }
 
 bool ChromePasswordProtectionService::IsExtendedReporting() {
diff --git a/chrome/browser/safe_browsing/client_side_detection_service.cc b/chrome/browser/safe_browsing/client_side_detection_service.cc
index cd4cdae..e6b7fcc2 100644
--- a/chrome/browser/safe_browsing/client_side_detection_service.cc
+++ b/chrome/browser/safe_browsing/client_side_detection_service.cc
@@ -182,7 +182,7 @@
   // and we select the model based on the extended reporting setting.
   Profile* profile = Profile::FromBrowserContext(process->GetBrowserContext());
   std::string model;
-  if (profile->GetPrefs()->GetBoolean(prefs::kSafeBrowsingEnabled)) {
+  if (IsSafeBrowsingEnabled(*profile->GetPrefs())) {
     if (IsExtendedReportingEnabled(*profile->GetPrefs()) ||
         IsEnhancedProtectionEnabled(*profile->GetPrefs())) {
       DVLOG(2) << "Sending phishing model " << model_loader_extended_->name()
diff --git a/chrome/browser/safe_browsing/download_protection/download_protection_service.cc b/chrome/browser/safe_browsing/download_protection/download_protection_service.cc
index 85769214..f77485e 100644
--- a/chrome/browser/safe_browsing/download_protection/download_protection_service.cc
+++ b/chrome/browser/safe_browsing/download_protection/download_protection_service.cc
@@ -199,7 +199,7 @@
   Profile* profile = Profile::FromBrowserContext(
       content::DownloadItemUtils::GetBrowserContext(item));
   bool safe_browsing_enabled =
-      profile && profile->GetPrefs()->GetBoolean(prefs::kSafeBrowsingEnabled);
+      profile && IsSafeBrowsingEnabled(*profile->GetPrefs());
   bool deep_scanning_enabled =
       DeepScanningRequest::ShouldUploadItemByPolicy(item);
 
@@ -246,7 +246,7 @@
     CheckDownloadCallback callback) {
   Profile* profile = Profile::FromBrowserContext(
       content::DownloadItemUtils::GetBrowserContext(item));
-  if (profile && profile->GetPrefs()->GetBoolean(prefs::kSafeBrowsingEnabled)) {
+  if (profile && IsSafeBrowsingEnabled(*profile->GetPrefs())) {
     CheckDownloadUrl(item, std::move(callback));
     return true;
   }
@@ -412,7 +412,7 @@
   content::BrowserContext* browser_context =
       content::DownloadItemUtils::GetBrowserContext(item);
   Profile* profile = Profile::FromBrowserContext(browser_context);
-  if (!profile || !profile->GetPrefs()->GetBoolean(prefs::kSafeBrowsingEnabled))
+  if (!profile || !IsSafeBrowsingEnabled(*profile->GetPrefs()))
     return;
 
   // When users are in incognito mode, no report will be sent and no
diff --git a/chrome/browser/safe_browsing/incident_reporting/incident_reporting_service.cc b/chrome/browser/safe_browsing/incident_reporting/incident_reporting_service.cc
index be8e5fc6..b12350930 100644
--- a/chrome/browser/safe_browsing/incident_reporting/incident_reporting_service.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/incident_reporting_service.cc
@@ -304,7 +304,7 @@
 bool IncidentReportingService::IsEnabledForProfile(Profile* profile) {
   if (profile->IsOffTheRecord())
     return false;
-  if (!profile->GetPrefs()->GetBoolean(prefs::kSafeBrowsingEnabled))
+  if (!IsSafeBrowsingEnabled(*profile->GetPrefs()))
     return false;
   return IsExtendedReportingEnabled(*profile->GetPrefs());
 }
diff --git a/chrome/browser/safe_browsing/safe_browsing_navigation_observer_manager.cc b/chrome/browser/safe_browsing/safe_browsing_navigation_observer_manager.cc
index 9384340f..5ee28df 100644
--- a/chrome/browser/safe_browsing/safe_browsing_navigation_observer_manager.cc
+++ b/chrome/browser/safe_browsing/safe_browsing_navigation_observer_manager.cc
@@ -253,7 +253,7 @@
 // static
 bool SafeBrowsingNavigationObserverManager::IsEnabledAndReady(
     Profile* profile) {
-  return profile->GetPrefs()->GetBoolean(prefs::kSafeBrowsingEnabled) &&
+  return IsSafeBrowsingEnabled(*profile->GetPrefs()) &&
          g_browser_process->safe_browsing_service() &&
          g_browser_process->safe_browsing_service()
              ->navigation_observer_manager();
diff --git a/chrome/browser/safe_browsing/safe_browsing_service.cc b/chrome/browser/safe_browsing/safe_browsing_service.cc
index 5b7031d..03ec9651 100644
--- a/chrome/browser/safe_browsing/safe_browsing_service.cc
+++ b/chrome/browser/safe_browsing/safe_browsing_service.cc
@@ -223,7 +223,7 @@
 
 PasswordProtectionService* SafeBrowsingService::GetPasswordProtectionService(
     Profile* profile) const {
-  if (profile->GetPrefs()->GetBoolean(prefs::kSafeBrowsingEnabled))
+  if (IsSafeBrowsingEnabled(*profile->GetPrefs()))
     return services_delegate_->GetPasswordProtectionService(profile);
   return nullptr;
 }
@@ -364,9 +364,14 @@
   prefs_map_[pref_service] = std::move(registrar);
   RefreshState();
 
-  // Record the current pref state.
+  // Record the current pref state for standard protection.
   UMA_HISTOGRAM_BOOLEAN("SafeBrowsing.Pref.General",
                         pref_service->GetBoolean(prefs::kSafeBrowsingEnabled));
+  // Record the current pref state for enhanced protection. Enhanced protection
+  // is a subset of the standard protection. Thus, |kSafeBrowsingEnabled| count
+  // should always be more than the count of enhanced protection.
+  UMA_HISTOGRAM_BOOLEAN("SafeBrowsing.Pref.Enhanced",
+                        pref_service->GetBoolean(prefs::kSafeBrowsingEnhanced));
   // Extended Reporting metrics are handled together elsewhere.
   RecordExtendedReportingMetrics(*pref_service);
 
@@ -407,7 +412,7 @@
   enabled_by_prefs_ = false;
   estimated_extended_reporting_by_prefs_ = SBER_LEVEL_OFF;
   for (const auto& pref : prefs_map_) {
-    if (pref.first->GetBoolean(prefs::kSafeBrowsingEnabled)) {
+    if (IsSafeBrowsingEnabled(*pref.first)) {
       enabled_by_prefs_ = true;
 
       ExtendedReportingLevel erl =
diff --git a/chrome/browser/safe_browsing/safe_browsing_tab_observer.cc b/chrome/browser/safe_browsing/safe_browsing_tab_observer.cc
index 91e6c6a5..f1cfdd77 100644
--- a/chrome/browser/safe_browsing/safe_browsing_tab_observer.cc
+++ b/chrome/browser/safe_browsing/safe_browsing_tab_observer.cc
@@ -48,7 +48,7 @@
         base::Bind(&SafeBrowsingTabObserver::UpdateSafebrowsingDetectionHost,
                    base::Unretained(this)));
 
-    if (prefs->GetBoolean(prefs::kSafeBrowsingEnabled) &&
+    if (IsSafeBrowsingEnabled(*prefs) &&
         g_browser_process->safe_browsing_detection_service()) {
       safebrowsing_detection_host_ =
           ClientSideDetectionHost::Create(web_contents);
@@ -68,7 +68,7 @@
   Profile* profile =
       Profile::FromBrowserContext(web_contents_->GetBrowserContext());
   PrefService* prefs = profile->GetPrefs();
-  bool safe_browsing = prefs->GetBoolean(prefs::kSafeBrowsingEnabled);
+  bool safe_browsing = IsSafeBrowsingEnabled(*prefs);
   if (safe_browsing &&
       g_browser_process->safe_browsing_detection_service()) {
     if (!safebrowsing_detection_host_.get()) {
diff --git a/chrome/browser/safe_browsing/telemetry/android/android_telemetry_service.cc b/chrome/browser/safe_browsing/telemetry/android/android_telemetry_service.cc
index fad93d6..01b69dd4 100644
--- a/chrome/browser/safe_browsing/telemetry/android/android_telemetry_service.cc
+++ b/chrome/browser/safe_browsing/telemetry/android/android_telemetry_service.cc
@@ -142,7 +142,7 @@
     return false;
   }
 
-  if (!IsSafeBrowsingEnabled()) {
+  if (!IsSafeBrowsingEnabled(*GetPrefs())) {
     RecordApkDownloadTelemetryOutcome(
         ApkDownloadTelemetryOutcome::NOT_SENT_SAFE_BROWSING_NOT_ENABLED);
     return false;
@@ -169,10 +169,6 @@
   return profile_->GetPrefs();
 }
 
-bool AndroidTelemetryService::IsSafeBrowsingEnabled() {
-  return GetPrefs()->GetBoolean(prefs::kSafeBrowsingEnabled);
-}
-
 void AndroidTelemetryService::FillReferrerChain(
     content::WebContents* web_contents,
     ClientSafeBrowsingReportRequest* report) {
diff --git a/chrome/browser/safe_browsing/telemetry/android/android_telemetry_service.h b/chrome/browser/safe_browsing/telemetry/android/android_telemetry_service.h
index c7dc26e..d0b1fb9e 100644
--- a/chrome/browser/safe_browsing/telemetry/android/android_telemetry_service.h
+++ b/chrome/browser/safe_browsing/telemetry/android/android_telemetry_service.h
@@ -111,9 +111,6 @@
   // Helper method to get prefs from |profile_|.
   const PrefService* GetPrefs();
 
-  // Helper method to check if Safe Browsing is enabled.
-  bool IsSafeBrowsingEnabled();
-
   // Profile associated with this instance. Unowned.
   Profile* profile_;
 
diff --git a/chrome/browser/search/instant_service.cc b/chrome/browser/search/instant_service.cc
index 32ff628..41f54bea 100644
--- a/chrome/browser/search/instant_service.cc
+++ b/chrome/browser/search/instant_service.cc
@@ -685,6 +685,8 @@
         GetOmniboxColor(&theme_provider, OmniboxPart::LOCATION_BAR_BACKGROUND);
     theme_->search_box.icon =
         GetOmniboxColor(&theme_provider, OmniboxPart::RESULTS_ICON);
+    theme_->search_box.icon_selected = GetOmniboxColor(
+        &theme_provider, OmniboxPart::RESULTS_ICON, OmniboxPartState::SELECTED);
     theme_->search_box.placeholder =
         GetOmniboxColor(&theme_provider, OmniboxPart::LOCATION_BAR_TEXT_DIMMED);
     theme_->search_box.results_bg =
@@ -697,10 +699,19 @@
                         OmniboxPartState::SELECTED);
     theme_->search_box.results_dim =
         GetOmniboxColor(&theme_provider, OmniboxPart::RESULTS_TEXT_DIMMED);
+    theme_->search_box.results_dim_selected =
+        GetOmniboxColor(&theme_provider, OmniboxPart::RESULTS_TEXT_DIMMED,
+                        OmniboxPartState::SELECTED);
     theme_->search_box.results_text =
         GetOmniboxColor(&theme_provider, OmniboxPart::RESULTS_TEXT_DEFAULT);
+    theme_->search_box.results_text_selected =
+        GetOmniboxColor(&theme_provider, OmniboxPart::RESULTS_TEXT_DEFAULT,
+                        OmniboxPartState::SELECTED);
     theme_->search_box.results_url =
         GetOmniboxColor(&theme_provider, OmniboxPart::RESULTS_TEXT_URL);
+    theme_->search_box.results_url_selected =
+        GetOmniboxColor(&theme_provider, OmniboxPart::RESULTS_TEXT_URL,
+                        OmniboxPartState::SELECTED);
     theme_->search_box.text = GetOmniboxColor(
         &theme_provider, OmniboxPart::LOCATION_BAR_TEXT_DEFAULT);
   }
diff --git a/chrome/browser/sharing/click_to_call/click_to_call_context_menu_observer_unittest.cc b/chrome/browser/sharing/click_to_call/click_to_call_context_menu_observer_unittest.cc
index 17cf4721..7db2ca0f 100644
--- a/chrome/browser/sharing/click_to_call/click_to_call_context_menu_observer_unittest.cc
+++ b/chrome/browser/sharing/click_to_call/click_to_call_context_menu_observer_unittest.cc
@@ -23,7 +23,6 @@
 #include "chrome/browser/sharing/sharing_constants.h"
 #include "chrome/browser/sharing/sharing_service_factory.h"
 #include "components/sync_device_info/device_info.h"
-#include "content/public/common/context_menu_params.h"
 #include "content/public/test/browser_task_environment.h"
 #include "content/public/test/web_contents_tester.h"
 #include "testing/gmock/include/gmock/gmock.h"
diff --git a/chrome/browser/sharing/shared_clipboard/remote_copy_message_handler.cc b/chrome/browser/sharing/shared_clipboard/remote_copy_message_handler.cc
index 51372bd..f1eb6446 100644
--- a/chrome/browser/sharing/shared_clipboard/remote_copy_message_handler.cc
+++ b/chrome/browser/sharing/shared_clipboard/remote_copy_message_handler.cc
@@ -25,7 +25,10 @@
 #include "chrome/browser/sharing/proto/sharing_message.pb.h"
 #include "chrome/browser/sharing/shared_clipboard/feature_flags.h"
 #include "chrome/browser/sharing/sharing_metrics.h"
+#include "chrome/browser/sharing/sharing_service.h"
+#include "chrome/browser/sharing/sharing_service_factory.h"
 #include "chrome/grit/generated_resources.h"
+#include "components/strings/grit/components_strings.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/storage_partition.h"
 #include "net/base/load_flags.h"
@@ -154,10 +157,7 @@
 
   // First cancel any pending async tasks that might otherwise overwrite the
   // results of the more recent message.
-  url_loader_.reset();
-  ImageDecoder::Cancel(this);
-  resize_callback_.Cancel();
-  write_detection_timer_.AbandonAndStop();
+  CancelAsyncTasks();
 
   device_name_ = message.sender_device_name();
 
@@ -291,8 +291,17 @@
 void RemoteCopyMessageHandler::UpdateProgressNotification(
     const base::string16& context) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  if (image_notification_id_.empty())
+  if (image_notification_id_.empty()) {
     image_notification_id_ = base::GenerateGUID();
+    // base::Unretained is safe as the SharingService owns |this| via the
+    // SharingHandlerRegistry and also the passed callback.
+    SharingServiceFactory::GetForBrowserContext(profile_)
+        ->SetNotificationActionHandler(
+            image_notification_id_,
+            base::BindRepeating(
+                &RemoteCopyMessageHandler::OnProgressNotificationAction,
+                base::Unretained(this)));
+  }
 
   message_center::RichNotificationData rich_notification_data;
   rich_notification_data.vector_small_image = &kSendTabToSelfIcon;
@@ -307,7 +316,13 @@
       /*display_source=*/base::string16(),
       /*origin_url=*/GURL(), message_center::NotifierId(),
       rich_notification_data,
-      base::MakeRefCounted<message_center::NotificationDelegate>());
+      /*delegate=*/nullptr);
+
+  std::vector<message_center::ButtonInfo> notification_actions;
+  message_center::ButtonInfo button_info =
+      message_center::ButtonInfo(l10n_util::GetStringUTF16(IDS_CANCEL));
+  notification_actions.push_back(button_info);
+  notification.set_buttons(notification_actions);
 
   if (image_content_length_ <= 0) {
     // TODO(knollr): Show transfer status if |image_content_progress_| is != 0.
@@ -323,7 +338,7 @@
   }
 
   NotificationDisplayServiceFactory::GetForProfile(profile_)->Display(
-      NotificationHandler::Type::TRANSIENT, notification, /*metadata=*/nullptr);
+      NotificationHandler::Type::SHARING, notification, /*metadata=*/nullptr);
 
   // Unretained(this) is safe here because |this| owns
   // |image_download_update_progress_timer_|.
@@ -341,12 +356,41 @@
   if (image_notification_id_.empty())
     return;
 
+  SharingServiceFactory::GetForBrowserContext(profile_)
+      ->SetNotificationActionHandler(image_notification_id_,
+                                     base::NullCallback());
   NotificationDisplayServiceFactory::GetForProfile(profile_)->Close(
       NotificationHandler::Type::SHARING, image_notification_id_);
 
   image_notification_id_.clear();
 }
 
+void RemoteCopyMessageHandler::OnProgressNotificationAction(
+    base::Optional<int> button,
+    bool closed) {
+  // Clicks on the progress notification body are ignored.
+  if (!closed && !button)
+    return;
+
+  // Stop updating the progress notification.
+  image_download_update_progress_timer_.AbandonAndStop();
+
+  // Let the download continue if the notification was dismissed.
+  if (closed) {
+    // Remove the handler as this notification is now closed.
+    SharingServiceFactory::GetForBrowserContext(profile_)
+        ->SetNotificationActionHandler(image_notification_id_,
+                                       base::NullCallback());
+    // The notification will be closed by the framework after this.
+    image_notification_id_.clear();
+    return;
+  }
+
+  // Cancel the download if the cancel button was pressed.
+  DCHECK_EQ(0, *button);
+  CancelAsyncTasks();
+}
+
 void RemoteCopyMessageHandler::OnURLLoadComplete(
     std::unique_ptr<std::string> content) {
   TRACE_EVENT0("sharing", "RemoteCopyMessageHandler::OnURLLoadComplete");
@@ -451,9 +495,14 @@
                      base::TimeTicks::Now(), /*is_image=*/true));
 
   std::string notification_id = image_notification_id_;
-  if (notification_id.empty())
+  if (notification_id.empty()) {
     notification_id = base::GenerateGUID();
-  image_notification_id_.clear();
+  } else {
+    SharingServiceFactory::GetForBrowserContext(profile_)
+        ->SetNotificationActionHandler(image_notification_id_,
+                                       base::NullCallback());
+    image_notification_id_.clear();
+  }
 
   ShowNotification(GetImageNotificationTitle(device_name_), resized_image,
                    notification_id);
@@ -524,3 +573,12 @@
   LogRemoteCopyHandleMessageResult(result);
   device_name_.clear();
 }
+
+void RemoteCopyMessageHandler::CancelAsyncTasks() {
+  url_loader_.reset();
+  ImageDecoder::Cancel(this);
+  resize_callback_.Cancel();
+  write_detection_timer_.AbandonAndStop();
+  image_download_update_progress_timer_.AbandonAndStop();
+  CancelProgressNotification();
+}
diff --git a/chrome/browser/sharing/shared_clipboard/remote_copy_message_handler.h b/chrome/browser/sharing/shared_clipboard/remote_copy_message_handler.h
index 9433446..1671d90 100644
--- a/chrome/browser/sharing/shared_clipboard/remote_copy_message_handler.h
+++ b/chrome/browser/sharing/shared_clipboard/remote_copy_message_handler.h
@@ -10,6 +10,7 @@
 
 #include "base/cancelable_callback.h"
 #include "base/macros.h"
+#include "base/optional.h"
 #include "base/strings/string16.h"
 #include "base/timer/elapsed_timer.h"
 #include "base/timer/timer.h"
@@ -51,6 +52,7 @@
   void OnImageDownloadProgress(uint64_t current);
   void UpdateProgressNotification(const base::string16& context);
   void CancelProgressNotification();
+  void OnProgressNotificationAction(base::Optional<int> button, bool closed);
   void OnURLLoadComplete(std::unique_ptr<std::string> content);
   void WriteImageAndShowNotification(const SkBitmap& original_image,
                                      const SkBitmap& resized_image);
@@ -61,6 +63,7 @@
                    base::TimeTicks start_ticks,
                    bool is_image);
   void Finish(RemoteCopyHandleMessageResult result);
+  void CancelAsyncTasks();
 
   Profile* profile_ = nullptr;
   std::unique_ptr<network::SimpleURLLoader> url_loader_;
diff --git a/chrome/browser/sharing/shared_clipboard/remote_copy_message_handler_unittest.cc b/chrome/browser/sharing/shared_clipboard/remote_copy_message_handler_unittest.cc
index f832801..4a0baae 100644
--- a/chrome/browser/sharing/shared_clipboard/remote_copy_message_handler_unittest.cc
+++ b/chrome/browser/sharing/shared_clipboard/remote_copy_message_handler_unittest.cc
@@ -13,11 +13,13 @@
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
 #include "chrome/browser/notifications/notification_display_service_tester.h"
+#include "chrome/browser/sharing/mock_sharing_service.h"
 #include "chrome/browser/sharing/proto/remote_copy_message.pb.h"
 #include "chrome/browser/sharing/proto/sharing_message.pb.h"
 #include "chrome/browser/sharing/shared_clipboard/feature_flags.h"
 #include "chrome/browser/sharing/shared_clipboard/remote_copy_handle_message_result.h"
 #include "chrome/browser/sharing/shared_clipboard/shared_clipboard_test_base.h"
+#include "chrome/browser/sharing/sharing_service_factory.h"
 #include "chrome/grit/generated_resources.h"
 #include "chrome/test/base/testing_profile.h"
 #include "components/sync/protocol/sync_enums.pb.h"
@@ -48,6 +50,11 @@
 
   void SetUp() override {
     SharedClipboardTestBase::SetUp();
+    SharingServiceFactory::GetInstance()->SetTestingFactory(
+        &profile_, base::BindRepeating([](content::BrowserContext* context)
+                                           -> std::unique_ptr<KeyedService> {
+          return std::make_unique<testing::NiceMock<MockSharingService>>();
+        }));
     message_handler_ = std::make_unique<RemoteCopyMessageHandler>(&profile_);
   }
 
@@ -243,6 +250,9 @@
   // download.
   task_environment_.RunUntilIdle();
 
+  // After finishing the transfer there should be no progress notification.
+  EXPECT_FALSE(HasProgressNotification());
+
   // Expect the image to be in the clipboard now.
   SkBitmap image = GetClipboardImage();
   EXPECT_TRUE(gfx::BitmapsAreEqual(*image_, image));
@@ -251,3 +261,54 @@
   auto notification = GetImageNotification();
   EXPECT_FALSE(notification.image().IsEmpty());
 }
+
+TEST_F(RemoteCopyMessageHandlerTest, CancelProgressNotification) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitWithFeaturesAndParameters(
+      {{kRemoteCopyReceiver, {{kRemoteCopyAllowedOrigins.name, kTestImageUrl}}},
+       {kRemoteCopyProgressNotification, {}}},
+      {});
+
+  message_handler_->OnMessage(CreateMessageWithImage(kTestImageUrl),
+                              base::DoNothing());
+  auto notification = GetProgressNotification();
+
+  // Simulate a click on the cancel button at index 0.
+  notification_tester_->SimulateClick(NotificationHandler::Type::SHARING,
+                                      notification.id(), /*action_index=*/0,
+                                      /*reply=*/base::nullopt);
+
+  // The progress notification should now be closed.
+  EXPECT_FALSE(HasProgressNotification());
+
+  // Run remaining tasks to ensure no notification is shown at the end.
+  task_environment_.RunUntilIdle();
+
+  EXPECT_FALSE(HasProgressNotification());
+  EXPECT_FALSE(HasImageNotification());
+}
+
+TEST_F(RemoteCopyMessageHandlerTest, DismissProgressNotification) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitWithFeaturesAndParameters(
+      {{kRemoteCopyReceiver, {{kRemoteCopyAllowedOrigins.name, kTestImageUrl}}},
+       {kRemoteCopyProgressNotification, {}}},
+      {});
+
+  message_handler_->OnMessage(CreateMessageWithImage(kTestImageUrl),
+                              base::DoNothing());
+  auto notification = GetProgressNotification();
+
+  // Simulate closing the notification by the user.
+  notification_tester_->RemoveNotification(NotificationHandler::Type::SHARING,
+                                           notification.id(), /*by_user=*/true,
+                                           /*silent=*/false);
+
+  // The progress notification should now be closed.
+  EXPECT_FALSE(HasProgressNotification());
+
+  // Let tasks run until the image is decoded and written to the clipboard.
+  task_environment_.RunUntilIdle();
+
+  EXPECT_TRUE(HasImageNotification());
+}
diff --git a/chrome/browser/sharing/shared_clipboard/shared_clipboard_context_menu_observer_unittest.cc b/chrome/browser/sharing/shared_clipboard/shared_clipboard_context_menu_observer_unittest.cc
index 06ea65d..afe1731 100644
--- a/chrome/browser/sharing/shared_clipboard/shared_clipboard_context_menu_observer_unittest.cc
+++ b/chrome/browser/sharing/shared_clipboard/shared_clipboard_context_menu_observer_unittest.cc
@@ -22,7 +22,7 @@
 #include "chrome/browser/sharing/sharing_service_factory.h"
 #include "chrome/browser/sharing/vapid_key_manager.h"
 #include "components/sync_device_info/device_info.h"
-#include "content/public/common/context_menu_params.h"
+#include "content/public/browser/context_menu_params.h"
 #include "content/public/test/browser_task_environment.h"
 #include "content/public/test/web_contents_tester.h"
 #include "testing/gmock/include/gmock/gmock.h"
diff --git a/chrome/browser/sharing/shared_clipboard/shared_clipboard_test_base.cc b/chrome/browser/sharing/shared_clipboard/shared_clipboard_test_base.cc
index 8bdf5fd..bd8ffca 100644
--- a/chrome/browser/sharing/shared_clipboard/shared_clipboard_test_base.cc
+++ b/chrome/browser/sharing/shared_clipboard/shared_clipboard_test_base.cc
@@ -51,9 +51,18 @@
       ui::ClipboardBuffer::kCopyPaste);
 }
 
+bool SharedClipboardTestBase::HasImageNotification() {
+  auto notifications = notification_tester_->GetDisplayedNotificationsForType(
+      NotificationHandler::Type::SHARING);
+  if (notifications.size() != 1u)
+    return false;
+
+  return notifications[0].type() == message_center::NOTIFICATION_TYPE_IMAGE;
+}
+
 bool SharedClipboardTestBase::HasProgressNotification() {
   auto notifications = notification_tester_->GetDisplayedNotificationsForType(
-      NotificationHandler::Type::TRANSIENT);
+      NotificationHandler::Type::SHARING);
   if (notifications.size() != 1u)
     return false;
 
@@ -74,7 +83,7 @@
 message_center::Notification
 SharedClipboardTestBase::GetProgressNotification() {
   auto notifications = notification_tester_->GetDisplayedNotificationsForType(
-      NotificationHandler::Type::TRANSIENT);
+      NotificationHandler::Type::SHARING);
   EXPECT_EQ(notifications.size(), 1u);
 
   const message_center::Notification& notification = notifications[0];
diff --git a/chrome/browser/sharing/shared_clipboard/shared_clipboard_test_base.h b/chrome/browser/sharing/shared_clipboard/shared_clipboard_test_base.h
index f9c8ba5..6922d399 100644
--- a/chrome/browser/sharing/shared_clipboard/shared_clipboard_test_base.h
+++ b/chrome/browser/sharing/shared_clipboard/shared_clipboard_test_base.h
@@ -41,6 +41,7 @@
   std::string GetClipboardText();
   SkBitmap GetClipboardImage();
 
+  bool HasImageNotification();
   bool HasProgressNotification();
 
   message_center::Notification GetNotification();
diff --git a/chrome/browser/sharing/sharing_notification_handler.cc b/chrome/browser/sharing/sharing_notification_handler.cc
index 35121d0..a9555ec3 100644
--- a/chrome/browser/sharing/sharing_notification_handler.cc
+++ b/chrome/browser/sharing/sharing_notification_handler.cc
@@ -8,6 +8,9 @@
 
 #include "chrome/browser/notifications/notification_display_service.h"
 #include "chrome/browser/notifications/notification_display_service_factory.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/sharing/sharing_service.h"
+#include "chrome/browser/sharing/sharing_service_factory.h"
 
 SharingNotificationHandler::SharingNotificationHandler() = default;
 SharingNotificationHandler::~SharingNotificationHandler() = default;
@@ -19,8 +22,27 @@
     const base::Optional<int>& action_index,
     const base::Optional<base::string16>& reply,
     base::OnceClosure completed_closure) {
-  NotificationDisplayServiceFactory::GetForProfile(profile)->Close(
-      NotificationHandler::Type::SHARING, notification_id);
+  auto handler = SharingServiceFactory::GetForBrowserContext(profile)
+                     ->GetNotificationActionHandler(notification_id);
+  if (handler) {
+    handler.Run(action_index, /*closed=*/false);
+  } else {
+    // Close the notification by default.
+    NotificationDisplayServiceFactory::GetForProfile(profile)->Close(
+        NotificationHandler::Type::SHARING, notification_id);
+  }
+  std::move(completed_closure).Run();
+}
+
+void SharingNotificationHandler::OnClose(Profile* profile,
+                                         const GURL& origin,
+                                         const std::string& notification_id,
+                                         bool by_user,
+                                         base::OnceClosure completed_closure) {
+  auto handler = SharingServiceFactory::GetForBrowserContext(profile)
+                     ->GetNotificationActionHandler(notification_id);
+  if (handler)
+    handler.Run(/*button=*/base::nullopt, /*closed=*/true);
   std::move(completed_closure).Run();
 }
 
diff --git a/chrome/browser/sharing/sharing_notification_handler.h b/chrome/browser/sharing/sharing_notification_handler.h
index 4e82cc1..887c4dc 100644
--- a/chrome/browser/sharing/sharing_notification_handler.h
+++ b/chrome/browser/sharing/sharing_notification_handler.h
@@ -5,15 +5,23 @@
 #ifndef CHROME_BROWSER_SHARING_SHARING_NOTIFICATION_HANDLER_H_
 #define CHROME_BROWSER_SHARING_SHARING_NOTIFICATION_HANDLER_H_
 
-#include <map>
 #include <string>
 
+#include "base/callback_forward.h"
+#include "base/optional.h"
+#include "base/strings/string16.h"
 #include "chrome/browser/notifications/notification_handler.h"
+#include "url/gurl.h"
+
+class Profile;
 
 // Handles SHARING nofication actions.
 class SharingNotificationHandler : public NotificationHandler {
  public:
   SharingNotificationHandler();
+  SharingNotificationHandler(const SharingNotificationHandler&) = delete;
+  SharingNotificationHandler& operator=(const SharingNotificationHandler&) =
+      delete;
   ~SharingNotificationHandler() override;
 
   // NotificationHandler implementation:
@@ -23,10 +31,12 @@
                const base::Optional<int>& action_index,
                const base::Optional<base::string16>& reply,
                base::OnceClosure completed_closure) override;
+  void OnClose(Profile* profile,
+               const GURL& origin,
+               const std::string& notification_id,
+               bool by_user,
+               base::OnceClosure completed_closure) override;
   void OpenSettings(Profile* profile, const GURL& origin) override;
-
- protected:
-  DISALLOW_COPY_AND_ASSIGN(SharingNotificationHandler);
 };
 
-#endif  // CHROME_BROWSER_SHARING_SHARING_NOTIFICATION_HANDLER_H_
\ No newline at end of file
+#endif  // CHROME_BROWSER_SHARING_SHARING_NOTIFICATION_HANDLER_H_
diff --git a/chrome/browser/sharing/sharing_service.cc b/chrome/browser/sharing/sharing_service.cc
index 1e6fcf73..a0b771c 100644
--- a/chrome/browser/sharing/sharing_service.cc
+++ b/chrome/browser/sharing/sharing_service.cc
@@ -130,6 +130,24 @@
   handler_registry_->UnregisterSharingHandler(payload_case);
 }
 
+void SharingService::SetNotificationActionHandler(
+    const std::string& notification_id,
+    NotificationActionCallback callback) {
+  if (callback)
+    notification_action_handlers_[notification_id] = callback;
+  else
+    notification_action_handlers_.erase(notification_id);
+}
+
+SharingService::NotificationActionCallback
+SharingService::GetNotificationActionHandler(
+    const std::string& notification_id) const {
+  auto iter = notification_action_handlers_.find(notification_id);
+  return iter == notification_action_handlers_.end()
+             ? NotificationActionCallback()
+             : iter->second;
+}
+
 SharingDeviceSource* SharingService::GetDeviceSource() const {
   return device_source_.get();
 }
diff --git a/chrome/browser/sharing/sharing_service.h b/chrome/browser/sharing/sharing_service.h
index 8cf4b5e9..47a2fd5e 100644
--- a/chrome/browser/sharing/sharing_service.h
+++ b/chrome/browser/sharing/sharing_service.h
@@ -44,6 +44,8 @@
 class SharingService : public KeyedService, public syncer::SyncServiceObserver {
  public:
   using SharingDeviceList = std::vector<std::unique_ptr<syncer::DeviceInfo>>;
+  using NotificationActionCallback =
+      base::RepeatingCallback<void(base::Optional<int> button, bool closed)>;
 
   enum class State {
     // Device is unregistered with FCM and Sharing is unavailable.
@@ -100,6 +102,18 @@
   void UnregisterSharingHandler(
       chrome_browser_sharing::SharingMessage::PayloadCase payload_case);
 
+  // Sets a notification action handler for |notification_id|. Replaces any
+  // previously set handlers for |notification_id|. |callback| may be a null
+  // callback which clears the handler for |notification_id|.
+  void SetNotificationActionHandler(const std::string& notification_id,
+                                    NotificationActionCallback callback);
+
+  // Returns the notification action handler for |notification_id| set by
+  // SetNotificationActionHandler(). The returned callback may be null if no
+  // handler has been set before for |notification_id|.
+  NotificationActionCallback GetNotificationActionHandler(
+      const std::string& notification_id) const;
+
   // Used to register devices with required capabilities in tests.
   void RegisterDeviceInTesting(
       std::set<sync_pb::SharingSpecificFields_EnabledFeatures> enabled_features,
@@ -149,6 +163,10 @@
   SharingServiceProxyAndroid sharing_service_proxy_android_{this};
 #endif  // defined(OS_ANDROID)
 
+  // Map of notification id to notification handler callback.
+  std::map<std::string, NotificationActionCallback>
+      notification_action_handlers_;
+
   base::WeakPtrFactory<SharingService> weak_ptr_factory_{this};
 };
 
diff --git a/chrome/browser/supervised_user/supervised_user_features.cc b/chrome/browser/supervised_user/supervised_user_features.cc
index ff8d82f..27d244e 100644
--- a/chrome/browser/supervised_user/supervised_user_features.cc
+++ b/chrome/browser/supervised_user/supervised_user_features.cc
@@ -11,5 +11,5 @@
 
 const base::Feature kSupervisedUserInitiatedExtensionInstall{
     "SupervisedUserInitiatedExtensionInstall",
-    base::FEATURE_DISABLED_BY_DEFAULT};
+    base::FEATURE_ENABLED_BY_DEFAULT};
 }
diff --git a/chrome/browser/thumbnail/generator/android/thumbnail_media_parser.cc b/chrome/browser/thumbnail/generator/android/thumbnail_media_parser.cc
index 6ce225d..4dec88f 100644
--- a/chrome/browser/thumbnail/generator/android/thumbnail_media_parser.cc
+++ b/chrome/browser/thumbnail/generator/android/thumbnail_media_parser.cc
@@ -247,7 +247,7 @@
 
 void ThumbnailMediaParser::RenderVideoFrame(
     scoped_refptr<media::VideoFrame> video_frame) {
-  auto context_provider =
+  auto* context_provider =
       gpu_factories_ ? gpu_factories_->GetMediaContextProvider() : nullptr;
 
   media::PaintCanvasVideoRenderer renderer;
@@ -257,7 +257,7 @@
 
   // Draw the video frame to |bitmap|.
   cc::SkiaPaintCanvas canvas(bitmap);
-  renderer.Copy(video_frame, &canvas, context_provider.get());
+  renderer.Copy(video_frame, &canvas, context_provider);
 
   RecordVideoThumbnailEvent(VideoThumbnailEvent::kVideoThumbnailComplete);
   NotifyComplete(std::move(bitmap));
diff --git a/chrome/browser/touch_to_fill/android/BUILD.gn b/chrome/browser/touch_to_fill/android/BUILD.gn
index 8367fd2..d8ad039a 100644
--- a/chrome/browser/touch_to_fill/android/BUILD.gn
+++ b/chrome/browser/touch_to_fill/android/BUILD.gn
@@ -31,7 +31,6 @@
     # TODO(crbug.com/1004415): Remove dependency on chrome_java and depend on
     # bottomsheet directly. Add public_java to chrome_java target instead.
     "//chrome/android:chrome_java",
-    "//chrome/android:chrome_public_java",
     "//ui/android:ui_java",
   ]
 
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index a5b23fe..811c38cc 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -2016,6 +2016,8 @@
       "webui/settings/chromeos/search/search_concept.h",
       "webui/settings/chromeos/search/settings_user_action_tracker.cc",
       "webui/settings/chromeos/search/settings_user_action_tracker.h",
+      "webui/settings/chromeos/server_printer_url_util.cc",
+      "webui/settings/chromeos/server_printer_url_util.h",
       "webui/settings/chromeos/wallpaper_handler.cc",
       "webui/settings/chromeos/wallpaper_handler.h",
       "webui/settings/tts_handler.cc",
@@ -2033,11 +2035,11 @@
 
     deps += [
       "//ash",
-      "//ash/components/shortcut_viewer",
       "//ash/keyboard/ui",
       "//ash/public/cpp",
       "//ash/public/cpp/resources:ash_public_unscaled_resources",
       "//ash/public/cpp/vector_icons",
+      "//ash/shortcut_viewer",
       "//chrome/browser/chromeos",
       "//chrome/browser/chromeos:backdrop_wallpaper_proto",
       "//chrome/browser/resources/chromeos:camera_resources",
diff --git a/chrome/browser/ui/android/context_menu_helper.cc b/chrome/browser/ui/android/context_menu_helper.cc
index c9540fd..5fad1b44 100644
--- a/chrome/browser/ui/android/context_menu_helper.cc
+++ b/chrome/browser/ui/android/context_menu_helper.cc
@@ -22,9 +22,9 @@
 #include "chrome/common/chrome_render_frame.mojom.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_headers.h"
 #include "components/embedder_support/android/contextmenu/context_menu_builder.h"
+#include "content/public/browser/context_menu_params.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/render_process_host.h"
-#include "content/public/common/context_menu_params.h"
 #include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
 #include "third_party/blink/public/common/context_menu_data/media_type.h"
 #include "ui/android/view_android.h"
diff --git a/chrome/browser/ui/android/context_menu_helper.h b/chrome/browser/ui/android/context_menu_helper.h
index 74c1b13..71d645b 100644
--- a/chrome/browser/ui/android/context_menu_helper.h
+++ b/chrome/browser/ui/android/context_menu_helper.h
@@ -14,8 +14,8 @@
 #include "base/macros.h"
 #include "chrome/common/chrome_render_frame.mojom.h"
 #include "components/optimization_guide/proto/performance_hints_metadata.pb.h"
+#include "content/public/browser/context_menu_params.h"
 #include "content/public/browser/web_contents_user_data.h"
-#include "content/public/common/context_menu_params.h"
 #include "mojo/public/cpp/bindings/associated_remote.h"
 
 namespace content {
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings.grd b/chrome/browser/ui/android/strings/android_chrome_strings.grd
index 836b428..8037e8f 100644
--- a/chrome/browser/ui/android/strings/android_chrome_strings.grd
+++ b/chrome/browser/ui/android/strings/android_chrome_strings.grd
@@ -936,6 +936,9 @@
       <message name="IDS_WEBSITE_SETTINGS_ADD_SITE_DESCRIPTION_SOUND_BLOCK" desc="The description for the mute sound for website dialog.">
         Mute sound for a specific site.
       </message>
+      <message name="IDS_WEBSITE_SETTINGS_COOKIE_INFO" desc="Text describing cookie and third-party cookie settings.">
+        Cookies are files created by websites you visit. Sites use them to remember your preferences. Third-party cookies are created by other sites. These sites own some of the content, like ads or images, that you see on the webpage you visit.
+      </message>
       <message name="IDS_WEBSITE_SETTINGS_ADD_SITE_SITE_URL" desc="The label for the input field where the user can type a website URL.">
         Site URL
       </message>
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_WEBSITE_SETTINGS_COOKIE_INFO.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_WEBSITE_SETTINGS_COOKIE_INFO.png.sha1
new file mode 100644
index 0000000..31487d7
--- /dev/null
+++ b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_WEBSITE_SETTINGS_COOKIE_INFO.png.sha1
@@ -0,0 +1 @@
+fdf156f0bd4ccb46204cafbb8f4a753174acbd79
\ No newline at end of file
diff --git a/chrome/browser/ui/android/tab_contents/chrome_web_contents_view_delegate_android.cc b/chrome/browser/ui/android/tab_contents/chrome_web_contents_view_delegate_android.cc
index 29a700f8..e874a014 100644
--- a/chrome/browser/ui/android/tab_contents/chrome_web_contents_view_delegate_android.cc
+++ b/chrome/browser/ui/android/tab_contents/chrome_web_contents_view_delegate_android.cc
@@ -6,9 +6,9 @@
 
 #include "base/logging.h"
 #include "chrome/browser/ui/android/context_menu_helper.h"
+#include "content/public/browser/context_menu_params.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_contents_view_delegate.h"
-#include "content/public/common/context_menu_params.h"
 
 ChromeWebContentsViewDelegateAndroid::ChromeWebContentsViewDelegateAndroid(
     content::WebContents* web_contents)
diff --git a/chrome/browser/ui/app_list/app_service/app_service_context_menu.cc b/chrome/browser/ui/app_list/app_service/app_service_context_menu.cc
index e122bd7..82d40a7 100644
--- a/chrome/browser/ui/app_list/app_service/app_service_context_menu.cc
+++ b/chrome/browser/ui/app_list/app_service/app_service_context_menu.cc
@@ -31,7 +31,7 @@
 #include "chrome/browser/web_applications/web_app_provider.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/grit/generated_resources.h"
-#include "content/public/common/context_menu_params.h"
+#include "content/public/browser/context_menu_params.h"
 #include "ui/gfx/vector_icon_types.h"
 
 namespace {
diff --git a/chrome/browser/ui/app_list/extension_app_context_menu.cc b/chrome/browser/ui/app_list/extension_app_context_menu.cc
index 41db4f3..4fb1f7ea 100644
--- a/chrome/browser/ui/app_list/extension_app_context_menu.cc
+++ b/chrome/browser/ui/app_list/extension_app_context_menu.cc
@@ -17,7 +17,7 @@
 #include "chrome/browser/web_applications/web_app_provider.h"
 #include "chrome/grit/chromium_strings.h"
 #include "chrome/grit/generated_resources.h"
-#include "content/public/common/context_menu_params.h"
+#include "content/public/browser/context_menu_params.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/gfx/paint_vector_icon.h"
 #include "ui/views/controls/menu/menu_config.h"
diff --git a/chrome/browser/ui/app_list/web_app_context_menu.cc b/chrome/browser/ui/app_list/web_app_context_menu.cc
index f2e45b9..4232b578 100644
--- a/chrome/browser/ui/app_list/web_app_context_menu.cc
+++ b/chrome/browser/ui/app_list/web_app_context_menu.cc
@@ -16,7 +16,6 @@
 #include "chrome/browser/web_applications/web_app_provider.h"
 #include "chrome/grit/chromium_strings.h"
 #include "chrome/grit/generated_resources.h"
-#include "content/public/common/context_menu_params.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/gfx/paint_vector_icon.h"
 #include "ui/views/controls/menu/menu_config.h"
diff --git a/chrome/browser/ui/ash/DEPS b/chrome/browser/ui/ash/DEPS
index 0c7954b..87c7d81 100644
--- a/chrome/browser/ui/ash/DEPS
+++ b/chrome/browser/ui/ash/DEPS
@@ -10,8 +10,8 @@
 specific_include_rules = {
   ".*test.*": [
    "!ash",
-   "+ash/components/shortcut_viewer",
-   "+ash/components/strings/grit/ash_components_strings.h",
+   "+ash/shortcut_viewer",
+   "+ash/shortcut_viewer/strings/grit/ash_components_strings.h",
    "+ash/keyboard/ui",
    "+ash/public",
    "+ash/assistant/ui/assistant_ui_constants.h",
diff --git a/chrome/browser/ui/ash/keyboard/chrome_keyboard_ui.cc b/chrome/browser/ui/ash/keyboard/chrome_keyboard_ui.cc
index 02e6718..341e46a 100644
--- a/chrome/browser/ui/ash/keyboard/chrome_keyboard_ui.cc
+++ b/chrome/browser/ui/ash/keyboard/chrome_keyboard_ui.cc
@@ -69,6 +69,12 @@
              : nullptr;
 }
 
+ui::GestureConsumer* ChromeKeyboardUI::GetGestureConsumer() const {
+  return keyboard_contents_
+             ? keyboard_contents_->web_contents()->GetContentNativeView()
+             : nullptr;
+}
+
 ui::InputMethod* ChromeKeyboardUI::GetInputMethod() {
   ui::IMEBridge* bridge = ui::IMEBridge::Get();
   if (!bridge || !bridge->GetInputContextHandler()) {
diff --git a/chrome/browser/ui/ash/keyboard/chrome_keyboard_ui.h b/chrome/browser/ui/ash/keyboard/chrome_keyboard_ui.h
index 97777e19..42f3d148 100644
--- a/chrome/browser/ui/ash/keyboard/chrome_keyboard_ui.h
+++ b/chrome/browser/ui/ash/keyboard/chrome_keyboard_ui.h
@@ -37,6 +37,7 @@
   // keyboard::KeyboardUI:
   aura::Window* LoadKeyboardWindow(LoadCallback callback) override;
   aura::Window* GetKeyboardWindow() const override;
+  ui::GestureConsumer* GetGestureConsumer() const override;
   ui::InputMethod* GetInputMethod() override;
   void ReloadKeyboardIfNeeded() override;
 
diff --git a/chrome/browser/ui/ash/keyboard_shortcut_viewer_metadata_unittest.cc b/chrome/browser/ui/ash/keyboard_shortcut_viewer_metadata_unittest.cc
index cf923709..9257bee 100644
--- a/chrome/browser/ui/ash/keyboard_shortcut_viewer_metadata_unittest.cc
+++ b/chrome/browser/ui/ash/keyboard_shortcut_viewer_metadata_unittest.cc
@@ -2,16 +2,16 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ash/components/shortcut_viewer/keyboard_shortcut_viewer_metadata.h"
+#include "ash/shortcut_viewer/keyboard_shortcut_viewer_metadata.h"
 
 #include <set>
 #include <string>
 #include <tuple>
 #include <vector>
 
-#include "ash/components/shortcut_viewer/keyboard_shortcut_item.h"
-#include "ash/components/strings/grit/ash_components_strings.h"
 #include "ash/public/cpp/accelerators.h"
+#include "ash/shortcut_viewer/keyboard_shortcut_item.h"
+#include "ash/shortcut_viewer/strings/grit/ash_components_strings.h"
 #include "base/hash/md5.h"
 #include "base/macros.h"
 #include "base/strings/string_util.h"
diff --git a/chrome/browser/ui/ash/launcher/app_service/app_service_shelf_context_menu.cc b/chrome/browser/ui/ash/launcher/app_service/app_service_shelf_context_menu.cc
index abd3d54c..3394f34 100644
--- a/chrome/browser/ui/ash/launcher/app_service/app_service_shelf_context_menu.cc
+++ b/chrome/browser/ui/ash/launcher/app_service/app_service_shelf_context_menu.cc
@@ -38,7 +38,7 @@
 #include "chrome/browser/web_applications/web_app_provider.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/grit/generated_resources.h"
-#include "content/public/common/context_menu_params.h"
+#include "content/public/browser/context_menu_params.h"
 #include "ui/gfx/vector_icon_types.h"
 
 namespace {
diff --git a/chrome/browser/ui/ash/launcher/extension_shelf_context_menu.cc b/chrome/browser/ui/ash/launcher/extension_shelf_context_menu.cc
index ccca7d8..8a75f0f 100644
--- a/chrome/browser/ui/ash/launcher/extension_shelf_context_menu.cc
+++ b/chrome/browser/ui/ash/launcher/extension_shelf_context_menu.cc
@@ -23,7 +23,7 @@
 #include "chrome/common/extensions/extension_constants.h"
 #include "chrome/grit/chromium_strings.h"
 #include "chrome/grit/generated_resources.h"
-#include "content/public/common/context_menu_params.h"
+#include "content/public/browser/context_menu_params.h"
 #include "extensions/browser/extension_prefs.h"
 #include "ui/display/screen.h"
 #include "ui/gfx/paint_vector_icon.h"
diff --git a/chrome/browser/ui/browser_window_state.cc b/chrome/browser/ui/browser_window_state.cc
index 8dc533c8..111264f 100644
--- a/chrome/browser/ui/browser_window_state.cc
+++ b/chrome/browser/ui/browser_window_state.cc
@@ -156,8 +156,8 @@
   DCHECK(bounds);
   DCHECK(show_state);
   *bounds = browser->override_bounds();
-  WindowSizer::GetBrowserWindowBoundsAndShowState(browser->app_name(), *bounds,
-                                                  browser, bounds, show_state);
+  WindowSizer::GetBrowserWindowBoundsAndShowState(*bounds, browser, bounds,
+                                                  show_state);
 
   const base::CommandLine& parsed_command_line =
       *base::CommandLine::ForCurrentProcess();
diff --git a/chrome/browser/ui/extensions/hosted_app_browsertest.cc b/chrome/browser/ui/extensions/hosted_app_browsertest.cc
index 866d655..66a87afc 100644
--- a/chrome/browser/ui/extensions/hosted_app_browsertest.cc
+++ b/chrome/browser/ui/extensions/hosted_app_browsertest.cc
@@ -49,12 +49,12 @@
 #include "chrome/test/base/ui_test_utils.h"
 #include "components/sessions/core/tab_restore_service.h"
 #include "content/public/browser/child_process_security_policy.h"
+#include "content/public/browser/context_menu_params.h"
 #include "content/public/browser/notification_service.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/content_switches.h"
-#include "content/public/common/context_menu_params.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/content_mock_cert_verifier.h"
diff --git a/chrome/browser/ui/messages/android/BUILD.gn b/chrome/browser/ui/messages/android/BUILD.gn
index 9012d35..72553d8 100644
--- a/chrome/browser/ui/messages/android/BUILD.gn
+++ b/chrome/browser/ui/messages/android/BUILD.gn
@@ -16,7 +16,10 @@
 
 android_library("java") {
   sources = [
+    "java/src/org/chromium/chrome/browser/ui/messages/infobar/InfoBarCompactLayout.java",
     "java/src/org/chromium/chrome/browser/ui/messages/infobar/InfoBarControlLayout.java",
+    "java/src/org/chromium/chrome/browser/ui/messages/infobar/InfoBarInteractionHandler.java",
+    "java/src/org/chromium/chrome/browser/ui/messages/infobar/InfoBarLayout.java",
     "java/src/org/chromium/chrome/browser/ui/messages/infobar/InfoBarMessageView.java",
     "java/src/org/chromium/chrome/browser/ui/messages/infobar/InfoBarUiItem.java",
     "java/src/org/chromium/chrome/browser/ui/messages/snackbar/Snackbar.java",
@@ -34,6 +37,7 @@
     "//third_party/android_deps:androidx_annotation_annotation_java",
     "//third_party/android_deps:com_android_support_appcompat_v7_java",
     "//ui/android:ui_full_java",
+    "//ui/android:ui_utils_java",
   ]
 }
 
diff --git a/chrome/browser/ui/messages/android/java/res/values/dimens.xml b/chrome/browser/ui/messages/android/java/res/values/dimens.xml
index 0a1afb1..5cb76b7 100644
--- a/chrome/browser/ui/messages/android/java/res/values/dimens.xml
+++ b/chrome/browser/ui/messages/android/java/res/values/dimens.xml
@@ -10,6 +10,28 @@
     <dimen name="infobar_control_margin_between_columns">32dp</dimen>
     <!-- Text size of the infobar message and other controls. -->
     <dimen name="infobar_text_size">16sp</dimen>
+    <!-- Text size of the infobar message when a big icon is shown. -->
+    <dimen name="infobar_big_icon_message_size">20sp</dimen>
+    <!-- Margin between stacked buttons in an infobar. -->
+    <dimen name="infobar_margin_between_stacked_buttons">24dp</dimen>
+    <!-- Padding surrounding the infobar. -->
+    <dimen name="infobar_padding">16dp</dimen>
+    <!-- Minimum width of an infobar. -->
+    <dimen name="infobar_min_width">220dp</dimen>
+
+    <!-- Dimensions for compact infobars are a little shorter. -->
+    <dimen name="infobar_compact_size">56dp</dimen>
+    <dimen name="infobar_compact_message_vertical_padding">8dp</dimen>
+
+    <!-- Dimensions applied to InfoBars with differently sized icons. -->
+    <dimen name="infobar_small_icon_size">24dp</dimen>
+    <dimen name="infobar_small_icon_margin">8dp</dimen>
+    <dimen name="infobar_big_icon_size">48dp</dimen>
+    <dimen name="infobar_big_icon_margin">16dp</dimen>
+
+    <!-- Vertical margin applied between groups of controls. -->
+    <dimen name="infobar_margin_above_control_groups">24dp</dimen>
+    <dimen name="infobar_margin_above_button_row">32dp</dimen>
 
     <!-- Snackbars -->
     <dimen name="snackbar_min_height">48dp</dimen>
diff --git a/chrome/browser/ui/messages/android/java/res/values/ids.xml b/chrome/browser/ui/messages/android/java/res/values/ids.xml
new file mode 100644
index 0000000..8d7677f
--- /dev/null
+++ b/chrome/browser/ui/messages/android/java/res/values/ids.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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. -->
+<resources>
+    <!-- InfoBar constants -->
+    <item type="id" name="infobar_icon" />
+    <item type="id" name="infobar_close_button" />
+</resources>
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/infobar/InfoBarCompactLayout.java b/chrome/browser/ui/messages/android/java/src/org/chromium/chrome/browser/ui/messages/infobar/InfoBarCompactLayout.java
similarity index 91%
rename from chrome/android/java/src/org/chromium/chrome/browser/infobar/InfoBarCompactLayout.java
rename to chrome/browser/ui/messages/android/java/src/org/chromium/chrome/browser/ui/messages/infobar/InfoBarCompactLayout.java
index c9b6764..9fc1fc9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/infobar/InfoBarCompactLayout.java
+++ b/chrome/browser/ui/messages/android/java/src/org/chromium/chrome/browser/ui/messages/infobar/InfoBarCompactLayout.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.chrome.browser.infobar;
+package org.chromium.chrome.browser.ui.messages.infobar;
 
 import android.content.Context;
 import android.content.res.Resources;
@@ -23,8 +23,7 @@
 
 import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.base.Callback;
-import org.chromium.chrome.R;
-import org.chromium.chrome.browser.ui.messages.infobar.InfoBarMessageView;
+import org.chromium.chrome.ui.messages.R;
 import org.chromium.ui.text.NoUnderlineClickableSpan;
 
 /**
@@ -46,8 +45,10 @@
      * @param iconTintId The {@link ColorRes} used as tint for {@code iconResourceId}.
      * @param iconBitmap Bitmap for the icon to use, if {@code iconResourceId} is not set.
      */
-    InfoBarCompactLayout(Context context, InfoBarInteractionHandler infoBar, int iconResourceId,
-            @ColorRes int iconTintId, Bitmap iconBitmap) {
+    // TODO(crbug/1056346): ctor is made public to allow access from InfoBar. Once
+    // InfoBar is modularized, restore access to package private.
+    public InfoBarCompactLayout(Context context, InfoBarInteractionHandler infoBar,
+            int iconResourceId, @ColorRes int iconTintId, Bitmap iconBitmap) {
         super(context);
         mInfoBar = infoBar;
         mCompactInfoBarSize =
@@ -75,7 +76,9 @@
      * @param view   View to insert.
      * @param weight Weight to assign to it.
      */
-    protected void addContent(View view, float weight) {
+    // TODO(crbug/1056346): addContent is made public to allow access from InfoBar. Once
+    // InfoBar is modularized, restore access to protected.
+    public void addContent(View view, float weight) {
         LinearLayout.LayoutParams params;
         if (weight <= 0.0f) {
             params = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, mCompactInfoBarSize);
@@ -181,7 +184,7 @@
             assert mMessage != null;
 
             final int messagePadding = mLayout.getResources().getDimensionPixelOffset(
-                    R.dimen.reader_mode_infobar_text_padding);
+                    R.dimen.infobar_compact_message_vertical_padding);
 
             SpannableStringBuilder builder = new SpannableStringBuilder();
             builder.append(mMessage);
diff --git a/chrome/browser/ui/messages/android/java/src/org/chromium/chrome/browser/ui/messages/infobar/InfoBarControlLayout.java b/chrome/browser/ui/messages/android/java/src/org/chromium/chrome/browser/ui/messages/infobar/InfoBarControlLayout.java
index 94376e9d..d17f3e7 100644
--- a/chrome/browser/ui/messages/android/java/src/org/chromium/chrome/browser/ui/messages/infobar/InfoBarControlLayout.java
+++ b/chrome/browser/ui/messages/android/java/src/org/chromium/chrome/browser/ui/messages/infobar/InfoBarControlLayout.java
@@ -480,9 +480,7 @@
      * Adds a full-width control showing the main InfoBar message.  For other text, you should call
      * {@link InfoBarControlLayout#addDescription(CharSequence)} instead.
      */
-    // TODO(crbug/1056346): addMainMessage is made public to allow access from InfoBarLayout. Once
-    // InfoBarLayout is modularized, restore access to package private.
-    public TextView addMainMessage(CharSequence mainMessage) {
+    TextView addMainMessage(CharSequence mainMessage) {
         ControlLayoutParams params = new ControlLayoutParams();
         params.mMustBeFullWidth = true;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/infobar/InfoBarInteractionHandler.java b/chrome/browser/ui/messages/android/java/src/org/chromium/chrome/browser/ui/messages/infobar/InfoBarInteractionHandler.java
similarity index 93%
rename from chrome/android/java/src/org/chromium/chrome/browser/infobar/InfoBarInteractionHandler.java
rename to chrome/browser/ui/messages/android/java/src/org/chromium/chrome/browser/ui/messages/infobar/InfoBarInteractionHandler.java
index c9c2871..a24fc85 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/infobar/InfoBarInteractionHandler.java
+++ b/chrome/browser/ui/messages/android/java/src/org/chromium/chrome/browser/ui/messages/infobar/InfoBarInteractionHandler.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.chrome.browser.infobar;
+package org.chromium.chrome.browser.ui.messages.infobar;
 
 /**
  * Functions needed to display an InfoBar UI.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/infobar/InfoBarLayout.java b/chrome/browser/ui/messages/android/java/src/org/chromium/chrome/browser/ui/messages/infobar/InfoBarLayout.java
similarity index 94%
rename from chrome/android/java/src/org/chromium/chrome/browser/infobar/InfoBarLayout.java
rename to chrome/browser/ui/messages/android/java/src/org/chromium/chrome/browser/ui/messages/infobar/InfoBarLayout.java
index f93a841..0d4abff 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/infobar/InfoBarLayout.java
+++ b/chrome/browser/ui/messages/android/java/src/org/chromium/chrome/browser/ui/messages/infobar/InfoBarLayout.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.chrome.browser.infobar;
+package org.chromium.chrome.browser.ui.messages.infobar;
 
 import android.content.Context;
 import android.content.res.ColorStateList;
@@ -27,8 +27,7 @@
 import androidx.annotation.Nullable;
 
 import org.chromium.base.ApiCompatibilityUtils;
-import org.chromium.chrome.R;
-import org.chromium.chrome.browser.ui.messages.infobar.InfoBarControlLayout;
+import org.chromium.chrome.ui.messages.R;
 import org.chromium.components.browser_ui.widget.DualControlLayout;
 import org.chromium.ui.UiUtils;
 import org.chromium.ui.text.NoUnderlineClickableSpan;
@@ -56,7 +55,6 @@
  * InfoBarInteractionHandler.
  */
 public final class InfoBarLayout extends ViewGroup implements View.OnClickListener {
-
     /**
      * Parameters used for laying out children.
      */
@@ -155,16 +153,20 @@
 
     /**
      * Returns the {@link TextView} corresponding to the main infobar message.
+     * The returned view is a part of internal layout strucutre and shouldn't be accessed by InfoBar
+     * implementations.
      */
-    TextView getMessageTextView() {
+    public TextView getMessageTextView() {
         return mMessageTextView;
     }
 
     /**
      * Returns the {@link InfoBarControlLayout} containing the TextView showing the main infobar
      * message and associated controls, which is sandwiched between its icon and close button.
+     * The returned view is a part of internal layout strucutre and shouldn't be accessed by InfoBar
+     * implementations.
      */
-    InfoBarControlLayout getMessageLayout() {
+    public InfoBarControlLayout getMessageLayout() {
         return mMessageLayout;
     }
 
@@ -192,7 +194,7 @@
      * @param rangeStart Where the link starts.
      * @param rangeEnd   Where the link ends.
      */
-    void setInlineMessageLink(int rangeStart, int rangeEnd) {
+    public void setInlineMessageLink(int rangeStart, int rangeEnd) {
         mMessageInlineLinkRangeStart = rangeStart;
         mMessageInlineLinkRangeEnd = rangeEnd;
         mMessageTextView.setText(prepareMainMessageString());
@@ -243,8 +245,8 @@
      */
     public void setBottomViews(String primaryText, View secondaryView, int alignment) {
         assert !TextUtils.isEmpty(primaryText);
-        Button primaryButton = DualControlLayout.createButtonForLayout(
-                getContext(), true, primaryText, this);
+        Button primaryButton =
+                DualControlLayout.createButtonForLayout(getContext(), true, primaryText, this);
 
         assert mButtonRowLayout == null;
         mButtonRowLayout = new DualControlLayout(getContext(), null);
@@ -279,7 +281,8 @@
      * Returns the primary button, or null if it doesn't exist.
      */
     public ButtonCompat getPrimaryButton() {
-        return mButtonRowLayout == null ? null
+        return mButtonRowLayout == null
+                ? null
                 : (ButtonCompat) mButtonRowLayout.findViewById(R.id.button_primary);
     }
 
@@ -294,7 +297,9 @@
      * Must be called after the message, buttons, and custom content have been set, and before the
      * first call to onMeasure().
      */
-    void onContentCreated() {
+    // TODO(crbug/1056346): onContentCreated is made public to allow access from InfoBar. Once
+    // InfoBar is modularized, restore access to package private.
+    public void onContentCreated() {
         // Add the child views in the desired focus order.
         if (mIconView != null) addView(mIconView);
         addView(mMessageLayout);
@@ -355,8 +360,8 @@
      */
     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        assert getLayoutParams().height == LayoutParams.WRAP_CONTENT
-                : "InfoBar heights cannot be constrained.";
+        assert getLayoutParams().height
+                == LayoutParams.WRAP_CONTENT : "InfoBar heights cannot be constrained.";
 
         // Apply the padding that surrounds all the infobar controls.
         final int layoutWidth = Math.max(MeasureSpec.getSize(widthMeasureSpec), mMinWidth);
@@ -395,8 +400,8 @@
         // Control layouts are placed below the message layout and the close button.  The icon is
         // ignored for this particular calculation because the icon enforces a left margin on all of
         // the control layouts and won't be overlapped.
-        layoutBottom += Math.max(getChildHeightWithMargins(mMessageLayout),
-                getChildHeightWithMargins(mCloseButton));
+        layoutBottom += Math.max(
+                getChildHeightWithMargins(mMessageLayout), getChildHeightWithMargins(mCloseButton));
 
         // The other control layouts are constrained only by the icon's width.
         final int controlPaddedStart = paddedStart + iconWidth;
diff --git a/chrome/browser/ui/tab_contents/core_tab_helper.cc b/chrome/browser/ui/tab_contents/core_tab_helper.cc
index 38174f5..f55db8f 100644
--- a/chrome/browser/ui/tab_contents/core_tab_helper.cc
+++ b/chrome/browser/ui/tab_contents/core_tab_helper.cc
@@ -30,7 +30,6 @@
 #include "content/public/browser/render_view_host.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/content_switches.h"
-#include "content/public/common/context_menu_params.h"
 #include "extensions/buildflags/buildflags.h"
 #include "net/base/load_states.h"
 #include "net/http/http_request_headers.h"
diff --git a/chrome/browser/ui/views/media_router/media_router_ui_browsertest.cc b/chrome/browser/ui/views/media_router/media_router_ui_browsertest.cc
index 88bc450b..19ef1d6 100644
--- a/chrome/browser/ui/views/media_router/media_router_ui_browsertest.cc
+++ b/chrome/browser/ui/views/media_router/media_router_ui_browsertest.cc
@@ -27,9 +27,9 @@
 #include "chrome/common/url_constants.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/ui_test_utils.h"
+#include "content/public/browser/context_menu_params.h"
 #include "content/public/browser/navigation_entry.h"
 #include "content/public/browser/web_contents.h"
-#include "content/public/common/context_menu_params.h"
 #include "content/public/test/test_navigation_observer.h"
 #include "content/public/test/test_utils.h"
 #include "ui/base/ui_base_features.h"
diff --git a/chrome/browser/ui/views/omnibox/omnibox_view_views.cc b/chrome/browser/ui/views/omnibox/omnibox_view_views.cc
index 2513d22..94f673e 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_view_views.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_view_views.cc
@@ -641,31 +641,25 @@
   if (!views::FocusManager::IsTabTraversalKeyEvent(event))
     return false;
 
-  if (model()->is_keyword_hint() && !event.IsShiftDown())
-    return model()->AcceptKeyword(OmniboxEventProto::TAB);
-
   if (!model()->popup_model()->IsOpen())
     return false;
 
-  if (event.IsShiftDown() && (model()->popup_model()->selected_line_state() ==
-                              OmniboxPopupModel::KEYWORD)) {
+  // This block steps the popup model, with special consideration
+  // for existing keyword logic. AcceptKeyword and ClearKeyword
+  // have side effects which include updating the popup model
+  // selection. If those don't change selection, it is done here.
+  const auto old_selection = model()->popup_model()->selection();
+  const auto new_selection = model()->popup_model()->GetNextSelection(
+      event.IsShiftDown() ? OmniboxPopupModel::kBackward
+                          : OmniboxPopupModel::kForward,
+      OmniboxPopupModel::kStateOrLine);
+  if (new_selection.IsChangeToKeyword(old_selection)) {
+    return model()->AcceptKeyword(OmniboxEventProto::TAB);
+  } else if (old_selection.IsChangeToKeyword(new_selection)) {
     model()->ClearKeyword();
     return true;
-  }
-
-  // If tabbing forwards (shift is not pressed) and suggestion button is not
-  // selected, select it.
-  if (!event.IsShiftDown()) {
-    if (MaybeFocusSecondaryButton())
-      return true;
-  }
-
-  // If tabbing backwards (shift is pressed), handle cases involving selecting
-  // the tab switch button.
-  if (event.IsShiftDown()) {
-    // If tab switch button is focused, unfocus it.
-    if (MaybeUnfocusSecondaryButton())
-      return true;
+  } else {
+    model()->popup_model()->SetSelection(new_selection);
   }
 
   if (base::FeatureList::IsEnabled(omnibox::kTabKeyCanEscapeOmniboxPopup)) {
@@ -686,15 +680,6 @@
     }
   }
 
-  // Translate tab and shift-tab into down and up respectively.
-  model()->OnUpOrDownKeyPressed(event.IsShiftDown() ? -1 : 1);
-  // If we shift-tabbed (and actually moved) to a suggestion with a tab
-  // switch button, select it.
-  if (event.IsShiftDown() && GetSecondaryButtonForSelectedLine()) {
-    model()->popup_model()->SetSelectedLineState(
-        OmniboxPopupModel::BUTTON_FOCUSED);
-  }
-
   return true;
 }
 
@@ -741,27 +726,6 @@
   return TextAndUIDirectionMatch() ? SelectionAtEnd() : SelectionAtBeginning();
 }
 
-bool OmniboxViewViews::MaybeFocusSecondaryButton() {
-  if (GetSecondaryButtonForSelectedLine() &&
-      model()->popup_model()->selected_line_state() ==
-          OmniboxPopupModel::NORMAL) {
-    model()->popup_model()->SetSelectedLineState(
-        OmniboxPopupModel::BUTTON_FOCUSED);
-    return true;
-  }
-  return false;
-}
-
-bool OmniboxViewViews::MaybeUnfocusSecondaryButton() {
-  if (GetSecondaryButtonForSelectedLine() &&
-      model()->popup_model()->selected_line_state() ==
-          OmniboxPopupModel::BUTTON_FOCUSED) {
-    model()->popup_model()->SetSelectedLineState(OmniboxPopupModel::NORMAL);
-    return true;
-  }
-  return false;
-}
-
 bool OmniboxViewViews::MaybeTriggerSecondaryButton(const ui::KeyEvent& event) {
   // TODO(tommycli): If we have a WebUI omnibox popup, we should move the
   // secondary button logic out of the View and into the OmniboxPopupModel.
@@ -1143,10 +1107,8 @@
 }
 
 bool OmniboxViewViews::OnMousePressed(const ui::MouseEvent& event) {
-  if (model()->popup_model() &&  // Can be null in tests.
-      model()->popup_model()->selected_line_state() ==
-          OmniboxPopupModel::BUTTON_FOCUSED) {
-    model()->popup_model()->SetSelectedLineState(OmniboxPopupModel::NORMAL);
+  if (model()->popup_model()) {  // Can be null in tests.
+    model()->popup_model()->ClearSelectionState();
   }
   is_mouse_pressed_ = true;
 
@@ -1678,42 +1640,49 @@
     case ui::VKEY_PRIOR:
       if (control || alt || shift || GetReadOnly())
         return false;
-      model()->OnUpOrDownKeyPressed(
-          -static_cast<int>(model()->popup_model()->selected_line()));
+      if (!model()->MaybeStartQueryForPopup()) {
+        model()->popup_model()->StepSelection(OmniboxPopupModel::kBackward,
+                                              OmniboxPopupModel::kAllLines);
+      }
       return true;
 
     case ui::VKEY_NEXT:
       if (control || alt || shift || GetReadOnly())
         return false;
-      model()->OnUpOrDownKeyPressed(model()->result().size() -
-                                    model()->popup_model()->selected_line() -
-                                    1);
+      if (!model()->MaybeStartQueryForPopup()) {
+        model()->popup_model()->StepSelection(OmniboxPopupModel::kForward,
+                                              OmniboxPopupModel::kAllLines);
+      }
       return true;
 
     case ui::VKEY_RIGHT:
-    case ui::VKEY_LEFT:
+    case ui::VKEY_LEFT: {
       if (control || alt || shift)
         return false;
 
+      const auto step = [=](auto direction) {
+        if (!model()->popup_model()) {
+          return false;
+        }
+        auto old_selection = model()->popup_model()->selection();
+        return model()->popup_model()->StepSelection(
+                   direction, OmniboxPopupModel::kStateOrNothing) !=
+               old_selection;
+      };
+
       // If advancing cursor (accounting for UI direction)
       if (base::i18n::IsRTL() == (event.key_code() == ui::VKEY_LEFT)) {
         if (!DirectionAwareSelectionAtEnd())
           return false;
 
-        if (OmniboxFieldTrial::IsExperimentalKeywordModeEnabled() &&
-            model()->is_keyword_hint()) {
-          OnBeforePossibleChange();
-          model()->AcceptKeyword(OmniboxEventProto::SELECT_SUGGESTION);
-          OnAfterPossibleChange(true);
-          return true;
-        } else if (MaybeFocusSecondaryButton()) {
+        if (step(OmniboxPopupModel::kForward)) {
           return true;
         }
-      } else if (MaybeUnfocusSecondaryButton()) {
+      } else if (step(OmniboxPopupModel::kBackward)) {
         return true;
       }
       break;
-
+    }
     case ui::VKEY_V:
       if (control && !alt &&
           IsTextEditCommandEnabled(ui::TextEditCommand::PASTE)) {
diff --git a/chrome/browser/ui/views/omnibox/omnibox_view_views.h b/chrome/browser/ui/views/omnibox/omnibox_view_views.h
index 4a1cb65..33a885dd9 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_view_views.h
+++ b/chrome/browser/ui/views/omnibox/omnibox_view_views.h
@@ -213,12 +213,6 @@
   // Like SelectionAtEnd(), but accounts for RTL.
   bool DirectionAwareSelectionAtEnd() const;
 
-  // Attempts to either focus or unfocus the secondary button (tests if all
-  // conditions are met and makes necessary subroutine call) and returns
-  // whether it succeeded.
-  bool MaybeFocusSecondaryButton();
-  bool MaybeUnfocusSecondaryButton();
-
   // If the Secondary button for the current suggestion is focused, clicks it
   // and returns true.
   bool MaybeTriggerSecondaryButton(const ui::KeyEvent& event);
diff --git a/chrome/browser/ui/views/tab_contents/chrome_web_contents_view_delegate_views_unittest.cc b/chrome/browser/ui/views/tab_contents/chrome_web_contents_view_delegate_views_unittest.cc
index e035c806..92f4b80 100644
--- a/chrome/browser/ui/views/tab_contents/chrome_web_contents_view_delegate_views_unittest.cc
+++ b/chrome/browser/ui/views/tab_contents/chrome_web_contents_view_delegate_views_unittest.cc
@@ -6,8 +6,8 @@
 
 #include "chrome/browser/ui/tab_contents/chrome_web_contents_view_delegate.h"
 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
+#include "content/public/browser/context_menu_params.h"
 #include "content/public/browser/web_contents.h"
-#include "content/public/common/context_menu_params.h"
 
 typedef ChromeRenderViewHostTestHarness ChromeWebContentsViewDelegateViewsTest;
 
diff --git a/chrome/browser/ui/webui/settings/chromeos/cups_printers_handler.cc b/chrome/browser/ui/webui/settings/chromeos/cups_printers_handler.cc
index 5f787a1d..9c690fbc 100644
--- a/chrome/browser/ui/webui/settings/chromeos/cups_printers_handler.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/cups_printers_handler.cc
@@ -39,6 +39,7 @@
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/chrome_select_file_policy.h"
+#include "chrome/browser/ui/webui/settings/chromeos/server_printer_url_util.h"
 #include "chrome/common/chrome_paths.h"
 #include "chrome/common/pref_names.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
@@ -265,40 +266,11 @@
   return ret;
 }
 
-bool ConvertToGURL(const std::string& url, GURL* gurl) {
-  *gurl = GURL(url);
-  if (!gurl->is_valid()) {
-    // URL is not valid.
-    return false;
-  }
-  if (!gurl->SchemeIsHTTPOrHTTPS() && !gurl->SchemeIs("ipp") &&
-      !gurl->SchemeIs("ipps")) {
-    // URL has unsupported scheme; we support only: http, https, ipp, ipps.
-    return false;
-  }
-  // Replaces ipp/ipps by http/https. IPP standard describes protocol built
-  // on top of HTTP, so both types of addresses have the same meaning in the
-  // context of IPP interface. Moreover, the URL must have http/https scheme
-  // to pass IsStandard() test from GURL library (see "Validation of the URL
-  // address" below).
-  bool set_ipp_port = false;
-  if (gurl->SchemeIs("ipp")) {
-    set_ipp_port = (gurl->IntPort() == url::PORT_UNSPECIFIED);
-    *gurl = GURL("http" + url.substr(url.find_first_of(':')));
-  } else if (gurl->SchemeIs("ipps")) {
-    *gurl = GURL("https" + url.substr(url.find_first_of(':')));
-  }
-  // The default port for ipp is 631. If the schema ipp is replaced by http
-  // and the port is not explicitly defined in the url, we have to overwrite
-  // the default http port with the default ipp port. For ipps we do nothing
-  // because implementers use the same port for ipps and https.
-  if (set_ipp_port) {
-    GURL::Replacements replacement;
-    replacement.SetPortStr("631");
-    *gurl = gurl->ReplaceComponents(replacement);
-  }
-  // Validation of the URL address.
-  return gurl->IsStandard();
+GURL GenerateHttpCupsServerUrl(const GURL& server_url) {
+  GURL::Replacements replacement;
+  replacement.SetSchemeStr("http");
+  replacement.SetPortStr("631");
+  return server_url.ReplaceComponents(replacement);
 }
 
 }  // namespace
@@ -1284,27 +1256,45 @@
   CHECK(args->GetString(0, &callback_id));
   CHECK(args->GetString(1, &server_url));
 
-  GURL server_gurl;
-  if (!ConvertToGURL(server_url, &server_gurl)) {
+  base::Optional<GURL> converted_server_url =
+      GenerateServerPrinterUrlWithValidScheme(server_url);
+  if (!converted_server_url) {
     RejectJavascriptCallback(
         base::Value(callback_id),
         base::Value(PrintServerQueryResult::kIncorrectUrl));
     return;
   }
 
+  // Use fallback only if HasValidServerPrinterScheme is false.
+  QueryPrintServer(callback_id, converted_server_url.value(),
+                   !HasValidServerPrinterScheme(GURL(server_url)));
+}
+
+void CupsPrintersHandler::QueryPrintServer(const std::string& callback_id,
+                                           const GURL& server_url,
+                                           bool should_fallback) {
   server_printers_fetcher_ = std::make_unique<ServerPrintersFetcher>(
-      server_gurl, "(from user)",
+      server_url, "(from user)",
       base::BindRepeating(&CupsPrintersHandler::OnQueryPrintServerCompleted,
-                          weak_factory_.GetWeakPtr(), callback_id));
+                          weak_factory_.GetWeakPtr(), callback_id,
+                          should_fallback));
 }
 
 void CupsPrintersHandler::OnQueryPrintServerCompleted(
     const std::string& callback_id,
+    bool should_fallback,
     const ServerPrintersFetcher* sender,
     const GURL& server_url,
     std::vector<PrinterDetector::DetectedPrinter>&& returned_printers) {
   const PrintServerQueryResult result = sender->GetLastError();
   if (result != PrintServerQueryResult::kNoErrors) {
+    if (should_fallback) {
+      // Apply the fallback query.
+      QueryPrintServer(callback_id, GenerateHttpCupsServerUrl(server_url),
+                       /*should_fallback=*/false);
+      return;
+    }
+
     RejectJavascriptCallback(base::Value(callback_id), base::Value(result));
     return;
   }
@@ -1314,9 +1304,10 @@
       printers_manager_->GetPrinters(PrinterClass::kSaved);
   std::set<GURL> known_printers;
   for (const Printer& printer : saved_printers) {
-    GURL gurl;
-    if (ConvertToGURL(printer.uri(), &gurl))
-      known_printers.insert(gurl);
+    base::Optional<GURL> gurl =
+        GenerateServerPrinterUrlWithValidScheme(printer.uri());
+    if (gurl)
+      known_printers.insert(gurl.value());
   }
 
   // Built final list of printers and a list of current names. If "current name"
@@ -1326,11 +1317,10 @@
   printers.reserve(returned_printers.size());
   for (PrinterDetector::DetectedPrinter& printer : returned_printers) {
     printers.push_back(std::move(printer.printer));
-    GURL printer_gurl;
-    if (ConvertToGURL(printers.back().uri(), &printer_gurl)) {
-      if (known_printers.count(printer_gurl))
-        printers.pop_back();
-    }
+    base::Optional<GURL> printer_gurl =
+        GenerateServerPrinterUrlWithValidScheme(printers.back().uri());
+    if (printer_gurl && known_printers.count(printer_gurl.value()))
+      printers.pop_back();
   }
 
   // Delete fetcher object.
diff --git a/chrome/browser/ui/webui/settings/chromeos/cups_printers_handler.h b/chrome/browser/ui/webui/settings/chromeos/cups_printers_handler.h
index 6e80a91..30748a5 100644
--- a/chrome/browser/ui/webui/settings/chromeos/cups_printers_handler.h
+++ b/chrome/browser/ui/webui/settings/chromeos/cups_printers_handler.h
@@ -214,8 +214,14 @@
                     const net::IPEndPoint& endpoint);
 
   void HandleQueryPrintServer(const base::ListValue* args);
+
+  void QueryPrintServer(const std::string& callback_id,
+                        const GURL& server_url,
+                        bool should_fallback);
+
   void OnQueryPrintServerCompleted(
       const std::string& callback_id,
+      bool should_fallback,
       const ServerPrintersFetcher* sender,
       const GURL& server_url,
       std::vector<PrinterDetector::DetectedPrinter>&& returned_printers);
diff --git a/chrome/browser/ui/webui/settings/chromeos/os_settings_localized_strings_provider.cc b/chrome/browser/ui/webui/settings/chromeos/os_settings_localized_strings_provider.cc
index cfed462..5fd4dfbc 100644
--- a/chrome/browser/ui/webui/settings/chromeos/os_settings_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/os_settings_localized_strings_provider.cc
@@ -724,12 +724,11 @@
       l10n_util::GetStringFUTF16(
           IDS_SETTINGS_CROSTINI_SUBTEXT, ui::GetChromeOSDeviceName(),
           GetHelpUrlWithBoard(chrome::kLinuxAppsLearnMoreURL)));
-  // TODO(crbug.com/893332): replace with the final URL
   html_source->AddString(
       "crostiniArcAdbPowerwashRequiredSublabel",
       l10n_util::GetStringFUTF16(
           IDS_SETTINGS_CROSTINI_ARC_ADB_POWERWASH_REQUIRED_SUBLABEL,
-          GetHelpUrlWithBoard(chrome::kLinuxAppsLearnMoreURL)));
+          base::ASCIIToUTF16(chrome::kArcAdbSideloadingLearnMoreURL)));
   html_source->AddString("crostiniRemove", l10n_util::GetStringFUTF16(
                                                IDS_SETTINGS_CROSTINI_REMOVE,
                                                ui::GetChromeOSDeviceName()));
diff --git a/chrome/browser/ui/webui/settings/chromeos/server_printer_url_util.cc b/chrome/browser/ui/webui/settings/chromeos/server_printer_url_util.cc
new file mode 100644
index 0000000..005d006a
--- /dev/null
+++ b/chrome/browser/ui/webui/settings/chromeos/server_printer_url_util.cc
@@ -0,0 +1,73 @@
+// 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/settings/chromeos/server_printer_url_util.h"
+
+#include "url/gurl.h"
+
+namespace {
+
+// Returns an updated |gurl| with the specified components from the params. If
+// |scheme| is not empty, returns an updated GURL with the specified scheme. If
+// |replace_port| is true, returns an updated GURL with 631 as the port. 631 is
+// the default port for IPP.
+GURL UpdateServerPrinterGURL(const GURL& gurl,
+                             const std::string& scheme,
+                             bool replace_ipp_port) {
+  GURL::Replacements replacement;
+  if (!scheme.empty())
+    replacement.SetSchemeStr(scheme);
+  if (replace_ipp_port)
+    replacement.SetPortStr("631");
+  return gurl.ReplaceComponents(replacement);
+}
+
+}  // namespace
+
+namespace chromeos {
+namespace settings {
+
+bool HasValidServerPrinterScheme(const GURL& gurl) {
+  return gurl.SchemeIsHTTPOrHTTPS() || gurl.SchemeIs("ipp") ||
+         gurl.SchemeIs("ipps");
+}
+
+base::Optional<GURL> GenerateServerPrinterUrlWithValidScheme(
+    const std::string& url) {
+  base::Optional<GURL> gurl = base::make_optional(GURL(url));
+  if (!HasValidServerPrinterScheme(*gurl)) {
+    // If we're missing a valid scheme, try querying with IPPS first.
+    gurl = GURL("ipps://" + url);
+  }
+
+  if (!gurl->is_valid())
+    return base::nullopt;
+
+  // Replaces IPP/IPPS by HTTP/HTTPS. IPP standard describes protocol built
+  // on top of HTTP, so both types of addresses have the same meaning in the
+  // context of IPP interface. Moreover, the URL must have HTTP/HTTPS scheme
+  // to pass IsStandard() test from GURL library (see "Validation of the URL
+  // address" below).
+  if (gurl->SchemeIs("ipp")) {
+    gurl = UpdateServerPrinterGURL(*gurl, "http",
+                                   /*replace_ipp_port=*/false);
+    // The default port for IPP is 631. If the schema IPP is replaced by HTTP
+    // and the port is not explicitly defined in the URL, we have to overwrite
+    // the default HTTP port with the default IPP port. For IPPS we do nothing
+    // because implementers use the same port for IPPS and HTTPS.
+    if (gurl->IntPort() == url::PORT_UNSPECIFIED) {
+      gurl = UpdateServerPrinterGURL(*gurl, /*scheme=*/"",
+                                     /*replace_ipp_port=*/true);
+    }
+  } else if (gurl->SchemeIs("ipps")) {
+    gurl = UpdateServerPrinterGURL(*gurl, "https",
+                                   /*replace_ipp_port=*/false);
+  }
+
+  // Check validation of the URL address and return |gurl| if valid.
+  return gurl->IsStandard() ? gurl : base::nullopt;
+}
+
+}  // namespace settings
+}  // namespace chromeos
diff --git a/chrome/browser/ui/webui/settings/chromeos/server_printer_url_util.h b/chrome/browser/ui/webui/settings/chromeos/server_printer_url_util.h
new file mode 100644
index 0000000..ad118c1
--- /dev/null
+++ b/chrome/browser/ui/webui/settings/chromeos/server_printer_url_util.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 CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_SERVER_PRINTER_URL_UTIL_H_
+#define CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_SERVER_PRINTER_URL_UTIL_H_
+
+#include <string>
+
+#include "base/optional.h"
+
+class GURL;
+
+namespace chromeos {
+namespace settings {
+
+// Returns true if |gurl| has any the following scheme: HTTP, HTTPS, IPP, or
+// IPPS. Returns false for an empty or any other scheme.
+bool HasValidServerPrinterScheme(const GURL& gurl);
+
+// Returns a GURL from the input |url|. Returns base::nullopt if
+// either |url| is invalid or constructing the GURL failed. This will also
+// default the server printer URI to use HTTPS if it detects a missing scheme.
+base::Optional<GURL> GenerateServerPrinterUrlWithValidScheme(
+    const std::string& url);
+
+}  // namespace settings
+}  // namespace chromeos
+
+#endif  // CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_SERVER_PRINTER_URL_UTIL_H_
diff --git a/chrome/browser/ui/webui/settings/chromeos/server_printer_url_util_unittest.cc b/chrome/browser/ui/webui/settings/chromeos/server_printer_url_util_unittest.cc
new file mode 100644
index 0000000..26e2ec1
--- /dev/null
+++ b/chrome/browser/ui/webui/settings/chromeos/server_printer_url_util_unittest.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/webui/settings/chromeos/server_printer_url_util.h"
+
+#include <string>
+
+#include "base/optional.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+namespace chromeos {
+namespace settings {
+
+class ServerPrinterUrlUtilTest : public testing::Test {
+ public:
+  ServerPrinterUrlUtilTest() = default;
+  ~ServerPrinterUrlUtilTest() override = default;
+};
+
+TEST_F(ServerPrinterUrlUtilTest, IsValidScheme) {
+  GURL gurl1("ipp://123.123.11.11:123");
+  ASSERT_TRUE(HasValidServerPrinterScheme(gurl1));
+
+  GURL gurl2("http://123.123.11.11:123");
+  ASSERT_TRUE(HasValidServerPrinterScheme(gurl2));
+
+  GURL gurl3("ipps://123.123.11.11:123");
+  ASSERT_TRUE(HasValidServerPrinterScheme(gurl3));
+
+  GURL gurl4("https://123.123.11.11:123");
+  ASSERT_TRUE(HasValidServerPrinterScheme(gurl4));
+
+  // Missing scheme.
+  GURL gurl5("123.123.11.11:123");
+  ASSERT_FALSE(HasValidServerPrinterScheme(gurl5));
+
+  // Invalid scheme.
+  GURL gurl6("test://123.123.11.11:123");
+  ASSERT_FALSE(HasValidServerPrinterScheme(gurl6));
+}
+
+TEST_F(ServerPrinterUrlUtilTest, ConvertToGURL) {
+  // Test that a GURL is created with |gurl1| as its source.
+  std::string url1("http://123.123.11.11:631");
+  base::Optional<GURL> gurl1 = GenerateServerPrinterUrlWithValidScheme(url1);
+  DCHECK(gurl1);
+  ASSERT_EQ("http://123.123.11.11:631/", gurl1->spec());
+  ASSERT_EQ("http", gurl1->scheme());
+  ASSERT_EQ("631", gurl1->port());
+
+  // Test that HTTPS is the default scheme if a scheme is not provided.
+  std::string url2("123.123.11.11:631");
+  base::Optional<GURL> gurl2 = GenerateServerPrinterUrlWithValidScheme(url2);
+  DCHECK(gurl2);
+  ASSERT_EQ("https", gurl2->scheme());
+  ASSERT_EQ("https://123.123.11.11:631/", gurl2->spec());
+
+  // Test that if a URL has IPP as its scheme, it will create a new GURL with
+  // HTTP as its scheme and 631 as its port.
+  std::string url3("ipp://123.123.11.11");
+  base::Optional<GURL> gurl3 = GenerateServerPrinterUrlWithValidScheme(url3);
+  DCHECK(gurl3);
+  ASSERT_EQ("http", gurl3->scheme());
+  ASSERT_EQ("631", gurl3->port());
+  ASSERT_EQ("http://123.123.11.11:631/", gurl3->spec());
+
+  // Test that if a URL has IPP as its scheme and a specified port, it will
+  // create a new GURL with HTTP as the scheme and keeps the same port.
+  std::string url4("ipp://123.123.11.11:321");
+  base::Optional<GURL> gurl4 = GenerateServerPrinterUrlWithValidScheme(url4);
+  DCHECK(gurl4);
+  ASSERT_EQ("http", gurl4->scheme());
+  ASSERT_EQ("321", gurl4->port());
+  ASSERT_EQ("http://123.123.11.11:321/", gurl4->spec());
+
+  // Test that if a URL has IPPS as its scheme and a specified port, a new GURL
+  // is created with the scheme as HTTPS and keeps the same port.
+  std::string url5("ipps://123.123.11.11:555");
+  base::Optional<GURL> gurl5 = GenerateServerPrinterUrlWithValidScheme(url5);
+  DCHECK(gurl5);
+  ASSERT_EQ("https", gurl5->scheme());
+  ASSERT_EQ("555", gurl5->port());
+  ASSERT_EQ("https://123.123.11.11:555/", gurl5->spec());
+}
+
+}  // namespace settings
+}  // namespace chromeos
diff --git a/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
index 273bf0b..597d960 100644
--- a/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
@@ -1086,6 +1086,12 @@
        IDS_SETTINGS_SECURE_DNS_DISABLED_FOR_PARENTAL_CONTROL},
       {"secureDnsAutomaticModeDescription",
        IDS_SETTINGS_AUTOMATIC_MODE_DESCRIPTION},
+      {"secureDnsAutomaticModeDescriptionSecondary",
+       IDS_SETTINGS_AUTOMATIC_MODE_DESCRIPTION_SECONDARY},
+      {"secureDnsSecureModeA11yLabel",
+       IDS_SETTINGS_SECURE_MODE_DESCRIPTION_ACCESSIBILITY_LABEL},
+      {"secureDnsDropdownA11yLabel",
+       IDS_SETTINGS_SECURE_DNS_DROPDOWN_ACCESSIBILITY_LABEL},
       {"secureDnsSecureDropdownModeDescription",
        IDS_SETTINGS_SECURE_DROPDOWN_MODE_DESCRIPTION},
       {"secureDnsSecureDropdownModePrivacyPolicy",
diff --git a/chrome/browser/ui/webui/settings/settings_secure_dns_handler.cc b/chrome/browser/ui/webui/settings/settings_secure_dns_handler.cc
index acf049af..92460eb 100644
--- a/chrome/browser/ui/webui/settings/settings_secure_dns_handler.cc
+++ b/chrome/browser/ui/webui/settings/settings_secure_dns_handler.cc
@@ -44,7 +44,8 @@
   SecureDnsUiManagementMode management_mode;
   SystemNetworkContextManager::GetStubResolverConfig(
       g_browser_process->local_state(), &insecure_stub_resolver_enabled,
-      &secure_dns_mode, &dns_over_https_servers, &management_mode);
+      &secure_dns_mode, &dns_over_https_servers, false /* record_metrics */,
+      &management_mode);
 
   std::string secure_dns_mode_str;
   switch (secure_dns_mode) {
diff --git a/chrome/browser/ui/webui/signin/inline_login_ui.cc b/chrome/browser/ui/webui/signin/inline_login_ui.cc
index 555c5e9b5..df42aa9c 100644
--- a/chrome/browser/ui/webui/signin/inline_login_ui.cc
+++ b/chrome/browser/ui/webui/signin/inline_login_ui.cc
@@ -8,6 +8,7 @@
 
 #include "base/bind.h"
 #include "base/command_line.h"
+#include "build/branding_buildflags.h"
 #include "build/build_config.h"
 #include "chrome/browser/extensions/chrome_extension_web_contents_observer.h"
 #include "chrome/browser/extensions/tab_helper.h"
@@ -41,6 +42,9 @@
   source->AddLocalizedString("okButton", IDS_APP_OK);
   source->AddLocalizedString("backButton", IDS_EDU_LOGIN_BACK);
   source->AddLocalizedString("nextButton", IDS_EDU_LOGIN_NEXT);
+
+  source->AddLocalizedString("welcomeTitle", IDS_EDU_LOGIN_WELCOME_TITLE);
+  source->AddLocalizedString("welcomeBody", IDS_EDU_LOGIN_WELCOME_BODY);
 }
 #endif  // defined(OS_CHROMEOS)
 
@@ -69,8 +73,8 @@
   source->OverrideContentSecurityPolicyScriptSrc(
       "script-src chrome://resources chrome://test 'self';");
 
-  source->AddResourcePath("edu", IDU_EDU_LOGIN_EDU_LOGIN_HTML);
-  source->AddResourcePath("app.js", IDU_EDU_LOGIN_EDU_LOGIN_JS);
+  source->AddResourcePath("edu", IDR_EDU_LOGIN_EDU_LOGIN_HTML);
+  source->AddResourcePath("app.js", IDR_EDU_LOGIN_EDU_LOGIN_JS);
   source->AddResourcePath("edu_login_button.js",
                           IDR_EDU_LOGIN_EDU_LOGIN_BUTTON_JS);
   source->AddResourcePath("edu_login_template.js",
@@ -78,13 +82,21 @@
   source->AddResourcePath("edu_login_css.js", IDR_EDU_LOGIN_EDU_LOGIN_CSS_JS);
   source->AddResourcePath("browser_proxy.js", IDR_EDU_LOGIN_BROWSER_PROXY_JS);
   source->AddResourcePath("edu_login_util.js", IDR_EDU_LOGIN_EDU_LOGIN_UTIL_JS);
+  source->AddResourcePath("edu_login_welcome.js",
+                          IDR_EDU_LOGIN_EDU_LOGIN_WELCOME_JS);
 
   source->AddResourcePath("test_loader.js", IDR_WEBUI_JS_TEST_LOADER);
   source->AddResourcePath("test_loader.html", IDR_WEBUI_HTML_TEST_LOADER);
 
-  AddEduStrings(source);
+#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
+  source->AddResourcePath("googleg.svg",
+                          IDR_ACCOUNT_MANAGER_WELCOME_GOOGLE_LOGO_SVG);
 #endif
 
+  source->EnableReplaceI18nInJS();
+  AddEduStrings(source);
+#endif  // defined(OS_CHROMEOS)
+
   source->AddLocalizedString("title", IDS_CHROME_SIGNIN_TITLE);
   source->AddLocalizedString(
       "accessibleCloseButtonLabel", IDS_SIGNIN_ACCESSIBLE_CLOSE_BUTTON);
diff --git a/chrome/browser/ui/webui/webui_webview_browsertest.cc b/chrome/browser/ui/webui/webui_webview_browsertest.cc
index 3dadde4..1d6c0e1 100644
--- a/chrome/browser/ui/webui/webui_webview_browsertest.cc
+++ b/chrome/browser/ui/webui/webui_webview_browsertest.cc
@@ -16,10 +16,10 @@
 #include "chrome/common/url_constants.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "chrome/test/base/web_ui_browser_test.h"
+#include "content/public/browser/context_menu_params.h"
 #include "content/public/browser/render_view_host.h"
 #include "content/public/browser/render_widget_host.h"
 #include "content/public/browser/web_contents.h"
-#include "content/public/common/context_menu_params.h"
 #include "content/public/common/drop_data.h"
 #include "content/public/test/browser_test_utils.h"
 #include "extensions/test/extension_test_message_listener.h"
diff --git a/chrome/browser/ui/window_sizer/window_sizer.cc b/chrome/browser/ui/window_sizer/window_sizer.cc
index d762180..222114f6 100644
--- a/chrome/browser/ui/window_sizer/window_sizer.cc
+++ b/chrome/browser/ui/window_sizer/window_sizer.cc
@@ -35,9 +35,7 @@
 // and persistent state from the browser window and the user's profile.
 class DefaultStateProvider : public WindowSizer::StateProvider {
  public:
-  DefaultStateProvider(const std::string& app_name, const Browser* browser)
-      : app_name_(app_name), browser_(browser) {
-  }
+  explicit DefaultStateProvider(const Browser* browser) : browser_(browser) {}
 
   // Overridden from WindowSizer::StateProvider:
   bool GetPersistentState(gfx::Rect* bounds,
@@ -87,7 +85,7 @@
       ui::WindowShowState* show_state) const override {
     DCHECK(show_state);
     // Applications are always restored with the same position.
-    if (!app_name_.empty())
+    if (browser_ && browser_->deprecated_is_app())
       return false;
 
     // If a reference browser is set, use its window. Otherwise find last
@@ -146,13 +144,11 @@
 
 // static
 void WindowSizer::GetBrowserWindowBoundsAndShowState(
-    const std::string& app_name,
     const gfx::Rect& specified_bounds,
     const Browser* browser,
     gfx::Rect* window_bounds,
     ui::WindowShowState* show_state) {
-  std::unique_ptr<StateProvider> state_provider(
-      new DefaultStateProvider(app_name, browser));
+  auto state_provider = std::make_unique<DefaultStateProvider>(browser);
   const WindowSizer sizer(std::move(state_provider), browser);
   sizer.DetermineWindowBoundsAndShowState(specified_bounds,
                                           window_bounds,
diff --git a/chrome/browser/ui/window_sizer/window_sizer.h b/chrome/browser/ui/window_sizer/window_sizer.h
index 61594a7..8b3a207a 100644
--- a/chrome/browser/ui/window_sizer/window_sizer.h
+++ b/chrome/browser/ui/window_sizer/window_sizer.h
@@ -72,12 +72,11 @@
       ui::WindowShowState* show_state) const;
 
   // Determines the size, position and maximized state for the browser window.
-  // See documentation for DetermineWindowBounds above. Normally,
+  // See documentation for DetermineWindowBoundsAndShowState above. Normally,
   // |window_bounds| is calculated by calling GetLastActiveWindowState(). To
   // explicitly specify a particular window to base the bounds on, pass in a
   // non-NULL value for |browser|.
   static void GetBrowserWindowBoundsAndShowState(
-      const std::string& app_name,
       const gfx::Rect& specified_bounds,
       const Browser* browser,
       gfx::Rect* window_bounds,
diff --git a/chrome/browser/ui/window_sizer/window_sizer_ash_unittest.cc b/chrome/browser/ui/window_sizer/window_sizer_ash_unittest.cc
index e97ad6c..908e5db 100644
--- a/chrome/browser/ui/window_sizer/window_sizer_ash_unittest.cc
+++ b/chrome/browser/ui/window_sizer/window_sizer_ash_unittest.cc
@@ -698,7 +698,7 @@
   ui::WindowShowState show_state = ui::SHOW_STATE_DEFAULT;
   gfx::Rect bounds;
   WindowSizer::GetBrowserWindowBoundsAndShowState(
-      std::string(), specified_bounds, browser.get(), &bounds, &show_state);
+      specified_bounds, browser.get(), &bounds, &show_state);
   // The window should start maximized with its restore bounds shrunken.
   EXPECT_EQ(ui::SHOW_STATE_MAXIMIZED, show_state);
   EXPECT_NE(display_bounds.ToString(), bounds.ToString());
@@ -708,7 +708,7 @@
   specified_bounds.Inset(100, 100);
   show_state = ui::SHOW_STATE_DEFAULT;
   WindowSizer::GetBrowserWindowBoundsAndShowState(
-      std::string(), specified_bounds, browser.get(), &bounds, &show_state);
+      specified_bounds, browser.get(), &bounds, &show_state);
   // The window should start in default state.
   EXPECT_EQ(ui::SHOW_STATE_DEFAULT, show_state);
   EXPECT_EQ(specified_bounds.ToString(), bounds.ToString());
@@ -726,8 +726,8 @@
   EXPECT_EQ(first_root, ash::Shell::GetRootWindowForNewWindows());
   gfx::Rect bounds;
   ui::WindowShowState show_state;
-  WindowSizer::GetBrowserWindowBoundsAndShowState(std::string(), gfx::Rect(),
-                                                  NULL, &bounds, &show_state);
+  WindowSizer::GetBrowserWindowBoundsAndShowState(gfx::Rect(), nullptr, &bounds,
+                                                  &show_state);
   EXPECT_TRUE(first_root->GetBoundsInScreen().Contains(bounds));
 
   {
@@ -741,7 +741,6 @@
     gfx::Rect bounds;
     ui::WindowShowState show_state;
     WindowSizer::GetBrowserWindowBoundsAndShowState(
-        std::string(),
         gfx::Rect(),
         NULL,
         &bounds,
diff --git a/chrome/chrome_repack_locales.gni b/chrome/chrome_repack_locales.gni
index aac49b9..9160857 100644
--- a/chrome/chrome_repack_locales.gni
+++ b/chrome/chrome_repack_locales.gni
@@ -65,14 +65,14 @@
     }
     if (is_chromeos) {
       source_patterns += [
-        "${root_gen_dir}/ash/components/strings/ash_components_strings_",
+        "${root_gen_dir}/ash/shortcut_viewer/strings/ash_components_strings_",
         "${root_gen_dir}/ash/strings/ash_strings_",
         "${root_gen_dir}/chromeos/strings/chromeos_strings_",
         "${root_gen_dir}/remoting/resources/",
         "${root_gen_dir}/ui/chromeos/strings/ui_chromeos_strings_",
       ]
       deps += [
-        "//ash/components/strings",
+        "//ash/shortcut_viewer/strings",
         "//ash/strings",
         "//chromeos/strings",
         "//remoting/resources",
diff --git a/chrome/common/extensions/api/passwords_private.idl b/chrome/common/extensions/api/passwords_private.idl
index 9a60ea82b..6684c42 100644
--- a/chrome/common/extensions/api/passwords_private.idl
+++ b/chrome/common/extensions/api/passwords_private.idl
@@ -52,6 +52,9 @@
     TOO_MANY_PASSWORDS,
     // The user hit the quota limit.
     QUOTA_LIMIT,
+    // The user has too many passwords saved and hit the quota limit. This is
+    // relevant for users that can't use the online Password Checkup.
+    TOO_MANY_PASSWORDS_AND_QUOTA_LIMIT,
     // Any other error state.
     OTHER_ERROR
   };
diff --git a/chrome/common/instant_mojom_traits.h b/chrome/common/instant_mojom_traits.h
index fdbc942d..46126ce 100644
--- a/chrome/common/instant_mojom_traits.h
+++ b/chrome/common/instant_mojom_traits.h
@@ -65,13 +65,17 @@
 IPC_STRUCT_TRAITS_BEGIN(SearchBoxTheme)
   IPC_STRUCT_TRAITS_MEMBER(bg)
   IPC_STRUCT_TRAITS_MEMBER(icon)
+  IPC_STRUCT_TRAITS_MEMBER(icon_selected)
   IPC_STRUCT_TRAITS_MEMBER(placeholder)
   IPC_STRUCT_TRAITS_MEMBER(results_bg)
   IPC_STRUCT_TRAITS_MEMBER(results_bg_hovered)
   IPC_STRUCT_TRAITS_MEMBER(results_bg_selected)
   IPC_STRUCT_TRAITS_MEMBER(results_dim)
+  IPC_STRUCT_TRAITS_MEMBER(results_dim_selected)
   IPC_STRUCT_TRAITS_MEMBER(results_text)
+  IPC_STRUCT_TRAITS_MEMBER(results_text_selected)
   IPC_STRUCT_TRAITS_MEMBER(results_url)
+  IPC_STRUCT_TRAITS_MEMBER(results_url_selected)
   IPC_STRUCT_TRAITS_MEMBER(text)
 IPC_STRUCT_TRAITS_END()
 
diff --git a/chrome/common/search/instant_types.cc b/chrome/common/search/instant_types.cc
index ea250d21..bbde344 100644
--- a/chrome/common/search/instant_types.cc
+++ b/chrome/common/search/instant_types.cc
@@ -11,12 +11,17 @@
 SearchBoxTheme::~SearchBoxTheme() = default;
 
 bool SearchBoxTheme::operator==(const SearchBoxTheme& rhs) const {
-  return bg == rhs.bg && icon == rhs.icon && placeholder == rhs.placeholder &&
+  return bg == rhs.bg && icon == rhs.icon &&
+         icon_selected == rhs.icon_selected && placeholder == rhs.placeholder &&
          results_bg == rhs.results_bg &&
          results_bg_hovered == rhs.results_bg_hovered &&
          results_bg_selected == rhs.results_bg_selected &&
-         results_dim == rhs.results_dim && results_text == rhs.results_text &&
-         results_url == rhs.results_url && text == rhs.text;
+         results_dim == rhs.results_dim &&
+         results_dim_selected == rhs.results_dim_selected &&
+         results_text == rhs.results_text &&
+         results_text_selected == rhs.results_text_selected &&
+         results_url == rhs.results_url &&
+         results_url_selected == rhs.results_url_selected && text == rhs.text;
 }
 
 NtpTheme::NtpTheme() = default;
diff --git a/chrome/common/search/instant_types.h b/chrome/common/search/instant_types.h
index ba35e23..7c5d595 100644
--- a/chrome/common/search/instant_types.h
+++ b/chrome/common/search/instant_types.h
@@ -53,13 +53,17 @@
 
   SkColor bg = gfx::kPlaceholderColor;
   SkColor icon = gfx::kPlaceholderColor;
+  SkColor icon_selected = gfx::kPlaceholderColor;
   SkColor placeholder = gfx::kPlaceholderColor;
   SkColor results_bg = gfx::kPlaceholderColor;
   SkColor results_bg_hovered = gfx::kPlaceholderColor;
   SkColor results_bg_selected = gfx::kPlaceholderColor;
   SkColor results_dim = gfx::kPlaceholderColor;
+  SkColor results_dim_selected = gfx::kPlaceholderColor;
   SkColor results_text = gfx::kPlaceholderColor;
+  SkColor results_text_selected = gfx::kPlaceholderColor;
   SkColor results_url = gfx::kPlaceholderColor;
+  SkColor results_url_selected = gfx::kPlaceholderColor;
   SkColor text = gfx::kPlaceholderColor;
 };
 
diff --git a/chrome/common/url_constants.cc b/chrome/common/url_constants.cc
index 0372181..3ae48827 100644
--- a/chrome/common/url_constants.cc
+++ b/chrome/common/url_constants.cc
@@ -300,6 +300,9 @@
 const char kAndroidAppsLearnMoreURL[] =
     "https://support.google.com/chromebook/?p=playapps";
 
+const char kArcAdbSideloadingLearnMoreURL[] =
+    "https://support.google.com/chromebook/?p=develop_android_apps";
+
 const char kArcExternalStorageLearnMoreURL[] =
     "https://support.google.com/chromebook?p=open_files";
 
diff --git a/chrome/common/url_constants.h b/chrome/common/url_constants.h
index 2b5cabda..86a6f14 100644
--- a/chrome/common/url_constants.h
+++ b/chrome/common/url_constants.h
@@ -244,6 +244,9 @@
 // The URL for the "learn more" link for Google Play Store (ARC) settings.
 extern const char kAndroidAppsLearnMoreURL[];
 
+// Help center URL for ARC ADB sideloading.
+extern const char kArcAdbSideloadingLearnMoreURL[];
+
 // The URL for the "Learn more" link in the External storage preferences
 // settings.
 extern const char kArcExternalStorageLearnMoreURL[];
diff --git a/chrome/renderer/pepper/pepper_flash_menu_host.cc b/chrome/renderer/pepper/pepper_flash_menu_host.cc
index 0b51bf1..21d401d5 100644
--- a/chrome/renderer/pepper/pepper_flash_menu_host.cc
+++ b/chrome/renderer/pepper/pepper_flash_menu_host.cc
@@ -7,7 +7,7 @@
 #include <stddef.h>
 
 #include "base/strings/utf_string_conversions.h"
-#include "content/public/common/context_menu_params.h"
+#include "content/public/common/untrustworthy_context_menu_params.h"
 #include "content/public/renderer/render_frame.h"
 #include "content/public/renderer/renderer_ppapi_host.h"
 #include "ipc/ipc_message.h"
diff --git a/chrome/renderer/plugins/chrome_plugin_placeholder.cc b/chrome/renderer/plugins/chrome_plugin_placeholder.cc
index 86eaf69..8a7d7402 100644
--- a/chrome/renderer/plugins/chrome_plugin_placeholder.cc
+++ b/chrome/renderer/plugins/chrome_plugin_placeholder.cc
@@ -26,7 +26,7 @@
 #include "chrome/renderer/plugins/plugin_uma.h"
 #include "components/strings/grit/components_strings.h"
 #include "content/public/common/content_switches.h"
-#include "content/public/common/context_menu_params.h"
+#include "content/public/common/untrustworthy_context_menu_params.h"
 #include "content/public/renderer/render_frame.h"
 #include "content/public/renderer/render_thread.h"
 #include "gin/object_template_builder.h"
diff --git a/chrome/renderer/searchbox/searchbox_extension.cc b/chrome/renderer/searchbox/searchbox_extension.cc
index d1e618f..cc8cdbc 100644
--- a/chrome/renderer/searchbox/searchbox_extension.cc
+++ b/chrome/renderer/searchbox/searchbox_extension.cc
@@ -377,6 +377,8 @@
   gin::DataObjectBuilder search_box(isolate);
   search_box.Set("bg", SkColorToArray(isolate, theme.search_box.bg));
   search_box.Set("icon", SkColorToArray(isolate, theme.search_box.icon));
+  search_box.Set("iconSelected",
+                 SkColorToArray(isolate, theme.search_box.icon_selected));
   search_box.Set("placeholder",
                  SkColorToArray(isolate, theme.search_box.placeholder));
   search_box.Set("resultsBg",
@@ -387,10 +389,19 @@
                  SkColorToArray(isolate, theme.search_box.results_bg_selected));
   search_box.Set("resultsDim",
                  SkColorToArray(isolate, theme.search_box.results_dim));
+  search_box.Set(
+      "resultsDimSelected",
+      SkColorToArray(isolate, theme.search_box.results_dim_selected));
   search_box.Set("resultsText",
                  SkColorToArray(isolate, theme.search_box.results_text));
+  search_box.Set(
+      "resultsTextSelected",
+      SkColorToArray(isolate, theme.search_box.results_text_selected));
   search_box.Set("resultsUrl",
                  SkColorToArray(isolate, theme.search_box.results_url));
+  search_box.Set(
+      "resultsUrlSelected",
+      SkColorToArray(isolate, theme.search_box.results_url_selected));
   search_box.Set("text", SkColorToArray(isolate, theme.search_box.text));
   builder.Set("searchBox", search_box.Build());
 
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 0c55d77b..280f051d 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -5098,6 +5098,7 @@
         sources += [
           "../browser/ui/webui/print_preview/local_printer_handler_chromeos_unittest.cc",
           "../browser/ui/webui/settings/chromeos/cups_printers_handler_unittest.cc",
+          "../browser/ui/webui/settings/chromeos/server_printer_url_util_unittest.cc",
         ]
       }
     } else {
diff --git a/chrome/test/android/BUILD.gn b/chrome/test/android/BUILD.gn
index 8ec7f35..4b60500 100644
--- a/chrome/test/android/BUILD.gn
+++ b/chrome/test/android/BUILD.gn
@@ -210,6 +210,7 @@
     "//chrome/browser/preferences:java",
     "//chrome/browser/ui/android/appmenu:java",
     "//chrome/browser/ui/android/appmenu:test_support_java",
+    "//chrome/browser/ui/messages/android:java",
     "//chrome/browser/util:java",
     "//components/bookmarks/common/android:bookmarks_java",
     "//components/browser_ui/widget/android:java",
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/ChromeActivityTestRule.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/ChromeActivityTestRule.java
index f9e405ece..4c30013 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/ChromeActivityTestRule.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/ChromeActivityTestRule.java
@@ -39,9 +39,9 @@
 import org.chromium.chrome.browser.init.ChromeBrowserInitializer;
 import org.chromium.chrome.browser.ntp.NewTabPage;
 import org.chromium.chrome.browser.omnibox.UrlBar;
+import org.chromium.chrome.browser.privacy.settings.PrivacyPreferencesManager;
 import org.chromium.chrome.browser.settings.SettingsActivity;
 import org.chromium.chrome.browser.settings.SettingsLauncher;
-import org.chromium.chrome.browser.settings.privacy.PrivacyPreferencesManager;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TabCreationState;
 import org.chromium.chrome.browser.tab.TabLaunchType;
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/TranslateUtil.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/TranslateUtil.java
index e8b6862..782f6f72 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/TranslateUtil.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/TranslateUtil.java
@@ -11,10 +11,10 @@
 
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.infobar.InfoBar;
-import org.chromium.chrome.browser.infobar.InfoBarCompactLayout;
 import org.chromium.chrome.browser.infobar.TranslateCompactInfoBar;
 import org.chromium.chrome.browser.infobar.translate.TranslateMenu;
 import org.chromium.chrome.browser.infobar.translate.TranslateTabLayout;
+import org.chromium.chrome.browser.ui.messages.infobar.InfoBarCompactLayout;
 import org.chromium.content_public.browser.test.util.Criteria;
 import org.chromium.content_public.browser.test.util.CriteriaHelper;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
diff --git a/chrome/test/chromedriver/chrome/web_view_impl.cc b/chrome/test/chromedriver/chrome/web_view_impl.cc
index d3fb25b..1b7ce01 100644
--- a/chrome/test/chromedriver/chrome/web_view_impl.cc
+++ b/chrome/test/chromedriver/chrome/web_view_impl.cc
@@ -1200,16 +1200,32 @@
   return std::unique_ptr<base::Value>(cast_tracker_->issue().DeepCopy());
 }
 
-WebViewImplHolder::WebViewImplHolder(WebViewImpl* web_view)
-    : web_view_(web_view), was_locked_(web_view && web_view->Lock()) {}
+WebViewImplHolder::WebViewImplHolder(WebViewImpl* web_view) {
+  // Lock input web view and all its parents, to prevent them from being
+  // deleted while still in use. Inside |items_|, each web view must appear
+  // before its parent. This ensures the destructor unlocks the web views in
+  // the right order.
+  while (web_view != nullptr) {
+    Item item;
+    item.web_view = web_view;
+    item.was_locked = web_view->Lock();
+    items_.push_back(item);
+    web_view = const_cast<WebViewImpl*>(web_view->GetParent());
+  }
+}
 
 WebViewImplHolder::~WebViewImplHolder() {
-  if (web_view_ != nullptr && !was_locked_) {
-    if (!web_view_->IsDetached())
-      web_view_->Unlock();
-    else if (web_view_->GetParent() != nullptr)
-      web_view_->GetParent()->GetFrameTracker()->DeleteTargetForFrame(
-          web_view_->GetId());
+  for (Item& item : items_) {
+    // Once we find a web view that is still locked, then all its parents must
+    // also be locked.
+    if (item.was_locked)
+      break;
+    WebViewImpl* web_view = item.web_view;
+    if (!web_view->IsDetached())
+      web_view->Unlock();
+    else if (web_view->GetParent() != nullptr)
+      web_view->GetParent()->GetFrameTracker()->DeleteTargetForFrame(
+          web_view->GetId());
   }
 }
 
diff --git a/chrome/test/chromedriver/chrome/web_view_impl.h b/chrome/test/chromedriver/chrome/web_view_impl.h
index c666d0a..0ec7ba6 100644
--- a/chrome/test/chromedriver/chrome/web_view_impl.h
+++ b/chrome/test/chromedriver/chrome/web_view_impl.h
@@ -226,8 +226,11 @@
   ~WebViewImplHolder();
 
  private:
-  WebViewImpl* web_view_;
-  bool was_locked_;
+  struct Item {
+    WebViewImpl* web_view;
+    bool was_locked;
+  };
+  std::vector<Item> items_;
 
   DISALLOW_COPY_AND_ASSIGN(WebViewImplHolder);
 };
diff --git a/chrome/test/chromedriver/server/chromedriver_server.cc b/chrome/test/chromedriver/server/chromedriver_server.cc
index bfb46b64..00f0c89 100644
--- a/chrome/test/chromedriver/server/chromedriver_server.cc
+++ b/chrome/test/chromedriver/server/chromedriver_server.cc
@@ -76,23 +76,53 @@
   return socket->ListenWithAddressAndPort(binding_ip, port, 5);
 }
 
-bool RequestIsSafeToServe(const net::HttpServerRequestInfo& info) {
+bool RequestIsSafeToServe(const net::HttpServerRequestInfo& info,
+                          bool allow_remote,
+                          const std::vector<net::IPAddress>& whitelisted_ips) {
   // To guard against browser-originating cross-site requests, when host header
-  // and/or origin header are present, serve only those coming from localhost.
-  std::string host_header = info.GetHeaderValue("host");
-  if (!host_header.empty()) {
-    GURL url = GURL("http://" + host_header);
-    if (!net::IsLocalhost(url)) {
-      LOG(ERROR) << "Rejecting request with host: " << host_header;
-      return false;
-    }
-  }
+  // and/or origin header are present, serve only those coming from localhost
+  // or from an explicitly whitelisted ip.
   std::string origin_header = info.GetHeaderValue("origin");
+  bool local_origin = false;
   if (!origin_header.empty()) {
     GURL url = GURL(origin_header);
-    if (!net::IsLocalhost(url)) {
-      LOG(ERROR) << "Rejecting request with origin: " << origin_header;
-      return false;
+    local_origin = net::IsLocalhost(url);
+    if (!local_origin) {
+      if (!allow_remote) {
+        LOG(ERROR)
+            << "Remote connections not allowed; rejecting request with origin: "
+            << origin_header;
+        return false;
+      }
+      if (!whitelisted_ips.empty()) {
+        net::IPAddress address = net::IPAddress();
+        if (!ParseURLHostnameToAddress(origin_header, &address)) {
+          LOG(ERROR) << "Unable to parse origin to IPAddress: "
+                     << origin_header;
+          return false;
+        }
+        if (!base::Contains(whitelisted_ips, address)) {
+          LOG(ERROR) << "Rejecting request with origin: " << origin_header;
+          return false;
+        }
+      }
+    }
+  }
+  // TODO https://crbug.com/chromedriver/3389
+  //  When remote access is allowed and origin is not specified,
+  // we should confirm that host is current machines ip or hostname
+
+  if (local_origin || !allow_remote) {
+    // when origin is localhost host must be localhost
+    // when origin is not set, and no remote access, host must be localhost
+    std::string host_header = info.GetHeaderValue("host");
+    if (!host_header.empty()) {
+      GURL url = GURL("http://" + host_header);
+      if (!net::IsLocalhost(url)) {
+        LOG(ERROR) << "Rejecting request with host: " << host_header
+                   << ". origin is " << origin_header;
+        return false;
+      }
     }
   }
   return true;
@@ -122,17 +152,19 @@
 class HttpServer : public net::HttpServer::Delegate {
  public:
   explicit HttpServer(const std::string& url_base,
+                      const std::vector<net::IPAddress>& whitelisted_ips,
                       const HttpRequestHandlerFunc& handle_request_func)
       : url_base_(url_base),
         handle_request_func_(handle_request_func),
-        allow_remote_(false) {}
+        allow_remote_(false),
+        whitelisted_ips_(whitelisted_ips) {}
 
-  ~HttpServer() override {}
+  ~HttpServer() override = default;
 
   int Start(uint16_t port, bool allow_remote, bool use_ipv4) {
     allow_remote_ = allow_remote;
     std::unique_ptr<net::ServerSocket> server_socket(
-        new net::TCPServerSocket(NULL, net::NetLogSource()));
+        new net::TCPServerSocket(nullptr, net::NetLogSource()));
     int status = use_ipv4
                      ? ListenOnIPv4(server_socket.get(), port, allow_remote)
                      : ListenOnIPv6(server_socket.get(), port, allow_remote);
@@ -154,11 +186,11 @@
 
   void OnHttpRequest(int connection_id,
                      const net::HttpServerRequestInfo& info) override {
-    if (!allow_remote_ && !RequestIsSafeToServe(info)) {
-      server_->Send500(
-          connection_id,
-          "Host header or origin header is specified and is not localhost.",
-          TRAFFIC_ANNOTATION_FOR_TESTS);
+    if (!RequestIsSafeToServe(info, allow_remote_, whitelisted_ips_)) {
+      server_->Send500(connection_id,
+                       "Host header or origin header is specified and is not "
+                       "whitelisted or localhost.",
+                       TRAFFIC_ANNOTATION_FOR_TESTS);
       return;
     }
     handle_request_func_.Run(
@@ -248,6 +280,7 @@
   std::unique_ptr<net::HttpServer> server_;
   std::map<int, std::string> connection_to_session_map;
   bool allow_remote_;
+  const std::vector<net::IPAddress> whitelisted_ips_;
   base::WeakPtrFactory<HttpServer> weak_factory_{this};  // Should be last.
 };
 
@@ -310,6 +343,7 @@
 void StartServerOnIOThread(uint16_t port,
                            bool allow_remote,
                            const std::string& url_base,
+                           const std::vector<net::IPAddress>& whitelisted_ips,
                            const HttpRequestHandlerFunc& handle_request_func) {
   std::unique_ptr<HttpServer> temp_server;
 
@@ -325,7 +359,8 @@
 // ensures that we successfully listen to both IPv4 and IPv6.
 
 #if defined(OS_MACOSX)
-  temp_server.reset(new HttpServer(url_base, handle_request_func));
+  temp_server.reset(
+      new HttpServer(url_base, whitelisted_ips, handle_request_func));
   int ipv4_status = temp_server->Start(port, allow_remote, true);
   if (ipv4_status == net::OK) {
     lazy_tls_server_ipv4.Pointer()->Set(temp_server.release());
@@ -341,7 +376,8 @@
   }
 #endif
 
-  temp_server.reset(new HttpServer(url_base, handle_request_func));
+  temp_server.reset(
+      new HttpServer(url_base, whitelisted_ips, handle_request_func));
   int ipv6_status = temp_server->Start(port, allow_remote, false);
   if (ipv6_status == net::OK) {
     lazy_tls_server_ipv6.Pointer()->Set(temp_server.release());
@@ -391,7 +427,8 @@
   if (need_ipv4 == NeedIPv4::NOT_NEEDED) {
     ipv4_status = ipv6_status;
   } else {
-    temp_server.reset(new HttpServer(url_base, handle_request_func));
+    temp_server.reset(
+        new HttpServer(url_base, whitelisted_ips, handle_request_func));
     ipv4_status = temp_server->Start(port, allow_remote, true);
     if (ipv4_status == net::OK) {
       lazy_tls_server_ipv4.Pointer()->Set(temp_server.release());
@@ -432,7 +469,7 @@
   io_thread.task_runner()->PostTask(
       FROM_HERE,
       base::BindOnce(
-          &StartServerOnIOThread, port, allow_remote, url_base,
+          &StartServerOnIOThread, port, allow_remote, url_base, whitelisted_ips,
           base::Bind(&HandleRequestOnIOThread, main_task_executor.task_runner(),
                      handle_request_func)));
   // Run the command loop. This loop is quit after the response for a shutdown
diff --git a/chrome/test/data/extensions/api_test/webstore_private/begin_install_fail_child.html b/chrome/test/data/extensions/api_test/webstore_private/install_blocked_child.html
similarity index 83%
rename from chrome/test/data/extensions/api_test/webstore_private/begin_install_fail_child.html
rename to chrome/test/data/extensions/api_test/webstore_private/install_blocked_child.html
index 49e4bad..b0892bf3 100644
--- a/chrome/test/data/extensions/api_test/webstore_private/begin_install_fail_child.html
+++ b/chrome/test/data/extensions/api_test/webstore_private/install_blocked_child.html
@@ -7,12 +7,12 @@
 <script>
 
 runTests([
-  function beginInstall() {
+  function installBlockedChild() {
     var manifest = getManifest();
     var expectedError =
-        "Apps and extensions can only be modified by the manager ()."
+        "Apps and extensions can only be modified by the manager (test_parent_0@google.com)."
     chrome.webstorePrivate.beginInstallWithManifest3(
-        {id: extensionId, manifest: manifest},
+        {id: appId, manifest: manifest},
         callbackFail(expectedError, function(result) {
       assertEq("blocked_for_child_account", result);
     }));
diff --git a/chrome/test/data/extensions/api_test/webstore_private/install_cancel_child.html b/chrome/test/data/extensions/api_test/webstore_private/install_cancel_child.html
new file mode 100644
index 0000000..777b320
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/webstore_private/install_cancel_child.html
@@ -0,0 +1,21 @@
+<!--
+ * 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.
+-->
+<script src="common.js"></script>
+<script>
+
+runTests([
+  function installCanceledChild() {
+    var manifest = getManifest();
+    chrome.webstorePrivate.beginInstallWithManifest3(
+        {id: appId, manifest: manifest},
+        function(result) {
+      assertEq(result, "user_cancelled");
+      succeed();
+    });
+  },
+]);
+
+</script>
diff --git a/chrome/test/data/extensions/api_test/webstore_private/install_child.html b/chrome/test/data/extensions/api_test/webstore_private/install_child.html
new file mode 100644
index 0000000..3c7487d0
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/webstore_private/install_child.html
@@ -0,0 +1,23 @@
+<!--
+ * 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.
+-->
+<script src="common.js"></script>
+<script>
+
+runTests([
+  function installChild() {
+    var manifest = getManifest();
+    chrome.webstorePrivate.beginInstallWithManifest3(
+        {id: appId, manifest: manifest},
+        callbackPass(function(result) {
+      assertEq("", result);
+
+      // Complete the installation
+      chrome.webstorePrivate.completeInstall(appId, callbackPass());
+    }));
+  },
+]);
+
+</script>
diff --git a/chrome/test/data/perf/throughput_test_cases/throughput_scroll.html b/chrome/test/data/perf/throughput_test_cases/throughput_scroll.html
new file mode 100644
index 0000000..8d700b4
--- /dev/null
+++ b/chrome/test/data/perf/throughput_test_cases/throughput_scroll.html
@@ -0,0 +1,78 @@
+<html>
+<style>
+.spacer {
+  width: 100%;
+  height: 10000px;
+  background: linear-gradient(to bottom,
+    #5d9634,
+    #5d9634 50%,
+    #538c2b 50%,
+    #538c2b
+  );
+  /* The rectangle in which to repeat.
+     It can be fully wide in this case */
+  background-size: 100% 50px;
+  text-align: right;
+}
+
+.scroller {
+  width: 200px;
+  height: 100px;
+  overflow: auto;
+}
+
+.composited {
+  will-change: transform;
+}
+
+.uncomposited {
+  position: absolute;
+  top: 400px;
+  clip: rect(0px, 200px, 200px, 50px);
+}
+
+</style>
+</head>
+<body>
+  <div class="scroller uncomposited">
+    <div class="spacer">Uncomposited</div>
+  </div>
+
+  <div class="scroller composited" id="composited">
+    <div class="spacer">Composited</div>
+  </div>
+
+  <div class="scroller composited" id="handler_passive">
+    <div class="spacer">Composited With Passive Handler</div>
+  </div>
+  <div class="scroller composited" id="handler_active">
+    <div class="spacer">Composited With Active Handler</div>
+  </div>
+
+  <script>
+    handler_passive.addEventListener('touchstart', ()=> {
+      console.log('Passive touchstart');
+      const start = new Date();
+      while ((new Date() - start) < 500) {}
+    }, {passive: true})
+
+    handler_passive.addEventListener('touchmove', ()=> {
+      console.log('Passive touchmove');
+      const start = new Date();
+      while ((new Date() - start) < 500) {}
+    }, {passive: true})
+
+    handler_active.addEventListener('touchstart', ()=> {
+      console.log('Blocking touchstart');
+      const start = new Date();
+      while ((new Date() - start) < 500) {}
+    }, {passive: false})
+
+    handler_active.addEventListener('touchmove', ()=> {
+      console.log('Blocking touchmove');
+      const start = new Date();
+      while ((new Date() - start) < 500) {}
+    }, {passive: false})
+  </script>
+</body>
+</html>
diff --git a/chrome/test/data/webui/BUILD.gn b/chrome/test/data/webui/BUILD.gn
index 792c146..0120e3c 100644
--- a/chrome/test/data/webui/BUILD.gn
+++ b/chrome/test/data/webui/BUILD.gn
@@ -48,6 +48,7 @@
     "$root_gen_dir/chrome/test/data/webui/cr_focus_row_behavior_test.m.js",
     "$root_gen_dir/chrome/test/data/webui/mock_controller.m.js",
     "$root_gen_dir/chrome/test/data/webui/settings/settings_animated_pages_test.m.js",
+    "$root_gen_dir/chrome/test/data/webui/settings/settings_ui_tests.m.js",
     "$root_gen_dir/chrome/test/data/webui/settings/test_util.m.js",
     "$root_gen_dir/chrome/test/data/webui/test_browser_proxy.m.js",
     "$root_gen_dir/chrome/test/data/webui/test_store.m.js",
@@ -252,6 +253,7 @@
     "$root_gen_dir/chrome/test/data/webui/settings/site_favicon_test.m.js",
     "$root_gen_dir/chrome/test/data/webui/settings/startup_urls_page_test.m.js",
     "$root_gen_dir/chrome/test/data/webui/settings/test_about_page_browser_proxy.m.js",
+    "$root_gen_dir/chrome/test/data/webui/settings/test_metrics_browser_proxy.m.js",
     "$root_gen_dir/chrome/test/data/webui/settings/sync_test_util.m.js",
     "$root_gen_dir/chrome/test/data/webui/settings/system_page_tests.m.js",
     "$root_gen_dir/chrome/test/data/webui/settings/test_extension_control_browser_proxy.m.js",
diff --git a/chrome/test/data/webui/settings/BUILD.gn b/chrome/test/data/webui/settings/BUILD.gn
index 439789a..a6a9c619d 100644
--- a/chrome/test/data/webui/settings/BUILD.gn
+++ b/chrome/test/data/webui/settings/BUILD.gn
@@ -49,6 +49,7 @@
     "settings_subpage_test.js",
     "settings_textarea_tests.js",
     "settings_toggle_button_tests.js",
+    "settings_ui_tests.js",
     "site_favicon_test.js",
     "startup_urls_page_test.js",
     "sync_test_util.js",
@@ -57,6 +58,7 @@
     "test_extension_control_browser_proxy.js",
     "test_languages_browser_proxy.js",
     "test_lifetime_browser_proxy.js",
+    "test_metrics_browser_proxy.js",
     "test_open_window_proxy.js",
     "test_password_manager_proxy.js",
     "test_reset_browser_proxy.js",
diff --git a/chrome/test/data/webui/settings/cr_settings_v3_interactive_ui_tests.js b/chrome/test/data/webui/settings/cr_settings_v3_interactive_ui_tests.js
index 467e588..22561ba 100644
--- a/chrome/test/data/webui/settings/cr_settings_v3_interactive_ui_tests.js
+++ b/chrome/test/data/webui/settings/cr_settings_v3_interactive_ui_tests.js
@@ -36,3 +36,15 @@
 TEST_F('CrSettingsAnimatedPagesV3Test', 'All', function() {
   mocha.run();
 });
+
+// eslint-disable-next-line no-var
+var SettingsUIV3InteractiveTest = class extends CrSettingsV3InteractiveUITest {
+  /** @override */
+  get browsePreload() {
+    return 'chrome://settings/test_loader.html?module=settings/settings_ui_tests.m.js';
+  }
+};
+
+TEST_F('SettingsUIV3InteractiveTest', 'All', function() {
+  mocha.run();
+});
diff --git a/chrome/test/data/webui/settings/secure_dns_interactive_test.js b/chrome/test/data/webui/settings/secure_dns_interactive_test.js
index a6dc867..a79f65e94 100644
--- a/chrome/test/data/webui/settings/secure_dns_interactive_test.js
+++ b/chrome/test/data/webui/settings/secure_dns_interactive_test.js
@@ -186,9 +186,10 @@
     Polymer.dom.flush();
     assertEquals(settings.SecureDnsMode.SECURE, secureDnsRadioGroup.selected);
     assertEquals(0, testElement.$$('#secureResolverSelect').selectedIndex);
-    assertTrue(testElement.$$('#privacyPolicy').hasAttribute('hidden'));
-    assertFalse(testElement.$$('#secureDnsInput').hasAttribute('hidden'));
-    assertFalse(testElement.$$('#secureDnsInput').matches(':focus-within'));
+    assertEquals(
+        'none', getComputedStyle(testElement.$$('#privacyPolicy')).display);
+    assertEquals(
+        'block', getComputedStyle(testElement.$$('#secureDnsInput')).display);
     assertEquals('custom', testElement.$$('#secureDnsInput').value);
   });
 
@@ -206,7 +207,8 @@
     const secureDnsInput = testElement.$$('#secureDnsInput');
 
     assertEquals(1, dropdownMenu.selectedIndex);
-    assertFalse(privacyPolicyLine.hasAttribute('hidden'));
+    assertEquals(
+        'block', getComputedStyle(testElement.$$('#privacyPolicy')).display);
     assertEquals(
         resolverList[1].policy, privacyPolicyLine.querySelector('a').href);
 
@@ -214,7 +216,8 @@
     dropdownMenu.value = resolverList[2].value;
     dropdownMenu.dispatchEvent(new Event('change'));
     assertEquals(2, dropdownMenu.selectedIndex);
-    assertFalse(privacyPolicyLine.hasAttribute('hidden'));
+    assertEquals(
+        'block', getComputedStyle(testElement.$$('#privacyPolicy')).display);
     assertEquals(
         resolverList[2].policy, privacyPolicyLine.querySelector('a').href);
     assertEquals(
@@ -225,7 +228,8 @@
     dropdownMenu.value = 'custom';
     dropdownMenu.dispatchEvent(new Event('change'));
     assertEquals(0, dropdownMenu.selectedIndex);
-    assertTrue(privacyPolicyLine.hasAttribute('hidden'));
+    assertEquals(
+        'none', getComputedStyle(testElement.$$('#privacyPolicy')).display);
     assertTrue(secureDnsInput.matches(':focus-within'));
     assertFalse(secureDnsInput.isInvalid());
     assertEquals(settings.SecureDnsMode.SECURE, secureDnsRadioGroup.selected);
@@ -271,7 +275,8 @@
     dropdownMenu.value = resolverList[3].value;
     dropdownMenu.dispatchEvent(new Event('change'));
     assertEquals(3, dropdownMenu.selectedIndex);
-    assertFalse(privacyPolicyLine.hasAttribute('hidden'));
+    assertEquals(
+        'block', getComputedStyle(testElement.$$('#privacyPolicy')).display);
     assertEquals(
         resolverList[3].policy, privacyPolicyLine.querySelector('a').href);
     assertEquals(
@@ -294,7 +299,8 @@
     Polymer.dom.flush();
     assertFalse(secureDnsRadioGroup.hidden);
     assertEquals(3, dropdownMenu.selectedIndex);
-    assertFalse(privacyPolicyLine.hasAttribute('hidden'));
+    assertEquals(
+        'block', getComputedStyle(testElement.$$('#privacyPolicy')).display);
     assertEquals(
         resolverList[3].policy, privacyPolicyLine.querySelector('a').href);
 
@@ -303,7 +309,8 @@
     assertFalse(secureDnsRadioGroup.hidden);
     assertEquals(settings.SecureDnsMode.SECURE, secureDnsRadioGroup.selected);
     assertEquals(3, dropdownMenu.selectedIndex);
-    assertFalse(privacyPolicyLine.hasAttribute('hidden'));
+    assertEquals(
+        'block', getComputedStyle(testElement.$$('#privacyPolicy')).display);
     assertEquals(
         resolverList[3].policy, privacyPolicyLine.querySelector('a').href);
     assertEquals(
@@ -323,7 +330,7 @@
     Polymer.dom.flush();
     const secureDnsRadioGroup = testElement.$$('#secureDnsRadioGroup');
     const secureDnsInput = testElement.$$('#secureDnsInput');
-    assertFalse(secureDnsInput.hasAttribute('hidden'));
+    assertEquals('block', getComputedStyle(secureDnsInput).display);
     assertFalse(secureDnsInput.matches(':focus-within'));
     assertFalse(secureDnsInput.isInvalid());
     assertEquals('https://dns.example/dns-query', secureDnsInput.value);
@@ -353,7 +360,7 @@
       managementMode: settings.SecureDnsUiManagementMode.NO_OVERRIDE,
     });
     Polymer.dom.flush();
-    assertFalse(secureDnsInput.hasAttribute('hidden'));
+    assertEquals('block', getComputedStyle(secureDnsInput).display);
     assertFalse(secureDnsInput.matches(':focus-within'));
     assertTrue(secureDnsInput.isInvalid());
     assertEquals(invalidEntry, secureDnsInput.value);
@@ -411,7 +418,7 @@
       managementMode: settings.SecureDnsUiManagementMode.NO_OVERRIDE,
     });
     Polymer.dom.flush();
-    assertFalse(secureDnsInput.hasAttribute('hidden'));
+    assertEquals('block', getComputedStyle(secureDnsInput).display);
     assertFalse(secureDnsInput.matches(':focus-within'));
     assertFalse(secureDnsInput.isInvalid());
     assertEquals(
diff --git a/chrome/test/data/webui/settings/settings_ui_tests.js b/chrome/test/data/webui/settings/settings_ui_tests.js
index 36a39bb..1d31997 100644
--- a/chrome/test/data/webui/settings/settings_ui_tests.js
+++ b/chrome/test/data/webui/settings/settings_ui_tests.js
@@ -2,6 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+// clang-format off
+// #import {CrSettingsPrefs, Router, routes} from 'chrome://settings/settings.js';
+// #import {eventToPromise} from 'chrome://test/test_util.m.js';
+// #import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+// clang-format on
+
 /** @fileoverview Suite of tests for the Settings layout. */
 suite('settings-ui', function() {
   let toolbar;
diff --git a/chrome/test/data/webui/settings/test_metrics_browser_proxy.js b/chrome/test/data/webui/settings/test_metrics_browser_proxy.js
index 27bbc8e..cac040c 100644
--- a/chrome/test/data/webui/settings/test_metrics_browser_proxy.js
+++ b/chrome/test/data/webui/settings/test_metrics_browser_proxy.js
@@ -2,8 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-/** @implements {settings.PrivacyPageBrowserProxy} */
-class TestMetricsBrowserProxy extends TestBrowserProxy {
+// #import {TestBrowserProxy} from 'chrome://test/test_browser_proxy.m.js';
+
+/** @implements {settings.MetricsBrowserProxy} */
+/* #export */ class TestMetricsBrowserProxy extends TestBrowserProxy {
   constructor() {
     super([
       'recordSettingsPageHistogram',
@@ -14,4 +16,4 @@
   recordSettingsPageHistogram(value) {
     this.methodCalled('recordSettingsPageHistogram', value);
   }
-}
\ No newline at end of file
+}
diff --git a/chrome/test/data/webui/test_browser_proxy.js b/chrome/test/data/webui/test_browser_proxy.js
index 7248787..b227946 100644
--- a/chrome/test/data/webui/test_browser_proxy.js
+++ b/chrome/test/data/webui/test_browser_proxy.js
@@ -99,7 +99,7 @@
   /**
    * Get number of times method is called.
    * @param {string} methodName
-   * @return {!boolean}
+   * @return {number}
    */
   getCallCount(methodName) {
     return this.getMethodData_(methodName).callCount;
diff --git a/chrome/updater/constants.cc b/chrome/updater/constants.cc
index 6fb403fd..7bf0ac6f 100644
--- a/chrome/updater/constants.cc
+++ b/chrome/updater/constants.cc
@@ -24,6 +24,7 @@
 const char kNoRateLimitSwitch[] = "no-rate-limit";
 const char kEnableLoggingSwitch[] = "enable-logging";
 const char kLoggingModuleSwitch[] = "vmodule";
+const char kSingleProcessSwitch[] = "single-process";
 
 // URLs.
 const char kUpdaterJSONDefaultUrl[] =
diff --git a/chrome/updater/constants.h b/chrome/updater/constants.h
index f260c50..1799efa 100644
--- a/chrome/updater/constants.h
+++ b/chrome/updater/constants.h
@@ -81,6 +81,11 @@
 // Specifies the logging module filter.
 extern const char kLoggingModuleSwitch[];
 
+// Specifies that the program uses a single process execution mode, meaning
+// out of process RPC is not being used. This mode is for debugging purposes
+// only and it may be removed at any time.
+extern const char kSingleProcessSwitch[];
+
 // URLs.
 //
 // Omaha server end point.
diff --git a/chrome/updater/update_apps_win.cc b/chrome/updater/update_apps_win.cc
index 35caa668..355b133 100644
--- a/chrome/updater/update_apps_win.cc
+++ b/chrome/updater/update_apps_win.cc
@@ -4,16 +4,20 @@
 
 #include "chrome/updater/update_apps.h"
 
+#include "base/command_line.h"
 #include "chrome/updater/configurator.h"
+#include "chrome/updater/constants.h"
 #include "chrome/updater/update_service_in_process.h"
+#include "chrome/updater/win/update_service_out_of_process.h"
 
 namespace updater {
 
 std::unique_ptr<UpdateService> CreateUpdateService(
     scoped_refptr<update_client::Configurator> config) {
-  // TODO(crbug.com/1048653): Try to connect to an existing OOP service. For
-  // now, run an in-process service.
-  return std::make_unique<UpdateServiceInProcess>(config);
+  if (base::CommandLine::ForCurrentProcess()->HasSwitch(kSingleProcessSwitch))
+    return std::make_unique<UpdateServiceInProcess>(config);
+  else
+    return std::make_unique<UpdateServiceOutOfProcess>();
 }
 
 }  // namespace updater
diff --git a/chrome/updater/updater.cc b/chrome/updater/updater.cc
index 1862a29d..304f9ee669 100644
--- a/chrome/updater/updater.cc
+++ b/chrome/updater/updater.cc
@@ -84,6 +84,11 @@
     CHECK(false) << "--crash-me was used.";
   }
 
+#if defined(OS_MACOSX)
+  // TODO(crbug.com/1060800) Consider implementing --single-process on macOS.
+  CHECK(!command_line->HasSwitch(kSingleProcessSwitch));
+#endif
+
   if (command_line->HasSwitch(kServerSwitch)) {
     return MakeAppServer()->Run();
   }
@@ -94,7 +99,7 @@
 
   if (command_line->HasSwitch(kInstallSwitch))
     return MakeAppInstall({kChromeAppId})->Run();
-#endif
+#endif  // OS_WIN
 
   if (command_line->HasSwitch(kUninstallSwitch))
     return MakeAppUninstall()->Run();
@@ -118,6 +123,7 @@
 
   InitLogging(*command_line);
 
+  VLOG(1) << "Command line: " << command_line->GetCommandLineString();
   if (command_line->HasSwitch(kCrashHandlerSwitch))
     return CrashReporterMain();
 
diff --git a/chrome/updater/win/BUILD.gn b/chrome/updater/win/BUILD.gn
index 6fa984c..ffee9ee 100644
--- a/chrome/updater/win/BUILD.gn
+++ b/chrome/updater/win/BUILD.gn
@@ -96,6 +96,8 @@
     "setup/setup_util.h",
     "setup/uninstall.cc",
     "setup/uninstall.h",
+    "update_service_out_of_process.cc",
+    "update_service_out_of_process.h",
   ]
 
   libs = [ "winhttp.lib" ]
diff --git a/chrome/updater/win/install_app.cc b/chrome/updater/win/install_app.cc
index eb43888..928db25d 100644
--- a/chrome/updater/win/install_app.cc
+++ b/chrome/updater/win/install_app.cc
@@ -10,6 +10,7 @@
 #include <vector>
 
 #include "base/bind.h"
+#include "base/command_line.h"
 #include "base/files/file_path.h"
 #include "base/i18n/icu_util.h"
 #include "base/logging.h"
@@ -752,6 +753,10 @@
 }
 
 scoped_refptr<App> MakeAppInstall(const std::string& app_id) {
+  // TODO(sorin) "--install" must be run with "--single-process" until
+  // crbug.com/1053729 is resolved.
+  DCHECK(
+      base::CommandLine::ForCurrentProcess()->HasSwitch(kSingleProcessSwitch));
   return base::MakeRefCounted<AppInstall>(app_id);
 }
 
diff --git a/chrome/updater/win/setup/setup.cc b/chrome/updater/win/setup/setup.cc
index 3564549..5a2817b 100644
--- a/chrome/updater/win/setup/setup.cc
+++ b/chrome/updater/win/setup/setup.cc
@@ -230,6 +230,10 @@
 
   base::CommandLine run_updater_ua_command(product_dir.Append(kUpdaterExe));
   run_updater_ua_command.AppendSwitch(kUpdateAppsSwitch);
+
+  // TODO(sorin) remove "single-process" when the updater COM client works.
+  // crbug.com/1053729.
+  run_updater_ua_command.AppendSwitch(kSingleProcessSwitch);
 #if !defined(NDEBUG)
   run_updater_ua_command.AppendSwitch(kEnableLoggingSwitch);
   run_updater_ua_command.AppendSwitchASCII(kLoggingModuleSwitch,
diff --git a/chrome/updater/win/update_service_out_of_process.cc b/chrome/updater/win/update_service_out_of_process.cc
new file mode 100644
index 0000000..82df517
--- /dev/null
+++ b/chrome/updater/win/update_service_out_of_process.cc
@@ -0,0 +1,48 @@
+// 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/updater/win/update_service_out_of_process.h"
+
+#include "base/callback.h"
+#include "base/logging.h"
+
+namespace updater {
+
+UpdateServiceOutOfProcess::UpdateServiceOutOfProcess() = default;
+
+UpdateServiceOutOfProcess::~UpdateServiceOutOfProcess() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+}
+
+void UpdateServiceOutOfProcess::RegisterApp(
+    const RegistrationRequest& request,
+    base::OnceCallback<void(const RegistrationResponse&)> callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  // TODO(sorin) the updater must be run with "--single-process" until
+  // crbug.com/1053729 is resolved.
+  NOTREACHED();
+}
+
+void UpdateServiceOutOfProcess::UpdateAll(
+    base::OnceCallback<void(Result)> callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  // TODO(sorin) the updater must be run with "--single-process" until
+  // crbug.com/1053729 is resolved.
+  NOTREACHED();
+}
+
+void UpdateServiceOutOfProcess::Update(const std::string& app_id,
+                                       UpdateService::Priority priority,
+                                       StateChangeCallback state_update,
+                                       base::OnceCallback<void(Result)> done) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  // TODO(sorin) the updater must be run with "--single-process" until
+  // crbug.com/1053729 is resolved.
+  NOTREACHED();
+}
+
+}  // namespace updater
diff --git a/chrome/updater/win/update_service_out_of_process.h b/chrome/updater/win/update_service_out_of_process.h
new file mode 100644
index 0000000..e0256810
--- /dev/null
+++ b/chrome/updater/win/update_service_out_of_process.h
@@ -0,0 +1,50 @@
+// 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_UPDATER_WIN_UPDATE_SERVICE_OUT_OF_PROCESS_H_
+#define CHROME_UPDATER_WIN_UPDATE_SERVICE_OUT_OF_PROCESS_H_
+
+#include <string>
+
+#include "base/callback_forward.h"
+#include "base/sequence_checker.h"
+#include "chrome/updater/update_service.h"
+
+namespace update_client {
+enum class Error;
+}  // namespace update_client
+
+namespace updater {
+
+using StateChangeCallback =
+    base::RepeatingCallback<void(updater::UpdateService::UpdateState)>;
+
+// All functions and callbacks must be called on the same sequence.
+class UpdateServiceOutOfProcess : public UpdateService {
+ public:
+  UpdateServiceOutOfProcess();
+  UpdateServiceOutOfProcess(const UpdateServiceOutOfProcess&) = delete;
+  UpdateServiceOutOfProcess& operator=(const UpdateServiceOutOfProcess&) =
+      delete;
+  ~UpdateServiceOutOfProcess() override;
+
+  // Overrides for updater::UpdateService.
+  // Update-checks all registered applications. Calls |callback| once the
+  // operation is complete.
+  void RegisterApp(
+      const RegistrationRequest& request,
+      base::OnceCallback<void(const RegistrationResponse&)> callback) override;
+  void UpdateAll(base::OnceCallback<void(Result)> callback) override;
+  void Update(const std::string& app_id,
+              Priority priority,
+              StateChangeCallback state_update,
+              base::OnceCallback<void(Result)> done) override;
+
+ private:
+  SEQUENCE_CHECKER(sequence_checker_);
+};
+
+}  // namespace updater
+
+#endif  // CHROME_UPDATER_WIN_UPDATE_SERVICE_OUT_OF_PROCESS_H_
diff --git a/chromecast/app/BUILD.gn b/chromecast/app/BUILD.gn
index fdcca6a..f927fda 100644
--- a/chromecast/app/BUILD.gn
+++ b/chromecast/app/BUILD.gn
@@ -54,7 +54,6 @@
   ]
 
   if (!is_fuchsia) {
-    # TODO(crbug.com/753619): Enable crash reporting on Fuchsia.
     deps += [
       "//chromecast/crash",
       "//components/crash/content/app",
@@ -98,7 +97,6 @@
 
   if (!is_fuchsia) {
     deps += [
-      # TODO(crbug.com/753619): Enable crash reporting on Fuchsia.
       ":cast_crash_client",
       "//chromecast/crash",
       "//chromecast/crash:test_support",
diff --git a/chromecast/browser/cast_browser_interface_binders.cc b/chromecast/browser/cast_browser_interface_binders.cc
index 0d09a484..47e54f6 100644
--- a/chromecast/browser/cast_browser_interface_binders.cc
+++ b/chromecast/browser/cast_browser_interface_binders.cc
@@ -32,7 +32,7 @@
   if (!web_contents)
     return;
   auto* cast_web_contents = CastWebContents::FromWebContents(web_contents);
-  if (!cast_web_contents)
+  if (!cast_web_contents || !cast_web_contents->can_bind_interfaces())
     return;
   auto interface_pipe = receiver.PassPipe();
   cast_web_contents->binder_registry()->TryBindInterface(
diff --git a/chromecast/browser/cast_web_contents.h b/chromecast/browser/cast_web_contents.h
index 7376fa73..c40e862fd 100644
--- a/chromecast/browser/cast_web_contents.h
+++ b/chromecast/browser/cast_web_contents.h
@@ -389,6 +389,10 @@
   // Returns true if mixer audio is enabled.
   virtual bool is_mixer_audio_enabled() = 0;
 
+  // Returns whether or not CastWebContents binder_registry() is valid for
+  // binding interfaces.
+  virtual bool can_bind_interfaces() = 0;
+
  private:
   DISALLOW_COPY_AND_ASSIGN(CastWebContents);
 };
diff --git a/chromecast/browser/cast_web_contents_impl.cc b/chromecast/browser/cast_web_contents_impl.cc
index d7160be..5b2e093 100644
--- a/chromecast/browser/cast_web_contents_impl.cc
+++ b/chromecast/browser/cast_web_contents_impl.cc
@@ -447,6 +447,13 @@
   return is_mixer_audio_enabled_;
 }
 
+bool CastWebContentsImpl::can_bind_interfaces() {
+  // We assume that the interface binders are owned by the delegate. This is a
+  // cheap trick so that all of the interfaces don't have to provide binder
+  // callbacks with WeakPtr.
+  return delegate_ != nullptr;
+}
+
 void CastWebContentsImpl::OnClosePageTimeout() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (!closing_ || stopped_) {
@@ -529,10 +536,7 @@
     mojo::ScopedMessagePipeHandle* interface_pipe) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-  if (!delegate_) {
-    // Don't let the page bind any more interfaces at this point, since the
-    // owning client has been torn down. This is a cheap trick so that all of
-    // the interfaces don't have to provide binder callbacks with WeakPtr.
+  if (!can_bind_interfaces()) {
     return;
   }
   if (binder_registry_.TryBindInterface(interface_name, interface_pipe)) {
diff --git a/chromecast/browser/cast_web_contents_impl.h b/chromecast/browser/cast_web_contents_impl.h
index 63b59c3..19ea2b0 100644
--- a/chromecast/browser/cast_web_contents_impl.h
+++ b/chromecast/browser/cast_web_contents_impl.h
@@ -85,6 +85,7 @@
   void RemoveObserver(Observer* observer) override;
   bool is_websql_enabled() override;
   bool is_mixer_audio_enabled() override;
+  bool can_bind_interfaces() override;
 
   // content::RenderProcessHostObserver implementation:
   void RenderProcessReady(content::RenderProcessHost* host) override;
diff --git a/chromecast/crash/BUILD.gn b/chromecast/crash/BUILD.gn
index a4bee04..eef21a6 100644
--- a/chromecast/crash/BUILD.gn
+++ b/chromecast/crash/BUILD.gn
@@ -5,71 +5,105 @@
 import("//chromecast/chromecast.gni")
 import("//testing/test.gni")
 
-# Crash reporting is not support on Fuchsia yet
-# TODO(crbug.com/753619): Enable crash reporting on Fuchsia.
-assert(!is_fuchsia)
+cast_source_set("crash_storage") {
+  sources = [
+    "cast_crash_keys.cc",
+    "cast_crash_keys.h",
+    "cast_crash_storage.h",
+  ]
+
+  if (is_fuchsia) {
+    sources += [
+      "cast_crash_storage_fuchsia.cc",
+      "fuchsia/cast_crash_storage_impl_fuchsia.cc",
+      "fuchsia/cast_crash_storage_impl_fuchsia.h",
+    ]
+
+    deps = [ "//base" ]
+  } else {
+    sources += [
+      "cast_crash_storage.cc",
+      "cast_crash_storage_impl.cc",
+      "cast_crash_storage_impl.h",
+    ]
+
+    deps = [
+      "//base",
+      "//components/crash/core/common",
+    ]
+  }
+}
 
 cast_source_set("crash") {
   sources = [
     "app_state_tracker.cc",
     "app_state_tracker.h",
-    "cast_crash_keys.cc",
-    "cast_crash_keys.h",
-    "cast_crashdump_uploader.cc",
-    "cast_crashdump_uploader.h",
-    "linux/crash_util.cc",
-    "linux/crash_util.h",
-    "linux/dummy_minidump_generator.cc",
-    "linux/dummy_minidump_generator.h",
-    "linux/dump_info.cc",
-    "linux/dump_info.h",
-    "linux/minidump_generator.h",
-    "linux/minidump_params.cc",
-    "linux/minidump_params.h",
-    "linux/minidump_uploader.cc",
-    "linux/minidump_uploader.h",
-    "linux/minidump_writer.cc",
-    "linux/minidump_writer.h",
-    "linux/synchronized_minidump_manager.cc",
-    "linux/synchronized_minidump_manager.h",
   ]
 
   deps = [
+    ":crash_storage",
     "//base",
-    "//chromecast/base",
-    "//chromecast/base:cast_sys_info_util",
-    "//chromecast/base:cast_version",
-    "//components/crash/core/common",
-    "//components/metrics",
-    "//components/prefs",
-    "//third_party/breakpad:client",
   ]
+
+  if (!is_fuchsia) {
+    sources += [
+      "cast_crashdump_uploader.cc",
+      "cast_crashdump_uploader.h",
+      "linux/crash_util.cc",
+      "linux/crash_util.h",
+      "linux/dummy_minidump_generator.cc",
+      "linux/dummy_minidump_generator.h",
+      "linux/dump_info.cc",
+      "linux/dump_info.h",
+      "linux/minidump_generator.h",
+      "linux/minidump_params.cc",
+      "linux/minidump_params.h",
+      "linux/minidump_uploader.cc",
+      "linux/minidump_uploader.h",
+      "linux/minidump_writer.cc",
+      "linux/minidump_writer.h",
+      "linux/synchronized_minidump_manager.cc",
+      "linux/synchronized_minidump_manager.h",
+    ]
+
+    deps += [
+      "//chromecast/base",
+      "//chromecast/base:cast_sys_info_util",
+      "//chromecast/base:cast_version",
+      "//components/crash/core/common",
+      "//components/metrics",
+      "//components/prefs",
+      "//third_party/breakpad:client",
+    ]
+  }
 }
 
-cast_executable("crash_uploader") {
-  sources = [ "linux/crash_uploader.cc" ]
+if (!is_fuchsia) {
+  cast_executable("crash_uploader") {
+    sources = [ "linux/crash_uploader.cc" ]
 
-  deps = [
-    ":crash",
-    "//base",
-    "//chromecast/base",
-    "//chromecast/base:default_create_sys_info",
-    "//chromecast/public",
-    "//chromecast/system/reboot:reboot_util",
-  ]
-}
+    deps = [
+      ":crash",
+      "//base",
+      "//chromecast/base",
+      "//chromecast/base:default_create_sys_info",
+      "//chromecast/public",
+      "//chromecast/system/reboot:reboot_util",
+    ]
+  }
 
-cast_source_set("test_support") {
-  sources = [
-    "linux/crash_testing_utils.cc",
-    "linux/crash_testing_utils.h",
-  ]
+  cast_source_set("test_support") {
+    sources = [
+      "linux/crash_testing_utils.cc",
+      "linux/crash_testing_utils.h",
+    ]
 
-  deps = [
-    ":crash",
-    "//base",
-    "//chromecast/base",
-  ]
+    deps = [
+      ":crash",
+      "//base",
+      "//chromecast/base",
+    ]
+  }
 }
 
 if (is_linux) {
diff --git a/chromecast/crash/app_state_tracker.cc b/chromecast/crash/app_state_tracker.cc
index f04b96d..090519e 100644
--- a/chromecast/crash/app_state_tracker.cc
+++ b/chromecast/crash/app_state_tracker.cc
@@ -5,8 +5,7 @@
 #include "chromecast/crash/app_state_tracker.h"
 
 #include "base/no_destructor.h"
-#include "chromecast/crash/cast_crash_keys.h"
-#include "components/crash/core/common/crash_key.h"
+#include "chromecast/crash/cast_crash_storage.h"
 
 namespace {
 
@@ -49,8 +48,7 @@
 // static
 void AppStateTracker::SetLastLaunchedApp(const std::string& app_id) {
   GetAppState()->last_launched_app = app_id;
-
-  crash_keys::last_app.Set(app_id);
+  CastCrashStorage::GetInstance()->SetLastLaunchedApp(app_id);
 }
 
 // static
@@ -59,20 +57,24 @@
   app_state->previous_app = app_state->current_app;
   app_state->current_app = app_id;
 
-  static crash_reporter::CrashKeyString<64> current_app("current_app");
-  current_app.Set(app_id);
+  CastCrashStorage::GetInstance()->SetCurrentApp(app_id);
+  CastCrashStorage::GetInstance()->SetPreviousApp(app_state->previous_app);
+}
 
-  crash_keys::previous_app.Set(app_state->previous_app);
+// static
+void AppStateTracker::SetPreviousApp(const std::string& app_id) {
+  GetAppState()->previous_app = app_id;
+  CastCrashStorage::GetInstance()->SetPreviousApp(app_id);
 }
 
 // static
 void AppStateTracker::SetStadiaSessionId(const std::string& stadia_session_id) {
   if (!stadia_session_id.empty()) {
     GetAppState()->stadia_session_id = stadia_session_id;
-    crash_keys::stadia_session_id.Set(stadia_session_id);
+    CastCrashStorage::GetInstance()->SetStadiaSessionId(stadia_session_id);
   } else {
     GetAppState()->stadia_session_id.clear();
-    crash_keys::stadia_session_id.Clear();
+    CastCrashStorage::GetInstance()->ClearStadiaSessionId();
   }
 }
 
diff --git a/chromecast/crash/app_state_tracker.h b/chromecast/crash/app_state_tracker.h
index 513d42c..389b576 100644
--- a/chromecast/crash/app_state_tracker.h
+++ b/chromecast/crash/app_state_tracker.h
@@ -17,6 +17,9 @@
   // The current app becomes the previous app, |app_id| becomes the current app.
   static void SetCurrentApp(const std::string& app_id);
 
+  // Record |app_id| as the previous app.
+  static void SetPreviousApp(const std::string& app_id);
+
   // Set the Stadia session ID, when a Stadia session starts running.
   // Clear the Stadia session ID by passing in an empty string
   static void SetStadiaSessionId(const std::string& stadia_session_id);
diff --git a/chromecast/crash/cast_crash_keys.cc b/chromecast/crash/cast_crash_keys.cc
index 0237a5c..45b1f32 100644
--- a/chromecast/crash/cast_crash_keys.cc
+++ b/chromecast/crash/cast_crash_keys.cc
@@ -7,11 +7,13 @@
 namespace chromecast {
 namespace crash_keys {
 
-crash_reporter::CrashKeyString<64> last_app("last_app");
+const char kLastApp[] = "last_app";
 
-crash_reporter::CrashKeyString<64> previous_app("previous_app");
+const char kCurrentApp[] = "current_app";
 
-crash_reporter::CrashKeyString<64> stadia_session_id("stadia_session_id");
+const char kPreviousApp[] = "previous_app";
+
+const char kStadiaSessionId[] = "stadia_session_id";
 
 }  // namespace crash_keys
 }  // namespace chromecast
diff --git a/chromecast/crash/cast_crash_keys.h b/chromecast/crash/cast_crash_keys.h
index 6f61531..9f7de161 100644
--- a/chromecast/crash/cast_crash_keys.h
+++ b/chromecast/crash/cast_crash_keys.h
@@ -5,16 +5,17 @@
 #ifndef CHROMECAST_CRASH_CAST_CRASH_KEYS_H_
 #define CHROMECAST_CRASH_CAST_CRASH_KEYS_H_
 
-#include "components/crash/core/common/crash_key.h"
-
 namespace chromecast {
 namespace crash_keys {
 
-extern crash_reporter::CrashKeyString<64> last_app;
+// Names of crash keys to be shared by multiple platforms.
+extern const char kLastApp[];
 
-extern crash_reporter::CrashKeyString<64> previous_app;
+extern const char kCurrentApp[];
 
-extern crash_reporter::CrashKeyString<64> stadia_session_id;
+extern const char kPreviousApp[];
+
+extern const char kStadiaSessionId[];
 
 }  // namespace crash_keys
 }  // namespace chromecast
diff --git a/chromecast/crash/cast_crash_storage.cc b/chromecast/crash/cast_crash_storage.cc
new file mode 100644
index 0000000..41eedc9
--- /dev/null
+++ b/chromecast/crash/cast_crash_storage.cc
@@ -0,0 +1,17 @@
+// 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 "chromecast/crash/cast_crash_storage.h"
+
+#include "base/no_destructor.h"
+#include "chromecast/crash/cast_crash_storage_impl.h"
+
+namespace chromecast {
+
+CastCrashStorage* CastCrashStorage::GetInstance() {
+  static base::NoDestructor<CastCrashStorageImpl> storage;
+  return storage.get();
+}
+
+}  // namespace chromecast
diff --git a/chromecast/crash/cast_crash_storage.h b/chromecast/crash/cast_crash_storage.h
new file mode 100644
index 0000000..c21838a
--- /dev/null
+++ b/chromecast/crash/cast_crash_storage.h
@@ -0,0 +1,37 @@
+// 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 CHROMECAST_CASH_CAST_CRASH_STORAGE_H_
+#define CHROMECAST_CASH_CAST_CRASH_STORAGE_H_
+
+#include "base/strings/string_piece.h"
+
+namespace chromecast {
+
+// Stores crash key annotations.
+// This is used to provide indirection for platform dependent storage
+// implementations.
+class CastCrashStorage {
+ public:
+  static CastCrashStorage* GetInstance();
+
+  CastCrashStorage() = default;
+  virtual ~CastCrashStorage() = default;
+
+  virtual void SetLastLaunchedApp(base::StringPiece app_id) = 0;
+  virtual void ClearLastLaunchedApp() = 0;
+
+  virtual void SetCurrentApp(base::StringPiece app_id) = 0;
+  virtual void ClearCurrentApp() = 0;
+
+  virtual void SetPreviousApp(base::StringPiece app_id) = 0;
+  virtual void ClearPreviousApp() = 0;
+
+  virtual void SetStadiaSessionId(base::StringPiece session_id) = 0;
+  virtual void ClearStadiaSessionId() = 0;
+};
+
+}  // namespace chromecast
+
+#endif  // CHROMECAST_CASH_CAST_CRASH_STORAGE_H_
diff --git a/chromecast/crash/cast_crash_storage_fuchsia.cc b/chromecast/crash/cast_crash_storage_fuchsia.cc
new file mode 100644
index 0000000..24ea02853
--- /dev/null
+++ b/chromecast/crash/cast_crash_storage_fuchsia.cc
@@ -0,0 +1,17 @@
+// 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 "chromecast/crash/cast_crash_storage.h"
+
+#include "base/no_destructor.h"
+#include "chromecast/crash/fuchsia/cast_crash_storage_impl_fuchsia.h"
+
+namespace chromecast {
+
+CastCrashStorage* CastCrashStorage::GetInstance() {
+  static base::NoDestructor<CastCrashStorageImplFuchsia> storage;
+  return storage.get();
+}
+
+}  // namespace chromecast
diff --git a/chromecast/crash/cast_crash_storage_impl.cc b/chromecast/crash/cast_crash_storage_impl.cc
new file mode 100644
index 0000000..68a5bbe
--- /dev/null
+++ b/chromecast/crash/cast_crash_storage_impl.cc
@@ -0,0 +1,56 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromecast/crash/cast_crash_storage_impl.h"
+
+#include "chromecast/crash/cast_crash_keys.h"
+#include "components/crash/core/common/crash_key.h"
+
+namespace chromecast {
+namespace {
+
+crash_reporter::CrashKeyString<64> last_app(crash_keys::kLastApp);
+crash_reporter::CrashKeyString<64> current_app(crash_keys::kCurrentApp);
+crash_reporter::CrashKeyString<64> previous_app(crash_keys::kPreviousApp);
+crash_reporter::CrashKeyString<64> stadia_session_id(
+    crash_keys::kStadiaSessionId);
+
+}  // namespace
+
+CastCrashStorageImpl::CastCrashStorageImpl() = default;
+CastCrashStorageImpl::~CastCrashStorageImpl() = default;
+
+void CastCrashStorageImpl::SetLastLaunchedApp(base::StringPiece app_id) {
+  last_app.Set(app_id);
+}
+
+void CastCrashStorageImpl::ClearLastLaunchedApp() {
+  last_app.Clear();
+}
+
+void CastCrashStorageImpl::SetCurrentApp(base::StringPiece app_id) {
+  current_app.Set(app_id);
+}
+
+void CastCrashStorageImpl::ClearCurrentApp() {
+  current_app.Clear();
+}
+
+void CastCrashStorageImpl::SetPreviousApp(base::StringPiece app_id) {
+  previous_app.Set(app_id);
+}
+
+void CastCrashStorageImpl::ClearPreviousApp() {
+  previous_app.Clear();
+}
+
+void CastCrashStorageImpl::SetStadiaSessionId(base::StringPiece session_id) {
+  stadia_session_id.Set(session_id);
+}
+
+void CastCrashStorageImpl::ClearStadiaSessionId() {
+  stadia_session_id.Clear();
+}
+
+}  // namespace chromecast
diff --git a/chromecast/crash/cast_crash_storage_impl.h b/chromecast/crash/cast_crash_storage_impl.h
new file mode 100644
index 0000000..bd2b5f6
--- /dev/null
+++ b/chromecast/crash/cast_crash_storage_impl.h
@@ -0,0 +1,32 @@
+// 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 CHROMECAST_CRASH_CAST_CRASH_STORAGE_DEFAULT_H_
+#define CHROMECAST_CRASH_CAST_CRASH_STORAGE_DEFAULT_H_
+
+#include "chromecast/crash/cast_crash_storage.h"
+
+namespace chromecast {
+
+class CastCrashStorageImpl : public CastCrashStorage {
+ public:
+  CastCrashStorageImpl();
+  ~CastCrashStorageImpl() final;
+  CastCrashStorageImpl& operator=(const CastCrashStorageImpl&) = delete;
+  CastCrashStorageImpl(const CastCrashStorageImpl&) = delete;
+
+  // CastCrashStorage implementation:
+  void SetLastLaunchedApp(base::StringPiece app_id) final;
+  void ClearLastLaunchedApp() final;
+  void SetCurrentApp(base::StringPiece app_id) final;
+  void ClearCurrentApp() final;
+  void SetPreviousApp(base::StringPiece app_id) final;
+  void ClearPreviousApp() final;
+  void SetStadiaSessionId(base::StringPiece session_id) final;
+  void ClearStadiaSessionId() final;
+};
+
+}  // namespace chromecast
+
+#endif  // CHROMECAST_CRASH_CAST_CRASH_STORAGE_DEFAULT_H_
diff --git a/chromecast/crash/fuchsia/cast_crash_storage_impl_fuchsia.cc b/chromecast/crash/fuchsia/cast_crash_storage_impl_fuchsia.cc
new file mode 100644
index 0000000..368c709d
--- /dev/null
+++ b/chromecast/crash/fuchsia/cast_crash_storage_impl_fuchsia.cc
@@ -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.
+
+#include "chromecast/crash/fuchsia/cast_crash_storage_impl_fuchsia.h"
+
+namespace chromecast {
+
+CastCrashStorageImplFuchsia::CastCrashStorageImplFuchsia() = default;
+CastCrashStorageImplFuchsia::~CastCrashStorageImplFuchsia() = default;
+
+void CastCrashStorageImplFuchsia::SetLastLaunchedApp(base::StringPiece app_id) {
+  // TODO(b/67907746): Implement.
+}
+
+void CastCrashStorageImplFuchsia::ClearLastLaunchedApp() {
+  // TODO(b/67907746): Implement.
+}
+
+void CastCrashStorageImplFuchsia::SetCurrentApp(base::StringPiece app_id) {
+  // TODO(b/67907746): Implement.
+}
+
+void CastCrashStorageImplFuchsia::ClearCurrentApp() {
+  // TODO(b/67907746): Implement.
+}
+
+void CastCrashStorageImplFuchsia::SetPreviousApp(base::StringPiece app_id) {
+  // TODO(b/67907746): Implement.
+}
+
+void CastCrashStorageImplFuchsia::ClearPreviousApp() {
+  // TODO(b/67907746): Implement.
+}
+
+void CastCrashStorageImplFuchsia::SetStadiaSessionId(
+    base::StringPiece session_id) {
+  // TODO(b/67907746): Implement.
+}
+
+void CastCrashStorageImplFuchsia::ClearStadiaSessionId() {
+  // TODO(b/67907746): Implement.
+}
+
+}  // namespace chromecast
diff --git a/chromecast/crash/fuchsia/cast_crash_storage_impl_fuchsia.h b/chromecast/crash/fuchsia/cast_crash_storage_impl_fuchsia.h
new file mode 100644
index 0000000..3734a9b2
--- /dev/null
+++ b/chromecast/crash/fuchsia/cast_crash_storage_impl_fuchsia.h
@@ -0,0 +1,33 @@
+// 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 CHROMECAST_CRASH_FUCHSIA_CAST_CRASH_STORAGE_IMPL_FUCHSIA_H_
+#define CHROMECAST_CRASH_FUCHSIA_CAST_CRASH_STORAGE_IMPL_FUCHSIA_H_
+
+#include "chromecast/crash/cast_crash_storage.h"
+
+namespace chromecast {
+
+class CastCrashStorageImplFuchsia : public CastCrashStorage {
+ public:
+  CastCrashStorageImplFuchsia();
+  ~CastCrashStorageImplFuchsia() final;
+  CastCrashStorageImplFuchsia& operator=(const CastCrashStorageImplFuchsia&) =
+      delete;
+  CastCrashStorageImplFuchsia(const CastCrashStorageImplFuchsia&) = delete;
+
+  // CastCrashStorage implementation:
+  void SetLastLaunchedApp(base::StringPiece app_id) final;
+  void ClearLastLaunchedApp() final;
+  void SetCurrentApp(base::StringPiece app_id) final;
+  void ClearCurrentApp() final;
+  void SetPreviousApp(base::StringPiece app_id) final;
+  void ClearPreviousApp() final;
+  void SetStadiaSessionId(base::StringPiece session_id) final;
+  void ClearStadiaSessionId() final;
+};
+
+}  // namespace chromecast
+
+#endif  // CHROMECAST_CRASH_FUCHSIA_CAST_CRASH_STORAGE_IMPL_FUCHSIA_H_
diff --git a/chromecast/media/cma/backend/cplay/cplay.cc b/chromecast/media/cma/backend/cplay/cplay.cc
index c86447fd..02e791e 100644
--- a/chromecast/media/cma/backend/cplay/cplay.cc
+++ b/chromecast/media/cma/backend/cplay/cplay.cc
@@ -53,6 +53,7 @@
   LOG(INFO) << "  -i input .wav file";
   LOG(INFO) << "  -o output .wav file";
   LOG(INFO) << "  -r output samples per second";
+  LOG(INFO) << "  -s saturate output";
   LOG(INFO) << " [-c cast_audio.json path]";
   LOG(INFO) << " [-v cast volume as fraction of 1 (0.0-1.0)]";
   LOG(INFO) << " [-d max duration (s)]";
@@ -62,6 +63,7 @@
   double cast_volume = 1.0;
   double duration_s = std::numeric_limits<double>::infinity();
   int output_samples_per_second = -1;
+  bool saturate_output = false;
   std::string device_id = "default";
   base::FilePath input_file_path;
   base::FilePath output_file_path;
@@ -149,7 +151,7 @@
 class OutputHandler {
  public:
   virtual ~OutputHandler() = default;
-  virtual void WriteData(float* data, int num_frames) = 0;
+  virtual void WriteData(float* data, int num_frames, bool saturate_output) = 0;
 };
 
 class WavOutputHandler : public OutputHandler {
@@ -179,12 +181,14 @@
                     sizeof(header_));
   }
 
-  void WriteData(float* data, int num_frames) override {
+  void WriteData(float* data, int num_frames, bool saturate_output) override {
     std::vector<float> clipped_data(num_frames * num_channels_);
     std::memcpy(clipped_data.data(), data,
                 clipped_data.size() * sizeof(clipped_data[0]));
-    for (size_t i = 0; i < clipped_data.size(); ++i) {
-      clipped_data[i] = base::ClampToRange(clipped_data[i], -1.0f, 1.0f);
+    if (saturate_output) {
+      for (size_t i = 0; i < clipped_data.size(); ++i) {
+        clipped_data[i] = base::ClampToRange(clipped_data[i], -1.0f, 1.0f);
+      }
     }
     wav_file_.WriteAtCurrentPos(reinterpret_cast<char*>(clipped_data.data()),
                                 sizeof(clipped_data[0]) * clipped_data.size());
@@ -261,6 +265,9 @@
       case 'r':
         params.output_samples_per_second = strtod(optarg, nullptr);
         break;
+      case 's':
+        params.saturate_output = true;
+        break;
       default:
         PrintHelp(argv[0]);
         exit(1);
@@ -337,7 +344,8 @@
              params.duration_s) {
     pipeline->MixAndFilter(kReadSize, MixerInput::RenderingDelay());
     audio_metrics.ProcessFrames(pipeline->GetOutput(), kReadSize);
-    output_handler_->WriteData(pipeline->GetOutput(), kReadSize);
+    output_handler_->WriteData(pipeline->GetOutput(), kReadSize,
+                               params.saturate_output);
     frames_written += kReadSize;
   }
 
diff --git a/chromecast/renderer/BUILD.gn b/chromecast/renderer/BUILD.gn
index 6d40ae8..d592638 100644
--- a/chromecast/renderer/BUILD.gn
+++ b/chromecast/renderer/BUILD.gn
@@ -65,6 +65,7 @@
     "//chromecast/common:queryable_data",
     "//chromecast/common/media",
     "//chromecast/common/mojom",
+    "//chromecast/crash",
     "//chromecast/media",
     "//chromecast/media/base:media_codec_support",
     "//components/network_hints/renderer",
@@ -79,11 +80,6 @@
     "//v8",
   ]
 
-  if (!is_fuchsia) {
-    # TODO(crbug.com/753619): Enable crash reporting on Fuchsia.
-    deps += [ "//chromecast/crash" ]
-  }
-
   if (!is_android) {
     sources += [
       "memory_pressure_observer_impl.cc",
diff --git a/chromecast/renderer/cast_content_renderer_client.cc b/chromecast/renderer/cast_content_renderer_client.cc
index 0896b82..dbd3b48 100644
--- a/chromecast/renderer/cast_content_renderer_client.cc
+++ b/chromecast/renderer/cast_content_renderer_client.cc
@@ -12,6 +12,7 @@
 #include "chromecast/base/bitstream_audio_codecs.h"
 #include "chromecast/base/cast_features.h"
 #include "chromecast/base/chromecast_switches.h"
+#include "chromecast/crash/app_state_tracker.h"
 #include "chromecast/media/base/media_codec_support.h"
 #include "chromecast/media/base/supported_codec_profile_levels_memo.h"
 #include "chromecast/public/media/media_capabilities_shlib.h"
@@ -48,10 +49,6 @@
 #include "chromecast/renderer/memory_pressure_observer_impl.h"
 #endif  // OS_ANDROID
 
-#if !defined(OS_FUCHSIA)
-#include "chromecast/crash/cast_crash_keys.h"
-#endif  // !defined(OS_FUCHSIA)
-
 #if BUILDFLAG(ENABLE_CHROMECAST_EXTENSIONS)
 #include "chromecast/common/cast_extensions_client.h"
 #include "chromecast/renderer/cast_extensions_renderer_client.h"
@@ -132,20 +129,17 @@
   memory_pressure_controller->AddObserver(std::move(memory_pressure_proxy));
 #endif
 
-#if !defined(OS_FUCHSIA)
-  // TODO(crbug.com/753619): Enable crash reporting on Fuchsia.
   base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
 
   std::string last_launched_app =
       command_line->GetSwitchValueNative(switches::kLastLaunchedApp);
   if (!last_launched_app.empty())
-    crash_keys::last_app.Set(last_launched_app);
+    AppStateTracker::SetLastLaunchedApp(last_launched_app);
 
   std::string previous_app =
       command_line->GetSwitchValueNative(switches::kPreviousApp);
   if (!previous_app.empty())
-    crash_keys::previous_app.Set(previous_app);
-#endif  // !defined(OS_FUCHSIA)
+    AppStateTracker::SetPreviousApp(previous_app);
 
 #if BUILDFLAG(ENABLE_CHROMECAST_EXTENSIONS)
   extensions_client_ = std::make_unique<extensions::CastExtensionsClient>();
diff --git a/chromeos/CHROMEOS_LKGM b/chromeos/CHROMEOS_LKGM
index cf2af3d..695e476 100644
--- a/chromeos/CHROMEOS_LKGM
+++ b/chromeos/CHROMEOS_LKGM
@@ -1 +1 @@
-12968.0.0
\ No newline at end of file
+12969.0.0
\ No newline at end of file
diff --git a/chromeos/components/sync_wifi/local_network_collector_impl.cc b/chromeos/components/sync_wifi/local_network_collector_impl.cc
index 2eee965..8e1d3f6 100644
--- a/chromeos/components/sync_wifi/local_network_collector_impl.cc
+++ b/chromeos/components/sync_wifi/local_network_collector_impl.cc
@@ -9,6 +9,7 @@
 #include "chromeos/components/sync_wifi/network_type_conversions.h"
 #include "chromeos/dbus/shill/shill_service_client.h"
 #include "chromeos/network/network_handler.h"
+#include "chromeos/network/network_metadata_store.h"
 #include "chromeos/network/network_state.h"
 #include "chromeos/network/network_state_handler.h"
 #include "components/device_event_log/device_event_log.h"
@@ -35,8 +36,10 @@
 }  // namespace
 
 LocalNetworkCollectorImpl::LocalNetworkCollectorImpl(
-    network_config::mojom::CrosNetworkConfig* cros_network_config)
-    : cros_network_config_(cros_network_config) {
+    network_config::mojom::CrosNetworkConfig* cros_network_config,
+    NetworkMetadataStore* network_metadata_store)
+    : cros_network_config_(cros_network_config),
+      network_metadata_store_(network_metadata_store) {
   cros_network_config_->AddObserver(
       cros_network_config_observer_receiver_.BindNewPipeAndPassRemote());
 
@@ -130,6 +133,12 @@
     return false;
   }
 
+  base::TimeDelta timestamp =
+      network_metadata_store_->GetLastConnectedTimestamp(network->guid);
+  if (timestamp.is_zero()) {
+    return false;
+  }
+
   return true;
 }
 
diff --git a/chromeos/components/sync_wifi/local_network_collector_impl.h b/chromeos/components/sync_wifi/local_network_collector_impl.h
index de8f982..f7aa0ae 100644
--- a/chromeos/components/sync_wifi/local_network_collector_impl.h
+++ b/chromeos/components/sync_wifi/local_network_collector_impl.h
@@ -23,6 +23,8 @@
 
 namespace chromeos {
 
+class NetworkMetadataStore;
+
 namespace sync_wifi {
 
 // Handles the retrieval, filtering, and conversion of local network
@@ -35,9 +37,10 @@
  public:
   // LocalNetworkCollector:
 
-  // |cros_network_config| must outlive this class.
+  // |cros_network_config| and |network_metadata_store| must outlive this class.
   LocalNetworkCollectorImpl(
-      network_config::mojom::CrosNetworkConfig* cros_network_config);
+      network_config::mojom::CrosNetworkConfig* cros_network_config,
+      NetworkMetadataStore* network_metadata_store);
   ~LocalNetworkCollectorImpl() override;
 
   // Can only execute one request at a time.
@@ -97,6 +100,7 @@
   mojo::Receiver<chromeos::network_config::mojom::CrosNetworkConfigObserver>
       cros_network_config_observer_receiver_{this};
   std::vector<network_config::mojom::NetworkStatePropertiesPtr> mojo_networks_;
+  NetworkMetadataStore* network_metadata_store_;
 
   base::flat_map<std::string, std::vector<sync_pb::WifiConfigurationSpecifics>>
       request_guid_to_complete_protos_;
diff --git a/chromeos/components/sync_wifi/local_network_collector_impl_unittest.cc b/chromeos/components/sync_wifi/local_network_collector_impl_unittest.cc
index 367a0bd..7517e85 100644
--- a/chromeos/components/sync_wifi/local_network_collector_impl_unittest.cc
+++ b/chromeos/components/sync_wifi/local_network_collector_impl_unittest.cc
@@ -18,6 +18,7 @@
 #include "chromeos/components/sync_wifi/network_type_conversions.h"
 #include "chromeos/components/sync_wifi/test_data_generator.h"
 #include "chromeos/dbus/shill/fake_shill_simulated_result.h"
+#include "chromeos/network/network_handler.h"
 #include "chromeos/services/network_config/cros_network_config.h"
 #include "chromeos/services/network_config/in_process_instance.h"
 #include "chromeos/services/network_config/public/cpp/cros_network_config_test_helper.h"
@@ -40,6 +41,7 @@
 const char kMangoSsid[] = "Mango";
 const char kAnnieSsid[] = "Annie";
 const char kOzzySsid[] = "Ozzy";
+const char kHopperSsid[] = "Hopper";
 
 }  // namespace
 
@@ -56,7 +58,8 @@
     testing::Test::SetUp();
     helper()->SetUp();
     local_network_collector_ = std::make_unique<LocalNetworkCollectorImpl>(
-        remote_cros_network_config_.get());
+        remote_cros_network_config_.get(),
+        NetworkHandler::Get()->network_metadata_store());
   }
 
   void TearDown() override {
@@ -102,7 +105,7 @@
 
 TEST_F(LocalNetworkCollectorImplTest, TestGetAllSyncableNetworks) {
   helper()->ConfigureWiFiNetwork(kFredSsid, /*is_secured=*/true,
-                                 /*in_profile=*/true);
+                                 /*in_profile=*/true, /*has_connected=*/true);
 
   std::vector<std::string> expected;
   expected.push_back(kFredSsid);
@@ -117,13 +120,15 @@
 TEST_F(LocalNetworkCollectorImplTest,
        TestGetAllSyncableNetworks_WithFiltering) {
   helper()->ConfigureWiFiNetwork(kFredSsid, /*is_secured=*/true,
-                                 /*in_profile=*/true);
+                                 /*in_profile=*/true, /*has_connected=*/true);
   helper()->ConfigureWiFiNetwork(kMangoSsid, /*is_secured=*/true,
-                                 /*in_profile=*/false);
+                                 /*in_profile=*/false, /*has_connected=*/true);
   helper()->ConfigureWiFiNetwork(kAnnieSsid, /*is_secured=*/false,
-                                 /*in_profile=*/true);
+                                 /*in_profile=*/true, /*has_connected=*/true);
   helper()->ConfigureWiFiNetwork(kOzzySsid, /*is_secured=*/true,
-                                 /*in_profile=*/true);
+                                 /*in_profile=*/true, /*has_connected=*/true);
+  helper()->ConfigureWiFiNetwork(kHopperSsid, /*is_secured=*/true,
+                                 /*in_profile=*/true, /*has_connected=*/false);
 
   std::vector<std::string> expected;
   expected.push_back(kFredSsid);
@@ -138,7 +143,7 @@
 
 TEST_F(LocalNetworkCollectorImplTest, TestGetSyncableNetwork) {
   helper()->ConfigureWiFiNetwork(kFredSsid, /*is_secured=*/true,
-                                 /*in_profile=*/true);
+                                 /*in_profile=*/true, /*has_connected=*/true);
 
   NetworkIdentifier id = GeneratePskNetworkId(kFredSsid);
   local_network_collector()->GetSyncableNetwork(
@@ -153,6 +158,16 @@
                          base::Unretained(this), std::string()));
 }
 
+TEST_F(LocalNetworkCollectorImplTest, TestGetSyncableNetwork_NeverConnected) {
+  helper()->ConfigureWiFiNetwork(kFredSsid, /*is_secured=*/true,
+                                 /*in_profile=*/true, /*has_connected=*/false);
+
+  NetworkIdentifier id = GeneratePskNetworkId(kFredSsid);
+  local_network_collector()->GetSyncableNetwork(
+      id, base::BindOnce(&LocalNetworkCollectorImplTest::OnGetSyncableNetwork,
+                         base::Unretained(this), std::string()));
+}
+
 }  // namespace sync_wifi
 
 }  // namespace chromeos
diff --git a/chromeos/components/sync_wifi/network_test_helper.cc b/chromeos/components/sync_wifi/network_test_helper.cc
index 2c569a2..5e988fbf 100644
--- a/chromeos/components/sync_wifi/network_test_helper.cc
+++ b/chromeos/components/sync_wifi/network_test_helper.cc
@@ -71,13 +71,16 @@
   NetworkHandler::Initialize();
   NetworkHandler::Get()->InitializePrefServices(&user_prefs_, &local_state_);
   network_state_helper_->ResetDevicesAndServices();
+  network_state_helper_->profile_test()->AddProfile(
+      network_state_helper_->UserHash(), std::string());
 
   base::RunLoop().RunUntilIdle();
 }
 
 void NetworkTestHelper::ConfigureWiFiNetwork(const std::string& ssid,
                                              bool is_secured,
-                                             bool in_profile) {
+                                             bool in_profile,
+                                             bool has_connected) {
   std::string security_entry =
       is_secured ? R"("SecurityClass": "psk", "Passphrase": "secretsauce", )"
                  : R"("SecurityClass": "none", )";
@@ -85,13 +88,20 @@
       in_profile ? base::StringPrintf(R"("Profile": "%s", )",
                                       network_state_helper_->UserHash())
                  : std::string();
-  network_state_helper_->ConfigureService(base::StringPrintf(
-      R"({"GUID": "%s_guid", "Type": "wifi", "SSID": "%s",
+  std::string service_path =
+      network_state_helper_->ConfigureService(base::StringPrintf(
+          R"({"GUID": "%s_guid", "Type": "wifi", "SSID": "%s",
             %s "State": "ready", "Strength": 100,
             %s "AutoConnect": true, "Connectable": true})",
-      ssid.c_str(), ssid.c_str(), security_entry.c_str(),
-      profile_entry.c_str()));
+          ssid.c_str(), ssid.c_str(), security_entry.c_str(),
+          profile_entry.c_str()));
+
   base::RunLoop().RunUntilIdle();
+
+  if (has_connected) {
+    NetworkHandler::Get()->network_metadata_store()->ConnectSucceeded(
+        service_path);
+  }
 }
 
 NetworkStateTestHelper* NetworkTestHelper::network_state_test_helper() {
diff --git a/chromeos/components/sync_wifi/network_test_helper.h b/chromeos/components/sync_wifi/network_test_helper.h
index ddee511..b2c4e4c 100644
--- a/chromeos/components/sync_wifi/network_test_helper.h
+++ b/chromeos/components/sync_wifi/network_test_helper.h
@@ -31,7 +31,8 @@
   void SetUp();
   void ConfigureWiFiNetwork(const std::string& ssid,
                             bool is_secured,
-                            bool in_profile);
+                            bool in_profile,
+                            bool has_connected);
 
   NetworkStateTestHelper* network_state_test_helper();
 
diff --git a/chromeos/components/sync_wifi/wifi_configuration_sync_service.cc b/chromeos/components/sync_wifi/wifi_configuration_sync_service.cc
index 40250a7..e14083f8 100644
--- a/chromeos/components/sync_wifi/wifi_configuration_sync_service.cc
+++ b/chromeos/components/sync_wifi/wifi_configuration_sync_service.cc
@@ -14,6 +14,7 @@
 #include "chromeos/components/sync_wifi/synced_network_updater_impl.h"
 #include "chromeos/components/sync_wifi/timer_factory.h"
 #include "chromeos/components/sync_wifi/wifi_configuration_bridge.h"
+#include "chromeos/network/network_handler.h"
 #include "chromeos/services/network_config/public/mojom/cros_network_config.mojom.h"
 #include "components/sync/base/report_unrecoverable_error.h"
 #include "components/sync/model/model_type_store.h"
@@ -33,7 +34,8 @@
       std::make_unique<PendingNetworkConfigurationTrackerImpl>(pref_service),
       remote_cros_network_config_.get(), std::make_unique<TimerFactory>());
   collector_ = std::make_unique<LocalNetworkCollectorImpl>(
-      remote_cros_network_config_.get());
+      remote_cros_network_config_.get(),
+      NetworkHandler::Get()->network_metadata_store());
   bridge_ = std::make_unique<sync_wifi::WifiConfigurationBridge>(
       updater_.get(), collector_.get(),
       std::make_unique<syncer::ClientTagBasedModelTypeProcessor>(
diff --git a/chromeos/services/assistant/service.cc b/chromeos/services/assistant/service.cc
index 0aa24d7..22df86fd 100644
--- a/chromeos/services/assistant/service.cc
+++ b/chromeos/services/assistant/service.cc
@@ -481,15 +481,6 @@
     return;
   }
 
-  // Work around a login crash for Active Directory accounts.
-  // TODO(https://crbug.com/1056717): Figure out if assistant is supposed to
-  // work for AD accounts. If not, we probably shouldn't create |this|.
-  if (!identity_manager_->HasPrimaryAccount(
-          signin::ConsentLevel::kNotRequired)) {
-    LOG(WARNING) << "No primary account info, stopping access token fetch.";
-    return;
-  }
-
   if (access_token_fetcher_) {
     LOG(WARNING) << "Access token already requested.";
     return;
diff --git a/chrome/android/java/res/drawable-hdpi/btn_close.png b/components/browser_ui/widget/android/java/res/drawable-hdpi/btn_close.png
similarity index 100%
rename from chrome/android/java/res/drawable-hdpi/btn_close.png
rename to components/browser_ui/widget/android/java/res/drawable-hdpi/btn_close.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-mdpi/btn_close.png b/components/browser_ui/widget/android/java/res/drawable-mdpi/btn_close.png
similarity index 100%
rename from chrome/android/java/res/drawable-mdpi/btn_close.png
rename to components/browser_ui/widget/android/java/res/drawable-mdpi/btn_close.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-xhdpi/btn_close.png b/components/browser_ui/widget/android/java/res/drawable-xhdpi/btn_close.png
similarity index 100%
rename from chrome/android/java/res/drawable-xhdpi/btn_close.png
rename to components/browser_ui/widget/android/java/res/drawable-xhdpi/btn_close.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-xxhdpi/btn_close.png b/components/browser_ui/widget/android/java/res/drawable-xxhdpi/btn_close.png
similarity index 100%
rename from chrome/android/java/res/drawable-xxhdpi/btn_close.png
rename to components/browser_ui/widget/android/java/res/drawable-xxhdpi/btn_close.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-xxxhdpi/btn_close.png b/components/browser_ui/widget/android/java/res/drawable-xxxhdpi/btn_close.png
similarity index 100%
rename from chrome/android/java/res/drawable-xxxhdpi/btn_close.png
rename to components/browser_ui/widget/android/java/res/drawable-xxxhdpi/btn_close.png
Binary files differ
diff --git a/components/contextual_search/core/browser/public.cc b/components/contextual_search/core/browser/public.cc
index b99638f..ca30524 100644
--- a/components/contextual_search/core/browser/public.cc
+++ b/components/contextual_search/core/browser/public.cc
@@ -23,7 +23,11 @@
 const int kContextualCardsTranslationsIntegration = 5;
 
 // For development.
-const int kContextualCardsDebugIntegration = 98;
 const int kContextualCardsDiagnosticIntegration = 99;
 
+// Mixin values. These are a bit mask * 100:
+const int kSimplifiedServerDeprecatedMixin = 100;
+const int kContextualCardsServerDebugMixin = 200;
+const int kExactSearchMixin = 400;
+
 }  // namespace contextual_search
diff --git a/components/contextual_search/core/browser/public.h b/components/contextual_search/core/browser/public.h
index 2f91d4b..345f719 100644
--- a/components/contextual_search/core/browser/public.h
+++ b/components/contextual_search/core/browser/public.h
@@ -30,12 +30,20 @@
 
 // Development-level CoCa integration codes.
 
-// For both client and server-side debugging.
-extern const int kContextualCardsDebugIntegration;
 // Generates diagnostics on the client and uses the ENTRYPOINT_UNSPECIFIED for
 // CoCa to return unlimited cards with diagnostics enabled.
 extern const int kContextualCardsDiagnosticIntegration;
 
+// Mixin values. You must choose only one of the above, but any combination
+// of these mixin values.
+
+// Deprecated value that might be sent by an old client.
+extern const int kSimplifiedServerDeprecatedMixin;
+// Activates server-side debugging.
+extern const int kContextualCardsServerDebugMixin;
+// Indicates that the current request is for an exact search.
+extern const int kExactSearchMixin;
+
 // Longpress resolve variations:
 extern const char kLongpressResolveParamName[];
 extern const char kLongpressResolvePreserveTap[];
diff --git a/components/domain_reliability/quic_error_mapping.cc b/components/domain_reliability/quic_error_mapping.cc
index 486faffb..be6d77c 100644
--- a/components/domain_reliability/quic_error_mapping.cc
+++ b/components/domain_reliability/quic_error_mapping.cc
@@ -350,6 +350,10 @@
     {quic::QUIC_HPACK_FRAGMENT_TOO_LONG, "quic.hpack.fragment_too_long"},
     {quic::QUIC_HPACK_COMPRESSED_HEADER_SIZE_EXCEEDS_LIMIT,
      "quic.hpack.compressed_header_size_exceeds_limit"},
+    {quic::QUIC_HTTP_INVALID_FRAME_SEQUENCE_ON_SPDY_STREAM,
+     "quic.http_invalid_frame_sequence_on_spdy_stream"},
+    {quic::QUIC_HTTP_INVALID_FRAME_SEQUENCE_ON_CONTROL_STREAM,
+     "quic.http_invalid_frame_sequence_on_control_stream"},
 
     // QUIC_INVALID_APPLICATION_CLOSE_DATA was code 101. The code has been
     // deprecated, but to keep the assert below happy, there needs to be
diff --git a/components/embedder_support/android/BUILD.gn b/components/embedder_support/android/BUILD.gn
index 16bfb6d..e3686c1 100644
--- a/components/embedder_support/android/BUILD.gn
+++ b/components/embedder_support/android/BUILD.gn
@@ -162,6 +162,7 @@
   deps = [
     ":context_menu_jni_headers",
     "//base",
+    "//content/public/browser",
     "//content/public/common",
   ]
 }
diff --git a/components/embedder_support/android/contextmenu/context_menu_builder.cc b/components/embedder_support/android/contextmenu/context_menu_builder.cc
index 8b8a98d..3c2b436 100644
--- a/components/embedder_support/android/contextmenu/context_menu_builder.cc
+++ b/components/embedder_support/android/contextmenu/context_menu_builder.cc
@@ -7,7 +7,7 @@
 #include "base/android/jni_android.h"
 #include "base/android/jni_string.h"
 #include "components/embedder_support/android/context_menu_jni_headers/ContextMenuParams_jni.h"
-#include "content/public/common/context_menu_params.h"
+#include "content/public/browser/context_menu_params.h"
 
 using base::android::ConvertUTF16ToJavaString;
 using base::android::ConvertUTF8ToJavaString;
diff --git a/components/omnibox/browser/autocomplete_controller.h b/components/omnibox/browser/autocomplete_controller.h
index 7556405..49414adb 100644
--- a/components/omnibox/browser/autocomplete_controller.h
+++ b/components/omnibox/browser/autocomplete_controller.h
@@ -167,6 +167,7 @@
                            SetSelectedLineWithNoDefaultMatches);
   FRIEND_TEST_ALL_PREFIXES(OmniboxPopupModelTest, TestFocusFixing);
   FRIEND_TEST_ALL_PREFIXES(OmniboxPopupModelTest, PopupPositionChanging);
+  FRIEND_TEST_ALL_PREFIXES(OmniboxPopupModelTest, PopupStepSelection);
   FRIEND_TEST_ALL_PREFIXES(OmniboxPopupContentsViewTest,
                            EmitSelectedChildrenChangedAccessibilityEvent);
 
diff --git a/components/omnibox/browser/autocomplete_result.cc b/components/omnibox/browser/autocomplete_result.cc
index 0e4d4945..6e9e396 100644
--- a/components/omnibox/browser/autocomplete_result.cc
+++ b/components/omnibox/browser/autocomplete_result.cc
@@ -523,12 +523,13 @@
   // expects to see a commonly visited URL as the default match, the URL is not
   // suppressed by type demotion.
   // However, we don't care about this URL behavior when the user is using the
-  // fakebox, which is intended to work more like a search-only box. Unless the
-  // user's input is a URL in which case we still want to ensure they can get a
-  // URL as the default match.
+  // fakebox/realbox, which is intended to work more like a search-only box.
+  // Unless the user's input is a URL in which case we still want to ensure they
+  // can get a URL as the default match.
   if ((input.current_page_classification() !=
-           OmniboxEventProto::INSTANT_NTP_WITH_FAKEBOX_AS_STARTING_FOCUS ||
-       input.type() == metrics::OmniboxInputType::URL)) {
+           OmniboxEventProto::INSTANT_NTP_WITH_FAKEBOX_AS_STARTING_FOCUS &&
+       input.current_page_classification() != OmniboxEventProto::NTP_REALBOX) ||
+      input.type() == metrics::OmniboxInputType::URL) {
     auto best = matches->end();
     for (auto it = matches->begin(); it != matches->end(); ++it) {
       if (it->allowed_to_be_default_match && !it->IsSubMatch() &&
diff --git a/components/omnibox/browser/autocomplete_result_unittest.cc b/components/omnibox/browser/autocomplete_result_unittest.cc
index f3dff82..a195ce41 100644
--- a/components/omnibox/browser/autocomplete_result_unittest.cc
+++ b/components/omnibox/browser/autocomplete_result_unittest.cc
@@ -145,6 +145,13 @@
                                  const TestData* expected,
                                  size_t expected_size);
 
+  void SortMatchesAndVerfiyOrder(
+      const std::string& input_text,
+      OmniboxEventProto::PageClassification page_classification,
+      const ACMatches& matches,
+      const std::vector<size_t>& expected_order,
+      const AutocompleteMatchTestData data[]);
+
   // Returns a (mock) AutocompleteProvider of given |provider_id|.
   MockAutocompleteProvider* GetProvider(int provider_id) {
     EXPECT_LT(provider_id, static_cast<int>(mock_provider_list_.size()));
@@ -231,6 +238,25 @@
   AssertResultMatches(current_result, expected, expected_size);
 }
 
+void AutocompleteResultTest::SortMatchesAndVerfiyOrder(
+    const std::string& input_text,
+    OmniboxEventProto::PageClassification page_classification,
+    const ACMatches& matches,
+    const std::vector<size_t>& expected_order,
+    const AutocompleteMatchTestData data[]) {
+  AutocompleteInput input(base::ASCIIToUTF16(input_text), page_classification,
+                          TestSchemeClassifier());
+  AutocompleteResult result;
+  result.AppendMatches(input, matches);
+  result.SortAndCull(input, template_url_service_.get());
+
+  ASSERT_EQ(expected_order.size(), result.size());
+  for (size_t i = 0; i < expected_order.size(); ++i) {
+    EXPECT_EQ(data[expected_order[i]].destination_url,
+              result.match_at(i)->destination_url.spec());
+  }
+}
+
 // Assertion testing for AutocompleteResult::Swap.
 TEST_F(AutocompleteResultTest, Swap) {
   AutocompleteResult r1;
@@ -1040,75 +1066,37 @@
   base::FieldTrialList::CreateFieldTrial(
       OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A");
 
-  {
-    AutocompleteInput input(base::ASCIIToUTF16("a"),
-                            OmniboxEventProto::HOME_PAGE,
-                            TestSchemeClassifier());
-    AutocompleteResult result;
-    result.AppendMatches(input, matches);
-    result.SortAndCull(input, template_url_service_.get());
+  // Because we want to ensure the highest naturally scoring
+  // allowed-to-be default suggestion is the default, make sure history-title
+  // is the default match despite demotion.
+  // Make sure history-URL is the last match due to the logic which groups
+  // searches and URLs together.
+  SortMatchesAndVerfiyOrder("a", OmniboxEventProto::HOME_PAGE, matches,
+                            {1, 2, 3, 0}, data);
 
-    // Because we want to ensure the highest naturally scoring
-    // allowed-to-be default suggestion is the default, make sure history-title
-    // is the default match despite demotion.
-    // Make sure history-URL is the last match due to the logic which groups
-    // searches and URLs together.
-    size_t expected_order[] = {1, 2, 3, 0};
+  // However, in the fakebox/realbox, we do want to use the demoted score when
+  // selecting the default match because we generally only expect it to be
+  // used for queries and we demote URLs strongly. So here we re-sort with a
+  // page classification of fakebox/realbox, and make sure history-title is now
+  // demoted. We also make sure history-URL is the last match due to the logic
+  // which groups searches and URLs together.
+  SortMatchesAndVerfiyOrder(
+      "a", OmniboxEventProto::INSTANT_NTP_WITH_FAKEBOX_AS_STARTING_FOCUS,
+      matches, {3, 2, 0, 1}, data);
+  SortMatchesAndVerfiyOrder("a", OmniboxEventProto::NTP_REALBOX, matches,
+                            {3, 2, 0, 1}, data);
 
-    ASSERT_EQ(base::size(expected_order), result.size());
-    for (size_t i = 0; i < base::size(expected_order); ++i) {
-      EXPECT_EQ(data[expected_order[i]].destination_url,
-                result.match_at(i)->destination_url.spec());
-    }
-  }
-
-  {
-    // However, in the fakebox, we do want to use the demoted score when
-    // selecting the default match because we generally only expect it to be
-    // used for queries and we demote URLs strongly. So here we re-sort with a
-    // page classification of fake-box, and make sure history-title is now
-    // demoted.
-    AutocompleteInput input(
-        base::ASCIIToUTF16("a"),
-        OmniboxEventProto::INSTANT_NTP_WITH_FAKEBOX_AS_STARTING_FOCUS,
-        TestSchemeClassifier());
-    AutocompleteResult result;
-    result.AppendMatches(input, matches);
-    result.SortAndCull(input, template_url_service_.get());
-
-    // Make sure history-URL is the last match due to the logic which groups
-    // searches and URLs together.
-    size_t expected_order[] = {3, 2, 0, 1};
-
-    ASSERT_EQ(base::size(expected_order), result.size());
-    for (size_t i = 0; i < base::size(expected_order); ++i) {
-      EXPECT_EQ(data[expected_order[i]].destination_url,
-                result.match_at(i)->destination_url.spec());
-    }
-  }
-
-  {
-    // Unless, the user's input looks like a URL, in which case we want to use
-    // the natural scoring again to make sure the user gets a URL if they're
-    // clearly trying to navigate. So here we re-sort with a page classification
-    // of fake-box and an input that's a URL, and make sure history-title is
-    // once again the default match.
-    AutocompleteInput input(
-        base::ASCIIToUTF16("www.example.com"),
-        OmniboxEventProto::INSTANT_NTP_WITH_FAKEBOX_AS_STARTING_FOCUS,
-        TestSchemeClassifier());
-    AutocompleteResult result;
-    result.AppendMatches(input, matches);
-    result.SortAndCull(input, template_url_service_.get());
-
-    size_t expected_order[] = {1, 2, 3, 0};
-
-    ASSERT_EQ(base::size(expected_order), result.size());
-    for (size_t i = 0; i < base::size(expected_order); ++i) {
-      EXPECT_EQ(data[expected_order[i]].destination_url,
-                result.match_at(i)->destination_url.spec());
-    }
-  }
+  // Unless, the user's input looks like a URL, in which case we want to use
+  // the natural scoring again to make sure the user gets a URL if they're
+  // clearly trying to navigate. So here we re-sort with a page classification
+  // of fakebox/realbox and an input that's a URL, and make sure history-title
+  // is once again the default match.
+  SortMatchesAndVerfiyOrder(
+      "www.example.com",
+      OmniboxEventProto::INSTANT_NTP_WITH_FAKEBOX_AS_STARTING_FOCUS, matches,
+      {1, 2, 3, 0}, data);
+  SortMatchesAndVerfiyOrder("www.example.com", OmniboxEventProto::NTP_REALBOX,
+                            matches, {1, 2, 3, 0}, data);
 }
 
 TEST_F(AutocompleteResultTest, SortAndCullReorderForDefaultMatch) {
diff --git a/components/omnibox/browser/omnibox_edit_model.cc b/components/omnibox/browser/omnibox_edit_model.cc
index 509b8ce..529e742a 100644
--- a/components/omnibox/browser/omnibox_edit_model.cc
+++ b/components/omnibox/browser/omnibox_edit_model.cc
@@ -1204,6 +1204,8 @@
 }
 
 void OmniboxEditModel::OnUpOrDownKeyPressed(int count) {
+  DCHECK(count == -1 || count == 1);
+
   // NOTE: This purposefully doesn't trigger any code that resets paste_state_.
   if (PopupIsOpen()) {
     // The popup is open, so the user should be able to interact with it
@@ -1216,17 +1218,30 @@
     // Reverting, however, does not make sense for on-focus suggestions
     // (user_input_in_progress_ is false) unless the first result is a
     // verbatim match of the omnibox input (on-focus query refinements on SERP).
-    const size_t line_no = GetNewSelectedLine(count);
-    if (result().default_match() && has_temporary_text_ && line_no == 0 &&
+    const auto next_selection = popup_model()->GetNextSelection(
+        count > 0 ? OmniboxPopupModel::kForward : OmniboxPopupModel::kBackward,
+        OmniboxPopupModel::kWholeLine);
+    if (result().default_match() && has_temporary_text_ &&
+        next_selection.line == 0 &&
         (user_input_in_progress_ ||
          result().default_match()->IsVerbatimType())) {
       RevertTemporaryTextAndPopup();
     } else {
-      popup_model()->MoveTo(line_no);
+      popup_model()->SetSelection(next_selection);
     }
     return;
   }
 
+  MaybeStartQueryForPopup();
+
+  // TODO(pkasting): Here, the popup could be working on a query but is not
+  // open. In that case, we should force it to open immediately.
+}
+
+bool OmniboxEditModel::MaybeStartQueryForPopup() {
+  if (PopupIsOpen()) {
+    return false;
+  }
   if (!query_in_progress()) {
     // The popup is neither open nor working on a query already.  So, start an
     // autocomplete query for the current text.  This also sets
@@ -1239,11 +1254,9 @@
     if (!user_input_in_progress_)
       InternalSetUserText(url_for_editing_);
     view_->UpdatePopup();
-    return;
+    return true;
   }
-
-  // TODO(pkasting): The popup is working on a query but is not open.  We should
-  // force it to open immediately.
+  return false;
 }
 
 void OmniboxEditModel::OnPopupDataChanged(const base::string16& text,
@@ -1693,11 +1706,3 @@
 
   client_->OnFocusChanged(focus_state_, reason);
 }
-
-size_t OmniboxEditModel::GetNewSelectedLine(int count) {
-  int line_no = (static_cast<int>(popup_model()->selected_line()) + count) %
-                static_cast<int>(popup_model()->result().size());
-  if (line_no < 0)
-    line_no += popup_model()->result().size();
-  return line_no;
-}
diff --git a/components/omnibox/browser/omnibox_edit_model.h b/components/omnibox/browser/omnibox_edit_model.h
index 862781f1..36cbe5f 100644
--- a/components/omnibox/browser/omnibox_edit_model.h
+++ b/components/omnibox/browser/omnibox_edit_model.h
@@ -351,6 +351,10 @@
   // negative for moving up, positive for moving down. Virtual for testing.
   virtual void OnUpOrDownKeyPressed(int count);
 
+  // If no query is in progress, starts working on an autocomplete query.
+  // Returns true if started; false otherwise.
+  bool MaybeStartQueryForPopup();
+
   // Called when any relevant data changes.  This rolls together several
   // separate pieces of data into one call so we can update all the UI
   // efficiently:
@@ -484,11 +488,6 @@
   // the view.
   void SetFocusState(OmniboxFocusState state, OmniboxFocusChangeReason reason);
 
-  // Calculates the new selected line based on |count|, how many
-  // suggestions are currently in the results, and any features
-  // that are enabled.
-  size_t GetNewSelectedLine(int count);
-
   // NOTE: |client_| must outlive |omnibox_controller_|, as the latter has a
   // reference to the former.
   std::unique_ptr<OmniboxClient> client_;
diff --git a/components/omnibox/browser/omnibox_field_trial.cc b/components/omnibox/browser/omnibox_field_trial.cc
index 3db6a7f..8acf64bf14 100644
--- a/components/omnibox/browser/omnibox_field_trial.cc
+++ b/components/omnibox/browser/omnibox_field_trial.cc
@@ -311,8 +311,10 @@
       demotion_rule = "1:61,2:61,3:61,4:61,16:61,24:61";
 #endif
     if (current_page_classification ==
-        OmniboxEventProto::INSTANT_NTP_WITH_FAKEBOX_AS_STARTING_FOCUS)
+            OmniboxEventProto::INSTANT_NTP_WITH_FAKEBOX_AS_STARTING_FOCUS ||
+        current_page_classification == OmniboxEventProto::NTP_REALBOX) {
       demotion_rule = "1:10,2:10,3:10,4:10,5:10,16:10,17:10,24:10";
+    }
   }
 
   // The value of the DemoteByType rule is a comma-separated list of
diff --git a/components/omnibox/browser/omnibox_popup_model.cc b/components/omnibox/browser/omnibox_popup_model.cc
index d9d6e93..8be6328 100644
--- a/components/omnibox/browser/omnibox_popup_model.cc
+++ b/components/omnibox/browser/omnibox_popup_model.cc
@@ -15,6 +15,7 @@
 #include "components/omnibox/browser/autocomplete_match_type.h"
 #include "components/omnibox/browser/omnibox_client.h"
 #include "components/omnibox/browser/omnibox_edit_controller.h"
+#include "components/omnibox/browser/omnibox_field_trial.h"
 #include "components/omnibox/browser/omnibox_popup_view.h"
 #include "components/omnibox/common/omnibox_features.h"
 #include "third_party/icu/source/common/unicode/ubidi.h"
@@ -27,6 +28,26 @@
 #endif
 
 ///////////////////////////////////////////////////////////////////////////////
+// OmniboxPopupModel::Selection
+
+bool OmniboxPopupModel::Selection::operator==(const Selection& b) const {
+  return line == b.line && state == b.state;
+}
+
+bool OmniboxPopupModel::Selection::operator!=(const Selection& b) const {
+  return !operator==(b);
+}
+
+bool OmniboxPopupModel::Selection::IsChangeToKeyword(Selection from) const {
+  return state == KEYWORD && from.state != KEYWORD;
+}
+
+OmniboxPopupModel::Selection OmniboxPopupModel::Selection::With(
+    LineState new_state) const {
+  return Selection(line, new_state);
+}
+
+///////////////////////////////////////////////////////////////////////////////
 // OmniboxPopupModel
 
 const size_t OmniboxPopupModel::kNoMatch = static_cast<size_t>(-1);
@@ -102,6 +123,47 @@
   }
 }
 
+// static
+// Defines forward and backward ordering for possible line states.
+OmniboxPopupModel::LineState OmniboxPopupModel::GetNextLineState(
+    LineState state,
+    Direction direction) {
+  switch (direction) {
+    case kForward:
+      switch (state) {
+        case NO_STATE:
+          return NORMAL;
+        case NORMAL:
+          return KEYWORD;
+        case KEYWORD:
+          return BUTTON_FOCUSED;
+        case BUTTON_FOCUSED:
+          return NO_STATE;
+        default:
+          break;
+      }
+      break;
+    case kBackward:
+      switch (state) {
+        case NO_STATE:
+          return BUTTON_FOCUSED;
+        case NORMAL:
+          return NO_STATE;
+        case KEYWORD:
+          return NORMAL;
+        case BUTTON_FOCUSED:
+          return KEYWORD;
+        default:
+          break;
+      }
+      break;
+    default:
+      break;
+  }
+  NOTREACHED();
+  return OmniboxPopupModel::NO_STATE;
+}
+
 bool OmniboxPopupModel::IsOpen() const {
   return view_->IsOpen();
 }
@@ -129,7 +191,7 @@
   // edit notifies its controller that something has changed, the controller
   // can get the correct updated data.
   const size_t prev_selected_line = selected_line();
-  SetSelection(Selection(line, NORMAL));
+  selection_ = Selection(line, NORMAL);
   view_->OnSelectedLineChanged(prev_selected_line, selected_line());
 
   if (line == kNoMatch)
@@ -161,13 +223,6 @@
   view_->OnDragCanceled();
 }
 
-void OmniboxPopupModel::MoveTo(size_t new_line) {
-  if (result().empty())
-    return;
-
-  SetSelectedLine(new_line, false, false);
-}
-
 void OmniboxPopupModel::SetSelectedLineState(LineState state) {
   DCHECK(!result().empty());
   DCHECK_NE(kNoMatch, selected_line());
@@ -179,8 +234,7 @@
   if (state == BUTTON_FOCUSED)
     old_focused_url_ = current_destination;
 
-  // selected_line_state_ = state;
-  SetSelection(Selection(selected_line(), state));
+  selection_ = Selection(selected_line(), state);
   view_->InvalidateLine(selected_line());
 
   if (state == BUTTON_FOCUSED) {
@@ -247,9 +301,9 @@
     if (!has_focused_match || has_changed) {
       selection.state = NORMAL;
     }
-    SetSelection(selection);
+    selection_ = selection;
   } else {
-    SetSelection(Selection(kNoMatch, NORMAL));
+    selection_ = Selection(kNoMatch, NORMAL);
   }
 
   bool popup_was_open = view_->IsOpen();
@@ -315,8 +369,114 @@
          result().match_at(selected_line()).IsTabSwitchSuggestion();
 }
 
+OmniboxPopupModel::Selection OmniboxPopupModel::GetNextSelection(
+    Direction direction,
+    Step step) const {
+  if (result().empty()) {
+    return selection_;
+  }
+  Selection next = selection_;
+  const bool skip_keyword =
+      !OmniboxFieldTrial::IsExperimentalKeywordModeEnabled() &&
+      step == kStateOrNothing;
+
+  // This block handles state transitions within the current line.
+  if (step == kStateOrLine || step == kStateOrNothing) {
+    LineState next_state =
+        GetNextAvailableLineState(next, direction, skip_keyword);
+    if (next_state != NO_STATE) {
+      next.state = next_state;
+      return next;
+    }
+    if (step == kStateOrNothing) {
+      return next;
+    }
+  }
+
+  // The rest handles stepping to other lines.
+  const int size = result().size();
+  const int line =
+      step == kAllLines
+          ? (direction == kForward ? (size - 1) : 0)
+          : ((next.line + (direction == kForward ? 1 : (size - 1))) % size);
+  next.line = line;
+  LineState next_state = GetNextAvailableLineState(
+      Selection(line, NO_STATE), (step != kStateOrLine) ? kForward : direction,
+      skip_keyword);
+  if (!OmniboxFieldTrial::IsSuggestionButtonRowEnabled() &&
+      (step == kStateOrLine) && direction != kForward &&
+      next_state == KEYWORD) {
+    // When semi-stepping backward with no button row, skip over keyword.
+    next_state =
+        GetNextAvailableLineState(next.With(KEYWORD), direction, skip_keyword);
+  }
+  next.state = next_state;
+  return next;
+}
+
+OmniboxPopupModel::Selection OmniboxPopupModel::StepSelection(
+    Direction direction,
+    Step step) {
+  SetSelection(GetNextSelection(direction, step));
+  return selection_;
+}
+
+OmniboxPopupModel::Selection OmniboxPopupModel::ClearSelectionState() {
+  // This is subtle. DCHECK in SetSelectedLineState will fail if there are no
+  // results, which can happen when the popup gets closed. In that case, though,
+  // the state is left as NORMAL.
+  if (selection_.state != NORMAL) {
+    SetSelectedLineState(NORMAL);
+  }
+  return selection_;
+}
+
+bool OmniboxPopupModel::IsSelectionAvailable(Selection selection) const {
+  if (selection.line >= result().size()) {
+    return false;
+  }
+  const auto& match = result().match_at(selection.line);
+  switch (selection.state) {
+    case OmniboxPopupModel::NO_STATE:
+      return false;
+    case OmniboxPopupModel::NORMAL:
+      return true;
+    case OmniboxPopupModel::KEYWORD:
+      return match.associated_keyword != nullptr;
+    case OmniboxPopupModel::BUTTON_FOCUSED:
+      // TODO(orinj): Here is an opportunity to clean up the presentational
+      //  logic that pkasting wanted to take out of AutocompleteMatch. The view
+      //  should be driven by the model, so this is really the place to decide.
+      return match.ShouldShowTabMatchButton();
+    default:
+      break;
+  }
+  NOTREACHED();
+  return false;
+}
+
 void OmniboxPopupModel::SetSelection(Selection selection) {
-  selection_ = selection;
+  if (selection.line != selection_.line) {
+    SetSelectedLine(selection.line, false, false);
+  }
+  if (selection.state != selection_.state) {
+    SetSelectedLineState(selection.state);
+  }
+}
+
+OmniboxPopupModel::LineState OmniboxPopupModel::GetNextAvailableLineState(
+    Selection from,
+    Direction direction,
+    bool skip_keyword) const {
+  Selection to = from;
+  do {
+    to.state = GetNextLineState(to.state, direction);
+    if (skip_keyword && to.state == KEYWORD) {
+      to.state = GetNextLineState(to.state, direction);
+    }
+  } while (to.state != OmniboxPopupModel::NO_STATE &&
+           !IsSelectionAvailable(to));
+  return to.state;
 }
 
 void OmniboxPopupModel::OnFaviconFetched(const GURL& page_url,
diff --git a/components/omnibox/browser/omnibox_popup_model.h b/components/omnibox/browser/omnibox_popup_model.h
index 013278e..e6b156e 100644
--- a/components/omnibox/browser/omnibox_popup_model.h
+++ b/components/omnibox/browser/omnibox_popup_model.h
@@ -28,11 +28,41 @@
 
 class OmniboxPopupModel {
  public:
+  // Directions for stepping through selections. These may apply for going
+  // up/down by lines or cycling left/right through states within a line.
+  enum Direction { kForward, kBackward };
+
+  // When changing selections, these are the possible stepping behaviors.
+  enum Step {
+    // Step by an entire line regardless of line state.
+    kWholeLine,
+
+    // Step by a state if another one is available on the current line;
+    // otherwise step by line.
+    kStateOrLine,
+
+    // Step by a state if another one is available on the current line;
+    // otherwise do not step.
+    kStateOrNothing,
+
+    // Step across all lines to the first or last line.
+    kAllLines
+  };
+
+  // The sentinel value for Selection::line which means no line is selected.
+  static const size_t kNoMatch;
+
   // See |Selection::state| below for details.
   enum LineState {
-    NORMAL = 0,
+    NORMAL,
     KEYWORD,
-    BUTTON_FOCUSED
+    BUTTON_FOCUSED,
+
+    // NO_STATE logically indicates unavailability of a state, and is
+    // only used internally. NO_STATE values are not persisted in members,
+    // are not returned from public methods, and should not be used by
+    // other classes.
+    NO_STATE
   };
 
   struct Selection {
@@ -48,6 +78,16 @@
     LineState state;
 
     Selection(size_t line, LineState state) : line(line), state(state) {}
+
+    bool operator==(const Selection&) const;
+    bool operator!=(const Selection&) const;
+
+    // Returns true if going to this selection from given |from| selection
+    // results in activation of keyword state when it wasn't active before.
+    bool IsChangeToKeyword(Selection from) const;
+
+    // Derive another selection as copy of this one but with given state change.
+    Selection With(LineState new_state) const;
   };
 
   OmniboxPopupModel(OmniboxPopupView* popup_view, OmniboxEditModel* edit_model);
@@ -75,6 +115,9 @@
                                     int* contents_max_width,
                                     int* description_max_width);
 
+  // Fully defines forward and backward ordering for all possible line states.
+  static LineState GetNextLineState(LineState state, Direction direction);
+
   // Returns true if the popup is currently open.
   bool IsOpen() const;
 
@@ -110,12 +153,6 @@
   // will reset the popup to the initial state.
   void ResetToInitialState();
 
-  // Immediately updates and opens the popup if necessary, then moves the
-  // current selection to the respective line. If the line is unchanged, the
-  // selection will be unchanged, but the popup will still redraw and modify
-  // the text in the OmniboxEditModel.
-  void MoveTo(size_t new_line);
-
   // If the selected line has both a normal match and a keyword match, this can
   // be used to choose which to select.  This allows the user to toggle between
   // normal and keyword mode with tab/shift-tab without rerunning autocomplete
@@ -167,12 +204,31 @@
 
   OmniboxEditModel* edit_model() { return edit_model_; }
 
-  // The token value for selected_line_ and functions dealing with a "line
-  // number" that indicates "no line".
-  static const size_t kNoMatch;
+  // Advances selection with consideration for both line number and line state.
+  // |direction| indicates direction of step, and |step| determines what kind
+  // of step to take. Returns the next selection, which could be anything.
+  Selection GetNextSelection(Direction direction, Step step) const;
+
+  // Applies the next selection as provided by GetNextSelection.
+  Selection StepSelection(Direction direction, Step step);
+
+  // Applies a given selection. Use GetNextSelection instead of constructing
+  // a selection from scratch.
+  void SetSelection(Selection selection);
+
+  // Preserves current selection line but resets it to default state.
+  // Returns new selection.
+  Selection ClearSelectionState();
 
  private:
-  void SetSelection(Selection selection);
+  // Returns true if the |selection| is available according to result matches.
+  bool IsSelectionAvailable(Selection selection) const;
+
+  // Returns the next line state that can be applied for given |from| selection,
+  // with |direction| indicating the direction of step. May return NO_STATE.
+  LineState GetNextAvailableLineState(Selection from,
+                                      Direction direction,
+                                      bool skip_keyword) const;
 
   void OnFaviconFetched(const GURL& page_url, const gfx::Image& icon);
 
diff --git a/components/omnibox/browser/omnibox_popup_model_unittest.cc b/components/omnibox/browser/omnibox_popup_model_unittest.cc
index 62aeb19..2bcc6c8 100644
--- a/components/omnibox/browser/omnibox_popup_model_unittest.cc
+++ b/components/omnibox/browser/omnibox_popup_model_unittest.cc
@@ -157,6 +157,87 @@
   }
 }
 
+TEST_F(OmniboxPopupModelTest, PopupStepSelection) {
+  ACMatches matches;
+  for (size_t i = 0; i < 3; ++i) {
+    AutocompleteMatch match(nullptr, 1000, false,
+                            AutocompleteMatchType::URL_WHAT_YOU_TYPED);
+    match.keyword = base::ASCIIToUTF16("match");
+    match.allowed_to_be_default_match = true;
+    matches.push_back(match);
+  }
+  // Give one match an associated keyword for irregular state stepping.
+  matches.back().associated_keyword =
+      std::make_unique<AutocompleteMatch>(matches.back());
+  auto* result = &model()->autocomplete_controller()->result_;
+  AutocompleteInput input(base::UTF8ToUTF16("match"),
+                          metrics::OmniboxEventProto::NTP,
+                          TestSchemeClassifier());
+  result->AppendMatches(input, matches);
+  result->SortAndCull(input, nullptr);
+  popup_model()->OnResultChanged();
+  EXPECT_EQ(0u, model()->popup_model()->selected_line());
+
+  // Step by lines forward.
+  for (size_t n : {1, 2, 0}) {
+    popup_model()->StepSelection(OmniboxPopupModel::kForward,
+                                 OmniboxPopupModel::kWholeLine);
+    EXPECT_EQ(n, model()->popup_model()->selected_line());
+  }
+  // Step by lines backward.
+  for (size_t n : {2, 1, 0}) {
+    popup_model()->StepSelection(OmniboxPopupModel::kBackward,
+                                 OmniboxPopupModel::kWholeLine);
+    EXPECT_EQ(n, model()->popup_model()->selected_line());
+  }
+  // Step by states forward.
+  for (auto selection : {
+           OmniboxPopupModel::Selection(1, OmniboxPopupModel::NORMAL),
+           OmniboxPopupModel::Selection(2, OmniboxPopupModel::NORMAL),
+           OmniboxPopupModel::Selection(2, OmniboxPopupModel::KEYWORD),
+           OmniboxPopupModel::Selection(0, OmniboxPopupModel::NORMAL),
+       }) {
+    popup_model()->StepSelection(OmniboxPopupModel::kForward,
+                                 OmniboxPopupModel::kStateOrLine);
+    EXPECT_EQ(selection, model()->popup_model()->selection());
+  }
+  // Step by states backward.
+  // Note the lack of KEYWORD. This is by design. Stepping forward
+  // should land on KEYWORD, but stepping backward should not.
+  for (auto selection : {
+           OmniboxPopupModel::Selection(2, OmniboxPopupModel::NORMAL),
+           OmniboxPopupModel::Selection(1, OmniboxPopupModel::NORMAL),
+           OmniboxPopupModel::Selection(0, OmniboxPopupModel::NORMAL),
+           OmniboxPopupModel::Selection(2, OmniboxPopupModel::NORMAL),
+       }) {
+    popup_model()->StepSelection(OmniboxPopupModel::kBackward,
+                                 OmniboxPopupModel::kStateOrLine);
+    EXPECT_EQ(selection, model()->popup_model()->selection());
+  }
+
+  // Try some kStateOrNothing steps on the keyword line.
+  // Note that keyword mode is specially excepted with this
+  // step behavior.
+  popup_model()->StepSelection(OmniboxPopupModel::kBackward,
+                               OmniboxPopupModel::kStateOrNothing);
+  EXPECT_EQ(OmniboxPopupModel::Selection(2, OmniboxPopupModel::NORMAL),
+            model()->popup_model()->selection());
+  popup_model()->StepSelection(OmniboxPopupModel::kForward,
+                               OmniboxPopupModel::kStateOrNothing);
+  EXPECT_EQ(OmniboxPopupModel::Selection(2, OmniboxPopupModel::NORMAL),
+            model()->popup_model()->selection());
+
+  // Try the kAllLines step behavior.
+  popup_model()->StepSelection(OmniboxPopupModel::kBackward,
+                               OmniboxPopupModel::kAllLines);
+  EXPECT_EQ(OmniboxPopupModel::Selection(0, OmniboxPopupModel::NORMAL),
+            model()->popup_model()->selection());
+  popup_model()->StepSelection(OmniboxPopupModel::kForward,
+                               OmniboxPopupModel::kAllLines);
+  EXPECT_EQ(OmniboxPopupModel::Selection(2, OmniboxPopupModel::NORMAL),
+            model()->popup_model()->selection());
+}
+
 TEST_F(OmniboxPopupModelTest, ComputeMatchMaxWidths) {
   int contents_max_width, description_max_width;
   const int separator_width = 10;
diff --git a/components/renderer_context_menu/context_menu_content_type.h b/components/renderer_context_menu/context_menu_content_type.h
index 696397d..6a19c969 100644
--- a/components/renderer_context_menu/context_menu_content_type.h
+++ b/components/renderer_context_menu/context_menu_content_type.h
@@ -6,7 +6,7 @@
 #define COMPONENTS_RENDERER_CONTEXT_MENU_CONTEXT_MENU_CONTENT_TYPE_H_
 
 #include "base/macros.h"
-#include "content/public/common/context_menu_params.h"
+#include "content/public/browser/context_menu_params.h"
 #include "ui/base/models/simple_menu_model.h"
 
 namespace content {
diff --git a/components/renderer_context_menu/render_view_context_menu_base.h b/components/renderer_context_menu/render_view_context_menu_base.h
index baaa73a..8183506 100644
--- a/components/renderer_context_menu/render_view_context_menu_base.h
+++ b/components/renderer_context_menu/render_view_context_menu_base.h
@@ -19,7 +19,7 @@
 #include "components/renderer_context_menu/context_menu_content_type.h"
 #include "components/renderer_context_menu/render_view_context_menu_observer.h"
 #include "components/renderer_context_menu/render_view_context_menu_proxy.h"
-#include "content/public/common/context_menu_params.h"
+#include "content/public/browser/context_menu_params.h"
 #include "ppapi/buildflags/buildflags.h"
 #include "ui/base/models/simple_menu_model.h"
 #include "ui/base/page_transition_types.h"
diff --git a/components/subresource_filter/android/BUILD.gn b/components/subresource_filter/android/BUILD.gn
new file mode 100644
index 0000000..c3969bd
--- /dev/null
+++ b/components/subresource_filter/android/BUILD.gn
@@ -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.
+
+import("//build/config/android/rules.gni")
+
+generate_jni("subresource_filter_jni_headers") {
+  sources = [ "java/src/org/chromium/components/subresource_filter/SubresourceFilterFeatureList.java" ]
+}
+
+android_library("java") {
+  sources = [ "java/src/org/chromium/components/subresource_filter/SubresourceFilterFeatureList.java" ]
+  deps = [
+    ":subresource_filter_jni_headers",
+    "//base:base_java",
+    "//base:jni_java",
+  ]
+  annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
+}
+
+source_set("android") {
+  sources = [ "subresource_filter_feature_list.cc" ]
+  deps = [
+    ":subresource_filter_jni_headers",
+    "//base",
+    "//components/subresource_filter/core/browser",
+  ]
+}
diff --git a/components/subresource_filter/android/java/src/org/chromium/components/subresource_filter/SubresourceFilterFeatureList.java b/components/subresource_filter/android/java/src/org/chromium/components/subresource_filter/SubresourceFilterFeatureList.java
new file mode 100644
index 0000000..717a2572
--- /dev/null
+++ b/components/subresource_filter/android/java/src/org/chromium/components/subresource_filter/SubresourceFilterFeatureList.java
@@ -0,0 +1,59 @@
+// 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.components.subresource_filter;
+
+import org.chromium.base.annotations.JNINamespace;
+import org.chromium.base.annotations.MainDex;
+import org.chromium.base.annotations.NativeMethods;
+import org.chromium.base.library_loader.LibraryLoader;
+
+/**
+ * Provides an API for querying the status of subresource_filter component Features.
+ */
+// TODO(crbug.com/1060097): Remove/update this once a generalized FeatureList exists.
+@JNINamespace("subresource_filter")
+@MainDex
+public class SubresourceFilterFeatureList {
+    public static final String SUBRESOURCE_FILTER = "SubresourceFilter";
+
+    private SubresourceFilterFeatureList() {}
+
+    /**
+     * Returns whether the specified feature is enabled or not.
+     *
+     * Note: Features queried through this API must be added to the array
+     * |kFeaturesExposedToJava| in
+     * //components/subresource_filter/core/browser/subresource_filter_feature_list.cc.
+     *
+     * @param featureName The name of the feature to query.
+     * @return Whether the feature is enabled or not.
+     */
+    public static boolean isEnabled(String featureName) {
+        assert isNativeInitialized();
+        return SubresourceFilterFeatureListJni.get().isEnabled(featureName);
+    }
+
+    /**
+     * @return Whether the native FeatureList is initialized or not.
+     */
+    private static boolean isNativeInitialized() {
+        if (!LibraryLoader.getInstance().isInitialized()) return false;
+        // Even if the native library is loaded, the C++ FeatureList might not be initialized yet.
+        // In that case, accessing it will not immediately fail, but instead cause a crash later
+        // when it is initialized. Return whether the native FeatureList has been initialized,
+        // so the return value can be tested, or asserted for a more actionable stack trace
+        // on failure.
+        //
+        // The FeatureList is however guaranteed to be initialized by the time
+        // AsyncInitializationActivity#finishNativeInitialization is called.
+        return SubresourceFilterFeatureListJni.get().isInitialized();
+    }
+
+    @NativeMethods
+    interface Natives {
+        boolean isInitialized();
+        boolean isEnabled(String featureName);
+    }
+}
diff --git a/components/subresource_filter/android/subresource_filter_feature_list.cc b/components/subresource_filter/android/subresource_filter_feature_list.cc
new file mode 100644
index 0000000..64122b3
--- /dev/null
+++ b/components/subresource_filter/android/subresource_filter_feature_list.cc
@@ -0,0 +1,50 @@
+// 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 "base/android/jni_string.h"
+#include "base/feature_list.h"
+#include "base/stl_util.h"
+#include "components/subresource_filter/android/subresource_filter_jni_headers/SubresourceFilterFeatureList_jni.h"
+#include "components/subresource_filter/core/browser/subresource_filter_features.h"
+
+using base::android::ConvertJavaStringToUTF8;
+using base::android::JavaParamRef;
+
+namespace subresource_filter {
+
+namespace {
+
+// Array of features exposed through the Java ContentFeatureList API. Entries in
+// this array may either refer to features defined in the header of this file or
+// in other locations in the code base (e.g. content_features.h).
+const base::Feature* kFeaturesExposedToJava[] = {
+    &kSafeBrowsingSubresourceFilter,
+};
+
+// TODO(crbug.com/1060097): Removethis once a generalized FeatureList exists.
+const base::Feature* FindFeatureExposedToJava(const std::string& feature_name) {
+  for (size_t i = 0; i < base::size(kFeaturesExposedToJava); ++i) {
+    if (kFeaturesExposedToJava[i]->name == feature_name)
+      return kFeaturesExposedToJava[i];
+  }
+  NOTREACHED() << "Queried feature not found in SubresourceFilterFeatureList: "
+               << feature_name;
+  return nullptr;
+}
+
+}  // namespace
+
+static jboolean JNI_SubresourceFilterFeatureList_IsInitialized(JNIEnv* env) {
+  return !!base::FeatureList::GetInstance();
+}
+
+static jboolean JNI_SubresourceFilterFeatureList_IsEnabled(
+    JNIEnv* env,
+    const JavaParamRef<jstring>& jfeature_name) {
+  const base::Feature* feature =
+      FindFeatureExposedToJava(ConvertJavaStringToUTF8(env, jfeature_name));
+  return base::FeatureList::IsEnabled(*feature);
+}
+
+}  // namespace subresource_filter
diff --git a/components/viz/common/surfaces/child_local_surface_id_allocator.cc b/components/viz/common/surfaces/child_local_surface_id_allocator.cc
index f29b1f8..0bdee61 100644
--- a/components/viz/common/surfaces/child_local_surface_id_allocator.cc
+++ b/components/viz/common/surfaces/child_local_surface_id_allocator.cc
@@ -72,10 +72,13 @@
         parent_local_surface_id_allocation.allocation_time();
   }
 
+  // If embed token has changed, accept all fields from the parent
+  // including child sequence number.
   if (current_local_surface_id.embed_token() !=
       parent_allocated_local_surface_id.embed_token()) {
     current_local_surface_id_allocation_.local_surface_id_
-        .child_sequence_number_ = 1;
+        .child_sequence_number_ =
+        parent_allocated_local_surface_id.child_sequence_number_;
   }
 
   current_local_surface_id_allocation_.local_surface_id_
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index 962d24e..7920c4e 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -1858,6 +1858,8 @@
     "web_package/prefetched_signed_exchange_cache.h",
     "web_package/prefetched_signed_exchange_cache_adapter.cc",
     "web_package/prefetched_signed_exchange_cache_adapter.h",
+    "web_package/save_as_web_bundle_job.cc",
+    "web_package/save_as_web_bundle_job.h",
     "web_package/signed_exchange_cert_fetcher.cc",
     "web_package/signed_exchange_cert_fetcher.h",
     "web_package/signed_exchange_cert_fetcher_factory.cc",
diff --git a/content/browser/accessibility/accessibility_win_browsertest.cc b/content/browser/accessibility/accessibility_win_browsertest.cc
index fad3fc5..fd1d301 100644
--- a/content/browser/accessibility/accessibility_win_browsertest.cc
+++ b/content/browser/accessibility/accessibility_win_browsertest.cc
@@ -49,6 +49,7 @@
 #include "third_party/isimpledom/ISimpleDOMNode.h"
 #include "ui/accessibility/accessibility_switches.h"
 #include "ui/accessibility/ax_event_generator.h"
+#include "ui/accessibility/platform/ax_fragment_root_win.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_tree_host.h"
 
@@ -4266,6 +4267,44 @@
 }
 
 IN_PROC_BROWSER_TEST_F(AccessibilityWinUIABrowserTest,
+                       GetFocusFromRootReachesWebContent) {
+  LoadInitialAccessibilityTreeFromHtml(
+      R"HTML(<!DOCTYPE html>
+      <html>
+        <button>Focus target</button>
+        <script>
+          document.querySelector('button').focus();
+        </script>
+      </html>)HTML");
+
+  // Obtain the fragment root from the top-level HWND.
+  HWND hwnd = shell()->window()->GetHost()->GetAcceleratedWidget();
+  ASSERT_NE(gfx::kNullAcceleratedWidget, hwnd);
+  ui::AXFragmentRootWin* fragment_root =
+      ui::AXFragmentRootWin::GetForAcceleratedWidget(hwnd);
+  ASSERT_NE(nullptr, fragment_root);
+  Microsoft::WRL::ComPtr<IRawElementProviderFragmentRoot> uia_fragment_root;
+  ASSERT_HRESULT_SUCCEEDED(
+      fragment_root->GetNativeViewAccessible()->QueryInterface(
+          IID_PPV_ARGS(&uia_fragment_root)));
+
+  // Verify that calling GetFocus on the fragment root reaches web content.
+  Microsoft::WRL::ComPtr<IRawElementProviderFragment> focused_fragment;
+  ASSERT_HRESULT_SUCCEEDED(uia_fragment_root->GetFocus(&focused_fragment));
+
+  Microsoft::WRL::ComPtr<IRawElementProviderSimple> focused_element;
+  ASSERT_HRESULT_SUCCEEDED(focused_fragment.As(&focused_element));
+
+  base::win::ScopedVariant name_property;
+  ASSERT_HRESULT_SUCCEEDED(focused_element->GetPropertyValue(
+      UIA_NamePropertyId, name_property.Receive()));
+  ASSERT_EQ(name_property.type(), VT_BSTR);
+  BSTR name_bstr = name_property.ptr()->bstrVal;
+  base::string16 actual_name(name_bstr, ::SysStringLen(name_bstr));
+  EXPECT_EQ(L"Focus target", actual_name);
+}
+
+IN_PROC_BROWSER_TEST_F(AccessibilityWinUIABrowserTest,
                        LegacyWindowIsNotControlElement) {
   LoadInitialAccessibilityTreeFromHtml(
       R"HTML(<!DOCTYPE html>
diff --git a/content/browser/android/selection/selection_popup_controller.cc b/content/browser/android/selection/selection_popup_controller.cc
index d587303a..d5ea917d 100644
--- a/content/browser/android/selection/selection_popup_controller.cc
+++ b/content/browser/android/selection/selection_popup_controller.cc
@@ -12,7 +12,7 @@
 #include "content/browser/web_contents/web_contents_impl.h"
 #include "content/browser/web_contents/web_contents_view_android.h"
 #include "content/public/android/content_jni_headers/SelectionPopupControllerImpl_jni.h"
-#include "content/public/common/context_menu_params.h"
+#include "content/public/browser/context_menu_params.h"
 #include "third_party/blink/public/common/context_menu_data/edit_flags.h"
 #include "third_party/blink/public/common/context_menu_data/input_field_type.h"
 #include "ui/gfx/geometry/point_conversions.h"
diff --git a/content/browser/back_forward_cache_browsertest.cc b/content/browser/back_forward_cache_browsertest.cc
index e366c1a..199d99b3 100644
--- a/content/browser/back_forward_cache_browsertest.cc
+++ b/content/browser/back_forward_cache_browsertest.cc
@@ -5550,4 +5550,28 @@
               ElementsAre(base::Bucket(0, 1), base::Bucket(1, 1)));
 }
 
+// Navigate from A(B) to C and check IsCurrent status for RenderFrameHost A
+// and B before and after entering back-forward cache.
+IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest, CheckIsCurrent) {
+  ASSERT_TRUE(embedded_test_server()->Start());
+  GURL url_a(embedded_test_server()->GetURL(
+      "a.com", "/cross_site_iframe_factory.html?a(b)"));
+  GURL url_c(embedded_test_server()->GetURL("c.com", "/title1.html"));
+
+  // 1) Navigate to A(B).
+  EXPECT_TRUE(NavigateToURL(shell(), url_a));
+  RenderFrameHostImpl* rfh_a = current_frame_host();
+  RenderFrameHostImpl* rfh_b = rfh_a->child_at(0)->current_frame_host();
+
+  EXPECT_TRUE(rfh_a->IsCurrent());
+  EXPECT_TRUE(rfh_b->IsCurrent());
+
+  // 2) Navigate to C.
+  EXPECT_TRUE(NavigateToURL(shell(), url_c));
+  EXPECT_TRUE(rfh_a->is_in_back_forward_cache());
+  EXPECT_TRUE(rfh_b->is_in_back_forward_cache());
+
+  EXPECT_FALSE(rfh_a->IsCurrent());
+  EXPECT_FALSE(rfh_b->IsCurrent());
+}
 }  // namespace content
diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc
index 362d1b0..6c20849 100644
--- a/content/browser/frame_host/render_frame_host_impl.cc
+++ b/content/browser/frame_host/render_frame_host_impl.cc
@@ -142,6 +142,7 @@
 #include "content/public/browser/browser_plugin_guest_manager.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/content_browser_client.h"
+#include "content/public/browser/context_menu_params.h"
 #include "content/public/browser/download_manager.h"
 #include "content/public/browser/global_routing_id.h"
 #include "content/public/browser/media_player_watch_time.h"
@@ -6188,7 +6189,19 @@
 }
 
 bool RenderFrameHostImpl::IsCurrent() {
-  return this == frame_tree_node_->current_frame_host();
+  RenderFrameHostImpl* rfh = this;
+  // Check this RenderFrameHost and all its ancestors to see if they are the
+  // current ones in their respective FrameTreeNodes.
+  // It is important to check for all ancestors as when navigation commits a new
+  // RenderFrameHost may replace one of the parents, swapping out the old with
+  // its entire subtree but |this| will still be a current one in its
+  // FrameTreeNode.
+  while (rfh) {
+    if (rfh->frame_tree_node()->current_frame_host() != rfh)
+      return false;
+    rfh = rfh->GetParent();
+  }
+  return true;
 }
 
 size_t RenderFrameHostImpl::GetProxyCount() {
diff --git a/content/browser/frame_host/render_frame_host_impl.h b/content/browser/frame_host/render_frame_host_impl.h
index 7ae1272c4..deb760c 100644
--- a/content/browser/frame_host/render_frame_host_impl.h
+++ b/content/browser/frame_host/render_frame_host_impl.h
@@ -1506,6 +1506,8 @@
                            TimerNotRestartedBySecondDialog);
   FRIEND_TEST_ALL_PREFIXES(RenderFrameHostImplBrowserTest,
                            ComputeSiteForCookiesParentNavigatedAway);
+  FRIEND_TEST_ALL_PREFIXES(RenderFrameHostImplBrowserTest,
+                           CheckIsCurrentBeforeAndAfterUnload);
   FRIEND_TEST_ALL_PREFIXES(RenderFrameHostManagerTest,
                            CreateRenderViewAfterProcessKillAndClosedProxy);
   FRIEND_TEST_ALL_PREFIXES(RenderFrameHostManagerTest, DontSelectInvalidFiles);
diff --git a/content/browser/frame_host/render_frame_host_impl_browsertest.cc b/content/browser/frame_host/render_frame_host_impl_browsertest.cc
index af92e4d..6cdee520 100644
--- a/content/browser/frame_host/render_frame_host_impl_browsertest.cc
+++ b/content/browser/frame_host/render_frame_host_impl_browsertest.cc
@@ -4,6 +4,7 @@
 
 #include "content/browser/frame_host/render_frame_host_impl.h"
 
+#include <string>
 #include <utility>
 
 #include "base/bind.h"
@@ -190,6 +191,10 @@
   }
   net::EmbeddedTestServer* https_server() { return &https_server_; }
 
+  WebContentsImpl* web_contents() const {
+    return static_cast<WebContentsImpl*>(shell()->web_contents());
+  }
+
  private:
   base::test::ScopedFeatureList feature_list_;
   net::EmbeddedTestServer https_server_;
@@ -3567,6 +3572,55 @@
             EvalJs(main_document, "document.querySelector('h3').textContent"));
 }
 
+// Start with A(B), navigate A to C. By emulating a slow unload handler B, check
+// the status of IsCurrent for subframes of A i.e., B before and after
+// navigating to C.
+IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,
+                       CheckIsCurrentBeforeAndAfterUnload) {
+  IsolateAllSitesForTesting(base::CommandLine::ForCurrentProcess());
+  std::string onunload_script = "window.onunload = function(){}";
+  GURL url_ab(embedded_test_server()->GetURL(
+      "a.com", "/cross_site_iframe_factory.html?a(b)"));
+  GURL url_c(embedded_test_server()->GetURL("c.com", "/title1.html"));
+
+  // 1) Navigate to a page with an iframe.
+  EXPECT_TRUE(NavigateToURL(shell(), url_ab));
+  RenderFrameHostImpl* rfh_a = web_contents()->GetMainFrame();
+  RenderFrameHostImpl* rfh_b = rfh_a->child_at(0)->current_frame_host();
+  RenderFrameDeletedObserver delete_rfh_b(rfh_b);
+  EXPECT_EQ(RenderFrameHostImpl::UnloadState::NotRun, rfh_b->unload_state_);
+
+  // 2) Set an arbitrarily long timeout to ensure the subframe unload timer
+  // doesn't fire before we call OnDetach(). Act as if there was a slow unload
+  // handler on rfh_b. The non navigating frames are waiting for
+  // FrameHostMsg_Detach.
+  rfh_b->SetSubframeUnloadTimeoutForTesting(base::TimeDelta::FromSeconds(30));
+  auto detach_filter = base::MakeRefCounted<DropMessageFilter>(
+      FrameMsgStart, FrameHostMsg_Detach::ID);
+  rfh_b->GetProcess()->AddFilter(detach_filter.get());
+  EXPECT_TRUE(ExecuteScript(rfh_b->frame_tree_node(), onunload_script));
+
+  // 3) Check the IsCurrent state of rfh_a, rfh_b before navigating to C.
+  EXPECT_TRUE(rfh_a->IsCurrent());
+  EXPECT_TRUE(rfh_b->IsCurrent());
+
+  // 4) Navigate rfh_a to C.
+  EXPECT_TRUE(NavigateToURL(shell(), url_c));
+  RenderFrameHostImpl* rfh_c = web_contents()->GetMainFrame();
+  EXPECT_EQ(RenderFrameHostImpl::UnloadState::InProgress, rfh_b->unload_state_);
+
+  // 5) Check the IsCurrent state of rfh_a, rfh_b and rfh_c after navigating to
+  // C.
+  EXPECT_FALSE(rfh_a->IsCurrent());
+  EXPECT_FALSE(rfh_b->IsCurrent());
+  EXPECT_TRUE(rfh_c->IsCurrent());
+
+  // 6) Run detach on rfh_b to delete its frame.
+  EXPECT_FALSE(delete_rfh_b.deleted());
+  rfh_b->OnDetach();
+  EXPECT_TRUE(delete_rfh_b.deleted());
+}
+
 namespace {
 
 // Collects the committed IPAddressSpaces, and makes them available for
diff --git a/content/browser/media/android/browser_gpu_video_accelerator_factories.cc b/content/browser/media/android/browser_gpu_video_accelerator_factories.cc
index 8354ba6..6cecea80 100644
--- a/content/browser/media/android/browser_gpu_video_accelerator_factories.cc
+++ b/content/browser/media/android/browser_gpu_video_accelerator_factories.cc
@@ -170,9 +170,9 @@
   return media::VideoEncodeAccelerator::SupportedProfiles();
 }
 
-scoped_refptr<viz::ContextProvider>
+viz::RasterContextProvider*
 BrowserGpuVideoAcceleratorFactories::GetMediaContextProvider() {
-  return context_provider_;
+  return context_provider_.get();
 }
 
 void BrowserGpuVideoAcceleratorFactories::SetRenderingColorSpace(
diff --git a/content/browser/media/android/browser_gpu_video_accelerator_factories.h b/content/browser/media/android/browser_gpu_video_accelerator_factories.h
index 1572ea1..b7d8911 100644
--- a/content/browser/media/android/browser_gpu_video_accelerator_factories.h
+++ b/content/browser/media/android/browser_gpu_video_accelerator_factories.h
@@ -52,7 +52,7 @@
   scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunner() override;
   base::Optional<media::VideoEncodeAccelerator::SupportedProfiles>
   GetVideoEncodeAcceleratorSupportedProfiles() override;
-  scoped_refptr<viz::ContextProvider> GetMediaContextProvider() override;
+  viz::RasterContextProvider* GetMediaContextProvider() override;
   void SetRenderingColorSpace(const gfx::ColorSpace& color_space) override;
 
   scoped_refptr<viz::ContextProviderCommandBuffer> context_provider_;
diff --git a/content/browser/renderer_host/input/touch_selection_controller_client_aura.cc b/content/browser/renderer_host/input/touch_selection_controller_client_aura.cc
index 9aeb99e..1d1d45a 100644
--- a/content/browser/renderer_host/input/touch_selection_controller_client_aura.cc
+++ b/content/browser/renderer_host/input/touch_selection_controller_client_aura.cc
@@ -12,8 +12,8 @@
 #include "content/browser/renderer_host/render_widget_host_impl.h"
 #include "content/browser/renderer_host/render_widget_host_view_aura.h"
 #include "content/common/view_messages.h"
+#include "content/public/browser/context_menu_params.h"
 #include "content/public/browser/render_view_host.h"
-#include "content/public/common/context_menu_params.h"
 #include "ui/aura/client/cursor_client.h"
 #include "ui/aura/client/screen_position_client.h"
 #include "ui/aura/env.h"
diff --git a/content/browser/renderer_host/render_message_filter.cc b/content/browser/renderer_host/render_message_filter.cc
index 014fb6f2..ec99318d 100644
--- a/content/browser/renderer_host/render_message_filter.cc
+++ b/content/browser/renderer_host/render_message_filter.cc
@@ -46,7 +46,6 @@
 #include "content/public/browser/storage_partition.h"
 #include "content/public/common/content_features.h"
 #include "content/public/common/content_switches.h"
-#include "content/public/common/context_menu_params.h"
 #include "content/public/common/url_constants.h"
 #include "gpu/ipc/common/gpu_memory_buffer_impl.h"
 #include "ipc/ipc_channel_handle.h"
diff --git a/content/browser/renderer_host/render_view_host_impl.cc b/content/browser/renderer_host/render_view_host_impl.cc
index bb89107b..464c6df 100644
--- a/content/browser/renderer_host/render_view_host_impl.cc
+++ b/content/browser/renderer_host/render_view_host_impl.cc
@@ -62,6 +62,7 @@
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/content_browser_client.h"
+#include "content/public/browser/context_menu_params.h"
 #include "content/public/browser/native_web_keyboard_event.h"
 #include "content/public/browser/notification_details.h"
 #include "content/public/browser/notification_service.h"
@@ -74,7 +75,6 @@
 #include "content/public/common/content_constants.h"
 #include "content/public/common/content_features.h"
 #include "content/public/common/content_switches.h"
-#include "content/public/common/context_menu_params.h"
 #include "content/public/common/result_codes.h"
 #include "content/public/common/url_constants.h"
 #include "content/public/common/url_utils.h"
diff --git a/content/browser/renderer_host/render_widget_host_view_aura.h b/content/browser/renderer_host/render_widget_host_view_aura.h
index 7d20671..2e48eb07 100644
--- a/content/browser/renderer_host/render_widget_host_view_aura.h
+++ b/content/browser/renderer_host/render_widget_host_view_aura.h
@@ -31,8 +31,8 @@
 #include "content/browser/renderer_host/text_input_manager.h"
 #include "content/common/content_export.h"
 #include "content/common/cursors/webcursor.h"
+#include "content/public/browser/context_menu_params.h"
 #include "content/public/browser/visibility.h"
-#include "content/public/common/context_menu_params.h"
 #include "third_party/skia/include/core/SkRegion.h"
 #include "ui/aura/client/cursor_client_observer.h"
 #include "ui/aura/client/focus_change_observer.h"
diff --git a/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc b/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
index 513a97c..495da83e 100644
--- a/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
+++ b/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
@@ -57,11 +57,11 @@
 #include "content/common/text_input_state.h"
 #include "content/common/view_messages.h"
 #include "content/common/widget_messages.h"
+#include "content/public/browser/context_menu_params.h"
 #include "content/public/browser/keyboard_event_processing_result.h"
 #include "content/public/browser/render_widget_host_view.h"
 #include "content/public/browser/web_contents_view_delegate.h"
 #include "content/public/common/content_features.h"
-#include "content/public/common/context_menu_params.h"
 #include "content/public/test/mock_render_process_host.h"
 #include "content/public/test/test_browser_context.h"
 #include "content/test/mock_render_widget_host_delegate.h"
diff --git a/content/browser/renderer_host/render_widget_host_view_event_handler.cc b/content/browser/renderer_host/render_widget_host_view_event_handler.cc
index 46c6776..e571258 100644
--- a/content/browser/renderer_host/render_widget_host_view_event_handler.cc
+++ b/content/browser/renderer_host/render_widget_host_view_event_handler.cc
@@ -35,7 +35,6 @@
 
 #if defined(OS_WIN)
 #include "content/browser/frame_host/render_frame_host_impl.h"
-#include "content/public/common/context_menu_params.h"
 #include "ui/aura/window_tree_host.h"
 #include "ui/display/screen.h"
 #endif  // defined(OS_WIN)
diff --git a/content/browser/service_worker/embedded_worker_test_helper.cc b/content/browser/service_worker/embedded_worker_test_helper.cc
index f15ac17e..7674716 100644
--- a/content/browser/service_worker/embedded_worker_test_helper.cc
+++ b/content/browser/service_worker/embedded_worker_test_helper.cc
@@ -171,7 +171,7 @@
     std::move(callback).Run();
     return;
   }
-  if (!version->GetMainScriptHttpResponseInfo())
+  if (!version->GetMainScriptResponse())
     version->SetMainScriptHttpResponseInfo(CreateHttpResponseInfo());
   if (!version->script_cache_map()->size()) {
     // Add a dummy ResourceRecord for the main script to the script cache map of
diff --git a/content/browser/service_worker/service_worker_context_core.cc b/content/browser/service_worker/service_worker_context_core.cc
index 3fe11b3..5b2d4b4 100644
--- a/content/browser/service_worker/service_worker_context_core.cc
+++ b/content/browser/service_worker/service_worker_context_core.cc
@@ -817,6 +817,14 @@
                          &ServiceWorkerContextCoreObserver::OnStorageWiped);
 }
 
+void ServiceWorkerContextCore::OnMainScriptResponseSet(
+    int64_t version_id,
+    const ServiceWorkerVersion::MainScriptResponse& response) {
+  observer_list_->Notify(
+      FROM_HERE, &ServiceWorkerContextCoreObserver::OnMainScriptResponseSet,
+      version_id, response.response_time, response.last_modified);
+}
+
 void ServiceWorkerContextCore::OnRunningStateChanged(
     ServiceWorkerVersion* version) {
   if (!version->context())
@@ -865,19 +873,6 @@
       version->embedded_worker()->worker_devtools_agent_route_id());
 }
 
-void ServiceWorkerContextCore::OnMainScriptHttpResponseInfoSet(
-    ServiceWorkerVersion* version) {
-  const net::HttpResponseInfo* info = version->GetMainScriptHttpResponseInfo();
-  DCHECK(info);
-  base::Time lastModified;
-  if (info->headers)
-    info->headers->GetLastModifiedValue(&lastModified);
-  observer_list_->Notify(
-      FROM_HERE,
-      &ServiceWorkerContextCoreObserver::OnMainScriptHttpResponseInfoSet,
-      version->version_id(), info->response_time, lastModified);
-}
-
 void ServiceWorkerContextCore::OnErrorReported(
     ServiceWorkerVersion* version,
     const base::string16& error_message,
diff --git a/content/browser/service_worker/service_worker_context_core.h b/content/browser/service_worker/service_worker_context_core.h
index 69df98df..dd04937 100644
--- a/content/browser/service_worker/service_worker_context_core.h
+++ b/content/browser/service_worker/service_worker_context_core.h
@@ -122,11 +122,14 @@
 
   void OnStorageWiped();
 
+  void OnMainScriptResponseSet(
+      int64_t version_id,
+      const ServiceWorkerVersion::MainScriptResponse& response);
+
   // ServiceWorkerVersion::Observer overrides.
   void OnRunningStateChanged(ServiceWorkerVersion* version) override;
   void OnVersionStateChanged(ServiceWorkerVersion* version) override;
   void OnDevToolsRoutingIdChanged(ServiceWorkerVersion* version) override;
-  void OnMainScriptHttpResponseInfoSet(ServiceWorkerVersion* version) override;
   void OnErrorReported(ServiceWorkerVersion* version,
                        const base::string16& error_message,
                        int line_number,
diff --git a/content/browser/service_worker/service_worker_context_core_observer.h b/content/browser/service_worker/service_worker_context_core_observer.h
index 75126bab..fec34e9 100644
--- a/content/browser/service_worker/service_worker_context_core_observer.h
+++ b/content/browser/service_worker/service_worker_context_core_observer.h
@@ -64,10 +64,9 @@
   virtual void OnVersionDevToolsRoutingIdChanged(int64_t version_id,
                                                  int process_id,
                                                  int devtools_agent_route_id) {}
-  virtual void OnMainScriptHttpResponseInfoSet(
-      int64_t version_id,
-      base::Time script_response_time,
-      base::Time script_last_modified) {}
+  virtual void OnMainScriptResponseSet(int64_t version_id,
+                                       base::Time script_response_time,
+                                       base::Time script_last_modified) {}
   virtual void OnErrorReported(int64_t version_id, const ErrorInfo& info) {}
   virtual void OnReportConsoleMessage(int64_t version_id,
                                       const ConsoleMessage& message) {}
diff --git a/content/browser/service_worker/service_worker_context_watcher.cc b/content/browser/service_worker/service_worker_context_watcher.cc
index 1c458426..cf2b47d 100644
--- a/content/browser/service_worker/service_worker_context_watcher.cc
+++ b/content/browser/service_worker/service_worker_context_watcher.cc
@@ -289,7 +289,7 @@
     version_info_map_.erase(version_id);
 }
 
-void ServiceWorkerContextWatcher::OnMainScriptHttpResponseInfoSet(
+void ServiceWorkerContextWatcher::OnMainScriptResponseSet(
     int64_t version_id,
     base::Time script_response_time,
     base::Time script_last_modified) {
diff --git a/content/browser/service_worker/service_worker_context_watcher.h b/content/browser/service_worker/service_worker_context_watcher.h
index 5c2b717..a2f529f 100644
--- a/content/browser/service_worker/service_worker_context_watcher.h
+++ b/content/browser/service_worker/service_worker_context_watcher.h
@@ -97,10 +97,9 @@
   void OnVersionDevToolsRoutingIdChanged(int64_t version_id,
                                          int process_id,
                                          int devtools_agent_route_id) override;
-  void OnMainScriptHttpResponseInfoSet(
-      int64_t version_id,
-      base::Time script_response_time,
-      base::Time script_last_modified) override;
+  void OnMainScriptResponseSet(int64_t version_id,
+                               base::Time script_response_time,
+                               base::Time script_last_modified) override;
   void OnErrorReported(int64_t version_id,
                        const ErrorInfo& info) override;
   void OnReportConsoleMessage(int64_t version_id,
diff --git a/content/browser/service_worker/service_worker_installed_script_loader.cc b/content/browser/service_worker/service_worker_installed_script_loader.cc
index 245d320..dba9af8 100644
--- a/content/browser/service_worker/service_worker_installed_script_loader.cc
+++ b/content/browser/service_worker/service_worker_installed_script_loader.cc
@@ -40,8 +40,7 @@
   // In this case, the main script info would not yet have been set, so set it
   // here.
   if (request_url == version_for_main_script_http_response_info->script_url() &&
-      !version_for_main_script_http_response_info
-           ->GetMainScriptHttpResponseInfo()) {
+      !version_for_main_script_http_response_info->GetMainScriptResponse()) {
     version_for_main_script_http_response_info_ =
         std::move(version_for_main_script_http_response_info);
   }
diff --git a/content/browser/service_worker/service_worker_navigation_loader.cc b/content/browser/service_worker/service_worker_navigation_loader.cc
index 657f0eb..957b635 100644
--- a/content/browser/service_worker/service_worker_navigation_loader.cc
+++ b/content/browser/service_worker/service_worker_navigation_loader.cc
@@ -345,8 +345,8 @@
   // browser. See https://crbug.com/392409 for details about this design.
   // TODO(horo): When we support mixed-content (HTTP) no-cors requests from a
   // ServiceWorker, we have to check the security level of the responses.
-  DCHECK(version->GetMainScriptHttpResponseInfo());
-  response_head_->ssl_info = version->GetMainScriptHttpResponseInfo()->ssl_info;
+  DCHECK(version->GetMainScriptResponse());
+  response_head_->ssl_info = version->GetMainScriptResponse()->ssl_info;
 
   // Handle a redirect response. ComputeRedirectInfo returns non-null redirect
   // info if the given response is a redirect.
diff --git a/content/browser/service_worker/service_worker_storage_unittest.cc b/content/browser/service_worker/service_worker_storage_unittest.cc
index ce9fb0c8..57fbe48 100644
--- a/content/browser/service_worker/service_worker_storage_unittest.cc
+++ b/content/browser/service_worker/service_worker_storage_unittest.cc
@@ -2004,7 +2004,7 @@
       base::MakeRefCounted<net::HttpResponseHeaders>("HTTP/1.1 200 OK");
   http_info.response_time = base::Time::FromJsTime(19940123);
   version->SetMainScriptHttpResponseInfo(http_info);
-  EXPECT_TRUE(version->main_script_http_info_);
+  EXPECT_TRUE(version->main_script_response_);
   EXPECT_EQ(http_info.response_time,
             version->script_response_time_for_devtools_);
   EXPECT_EQ(http_info.response_time, version->GetInfo().script_response_time);
@@ -2026,7 +2026,7 @@
   ASSERT_TRUE(found_registration);
   auto* waiting_version = found_registration->waiting_version();
   ASSERT_TRUE(waiting_version);
-  EXPECT_FALSE(waiting_version->main_script_http_info_);
+  EXPECT_FALSE(waiting_version->main_script_response_);
   EXPECT_EQ(http_info.response_time,
             waiting_version->script_response_time_for_devtools_);
   EXPECT_EQ(http_info.response_time,
diff --git a/content/browser/service_worker/service_worker_version.cc b/content/browser/service_worker/service_worker_version.cc
index 64c3b10..9f7a20d 100644
--- a/content/browser/service_worker/service_worker_version.cc
+++ b/content/browser/service_worker/service_worker_version.cc
@@ -419,17 +419,16 @@
   }
 
   info.script_response_time = script_response_time_for_devtools_;
-  if (!main_script_http_info_)
+  if (!main_script_response_)
     return info;
-  // If the service worker hasn't started, then |main_script_http_info_| is not
+
+  // If the service worker hasn't started, then |main_script_response_| is not
   // set, so we use |script_response_time_for_devtools_| to populate |info|. If
   // the worker has started, this value should match with the timestamp stored
-  // in |main_script_http_info_|.
-  DCHECK_EQ(info.script_response_time, main_script_http_info_->response_time);
+  // in |main_script_response_|.
+  DCHECK_EQ(info.script_response_time, main_script_response_->response_time);
+  info.script_last_modified = main_script_response_->last_modified;
 
-  if (main_script_http_info_->headers)
-    main_script_http_info_->headers->GetLastModifiedValue(
-        &info.script_last_modified);
   return info;
 }
 
@@ -1090,7 +1089,13 @@
 void ServiceWorkerVersion::SetMainScriptHttpResponseInfo(
     const net::HttpResponseInfo& http_info) {
   script_response_time_for_devtools_ = http_info.response_time;
-  main_script_http_info_.reset(new net::HttpResponseInfo(http_info));
+  auto response = std::make_unique<MainScriptResponse>();
+  response->response_time = http_info.response_time;
+  if (http_info.headers) {
+    http_info.headers->GetLastModifiedValue(&response->last_modified);
+  }
+  response->ssl_info = http_info.ssl_info;
+  main_script_response_ = std::move(response);
 
   // Updates |origin_trial_tokens_| if it is not set yet. This happens when:
   //  1) The worker is a new one.
@@ -1103,8 +1108,9 @@
         url::Origin::Create(scope()), http_info.headers.get(), clock_->Now());
   }
 
-  for (auto& observer : observers_)
-    observer.OnMainScriptHttpResponseInfoSet(this);
+  if (context_) {
+    context_->OnMainScriptResponseSet(version_id(), *main_script_response_);
+  }
 }
 
 void ServiceWorkerVersion::SimulatePingTimeoutForTesting() {
@@ -1124,9 +1130,9 @@
   return !HasWorkInBrowser() && worker_is_idle_on_renderer_;
 }
 
-const net::HttpResponseInfo*
-ServiceWorkerVersion::GetMainScriptHttpResponseInfo() {
-  return main_script_http_info_.get();
+const ServiceWorkerVersion::MainScriptResponse*
+ServiceWorkerVersion::GetMainScriptResponse() {
+  return main_script_response_.get();
 }
 
 ServiceWorkerVersion::InflightRequestTimeoutInfo::InflightRequestTimeoutInfo(
diff --git a/content/browser/service_worker/service_worker_version.h b/content/browser/service_worker/service_worker_version.h
index 288ab528..b46ec06 100644
--- a/content/browser/service_worker/service_worker_version.h
+++ b/content/browser/service_worker/service_worker_version.h
@@ -154,13 +154,21 @@
                          // timed out.
   };
 
+  // Contains a subset of the main script's response information.
+  struct MainScriptResponse {
+    base::Time response_time;
+    base::Time last_modified;
+    // This is used for all responses sent back from a service worker, as
+    // effective security of these responses is equivalent to that of the
+    // service worker.
+    net::SSLInfo ssl_info;
+  };
+
   class Observer {
    public:
     virtual void OnRunningStateChanged(ServiceWorkerVersion* version) {}
     virtual void OnVersionStateChanged(ServiceWorkerVersion* version) {}
     virtual void OnDevToolsRoutingIdChanged(ServiceWorkerVersion* version) {}
-    virtual void OnMainScriptHttpResponseInfoSet(
-        ServiceWorkerVersion* version) {}
     virtual void OnErrorReported(ServiceWorkerVersion* version,
                                  const base::string16& error_message,
                                  int line_number,
@@ -482,11 +490,9 @@
   void SetDevToolsAttached(bool attached);
 
   // Sets the HttpResponseInfo used to load the main script.
-  // This HttpResponseInfo will be used for all responses sent back from the
-  // service worker, as the effective security of these responses is equivalent
-  // to that of the ServiceWorker.
+  // TODO(bashi): Make this method take MainScriptResponse.
   void SetMainScriptHttpResponseInfo(const net::HttpResponseInfo& http_info);
-  const net::HttpResponseInfo* GetMainScriptHttpResponseInfo();
+  const MainScriptResponse* GetMainScriptResponse();
 
   // Simulate ping timeout. Should be used for tests-only.
   void SimulatePingTimeoutForTesting();
@@ -1022,7 +1028,8 @@
   // the subresource loader factories are updated.
   bool initialize_global_scope_after_main_script_loaded_ = false;
 
-  std::unique_ptr<net::HttpResponseInfo> main_script_http_info_;
+  // Populated via net::HttpResponseInfo of the main script.
+  std::unique_ptr<MainScriptResponse> main_script_response_;
 
   // DevTools requires each service worker's script receive time, even for
   // the ones that haven't started. However, a ServiceWorkerVersion's field
diff --git a/content/browser/site_per_process_browsertest.cc b/content/browser/site_per_process_browsertest.cc
index 83348e3..c170686 100644
--- a/content/browser/site_per_process_browsertest.cc
+++ b/content/browser/site_per_process_browsertest.cc
@@ -83,6 +83,7 @@
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
+#include "content/public/browser/context_menu_params.h"
 #include "content/public/browser/interstitial_page_delegate.h"
 #include "content/public/browser/javascript_dialog_manager.h"
 #include "content/public/browser/navigation_handle.h"
diff --git a/content/browser/site_per_process_hit_test_browsertest.cc b/content/browser/site_per_process_hit_test_browsertest.cc
index e420211..9ccb2c1 100644
--- a/content/browser/site_per_process_hit_test_browsertest.cc
+++ b/content/browser/site_per_process_hit_test_browsertest.cc
@@ -29,6 +29,7 @@
 #include "content/common/widget_messages.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
+#include "content/public/browser/context_menu_params.h"
 #include "content/public/common/content_features.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/common/screen_info.h"
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index 056153a0..b57520fe 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -86,6 +86,7 @@
 #include "content/browser/site_instance_impl.h"
 #include "content/browser/web_contents/javascript_dialog_navigation_deferrer.h"
 #include "content/browser/web_contents/web_contents_view_child_frame.h"
+#include "content/browser/web_package/save_as_web_bundle_job.h"
 #include "content/browser/webui/web_ui_controller_factory_registry.h"
 #include "content/browser/webui/web_ui_impl.h"
 #include "content/common/browser_plugin/browser_plugin_constants.h"
@@ -104,6 +105,7 @@
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/content_browser_client.h"
+#include "content/public/browser/context_menu_params.h"
 #include "content/public/browser/device_service.h"
 #include "content/public/browser/download_manager.h"
 #include "content/public/browser/file_select_listener.h"
@@ -4044,6 +4046,13 @@
                                                    std::move(callback));
 }
 
+void WebContentsImpl::GenerateWebBundle(
+    const base::FilePath& file_path,
+    base::OnceCallback<void(uint64_t /* file_size */,
+                            data_decoder::mojom::WebBundlerError)> callback) {
+  SaveAsWebBundleJob::Start(this, file_path, std::move(callback));
+}
+
 const std::string& WebContentsImpl::GetContentsMimeType() {
   return contents_mime_type_;
 }
diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h
index c3731f17..1a0b33f1 100644
--- a/content/browser/web_contents/web_contents_impl.h
+++ b/content/browser/web_contents/web_contents_impl.h
@@ -461,6 +461,10 @@
   void GenerateMHTMLWithResult(
       const MHTMLGenerationParams& params,
       MHTMLGenerationResult::GenerateMHTMLCallback callback) override;
+  void GenerateWebBundle(
+      const base::FilePath& file_path,
+      base::OnceCallback<void(uint64_t, data_decoder::mojom::WebBundlerError)>
+          callback) override;
   const std::string& GetContentsMimeType() override;
   bool WillNotifyDisconnection() override;
   blink::mojom::RendererPreferences* GetMutableRendererPrefs() override;
diff --git a/content/browser/web_contents/web_contents_impl_unittest.cc b/content/browser/web_contents/web_contents_impl_unittest.cc
index 47ecf39..cb2f1d1 100644
--- a/content/browser/web_contents/web_contents_impl_unittest.cc
+++ b/content/browser/web_contents/web_contents_impl_unittest.cc
@@ -36,6 +36,7 @@
 #include "content/common/view_messages.h"
 #include "content/public/browser/child_process_security_policy.h"
 #include "content/public/browser/content_browser_client.h"
+#include "content/public/browser/context_menu_params.h"
 #include "content/public/browser/global_request_id.h"
 #include "content/public/browser/interstitial_page_delegate.h"
 #include "content/public/browser/javascript_dialog_manager.h"
diff --git a/content/browser/web_contents/web_contents_view_android.h b/content/browser/web_contents/web_contents_view_android.h
index 9dae989..1ac6ecc 100644
--- a/content/browser/web_contents/web_contents_view_android.h
+++ b/content/browser/web_contents/web_contents_view_android.h
@@ -11,7 +11,6 @@
 #include "content/browser/renderer_host/render_view_host_delegate_view.h"
 #include "content/browser/web_contents/web_contents_view.h"
 #include "content/public/browser/web_contents_view_delegate.h"
-#include "content/public/common/context_menu_params.h"
 #include "content/public/common/drop_data.h"
 #include "ui/android/overscroll_refresh.h"
 #include "ui/android/view_android.h"
diff --git a/content/browser/web_package/save_as_web_bundle_job.cc b/content/browser/web_package/save_as_web_bundle_job.cc
new file mode 100644
index 0000000..b5c64f7
--- /dev/null
+++ b/content/browser/web_package/save_as_web_bundle_job.cc
@@ -0,0 +1,102 @@
+// 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 "content/browser/web_package/save_as_web_bundle_job.h"
+
+#include "base/callback.h"
+#include "base/files/file_path.h"
+#include "components/download/public/common/download_task_runner.h"
+#include "content/browser/frame_host/render_frame_host_impl.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/web_contents.h"
+#include "services/data_decoder/public/cpp/data_decoder.h"
+#include "third_party/blink/public/mojom/frame/frame.mojom.h"
+
+namespace content {
+
+// static
+void SaveAsWebBundleJob::Start(
+    WebContents* web_contents,
+    const base::FilePath& file_path,
+    base::OnceCallback<void(uint64_t /* file_size */,
+                            data_decoder::mojom::WebBundlerError)> callback) {
+  std::vector<
+      mojo::PendingRemote<data_decoder::mojom::ResourceSnapshotForWebBundle>>
+      snapshots;
+  web_contents->ForEachFrame(base::BindRepeating(
+      [](std::vector<mojo::PendingRemote<
+             data_decoder::mojom::ResourceSnapshotForWebBundle>>* snapshots,
+         RenderFrameHost* render_frame_host) {
+        mojo::Remote<data_decoder::mojom::ResourceSnapshotForWebBundle>
+            snapshot;
+        static_cast<RenderFrameHostImpl*>(render_frame_host)
+            ->GetAssociatedLocalFrame()
+            ->GetResourceSnapshotForWebBundle(
+                snapshot.BindNewPipeAndPassReceiver());
+        snapshots->push_back(snapshot.Unbind());
+      },
+      base::Unretained(&snapshots)));
+  new SaveAsWebBundleJob(file_path, std::move(snapshots), std::move(callback));
+}
+
+SaveAsWebBundleJob::SaveAsWebBundleJob(
+    const base::FilePath& file_path,
+    std::vector<
+        mojo::PendingRemote<data_decoder::mojom::ResourceSnapshotForWebBundle>>
+        snapshots,
+    base::OnceCallback<void(uint64_t /* file_size */,
+                            data_decoder::mojom::WebBundlerError)> callback)
+    : data_decoder_(std::make_unique<data_decoder::DataDecoder>()),
+      snapshots_(std::move(snapshots)),
+      callback_(std::move(callback)) {
+  base::PostTaskAndReplyWithResult(
+      download::GetDownloadTaskRunner().get(), FROM_HERE,
+      base::BindOnce(
+          [](const base::FilePath& file_path) {
+            const uint32_t file_flags =
+                base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE;
+            return base::File(file_path, file_flags);
+          },
+          file_path),
+      base::BindOnce(&SaveAsWebBundleJob::OnFileAvailable,
+                     base::Unretained(this)));
+}
+
+SaveAsWebBundleJob::~SaveAsWebBundleJob() = default;
+
+void SaveAsWebBundleJob::OnFileAvailable(base::File file) {
+  if (!file.IsValid()) {
+    LOG(ERROR) << "Failed to create file to save page as WebBundle.";
+    OnFinished(0, data_decoder::mojom::WebBundlerError::kFileOpenFailed);
+    return;
+  }
+  data_decoder_->GetService()->BindWebBundler(
+      bundler_.BindNewPipeAndPassReceiver());
+  bundler_.set_disconnect_handler(base::BindOnce(
+      &SaveAsWebBundleJob::OnConnectionError, base::Unretained(this)));
+  bundler_->Generate(
+      std::move(snapshots_), std::move(file),
+      base::BindOnce(&SaveAsWebBundleJob::OnGenerated, base::Unretained(this)));
+}
+
+void SaveAsWebBundleJob::OnConnectionError() {
+  OnFinished(0,
+             data_decoder::mojom::WebBundlerError::kWebBundlerConnectionError);
+}
+
+void SaveAsWebBundleJob::OnGenerated(
+    uint64_t file_size,
+    data_decoder::mojom::WebBundlerError error) {
+  OnFinished(file_size, error);
+}
+
+void SaveAsWebBundleJob::OnFinished(
+    uint64_t file_size,
+    data_decoder::mojom::WebBundlerError error) {
+  DCHECK(callback_);
+  std::move(callback_).Run(file_size, error);
+  delete this;  // This is the last time the SaveAsWebBundleJob is referenced.
+}
+
+}  // namespace content
diff --git a/content/browser/web_package/save_as_web_bundle_job.h b/content/browser/web_package/save_as_web_bundle_job.h
new file mode 100644
index 0000000..e78fe49
--- /dev/null
+++ b/content/browser/web_package/save_as_web_bundle_job.h
@@ -0,0 +1,74 @@
+// 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 CONTENT_BROWSER_WEB_PACKAGE_SAVE_AS_WEB_BUNDLE_JOB_H_
+#define CONTENT_BROWSER_WEB_PACKAGE_SAVE_AS_WEB_BUNDLE_JOB_H_
+
+#include <memory>
+#include <vector>
+
+#include "base/callback_forward.h"
+#include "base/files/file.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/remote.h"
+#include "services/data_decoder/public/mojom/resource_snapshot_for_web_bundle.mojom.h"
+#include "services/data_decoder/public/mojom/web_bundler.mojom.h"
+
+namespace base {
+class FilePath;
+}  // namespace base
+
+namespace data_decoder {
+class DataDecoder;
+}  // namespace data_decoder
+
+namespace content {
+
+class WebContents;
+
+// This class is used by WebContents::GenerateWebBundle() method to generate
+// a Web Bundle file. The instances are created by Start() static method. Every
+// instance is self-owned and responsible for deleting itself upon invoking
+// OnFinished.
+class SaveAsWebBundleJob {
+ public:
+  static void Start(
+      WebContents* web_contents,
+      const base::FilePath& file_path,
+      base::OnceCallback<void(uint64_t /* file_size */,
+                              data_decoder::mojom::WebBundlerError)> callback);
+
+  SaveAsWebBundleJob(const SaveAsWebBundleJob&) = delete;
+  SaveAsWebBundleJob& operator=(const SaveAsWebBundleJob&) = delete;
+
+ private:
+  SaveAsWebBundleJob(
+      const base::FilePath& file_path,
+      std::vector<mojo::PendingRemote<
+          data_decoder::mojom::ResourceSnapshotForWebBundle>> snapshots,
+      base::OnceCallback<void(uint64_t /* file_size */,
+                              data_decoder::mojom::WebBundlerError)> callback);
+  ~SaveAsWebBundleJob();
+
+  void OnFileAvailable(base::File file);
+  void OnConnectionError();
+  void OnGenerated(uint64_t file_size,
+                   data_decoder::mojom::WebBundlerError error);
+
+  void OnFinished(uint64_t file_size,
+                  data_decoder::mojom::WebBundlerError error);
+
+  std::unique_ptr<data_decoder::DataDecoder> data_decoder_;
+  std::vector<
+      mojo::PendingRemote<data_decoder::mojom::ResourceSnapshotForWebBundle>>
+      snapshots_;
+  mojo::Remote<data_decoder::mojom::WebBundler> bundler_;
+  base::OnceCallback<void(uint64_t /* file_size */,
+                          data_decoder::mojom::WebBundlerError)>
+      callback_;
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_WEB_PACKAGE_SAVE_AS_WEB_BUNDLE_JOB_H_
diff --git a/content/browser/web_package/save_page_as_web_bundle_browsertest.cc b/content/browser/web_package/save_page_as_web_bundle_browsertest.cc
index 788b5dbf8..67095f1 100644
--- a/content/browser/web_package/save_page_as_web_bundle_browsertest.cc
+++ b/content/browser/web_package/save_page_as_web_bundle_browsertest.cc
@@ -2,22 +2,34 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <tuple>
+
 #include "base/base_paths.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
 #include "base/path_service.h"
 #include "base/strings/stringprintf.h"
 #include "base/test/bind_test_util.h"
+#include "base/threading/scoped_blocking_call.h"
 #include "content/browser/frame_host/render_frame_host_impl.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/test/content_browser_test.h"
 #include "content/public/test/content_browser_test_utils.h"
 #include "content/shell/browser/shell.h"
+#include "mojo/public/cpp/bindings/receiver.h"
+#include "services/data_decoder/public/cpp/test_support/in_process_data_decoder.h"
 #include "services/data_decoder/public/mojom/resource_snapshot_for_web_bundle.mojom.h"
 
 namespace content {
 namespace {
 
+const char kOnePageSimplePath[] =
+    "/web_bundle/save_page_as_web_bundle/one_page_simple.html";
+const char kOnePageWithImgPath[] =
+    "/web_bundle/save_page_as_web_bundle/one_page_with_img.html";
+const char kImgPngPath[] = "/web_bundle/save_page_as_web_bundle/img.png";
+
 uint64_t GetResourceCount(
     mojo::Remote<data_decoder::mojom::ResourceSnapshotForWebBundle>& snapshot) {
   uint64_t count_out = 0;
@@ -63,6 +75,58 @@
   return data_out;
 }
 
+class MockWebBundler : public data_decoder::mojom::WebBundler {
+ public:
+  MockWebBundler() = default;
+  ~MockWebBundler() override {
+    if (file_.IsValid()) {
+      base::ScopedAllowBlockingForTesting allow_blocking;
+      file_.Close();
+    }
+  }
+
+  MockWebBundler(const MockWebBundler&) = delete;
+  MockWebBundler& operator=(const MockWebBundler&) = delete;
+
+  void Bind(mojo::PendingReceiver<data_decoder::mojom::WebBundler> receiver) {
+    receiver_.Bind(std::move(receiver));
+  }
+
+  void WaitUntilGenerateCalled() {
+    if (callback_)
+      return;
+    base::RunLoop loop;
+    generate_called_callback_ = loop.QuitClosure();
+    loop.Run();
+  }
+
+  void ResetReceiver() { receiver_.reset(); }
+
+ private:
+  // mojom::WebBundleParserFactory implementation.
+  void Generate(
+      std::vector<mojo::PendingRemote<
+          data_decoder::mojom::ResourceSnapshotForWebBundle>> snapshots,
+      base::File file,
+      GenerateCallback callback) override {
+    DCHECK(!callback_);
+    snapshots_ = std::move(snapshots);
+    file_ = std::move(file);
+    callback_ = std::move(callback);
+    if (generate_called_callback_)
+      std::move(generate_called_callback_).Run();
+  }
+
+  std::vector<
+      mojo::PendingRemote<data_decoder::mojom::ResourceSnapshotForWebBundle>>
+      snapshots_;
+  base::File file_;
+  GenerateCallback callback_;
+  base::OnceClosure generate_called_callback_;
+
+  mojo::Receiver<data_decoder::mojom::WebBundler> receiver_{this};
+};
+
 }  // namespace
 
 class SavePageAsWebBundleBrowserTest : public ContentBrowserTest {
@@ -82,11 +146,36 @@
             snapshot.BindNewPipeAndPassReceiver());
     return snapshot;
   }
+
+  bool CreateSaveDir() {
+    base::ScopedAllowBlockingForTesting allow_blocking;
+    return save_dir_.CreateUniqueTempDir();
+  }
+
+  std::tuple<uint64_t, data_decoder::mojom::WebBundlerError> GenerateWebBundle(
+      const base::FilePath& file_path) {
+    uint64_t ret_file_size = 0;
+    data_decoder::mojom::WebBundlerError ret_error =
+        data_decoder::mojom::WebBundlerError::kOK;
+    base::RunLoop run_loop;
+    shell()->web_contents()->GenerateWebBundle(
+        file_path, base::BindLambdaForTesting(
+                       [&run_loop, &ret_file_size, &ret_error](
+                           uint64_t file_size,
+                           data_decoder::mojom::WebBundlerError error) {
+                         ret_file_size = file_size;
+                         ret_error = error;
+                         run_loop.Quit();
+                       }));
+    run_loop.Run();
+    return std::make_tuple(ret_file_size, ret_error);
+  }
+
+  base::ScopedTempDir save_dir_;
 };
 
-IN_PROC_BROWSER_TEST_F(SavePageAsWebBundleBrowserTest, OnePageSimple) {
-  const auto page_url = embedded_test_server()->GetURL(
-      "/web_bundle/save_page_as_web_bundle/one_page_simple.html");
+IN_PROC_BROWSER_TEST_F(SavePageAsWebBundleBrowserTest, SnapshotOnePageSimple) {
+  const auto page_url = embedded_test_server()->GetURL(kOnePageSimplePath);
   auto snapshot = NavigateAndGetSnapshot(page_url);
   ASSERT_EQ(1u, GetResourceCount(snapshot));
 
@@ -116,11 +205,9 @@
   EXPECT_FALSE(GetResourceBody(snapshot, 1).has_value());
 }
 
-IN_PROC_BROWSER_TEST_F(SavePageAsWebBundleBrowserTest, OnePageWithImg) {
-  const auto page_url = embedded_test_server()->GetURL(
-      "/web_bundle/save_page_as_web_bundle/one_page_with_img.html");
-  const auto img_url = embedded_test_server()->GetURL(
-      "/web_bundle/save_page_as_web_bundle/img.png");
+IN_PROC_BROWSER_TEST_F(SavePageAsWebBundleBrowserTest, SnapshotOnePageWithImg) {
+  const auto page_url = embedded_test_server()->GetURL(kOnePageWithImgPath);
+  const auto img_url = embedded_test_server()->GetURL(kImgPngPath);
   auto snapshot = NavigateAndGetSnapshot(page_url);
   ASSERT_EQ(2u, GetResourceCount(snapshot));
 
@@ -176,4 +263,69 @@
 // TODO(crbug.com/1040752): Implement sub frames support and add tests.
 // TODO(crbug.com/1040752): Implement style sheet support and add tests.
 
+IN_PROC_BROWSER_TEST_F(SavePageAsWebBundleBrowserTest,
+                       GenerateOnePageSimpleWebBundle) {
+  const auto page_url = embedded_test_server()->GetURL(kOnePageSimplePath);
+  NavigateToURLBlockUntilNavigationsComplete(shell(), page_url, 1);
+  ASSERT_TRUE(CreateSaveDir());
+  const auto file_path =
+      save_dir_.GetPath().Append(FILE_PATH_LITERAL("test.wbn"));
+  // Currently WebBundler in the data decoder service is not implemented yet,
+  // and just returns kNotImplemented.
+  // TODO(crbug.com/1040752): Implement WebBundler and update test.
+  EXPECT_EQ(
+      std::make_tuple(0, data_decoder::mojom::WebBundlerError::kNotImplemented),
+      GenerateWebBundle(file_path));
+}
+
+IN_PROC_BROWSER_TEST_F(SavePageAsWebBundleBrowserTest,
+                       GenerateWebBundleInvalidFilePath) {
+  const auto page_url = embedded_test_server()->GetURL(kOnePageSimplePath);
+  NavigateToURLBlockUntilNavigationsComplete(shell(), page_url, 1);
+  ASSERT_TRUE(CreateSaveDir());
+  const auto file_path = save_dir_.GetPath();
+  // Generating Web Bundle file using the existing directory path name must
+  // fail with kFileOpenFailed error.
+  EXPECT_EQ(
+      std::make_tuple(0, data_decoder::mojom::WebBundlerError::kFileOpenFailed),
+      GenerateWebBundle(file_path));
+}
+
+IN_PROC_BROWSER_TEST_F(SavePageAsWebBundleBrowserTest,
+                       GenerateWebBundleConnectionError) {
+  data_decoder::test::InProcessDataDecoder in_process_data_decoder;
+  MockWebBundler mock_web_bundler;
+  in_process_data_decoder.service().SetWebBundlerBinderForTesting(
+      base::BindRepeating(&MockWebBundler::Bind,
+                          base::Unretained(&mock_web_bundler)));
+
+  const auto page_url = embedded_test_server()->GetURL(kOnePageSimplePath);
+  NavigateToURLBlockUntilNavigationsComplete(shell(), page_url, 1);
+  ASSERT_TRUE(CreateSaveDir());
+  const auto file_path =
+      save_dir_.GetPath().Append(FILE_PATH_LITERAL("test.wbn"));
+  uint64_t result_file_size = 0ul;
+  data_decoder::mojom::WebBundlerError result_error =
+      data_decoder::mojom::WebBundlerError::kOK;
+
+  base::RunLoop run_loop;
+  shell()->web_contents()->GenerateWebBundle(
+      file_path,
+      base::BindLambdaForTesting(
+          [&run_loop, &result_file_size, &result_error](
+              uint64_t file_size, data_decoder::mojom::WebBundlerError error) {
+            result_file_size = file_size;
+            result_error = error;
+            run_loop.Quit();
+          }));
+  mock_web_bundler.WaitUntilGenerateCalled();
+  mock_web_bundler.ResetReceiver();
+  run_loop.Run();
+  // When the connection to the WebBundler in the data decoder service is
+  // disconnected, the result must be kWebBundlerConnectionError.
+  EXPECT_EQ(0ULL, result_file_size);
+  EXPECT_EQ(data_decoder::mojom::WebBundlerError::kWebBundlerConnectionError,
+            result_error);
+}
+
 }  // namespace content
diff --git a/content/common/frame_messages.h b/content/common/frame_messages.h
index 4bdc050..c71e15c 100644
--- a/content/common/frame_messages.h
+++ b/content/common/frame_messages.h
@@ -30,7 +30,6 @@
 #include "content/common/navigation_params.h"
 #include "content/common/savable_subframe.h"
 #include "content/public/common/common_param_traits.h"
-#include "content/public/common/context_menu_params.h"
 #include "content/public/common/frame_navigate_params.h"
 #include "content/public/common/page_state.h"
 #include "content/public/common/previews_state.h"
@@ -38,6 +37,7 @@
 #include "content/public/common/screen_info.h"
 #include "content/public/common/stop_find_action.h"
 #include "content/public/common/three_d_api_types.h"
+#include "content/public/common/untrustworthy_context_menu_params.h"
 #include "ipc/ipc_message_macros.h"
 #include "ipc/ipc_platform_file.h"
 #include "mojo/public/cpp/system/message_pipe.h"
diff --git a/content/public/browser/BUILD.gn b/content/public/browser/BUILD.gn
index 5d7bdc1..0b52f75 100644
--- a/content/public/browser/BUILD.gn
+++ b/content/public/browser/BUILD.gn
@@ -121,6 +121,8 @@
     "content_index_context.h",
     "content_index_provider.cc",
     "content_index_provider.h",
+    "context_menu_params.cc",
+    "context_menu_params.h",
     "cookie_store_factory.h",
     "cors_exempt_headers.cc",
     "cors_exempt_headers.h",
diff --git a/content/public/browser/context_menu_params.cc b/content/public/browser/context_menu_params.cc
new file mode 100644
index 0000000..dd1f25d
--- /dev/null
+++ b/content/public/browser/context_menu_params.cc
@@ -0,0 +1,17 @@
+// 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 "content/public/browser/context_menu_params.h"
+
+namespace content {
+
+ContextMenuParams::ContextMenuParams() = default;
+ContextMenuParams::ContextMenuParams(const ContextMenuParams& other) = default;
+ContextMenuParams::~ContextMenuParams() = default;
+
+ContextMenuParams::ContextMenuParams(
+    const UntrustworthyContextMenuParams& other)
+    : UntrustworthyContextMenuParams(other) {}
+
+}  // namespace content
diff --git a/content/public/browser/context_menu_params.h b/content/public/browser/context_menu_params.h
new file mode 100644
index 0000000..5d54099
--- /dev/null
+++ b/content/public/browser/context_menu_params.h
@@ -0,0 +1,50 @@
+// 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 CONTENT_PUBLIC_BROWSER_CONTEXT_MENU_PARAMS_H_
+#define CONTENT_PUBLIC_BROWSER_CONTEXT_MENU_PARAMS_H_
+
+#include "content/common/content_export.h"
+#include "content/public/common/untrustworthy_context_menu_params.h"
+#include "url/gurl.h"
+
+namespace content {
+
+class RenderFrameHostImpl;
+
+// FIXME(beng): This would be more useful in the future and more efficient
+//              if the parameters here weren't so literally mapped to what
+//              they contain for the ContextMenu task. It might be better
+//              to make the string fields more generic so that this object
+//              could be used for more contextual actions.
+//
+// SECURITY NOTE: This struct should be populated by the browser process,
+// after validating the IPC payload from UntrustworthyContextMenuParams.
+// Note that the fields declared in ContextMenuParams can be populated based on
+// the trustworthy, browser-side data (i.e. don't need to be sent over IPC and
+// therefore don't need to be covered by UntrustworthyContextMenuParams).
+struct CONTENT_EXPORT ContextMenuParams
+    : public UntrustworthyContextMenuParams {
+  ContextMenuParams();
+  ContextMenuParams(const ContextMenuParams& other);
+  ~ContextMenuParams();
+
+  // This is the URL of the top level page that the context menu was invoked
+  // on.
+  GURL page_url;
+
+  // This is the URL of the subframe that the context menu was invoked on.
+  GURL frame_url;
+
+ private:
+  // RenderFrameHostImpl is responsible for validating and sanitizing
+  // UntrustworthyContextMenuParams into ContextMenuParams and therefore is a
+  // friend.
+  friend class RenderFrameHostImpl;
+  explicit ContextMenuParams(const UntrustworthyContextMenuParams& other);
+};
+
+}  // namespace content
+
+#endif  // CONTENT_PUBLIC_BROWSER_CONTEXT_MENU_PARAMS_H_
diff --git a/content/public/browser/render_frame_host.h b/content/public/browser/render_frame_host.h
index aa562e9f..dcc9c025 100644
--- a/content/public/browser/render_frame_host.h
+++ b/content/public/browser/render_frame_host.h
@@ -298,14 +298,21 @@
   // and still has a connection.  This is valid for all frames.
   virtual bool IsRenderFrameLive() = 0;
 
-  // Returns true if this is the currently-visible RenderFrameHost for our frame
-  // tree node. During process transfer, a RenderFrameHost may be created that
-  // is not current. After process transfer, the old RenderFrameHost becomes
-  // non-current until it is deleted (which may not happen until its unload
-  // handler runs).
+  // Returns true if this RenderFrameHost is currently in the frame tree for its
+  // page. Specifically, this is when the RenderFrameHost and all of its
+  // ancestors are the current RenderFrameHost in their respective
+  // FrameTreeNodes.
   //
-  // Changes to the IsCurrent() state of a RenderFrameHost may be observed via
-  // WebContentsObserver::RenderFrameHostChanged().
+  // For instance, during a navigation, if a new RenderFrameHost replaces this
+  // RenderFrameHost, IsCurrent() becomes false for this frame and its
+  // children even if the children haven't been replaced.
+  //
+  // After a RenderFrameHost has been replaced in its frame, it will either:
+  //  1) Enter the BackForwardCache.
+  //  2) Start running unload handlers and will be deleted after this ("pending
+  //  deletion").
+  // In both cases, IsCurrent() becomes false for this frame and all its
+  // children.
   virtual bool IsCurrent() = 0;
 
   // Get the number of proxies to this frame, in all processes. Exposed for
diff --git a/content/public/browser/web_contents.h b/content/public/browser/web_contents.h
index 96d22f68..cf981807 100644
--- a/content/public/browser/web_contents.h
+++ b/content/public/browser/web_contents.h
@@ -33,6 +33,7 @@
 #include "content/public/browser/visibility.h"
 #include "content/public/common/stop_find_action.h"
 #include "mojo/public/cpp/bindings/remote.h"
+#include "services/data_decoder/public/mojom/web_bundler.mojom.h"
 #include "services/metrics/public/cpp/ukm_source_id.h"
 #include "third_party/blink/public/common/frame/sandbox_flags.h"
 #include "third_party/blink/public/mojom/frame/find_in_page.mojom-forward.h"
@@ -819,11 +820,18 @@
   // the file size and more.
   virtual void GenerateMHTML(
       const MHTMLGenerationParams& params,
-      base::OnceCallback<void(int64_t /* size of the file */)> callback) = 0;
+      base::OnceCallback<void(int64_t /* file_size */)> callback) = 0;
   virtual void GenerateMHTMLWithResult(
       const MHTMLGenerationParams& params,
       MHTMLGenerationResult::GenerateMHTMLCallback callback) = 0;
 
+  // Generates a Web Bundle representation of the current page.
+  virtual void GenerateWebBundle(
+      const base::FilePath& file_path,
+      base::OnceCallback<void(uint64_t /* file_size */,
+                              data_decoder::mojom::WebBundlerError)>
+          callback) = 0;
+
   // Returns the contents MIME type after a navigation.
   virtual const std::string& GetContentsMimeType() = 0;
 
diff --git a/content/public/common/BUILD.gn b/content/public/common/BUILD.gn
index 4c6ffb5..962a420 100644
--- a/content/public/common/BUILD.gn
+++ b/content/public/common/BUILD.gn
@@ -117,8 +117,6 @@
     "content_switch_dependent_feature_overrides.h",
     "content_switches.cc",
     "content_switches.h",
-    "context_menu_params.cc",
-    "context_menu_params.h",
     "drop_data.cc",
     "drop_data.h",
     "font_cache_dispatcher_win.h",
@@ -174,6 +172,8 @@
     "stop_find_action.h",
     "storage_quota_params.h",
     "three_d_api_types.h",
+    "untrustworthy_context_menu_params.cc",
+    "untrustworthy_context_menu_params.h",
     "url_constants.cc",
     "url_constants.h",
     "url_utils.cc",
diff --git a/content/public/common/context_menu_params.cc b/content/public/common/untrustworthy_context_menu_params.cc
similarity index 76%
rename from content/public/common/context_menu_params.cc
rename to content/public/common/untrustworthy_context_menu_params.cc
index 524c40d..f3b15a4 100644
--- a/content/public/common/context_menu_params.cc
+++ b/content/public/common/untrustworthy_context_menu_params.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "content/public/common/context_menu_params.h"
+#include "content/public/common/untrustworthy_context_menu_params.h"
 
 namespace content {
 
@@ -11,8 +11,7 @@
 CustomContextMenuContext::CustomContextMenuContext()
     : is_pepper_menu(false),
       request_id(0),
-      render_widget_id(kCurrentRenderWidget) {
-}
+      render_widget_id(kCurrentRenderWidget) {}
 
 UntrustworthyContextMenuParams::UntrustworthyContextMenuParams()
     : media_type(blink::ContextMenuDataMediaType::kNone),
@@ -39,12 +38,4 @@
 
 UntrustworthyContextMenuParams::~UntrustworthyContextMenuParams() = default;
 
-ContextMenuParams::ContextMenuParams() = default;
-ContextMenuParams::ContextMenuParams(const ContextMenuParams& other) = default;
-ContextMenuParams::~ContextMenuParams() = default;
-
-ContextMenuParams::ContextMenuParams(
-    const UntrustworthyContextMenuParams& other)
-    : UntrustworthyContextMenuParams(other) {}
-
 }  // namespace content
diff --git a/content/public/common/context_menu_params.h b/content/public/common/untrustworthy_context_menu_params.h
similarity index 76%
rename from content/public/common/context_menu_params.h
rename to content/public/common/untrustworthy_context_menu_params.h
index 80afbaa..dc333a4 100644
--- a/content/public/common/context_menu_params.h
+++ b/content/public/common/untrustworthy_context_menu_params.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CONTENT_PUBLIC_COMMON_CONTEXT_MENU_PARAMS_H_
-#define CONTENT_PUBLIC_COMMON_CONTEXT_MENU_PARAMS_H_
+#ifndef CONTENT_PUBLIC_COMMON_UNTRUSTWORTHY_CONTEXT_MENU_PARAMS_H_
+#define CONTENT_PUBLIC_COMMON_UNTRUSTWORTHY_CONTEXT_MENU_PARAMS_H_
 
 #include <stdint.h>
 
@@ -23,8 +23,6 @@
 
 namespace content {
 
-class RenderFrameHostImpl;
-
 struct CONTENT_EXPORT CustomContextMenuContext {
   static const int32_t kCurrentRenderWidget;
 
@@ -152,38 +150,6 @@
   int selection_start_offset;
 };
 
-// FIXME(beng): This would be more useful in the future and more efficient
-//              if the parameters here weren't so literally mapped to what
-//              they contain for the ContextMenu task. It might be better
-//              to make the string fields more generic so that this object
-//              could be used for more contextual actions.
-//
-// SECURITY NOTE: This struct should be populated by the browser process,
-// after validating the IPC payload from UntrustworthyContextMenuParams.
-// Note that the fields declared in ContextMenuParams can be populated based on
-// the trustworthy, browser-side data (i.e. don't need to be sent over IPC and
-// therefore don't need to be covered by UntrustworthyContextMenuParams).
-struct CONTENT_EXPORT ContextMenuParams
-    : public UntrustworthyContextMenuParams {
-  ContextMenuParams();
-  ContextMenuParams(const ContextMenuParams& other);
-  ~ContextMenuParams();
-
-  // This is the URL of the top level page that the context menu was invoked
-  // on.
-  GURL page_url;
-
-  // This is the URL of the subframe that the context menu was invoked on.
-  GURL frame_url;
-
- private:
-  // RenderFrameHostImpl is responsible for validating and sanitizing
-  // UntrustworthyContextMenuParams into ContextMenuParams and therefore is a
-  // friend.
-  friend class RenderFrameHostImpl;
-  explicit ContextMenuParams(const UntrustworthyContextMenuParams& other);
-};
-
 }  // namespace content
 
-#endif  // CONTENT_PUBLIC_COMMON_CONTEXT_MENU_PARAMS_H_
+#endif  // CONTENT_PUBLIC_COMMON_UNTRUSTWORTHY_CONTEXT_MENU_PARAMS_H_
diff --git a/content/public/test/browser_test_utils.h b/content/public/test/browser_test_utils.h
index 75b32d0a..123f31b 100644
--- a/content/public/test/browser_test_utils.h
+++ b/content/public/test/browser_test_utils.h
@@ -33,9 +33,9 @@
 #include "content/public/browser/render_widget_host.h"
 #include "content/public/browser/web_contents_delegate.h"
 #include "content/public/browser/web_contents_observer.h"
-#include "content/public/common/context_menu_params.h"
 #include "content/public/common/isolated_world_ids.h"
 #include "content/public/common/page_type.h"
+#include "content/public/common/untrustworthy_context_menu_params.h"
 #include "ipc/message_filter.h"
 #include "net/base/load_flags.h"
 #include "services/network/public/mojom/network_service.mojom.h"
diff --git a/content/renderer/context_menu_params_builder.cc b/content/renderer/context_menu_params_builder.cc
index ef437a3..3ef0203 100644
--- a/content/renderer/context_menu_params_builder.cc
+++ b/content/renderer/context_menu_params_builder.cc
@@ -6,7 +6,7 @@
 
 #include <stddef.h>
 
-#include "content/public/common/context_menu_params.h"
+#include "content/public/common/untrustworthy_context_menu_params.h"
 #include "content/public/renderer/content_renderer_client.h"
 #include "content/renderer/history_serialization.h"
 #include "content/renderer/menu_item_builder.h"
diff --git a/content/renderer/media/gpu/gpu_video_accelerator_factories_impl.cc b/content/renderer/media/gpu/gpu_video_accelerator_factories_impl.cc
index 8684360..d051233 100644
--- a/content/renderer/media/gpu/gpu_video_accelerator_factories_impl.cc
+++ b/content/renderer/media/gpu/gpu_video_accelerator_factories_impl.cc
@@ -425,9 +425,9 @@
   return supported_vea_profiles_;
 }
 
-scoped_refptr<viz::ContextProvider>
+viz::RasterContextProvider*
 GpuVideoAcceleratorFactoriesImpl::GetMediaContextProvider() {
-  return CheckContextLost() ? nullptr : context_provider_;
+  return CheckContextLost() ? nullptr : context_provider_.get();
 }
 
 void GpuVideoAcceleratorFactoriesImpl::SetRenderingColorSpace(
diff --git a/content/renderer/media/gpu/gpu_video_accelerator_factories_impl.h b/content/renderer/media/gpu/gpu_video_accelerator_factories_impl.h
index ffe9fa2..c15269d 100644
--- a/content/renderer/media/gpu/gpu_video_accelerator_factories_impl.h
+++ b/content/renderer/media/gpu/gpu_video_accelerator_factories_impl.h
@@ -116,7 +116,7 @@
   base::Optional<media::VideoEncodeAccelerator::SupportedProfiles>
   GetVideoEncodeAcceleratorSupportedProfiles() override;
 
-  scoped_refptr<viz::ContextProvider> GetMediaContextProvider() override;
+  viz::RasterContextProvider* GetMediaContextProvider() override;
 
   void SetRenderingColorSpace(const gfx::ColorSpace& color_space) override;
 
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index bb807c7a..b635410 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -73,11 +73,11 @@
 #include "content/public/common/content_constants.h"
 #include "content/public/common/content_features.h"
 #include "content/public/common/content_switches.h"
-#include "content/public/common/context_menu_params.h"
 #include "content/public/common/isolated_world_ids.h"
 #include "content/public/common/navigation_policy.h"
 #include "content/public/common/page_state.h"
 #include "content/public/common/service_manager_connection.h"
+#include "content/public/common/untrustworthy_context_menu_params.h"
 #include "content/public/common/url_constants.h"
 #include "content/public/common/url_utils.h"
 #include "content/public/renderer/browser_plugin_delegate.h"
diff --git a/content/renderer/render_widget.cc b/content/renderer/render_widget.cc
index 2370f1c..48965b64f 100644
--- a/content/renderer/render_widget.cc
+++ b/content/renderer/render_widget.cc
@@ -48,9 +48,9 @@
 #include "content/public/common/content_client.h"
 #include "content/public/common/content_features.h"
 #include "content/public/common/content_switches.h"
-#include "content/public/common/context_menu_params.h"
 #include "content/public/common/drop_data.h"
 #include "content/public/common/service_names.mojom.h"
+#include "content/public/common/untrustworthy_context_menu_params.h"
 #include "content/public/common/use_zoom_for_dsf_policy.h"
 #include "content/public/renderer/content_renderer_client.h"
 #include "content/public/renderer/render_thread.h"
diff --git a/content/renderer/render_widget_screen_metrics_emulator.cc b/content/renderer/render_widget_screen_metrics_emulator.cc
index 91112bf5..c1dbc65 100644
--- a/content/renderer/render_widget_screen_metrics_emulator.cc
+++ b/content/renderer/render_widget_screen_metrics_emulator.cc
@@ -4,7 +4,7 @@
 
 #include "content/renderer/render_widget_screen_metrics_emulator.h"
 
-#include "content/public/common/context_menu_params.h"
+#include "content/public/common/untrustworthy_context_menu_params.h"
 #include "content/renderer/render_widget_screen_metrics_emulator_delegate.h"
 
 namespace content {
diff --git a/content/renderer/renderer_blink_platform_impl.cc b/content/renderer/renderer_blink_platform_impl.cc
index d381148..8c641e6e 100644
--- a/content/renderer/renderer_blink_platform_impl.cc
+++ b/content/renderer/renderer_blink_platform_impl.cc
@@ -493,7 +493,7 @@
       RenderFrame::GetRoutingIdForWebFrame(web_frame), params);
 }
 
-viz::ContextProvider*
+viz::RasterContextProvider*
 RendererBlinkPlatformImpl::SharedMainThreadContextProvider() {
   return RenderThreadImpl::current()->SharedMainThreadContextProvider().get();
 }
diff --git a/content/renderer/renderer_blink_platform_impl.h b/content/renderer/renderer_blink_platform_impl.h
index bb6da44..80490f5b8 100644
--- a/content/renderer/renderer_blink_platform_impl.h
+++ b/content/renderer/renderer_blink_platform_impl.h
@@ -51,6 +51,10 @@
 class SharedURLLoaderFactory;
 }
 
+namespace viz {
+class RasterContextProvider;
+}
+
 namespace content {
 class ChildURLLoaderFactoryBundle;
 class ThreadSafeSender;
@@ -123,7 +127,7 @@
   scoped_refptr<media::AudioCapturerSource> NewAudioCapturerSource(
       blink::WebLocalFrame* web_frame,
       const media::AudioSourceParameters& params) override;
-  viz::ContextProvider* SharedMainThreadContextProvider() override;
+  viz::RasterContextProvider* SharedMainThreadContextProvider() override;
   bool RTCSmoothnessAlgorithmEnabled() override;
   base::Optional<double> GetWebRtcMaxCaptureFrameRate() override;
   scoped_refptr<media::AudioRendererSink> NewAudioRendererSink(
diff --git a/content/shell/browser/shell_web_contents_view_delegate.h b/content/shell/browser/shell_web_contents_view_delegate.h
index 11e152c..96795bb0 100644
--- a/content/shell/browser/shell_web_contents_view_delegate.h
+++ b/content/shell/browser/shell_web_contents_view_delegate.h
@@ -9,9 +9,9 @@
 
 #include "base/macros.h"
 #include "build/build_config.h"
+#include "content/public/browser/context_menu_params.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_contents_view_delegate.h"
-#include "content/public/common/context_menu_params.h"
 
 #if defined(TOOLKIT_VIEWS)
 #include "ui/base/models/simple_menu_model.h"    // nogncheck
diff --git a/content/shell/browser/shell_web_contents_view_delegate_android.cc b/content/shell/browser/shell_web_contents_view_delegate_android.cc
index 37ea944e..3fb55a94 100644
--- a/content/shell/browser/shell_web_contents_view_delegate_android.cc
+++ b/content/shell/browser/shell_web_contents_view_delegate_android.cc
@@ -5,8 +5,8 @@
 #include "content/shell/browser/shell_web_contents_view_delegate.h"
 
 #include "base/command_line.h"
+#include "content/public/browser/context_menu_params.h"
 #include "content/public/browser/web_contents.h"
-#include "content/public/common/context_menu_params.h"
 #include "content/shell/browser/shell_web_contents_view_delegate_creator.h"
 
 namespace content {
diff --git a/content/shell/browser/shell_web_contents_view_delegate_mac.mm b/content/shell/browser/shell_web_contents_view_delegate_mac.mm
index 06ece29..5bf15982 100644
--- a/content/shell/browser/shell_web_contents_view_delegate_mac.mm
+++ b/content/shell/browser/shell_web_contents_view_delegate_mac.mm
@@ -7,12 +7,12 @@
 #import  <Cocoa/Cocoa.h>
 
 #include "base/command_line.h"
+#include "content/public/browser/context_menu_params.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/render_view_host.h"
 #include "content/public/browser/render_widget_host_view.h"
 #include "content/public/browser/web_contents.h"
-#include "content/public/common/context_menu_params.h"
 #include "content/shell/browser/renderer_host/shell_render_widget_host_view_mac_delegate.h"
 #include "content/shell/browser/shell.h"
 #include "content/shell/browser/shell_browser_context.h"
diff --git a/content/shell/browser/shell_web_contents_view_delegate_views.cc b/content/shell/browser/shell_web_contents_view_delegate_views.cc
index 9289cdb..14728ef 100644
--- a/content/shell/browser/shell_web_contents_view_delegate_views.cc
+++ b/content/shell/browser/shell_web_contents_view_delegate_views.cc
@@ -5,6 +5,7 @@
 #include "content/shell/browser/shell_web_contents_view_delegate.h"
 
 #include "base/strings/utf_string_conversions.h"
+#include "content/public/browser/context_menu_params.h"
 #include "content/public/browser/web_contents.h"
 #include "content/shell/browser/shell_devtools_frontend.h"
 #include "content/shell/common/shell_switches.h"
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index ff845c4..d44098e 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -1124,7 +1124,6 @@
   public_deps = [
     "//content:content_resources",
     "//content:dev_ui_content_resources",
-    "//services/data_decoder/public/mojom:mojom_resource_snapshot_for_web_bundle",
   ]
 
   deps = [
diff --git a/content/test/gpu/gpu_tests/test_expectations_unittest.py b/content/test/gpu/gpu_tests/test_expectations_unittest.py
index 64ed0325..6d6040c 100644
--- a/content/test/gpu/gpu_tests/test_expectations_unittest.py
+++ b/content/test/gpu/gpu_tests/test_expectations_unittest.py
@@ -36,6 +36,8 @@
 _map_specific_to_generic = {sos:'win' for sos in WIN_CONDITIONS}
 _map_specific_to_generic.update({sos:'mac' for sos in MAC_CONDITIONS})
 _map_specific_to_generic.update({sos:'android' for sos in ANDROID_CONDITIONS})
+_map_specific_to_generic['debug-x64'] = 'debug'
+_map_specific_to_generic['release-x64'] = 'release'
 
 _get_generic = lambda tags: set(
     [_map_specific_to_generic.get(tag, tag) for tag in tags])
diff --git a/content/test/web_contents_observer_sanity_checker.cc b/content/test/web_contents_observer_sanity_checker.cc
index 7ea7c26..cb1e58db 100644
--- a/content/test/web_contents_observer_sanity_checker.cc
+++ b/content/test/web_contents_observer_sanity_checker.cc
@@ -123,11 +123,15 @@
     RenderFrameHost* new_host) {
   CHECK(new_host);
   CHECK_NE(new_host, old_host);
+  CHECK(GetRoutingPair(old_host) != GetRoutingPair(new_host));
 
   if (old_host) {
     EnsureStableParentValue(old_host);
     CHECK_EQ(old_host->GetParent(), new_host->GetParent());
     GlobalRoutingID routing_pair = GetRoutingPair(old_host);
+    // If the navigation requires a new RFH, IsCurrent on old host should be
+    // false.
+    CHECK(!old_host->IsCurrent());
     bool old_did_exist = !!current_hosts_.erase(routing_pair);
     if (!old_did_exist) {
       CHECK(false)
@@ -136,6 +140,7 @@
     }
   }
 
+  CHECK(new_host->IsCurrent());
   EnsureStableParentValue(new_host);
   if (new_host->GetParent()) {
     AssertRenderFrameExists(new_host->GetParent());
@@ -226,6 +231,9 @@
   CHECK(!navigation_handle->HasCommitted() ||
         navigation_handle->GetRenderFrameHost() != nullptr);
 
+  CHECK(!navigation_handle->HasCommitted() ||
+        navigation_handle->GetRenderFrameHost()->IsCurrent());
+
   // If ReadyToCommitNavigation was dispatched, verify that the
   // |navigation_handle| has the same RenderFrameHost at this time as the one
   // returned at ReadyToCommitNavigation.
diff --git a/extensions/shell/browser/shell_app_view_guest_delegate.h b/extensions/shell/browser/shell_app_view_guest_delegate.h
index f74ff6d..80e444f8 100644
--- a/extensions/shell/browser/shell_app_view_guest_delegate.h
+++ b/extensions/shell/browser/shell_app_view_guest_delegate.h
@@ -6,7 +6,7 @@
 #define EXTENSIONS_SHELL_BROWSER_SHELL_APP_VIEW_GUEST_DELEGATE_H_
 
 #include "base/macros.h"
-#include "content/public/common/context_menu_params.h"
+#include "content/public/browser/context_menu_params.h"
 #include "extensions/browser/guest_view/app_view/app_view_guest_delegate.h"
 
 namespace extensions {
diff --git a/fuchsia/engine/BUILD.gn b/fuchsia/engine/BUILD.gn
index 421df73..6bba840 100644
--- a/fuchsia/engine/BUILD.gn
+++ b/fuchsia/engine/BUILD.gn
@@ -134,6 +134,8 @@
     "browser/frame_impl.h",
     "browser/frame_layout_manager.cc",
     "browser/frame_layout_manager.h",
+    "browser/frame_permission_controller.cc",
+    "browser/frame_permission_controller.h",
     "browser/media_resource_provider_service.cc",
     "browser/media_resource_provider_service.h",
     "browser/navigation_controller_impl.cc",
@@ -154,8 +156,8 @@
     "browser/web_engine_devtools_controller.h",
     "browser/web_engine_net_log_observer.cc",
     "browser/web_engine_net_log_observer.h",
-    "browser/web_engine_permission_manager.cc",
-    "browser/web_engine_permission_manager.h",
+    "browser/web_engine_permission_delegate.cc",
+    "browser/web_engine_permission_delegate.h",
     "common/web_engine_content_client.cc",
     "common/web_engine_content_client.h",
     "common/web_engine_url_loader_throttle.cc",
diff --git a/fuchsia/engine/browser/DEPS b/fuchsia/engine/browser/DEPS
index 9daf81a..eb0865fe 100644
--- a/fuchsia/engine/browser/DEPS
+++ b/fuchsia/engine/browser/DEPS
@@ -12,6 +12,7 @@
   "+third_party/blink/public/common/logging",
   "+third_party/blink/public/common/messaging",
   "+third_party/blink/public/mojom/messaging",
+  "+third_party/blink/public/mojom/permissions",
   "+third_party/widevine/cdm",
   "+ui/accessibility",
   "+ui/aura",
diff --git a/fuchsia/engine/browser/frame_impl.cc b/fuchsia/engine/browser/frame_impl.cc
index 7c25d973..e9b69b4f 100644
--- a/fuchsia/engine/browser/frame_impl.cc
+++ b/fuchsia/engine/browser/frame_impl.cc
@@ -201,6 +201,38 @@
   return *frame_impl_map;
 }
 
+content::PermissionType FidlPermissionTypeToContentPermissionType(
+    fuchsia::web::PermissionType fidl_type) {
+  switch (fidl_type) {
+    case fuchsia::web::PermissionType::MICROPHONE:
+      return content::PermissionType::AUDIO_CAPTURE;
+    case fuchsia::web::PermissionType::CAMERA:
+      return content::PermissionType::VIDEO_CAPTURE;
+    case fuchsia::web::PermissionType::PROTECTED_MEDIA_IDENTIFIER:
+      return content::PermissionType::PROTECTED_MEDIA_IDENTIFIER;
+    case fuchsia::web::PermissionType::PERSISTENT_STORAGE:
+      return content::PermissionType::DURABLE_STORAGE;
+  }
+}
+
+base::Optional<url::Origin> ParseAndValidateWebOrigin(
+    const std::string& origin_str) {
+  GURL origin_url(origin_str);
+  if (!origin_url.username().empty() || !origin_url.password().empty() ||
+      !origin_url.query().empty() || !origin_url.ref().empty()) {
+    return base::nullopt;
+  }
+
+  if (!origin_url.path().empty() && origin_url.path() != "/")
+    return base::nullopt;
+
+  auto origin = url::Origin::Create(origin_url);
+  if (origin.opaque())
+    return base::nullopt;
+
+  return origin;
+}
+
 }  // namespace
 
 // static
@@ -780,6 +812,36 @@
     layout_manager_->ForceContentDimensions(web_dips_converted);
 }
 
+void FrameImpl::SetPermissionState(
+    fuchsia::web::PermissionDescriptor fidl_permission,
+    std::string web_origin_string,
+    fuchsia::web::PermissionState fidl_state) {
+  if (!fidl_permission.has_type()) {
+    LOG(ERROR) << "PermissionDescriptor.type is not specified in "
+                  "SetPermissionState().";
+    CloseAndDestroyFrame(ZX_ERR_INVALID_ARGS);
+    return;
+  }
+
+  auto web_origin = ParseAndValidateWebOrigin(web_origin_string);
+  if (!web_origin) {
+    LOG(ERROR) << "SetPermissionState() called with invalid web_origin: "
+               << web_origin_string;
+    CloseAndDestroyFrame(ZX_ERR_INVALID_ARGS);
+    return;
+  }
+
+  content::PermissionType type =
+      FidlPermissionTypeToContentPermissionType(fidl_permission.type());
+
+  blink::mojom::PermissionStatus state =
+      (fidl_state == fuchsia::web::PermissionState::GRANTED)
+          ? blink::mojom::PermissionStatus::GRANTED
+          : blink::mojom::PermissionStatus::DENIED;
+
+  permission_controller_.SetPermissionState(type, web_origin.value(), state);
+}
+
 void FrameImpl::CloseContents(content::WebContents* source) {
   DCHECK_EQ(source, web_contents_.get());
   context_->DestroyFrame(this);
@@ -830,8 +892,9 @@
     return;
 
   if (!navigation_handle->IsInMainFrame() ||
-      navigation_handle->IsSameDocument() || navigation_handle->IsErrorPage())
+      navigation_handle->IsSameDocument() || navigation_handle->IsErrorPage()) {
     return;
+  }
 
   mojo::AssociatedRemote<mojom::OnLoadScriptInjector>
       before_load_script_injector;
diff --git a/fuchsia/engine/browser/frame_impl.h b/fuchsia/engine/browser/frame_impl.h
index 63af67ab..77f886e 100644
--- a/fuchsia/engine/browser/frame_impl.h
+++ b/fuchsia/engine/browser/frame_impl.h
@@ -22,6 +22,7 @@
 #include "content/public/browser/web_contents_observer.h"
 #include "fuchsia/engine/browser/accessibility_bridge.h"
 #include "fuchsia/engine/browser/discarding_event_filter.h"
+#include "fuchsia/engine/browser/frame_permission_controller.h"
 #include "fuchsia/engine/browser/navigation_controller_impl.h"
 #include "fuchsia/engine/browser/url_request_rewrite_rules_manager.h"
 #include "fuchsia/engine/on_load_script_injector.mojom.h"
@@ -57,6 +58,10 @@
 
   uint64_t media_session_id() const { return media_session_id_; }
 
+  FramePermissionController* permission_controller() {
+    return &permission_controller_;
+  }
+
   zx::unowned_channel GetBindingChannelForTest() const;
   content::WebContents* web_contents_for_test() const {
     return web_contents_.get();
@@ -167,6 +172,9 @@
   void SetMediaSessionId(uint64_t session_id) override;
   void ForceContentDimensions(
       std::unique_ptr<fuchsia::ui::gfx::vec2> web_dips) override;
+  void SetPermissionState(fuchsia::web::PermissionDescriptor permission,
+                          std::string web_origin,
+                          fuchsia::web::PermissionState state) override;
 
   // content::WebContentsDelegate implementation.
   void CloseContents(content::WebContents* source) override;
@@ -221,6 +229,7 @@
   std::vector<uint64_t> before_load_scripts_order_;
   base::RepeatingCallback<void(base::StringPiece)> console_log_message_hook_;
   UrlRequestRewriteRulesManager url_request_rewrite_rules_manager_;
+  FramePermissionController permission_controller_;
 
   // Session ID to use for fuchsia.media.AudioConsumer. Set with
   // SetMediaSessionId().
diff --git a/fuchsia/engine/browser/frame_permission_controller.cc b/fuchsia/engine/browser/frame_permission_controller.cc
new file mode 100644
index 0000000..1b9381f
--- /dev/null
+++ b/fuchsia/engine/browser/frame_permission_controller.cc
@@ -0,0 +1,76 @@
+// 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 "fuchsia/engine/browser/frame_permission_controller.h"
+
+#include "base/logging.h"
+#include "url/origin.h"
+
+using PermissionState = blink::mojom::PermissionStatus;
+using PermissionType = content::PermissionType;
+
+namespace {
+
+size_t GetPermissionIndex(PermissionType type) {
+  size_t index = static_cast<size_t>(type);
+  DCHECK_LT(index, static_cast<size_t>(PermissionType::NUM));
+  return index;
+}
+
+}  // namespace
+
+FramePermissionController::PermissionSet::PermissionSet() {
+  for (auto& permission : permission_state) {
+    permission = PermissionState::DENIED;
+  }
+}
+
+FramePermissionController::FramePermissionController() = default;
+FramePermissionController::~FramePermissionController() = default;
+
+void FramePermissionController::SetPermissionState(PermissionType permission,
+                                                   const url::Origin& origin,
+                                                   PermissionState state) {
+  auto it = per_origin_permissions_.find(origin);
+  if (it == per_origin_permissions_.end()) {
+    // All permissions are denied by default.
+    if (state == PermissionState::DENIED)
+      return;
+
+    it = per_origin_permissions_.insert(std::make_pair(origin, PermissionSet()))
+             .first;
+  }
+
+  it->second.permission_state[GetPermissionIndex(permission)] = state;
+}
+
+PermissionState FramePermissionController::GetPermissionState(
+    PermissionType permission,
+    const url::Origin& origin) {
+  auto it = per_origin_permissions_.find(origin);
+  if (it == per_origin_permissions_.end()) {
+    return PermissionState::DENIED;
+  }
+  return it->second.permission_state[GetPermissionIndex(permission)];
+}
+
+void FramePermissionController::RequestPermissions(
+    const std::vector<PermissionType>& permissions,
+    const url::Origin& origin,
+    bool user_gesture,
+    base::OnceCallback<void(const std::vector<PermissionState>&)> callback) {
+  std::vector<PermissionState> result;
+
+  auto it = per_origin_permissions_.find(origin);
+  if (it == per_origin_permissions_.end()) {
+    result.resize(permissions.size(), PermissionState::DENIED);
+  } else {
+    result.reserve(permissions.size());
+    for (auto& permission : permissions) {
+      result.push_back(
+          it->second.permission_state[GetPermissionIndex(permission)]);
+    }
+  }
+  std::move(callback).Run(result);
+}
diff --git a/fuchsia/engine/browser/frame_permission_controller.h b/fuchsia/engine/browser/frame_permission_controller.h
new file mode 100644
index 0000000..d0f4eb75
--- /dev/null
+++ b/fuchsia/engine/browser/frame_permission_controller.h
@@ -0,0 +1,65 @@
+// 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 FUCHSIA_ENGINE_BROWSER_FRAME_PERMISSION_CONTROLLER_H_
+#define FUCHSIA_ENGINE_BROWSER_FRAME_PERMISSION_CONTROLLER_H_
+
+#include <map>
+#include <vector>
+
+#include "base/callback_forward.h"
+#include "content/public/browser/permission_type.h"
+#include "third_party/blink/public/mojom/permissions/permission_status.mojom.h"
+
+namespace url {
+class Origin;
+}
+
+// FramePermissionController is responsible for web permissions state for a
+// fuchsia.web.Frame instance.
+class FramePermissionController {
+ public:
+  FramePermissionController();
+  ~FramePermissionController();
+
+  FramePermissionController(FramePermissionController&) = delete;
+  FramePermissionController& operator=(FramePermissionController&) = delete;
+
+  // Sets the |state| for the specified |permission| and |origin|.
+  void SetPermissionState(content::PermissionType permission,
+                          const url::Origin& origin,
+                          blink::mojom::PermissionStatus state);
+
+  // Returns current permission state of the specified |permission| and
+  // |origin|.
+  blink::mojom::PermissionStatus GetPermissionState(
+      content::PermissionType permission,
+      const url::Origin& origin);
+
+  // Requests permission state for the specified |permissions|. When the request
+  // is resolved, the |callback| is called with a list of status values, one for
+  // each value in |permissions|, in the same order.
+  //
+  // TODO(crbug.com/922833): Current implementation doesn't actually prompt the
+  // user: all permissions in the PROMPT state are denied silently. Define
+  // fuchsia.web.PermissionManager protocol and use it to request permissions.
+  void RequestPermissions(
+      const std::vector<content::PermissionType>& permissions,
+      const url::Origin& origin,
+      bool user_gesture,
+      base::OnceCallback<
+          void(const std::vector<blink::mojom::PermissionStatus>&)> callback);
+
+ private:
+  struct PermissionSet {
+    PermissionSet();
+
+    blink::mojom::PermissionStatus
+        permission_state[static_cast<int>(content::PermissionType::NUM)];
+  };
+
+  std::map<url::Origin, PermissionSet> per_origin_permissions_;
+};
+
+#endif  // FUCHSIA_ENGINE_BROWSER_FRAME_PERMISSION_CONTROLLER_H_
\ No newline at end of file
diff --git a/fuchsia/engine/browser/web_engine_browser_context.cc b/fuchsia/engine/browser/web_engine_browser_context.cc
index 4818de1..06defeb 100644
--- a/fuchsia/engine/browser/web_engine_browser_context.cc
+++ b/fuchsia/engine/browser/web_engine_browser_context.cc
@@ -18,7 +18,7 @@
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/resource_context.h"
 #include "fuchsia/engine/browser/web_engine_net_log_observer.h"
-#include "fuchsia/engine/browser/web_engine_permission_manager.h"
+#include "fuchsia/engine/browser/web_engine_permission_delegate.h"
 #include "services/network/public/cpp/network_switches.h"
 
 class WebEngineBrowserContext::ResourceContext
@@ -123,9 +123,9 @@
 
 content::PermissionControllerDelegate*
 WebEngineBrowserContext::GetPermissionControllerDelegate() {
-  if (!permission_manager_)
-    permission_manager_ = std::make_unique<WebEnginePermissionManager>();
-  return permission_manager_.get();
+  if (!permission_delegate_)
+    permission_delegate_ = std::make_unique<WebEnginePermissionDelegate>();
+  return permission_delegate_.get();
 }
 
 content::ClientHintsControllerDelegate*
diff --git a/fuchsia/engine/browser/web_engine_browser_context.h b/fuchsia/engine/browser/web_engine_browser_context.h
index 13e7da6f..f0b72bcc 100644
--- a/fuchsia/engine/browser/web_engine_browser_context.h
+++ b/fuchsia/engine/browser/web_engine_browser_context.h
@@ -13,7 +13,7 @@
 #include "content/public/browser/browser_context.h"
 
 class WebEngineNetLogObserver;
-class WebEnginePermissionManager;
+class WebEnginePermissionDelegate;
 
 class WebEngineBrowserContext : public content::BrowserContext {
  public:
@@ -52,7 +52,7 @@
   std::unique_ptr<WebEngineNetLogObserver> net_log_observer_;
   std::unique_ptr<SimpleFactoryKey> simple_factory_key_;
   std::unique_ptr<ResourceContext> resource_context_;
-  std::unique_ptr<WebEnginePermissionManager> permission_manager_;
+  std::unique_ptr<WebEnginePermissionDelegate> permission_delegate_;
 
   DISALLOW_COPY_AND_ASSIGN(WebEngineBrowserContext);
 };
diff --git a/fuchsia/engine/browser/web_engine_permission_delegate.cc b/fuchsia/engine/browser/web_engine_permission_delegate.cc
new file mode 100644
index 0000000..ae20cb9
--- /dev/null
+++ b/fuchsia/engine/browser/web_engine_permission_delegate.cc
@@ -0,0 +1,97 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "fuchsia/engine/browser/web_engine_permission_delegate.h"
+
+#include "base/callback.h"
+#include "base/logging.h"
+#include "content/public/browser/permission_controller.h"
+#include "content/public/browser/permission_type.h"
+#include "fuchsia/engine/browser/frame_impl.h"
+#include "url/origin.h"
+
+WebEnginePermissionDelegate::WebEnginePermissionDelegate() = default;
+WebEnginePermissionDelegate::~WebEnginePermissionDelegate() = default;
+
+int WebEnginePermissionDelegate::RequestPermission(
+    content::PermissionType permission,
+    content::RenderFrameHost* render_frame_host,
+    const GURL& origin,
+    bool user_gesture,
+    base::OnceCallback<void(blink::mojom::PermissionStatus)> callback) {
+  std::vector<content::PermissionType> permissions{permission};
+  RequestPermissions(
+      permissions, render_frame_host, origin, user_gesture,
+      base::BindOnce(
+          [](base::OnceCallback<void(blink::mojom::PermissionStatus)> callback,
+             const std::vector<blink::mojom::PermissionStatus>& state) {
+            DCHECK_EQ(state.size(), 1U);
+            std::move(callback).Run(state[0]);
+          },
+          base::Passed(std::move(callback))));
+
+  return content::PermissionController::kNoPendingOperation;
+}
+
+int WebEnginePermissionDelegate::RequestPermissions(
+    const std::vector<content::PermissionType>& permissions,
+    content::RenderFrameHost* render_frame_host,
+    const GURL& requesting_origin,
+    bool user_gesture,
+    base::OnceCallback<void(const std::vector<blink::mojom::PermissionStatus>&)>
+        callback) {
+  FrameImpl* frame = FrameImpl::FromRenderFrameHost(render_frame_host);
+  DCHECK(frame);
+  frame->permission_controller()->RequestPermissions(
+      permissions, url::Origin::Create(requesting_origin), user_gesture,
+      std::move(callback));
+
+  return content::PermissionController::kNoPendingOperation;
+}
+
+void WebEnginePermissionDelegate::ResetPermission(
+    content::PermissionType permission,
+    const GURL& requesting_origin,
+    const GURL& embedding_origin) {
+  // TODO(crbug.com/922833): Update PermissionControllerDelegate to pass
+  // RenderFrameHost.
+  NOTIMPLEMENTED() << ": " << static_cast<int>(permission);
+}
+
+blink::mojom::PermissionStatus WebEnginePermissionDelegate::GetPermissionStatus(
+    content::PermissionType permission,
+    const GURL& requesting_origin,
+    const GURL& embedding_origin) {
+  // GetPermissionStatus() is deprecated and it's not expected to be called in
+  // WebEngine.
+  NOTREACHED();
+  return blink::mojom::PermissionStatus::DENIED;
+}
+
+blink::mojom::PermissionStatus
+WebEnginePermissionDelegate::GetPermissionStatusForFrame(
+    content::PermissionType permission,
+    content::RenderFrameHost* render_frame_host,
+    const GURL& requesting_origin) {
+  FrameImpl* frame = FrameImpl::FromRenderFrameHost(render_frame_host);
+  DCHECK(frame);
+  return frame->permission_controller()->GetPermissionState(
+      permission, url::Origin::Create(requesting_origin));
+}
+
+int WebEnginePermissionDelegate::SubscribePermissionStatusChange(
+    content::PermissionType permission,
+    content::RenderFrameHost* render_frame_host,
+    const GURL& requesting_origin,
+    base::RepeatingCallback<void(blink::mojom::PermissionStatus)> callback) {
+  // TODO(crbug.com/922833): Implement permission status subscription. It's
+  // used in blink to emit PermissionStatus.onchange notifications.
+  NOTIMPLEMENTED() << ": " << static_cast<int>(permission);
+  return content::PermissionController::kNoPendingOperation;
+}
+
+void WebEnginePermissionDelegate::UnsubscribePermissionStatusChange(
+    int subscription_id) {
+  NOTREACHED();
+}
diff --git a/fuchsia/engine/browser/web_engine_permission_manager.h b/fuchsia/engine/browser/web_engine_permission_delegate.h
similarity index 75%
rename from fuchsia/engine/browser/web_engine_permission_manager.h
rename to fuchsia/engine/browser/web_engine_permission_delegate.h
index d0fa135..036207b 100644
--- a/fuchsia/engine/browser/web_engine_permission_manager.h
+++ b/fuchsia/engine/browser/web_engine_permission_delegate.h
@@ -2,17 +2,22 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef FUCHSIA_ENGINE_BROWSER_WEB_ENGINE_PERMISSION_MANAGER_H_
-#define FUCHSIA_ENGINE_BROWSER_WEB_ENGINE_PERMISSION_MANAGER_H_
+#ifndef FUCHSIA_ENGINE_BROWSER_WEB_ENGINE_PERMISSION_DELEGATE_H_
+#define FUCHSIA_ENGINE_BROWSER_WEB_ENGINE_PERMISSION_DELEGATE_H_
 
-#include "base/macros.h"
 #include "content/public/browser/permission_controller_delegate.h"
 
-class WebEnginePermissionManager
+// PermissionControllerDelegate implementation for WebEngine. It redirects
+// permission redirects all calls to the appropriate FramePermissionController
+// instance.
+class WebEnginePermissionDelegate
     : public content::PermissionControllerDelegate {
  public:
-  WebEnginePermissionManager();
-  ~WebEnginePermissionManager() override;
+  WebEnginePermissionDelegate();
+  ~WebEnginePermissionDelegate() override;
+
+  WebEnginePermissionDelegate(WebEnginePermissionDelegate&) = delete;
+  WebEnginePermissionDelegate& operator=(WebEnginePermissionDelegate&) = delete;
 
   // content::PermissionControllerDelegate implementation:
   int RequestPermission(content::PermissionType permission,
@@ -47,9 +52,6 @@
       base::RepeatingCallback<void(blink::mojom::PermissionStatus)> callback)
       override;
   void UnsubscribePermissionStatusChange(int subscription_id) override;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(WebEnginePermissionManager);
 };
 
-#endif  // FUCHSIA_ENGINE_BROWSER_WEB_ENGINE_PERMISSION_MANAGER_H_
+#endif  // FUCHSIA_ENGINE_BROWSER_WEB_ENGINE_PERMISSION_DELEGATE_H_
diff --git a/fuchsia/engine/browser/web_engine_permission_manager.cc b/fuchsia/engine/browser/web_engine_permission_manager.cc
deleted file mode 100644
index cac44b9..0000000
--- a/fuchsia/engine/browser/web_engine_permission_manager.cc
+++ /dev/null
@@ -1,86 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "fuchsia/engine/browser/web_engine_permission_manager.h"
-
-#include "base/callback.h"
-#include "base/logging.h"
-#include "content/public/browser/permission_controller.h"
-#include "content/public/browser/permission_type.h"
-
-namespace {
-blink::mojom::PermissionStatus CheckPermissionStatus(
-    content::PermissionType permission_type) {
-  // TODO(crbug/922833): Temporary grant permission of
-  // PROTECTED_MEDIA_IDENTIFIER to unblock EME development.
-  return permission_type == content::PermissionType::PROTECTED_MEDIA_IDENTIFIER
-             ? blink::mojom::PermissionStatus::GRANTED
-             : blink::mojom::PermissionStatus::DENIED;
-}
-}  // namespace
-
-WebEnginePermissionManager::WebEnginePermissionManager() = default;
-
-WebEnginePermissionManager::~WebEnginePermissionManager() = default;
-
-int WebEnginePermissionManager::RequestPermission(
-    content::PermissionType permission,
-    content::RenderFrameHost* render_frame_host,
-    const GURL& origin,
-    bool user_gesture,
-    base::OnceCallback<void(blink::mojom::PermissionStatus)> callback) {
-  std::move(callback).Run(CheckPermissionStatus(permission));
-  return content::PermissionController::kNoPendingOperation;
-}
-
-int WebEnginePermissionManager::RequestPermissions(
-    const std::vector<content::PermissionType>& permissions,
-    content::RenderFrameHost* render_frame_host,
-    const GURL& requesting_origin,
-    bool user_gesture,
-    base::OnceCallback<void(const std::vector<blink::mojom::PermissionStatus>&)>
-        callback) {
-  std::vector<blink::mojom::PermissionStatus> statuses;
-  for (auto permission : permissions)
-    statuses.push_back(CheckPermissionStatus(permission));
-
-  std::move(callback).Run(statuses);
-  return content::PermissionController::kNoPendingOperation;
-}
-
-void WebEnginePermissionManager::ResetPermission(
-    content::PermissionType permission,
-    const GURL& requesting_origin,
-    const GURL& embedding_origin) {
-  NOTIMPLEMENTED() << ": " << static_cast<int>(permission);
-}
-
-blink::mojom::PermissionStatus WebEnginePermissionManager::GetPermissionStatus(
-    content::PermissionType permission,
-    const GURL& requesting_origin,
-    const GURL& embedding_origin) {
-  return CheckPermissionStatus(permission);
-}
-
-blink::mojom::PermissionStatus
-WebEnginePermissionManager::GetPermissionStatusForFrame(
-    content::PermissionType permission,
-    content::RenderFrameHost* render_frame_host,
-    const GURL& requesting_origin) {
-  return CheckPermissionStatus(permission);
-}
-
-int WebEnginePermissionManager::SubscribePermissionStatusChange(
-    content::PermissionType permission,
-    content::RenderFrameHost* render_frame_host,
-    const GURL& requesting_origin,
-    base::RepeatingCallback<void(blink::mojom::PermissionStatus)> callback) {
-  NOTIMPLEMENTED() << ": " << static_cast<int>(permission);
-  return content::PermissionController::kNoPendingOperation;
-}
-
-void WebEnginePermissionManager::UnsubscribePermissionStatusChange(
-    int subscription_id) {
-  NOTIMPLEMENTED();
-}
diff --git a/fuchsia/engine/test/data/check_mic_permission.html b/fuchsia/engine/test/data/check_mic_permission.html
new file mode 100644
index 0000000..1b433be
--- /dev/null
+++ b/fuchsia/engine/test/data/check_mic_permission.html
@@ -0,0 +1,12 @@
+<html>
+  <body>
+    <video></video>
+    <script>
+      window.onload = () => {
+        navigator.permissions.query({name: 'microphone'}).then(r => {
+          document.title = r.state;
+        });
+      }
+    </script>
+  </body>
+</html>
diff --git a/fuchsia/engine/web_engine_integration_test.cc b/fuchsia/engine/web_engine_integration_test.cc
index b3efd93..8eda74e 100644
--- a/fuchsia/engine/web_engine_integration_test.cc
+++ b/fuchsia/engine/web_engine_integration_test.cc
@@ -129,6 +129,8 @@
   }
 
  protected:
+  void RunPermissionTest(bool grant);
+
   const base::test::TaskEnvironment task_environment_;
 
   fidl::InterfaceHandle<fuchsia::sys::ComponentController>
@@ -398,3 +400,38 @@
   frame_->SetNavigationEventListener(listener_binding.NewBinding());
   navigation_listener.RunUntilTitleEquals("ended");
 }
+
+void WebEngineIntegrationTest::RunPermissionTest(bool grant) {
+  StartWebEngine();
+
+  fuchsia::web::CreateContextParams create_params =
+      DefaultContextParamsWithTestData();
+  CreateContextAndFrame(std::move(create_params));
+
+  if (grant) {
+    // Grant microphone permission.
+    fuchsia::web::PermissionDescriptor mic_permission;
+    mic_permission.set_type(fuchsia::web::PermissionType::MICROPHONE);
+    frame_->SetPermissionState(std::move(mic_permission),
+                               "fuchsia-dir://testdata/",
+                               fuchsia::web::PermissionState::GRANTED);
+  }
+
+  EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse(
+      navigation_controller_.get(), fuchsia::web::LoadUrlParams(),
+      "fuchsia-dir://testdata/check_mic_permission.html"));
+
+  cr_fuchsia::TestNavigationListener navigation_listener;
+  fidl::Binding<fuchsia::web::NavigationEventListener> listener_binding(
+      &navigation_listener);
+  frame_->SetNavigationEventListener(listener_binding.NewBinding());
+  navigation_listener.RunUntilTitleEquals(grant ? "granted" : "denied");
+}
+
+TEST_F(WebEngineIntegrationTest, PermissionDenied) {
+  RunPermissionTest(false);
+}
+
+TEST_F(WebEngineIntegrationTest, PermissionGranted) {
+  RunPermissionTest(true);
+}
diff --git a/fuchsia/runners/cast/cast_runner_integration_test.cc b/fuchsia/runners/cast/cast_runner_integration_test.cc
index daa168ef..8c85220 100644
--- a/fuchsia/runners/cast/cast_runner_integration_test.cc
+++ b/fuchsia/runners/cast/cast_runner_integration_test.cc
@@ -42,10 +42,16 @@
 
 namespace {
 
-const char kTestServerRoot[] =
+constexpr char kTestAppId[] = "00000000";
+
+constexpr char kBlankAppUrl[] = "/defaultresponse";
+constexpr char kEchoHeaderPath[] = "/echoheader?Test";
+constexpr char kEchoAppPath[] = "/echo.html";
+
+constexpr char kTestServerRoot[] =
     FILE_PATH_LITERAL("fuchsia/runners/cast/testdata");
 
-const char kDummyAgentUrl[] =
+constexpr char kDummyAgentUrl[] =
     "fuchsia-pkg://fuchsia.com/dummy_agent#meta/dummy_agent.cmx";
 
 void ComponentErrorHandler(zx_status_t status) {
@@ -189,48 +195,6 @@
     base::RunLoop().RunUntilIdle();
   }
 
-  fuchsia::sys::ComponentControllerPtr StartCastComponent(
-      base::StringPiece component_url) {
-    // Configure the Runner, including a service directory channel to publish
-    // services to.
-    fidl::InterfaceHandle<fuchsia::io::Directory> directory;
-    component_services_.GetOrCreateDirectory("svc")->Serve(
-        fuchsia::io::OPEN_RIGHT_READABLE | fuchsia::io::OPEN_RIGHT_WRITABLE,
-        directory.NewRequest().TakeChannel());
-    fuchsia::sys::StartupInfo startup_info;
-    startup_info.launch_info.url = component_url.as_string();
-
-    fidl::InterfaceHandle<fuchsia::io::Directory> outgoing_directory;
-    startup_info.launch_info.directory_request =
-        outgoing_directory.NewRequest().TakeChannel();
-
-    fidl::InterfaceHandle<::fuchsia::io::Directory> svc_directory;
-    CHECK_EQ(fdio_service_connect_at(
-                 outgoing_directory.channel().get(), "svc",
-                 svc_directory.NewRequest().TakeChannel().release()),
-             ZX_OK);
-
-    component_services_client_ =
-        std::make_unique<sys::ServiceDirectory>(std::move(svc_directory));
-
-    // Place the ServiceDirectory in the |flat_namespace|.
-    startup_info.flat_namespace.paths.emplace_back(
-        base::fuchsia::kServiceDirectoryPath);
-    startup_info.flat_namespace.directories.emplace_back(
-        directory.TakeChannel());
-
-    fuchsia::sys::Package package;
-    package.resolved_url = component_url.as_string();
-
-    fuchsia::sys::ComponentControllerPtr component_controller;
-    cast_runner_ptr_->StartComponent(std::move(package),
-                                     std::move(startup_info),
-                                     component_controller.NewRequest());
-
-    EXPECT_TRUE(component_controller.is_bound());
-    return component_controller;
-  }
-
  protected:
   explicit CastRunnerIntegrationTest(
       fuchsia::web::ContextFeatureFlags feature_flags) {
@@ -275,14 +239,99 @@
     return component_state;
   }
 
-  std::unique_ptr<cr_fuchsia::FakeComponentContext> CreateComponentContext(
-      const base::StringPiece& component_url) {
-    return std::make_unique<cr_fuchsia::FakeComponentContext>(
+  void RegisterAppWithTestData(GURL url) {
+    fuchsia::web::ContentDirectoryProvider provider;
+    provider.set_name("testdata");
+    base::FilePath pkg_path;
+    CHECK(base::PathService::Get(base::DIR_ASSETS, &pkg_path));
+    provider.set_directory(base::fuchsia::OpenDirectory(
+        pkg_path.AppendASCII("fuchsia/runners/cast/testdata")));
+    std::vector<fuchsia::web::ContentDirectoryProvider> providers;
+    providers.emplace_back(std::move(provider));
+
+    auto app_config =
+        FakeApplicationConfigManager::CreateConfig(kTestAppId, url);
+    app_config.set_content_directories_for_isolated_application(
+        std::move(providers));
+    app_config_manager_.AddAppConfig(std::move(app_config));
+  }
+
+  void CreateComponentContextAndStartComponent() {
+    auto component_url = base::StringPrintf("cast:%s", kTestAppId);
+    CreateComponentContext(component_url);
+    StartCastComponent(component_url);
+    WaitComponentCreated();
+  }
+
+  void CreateComponentContext(const base::StringPiece& component_url) {
+    component_context_ = std::make_unique<cr_fuchsia::FakeComponentContext>(
         base::BindRepeating(&CastRunnerIntegrationTest::OnComponentConnect,
                             base::Unretained(this)),
         &component_services_, component_url);
   }
 
+  void StartCastComponent(base::StringPiece component_url) {
+    // Configure the Runner, including a service directory channel to publish
+    // services to.
+    fidl::InterfaceHandle<fuchsia::io::Directory> directory;
+    component_services_.GetOrCreateDirectory("svc")->Serve(
+        fuchsia::io::OPEN_RIGHT_READABLE | fuchsia::io::OPEN_RIGHT_WRITABLE,
+        directory.NewRequest().TakeChannel());
+    fuchsia::sys::StartupInfo startup_info;
+    startup_info.launch_info.url = component_url.as_string();
+
+    fidl::InterfaceHandle<fuchsia::io::Directory> outgoing_directory;
+    startup_info.launch_info.directory_request =
+        outgoing_directory.NewRequest().TakeChannel();
+
+    fidl::InterfaceHandle<::fuchsia::io::Directory> svc_directory;
+    CHECK_EQ(fdio_service_connect_at(
+                 outgoing_directory.channel().get(), "svc",
+                 svc_directory.NewRequest().TakeChannel().release()),
+             ZX_OK);
+
+    component_services_client_ =
+        std::make_unique<sys::ServiceDirectory>(std::move(svc_directory));
+
+    // Place the ServiceDirectory in the |flat_namespace|.
+    startup_info.flat_namespace.paths.emplace_back(
+        base::fuchsia::kServiceDirectoryPath);
+    startup_info.flat_namespace.directories.emplace_back(
+        directory.TakeChannel());
+
+    fuchsia::sys::Package package;
+    package.resolved_url = component_url.as_string();
+
+    cast_runner_ptr_->StartComponent(std::move(package),
+                                     std::move(startup_info),
+                                     component_controller_.NewRequest());
+    component_controller_.set_error_handler(&ComponentErrorHandler);
+  }
+
+  void WaitComponentCreated() {
+    EXPECT_FALSE(cast_component_);
+
+    base::RunLoop run_loop;
+    cr_fuchsia::ResultReceiver<WebComponent*> component_receiver(
+        run_loop.QuitClosure());
+    cast_runner_->SetWebComponentCreatedCallbackForTest(
+        base::AdaptCallbackForRepeating(
+            component_receiver.GetReceiveCallback()));
+    run_loop.Run();
+    ASSERT_NE(*component_receiver, nullptr);
+    cast_component_ = reinterpret_cast<CastComponent*>(*component_receiver);
+  }
+
+  void WaitUrlAndTitle(const GURL& url, const std::string& title) {
+    base::RunLoop run_loop;
+    cr_fuchsia::TestNavigationListener listener;
+    fidl::Binding<fuchsia::web::NavigationEventListener> listener_binding(
+        &listener);
+    cast_component_->frame()->SetNavigationEventListener(
+        listener_binding.NewBinding());
+    listener.RunUntilUrlAndTitleEquals(url, title);
+  }
+
   base::test::SingleThreadTaskEnvironment task_environment_{
       base::test::SingleThreadTaskEnvironment::MainThreadType::IO};
   net::EmbeddedTestServer test_server_;
@@ -294,8 +343,10 @@
   // Incoming service directory, ComponentContext and per-component state.
   sys::OutgoingDirectory component_services_;
   std::unique_ptr<cr_fuchsia::FakeComponentContext> component_context_;
+  fuchsia::sys::ComponentControllerPtr component_controller_;
   std::unique_ptr<sys::ServiceDirectory> component_services_client_;
   FakeComponentState* component_state_ = nullptr;
+  CastComponent* cast_component_ = nullptr;
 
   // ServiceDirectory into which the CastRunner will publish itself.
   sys::OutgoingDirectory outgoing_directory_;
@@ -308,33 +359,13 @@
 // A basic integration test ensuring a basic cast request launches the right
 // URL in the Chromium service.
 TEST_F(CastRunnerIntegrationTest, BasicRequest) {
-  const char kBlankAppId[] = "00000000";
-  const char kBlankAppPath[] = "/defaultresponse";
-  app_config_manager_.AddAppMapping(kBlankAppId,
-                                    test_server_.GetURL(kBlankAppPath), false);
+  GURL app_url = test_server_.GetURL(kBlankAppUrl);
+  app_config_manager_.AddApp(kTestAppId, app_url);
 
-  auto component_url = base::StringPrintf("cast:%s", kBlankAppId);
-
-  // Create a FakeComponentContext and publish it into component_services_.
-  component_context_ = CreateComponentContext(component_url);
-
-  // Launch the test-app component.
-  fuchsia::sys::ComponentControllerPtr component_controller =
-      StartCastComponent(component_url);
-  component_controller.set_error_handler(&ComponentErrorHandler);
-
-  // Access the NavigationController from the CastComponent. The test will hang
-  // here if no CastComponent was created.
+  CreateComponentContextAndStartComponent();
   fuchsia::web::NavigationControllerPtr nav_controller;
-  {
-    base::RunLoop run_loop;
-    cr_fuchsia::ResultReceiver<WebComponent*> component(run_loop.QuitClosure());
-    cast_runner_->SetWebComponentCreatedCallbackForTest(
-        base::AdaptCallbackForRepeating(component.GetReceiveCallback()));
-    run_loop.Run();
-    ASSERT_NE(*component, nullptr);
-    (*component)->frame()->GetNavigationController(nav_controller.NewRequest());
-  }
+  cast_component_->frame()->GetNavigationController(
+      nav_controller.NewRequest());
 
   // Ensure the NavigationState has the expected URL.
   {
@@ -345,7 +376,7 @@
         cr_fuchsia::CallbackToFitFunction(nav_entry.GetReceiveCallback()));
     run_loop.Run();
     ASSERT_TRUE(nav_entry->has_url());
-    EXPECT_EQ(nav_entry->url(), test_server_.GetURL(kBlankAppPath).spec());
+    EXPECT_EQ(nav_entry->url(), app_url.spec());
   }
 
   EXPECT_FALSE(cast_runner_->is_headless());
@@ -354,17 +385,12 @@
   // unbound.
   base::RunLoop run_loop;
   component_state_->set_on_delete(run_loop.QuitClosure());
-  component_controller.Unbind();
+  component_controller_.Unbind();
   run_loop.Run();
 }
 
 TEST_F(CastRunnerIntegrationTest, ApiBindings) {
-  const char kBlankAppId[] = "00000000";
-  const char kBlankAppPath[] = "/echo.html";
-  auto component_url = base::StringPrintf("cast:%s", kBlankAppId);
-
-  app_config_manager_.AddAppMapping(kBlankAppId,
-                                    test_server_.GetURL(kBlankAppPath), false);
+  app_config_manager_.AddApp(kTestAppId, test_server_.GetURL(kEchoAppPath));
 
   std::vector<chromium::cast::ApiBinding> binding_list;
   chromium::cast::ApiBinding echo_binding;
@@ -374,13 +400,7 @@
   binding_list.emplace_back(std::move(echo_binding));
   api_bindings_.set_bindings(std::move(binding_list));
 
-  // Create a FakeComponentContext and publish it into component_services_.
-  component_context_ = CreateComponentContext(component_url);
-
-  // Launch the test-app component.
-  fuchsia::sys::ComponentControllerPtr component_controller =
-      StartCastComponent(component_url);
-  component_controller.set_error_handler(&ComponentErrorHandler);
+  CreateComponentContextAndStartComponent();
 
   fuchsia::web::MessagePortPtr port =
       api_bindings_.RunUntilMessagePortReceived("echoService").Bind();
@@ -409,18 +429,13 @@
 TEST_F(CastRunnerIntegrationTest, IncorrectCastAppId) {
   const char kIncorrectComponentUrl[] = "cast:99999999";
 
-  // Create a FakeComponentContext and publish it into component_services_.
-  component_context_ = CreateComponentContext(kIncorrectComponentUrl);
-
-  // Launch the a component with an invalid Cast app Id.
-  fuchsia::sys::ComponentControllerPtr component_controller =
-      StartCastComponent(kIncorrectComponentUrl);
-  component_controller.set_error_handler(&ComponentErrorHandler);
+  CreateComponentContext(kIncorrectComponentUrl);
+  StartCastComponent(kIncorrectComponentUrl);
 
   // Run the loop until the ComponentController is dropped, or a WebComponent is
   // created.
   base::RunLoop run_loop;
-  component_controller.set_error_handler([&run_loop](zx_status_t status) {
+  component_controller_.set_error_handler([&run_loop](zx_status_t status) {
     EXPECT_EQ(status, ZX_ERR_PEER_CLOSED);
     run_loop.Quit();
   });
@@ -433,62 +448,31 @@
 }
 
 TEST_F(CastRunnerIntegrationTest, UrlRequestRewriteRulesProvider) {
-  const char kEchoAppId[] = "00000000";
-  const char kEchoAppPath[] = "/echoheader?Test";
-  const GURL echo_app_url = test_server_.GetURL(kEchoAppPath);
-  auto component_url = base::StringPrintf("cast:%s", kEchoAppId);
+  GURL echo_app_url = test_server_.GetURL(kEchoHeaderPath);
+  app_config_manager_.AddApp(kTestAppId, echo_app_url);
 
-  app_config_manager_.AddAppMapping(kEchoAppId, echo_app_url, false);
-
-  // Create a FakeComponentContext and publish it into component_services_.
-  component_context_ = CreateComponentContext(component_url);
-
-  // Launch the test-app component.
-  fuchsia::sys::ComponentControllerPtr component_controller =
-      StartCastComponent(component_url);
-  component_controller.set_error_handler(&ComponentErrorHandler);
-
-  WebComponent* web_component = nullptr;
-  {
-    base::RunLoop run_loop;
-    cr_fuchsia::ResultReceiver<WebComponent*> web_component_receiver(
-        run_loop.QuitClosure());
-    cast_runner_->SetWebComponentCreatedCallbackForTest(
-        AdaptCallbackForRepeating(web_component_receiver.GetReceiveCallback()));
-    run_loop.Run();
-    ASSERT_NE(*web_component_receiver, nullptr);
-    web_component = *web_component_receiver;
-  }
+  CreateComponentContextAndStartComponent();
 
   // Bind a TestNavigationListener to the Frame.
   cr_fuchsia::TestNavigationListener navigation_listener;
   fidl::Binding<fuchsia::web::NavigationEventListener>
       navigation_listener_binding(&navigation_listener);
-  web_component->frame()->SetNavigationEventListener(
+  cast_component_->frame()->SetNavigationEventListener(
       navigation_listener_binding.NewBinding());
   navigation_listener.RunUntilUrlEquals(echo_app_url);
 
   // Check the header was properly set.
   base::Optional<base::Value> result = cr_fuchsia::ExecuteJavaScript(
-      web_component->frame(), "document.body.innerText");
+      cast_component_->frame(), "document.body.innerText");
   ASSERT_TRUE(result);
   ASSERT_TRUE(result->is_string());
   EXPECT_EQ(result->GetString(), "Value");
 }
 
 TEST_F(CastRunnerIntegrationTest, ApplicationControllerBound) {
-  const char kCastChannelAppId[] = "00000001";
-  const char kCastChannelAppPath[] = "/defaultresponse";
-  auto component_url = base::StringPrintf("cast:%s", kCastChannelAppId);
+  app_config_manager_.AddApp(kTestAppId, test_server_.GetURL(kBlankAppUrl));
 
-  app_config_manager_.AddAppMapping(
-      kCastChannelAppId, test_server_.GetURL(kCastChannelAppPath), false);
-
-  // Create a FakeComponentContext and publish it into component_services_.
-  component_context_ = CreateComponentContext(component_url);
-
-  fuchsia::sys::ComponentControllerPtr component_controller =
-      StartCastComponent(component_url);
+  CreateComponentContextAndStartComponent();
 
   // Spin the message loop to handle creation of the component state.
   base::RunLoop().RunUntilIdle();
@@ -498,20 +482,13 @@
 
 // Verify an App launched with remote debugging enabled is properly reachable.
 TEST_F(CastRunnerIntegrationTest, RemoteDebugging) {
-  const char kBlankAppId[] = "00000000";
-  const char kBlankAppPath[] = "/defaultresponse";
-  const GURL kBlankAppUrl = test_server_.GetURL(kBlankAppPath);
-  auto component_url = base::StringPrintf("cast:%s", kBlankAppId);
+  GURL app_url = test_server_.GetURL(kBlankAppUrl);
+  auto app_config =
+      FakeApplicationConfigManager::CreateConfig(kTestAppId, app_url);
+  app_config.set_enable_remote_debugging(true);
+  app_config_manager_.AddAppConfig(std::move(app_config));
 
-  app_config_manager_.AddAppMapping(kBlankAppId, kBlankAppUrl, true);
-
-  // Create a FakeComponentContext and publish it into component_services_.
-  component_context_ = CreateComponentContext(component_url);
-
-  // Launch the test-app component.
-  fuchsia::sys::ComponentControllerPtr component_controller =
-      StartCastComponent(component_url);
-  component_controller.set_error_handler(&ComponentErrorHandler);
+  CreateComponentContextAndStartComponent();
 
   // Get the remote debugging port from the Context.
   uint16_t remote_debugging_port = 0;
@@ -537,61 +514,26 @@
 
   base::Value* devtools_url = devtools_list.GetList()[0].FindPath("url");
   ASSERT_TRUE(devtools_url->is_string());
-  EXPECT_EQ(devtools_url->GetString(), kBlankAppUrl.spec());
+  EXPECT_EQ(devtools_url->GetString(), app_url.spec());
 }
 
 TEST_F(CastRunnerIntegrationTest, IsolatedContext) {
-  const char kBlankAppId[] = "00000000";
   const GURL kContentDirectoryUrl("fuchsia-dir://testdata/echo.html");
-  auto component_url = base::StringPrintf("cast:%s", kBlankAppId);
 
   EXPECT_EQ(cast_runner_->GetChildCastRunnerCountForTest(), 0u);
 
-  fuchsia::web::ContentDirectoryProvider provider;
-  provider.set_name("testdata");
-  base::FilePath pkg_path;
-  CHECK(base::PathService::Get(base::DIR_ASSETS, &pkg_path));
-  provider.set_directory(base::fuchsia::OpenDirectory(
-      pkg_path.AppendASCII("fuchsia/runners/cast/testdata")));
-  std::vector<fuchsia::web::ContentDirectoryProvider> providers;
-  providers.emplace_back(std::move(provider));
-  app_config_manager_.AddAppMappingWithContentDirectories(
-      kBlankAppId, kContentDirectoryUrl, std::move(providers));
+  RegisterAppWithTestData(kContentDirectoryUrl);
 
-  // Create a FakeComponentContext and publish it into component_services_.
-  component_context_ = CreateComponentContext(component_url);
+  CreateComponentContextAndStartComponent();
+  EXPECT_EQ(cast_runner_->GetChildCastRunnerCountForTest(), 1u);
 
-  // Launch the test-app component.
-  fuchsia::sys::ComponentControllerPtr component_controller =
-      StartCastComponent(component_url);
-  component_controller.set_error_handler(&ComponentErrorHandler);
-
-  // Navigate to the page and verify that we read it.
-  {
-    base::RunLoop run_loop;
-    cr_fuchsia::ResultReceiver<WebComponent*> web_component(
-        run_loop.QuitClosure());
-    cast_runner_->SetWebComponentCreatedCallbackForTest(
-        AdaptCallbackForRepeating(web_component.GetReceiveCallback()));
-    run_loop.Run();
-    ASSERT_NE(*web_component, nullptr);
-
-    EXPECT_EQ(cast_runner_->GetChildCastRunnerCountForTest(), 1u);
-
-    cr_fuchsia::TestNavigationListener listener;
-    fidl::Binding<fuchsia::web::NavigationEventListener> listener_binding(
-        &listener);
-    (*web_component)
-        ->frame()
-        ->SetNavigationEventListener(listener_binding.NewBinding());
-    listener.RunUntilUrlAndTitleEquals(kContentDirectoryUrl, "echo");
-  }
+  WaitUrlAndTitle(kContentDirectoryUrl, "echo");
 
   // Verify that the component is torn down when |component_controller| is
   // unbound.
   base::RunLoop run_loop;
   component_state_->set_on_delete(run_loop.QuitClosure());
-  component_controller.Unbind();
+  component_controller_.Unbind();
   run_loop.Run();
 
   EXPECT_EQ(cast_runner_->GetChildCastRunnerCountForTest(), 0u);
@@ -599,17 +541,12 @@
 
 // Test the lack of CastAgent service does not cause a CastRunner crash.
 TEST_F(CastRunnerIntegrationTest, NoCastAgent) {
-  const char kEchoAppId[] = "00000000";
-  const char kEchoAppPath[] = "/echoheader?Test";
-  const GURL echo_app_url = test_server_.GetURL(kEchoAppPath);
-  app_config_manager_.AddAppMapping(kEchoAppId, echo_app_url, false);
+  app_config_manager_.AddApp(kTestAppId, test_server_.GetURL(kEchoHeaderPath));
 
-  // Launch the test-app component.
-  fuchsia::sys::ComponentControllerPtr component_controller =
-      StartCastComponent(base::StringPrintf("cast:%s", kEchoAppId));
+  StartCastComponent(base::StringPrintf("cast:%s", kTestAppId));
 
   base::RunLoop run_loop;
-  component_controller.set_error_handler([&run_loop](zx_status_t error) {
+  component_controller_.set_error_handler([&run_loop](zx_status_t error) {
     EXPECT_EQ(error, ZX_ERR_PEER_CLOSED);
     run_loop.Quit();
   });
@@ -618,37 +555,15 @@
 
 // Test the CastAgent disconnecting does not cause a CastRunner crash.
 TEST_F(CastRunnerIntegrationTest, DisconnectedCastAgent) {
-  const char kEchoAppId[] = "00000000";
-  const char kEchoAppPath[] = "/echoheader?Test";
-  const GURL echo_app_url = test_server_.GetURL(kEchoAppPath);
-  auto component_url = base::StringPrintf("cast:%s", kEchoAppId);
+  app_config_manager_.AddApp(kTestAppId, test_server_.GetURL(kEchoHeaderPath));
 
-  app_config_manager_.AddAppMapping(kEchoAppId, echo_app_url, false);
-
-  component_context_ = CreateComponentContext(component_url);
-
-  // Launch the test-app component.
-  fuchsia::sys::ComponentControllerPtr component_controller =
-      StartCastComponent(component_url);
-
-  // Access the NavigationController from the WebComponent. The test will hang
-  // here if no WebComponent was created.
+  CreateComponentContextAndStartComponent();
   fuchsia::web::NavigationControllerPtr nav_controller;
-  {
-    base::RunLoop run_loop;
-    cr_fuchsia::ResultReceiver<WebComponent*> web_component(
-        run_loop.QuitClosure());
-    cast_runner_->SetWebComponentCreatedCallbackForTest(
-        base::AdaptCallbackForRepeating(web_component.GetReceiveCallback()));
-    run_loop.Run();
-    ASSERT_NE(*web_component, nullptr);
-    (*web_component)
-        ->frame()
-        ->GetNavigationController(nav_controller.NewRequest());
-  }
+  cast_component_->frame()->GetNavigationController(
+      nav_controller.NewRequest());
 
   base::RunLoop run_loop;
-  component_controller.set_error_handler([&run_loop](zx_status_t error) {
+  component_controller_.set_error_handler([&run_loop](zx_status_t error) {
     EXPECT_EQ(error, ZX_ERR_PEER_CLOSED);
     run_loop.Quit();
   });
@@ -665,18 +580,16 @@
 // AppConfigManager is the one used to retrieve the bindings and the rewrite
 // rules.
 TEST_F(CastRunnerIntegrationTest, ApplicationConfigAgentUrl) {
-  const char kBlankAppId[] = "00000000";
-  const char kBlankAppPath[] = "/echo.html";
-  auto component_url = base::StringPrintf("cast:%s", kBlankAppId);
-
   // These are part of the secondary agent, and CastRunner will contact
   // the secondary agent for both of them.
   FakeUrlRequestRewriteRulesProvider dummy_url_request_rewrite_rules_provider;
   TestApiBindings dummy_agent_api_bindings;
 
   // Indicate that this app is to get bindings from a secondary agent.
-  app_config_manager_.AddAppMappingWithAgent(
-      kBlankAppId, test_server_.GetURL(kBlankAppPath), false, kDummyAgentUrl);
+  auto app_config = FakeApplicationConfigManager::CreateConfig(
+      kTestAppId, test_server_.GetURL(kEchoAppPath));
+  app_config.set_agent_url(kDummyAgentUrl);
+  app_config_manager_.AddAppConfig(std::move(app_config));
 
   // Instantiate the bindings that are returned in the multi-agent scenario. The
   // bindings returned for the single-agent scenario are not initialized.
@@ -689,7 +602,8 @@
   // Assign the bindings to the multi-agent binding.
   dummy_agent_api_bindings.set_bindings(std::move(binding_list));
 
-  component_context_ = CreateComponentContext(component_url);
+  auto component_url = base::StringPrintf("cast:%s", kTestAppId);
+  CreateComponentContext(component_url);
   EXPECT_NE(component_context_, nullptr);
   component_context_->RegisterCreateComponentStateCallback(
       kDummyAgentUrl,
@@ -701,10 +615,7 @@
                 &dummy_url_request_rewrite_rules_provider);
           }));
 
-  // Launch the test-app component.
-  fuchsia::sys::ComponentControllerPtr component_controller =
-      StartCastComponent(component_url);
-  component_controller.set_error_handler(&ComponentErrorHandler);
+  StartCastComponent(component_url);
 
   base::RunLoop().RunUntilIdle();
 
@@ -718,15 +629,13 @@
 // created. Further validate that the primary agent does not provide ApiBindings
 // or RewriteRules.
 TEST_F(CastRunnerIntegrationTest, ApplicationConfigAgentUrlRewriteOptional) {
-  const char kBlankAppId[] = "00000000";
-  const char kBlankAppPath[] = "/echo.html";
-  const std::string component_url = base::StringPrintf("cast:%s", kBlankAppId);
-
   TestApiBindings dummy_agent_api_bindings;
 
   // Indicate that this app is to get bindings from a secondary agent.
-  app_config_manager_.AddAppMappingWithAgent(
-      kBlankAppId, test_server_.GetURL(kBlankAppPath), false, kDummyAgentUrl);
+  auto app_config = FakeApplicationConfigManager::CreateConfig(
+      kTestAppId, test_server_.GetURL(kEchoAppPath));
+  app_config.set_agent_url(kDummyAgentUrl);
+  app_config_manager_.AddAppConfig(std::move(app_config));
 
   // Instantiate the bindings that are returned in the multi-agent scenario. The
   // bindings returned for the single-agent scenario are not initialized.
@@ -739,7 +648,8 @@
   // Assign the bindings to the multi-agent binding.
   dummy_agent_api_bindings.set_bindings(std::move(binding_list));
 
-  component_context_ = CreateComponentContext(component_url);
+  auto component_url = base::StringPrintf("cast:%s", kTestAppId);
+  CreateComponentContext(component_url);
   EXPECT_NE(component_context_, nullptr);
   component_context_->RegisterCreateComponentStateCallback(
       kDummyAgentUrl,
@@ -751,22 +661,8 @@
                 nullptr);
           }));
 
-  // Launch the test-app component.
-  fuchsia::sys::ComponentControllerPtr component_controller =
-      StartCastComponent(component_url);
-  component_controller.set_error_handler(&ComponentErrorHandler);
-
-  // Validate that the WebComponent was created successfully when RewriteRules
-  // are not provided.
-  {
-    base::RunLoop run_loop;
-    cr_fuchsia::ResultReceiver<WebComponent*> web_component(
-        run_loop.QuitClosure());
-    cast_runner_->SetWebComponentCreatedCallbackForTest(
-        base::AdaptCallbackForRepeating(web_component.GetReceiveCallback()));
-    run_loop.Run();
-    ASSERT_NE(*web_component, nullptr);
-  }
+  StartCastComponent(component_url);
+  WaitComponentCreated();
 
   base::RunLoop().RunUntilIdle();
 
@@ -788,59 +684,27 @@
 TEST_F(HeadlessCastRunnerIntegrationTest, Headless) {
   ASSERT_TRUE(cast_runner_->is_headless());
 
-  const char kAnimationAppId[] = "00000000";
   const char kAnimationPath[] = "/css_animation.html";
   const GURL animation_url = test_server_.GetURL(kAnimationPath);
-  auto component_url = base::StringPrintf("cast:%s", kAnimationAppId);
-  app_config_manager_.AddAppMapping(kAnimationAppId, animation_url, false);
+  app_config_manager_.AddApp(kTestAppId, animation_url);
 
-  // Create a FakeComponentContext and publish it into component_services_.
-  component_context_ = CreateComponentContext(component_url);
-
-  // Launch the test-app component.
-  fuchsia::sys::ComponentControllerPtr component_controller =
-      StartCastComponent(component_url);
-  component_controller.set_error_handler(&ComponentErrorHandler);
-
-  CastComponent* component;
-
-  // Access the NavigationController from the CastComponent. The test will hang
-  // here if no CastComponent was created.
-  fuchsia::web::NavigationControllerPtr nav_controller;
-  {
-    base::RunLoop run_loop;
-    cr_fuchsia::ResultReceiver<WebComponent*> component_receiver(
-        run_loop.QuitClosure());
-    cast_runner_->SetWebComponentCreatedCallbackForTest(
-        base::AdaptCallbackForRepeating(
-            component_receiver.GetReceiveCallback()));
-    run_loop.Run();
-    ASSERT_NE(*component_receiver, nullptr);
-    component = reinterpret_cast<CastComponent*>(*component_receiver);
-  }
-
+  CreateComponentContextAndStartComponent();
   auto tokens = scenic::ViewTokenPair::New();
-  component->CreateView(std::move(tokens.view_holder_token.value), {}, {});
+  cast_component_->CreateView(std::move(tokens.view_holder_token.value), {},
+                              {});
 
-  // Ensure the NavigationState has the expected URL.
-  {
-    cr_fuchsia::TestNavigationListener listener;
-    fidl::Binding<fuchsia::web::NavigationEventListener> listener_binding(
-        &listener);
-    component->frame()->SetNavigationEventListener(
-        listener_binding.NewBinding());
-    listener.RunUntilUrlAndTitleEquals(animation_url, "animation finished");
-  }
+  WaitUrlAndTitle(animation_url, "animation finished");
 
   // Verify that dropping the "view" EventPair is handled by the CastComponent.
   {
     base::RunLoop run_loop;
-    component->set_on_headless_disconnect_for_test(run_loop.QuitClosure());
+    cast_component_->set_on_headless_disconnect_for_test(
+        run_loop.QuitClosure());
     tokens.view_token.value.reset();
     run_loop.Run();
   }
 
-  component_controller.Unbind();
+  component_controller_.Unbind();
   base::RunLoop().RunUntilIdle();
 }
 
@@ -848,58 +712,23 @@
 TEST_F(HeadlessCastRunnerIntegrationTest, IsolatedAndHeadless) {
   ASSERT_TRUE(cast_runner_->is_headless());
 
-  const char kBlankAppId[] = "00000000";
   const GURL kContentDirectoryUrl("fuchsia-dir://testdata/echo.html");
-  const std::string component_url = base::StringPrintf("cast:%s", kBlankAppId);
 
   EXPECT_EQ(cast_runner_->GetChildCastRunnerCountForTest(), 0u);
 
-  fuchsia::web::ContentDirectoryProvider provider;
-  provider.set_name("testdata");
-  base::FilePath pkg_path;
-  CHECK(base::PathService::Get(base::DIR_ASSETS, &pkg_path));
-  provider.set_directory(base::fuchsia::OpenDirectory(
-      pkg_path.AppendASCII("fuchsia/runners/cast/testdata")));
-  std::vector<fuchsia::web::ContentDirectoryProvider> providers;
-  providers.emplace_back(std::move(provider));
-  app_config_manager_.AddAppMappingWithContentDirectories(
-      kBlankAppId, kContentDirectoryUrl, std::move(providers));
+  RegisterAppWithTestData(kContentDirectoryUrl);
 
-  // Create a FakeComponentContext and publish it into component_services_.
-  component_context_ = CreateComponentContext(component_url);
+  CreateComponentContextAndStartComponent();
+  EXPECT_TRUE(cast_component_->runner()->is_headless());
+  EXPECT_EQ(cast_runner_->GetChildCastRunnerCountForTest(), 1u);
 
-  // Launch the test-app component.
-  fuchsia::sys::ComponentControllerPtr component_controller =
-      StartCastComponent(component_url);
-  component_controller.set_error_handler(&ComponentErrorHandler);
-
-  // Navigate to the page and verify that we read it.
-  {
-    base::RunLoop run_loop;
-    cr_fuchsia::ResultReceiver<WebComponent*> component(run_loop.QuitClosure());
-    cast_runner_->SetWebComponentCreatedCallbackForTest(
-        AdaptCallbackForRepeating(component.GetReceiveCallback()));
-    run_loop.Run();
-    ASSERT_NE(*component, nullptr);
-
-    EXPECT_TRUE((*component)->runner()->is_headless());
-
-    EXPECT_EQ(cast_runner_->GetChildCastRunnerCountForTest(), 1u);
-
-    cr_fuchsia::TestNavigationListener listener;
-    fidl::Binding<fuchsia::web::NavigationEventListener> listener_binding(
-        &listener);
-    (*component)
-        ->frame()
-        ->SetNavigationEventListener(listener_binding.NewBinding());
-    listener.RunUntilUrlAndTitleEquals(kContentDirectoryUrl, "echo");
-  }
+  WaitUrlAndTitle(kContentDirectoryUrl, "echo");
 
   // Verify that the component is torn down when |component_controller| is
   // unbound.
   base::RunLoop run_loop;
   component_state_->set_on_delete(run_loop.QuitClosure());
-  component_controller.Unbind();
+  component_controller_.Unbind();
   run_loop.Run();
 
   EXPECT_EQ(cast_runner_->GetChildCastRunnerCountForTest(), 0u);
diff --git a/fuchsia/runners/cast/fake_application_config_manager.cc b/fuchsia/runners/cast/fake_application_config_manager.cc
index 26ee3505..07e396c 100644
--- a/fuchsia/runners/cast/fake_application_config_manager.cc
+++ b/fuchsia/runners/cast/fake_application_config_manager.cc
@@ -14,10 +14,31 @@
     "fuchsia-pkg://fuchsia.com/cast_agent#meta/cast_agent.cmx";
 }  // namespace
 
-FakeApplicationConfigManager::FakeApplicationConfigManager() = default;
+// static
+chromium::cast::ApplicationConfig FakeApplicationConfigManager::CreateConfig(
+    const std::string& id,
+    const GURL& url) {
+  chromium::cast::ApplicationConfig app_config;
+  app_config.set_id(id);
+  app_config.set_display_name("Dummy test app");
+  app_config.set_web_url(url.spec());
+  app_config.set_agent_url(kAgentComponentUrl);
+  return app_config;
+}
 
+FakeApplicationConfigManager::FakeApplicationConfigManager() = default;
 FakeApplicationConfigManager::~FakeApplicationConfigManager() = default;
 
+void FakeApplicationConfigManager::AddAppConfig(
+    chromium::cast::ApplicationConfig app_config) {
+  id_to_config_[app_config.id()] = std::move(app_config);
+}
+
+void FakeApplicationConfigManager::AddApp(const std::string& id,
+                                          const GURL& url) {
+  AddAppConfig(CreateConfig(id, url));
+}
+
 void FakeApplicationConfigManager::GetConfig(std::string id,
                                              GetConfigCallback callback) {
   if (id_to_config_.find(id) == id_to_config_.end()) {
@@ -29,40 +50,3 @@
   callback(std::move(std::move(id_to_config_[id])));
   id_to_config_.erase(id);
 }
-
-void FakeApplicationConfigManager::AddAppMapping(const std::string& id,
-                                                 const GURL& url,
-                                                 bool enable_remote_debugging) {
-  AddAppMappingWithAgent(id, url, enable_remote_debugging, kAgentComponentUrl);
-}
-
-void FakeApplicationConfigManager::AddAppMappingWithAgent(
-    const std::string& id,
-    const GURL& url,
-    bool enable_remote_debugging,
-    const std::string& agent_url) {
-  chromium::cast::ApplicationConfig app_config;
-  app_config.set_id(id);
-  app_config.set_display_name("Dummy test app");
-  app_config.set_web_url(url.spec());
-  app_config.set_enable_remote_debugging(enable_remote_debugging);
-  app_config.set_agent_url(agent_url);
-  id_to_config_[id] = std::move(app_config);
-}
-
-void FakeApplicationConfigManager::AddAppMappingWithContentDirectories(
-    const std::string& id,
-    const GURL& url,
-    std::vector<fuchsia::web::ContentDirectoryProvider> directories) {
-  chromium::cast::ApplicationConfig app_config;
-  app_config.set_id(id);
-  app_config.set_display_name("Dummy test app");
-  app_config.set_web_url(url.spec());
-  app_config.set_agent_url(kAgentComponentUrl);
-  if (!directories.empty()) {
-    app_config.set_content_directories_for_isolated_application(
-        std::move(directories));
-  }
-
-  id_to_config_[id] = std::move(app_config);
-}
diff --git a/fuchsia/runners/cast/fake_application_config_manager.h b/fuchsia/runners/cast/fake_application_config_manager.h
index e12acb13..2f933ef0 100644
--- a/fuchsia/runners/cast/fake_application_config_manager.h
+++ b/fuchsia/runners/cast/fake_application_config_manager.h
@@ -21,25 +21,17 @@
   FakeApplicationConfigManager();
   ~FakeApplicationConfigManager() override;
 
-  // Associates a Cast application |id| with a url, to be served from the
-  // EmbeddedTestServer.
-  void AddAppMapping(const std::string& id,
-                     const GURL& url,
-                     bool enable_remote_debugging);
+  // Creates a config for a dummy application with the specified |id| and |url|.
+  // Callers should updated the returned config as necessary and then register
+  // the app by calling AddAppConfig().
+  static chromium::cast::ApplicationConfig CreateConfig(const std::string& id,
+                                                        const GURL& url);
 
-  // Associates a Cast application |id| with a url and an agent that handles
-  // its bindings, to be served from the EmbeddedTestServer.
-  void AddAppMappingWithAgent(const std::string& id,
-                              const GURL& url,
-                              bool enable_remote_debugging,
-                              const std::string& agent_url);
+  // Adds |app_config| to the list of apps.
+  void AddAppConfig(chromium::cast::ApplicationConfig app_config);
 
-  // Associates a Cast application |id| with a url and a set of content
-  // directories, to be served from the EmbeddedTestServer.
-  void AddAppMappingWithContentDirectories(
-      const std::string& id,
-      const GURL& url,
-      std::vector<fuchsia::web::ContentDirectoryProvider> directories);
+  // Associates a Cast application |id| with the |url|.
+  void AddApp(const std::string& id, const GURL& url);
 
   // chromium::cast::ApplicationConfigManager interface.
   void GetConfig(std::string id, GetConfigCallback config_callback) override;
diff --git a/gpu/command_buffer/client/raster_implementation_gles.cc b/gpu/command_buffer/client/raster_implementation_gles.cc
index 2f85849..70aabbb 100644
--- a/gpu/command_buffer/client/raster_implementation_gles.cc
+++ b/gpu/command_buffer/client/raster_implementation_gles.cc
@@ -294,8 +294,9 @@
 
 GLuint RasterImplementationGLES::CreateAndConsumeForGpuRaster(
     const gpu::Mailbox& mailbox) {
-  DCHECK(mailbox.IsSharedImage());
-  return gl_->CreateAndTexStorage2DSharedImageCHROMIUM(mailbox.name);
+  return mailbox.IsSharedImage()
+             ? gl_->CreateAndTexStorage2DSharedImageCHROMIUM(mailbox.name)
+             : gl_->CreateAndConsumeTextureCHROMIUM(mailbox.name);
 }
 
 void RasterImplementationGLES::DeleteGpuRasterTexture(GLuint texture) {
diff --git a/gpu/command_buffer/service/decoder_context.h b/gpu/command_buffer/service/decoder_context.h
index ac8af44..3b6a708b 100644
--- a/gpu/command_buffer/service/decoder_context.h
+++ b/gpu/command_buffer/service/decoder_context.h
@@ -193,6 +193,15 @@
                                            unsigned format,
                                            int width,
                                            int height) = 0;
+  // Clears a level sub area of a compressed 3D texture.
+  // Returns false if a GL error should be generated.
+  virtual bool ClearCompressedTextureLevel3D(gles2::Texture* texture,
+                                             unsigned target,
+                                             int level,
+                                             unsigned format,
+                                             int width,
+                                             int height,
+                                             int depth) = 0;
   // Clears a level of a 3D texture.
   // Returns false if a GL error should be generated.
   virtual bool ClearLevel3D(gles2::Texture* texture,
diff --git a/gpu/command_buffer/service/feature_info.cc b/gpu/command_buffer/service/feature_info.cc
index 1322c24..22809e442 100644
--- a/gpu/command_buffer/service/feature_info.cc
+++ b/gpu/command_buffer/service/feature_info.cc
@@ -637,6 +637,7 @@
     bool have_astc_hdr =
         gfx::HasExtension(extensions, "GL_KHR_texture_compression_astc_hdr");
     if (have_astc_hdr) {
+      feature_flags_.ext_texture_format_astc_hdr = true;
       AddExtensionString("GL_KHR_texture_compression_astc_hdr");
     }
 
diff --git a/gpu/command_buffer/service/feature_info.h b/gpu/command_buffer/service/feature_info.h
index 50cd486..aa9dbdf1 100644
--- a/gpu/command_buffer/service/feature_info.h
+++ b/gpu/command_buffer/service/feature_info.h
@@ -75,6 +75,7 @@
     bool use_arb_occlusion_query_for_occlusion_query_boolean = false;
     bool native_vertex_array_object = false;
     bool ext_texture_format_astc = false;
+    bool ext_texture_format_astc_hdr = false;
     bool ext_texture_format_atc = false;
     bool ext_texture_format_bgra8888 = false;
     bool ext_texture_format_dxt1 = false;
diff --git a/gpu/command_buffer/service/gl_utils.cc b/gpu/command_buffer/service/gl_utils.cc
index 798ff95..cfd3e07f 100644
--- a/gpu/command_buffer/service/gl_utils.cc
+++ b/gpu/command_buffer/service/gl_utils.cc
@@ -552,37 +552,6 @@
       case GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2:
       case GL_COMPRESSED_RGBA8_ETC2_EAC:
       case GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC:
-      // TODO(crbug.com/1049880)
-      // GL_KHR_texture_compression_astc_hdr, TEXTURE_3D is not supported
-      // without HDR profile
-      case GL_COMPRESSED_RGBA_ASTC_4x4_KHR:
-      case GL_COMPRESSED_RGBA_ASTC_5x4_KHR:
-      case GL_COMPRESSED_RGBA_ASTC_5x5_KHR:
-      case GL_COMPRESSED_RGBA_ASTC_6x5_KHR:
-      case GL_COMPRESSED_RGBA_ASTC_6x6_KHR:
-      case GL_COMPRESSED_RGBA_ASTC_8x5_KHR:
-      case GL_COMPRESSED_RGBA_ASTC_8x6_KHR:
-      case GL_COMPRESSED_RGBA_ASTC_8x8_KHR:
-      case GL_COMPRESSED_RGBA_ASTC_10x5_KHR:
-      case GL_COMPRESSED_RGBA_ASTC_10x6_KHR:
-      case GL_COMPRESSED_RGBA_ASTC_10x8_KHR:
-      case GL_COMPRESSED_RGBA_ASTC_10x10_KHR:
-      case GL_COMPRESSED_RGBA_ASTC_12x10_KHR:
-      case GL_COMPRESSED_RGBA_ASTC_12x12_KHR:
-      case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR:
-      case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR:
-      case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR:
-      case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR:
-      case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR:
-      case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR:
-      case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR:
-      case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR:
-      case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR:
-      case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR:
-      case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR:
-      case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR:
-      case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR:
-      case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR:
       // GL_EXT_texture_compression_s3tc
       case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
       case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
@@ -598,6 +567,9 @@
       case GL_COMPRESSED_RED_GREEN_RGTC2_EXT:
       case GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT:
         return false;
+      // GL_KHR_texture_compression_astc_hdr, TEXTURE_3D is not supported
+      // without HDR profile. This is guaranteed to be validated beforehand
+      // in GLES2DecoderImpl::TexStorageImpl before calling this.
       default:
         break;
     }
@@ -1248,5 +1220,124 @@
   }
 }
 
+bool IsASTCFormat(GLenum internal_format) {
+  switch (internal_format) {
+    case GL_COMPRESSED_RGBA_ASTC_4x4_KHR:
+    case GL_COMPRESSED_RGBA_ASTC_5x4_KHR:
+    case GL_COMPRESSED_RGBA_ASTC_5x5_KHR:
+    case GL_COMPRESSED_RGBA_ASTC_6x5_KHR:
+    case GL_COMPRESSED_RGBA_ASTC_6x6_KHR:
+    case GL_COMPRESSED_RGBA_ASTC_8x5_KHR:
+    case GL_COMPRESSED_RGBA_ASTC_8x6_KHR:
+    case GL_COMPRESSED_RGBA_ASTC_8x8_KHR:
+    case GL_COMPRESSED_RGBA_ASTC_10x5_KHR:
+    case GL_COMPRESSED_RGBA_ASTC_10x6_KHR:
+    case GL_COMPRESSED_RGBA_ASTC_10x8_KHR:
+    case GL_COMPRESSED_RGBA_ASTC_10x10_KHR:
+    case GL_COMPRESSED_RGBA_ASTC_12x10_KHR:
+    case GL_COMPRESSED_RGBA_ASTC_12x12_KHR:
+    case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR:
+    case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR:
+    case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR:
+    case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR:
+    case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR:
+    case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR:
+    case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR:
+    case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR:
+    case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR:
+    case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR:
+    case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR:
+    case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR:
+    case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR:
+    case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR:
+      return true;
+    default:
+      break;
+  }
+  return false;
+}
+
+// This is only called in Texture::SetLevelInfo in texture_manager.cc
+// where there is no direct access to decoder->IsCompressedTextureFormat
+// or feature_info->validators()->compressed_texture_format.IsValid
+bool IsCompressedTextureFormat(GLenum internal_format) {
+  switch (internal_format) {
+    // S3TC
+    case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
+    case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
+    case GL_COMPRESSED_SRGB_S3TC_DXT1_EXT:
+    case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT:
+    case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
+    case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
+    case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT:
+    case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT:
+    // ASTC
+    case GL_COMPRESSED_RGBA_ASTC_4x4_KHR:
+    case GL_COMPRESSED_RGBA_ASTC_5x4_KHR:
+    case GL_COMPRESSED_RGBA_ASTC_5x5_KHR:
+    case GL_COMPRESSED_RGBA_ASTC_6x5_KHR:
+    case GL_COMPRESSED_RGBA_ASTC_6x6_KHR:
+    case GL_COMPRESSED_RGBA_ASTC_8x5_KHR:
+    case GL_COMPRESSED_RGBA_ASTC_8x6_KHR:
+    case GL_COMPRESSED_RGBA_ASTC_8x8_KHR:
+    case GL_COMPRESSED_RGBA_ASTC_10x5_KHR:
+    case GL_COMPRESSED_RGBA_ASTC_10x6_KHR:
+    case GL_COMPRESSED_RGBA_ASTC_10x8_KHR:
+    case GL_COMPRESSED_RGBA_ASTC_10x10_KHR:
+    case GL_COMPRESSED_RGBA_ASTC_12x10_KHR:
+    case GL_COMPRESSED_RGBA_ASTC_12x12_KHR:
+    case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR:
+    case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR:
+    case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR:
+    case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR:
+    case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR:
+    case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR:
+    case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR:
+    case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR:
+    case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR:
+    case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR:
+    case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR:
+    case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR:
+    case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR:
+    case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR:
+    // BPTC
+    case GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_EXT:
+    case GL_COMPRESSED_RGBA_BPTC_UNORM_EXT:
+    case GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_EXT:
+    case GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_EXT:
+    // RGTC
+    case GL_COMPRESSED_RED_RGTC1_EXT:
+    case GL_COMPRESSED_SIGNED_RED_RGTC1_EXT:
+    case GL_COMPRESSED_RED_GREEN_RGTC2_EXT:
+    case GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT:
+    // ETC2/EAC
+    case GL_COMPRESSED_R11_EAC:
+    case GL_COMPRESSED_SIGNED_R11_EAC:
+    case GL_COMPRESSED_RGB8_ETC2:
+    case GL_COMPRESSED_SRGB8_ETC2:
+    case GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2:
+    case GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2:
+    case GL_COMPRESSED_RG11_EAC:
+    case GL_COMPRESSED_SIGNED_RG11_EAC:
+    case GL_COMPRESSED_RGBA8_ETC2_EAC:
+    case GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC:
+    // ETC1
+    case GL_ETC1_RGB8_OES:
+    // PVRTC
+    case GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG:
+    case GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG:
+    case GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG:
+    case GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG:
+    // ATC
+    case GL_ATC_RGB_AMD:
+    case GL_ATC_RGBA_EXPLICIT_ALPHA_AMD:
+    case GL_ATC_RGBA_INTERPOLATED_ALPHA_AMD:
+      return true;
+    default:
+      break;
+  }
+  return false;
+}
+
 }  // namespace gles2
 }  // namespace gpu
diff --git a/gpu/command_buffer/service/gl_utils.h b/gpu/command_buffer/service/gl_utils.h
index eefa1cf7..b96b5a4 100644
--- a/gpu/command_buffer/service/gl_utils.h
+++ b/gpu/command_buffer/service/gl_utils.h
@@ -160,6 +160,9 @@
 bool GetGFXBufferFormat(GLenum internal_format, gfx::BufferFormat* out_format);
 bool GetGFXBufferUsage(GLenum buffer_usage, gfx::BufferUsage* out_usage);
 
+bool IsASTCFormat(GLenum internal_format);
+bool IsCompressedTextureFormat(GLenum internal_format);
+
 }  // namespace gles2
 }  // namespace gpu
 
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc
index 61f7d798..fc1f12cb 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc
@@ -1500,6 +1500,13 @@
                                    unsigned format,
                                    int width,
                                    int height) override;
+  bool ClearCompressedTextureLevel3D(Texture* texture,
+                                     unsigned target,
+                                     int level,
+                                     unsigned format,
+                                     int width,
+                                     int height,
+                                     int depth) override;
   bool IsCompressedTextureFormat(unsigned format) override;
 
   // overridden from GLES2Decoder
@@ -14491,6 +14498,53 @@
   return true;
 }
 
+bool GLES2DecoderImpl::ClearCompressedTextureLevel3D(Texture* texture,
+                                                     unsigned target,
+                                                     int level,
+                                                     unsigned format,
+                                                     int width,
+                                                     int height,
+                                                     int depth) {
+  DCHECK(target == GL_TEXTURE_3D || target == GL_TEXTURE_2D_ARRAY);
+  // This code path can only be called if the texture was originally
+  // allocated via TexStorage3D. Note that TexStorage3D is exposed
+  // internally for ES 2.0 contexts, but compressed texture support is
+  // not part of that exposure.
+  DCHECK(feature_info_->IsWebGL2OrES3Context());
+
+  GLsizei bytes_required = 0;
+  if (!GetCompressedTexSizeInBytes("ClearCompressedTextureLevel3D", width,
+                                   height, 1, format, &bytes_required,
+                                   error_state_.get())) {
+    return false;
+  }
+
+  TRACE_EVENT1("gpu", "GLES2DecoderImpl::ClearCompressedTextureLevel3D",
+               "bytes_required", bytes_required);
+
+  api()->glBindBufferFn(GL_PIXEL_UNPACK_BUFFER, 0);
+  {
+    // Add extra scope to destroy zero and the object it owns right
+    // after its usage.
+    std::unique_ptr<char[]> zero(new char[bytes_required]);
+    memset(zero.get(), 0, bytes_required);
+    api()->glBindTextureFn(texture->target(), texture->service_id());
+    api()->glCompressedTexSubImage3DFn(target, level, 0, 0, 0, width, height,
+                                       depth, format, bytes_required,
+                                       zero.get());
+  }
+  TextureRef* bound_texture =
+      texture_manager()->GetTextureInfoForTarget(&state_, texture->target());
+  api()->glBindTextureFn(texture->target(),
+                         bound_texture ? bound_texture->service_id() : 0);
+  Buffer* bound_buffer =
+      buffer_manager()->GetBufferInfoForTarget(&state_, GL_PIXEL_UNPACK_BUFFER);
+  if (bound_buffer) {
+    api()->glBindBufferFn(GL_PIXEL_UNPACK_BUFFER, bound_buffer->service_id());
+  }
+  return true;
+}
+
 bool GLES2DecoderImpl::IsCompressedTextureFormat(unsigned format) {
   return feature_info_->validators()->compressed_texture_format.IsValid(
       format);
@@ -18560,6 +18614,13 @@
   }
   bool is_compressed_format = IsCompressedTextureFormat(internal_format);
   if (is_compressed_format) {
+    if (target == GL_TEXTURE_3D &&
+        !feature_info_->feature_flags().ext_texture_format_astc_hdr &&
+        ::gpu::gles2::IsASTCFormat(internal_format)) {
+      LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, function_name,
+                         "target invalid for format");
+      return;
+    }
     if (!::gpu::gles2::ValidateCompressedFormatTarget(target,
                                                       internal_format)) {
       LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, function_name,
@@ -18572,13 +18633,16 @@
   // ValidForTarget) do not. So we have to add an extra check here.
   bool is_invalid_texstorage_size = width < 1 || height < 1 || depth < 1;
   if (!texture_manager()->ValidForTarget(target, 0, width, height, depth) ||
-      is_invalid_texstorage_size ||
-      TextureManager::ComputeMipMapCount(target, width, height, depth) <
-          levels) {
+      is_invalid_texstorage_size) {
     LOCAL_SET_GL_ERROR(
         GL_INVALID_VALUE, function_name, "dimensions out of range");
     return;
   }
+  if (TextureManager::ComputeMipMapCount(target, width, height, depth) <
+      levels) {
+    LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, function_name, "too many levels");
+    return;
+  }
   TextureRef* texture_ref = texture_manager()->GetTextureInfoForTarget(
       &state_, target);
   if (!texture_ref) {
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_mock.h b/gpu/command_buffer/service/gles2_cmd_decoder_mock.h
index be417a0d..d740594 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_mock.h
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_mock.h
@@ -129,6 +129,14 @@
                     unsigned format,
                     int width,
                     int height));
+  MOCK_METHOD7(ClearCompressedTextureLevel3D,
+               bool(Texture* texture,
+                    unsigned target,
+                    int level,
+                    unsigned format,
+                    int width,
+                    int height,
+                    int depth));
   MOCK_METHOD1(IsCompressedTextureFormat,
                bool(unsigned format));
   MOCK_METHOD8(ClearLevel3D,
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc
index 0194453..5aaa0fe1 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc
@@ -1778,6 +1778,17 @@
   return true;
 }
 
+bool GLES2DecoderPassthroughImpl::ClearCompressedTextureLevel3D(
+    Texture* texture,
+    unsigned target,
+    int level,
+    unsigned format,
+    int width,
+    int height,
+    int depth) {
+  return true;
+}
+
 bool GLES2DecoderPassthroughImpl::IsCompressedTextureFormat(unsigned format) {
   return false;
 }
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.h b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.h
index 1573e77..ade661ee 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.h
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.h
@@ -294,6 +294,16 @@
                                    int width,
                                    int height) override;
 
+  // Clears a level sub area of a compressed 3D texture.
+  // Returns false if a GL error should be generated.
+  bool ClearCompressedTextureLevel3D(Texture* texture,
+                                     unsigned target,
+                                     int level,
+                                     unsigned format,
+                                     int width,
+                                     int height,
+                                     int depth) override;
+
   // Indicates whether a given internal format is one for a compressed
   // texture.
   bool IsCompressedTextureFormat(unsigned format) override;
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_textures.cc b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_textures.cc
index d2b43b9..4b7cd418 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_textures.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_textures.cc
@@ -4217,7 +4217,7 @@
   cmds::TexStorage2DEXT cmd;
   cmd.Init(GL_TEXTURE_RECTANGLE_ARB, 2, GL_RGBA8, 4, 4);
   EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
-  EXPECT_EQ(GL_INVALID_VALUE, GetGLError());
+  EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
 }
 
 TEST_P(GLES2DecoderManualInitTest, TexStorageInvalidSize) {
diff --git a/gpu/command_buffer/service/raster_decoder.cc b/gpu/command_buffer/service/raster_decoder.cc
index f6e3c744..6bf8b0f 100644
--- a/gpu/command_buffer/service/raster_decoder.cc
+++ b/gpu/command_buffer/service/raster_decoder.cc
@@ -322,6 +322,13 @@
                                    unsigned format,
                                    int width,
                                    int height) override;
+  bool ClearCompressedTextureLevel3D(gles2::Texture* texture,
+                                     unsigned target,
+                                     int level,
+                                     unsigned format,
+                                     int width,
+                                     int height,
+                                     int depth) override;
   bool ClearLevel3D(gles2::Texture* texture,
                     unsigned target,
                     int level,
@@ -1423,6 +1430,17 @@
   return false;
 }
 
+bool RasterDecoderImpl::ClearCompressedTextureLevel3D(gles2::Texture* texture,
+                                                      unsigned target,
+                                                      int level,
+                                                      unsigned format,
+                                                      int width,
+                                                      int height,
+                                                      int depth) {
+  NOTREACHED();
+  return false;
+}
+
 int RasterDecoderImpl::GetRasterDecoderId() const {
   return raster_decoder_id_;
 }
diff --git a/gpu/command_buffer/service/texture_manager.cc b/gpu/command_buffer/service/texture_manager.cc
index e0f0c55..5c083aa 100644
--- a/gpu/command_buffer/service/texture_manager.cc
+++ b/gpu/command_buffer/service/texture_manager.cc
@@ -1367,16 +1367,16 @@
     ScopedMemTrackerChange change(this);
     estimated_size_ -= info.estimated_size;
 
-    if (format != GL_NONE) {
-      // Uncompressed image
-      GLES2Util::ComputeImageDataSizes(width, height, depth, format, type, 4,
-                                       &info.estimated_size, nullptr, nullptr);
-    } else if (internal_format != GL_NONE) {
+    if (::gpu::gles2::IsCompressedTextureFormat(internal_format)) {
       // Compressed image
       GLsizei compressed_size = 0;
       GetCompressedTexSizeInBytes(nullptr, width, height, depth,
                                   internal_format, &compressed_size, nullptr);
       info.estimated_size = compressed_size;
+    } else if (format != GL_NONE) {
+      // Uncompressed image
+      GLES2Util::ComputeImageDataSizes(width, height, depth, format, type, 4,
+                                       &info.estimated_size, nullptr, nullptr);
     } else {
       // No image
       info.estimated_size = 0;
@@ -1833,14 +1833,24 @@
   }
 
   if (info.target == GL_TEXTURE_3D || info.target == GL_TEXTURE_2D_ARRAY) {
-    // For 3D textures, we always clear the entire texture.
-    DCHECK(info.cleared_rect == gfx::Rect());
-    bool cleared = decoder->ClearLevel3D(
-        this, info.target, info.level,
-        TextureManager::AdjustTexFormat(decoder->GetFeatureInfo(), info.format),
-        info.type, info.width, info.height, info.depth);
-    if (!cleared)
-      return false;
+    if (decoder->IsCompressedTextureFormat(info.internal_format)) {
+      DCHECK(IsImmutable());
+      bool cleared = decoder->ClearCompressedTextureLevel3D(
+          this, info.target, info.level, info.internal_format, info.width,
+          info.height, info.depth);
+      if (!cleared)
+        return false;
+    } else {
+      // For 3D textures, we always clear the entire texture.
+      DCHECK(info.cleared_rect == gfx::Rect());
+      bool cleared =
+          decoder->ClearLevel3D(this, info.target, info.level,
+                                TextureManager::AdjustTexFormat(
+                                    decoder->GetFeatureInfo(), info.format),
+                                info.type, info.width, info.height, info.depth);
+      if (!cleared)
+        return false;
+    }
   } else {
     if (decoder->IsCompressedTextureFormat(info.internal_format)) {
       // An uncleared level of a compressed texture can only occur when
@@ -2305,7 +2315,7 @@
 
 bool TextureManager::ValidForTarget(
     GLenum target, GLint level, GLsizei width, GLsizei height, GLsizei depth) {
-  if (level < 0 || level >= MaxLevelsForTarget(target))
+  if (level < 0)
     return false;
   GLsizei max_size = MaxSizeForTarget(target) >> level;
   GLsizei max_depth =
@@ -3912,6 +3922,34 @@
     case GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_EXT:
     case GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC:
     case GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2:
+    case GL_COMPRESSED_RGBA_ASTC_4x4_KHR:
+    case GL_COMPRESSED_RGBA_ASTC_5x4_KHR:
+    case GL_COMPRESSED_RGBA_ASTC_5x5_KHR:
+    case GL_COMPRESSED_RGBA_ASTC_6x5_KHR:
+    case GL_COMPRESSED_RGBA_ASTC_6x6_KHR:
+    case GL_COMPRESSED_RGBA_ASTC_8x5_KHR:
+    case GL_COMPRESSED_RGBA_ASTC_8x6_KHR:
+    case GL_COMPRESSED_RGBA_ASTC_8x8_KHR:
+    case GL_COMPRESSED_RGBA_ASTC_10x5_KHR:
+    case GL_COMPRESSED_RGBA_ASTC_10x6_KHR:
+    case GL_COMPRESSED_RGBA_ASTC_10x8_KHR:
+    case GL_COMPRESSED_RGBA_ASTC_10x10_KHR:
+    case GL_COMPRESSED_RGBA_ASTC_12x10_KHR:
+    case GL_COMPRESSED_RGBA_ASTC_12x12_KHR:
+    case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR:
+    case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR:
+    case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR:
+    case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR:
+    case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR:
+    case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR:
+    case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR:
+    case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR:
+    case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR:
+    case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR:
+    case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR:
+    case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR:
+    case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR:
+    case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR:
     case GL_RGBA:
     case GL_RGBA8:
     case GL_RGBA16:
@@ -4125,6 +4163,78 @@
       return GL_HALF_FLOAT_OES;
     case GL_BGRA8_EXT:
       return GL_UNSIGNED_BYTE;
+    // Compressed Formats
+    // S3TC
+    case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
+    case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
+    case GL_COMPRESSED_SRGB_S3TC_DXT1_EXT:
+    case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT:
+    case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
+    case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
+    case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT:
+    case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT:
+    // ASTC
+    case GL_COMPRESSED_RGBA_ASTC_4x4_KHR:
+    case GL_COMPRESSED_RGBA_ASTC_5x4_KHR:
+    case GL_COMPRESSED_RGBA_ASTC_5x5_KHR:
+    case GL_COMPRESSED_RGBA_ASTC_6x5_KHR:
+    case GL_COMPRESSED_RGBA_ASTC_6x6_KHR:
+    case GL_COMPRESSED_RGBA_ASTC_8x5_KHR:
+    case GL_COMPRESSED_RGBA_ASTC_8x6_KHR:
+    case GL_COMPRESSED_RGBA_ASTC_8x8_KHR:
+    case GL_COMPRESSED_RGBA_ASTC_10x5_KHR:
+    case GL_COMPRESSED_RGBA_ASTC_10x6_KHR:
+    case GL_COMPRESSED_RGBA_ASTC_10x8_KHR:
+    case GL_COMPRESSED_RGBA_ASTC_10x10_KHR:
+    case GL_COMPRESSED_RGBA_ASTC_12x10_KHR:
+    case GL_COMPRESSED_RGBA_ASTC_12x12_KHR:
+    case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR:
+    case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR:
+    case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR:
+    case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR:
+    case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR:
+    case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR:
+    case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR:
+    case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR:
+    case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR:
+    case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR:
+    case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR:
+    case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR:
+    case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR:
+    case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR:
+    // BPTC
+    case GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_EXT:
+    case GL_COMPRESSED_RGBA_BPTC_UNORM_EXT:
+    case GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_EXT:
+    case GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_EXT:
+    // RGTC
+    case GL_COMPRESSED_RED_RGTC1_EXT:
+    case GL_COMPRESSED_SIGNED_RED_RGTC1_EXT:
+    case GL_COMPRESSED_RED_GREEN_RGTC2_EXT:
+    case GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT:
+    // ETC2/EAC
+    case GL_COMPRESSED_R11_EAC:
+    case GL_COMPRESSED_SIGNED_R11_EAC:
+    case GL_COMPRESSED_RGB8_ETC2:
+    case GL_COMPRESSED_SRGB8_ETC2:
+    case GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2:
+    case GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2:
+    case GL_COMPRESSED_RG11_EAC:
+    case GL_COMPRESSED_SIGNED_RG11_EAC:
+    case GL_COMPRESSED_RGBA8_ETC2_EAC:
+    case GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC:
+    // ETC1
+    case GL_ETC1_RGB8_OES:
+    // PVRTC
+    case GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG:
+    case GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG:
+    case GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG:
+    case GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG:
+    // ATC
+    case GL_ATC_RGB_AMD:
+    case GL_ATC_RGBA_EXPLICIT_ALPHA_AMD:
+    case GL_ATC_RGBA_INTERPOLATED_ALPHA_AMD:
+      return GL_UNSIGNED_BYTE;
     default:
       return GL_NONE;
   }
@@ -4212,10 +4322,11 @@
     return category == SAMPLER_SHADOW;
   }
 
-  if (level_info->type == GL_NONE && level_info->format == GL_NONE &&
-      level_info->internal_format != GL_NONE) {
-    // This is probably a compressed texture format. All compressed formats are
-    // sampled as float.
+  DCHECK(memory_tracking_ref_);
+  if (memory_tracking_ref_->manager()
+          ->feature_info_->validators()
+          ->compressed_texture_format.IsValid(level_info->internal_format)) {
+    // Compressed texture format. All compressed formats are sampled as float
     return category == SAMPLER_FLOAT;
   }
 
diff --git a/gpu/command_buffer/service/webgpu_decoder_impl.cc b/gpu/command_buffer/service/webgpu_decoder_impl.cc
index 5dd3de8..80b434f 100644
--- a/gpu/command_buffer/service/webgpu_decoder_impl.cc
+++ b/gpu/command_buffer/service/webgpu_decoder_impl.cc
@@ -485,6 +485,16 @@
     NOTREACHED();
     return false;
   }
+  bool ClearCompressedTextureLevel3D(gles2::Texture* texture,
+                                     unsigned target,
+                                     int level,
+                                     unsigned format,
+                                     int width,
+                                     int height,
+                                     int depth) override {
+    NOTREACHED();
+    return false;
+  }
   bool ClearLevel3D(gles2::Texture* texture,
                     unsigned target,
                     int level,
diff --git a/infra/config/buckets/ci.star b/infra/config/buckets/ci.star
index 79ea772..a7ed274c 100644
--- a/infra/config/buckets/ci.star
+++ b/infra/config/buckets/ci.star
@@ -1116,13 +1116,13 @@
 
 ci.fyi_coverage_builder(
     name = 'ios-simulator-code-coverage',
-    caches = [xcode_cache.x11m382q],
+    caches = [xcode_cache.x11c29],
     cores = None,
     goma_backend = None,  # TODO(crbug.com/950413): Use goma.backend.RBE_PROD
     os = os.MAC_ANY,
     use_clang_coverage = True,
     properties = {
-        'xcode_build_version': '11m382q',
+        'xcode_build_version': '11c29',
     },
 )
 
diff --git a/infra/config/generated/cr-buildbucket.cfg b/infra/config/generated/cr-buildbucket.cfg
index d5e77d1..cf8a30c5 100644
--- a/infra/config/generated/cr-buildbucket.cfg
+++ b/infra/config/generated/cr-buildbucket.cfg
@@ -6825,12 +6825,12 @@
         properties_j: "$build/code_coverage:{\"use_clang_coverage\":true}"
         properties_j: "$kitchen:{\"devshell\":true,\"git_auth\":true}"
         properties_j: "mastername:\"chromium.fyi\""
-        properties_j: "xcode_build_version:\"11m382q\""
+        properties_j: "xcode_build_version:\"11c29\""
       >
       execution_timeout_secs: 72000
       caches: <
-        name: "xcode_ios_11m382q"
-        path: "xcode_ios_11m382q.app"
+        name: "xcode_ios_11c29"
+        path: "xcode_ios_11c29.app"
       >
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
diff --git a/ios/chrome/browser/flags/about_flags_unittest.mm b/ios/chrome/browser/flags/about_flags_unittest.mm
index f3f0b64..29baffcd 100644
--- a/ios/chrome/browser/flags/about_flags_unittest.mm
+++ b/ios/chrome/browser/flags/about_flags_unittest.mm
@@ -16,9 +16,7 @@
 
 // Makes sure that every flag has an owner and an expiry entry in
 // flag-metadata.json.
-// TODO(crbug.com/1058614): Enable once iOS flags are added to
-// flag-metadata.json.
-TEST_F(AboutFlagsTest, DISABLED_EveryFlagHasMetadata) {
+TEST_F(AboutFlagsTest, EveryFlagHasMetadata) {
   size_t count;
   const flags_ui::FeatureEntry* entries = testing::GetFeatureEntries(&count);
   flags_ui::testing::EnsureEveryFlagHasMetadata(entries, count);
diff --git a/ios/web/common/features.mm b/ios/web/common/features.mm
index f5f43616..b5644ddf 100644
--- a/ios/web/common/features.mm
+++ b/ios/web/common/features.mm
@@ -42,7 +42,7 @@
     "UseDefaultUserAgentInWebClient", base::FEATURE_DISABLED_BY_DEFAULT};
 
 const base::Feature kPreserveScrollViewProperties{
-    "PreserveScrollViewProperties", base::FEATURE_DISABLED_BY_DEFAULT};
+    "PreserveScrollViewProperties", base::FEATURE_ENABLED_BY_DEFAULT};
 
 }  // namespace features
 }  // namespace web
diff --git a/media/blink/DEPS b/media/blink/DEPS
index e0731c5..87f8a9e 100644
--- a/media/blink/DEPS
+++ b/media/blink/DEPS
@@ -6,6 +6,7 @@
   "+components/scheduler",  # Only allowed in tests.
   "+components/viz/common/frame_sinks/begin_frame_args.h",
   "+components/viz/common/gpu/context_provider.h",
+  "+components/viz/common/gpu/raster_context_provider.h",
   "+components/viz/common/surfaces/frame_sink_id.h",
   "+gin",
   "+media",
diff --git a/media/blink/webmediaplayer_impl.cc b/media/blink/webmediaplayer_impl.cc
index ca5df6e..09ed161 100644
--- a/media/blink/webmediaplayer_impl.cc
+++ b/media/blink/webmediaplayer_impl.cc
@@ -31,7 +31,6 @@
 #include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
 #include "cc/layers/video_layer.h"
-#include "components/viz/common/gpu/context_provider.h"
 #include "media/audio/null_audio_sink.h"
 #include "media/base/bind_to_current_loop.h"
 #include "media/base/cdm_context.h"
@@ -316,7 +315,7 @@
       adjust_allocated_memory_cb_(params->adjust_allocated_memory_cb()),
       tick_clock_(base::DefaultTickClock::GetInstance()),
       url_index_(url_index),
-      context_provider_(params->context_provider()),
+      raster_context_provider_(params->raster_context_provider()),
       vfc_task_runner_(params->video_frame_compositor_task_runner()),
       compositor_(std::move(compositor)),
       renderer_factory_selector_(std::move(renderer_factory_selector)),
@@ -1264,9 +1263,9 @@
 
   gfx::Rect gfx_rect(rect);
   if (video_frame.get() && video_frame->HasTextures()) {
-    if (!context_provider_)
+    if (!raster_context_provider_)
       return;  // Unable to get/create a shared main thread context.
-    if (!context_provider_->GrContext())
+    if (!raster_context_provider_->GrContext())
       return;  // The context has been lost since and can't setup a GrContext.
   }
   if (out_metadata && video_frame) {
@@ -1281,7 +1280,7 @@
   video_renderer_.Paint(
       video_frame, canvas, gfx::RectF(gfx_rect), flags,
       pipeline_metadata_.video_decoder_config.video_transformation(),
-      context_provider_.get());
+      raster_context_provider_.get());
 }
 
 bool WebMediaPlayerImpl::WouldTaintOrigin() const {
@@ -1368,7 +1367,7 @@
   }
 
   return video_renderer_.CopyVideoFrameTexturesToGLTexture(
-      context_provider_.get(), gl, video_frame.get(), target, texture,
+      raster_context_provider_.get(), gl, video_frame.get(), target, texture,
       internal_format, format, type, level, premultiply_alpha, flip_y);
 }
 
@@ -1397,7 +1396,7 @@
   }
 
   return video_renderer_.PrepareVideoFrameForWebGL(
-      context_provider_.get(), gl, video_frame.get(), target, texture);
+      raster_context_provider_.get(), gl, video_frame.get(), target, texture);
 }
 
 // static
diff --git a/media/blink/webmediaplayer_impl.h b/media/blink/webmediaplayer_impl.h
index afe42780..5921924 100644
--- a/media/blink/webmediaplayer_impl.h
+++ b/media/blink/webmediaplayer_impl.h
@@ -25,7 +25,7 @@
 #include "base/timer/timer.h"
 #include "build/build_config.h"
 #include "cc/layers/surface_layer.h"
-#include "components/viz/common/gpu/context_provider.h"
+#include "components/viz/common/gpu/raster_context_provider.h"
 #include "media/base/cdm_config.h"
 #include "media/base/encryption_scheme.h"
 #include "media/base/media_observer.h"
@@ -788,7 +788,7 @@
 
   std::unique_ptr<BufferedDataSourceHostImpl> buffered_data_source_host_;
   UrlIndex* const url_index_;
-  scoped_refptr<viz::ContextProvider> context_provider_;
+  scoped_refptr<viz::RasterContextProvider> raster_context_provider_;
 
   // Video rendering members.
   // The |compositor_| runs on the compositor thread, or if
diff --git a/media/blink/webmediaplayer_params.cc b/media/blink/webmediaplayer_params.cc
index 41cd376..24fda57 100644
--- a/media/blink/webmediaplayer_params.cc
+++ b/media/blink/webmediaplayer_params.cc
@@ -27,7 +27,7 @@
     bool embedded_media_experience_enabled,
     mojo::PendingRemote<mojom::MediaMetricsProvider> metrics_provider,
     CreateSurfaceLayerBridgeCB create_bridge_callback,
-    scoped_refptr<viz::ContextProvider> context_provider,
+    scoped_refptr<viz::RasterContextProvider> raster_context_provider,
     blink::WebMediaPlayer::SurfaceLayerMode use_surface_layer_for_video,
     bool is_background_suspend_enabled,
     bool is_background_video_playback_enabled,
@@ -49,7 +49,7 @@
       embedded_media_experience_enabled_(embedded_media_experience_enabled),
       metrics_provider_(std::move(metrics_provider)),
       create_bridge_callback_(std::move(create_bridge_callback)),
-      context_provider_(std::move(context_provider)),
+      raster_context_provider_(std::move(raster_context_provider)),
       use_surface_layer_for_video_(use_surface_layer_for_video),
       is_background_suspend_enabled_(is_background_suspend_enabled),
       is_background_video_playback_enabled_(
diff --git a/media/blink/webmediaplayer_params.h b/media/blink/webmediaplayer_params.h
index dd732c0..0a324a32 100644
--- a/media/blink/webmediaplayer_params.h
+++ b/media/blink/webmediaplayer_params.h
@@ -14,7 +14,7 @@
 #include "base/memory/weak_ptr.h"
 #include "base/time/time.h"
 #include "cc/layers/surface_layer.h"
-#include "components/viz/common/gpu/context_provider.h"
+#include "components/viz/common/gpu/raster_context_provider.h"
 #include "media/base/media_log.h"
 #include "media/base/media_observer.h"
 #include "media/base/media_switches.h"
@@ -79,7 +79,7 @@
       bool embedded_media_experience_enabled,
       mojo::PendingRemote<mojom::MediaMetricsProvider> metrics_provider,
       CreateSurfaceLayerBridgeCB bridge_callback,
-      scoped_refptr<viz::ContextProvider> context_provider,
+      scoped_refptr<viz::RasterContextProvider> raster_context_provider,
       blink::WebMediaPlayer::SurfaceLayerMode use_surface_layer_for_video,
       bool is_background_suspend_enabled,
       bool is_background_video_play_enabled,
@@ -148,8 +148,8 @@
     return std::move(create_bridge_callback_);
   }
 
-  scoped_refptr<viz::ContextProvider> context_provider() {
-    return context_provider_;
+  scoped_refptr<viz::RasterContextProvider> raster_context_provider() {
+    return raster_context_provider_;
   }
 
   blink::WebMediaPlayer::SurfaceLayerMode use_surface_layer_for_video() const {
@@ -194,7 +194,7 @@
   const bool embedded_media_experience_enabled_;
   mojo::PendingRemote<mojom::MediaMetricsProvider> metrics_provider_;
   CreateSurfaceLayerBridgeCB create_bridge_callback_;
-  scoped_refptr<viz::ContextProvider> context_provider_;
+  scoped_refptr<viz::RasterContextProvider> raster_context_provider_;
   blink::WebMediaPlayer::SurfaceLayerMode use_surface_layer_for_video_;
 
   // Whether the renderer should automatically suspend media playback in
diff --git a/media/device_monitors/device_monitor_mac.mm b/media/device_monitors/device_monitor_mac.mm
index b9ee118..f8d651e 100644
--- a/media/device_monitors/device_monitor_mac.mm
+++ b/media/device_monitors/device_monitor_mac.mm
@@ -125,7 +125,7 @@
 @interface CrAVFoundationDeviceObserver : NSObject {
  @private
   // Callback for device changed, has to run on Device Thread.
-  base::Closure _onDeviceChangedCallback;
+  base::RepeatingClosure _onDeviceChangedCallback;
 
   // Member to keep track of the devices we are already monitoring.
   std::set<base::scoped_nsobject<AVCaptureDevice>> _monitoredDevices;
@@ -134,7 +134,7 @@
   base::ThreadChecker _mainThreadChecker;
 }
 
-- (id)initWithOnChangedCallback:(const base::Closure&)callback;
+- (id)initWithOnChangedCallback:(const base::RepeatingClosure&)callback;
 - (void)startObserving:(base::scoped_nsobject<AVCaptureDevice>)device;
 - (void)stopObserving:(AVCaptureDevice*)device;
 - (void)clearOnDeviceChangedCallback;
@@ -196,7 +196,7 @@
     const scoped_refptr<base::SingleThreadTaskRunner>& device_thread) {
   DCHECK(main_thread_checker_.CalledOnValidThread());
 
-  base::Closure on_device_changed_callback = base::Bind(
+  base::RepeatingClosure on_device_changed_callback = base::BindRepeating(
       &SuspendObserverDelegate::OnDeviceChanged, this, device_thread);
   suspend_observer_.reset([[CrAVFoundationDeviceObserver alloc]
       initWithOnChangedCallback:on_device_changed_callback]);
@@ -348,7 +348,7 @@
 
 @implementation CrAVFoundationDeviceObserver
 
-- (id)initWithOnChangedCallback:(const base::Closure&)callback {
+- (id)initWithOnChangedCallback:(const base::RepeatingClosure&)callback {
   DCHECK(_mainThreadChecker.CalledOnValidThread());
   if ((self = [super init])) {
     DCHECK(!callback.is_null());
diff --git a/media/learning/common/BUILD.gn b/media/learning/common/BUILD.gn
index cb3d2e7..24a2e5d 100644
--- a/media/learning/common/BUILD.gn
+++ b/media/learning/common/BUILD.gn
@@ -10,6 +10,7 @@
     "//media/learning/impl:test_support",
     "//media/learning/mojo/public/cpp:*",
     "//media/learning/mojo/public/mojom:mojom",
+    "//media/learning/mojo/public/mojom:mojom_blink",
     "//media/learning/mojo:*",
     "//media/learning/common:unit_tests",
 
diff --git a/media/learning/common/target_histogram.h b/media/learning/common/target_histogram.h
index 959fdc1..366eefbf 100644
--- a/media/learning/common/target_histogram.h
+++ b/media/learning/common/target_histogram.h
@@ -23,6 +23,13 @@
 class TargetHistogramDataView;
 }
 
+// Intermediate type for mojom struct traits translation.
+// See learning_types.mojom.
+struct COMPONENT_EXPORT(LEARNING_COMMON) TargetHistogramPair {
+  TargetValue target_value;
+  double count;
+};
+
 // Histogram of target values that allows fractional counts.
 class COMPONENT_EXPORT(LEARNING_COMMON) TargetHistogram {
  public:
diff --git a/media/learning/mojo/public/cpp/BUILD.gn b/media/learning/mojo/public/cpp/BUILD.gn
index 2f70a90..791caba 100644
--- a/media/learning/mojo/public/cpp/BUILD.gn
+++ b/media/learning/mojo/public/cpp/BUILD.gn
@@ -23,6 +23,26 @@
   ]
 }
 
+# Normally typemap traits sources should be build directly into mojom targets
+# via the typemap file. This target is for typemapped mojo_base types whose
+# traits are shared between chromium and blink variants.
+component("shared_typemap_traits") {
+  output_name = "media_learning_shared_typemap_traits"
+
+  defines = [ "IS_MEDIA_LEARNING_SHARED_TYPEMAP_TRAITS_IMPL" ]
+
+  sources = [
+    "//media/learning/mojo/public/cpp/learning_mojom_traits.cc",
+    "//media/learning/mojo/public/cpp/learning_mojom_traits.h",
+  ]
+
+  deps = [
+    "//base",
+    "//media/learning/common",
+    "//media/learning/mojo/public/mojom:mojom_shared",
+  ]
+}
+
 source_set("unit_tests") {
   testonly = true
 
diff --git a/media/learning/mojo/public/cpp/learning_mojom_traits.cc b/media/learning/mojo/public/cpp/learning_mojom_traits.cc
index 9124e8b..9756815 100644
--- a/media/learning/mojo/public/cpp/learning_mojom_traits.cc
+++ b/media/learning/mojo/public/cpp/learning_mojom_traits.cc
@@ -51,13 +51,36 @@
 }
 
 // static
+bool StructTraits<media::learning::mojom::TargetHistogramPairDataView,
+                  media::learning::TargetHistogramPair>::
+    Read(media::learning::mojom::TargetHistogramPairDataView data,
+         media::learning::TargetHistogramPair* out_pair) {
+  if (!data.ReadTargetValue(&out_pair->target_value))
+    return false;
+  out_pair->count = data.count();
+  return true;
+}
+
+// static
 bool StructTraits<media::learning::mojom::TargetHistogramDataView,
                   media::learning::TargetHistogram>::
     Read(media::learning::mojom::TargetHistogramDataView data,
          media::learning::TargetHistogram* out_target_histogram) {
-  if (!data.ReadCounts(&out_target_histogram->counts_))
+  ArrayDataView<media::learning::mojom::TargetHistogramPairDataView> pairs;
+  data.GetPairsDataView(&pairs);
+  if (pairs.is_null())
     return false;
 
+  for (size_t i = 0; i < pairs.size(); ++i) {
+    media::learning::mojom::TargetHistogramPairDataView pair_data;
+    pairs.GetDataView(i, &pair_data);
+    media::learning::TargetValue value;
+    if (!pair_data.ReadTargetValue(&value))
+      return false;
+
+    out_target_histogram->counts_.emplace(std::move(value), pair_data.count());
+  }
+
   return true;
 }
 
diff --git a/media/learning/mojo/public/cpp/learning_mojom_traits.h b/media/learning/mojo/public/cpp/learning_mojom_traits.h
index 2c42b7f6..9203334 100644
--- a/media/learning/mojo/public/cpp/learning_mojom_traits.h
+++ b/media/learning/mojo/public/cpp/learning_mojom_traits.h
@@ -7,16 +7,18 @@
 
 #include <vector>
 
+#include "base/component_export.h"
 #include "media/learning/common/learning_task_controller.h"
 #include "media/learning/common/value.h"
-#include "media/learning/mojo/public/mojom/learning_types.mojom.h"
+#include "media/learning/mojo/public/mojom/learning_types.mojom-shared.h"
 #include "mojo/public/cpp/bindings/struct_traits.h"
 
 namespace mojo {
 
 template <>
-struct StructTraits<media::learning::mojom::LabelledExampleDataView,
-                    media::learning::LabelledExample> {
+struct COMPONENT_EXPORT(MEDIA_LEARNING_SHARED_TYPEMAP_TRAITS)
+    StructTraits<media::learning::mojom::LabelledExampleDataView,
+                 media::learning::LabelledExample> {
   static const std::vector<media::learning::FeatureValue>& features(
       const media::learning::LabelledExample& e) {
     return e.features;
@@ -31,9 +33,10 @@
 };
 
 template <>
-struct StructTraits<media::learning::mojom::FeatureValueDataView,
-                    media::learning::FeatureValue> {
-  static int64_t value(const media::learning::FeatureValue& e) {
+struct COMPONENT_EXPORT(MEDIA_LEARNING_SHARED_TYPEMAP_TRAITS)
+    StructTraits<media::learning::mojom::FeatureValueDataView,
+                 media::learning::FeatureValue> {
+  static double value(const media::learning::FeatureValue& e) {
     return e.value();
   }
   static bool Read(media::learning::mojom::FeatureValueDataView data,
@@ -41,9 +44,10 @@
 };
 
 template <>
-struct StructTraits<media::learning::mojom::TargetValueDataView,
-                    media::learning::TargetValue> {
-  static int64_t value(const media::learning::TargetValue& e) {
+struct COMPONENT_EXPORT(MEDIA_LEARNING_SHARED_TYPEMAP_TRAITS)
+    StructTraits<media::learning::mojom::TargetValueDataView,
+                 media::learning::TargetValue> {
+  static double value(const media::learning::TargetValue& e) {
     return e.value();
   }
   static bool Read(media::learning::mojom::TargetValueDataView data,
@@ -51,8 +55,9 @@
 };
 
 template <>
-struct StructTraits<media::learning::mojom::ObservationCompletionDataView,
-                    media::learning::ObservationCompletion> {
+struct COMPONENT_EXPORT(MEDIA_LEARNING_SHARED_TYPEMAP_TRAITS)
+    StructTraits<media::learning::mojom::ObservationCompletionDataView,
+                 media::learning::ObservationCompletion> {
   static media::learning::TargetValue target_value(
       const media::learning::ObservationCompletion& e) {
     return e.target_value;
@@ -67,11 +72,32 @@
 };
 
 template <>
-struct StructTraits<media::learning::mojom::TargetHistogramDataView,
-                    media::learning::TargetHistogram> {
-  static media::learning::TargetHistogram::CountMap counts(
+struct COMPONENT_EXPORT(MEDIA_LEARNING_SHARED_TYPEMAP_TRAITS)
+    StructTraits<media::learning::mojom::TargetHistogramPairDataView,
+                 media::learning::TargetHistogramPair> {
+  static media::learning::TargetValue target_value(
+      const media::learning::TargetHistogramPair& e) {
+    return e.target_value;
+  }
+  static double count(const media::learning::TargetHistogramPair& e) {
+    return e.count;
+  }
+  static bool Read(media::learning::mojom::TargetHistogramPairDataView data,
+                   media::learning::TargetHistogramPair* out_pair);
+};
+
+template <>
+struct COMPONENT_EXPORT(MEDIA_LEARNING_SHARED_TYPEMAP_TRAITS)
+    StructTraits<media::learning::mojom::TargetHistogramDataView,
+                 media::learning::TargetHistogram> {
+  static std::vector<media::learning::TargetHistogramPair> pairs(
       const media::learning::TargetHistogram& e) {
-    return e.counts();
+    std::vector<media::learning::TargetHistogramPair> pairs;
+    for (auto const& entry : e.counts_) {
+      pairs.push_back({entry.first, entry.second});
+    }
+
+    return pairs;
   }
 
   static bool Read(media::learning::mojom::TargetHistogramDataView data,
diff --git a/media/learning/mojo/public/mojom/BUILD.gn b/media/learning/mojo/public/mojom/BUILD.gn
index 6956512..0790062c 100644
--- a/media/learning/mojo/public/mojom/BUILD.gn
+++ b/media/learning/mojo/public/mojom/BUILD.gn
@@ -13,4 +13,8 @@
   ]
 
   public_deps = [ "//mojo/public/mojom/base" ]
+
+  export_class_attribute_blink = "BLINK_PLATFORM_EXPORT"
+  export_define_blink = "BLINK_PLATFORM_IMPLEMENTATION=1"
+  export_header_blink = "third_party/blink/public/platform/web_common.h"
 }
diff --git a/media/learning/mojo/public/mojom/learning_types.mojom b/media/learning/mojo/public/mojom/learning_types.mojom
index c2a4b8f..22c7348 100644
--- a/media/learning/mojo/public/mojom/learning_types.mojom
+++ b/media/learning/mojo/public/mojom/learning_types.mojom
@@ -6,12 +6,12 @@
 
 // learning::FeatureValue (common/value.h)
 struct FeatureValue {
-  int64 value;
+  double value;
 };
 
 // learning::TargetValue (common/value.h)
 struct TargetValue {
-  int64 value;
+  double value;
 };
 
 // learning::LabelledExample (common/training_example.h)
@@ -26,7 +26,19 @@
   uint64 weight = 1;
 };
 
+// Hack for TargetHistogram. Would ideally be a map<TargetValue, double>, but
+// this causes pain in the translation to WTF::HashMap. HashMap requires
+// reservations for "deleted" and "empty" sentinel values. This is especially
+// undesirable in our case because TargetValue (the map key) is designed to be
+// completely generic, supporting any possible value for double. We instead
+// use a list of pairs (key, value) to avoid having to reserve any values as
+// "empty" or "deleted".
+struct TargetHistogramPair {
+  TargetValue target_value;
+  double count;
+};
+
 // learning::TargetHistogram (common/target_histogram.h)
 struct TargetHistogram {
-  map<TargetValue, double> counts;
+  array<TargetHistogramPair> pairs;
 };
diff --git a/media/learning/mojo/public/mojom/learning_types.typemap b/media/learning/mojo/public/mojom/learning_types.typemap
index 74528c7..93ef123de 100644
--- a/media/learning/mojo/public/mojom/learning_types.typemap
+++ b/media/learning/mojo/public/mojom/learning_types.typemap
@@ -5,15 +5,15 @@
   "//media/learning/common/value.h",
 ]
 traits_headers = [ "//media/learning/mojo/public/cpp/learning_mojom_traits.h" ]
-sources = [
-  "//media/learning/mojo/public/cpp/learning_mojom_traits.cc",
-  "//media/learning/mojo/public/cpp/learning_mojom_traits.h",
+public_deps = [
+  "//media/learning/common",
+  "//media/learning/mojo/public/cpp:shared_typemap_traits",
 ]
-public_deps = [ "//media/learning/common" ]
 type_mappings = [
   "media.learning.mojom.LabelledExample=::media::learning::LabelledExample",
   "media.learning.mojom.FeatureValue=::media::learning::FeatureValue",
   "media.learning.mojom.TargetValue=::media::learning::TargetValue",
   "media.learning.mojom.ObservationCompletion=::media::learning::ObservationCompletion",
+  "media.learning.mojom.TargetHistogramPair=::media::learning::TargetHistogramPair",
   "media.learning.mojom.TargetHistogram=::media::learning::TargetHistogram",
 ]
diff --git a/media/renderers/BUILD.gn b/media/renderers/BUILD.gn
index d796d780..8e32a8f2 100644
--- a/media/renderers/BUILD.gn
+++ b/media/renderers/BUILD.gn
@@ -41,6 +41,7 @@
     "//components/viz/client",
     "//components/viz/common",
     "//gpu/command_buffer/client:gles2_interface",
+    "//gpu/command_buffer/client:raster_interface",
     "//gpu/command_buffer/common",
     "//media:media_buildflags",
     "//media/audio",
diff --git a/media/renderers/default_decoder_factory.cc b/media/renderers/default_decoder_factory.cc
index 7e83ab1..479f22e9 100644
--- a/media/renderers/default_decoder_factory.cc
+++ b/media/renderers/default_decoder_factory.cc
@@ -11,7 +11,7 @@
 #include "base/single_thread_task_runner.h"
 #include "build/build_config.h"
 #include "build/buildflag.h"
-#include "components/viz/common/gpu/context_provider.h"
+#include "components/viz/common/gpu/raster_context_provider.h"
 #include "media/base/decoder_factory.h"
 #include "media/base/media_switches.h"
 #include "media/media_buildflags.h"
@@ -120,9 +120,11 @@
 
 #if defined(OS_FUCHSIA)
   if (gpu_factories) {
-    video_decoders->push_back(CreateFuchsiaVideoDecoder(
-        gpu_factories->SharedImageInterface(),
-        gpu_factories->GetMediaContextProvider()->ContextSupport()));
+    auto* context_provider = gpu_factories->GetMediaContextProvider();
+    DCHECK(context_provider);
+    video_decoders->push_back(
+        CreateFuchsiaVideoDecoder(gpu_factories->SharedImageInterface(),
+                                  context_provider->ContextSupport()));
   }
 
   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
diff --git a/media/renderers/paint_canvas_video_renderer.cc b/media/renderers/paint_canvas_video_renderer.cc
index 4871034..6f8f515 100644
--- a/media/renderers/paint_canvas_video_renderer.cc
+++ b/media/renderers/paint_canvas_video_renderer.cc
@@ -20,11 +20,12 @@
 #include "cc/paint/paint_canvas.h"
 #include "cc/paint/paint_flags.h"
 #include "cc/paint/paint_image_builder.h"
-#include "components/viz/common/gpu/context_provider.h"
+#include "components/viz/common/gpu/raster_context_provider.h"
 #include "components/viz/common/resources/resource_format.h"
 #include "gpu/GLES2/gl2extchromium.h"
 #include "gpu/command_buffer/client/context_support.h"
 #include "gpu/command_buffer/client/gles2_interface.h"
+#include "gpu/command_buffer/client/raster_interface.h"
 #include "gpu/command_buffer/client/shared_image_interface.h"
 #include "gpu/command_buffer/common/capabilities.h"
 #include "gpu/command_buffer/common/mailbox_holder.h"
@@ -131,17 +132,17 @@
 
 class SyncTokenClientImpl : public VideoFrame::SyncTokenClient {
  public:
-  explicit SyncTokenClientImpl(gpu::gles2::GLES2Interface* gl) : gl_(gl) {}
+  explicit SyncTokenClientImpl(gpu::InterfaceBase* ib) : ib_(ib) {}
   ~SyncTokenClientImpl() override = default;
   void GenerateSyncToken(gpu::SyncToken* sync_token) override {
-    gl_->GenSyncTokenCHROMIUM(sync_token->GetData());
+    ib_->GenSyncTokenCHROMIUM(sync_token->GetData());
   }
   void WaitSyncToken(const gpu::SyncToken& sync_token) override {
-    gl_->WaitSyncTokenCHROMIUM(sync_token.GetConstData());
+    ib_->WaitSyncTokenCHROMIUM(sync_token.GetConstData());
   }
 
  private:
-  gpu::gles2::GLES2Interface* gl_;
+  gpu::InterfaceBase* ib_;
 
   DISALLOW_IMPLICIT_CONSTRUCTORS(SyncTokenClientImpl);
 };
@@ -181,18 +182,42 @@
       GLuint texture,
       const gpu::Mailbox& mailbox,
       GLenum access = GL_SHARED_IMAGE_ACCESS_MODE_READ_CHROMIUM)
-      : gl(gl), texture(texture), is_shared_image(mailbox.IsSharedImage()) {
+      : gl(gl),
+        ri(nullptr),
+        texture(texture),
+        is_shared_image(mailbox.IsSharedImage()) {
     if (is_shared_image)
       gl->BeginSharedImageAccessDirectCHROMIUM(texture, access);
   }
 
-  ~ScopedSharedImageAccess() {
+  // TODO(crbug.com/1023270): Remove this ctor once we're no longer relying on
+  // texture ids for Mailbox access as that is only supported on
+  // RasterImplementationGLES.
+  ScopedSharedImageAccess(
+      gpu::raster::RasterInterface* ri,
+      GLuint texture,
+      const gpu::Mailbox& mailbox,
+      GLenum access = GL_SHARED_IMAGE_ACCESS_MODE_READ_CHROMIUM)
+      : gl(nullptr),
+        ri(ri),
+        texture(texture),
+        is_shared_image(mailbox.IsSharedImage()) {
     if (is_shared_image)
-      gl->EndSharedImageAccessDirectCHROMIUM(texture);
+      ri->BeginSharedImageAccessDirectCHROMIUM(texture, access);
+  }
+
+  ~ScopedSharedImageAccess() {
+    if (is_shared_image) {
+      if (gl)
+        gl->EndSharedImageAccessDirectCHROMIUM(texture);
+      else
+        ri->EndSharedImageAccessDirectCHROMIUM(texture);
+    }
   }
 
  private:
   gpu::gles2::GLES2Interface* gl;
+  gpu::raster::RasterInterface* ri;
   GLuint texture;
   bool is_shared_image;
 };
@@ -202,11 +227,19 @@
                                    const gpu::SyncToken& sync_token,
                                    const gpu::Mailbox& mailbox) {
   gl->WaitSyncTokenCHROMIUM(sync_token.GetConstData());
-  if (mailbox.IsSharedImage()) {
-    return gl->CreateAndTexStorage2DSharedImageCHROMIUM(mailbox.name);
-  } else {
-    return gl->CreateAndConsumeTextureCHROMIUM(mailbox.name);
-  }
+  return mailbox.IsSharedImage()
+             ? gl->CreateAndTexStorage2DSharedImageCHROMIUM(mailbox.name)
+             : gl->CreateAndConsumeTextureCHROMIUM(mailbox.name);
+}
+
+// TODO(crbug.com/1023270): Remove this ctor once we're no longer relying on
+// texture ids for Mailbox access as that is only supported on
+// RasterImplementationGLES.
+GLuint SynchronizeAndImportMailbox(gpu::raster::RasterInterface* ri,
+                                   const gpu::SyncToken& sync_token,
+                                   const gpu::Mailbox& mailbox) {
+  ri->WaitSyncTokenCHROMIUM(sync_token.GetConstData());
+  return ri->CreateAndConsumeForGpuRaster(mailbox);
 }
 
 static constexpr size_t kNumYUVPlanes = 3;
@@ -216,12 +249,13 @@
 };
 using YUVTexturesInfo = std::array<YUVPlaneTextureInfo, kNumYUVPlanes>;
 
-YUVTexturesInfo GetYUVTexturesInfo(const VideoFrame* video_frame,
-                                   viz::ContextProvider* context_provider) {
+YUVTexturesInfo GetYUVTexturesInfo(
+    const VideoFrame* video_frame,
+    viz::RasterContextProvider* raster_context_provider) {
   YUVTexturesInfo yuv_textures_info;
 
-  gpu::gles2::GLES2Interface* gl = context_provider->ContextGL();
-  DCHECK(gl);
+  gpu::raster::RasterInterface* ri = raster_context_provider->RasterInterface();
+  DCHECK(ri);
   // TODO(bsalomon): Use GL_RGB8 once Skia supports it.
   // skbug.com/7533
   GrGLenum skia_texture_format =
@@ -235,10 +269,10 @@
         << "Unsupported texture target " << std::hex << std::showbase
         << mailbox_holder.texture_target;
     yuv_textures_info[i].texture.fID = SynchronizeAndImportMailbox(
-        gl, mailbox_holder.sync_token, mailbox_holder.mailbox);
+        ri, mailbox_holder.sync_token, mailbox_holder.mailbox);
     if (mailbox_holder.mailbox.IsSharedImage()) {
       yuv_textures_info[i].is_shared_image = true;
-      gl->BeginSharedImageAccessDirectCHROMIUM(
+      ri->BeginSharedImageAccessDirectCHROMIUM(
           yuv_textures_info[i].texture.fID,
           GL_SHARED_IMAGE_ACCESS_MODE_READ_CHROMIUM);
     }
@@ -251,25 +285,25 @@
 }
 
 void DeleteYUVTextures(const VideoFrame* video_frame,
-                       viz::ContextProvider* context_provider,
+                       viz::RasterContextProvider* raster_context_provider,
                        const YUVTexturesInfo& yuv_textures_info) {
-  gpu::gles2::GLES2Interface* gl = context_provider->ContextGL();
-  DCHECK(gl);
+  gpu::raster::RasterInterface* ri = raster_context_provider->RasterInterface();
+  DCHECK(ri);
 
   for (size_t i = 0; i < video_frame->NumTextures(); ++i) {
     if (yuv_textures_info[i].is_shared_image)
-      gl->EndSharedImageAccessDirectCHROMIUM(yuv_textures_info[i].texture.fID);
-    gl->DeleteTextures(1, &yuv_textures_info[i].texture.fID);
+      ri->EndSharedImageAccessDirectCHROMIUM(yuv_textures_info[i].texture.fID);
+    ri->DeleteGpuRasterTexture(yuv_textures_info[i].texture.fID);
   }
 }
 
 sk_sp<SkImage> NewSkImageFromVideoFrameYUVTexturesWithExternalBackend(
     const VideoFrame* video_frame,
-    viz::ContextProvider* context_provider,
+    viz::RasterContextProvider* raster_context_provider,
     unsigned int texture_target,
     unsigned int texture_id) {
   DCHECK(video_frame->HasTextures());
-  GrContext* gr_context = context_provider->GrContext();
+  GrContext* gr_context = raster_context_provider->GrContext();
   DCHECK(gr_context);
   // TODO: We should compare the DCHECK vs when UpdateLastImage calls this
   // function. (https://crbug.com/674185)
@@ -283,7 +317,7 @@
   GrGLTextureInfo backend_texture{};
 
   YUVTexturesInfo yuv_textures_info =
-      GetYUVTexturesInfo(video_frame, context_provider);
+      GetYUVTexturesInfo(video_frame, raster_context_provider);
 
   GrBackendTexture yuv_textures[3] = {
       GrBackendTexture(ya_tex_size.width(), ya_tex_size.height(),
@@ -305,17 +339,12 @@
       yuv_textures, result_texture);
   gr_context->flush();
 
-  DeleteYUVTextures(video_frame, context_provider, yuv_textures_info);
+  DeleteYUVTextures(video_frame, raster_context_provider, yuv_textures_info);
 
   return img;
 }
 
-// Imports a VideoFrame that contains a single mailbox into a newly created GL
-// texture, after synchronization with the sync token. Returns the GL texture.
-// |mailbox| is set to the imported mailbox.
-GLuint ImportVideoFrameSingleMailbox(gpu::gles2::GLES2Interface* gl,
-                                     VideoFrame* video_frame,
-                                     gpu::Mailbox* mailbox) {
+const gpu::MailboxHolder& GetVideoFrameMailboxHolder(VideoFrame* video_frame) {
   DCHECK(video_frame->HasTextures());
   DCHECK_EQ(video_frame->NumTextures(), 1u);
 
@@ -333,17 +362,40 @@
          mailbox_holder.texture_target == GL_TEXTURE_RECTANGLE_ARB ||
          mailbox_holder.texture_target == GL_TEXTURE_EXTERNAL_OES)
       << mailbox_holder.texture_target;
+  return mailbox_holder;
+}
 
+// Imports a VideoFrame that contains a single mailbox into a newly created GL
+// texture, after synchronization with the sync token. Returns the GL texture.
+// |mailbox| is set to the imported mailbox.
+GLuint ImportVideoFrameSingleMailbox(gpu::gles2::GLES2Interface* gl,
+                                     VideoFrame* video_frame,
+                                     gpu::Mailbox* mailbox) {
+  const gpu::MailboxHolder& mailbox_holder =
+      GetVideoFrameMailboxHolder(video_frame);
   *mailbox = mailbox_holder.mailbox;
   return SynchronizeAndImportMailbox(gl, mailbox_holder.sync_token, *mailbox);
 }
 
+// TODO(crbug.com/1023270): Remove this function once we're no longer relying on
+// texture ids for Mailbox access as that is only supported on
+// RasterImplementationGLES.
+GLuint ImportVideoFrameSingleMailbox(gpu::raster::RasterInterface* ri,
+                                     VideoFrame* video_frame,
+                                     gpu::Mailbox* mailbox) {
+  const gpu::MailboxHolder& mailbox_holder =
+      GetVideoFrameMailboxHolder(video_frame);
+  *mailbox = mailbox_holder.mailbox;
+  return SynchronizeAndImportMailbox(ri, mailbox_holder.sync_token, *mailbox);
+}
+
 // Wraps a GL RGBA texture into a SkImage.
-sk_sp<SkImage> WrapGLTexture(GLenum target,
-                             GLuint texture_id,
-                             const gfx::Size& size,
-                             const gfx::ColorSpace& color_space,
-                             viz::ContextProvider* context_provider) {
+sk_sp<SkImage> WrapGLTexture(
+    GLenum target,
+    GLuint texture_id,
+    const gfx::Size& size,
+    const gfx::ColorSpace& color_space,
+    viz::RasterContextProvider* raster_context_provider) {
   GrGLTextureInfo texture_info;
   texture_info.fID = texture_id;
   texture_info.fTarget = target;
@@ -354,9 +406,9 @@
   GrBackendTexture backend_texture(size.width(), size.height(),
                                    GrMipMapped::kNo, texture_info);
   return SkImage::MakeFromTexture(
-      context_provider->GrContext(), backend_texture, kTopLeft_GrSurfaceOrigin,
-      kRGBA_8888_SkColorType, kPremul_SkAlphaType, color_space.ToSkColorSpace(),
-      nullptr, nullptr);
+      raster_context_provider->GrContext(), backend_texture,
+      kTopLeft_GrSurfaceOrigin, kRGBA_8888_SkColorType, kPremul_SkAlphaType,
+      color_space.ToSkColorSpace(), nullptr, nullptr);
 }
 
 void VideoFrameCopyTextureOrSubTexture(gpu::gles2::GLES2Interface* gl,
@@ -400,17 +452,17 @@
 }
 
 void OnQueryDone(scoped_refptr<VideoFrame> video_frame,
-                 gpu::gles2::GLES2Interface* gl,
+                 gpu::raster::RasterInterface* ri,
                  unsigned query_id) {
-  gl->DeleteQueriesEXT(1, &query_id);
+  ri->DeleteQueriesEXT(1, &query_id);
   // |video_frame| is dropped here.
 }
 
 void SynchronizeVideoFrameRead(scoped_refptr<VideoFrame> video_frame,
-                               gpu::gles2::GLES2Interface* gl,
+                               gpu::raster::RasterInterface* ri,
                                gpu::ContextSupport* context_support) {
-  DCHECK(gl);
-  SyncTokenClientImpl client(gl);
+  DCHECK(ri);
+  SyncTokenClientImpl client(ri);
   video_frame->UpdateReleaseSyncToken(&client);
 
   if (video_frame->metadata()->IsTrue(
@@ -418,12 +470,12 @@
     // |video_frame| must be kept alive during read operations.
     DCHECK(context_support);
     unsigned query_id = 0;
-    gl->GenQueriesEXT(1, &query_id);
+    ri->GenQueriesEXT(1, &query_id);
     DCHECK(query_id);
-    gl->BeginQueryEXT(GL_COMMANDS_COMPLETED_CHROMIUM, query_id);
-    gl->EndQueryEXT(GL_COMMANDS_COMPLETED_CHROMIUM);
+    ri->BeginQueryEXT(GL_COMMANDS_COMPLETED_CHROMIUM, query_id);
+    ri->EndQueryEXT(GL_COMMANDS_COMPLETED_CHROMIUM);
     context_support->SignalQuery(
-        query_id, base::BindOnce(&OnQueryDone, video_frame, gl, query_id));
+        query_id, base::BindOnce(&OnQueryDone, video_frame, ri, query_id));
   }
 }
 
@@ -817,12 +869,13 @@
 
 PaintCanvasVideoRenderer::~PaintCanvasVideoRenderer() = default;
 
-void PaintCanvasVideoRenderer::Paint(scoped_refptr<VideoFrame> video_frame,
-                                     cc::PaintCanvas* canvas,
-                                     const gfx::RectF& dest_rect,
-                                     cc::PaintFlags& flags,
-                                     VideoTransformation video_transformation,
-                                     viz::ContextProvider* context_provider) {
+void PaintCanvasVideoRenderer::Paint(
+    scoped_refptr<VideoFrame> video_frame,
+    cc::PaintCanvas* canvas,
+    const gfx::RectF& dest_rect,
+    cc::PaintFlags& flags,
+    VideoTransformation video_transformation,
+    viz::RasterContextProvider* raster_context_provider) {
   DCHECK(thread_checker_.CalledOnValidThread());
   if (flags.getAlpha() == 0) {
     return;
@@ -847,7 +900,7 @@
 
   // Don't allow wrapping the VideoFrame texture, as we want to be able to cache
   // the PaintImage, to avoid redundant readbacks if the canvas is software.
-  if (!UpdateLastImage(video_frame, context_provider,
+  if (!UpdateLastImage(video_frame, raster_context_provider,
                        false /* allow_wrap_texture */))
     return;
   DCHECK(cache_);
@@ -858,8 +911,8 @@
   if (video_frame->HasTextures()) {
     DCHECK(!cache_->source_mailbox.IsZero());
     DCHECK(cache_->source_texture);
-    source_access.emplace(context_provider->ContextGL(), cache_->source_texture,
-                          cache_->source_mailbox);
+    source_access.emplace(raster_context_provider->RasterInterface(),
+                          cache_->source_texture, cache_->source_mailbox);
   }
 
   cc::PaintFlags video_flags;
@@ -936,8 +989,8 @@
     // Synchronize |video_frame| with the read operations in UpdateLastImage(),
     // which are triggered by canvas->flush().
     SynchronizeVideoFrameRead(std::move(video_frame),
-                              context_provider->ContextGL(),
-                              context_provider->ContextSupport());
+                              raster_context_provider->RasterInterface(),
+                              raster_context_provider->ContextSupport());
   }
   // Because we are not retaining a reference to the VideoFrame, it would be
   // invalid for the cache to directly wrap its texture(s), as they will be
@@ -945,16 +998,17 @@
   DCHECK(!cache_ || !cache_->wraps_video_frame_texture);
 }
 
-void PaintCanvasVideoRenderer::Copy(scoped_refptr<VideoFrame> video_frame,
-                                    cc::PaintCanvas* canvas,
-                                    viz::ContextProvider* context_provider) {
+void PaintCanvasVideoRenderer::Copy(
+    scoped_refptr<VideoFrame> video_frame,
+    cc::PaintCanvas* canvas,
+    viz::RasterContextProvider* raster_context_provider) {
   cc::PaintFlags flags;
   flags.setBlendMode(SkBlendMode::kSrc);
   flags.setFilterQuality(kLow_SkFilterQuality);
 
   auto dest_rect = gfx::RectF(gfx::SizeF(video_frame->visible_rect().size()));
   Paint(std::move(video_frame), canvas, dest_rect, flags,
-        media::kNoTransformation, context_provider);
+        media::kNoTransformation, raster_context_provider);
 }
 
 namespace {
@@ -1231,7 +1285,7 @@
 }
 
 bool PaintCanvasVideoRenderer::CopyVideoFrameTexturesToGLTexture(
-    viz::ContextProvider* context_provider,
+    viz::RasterContextProvider* raster_context_provider,
     gpu::gles2::GLES2Interface* destination_gl,
     scoped_refptr<VideoFrame> video_frame,
     unsigned int target,
@@ -1248,24 +1302,25 @@
   if (video_frame->NumTextures() > 1 ||
       video_frame->metadata()->IsTrue(
           VideoFrameMetadata::READ_LOCK_FENCES_ENABLED)) {
-    if (!context_provider)
+    if (!raster_context_provider)
       return false;
-    GrContext* gr_context = context_provider->GrContext();
+    GrContext* gr_context = raster_context_provider->GrContext();
     if (!gr_context)
       return false;
-    if (!UpdateLastImage(video_frame, context_provider,
+    if (!UpdateLastImage(video_frame, raster_context_provider,
                          true /* allow_wrap_texture */)) {
       return false;
     }
 
     DCHECK(cache_);
     DCHECK(!cache_->source_mailbox.IsZero());
-    gpu::gles2::GLES2Interface* canvas_gl = context_provider->ContextGL();
+    gpu::raster::RasterInterface* canvas_ri =
+        raster_context_provider->RasterInterface();
 
     gpu::SyncToken sync_token;
     // Wait for mailbox creation on canvas context before consuming it and
     // copying from it on the consumer context.
-    canvas_gl->GenUnverifiedSyncTokenCHROMIUM(sync_token.GetData());
+    canvas_ri->GenUnverifiedSyncTokenCHROMIUM(sync_token.GetData());
 
     uint32_t intermediate_texture = SynchronizeAndImportMailbox(
         destination_gl, sync_token, cache_->source_mailbox);
@@ -1284,7 +1339,7 @@
     // canvas context.
     gpu::SyncToken dest_sync_token;
     destination_gl->GenUnverifiedSyncTokenCHROMIUM(dest_sync_token.GetData());
-    canvas_gl->WaitSyncTokenCHROMIUM(dest_sync_token.GetConstData());
+    canvas_ri->WaitSyncTokenCHROMIUM(dest_sync_token.GetConstData());
 
     // Because we are not retaining a reference to the VideoFrame, it would be
     // invalid to keep the cache around if it directly wraps the VideoFrame
@@ -1295,13 +1350,14 @@
     // Synchronize |video_frame| with the read operations in UpdateLastImage(),
     // which are triggered by getBackendTexture() or CopyTextureCHROMIUM (in the
     // case the cache was referencing its texture(s) directly).
-    SynchronizeVideoFrameRead(std::move(video_frame), canvas_gl,
-                              context_provider->ContextSupport());
+    SynchronizeVideoFrameRead(std::move(video_frame), canvas_ri,
+                              raster_context_provider->ContextSupport());
   } else {
     CopyVideoFrameSingleTextureToGLTexture(
         destination_gl, video_frame.get(), target, texture, internal_format,
         format, type, level, premultiply_alpha, flip_y);
-    SynchronizeVideoFrameRead(std::move(video_frame), destination_gl, nullptr);
+    SyncTokenClientImpl client(destination_gl);
+    video_frame->UpdateReleaseSyncToken(&client);
   }
   DCHECK(!cache_ || !cache_->wraps_video_frame_texture);
 
@@ -1309,7 +1365,7 @@
 }
 
 bool PaintCanvasVideoRenderer::PrepareVideoFrameForWebGL(
-    viz::ContextProvider* context_provider,
+    viz::RasterContextProvider* raster_context_provider,
     gpu::gles2::GLES2Interface* destination_gl,
     scoped_refptr<VideoFrame> video_frame,
     unsigned int target,
@@ -1328,7 +1384,7 @@
     return false;
   }
 
-  if (!context_provider || !context_provider->GrContext())
+  if (!raster_context_provider || !raster_context_provider->GrContext())
     return false;
 
   // Take webgl video texture as 2D texture. Setting it as external render
@@ -1339,7 +1395,8 @@
                              video_frame->coded_size().height(), 0, GL_RGBA,
                              GL_UNSIGNED_BYTE, nullptr);
 
-  gpu::gles2::GLES2Interface* source_gl = context_provider->ContextGL();
+  gpu::raster::RasterInterface* source_ri =
+      raster_context_provider->RasterInterface();
   gpu::MailboxHolder mailbox_holder;
   mailbox_holder.texture_target = target;
   destination_gl->ProduceTextureDirectCHROMIUM(texture,
@@ -1348,35 +1405,25 @@
   destination_gl->GenUnverifiedSyncTokenCHROMIUM(
       mailbox_holder.sync_token.GetData());
 
-  source_gl->WaitSyncTokenCHROMIUM(mailbox_holder.sync_token.GetConstData());
+  source_ri->WaitSyncTokenCHROMIUM(mailbox_holder.sync_token.GetConstData());
 
   uint32_t shared_texture =
-      source_gl->CreateAndConsumeTextureCHROMIUM(mailbox_holder.mailbox.name);
+      source_ri->CreateAndConsumeForGpuRaster(mailbox_holder.mailbox);
 
-  if (!PrepareVideoFrame(video_frame, context_provider, target,
+  if (!PrepareVideoFrame(video_frame, raster_context_provider, target,
                          shared_texture)) {
     return false;
   }
 
-  // Warning : This approach has failed previously. The history is
-  // https://chromium-review.googlesource.com/c/chromium/src/+/1251321.
-  // It failed to execute texture copy on mac.
-  // The possible solution is here:
-  // https://chromium-review.googlesource.com/c/chromium/src/+/1258212
-  // make a copy of the video texture in that case so that the copy
-  // could be done in |destination_gl|.
-  source_gl->ProduceTextureDirectCHROMIUM(shared_texture,
-                                          mailbox_holder.mailbox.name);
-
   // Wait for mailbox creation on canvas context before consuming it and
   // copying from it on the consumer context.
-  source_gl->GenUnverifiedSyncTokenCHROMIUM(
+  source_ri->GenUnverifiedSyncTokenCHROMIUM(
       mailbox_holder.sync_token.GetData());
 
   destination_gl->WaitSyncTokenCHROMIUM(
       mailbox_holder.sync_token.GetConstData());
 
-  SyncTokenClientImpl client(source_gl);
+  SyncTokenClientImpl client(source_ri);
   video_frame->UpdateReleaseSyncToken(&client);
 
   DCHECK(!cache_ || !cache_->wraps_video_frame_texture);
@@ -1384,7 +1431,7 @@
 }
 
 bool PaintCanvasVideoRenderer::CopyVideoFrameYUVDataToGLTexture(
-    viz::ContextProvider* context_provider,
+    viz::RasterContextProvider* raster_context_provider,
     gpu::gles2::GLES2Interface* destination_gl,
     const VideoFrame& video_frame,
     unsigned int target,
@@ -1395,8 +1442,8 @@
     int level,
     bool premultiply_alpha,
     bool flip_y) {
-  DCHECK(context_provider);
-  GrContext* gr_context = context_provider->GrContext();
+  DCHECK(raster_context_provider);
+  GrContext* gr_context = raster_context_provider->GrContext();
   if (!gr_context) {
     return false;
   }
@@ -1450,29 +1497,30 @@
     yuv_textures[plane] = yuv_images[plane]->getBackendTexture(false);
   }
 
-  auto* sii = context_provider->SharedImageInterface();
-  gpu::gles2::GLES2Interface* source_gl = context_provider->ContextGL();
+  auto* sii = raster_context_provider->SharedImageInterface();
+  gpu::raster::RasterInterface* source_ri =
+      raster_context_provider->RasterInterface();
 
   // We need a shared image to receive the intermediate RGB result. Try to reuse
   // one if compatible, otherwise create a new one.
   if (yuv_cache_.texture && yuv_cache_.size == video_frame.coded_size() &&
-      yuv_cache_.context_provider == context_provider) {
-    source_gl->WaitSyncTokenCHROMIUM(yuv_cache_.sync_token.GetConstData());
+      yuv_cache_.raster_context_provider == raster_context_provider) {
+    source_ri->WaitSyncTokenCHROMIUM(yuv_cache_.sync_token.GetConstData());
   } else {
     yuv_cache_.Reset();
-    yuv_cache_.context_provider = context_provider;
+    yuv_cache_.raster_context_provider = raster_context_provider;
     yuv_cache_.size = video_frame.coded_size();
     yuv_cache_.mailbox = sii->CreateSharedImage(
         viz::ResourceFormat::RGBA_8888, video_frame.coded_size(),
         gfx::ColorSpace(), gpu::SHARED_IMAGE_USAGE_GLES2);
     yuv_cache_.texture = SynchronizeAndImportMailbox(
-        source_gl, sii->GenUnverifiedSyncToken(), yuv_cache_.mailbox);
+        source_ri, sii->GenUnverifiedSyncToken(), yuv_cache_.mailbox);
   }
 
   // On the source GL context, do the YUV->RGB conversion using Skia.
   gpu::SyncToken post_conversion_sync_token;
   {
-    source_gl->BeginSharedImageAccessDirectCHROMIUM(
+    source_ri->BeginSharedImageAccessDirectCHROMIUM(
         yuv_cache_.texture, GL_SHARED_IMAGE_ACCESS_MODE_READWRITE_CHROMIUM);
 
     GrGLTextureInfo backend_texture = {};
@@ -1488,9 +1536,9 @@
         yuv_textures, result_texture);
 
     gr_context->flush();
-    source_gl->EndSharedImageAccessDirectCHROMIUM(yuv_cache_.texture);
+    source_ri->EndSharedImageAccessDirectCHROMIUM(yuv_cache_.texture);
 
-    source_gl->GenUnverifiedSyncTokenCHROMIUM(
+    source_ri->GenUnverifiedSyncTokenCHROMIUM(
         post_conversion_sync_token.GetData());
 
     if (!yuv_image) {
@@ -1609,18 +1657,18 @@
 PaintCanvasVideoRenderer::Cache::Cache(int frame_id) : frame_id(frame_id) {}
 
 PaintCanvasVideoRenderer::Cache::~Cache() {
-  if (!context_provider)
+  if (!raster_context_provider)
     return;
 
   DCHECK(!source_mailbox.IsZero());
   DCHECK(source_texture);
-  auto* gl = context_provider->ContextGL();
+  auto* ri = raster_context_provider->RasterInterface();
   if (!texture_ownership_in_skia)
-    gl->DeleteTextures(1, &source_texture);
+    ri->DeleteGpuRasterTexture(source_texture);
   if (!wraps_video_frame_texture) {
     gpu::SyncToken sync_token;
-    gl->GenUnverifiedSyncTokenCHROMIUM(sync_token.GetData());
-    auto* sii = context_provider->SharedImageInterface();
+    ri->GenUnverifiedSyncTokenCHROMIUM(sync_token.GetData());
+    auto* sii = raster_context_provider->SharedImageInterface();
     sii->DestroySharedImage(sync_token, source_mailbox);
   }
 }
@@ -1635,19 +1683,20 @@
     return false;
 
   // Flush any pending GPU work using this texture.
-  sk_image->flush(context_provider->GrContext());
+  sk_image->flush(raster_context_provider->GrContext());
 
   // We need a new texture ID because skia will destroy the previous one with
   // the SkImage.
   texture_ownership_in_skia = false;
-  source_texture = SynchronizeAndImportMailbox(
-      context_provider->ContextGL(), gpu::SyncToken(), source_mailbox);
+  source_texture =
+      SynchronizeAndImportMailbox(raster_context_provider->RasterInterface(),
+                                  gpu::SyncToken(), source_mailbox);
   return true;
 }
 
 bool PaintCanvasVideoRenderer::UpdateLastImage(
     scoped_refptr<VideoFrame> video_frame,
-    viz::ContextProvider* context_provider,
+    viz::RasterContextProvider* raster_context_provider,
     bool allow_wrap_texture) {
   DCHECK(!cache_ || !cache_->wraps_video_frame_texture);
   if (!cache_ || video_frame->unique_id() != cache_->frame_id ||
@@ -1664,39 +1713,41 @@
     // Holding |video_frame| longer than this call when using GPUVideoDecoder
     // could cause problems since the pool of VideoFrames has a fixed size.
     if (video_frame->HasTextures()) {
-      DCHECK(context_provider);
-      DCHECK(context_provider->GrContext());
-      auto* gl = context_provider->ContextGL();
-      DCHECK(gl);
+      DCHECK(raster_context_provider);
+      DCHECK(raster_context_provider->GrContext());
+      auto* ri = raster_context_provider->RasterInterface();
+      DCHECK(ri);
 
       sk_sp<SkImage> source_image;
 
       if (allow_wrap_texture && video_frame->NumTextures() == 1) {
         cache_.emplace(video_frame->unique_id());
         cache_->source_texture = ImportVideoFrameSingleMailbox(
-            gl, video_frame.get(), &cache_->source_mailbox);
+            ri, video_frame.get(), &cache_->source_mailbox);
         cache_->wraps_video_frame_texture = true;
         source_image =
             WrapGLTexture(video_frame->mailbox_holder(0).texture_target,
                           cache_->source_texture, video_frame->coded_size(),
-                          video_frame->ColorSpace(), context_provider);
+                          video_frame->ColorSpace(), raster_context_provider);
       } else {
-        if (cache_ && cache_->context_provider == context_provider &&
+        if (cache_ &&
+            cache_->raster_context_provider == raster_context_provider &&
             cache_->coded_size == video_frame->coded_size() &&
             cache_->Recycle()) {
           // We can reuse the shared image from the previous cache.
           cache_->frame_id = video_frame->unique_id();
         } else {
           cache_.emplace(video_frame->unique_id());
-          auto* sii = context_provider->SharedImageInterface();
+          auto* sii = raster_context_provider->SharedImageInterface();
           cache_->source_mailbox = sii->CreateSharedImage(
               viz::ResourceFormat::RGBA_8888, video_frame->coded_size(),
               gfx::ColorSpace(), gpu::SHARED_IMAGE_USAGE_GLES2);
           cache_->source_texture = SynchronizeAndImportMailbox(
-              gl, sii->GenUnverifiedSyncToken(), cache_->source_mailbox);
+              ri, sii->GenUnverifiedSyncToken(), cache_->source_mailbox);
         }
 
         DCHECK(!cache_->texture_ownership_in_skia);
+        auto* gl = raster_context_provider->ContextGL();
         ScopedSharedImageAccess dest_access(
             gl, cache_->source_texture, cache_->source_mailbox,
             GL_SHARED_IMAGE_ACCESS_MODE_READWRITE_CHROMIUM);
@@ -1713,22 +1764,22 @@
                                        GL_FALSE, GL_FALSE, GL_FALSE);
           }
           gl->DeleteTextures(1, &frame_texture);
-          source_image = WrapGLTexture(GL_TEXTURE_2D, cache_->source_texture,
-                                       video_frame->coded_size(),
-                                       gfx::ColorSpace(), context_provider);
+          source_image = WrapGLTexture(
+              GL_TEXTURE_2D, cache_->source_texture, video_frame->coded_size(),
+              gfx::ColorSpace(), raster_context_provider);
         } else {
           source_image = NewSkImageFromVideoFrameYUVTexturesWithExternalBackend(
-              video_frame.get(), context_provider, GL_TEXTURE_2D,
+              video_frame.get(), raster_context_provider, GL_TEXTURE_2D,
               cache_->source_texture);
         }
-        context_provider->GrContext()->flush();
+        raster_context_provider->GrContext()->flush();
       }
       if (!source_image) {
         // Couldn't create the SkImage.
         cache_.reset();
         return false;
       }
-      cache_->context_provider = context_provider;
+      cache_->raster_context_provider = raster_context_provider;
       cache_->coded_size = video_frame->coded_size();
       cache_->visible_rect = video_frame->visible_rect();
       sk_sp<SkImage> source_subset =
@@ -1750,7 +1801,7 @@
             subset_backend.isSameTexture(image_backend)) {
           cache_->texture_ownership_in_skia = true;
           source_subset = SkImage::MakeFromAdoptedTexture(
-              cache_->context_provider->GrContext(), image_backend,
+              cache_->raster_context_provider->GrContext(), image_backend,
               kTopLeft_GrSurfaceOrigin, kRGBA_8888_SkColorType,
               kPremul_SkAlphaType, source_image->imageInfo().refColorSpace());
         }
@@ -1777,7 +1828,7 @@
 
 bool PaintCanvasVideoRenderer::PrepareVideoFrame(
     scoped_refptr<VideoFrame> video_frame,
-    viz::ContextProvider* context_provider,
+    viz::RasterContextProvider* raster_context_provider,
     unsigned int textureTarget,
     unsigned int texture) {
   cache_.emplace(video_frame->unique_id());
@@ -1793,13 +1844,13 @@
   // Holding |video_frame| longer than this call when using GPUVideoDecoder
   // could cause problems since the pool of VideoFrames has a fixed size.
   if (video_frame->HasTextures()) {
-    DCHECK(context_provider);
-    DCHECK(context_provider->GrContext());
-    DCHECK(context_provider->ContextGL());
+    DCHECK(raster_context_provider);
+    DCHECK(raster_context_provider->GrContext());
+    DCHECK(raster_context_provider->RasterInterface());
     sk_sp<SkImage> source_image;
     if (video_frame->NumTextures() > 1) {
       source_image = NewSkImageFromVideoFrameYUVTexturesWithExternalBackend(
-          video_frame.get(), context_provider, textureTarget, texture);
+          video_frame.get(), raster_context_provider, textureTarget, texture);
       if (!source_image) {
         // Couldn't create the SkImage.
         cache_.reset();
@@ -1829,22 +1880,22 @@
 void PaintCanvasVideoRenderer::YUVTextureCache::Reset() {
   if (!texture)
     return;
-  DCHECK(context_provider);
+  DCHECK(raster_context_provider);
 
-  gpu::gles2::GLES2Interface* gl = context_provider->ContextGL();
-  gl->WaitSyncTokenCHROMIUM(sync_token.GetConstData());
-  gl->DeleteTextures(1, &texture);
+  gpu::raster::RasterInterface* ri = raster_context_provider->RasterInterface();
+  ri->WaitSyncTokenCHROMIUM(sync_token.GetConstData());
+  ri->DeleteGpuRasterTexture(texture);
   texture = 0;
-  gl->OrderingBarrierCHROMIUM();
+  ri->OrderingBarrierCHROMIUM();
 
-  auto* sii = context_provider->SharedImageInterface();
+  auto* sii = raster_context_provider->SharedImageInterface();
   sii->DestroySharedImage(sync_token, mailbox);
 
   // Kick off the GL work up to the OrderingBarrierCHROMIUM above as well as the
   // SharedImageInterface work, to ensure the shared image memory is released in
   // a timely fashion.
-  context_provider->ContextSupport()->FlushPendingWork();
-  context_provider.reset();
+  raster_context_provider->ContextSupport()->FlushPendingWork();
+  raster_context_provider.reset();
 }
 
 gfx::Size PaintCanvasVideoRenderer::LastImageDimensionsForTesting() {
diff --git a/media/renderers/paint_canvas_video_renderer.h b/media/renderers/paint_canvas_video_renderer.h
index 02c277cf..d8ca22e3 100644
--- a/media/renderers/paint_canvas_video_renderer.h
+++ b/media/renderers/paint_canvas_video_renderer.h
@@ -36,7 +36,7 @@
 }  // namespace gpu
 
 namespace viz {
-class ContextProvider;
+class RasterContextProvider;
 }
 
 namespace media {
@@ -59,7 +59,7 @@
              const gfx::RectF& dest_rect,
              cc::PaintFlags& flags,
              VideoTransformation video_transformation,
-             viz::ContextProvider* context_provider);
+             viz::RasterContextProvider* raster_context_provider);
 
   // Paints |video_frame| scaled to its visible size on |canvas|.
   //
@@ -67,7 +67,7 @@
   // and |context_support| must be provided.
   void Copy(scoped_refptr<VideoFrame> video_frame,
             cc::PaintCanvas* canvas,
-            viz::ContextProvider* context_provider);
+            viz::RasterContextProvider* raster_context_provider);
 
   // Convert the contents of |video_frame| to raw RGB pixels. |rgb_pixels|
   // should point into a buffer large enough to hold as many 32 bit RGBA pixels
@@ -96,7 +96,7 @@
   //
   // The format of |video_frame| must be VideoFrame::NATIVE_TEXTURE.
   bool CopyVideoFrameTexturesToGLTexture(
-      viz::ContextProvider* context_provider,
+      viz::RasterContextProvider* raster_context_provider,
       gpu::gles2::GLES2Interface* destination_gl,
       scoped_refptr<VideoFrame> video_frame,
       unsigned int target,
@@ -108,11 +108,12 @@
       bool premultiply_alpha,
       bool flip_y);
 
-  bool PrepareVideoFrameForWebGL(viz::ContextProvider* context_provider,
-                                 gpu::gles2::GLES2Interface* gl,
-                                 scoped_refptr<VideoFrame> video_frame,
-                                 unsigned int target,
-                                 unsigned int texture);
+  bool PrepareVideoFrameForWebGL(
+      viz::RasterContextProvider* raster_context_provider,
+      gpu::gles2::GLES2Interface* gl,
+      scoped_refptr<VideoFrame> video_frame,
+      unsigned int target,
+      unsigned int texture);
 
   // Copy the CPU-side YUV contents of |video_frame| to texture |texture| in
   // context |destination_gl|.
@@ -122,7 +123,7 @@
   // CorrectLastImageDimensions() ensures that the source texture will be
   // cropped to |visible_rect|. Returns true on success.
   bool CopyVideoFrameYUVDataToGLTexture(
-      viz::ContextProvider* context_provider,
+      viz::RasterContextProvider* raster_context_provider,
       gpu::gles2::GLES2Interface* destination_gl,
       const VideoFrame& video_frame,
       unsigned int target,
@@ -201,7 +202,7 @@
 
     // The context provider used to generate |source_mailbox| and
     // |source_texture|. This is only set if the VideoFrame was texture-backed.
-    scoped_refptr<viz::ContextProvider> context_provider;
+    scoped_refptr<viz::RasterContextProvider> raster_context_provider;
 
     // The mailbox for the source texture. This can be either the source
     // VideoFrame's texture (if |wraps_video_frame_texture| is true) or a newly
@@ -239,11 +240,11 @@
   // Update the cache holding the most-recently-painted frame. Returns false
   // if the image couldn't be updated.
   bool UpdateLastImage(scoped_refptr<VideoFrame> video_frame,
-                       viz::ContextProvider* context_provider,
+                       viz::RasterContextProvider* raster_context_provider,
                        bool allow_wrap_texture);
 
   bool PrepareVideoFrame(scoped_refptr<VideoFrame> video_frame,
-                         viz::ContextProvider* context_provider,
+                         viz::RasterContextProvider* raster_context_provider,
                          unsigned int textureTarget,
                          unsigned int texture);
 
@@ -263,7 +264,7 @@
     void Reset();
 
     // The ContextProvider that holds the texture.
-    scoped_refptr<viz::ContextProvider> context_provider;
+    scoped_refptr<viz::RasterContextProvider> raster_context_provider;
 
     // The size of the texture.
     gfx::Size size;
diff --git a/media/video/gpu_video_accelerator_factories.h b/media/video/gpu_video_accelerator_factories.h
index 70677dd..cfdbdbb 100644
--- a/media/video/gpu_video_accelerator_factories.h
+++ b/media/video/gpu_video_accelerator_factories.h
@@ -41,7 +41,7 @@
 }
 
 namespace viz {
-class ContextProvider;
+class RasterContextProvider;
 }  // namespace viz
 
 namespace media {
@@ -146,7 +146,7 @@
   virtual base::Optional<VideoEncodeAccelerator::SupportedProfiles>
   GetVideoEncodeAcceleratorSupportedProfiles() = 0;
 
-  virtual scoped_refptr<viz::ContextProvider> GetMediaContextProvider() = 0;
+  virtual viz::RasterContextProvider* GetMediaContextProvider() = 0;
 
   // Sets the current pipeline rendering color space.
   virtual void SetRenderingColorSpace(const gfx::ColorSpace& color_space) = 0;
diff --git a/media/video/mock_gpu_video_accelerator_factories.h b/media/video/mock_gpu_video_accelerator_factories.h
index 2a73a298..63b5877c 100644
--- a/media/video/mock_gpu_video_accelerator_factories.h
+++ b/media/video/mock_gpu_video_accelerator_factories.h
@@ -44,7 +44,7 @@
   MOCK_METHOD0(DoCreateVideoEncodeAccelerator, VideoEncodeAccelerator*());
 
   MOCK_METHOD0(GetTaskRunner, scoped_refptr<base::SingleThreadTaskRunner>());
-  MOCK_METHOD0(GetMediaContextProvider, scoped_refptr<viz::ContextProvider>());
+  MOCK_METHOD0(GetMediaContextProvider, viz::RasterContextProvider*());
   MOCK_METHOD1(SetRenderingColorSpace, void(const gfx::ColorSpace&));
 
   std::unique_ptr<gfx::GpuMemoryBuffer> CreateGpuMemoryBuffer(
diff --git a/mojo/public/tools/bindings/blink_bindings_configuration.gni b/mojo/public/tools/bindings/blink_bindings_configuration.gni
index 2684c0a..628fc3e 100644
--- a/mojo/public/tools/bindings/blink_bindings_configuration.gni
+++ b/mojo/public/tools/bindings/blink_bindings_configuration.gni
@@ -10,6 +10,7 @@
   "//cc/typemaps.gni",
   "//device/gamepad/public/cpp/typemaps.gni",
   "//device/vr/public/mojom/blink_typemaps.gni",
+  "//media/learning/mojo/public/cpp/typemaps.gni",
   "//mojo/public/cpp/bindings/tests/blink_typemaps.gni",
   "//skia/public/mojom/typemaps.gni",
   "//third_party/blink/renderer/platform/mojo/blink_typemaps.gni",
diff --git a/mojo/public/tools/bindings/gen_data_files_list.py b/mojo/public/tools/bindings/gen_data_files_list.py
index e797749..79c9e50 100644
--- a/mojo/public/tools/bindings/gen_data_files_list.py
+++ b/mojo/public/tools/bindings/gen_data_files_list.py
@@ -21,10 +21,12 @@
 from cStringIO import StringIO
 from optparse import OptionParser
 
-sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)),
-                                "pylib"))
+sys.path.insert(
+    0,
+    os.path.join(
+        os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "mojom"))
 
-from mojom.generate.generator import  WriteFile
+from mojom.generate.generator import WriteFile
 
 
 def main():
diff --git a/mojo/public/tools/bindings/generate_type_mappings.py b/mojo/public/tools/bindings/generate_type_mappings.py
index f58bcdc..7a53457 100755
--- a/mojo/public/tools/bindings/generate_type_mappings.py
+++ b/mojo/public/tools/bindings/generate_type_mappings.py
@@ -63,8 +63,10 @@
 import re
 import sys
 
-sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)),
-                                "pylib"))
+sys.path.insert(
+    0,
+    os.path.join(
+        os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "mojom"))
 
 from mojom.generate.generator import WriteFile
 
diff --git a/mojo/public/tools/bindings/mojom.gni b/mojo/public/tools/bindings/mojom.gni
index a395b9c..28318be6 100644
--- a/mojo/public/tools/bindings/mojom.gni
+++ b/mojo/public/tools/bindings/mojom.gni
@@ -63,27 +63,28 @@
     (is_mac || is_win || (is_linux && !is_chromeos && !is_chromecast) ||
      ((enable_nacl || is_nacl || is_nacl_nonsfi) && target_os != "chromeos"))
 
-mojom_generator_root = "//mojo/public/tools/bindings"
-compile_typescript_script = "$mojom_generator_root/compile_typescript.py"
+_mojom_tools_root = "//mojo/public/tools"
+_mojom_library_root = "$_mojom_tools_root/mojom/mojom"
+mojom_generator_root = "$_mojom_tools_root/bindings"
 mojom_generator_script = "$mojom_generator_root/mojom_bindings_generator.py"
 mojom_generator_sources = [
+  "$_mojom_library_root/__init__.py",
+  "$_mojom_library_root/error.py",
+  "$_mojom_library_root/generate/__init__.py",
+  "$_mojom_library_root/generate/constant_resolver.py",
+  "$_mojom_library_root/generate/generator.py",
+  "$_mojom_library_root/generate/module.py",
+  "$_mojom_library_root/generate/pack.py",
+  "$_mojom_library_root/generate/template_expander.py",
+  "$_mojom_library_root/generate/translate.py",
+  "$_mojom_library_root/parse/__init__.py",
+  "$_mojom_library_root/parse/ast.py",
+  "$_mojom_library_root/parse/lexer.py",
+  "$_mojom_library_root/parse/parser.py",
   "$mojom_generator_root/generators/mojom_cpp_generator.py",
   "$mojom_generator_root/generators/mojom_java_generator.py",
   "$mojom_generator_root/generators/mojom_js_generator.py",
   "$mojom_generator_root/generators/mojom_ts_generator.py",
-  "$mojom_generator_root/pylib/mojom/__init__.py",
-  "$mojom_generator_root/pylib/mojom/error.py",
-  "$mojom_generator_root/pylib/mojom/generate/__init__.py",
-  "$mojom_generator_root/pylib/mojom/generate/constant_resolver.py",
-  "$mojom_generator_root/pylib/mojom/generate/generator.py",
-  "$mojom_generator_root/pylib/mojom/generate/module.py",
-  "$mojom_generator_root/pylib/mojom/generate/pack.py",
-  "$mojom_generator_root/pylib/mojom/generate/template_expander.py",
-  "$mojom_generator_root/pylib/mojom/generate/translate.py",
-  "$mojom_generator_root/pylib/mojom/parse/__init__.py",
-  "$mojom_generator_root/pylib/mojom/parse/ast.py",
-  "$mojom_generator_root/pylib/mojom/parse/lexer.py",
-  "$mojom_generator_root/pylib/mojom/parse/parser.py",
   "$mojom_generator_script",
 ]
 
@@ -1604,7 +1605,7 @@
 
     generator_js_target_name = "${target_name}_js__generator"
     action(generator_js_target_name) {
-      script = compile_typescript_script
+      script = "$mojom_generator_root/compile_typescript.py"
       sources = ts_outputs
       outputs = js_outputs
       public_deps = [ ":$generator_ts_target_name" ]
diff --git a/mojo/public/tools/bindings/mojom_bindings_generator.py b/mojo/public/tools/bindings/mojom_bindings_generator.py
index 8cca6411..b53b372 100755
--- a/mojo/public/tools/bindings/mojom_bindings_generator.py
+++ b/mojo/public/tools/bindings/mojom_bindings_generator.py
@@ -36,13 +36,11 @@
     if tail == dirname:
       return path
 
-# Manually check for the command-line flag. (This isn't quite right, since it
-# ignores, e.g., "--", but it's close enough.)
-if "--use_bundled_pylibs" in sys.argv[1:]:
-  sys.path.insert(0, os.path.join(_GetDirAbove("mojo"), "third_party"))
 
-sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)),
-                                "pylib"))
+sys.path.insert(
+    0,
+    os.path.join(
+        os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "mojom"))
 
 from mojom.error import Error
 import mojom.fileutil as fileutil
diff --git a/mojo/public/tools/bindings/pylib/mojom/fileutil.py b/mojo/public/tools/bindings/pylib/mojom/fileutil.py
deleted file mode 100644
index 702c1d3..0000000
--- a/mojo/public/tools/bindings/pylib/mojom/fileutil.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# Copyright 2015 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import errno
-import os.path
-
-
-def EnsureDirectoryExists(path, always_try_to_create=False):
-  """A wrapper for os.makedirs that does not error if the directory already
-  exists. A different process could be racing to create this directory."""
-
-  if not os.path.exists(path) or always_try_to_create:
-    try:
-      os.makedirs(path)
-    except OSError as e:
-      # There may have been a race to create this directory.
-      if e.errno != errno.EEXIST:
-        raise
diff --git a/mojo/public/tools/bindings/pylib/mojom/generate/run_tests.py b/mojo/public/tools/bindings/pylib/mojom/generate/run_tests.py
deleted file mode 100755
index a40799b9..0000000
--- a/mojo/public/tools/bindings/pylib/mojom/generate/run_tests.py
+++ /dev/null
@@ -1,37 +0,0 @@
-#!/usr/bin/env python
-# Copyright 2013 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.
-
-""" Test runner for Mojom """
-
-from __future__ import print_function
-
-import subprocess
-import sys
-
-def TestMojom(testname, args):
-  print('\nRunning unit tests for %s.' % testname)
-  try:
-    args = [sys.executable, testname] + args
-    subprocess.check_call(args, stdout=sys.stdout)
-    print('Succeeded')
-    return 0
-  except subprocess.CalledProcessError as err:
-    print('Failed with %s.' % str(err))
-    return 1
-
-
-def main(args):
-  errors = 0
-  errors += TestMojom('data_tests.py', ['--test'])
-  errors += TestMojom('module_tests.py', ['--test'])
-  errors += TestMojom('pack_tests.py', ['--test'])
-
-  if errors:
-    print('\nFailed tests.')
-  return min(errors, 127)  # Make sure the return value doesn't "wrap".
-
-
-if __name__ == '__main__':
-  sys.exit(main(sys.argv[1:]))
diff --git a/mojo/public/tools/bindings/pylib/mojom/generate/test_support.py b/mojo/public/tools/bindings/pylib/mojom/generate/test_support.py
deleted file mode 100644
index b9f9e06..0000000
--- a/mojo/public/tools/bindings/pylib/mojom/generate/test_support.py
+++ /dev/null
@@ -1,195 +0,0 @@
-# Copyright 2013 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.
-
-from __future__ import print_function
-
-import sys
-import traceback
-
-import module as mojom
-
-# Support for writing mojom test cases.
-# RunTest(fn) will execute fn, catching any exceptions. fn should return
-# the number of errors that are encountered.
-#
-# EXPECT_EQ(a, b) and EXPECT_TRUE(b) will print error information if the
-# expectations are not true and return a non zero value. This allows test cases
-# to be written like this
-#
-# def Foo():
-#   errors = 0
-#   errors += EXPECT_EQ('test', test())
-#   ...
-#   return errors
-#
-# RunTest(foo)
-
-def FieldsAreEqual(field1, field2):
-  if field1 == field2:
-    return True
-  return field1.name == field2.name and \
-      KindsAreEqual(field1.kind, field2.kind) and \
-      field1.ordinal == field2.ordinal and \
-      field1.default == field2.default
-
-
-def KindsAreEqual(kind1, kind2):
-  if kind1 == kind2:
-    return True
-  if kind1.__class__ != kind2.__class__ or kind1.spec != kind2.spec:
-    return False
-  if kind1.__class__ == mojom.Kind:
-    return kind1.spec == kind2.spec
-  if kind1.__class__ == mojom.Struct:
-    if kind1.name != kind2.name or \
-        kind1.spec != kind2.spec or \
-        len(kind1.fields) != len(kind2.fields):
-      return False
-    for i in range(len(kind1.fields)):
-      if not FieldsAreEqual(kind1.fields[i], kind2.fields[i]):
-        return False
-    return True
-  if kind1.__class__ == mojom.Array:
-    return KindsAreEqual(kind1.kind, kind2.kind)
-  print('Unknown Kind class: ', kind1.__class__.__name__)
-  return False
-
-
-def ParametersAreEqual(parameter1, parameter2):
-  if parameter1 == parameter2:
-    return True
-  return parameter1.name == parameter2.name and \
-     parameter1.ordinal == parameter2.ordinal and \
-     parameter1.default == parameter2.default and \
-     KindsAreEqual(parameter1.kind, parameter2.kind)
-
-
-def MethodsAreEqual(method1, method2):
-  if method1 == method2:
-    return True
-  if method1.name != method2.name or \
-      method1.ordinal != method2.ordinal or \
-      len(method1.parameters) != len(method2.parameters):
-    return False
-  for i in range(len(method1.parameters)):
-    if not ParametersAreEqual(method1.parameters[i], method2.parameters[i]):
-      return False
-  return True
-
-
-def InterfacesAreEqual(interface1, interface2):
-  if interface1 == interface2:
-    return True
-  if interface1.name != interface2.name or \
-      len(interface1.methods) != len(interface2.methods):
-    return False
-  for i in range(len(interface1.methods)):
-    if not MethodsAreEqual(interface1.methods[i], interface2.methods[i]):
-      return False
-  return True
-
-
-def ModulesAreEqual(module1, module2):
-  if module1 == module2:
-    return True
-  if module1.name != module2.name or \
-      module1.namespace != module2.namespace or \
-      len(module1.structs) != len(module2.structs) or \
-      len(module1.interfaces) != len(module2.interfaces):
-    return False
-  for i in range(len(module1.structs)):
-    if not KindsAreEqual(module1.structs[i], module2.structs[i]):
-      return False
-  for i in range(len(module1.interfaces)):
-    if not InterfacesAreEqual(module1.interfaces[i], module2.interfaces[i]):
-      return False
-  return True
-
-
-# Builds and returns a Module suitable for testing/
-def BuildTestModule():
-  module = mojom.Module('test', 'testspace')
-  struct = module.AddStruct('teststruct')
-  struct.AddField('testfield1', mojom.INT32)
-  struct.AddField('testfield2', mojom.Array(mojom.INT32), 42)
-
-  interface = module.AddInterface('Server')
-  method = interface.AddMethod('Foo', 42)
-  method.AddParameter('foo', mojom.INT32)
-  method.AddParameter('bar', mojom.Array(struct))
-
-  return module
-
-
-# Tests if |module| is as built by BuildTestModule(). Returns the number of
-# errors
-def TestTestModule(module):
-  errors = 0
-
-  errors += EXPECT_EQ('test', module.name)
-  errors += EXPECT_EQ('testspace', module.namespace)
-  errors += EXPECT_EQ(1, len(module.structs))
-  errors += EXPECT_EQ('teststruct', module.structs[0].name)
-  errors += EXPECT_EQ(2, len(module.structs[0].fields))
-  errors += EXPECT_EQ('testfield1', module.structs[0].fields[0].name)
-  errors += EXPECT_EQ(mojom.INT32, module.structs[0].fields[0].kind)
-  errors += EXPECT_EQ('testfield2', module.structs[0].fields[1].name)
-  errors += EXPECT_EQ(mojom.Array, module.structs[0].fields[1].kind.__class__)
-  errors += EXPECT_EQ(mojom.INT32, module.structs[0].fields[1].kind.kind)
-
-  errors += EXPECT_EQ(1, len(module.interfaces))
-  errors += EXPECT_EQ('Server', module.interfaces[0].name)
-  errors += EXPECT_EQ(1, len(module.interfaces[0].methods))
-  errors += EXPECT_EQ('Foo', module.interfaces[0].methods[0].name)
-  errors += EXPECT_EQ(2, len(module.interfaces[0].methods[0].parameters))
-  errors += EXPECT_EQ('foo', module.interfaces[0].methods[0].parameters[0].name)
-  errors += EXPECT_EQ(mojom.INT32,
-                      module.interfaces[0].methods[0].parameters[0].kind)
-  errors += EXPECT_EQ('bar', module.interfaces[0].methods[0].parameters[1].name)
-  errors += EXPECT_EQ(
-    mojom.Array,
-    module.interfaces[0].methods[0].parameters[1].kind.__class__)
-  errors += EXPECT_EQ(
-    module.structs[0],
-    module.interfaces[0].methods[0].parameters[1].kind.kind)
-  return errors
-
-
-def PrintFailure(string):
-  stack = traceback.extract_stack()
-  frame = stack[len(stack)-3]
-  sys.stderr.write("ERROR at %s:%d, %s\n" % (frame[0], frame[1], string))
-  print("Traceback:")
-  for line in traceback.format_list(stack[:len(stack)-2]):
-    sys.stderr.write(line)
-
-
-def EXPECT_EQ(a, b):
-  if a != b:
-    PrintFailure("%s != %s" % (a, b))
-    return 1
-  return 0
-
-
-def EXPECT_TRUE(a):
-  if not a:
-    PrintFailure('Expecting True')
-    return 1
-  return 0
-
-
-def RunTest(fn):
-  sys.stdout.write('Running %s...' % fn.__name__)
-  try:
-    errors = fn()
-  except:
-    traceback.print_exc(sys.stderr)
-    errors = 1
-  if errors == 0:
-    sys.stdout.write('OK\n')
-  elif errors == 1:
-    sys.stdout.write('1 ERROR\n')
-  else:
-    sys.stdout.write('%d ERRORS\n' % errors)
-  return errors
diff --git a/mojo/public/tools/mojom/README.md b/mojo/public/tools/mojom/README.md
new file mode 100644
index 0000000..6a4ff78
--- /dev/null
+++ b/mojo/public/tools/mojom/README.md
@@ -0,0 +1,14 @@
+# The Mojom Parser
+
+The Mojom format is an interface definition language (IDL) for describing
+interprocess communication (IPC) messages and data types for use with the
+low-level cross-platform
+[Mojo IPC library](https://chromium.googlesource.com/chromium/src/+/master/mojo/public/c/system/README.md).
+
+This directory consists of a `mojom` Python module, its tests, and supporting
+command-line tools. The Python module implements the parser used by the
+command-line tools and exposes an API to help external bindings generators emit
+useful code from the parser's outputs.
+
+TODO(https://crbug.com/1060464): Fill out this documentation once the library
+and tools have stabilized.
diff --git a/mojo/public/tools/bindings/pylib/mojom/__init__.py b/mojo/public/tools/mojom/mojom/__init__.py
similarity index 100%
rename from mojo/public/tools/bindings/pylib/mojom/__init__.py
rename to mojo/public/tools/mojom/mojom/__init__.py
diff --git a/mojo/public/tools/bindings/pylib/mojom/error.py b/mojo/public/tools/mojom/mojom/error.py
similarity index 100%
rename from mojo/public/tools/bindings/pylib/mojom/error.py
rename to mojo/public/tools/mojom/mojom/error.py
diff --git a/mojo/public/tools/mojom/mojom/fileutil.py b/mojo/public/tools/mojom/mojom/fileutil.py
new file mode 100644
index 0000000..73d6bf3
--- /dev/null
+++ b/mojo/public/tools/mojom/mojom/fileutil.py
@@ -0,0 +1,44 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import errno
+import imp
+import os.path
+import sys
+
+
+def _GetDirAbove(dirname):
+  """Returns the directory "above" this file containing |dirname| (which must
+  also be "above" this file)."""
+  path = os.path.abspath(__file__)
+  while True:
+    path, tail = os.path.split(path)
+    assert tail
+    if tail == dirname:
+      return path
+
+
+def EnsureDirectoryExists(path, always_try_to_create=False):
+  """A wrapper for os.makedirs that does not error if the directory already
+  exists. A different process could be racing to create this directory."""
+
+  if not os.path.exists(path) or always_try_to_create:
+    try:
+      os.makedirs(path)
+    except OSError as e:
+      # There may have been a race to create this directory.
+      if e.errno != errno.EEXIST:
+        raise
+
+
+def EnsureModuleAvailable(module_name):
+  """Helper function which attempts to find the Python module named
+  |module_name| using the usual module search. If that fails, this assumes it's
+  being called within the Chromium tree, or an equivalent tree where this
+  library lives somewhere under a "mojo" directory which has a "third_party"
+  sibling."""
+  try:
+    imp.find_module(module_name)
+  except ImportError:
+    sys.path.append(os.path.join(_GetDirAbove("mojo"), "third_party"))
diff --git a/mojo/public/tools/bindings/pylib/mojom/generate/__init__.py b/mojo/public/tools/mojom/mojom/generate/__init__.py
similarity index 100%
rename from mojo/public/tools/bindings/pylib/mojom/generate/__init__.py
rename to mojo/public/tools/mojom/mojom/generate/__init__.py
diff --git a/mojo/public/tools/bindings/pylib/mojom/generate/constant_resolver.py b/mojo/public/tools/mojom/mojom/generate/constant_resolver.py
similarity index 98%
rename from mojo/public/tools/bindings/pylib/mojom/generate/constant_resolver.py
rename to mojo/public/tools/mojom/mojom/generate/constant_resolver.py
index 54b0ce375..0dfd996 100644
--- a/mojo/public/tools/bindings/pylib/mojom/generate/constant_resolver.py
+++ b/mojo/public/tools/mojom/mojom/generate/constant_resolver.py
@@ -4,7 +4,8 @@
 """Resolves the values used for constants and enums."""
 
 from itertools import ifilter
-import mojom.generate.module as mojom
+
+from mojom.generate import module as mojom
 
 
 def ResolveConstants(module, expression_to_text):
diff --git a/mojo/public/tools/bindings/pylib/mojom/generate/generator.py b/mojo/public/tools/mojom/mojom/generate/generator.py
similarity index 98%
rename from mojo/public/tools/bindings/pylib/mojom/generate/generator.py
rename to mojo/public/tools/mojom/mojom/generate/generator.py
index a74215d..e7a87b3e 100644
--- a/mojo/public/tools/bindings/pylib/mojom/generate/generator.py
+++ b/mojo/public/tools/mojom/mojom/generate/generator.py
@@ -9,9 +9,9 @@
 import os.path
 import re
 
-import mojom.generate.module as mojom
-import mojom.fileutil as fileutil
-import mojom.generate.pack as pack
+from mojom import fileutil
+from mojom.generate import module as mojom
+from mojom.generate import pack
 
 
 def ExpectedArraySize(kind):
@@ -114,7 +114,7 @@
 
   # Dump the data to disk.
   with open(full_path, "wb") as f:
-    if type(contents) != bytes:
+    if not isinstance(contents, bytes):
       f.write(contents.encode('utf-8'))
     else:
       f.write(contents)
diff --git a/mojo/public/tools/bindings/pylib/mojom/generate/module.py b/mojo/public/tools/mojom/mojom/generate/module.py
similarity index 100%
rename from mojo/public/tools/bindings/pylib/mojom/generate/module.py
rename to mojo/public/tools/mojom/mojom/generate/module.py
diff --git a/mojo/public/tools/bindings/pylib/mojom/generate/module_tests.py b/mojo/public/tools/mojom/mojom/generate/module_tests.py
similarity index 100%
rename from mojo/public/tools/bindings/pylib/mojom/generate/module_tests.py
rename to mojo/public/tools/mojom/mojom/generate/module_tests.py
diff --git a/mojo/public/tools/bindings/pylib/mojom/generate/pack.py b/mojo/public/tools/mojom/mojom/generate/pack.py
similarity index 95%
rename from mojo/public/tools/bindings/pylib/mojom/generate/pack.py
rename to mojo/public/tools/mojom/mojom/generate/pack.py
index c4e5e30a..88b77c98 100644
--- a/mojo/public/tools/bindings/pylib/mojom/generate/pack.py
+++ b/mojo/public/tools/mojom/mojom/generate/pack.py
@@ -2,7 +2,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import mojom.generate.module as mojom
+from mojom.generate import module as mojom
 
 # This module provides a mechanism for determining the packed order and offsets
 # of a mojom.Struct.
@@ -195,23 +195,23 @@
 def GetByteLayout(packed_struct):
   total_payload_size = GetPayloadSizeUpToField(
       packed_struct.packed_fields[-1] if packed_struct.packed_fields else None)
-  bytes = [ByteInfo() for i in range(total_payload_size)]
+  byte_info = [ByteInfo() for i in range(total_payload_size)]
 
   limit_of_previous_field = 0
   for packed_field in packed_struct.packed_fields:
     for i in range(limit_of_previous_field, packed_field.offset):
-      bytes[i].is_padding = True
-    bytes[packed_field.offset].packed_fields.append(packed_field)
+      byte_info[i].is_padding = True
+    byte_info[packed_field.offset].packed_fields.append(packed_field)
     limit_of_previous_field = packed_field.offset + packed_field.size
 
-  for i in range(limit_of_previous_field, len(bytes)):
-    bytes[i].is_padding = True
+  for i in range(limit_of_previous_field, len(byte_info)):
+    byte_info[i].is_padding = True
 
-  for byte in bytes:
+  for byte in byte_info:
     # A given byte cannot both be padding and have a fields packed into it.
     assert not (byte.is_padding and byte.packed_fields)
 
-  return bytes
+  return byte_info
 
 
 class VersionInfo(object):
diff --git a/mojo/public/tools/bindings/pylib/mojom/generate/pack_tests.py b/mojo/public/tools/mojom/mojom/generate/pack_tests.py
similarity index 100%
rename from mojo/public/tools/bindings/pylib/mojom/generate/pack_tests.py
rename to mojo/public/tools/mojom/mojom/generate/pack_tests.py
diff --git a/mojo/public/tools/bindings/pylib/mojom/generate/template_expander.py b/mojo/public/tools/mojom/mojom/generate/template_expander.py
similarity index 90%
rename from mojo/public/tools/bindings/pylib/mojom/generate/template_expander.py
rename to mojo/public/tools/mojom/mojom/generate/template_expander.py
index 43653dc..1fa8ed05 100644
--- a/mojo/public/tools/bindings/pylib/mojom/generate/template_expander.py
+++ b/mojo/public/tools/mojom/mojom/generate/template_expander.py
@@ -7,11 +7,9 @@
 import os.path
 import sys
 
-_current_dir = os.path.dirname(os.path.realpath(__file__))
-# jinja2 is in chromium's third_party directory
-# Insert at front to override system libraries, and after path[0] == script dir
-sys.path.insert(
-    1, os.path.join(_current_dir, *([os.pardir] * 7 + ['third_party'])))
+from mojom import fileutil
+
+fileutil.EnsureModuleAvailable("jinja2")
 import jinja2
 
 
diff --git a/mojo/public/tools/bindings/pylib/mojom/generate/translate.py b/mojo/public/tools/mojom/mojom/generate/translate.py
similarity index 99%
rename from mojo/public/tools/bindings/pylib/mojom/generate/translate.py
rename to mojo/public/tools/mojom/mojom/generate/translate.py
index 325a14a..c50f1dd 100644
--- a/mojo/public/tools/bindings/pylib/mojom/generate/translate.py
+++ b/mojo/public/tools/mojom/mojom/generate/translate.py
@@ -13,7 +13,7 @@
 import os
 import re
 
-import mojom.generate.module as mojom
+from mojom.generate import module as mojom
 from mojom.parse import ast
 
 
@@ -507,11 +507,11 @@
       prev_value += 1
 
     # Integral value (e.g: BEGIN = -0x1).
-    elif type(field.value) is str:
+    elif isinstance(field.value, str):
       prev_value = int(field.value, 0)
 
     # Reference to a previous enum value (e.g: INIT = BEGIN).
-    elif type(field.value) is mojom.EnumValue:
+    elif isinstance(field.value, mojom.EnumValue):
       prev_value = resolved_enum_values[field.value.mojom_name]
     else:
       raise Exception("Unresolved enum value.")
diff --git a/mojo/public/tools/bindings/pylib/mojom/parse/__init__.py b/mojo/public/tools/mojom/mojom/parse/__init__.py
similarity index 100%
rename from mojo/public/tools/bindings/pylib/mojom/parse/__init__.py
rename to mojo/public/tools/mojom/mojom/parse/__init__.py
diff --git a/mojo/public/tools/bindings/pylib/mojom/parse/ast.py b/mojo/public/tools/mojom/mojom/parse/ast.py
similarity index 99%
rename from mojo/public/tools/bindings/pylib/mojom/parse/ast.py
rename to mojo/public/tools/mojom/mojom/parse/ast.py
index ff66bacb..02f624e 100644
--- a/mojo/public/tools/bindings/pylib/mojom/parse/ast.py
+++ b/mojo/public/tools/mojom/mojom/parse/ast.py
@@ -17,7 +17,7 @@
     self.lineno = lineno
 
   def __eq__(self, other):
-    return isinstance(self, other)
+    return type(self) == type(other)
 
   # Make != the inverse of ==. (Subclasses shouldn't have to override this.)
   def __ne__(self, other):
diff --git a/mojo/public/tools/bindings/pylib/mojom/parse/conditional_features.py b/mojo/public/tools/mojom/mojom/parse/conditional_features.py
similarity index 97%
rename from mojo/public/tools/bindings/pylib/mojom/parse/conditional_features.py
rename to mojo/public/tools/mojom/mojom/parse/conditional_features.py
index 550843d..3cb73c5 100644
--- a/mojo/public/tools/bindings/pylib/mojom/parse/conditional_features.py
+++ b/mojo/public/tools/mojom/mojom/parse/conditional_features.py
@@ -3,8 +3,8 @@
 # found in the LICENSE file.
 """Helpers for processing conditionally enabled features in a mojom."""
 
-from . import ast
-from ..error import Error
+from mojom.error import Error
+from mojom.parse import ast
 
 
 class EnableIfError(Error):
diff --git a/mojo/public/tools/bindings/pylib/mojom/parse/lexer.py b/mojo/public/tools/mojom/mojom/parse/lexer.py
similarity index 92%
rename from mojo/public/tools/bindings/pylib/mojom/parse/lexer.py
rename to mojo/public/tools/mojom/mojom/parse/lexer.py
index b219b5b..15c02fe4 100644
--- a/mojo/public/tools/bindings/pylib/mojom/parse/lexer.py
+++ b/mojo/public/tools/mojom/mojom/parse/lexer.py
@@ -6,26 +6,12 @@
 import os.path
 import sys
 
+from mojom import fileutil
+from mojom.error import Error
 
-def _GetDirAbove(dirname):
-  """Returns the directory "above" this file containing |dirname| (which must
-  also be "above" this file)."""
-  path = os.path.abspath(__file__)
-  while True:
-    path, tail = os.path.split(path)
-    assert tail
-    if tail == dirname:
-      return path
-
-
-try:
-  imp.find_module("ply")
-except ImportError:
-  sys.path.append(os.path.join(_GetDirAbove("mojo"), "third_party"))
+fileutil.EnsureModuleAvailable("ply")
 from ply.lex import TOKEN
 
-from ..error import Error
-
 
 class LexError(Error):
   """Class for errors from the lexer."""
diff --git a/mojo/public/tools/bindings/pylib/mojom/parse/parser.py b/mojo/public/tools/mojom/mojom/parse/parser.py
similarity index 98%
rename from mojo/public/tools/bindings/pylib/mojom/parse/parser.py
rename to mojo/public/tools/mojom/mojom/parse/parser.py
index fcc4c52a..18c500e6 100644
--- a/mojo/public/tools/bindings/pylib/mojom/parse/parser.py
+++ b/mojo/public/tools/mojom/mojom/parse/parser.py
@@ -6,16 +6,15 @@
 import os.path
 import sys
 
-_current_dir = os.path.dirname(os.path.realpath(__file__))
-sys.path.insert(
-    1, os.path.join(_current_dir, *([os.pardir] * 7 + ['third_party'])))
+from mojom import fileutil
+from mojom.error import Error
+from mojom.parse import ast
+from mojom.parse.lexer import Lexer
+
+fileutil.EnsureModuleAvailable("ply")
 from ply import lex
 from ply import yacc
 
-from ..error import Error
-from . import ast
-from .lexer import Lexer
-
 _MAX_ORDINAL_VALUE = 0xffffffff
 _MAX_ARRAY_SIZE = 0xffffffff
 
diff --git a/net/dns/dns_client.cc b/net/dns/dns_client.cc
index 8f39223..d5b0aff9 100644
--- a/net/dns/dns_client.cc
+++ b/net/dns/dns_client.cc
@@ -43,10 +43,11 @@
 }
 
 void UpdateConfigForDohUpgrade(DnsConfig* config) {
-  // TODO(crbug.com/878582): Reconsider whether the hardcoded mapping should
-  // also be applied in SECURE mode.
   bool has_doh_servers = !config->dns_over_https_servers.empty();
-  if (config->allow_dns_over_https_upgrade && !has_doh_servers &&
+  // Do not attempt upgrade when there are already DoH servers specified or
+  // when there are aspects of the system DNS config that are unhandled.
+  if (!config->unhandled_options && config->allow_dns_over_https_upgrade &&
+      !has_doh_servers &&
       config->secure_dns_mode == DnsConfig::SecureDnsMode::AUTOMATIC) {
     // If we're in strict mode on Android, only attempt to upgrade the
     // specified DoT hostname.
@@ -76,6 +77,8 @@
   } else {
     UMA_HISTOGRAM_BOOLEAN("Net.DNS.UpgradeConfig.Ineligible.DohSpecified",
                           has_doh_servers);
+    UMA_HISTOGRAM_BOOLEAN("Net.DNS.UpgradeConfig.Ineligible.UnhandledOptions",
+                          config->unhandled_options);
   }
 }
 
@@ -96,8 +99,9 @@
   }
 
   bool CanUseInsecureDnsTransactions() const override {
-    return session_ != nullptr && insecure_enabled_ &&
-           !GetEffectiveConfig()->dns_over_tls_active;
+    const DnsConfig* config = GetEffectiveConfig();
+    return config && config->nameservers.size() > 0 && insecure_enabled_ &&
+           !config->unhandled_options && !config->dns_over_tls_active;
   }
 
   void SetInsecureEnabled(bool enabled) override {
@@ -195,7 +199,15 @@
 
     UpdateConfigForDohUpgrade(&config);
 
-    if (!config.IsValid() || config.unhandled_options)
+    // TODO(ericorth): Consider keeping a separate DnsConfig for pure Chrome-
+    // produced configs to allow respecting all fields like |unhandled_options|
+    // while still being able to fallback to system config for DoH.
+    // For now, clear the nameservers for extra security if parts of the system
+    // config are unhandled.
+    if (config.unhandled_options)
+      config.nameservers.clear();
+
+    if (!config.IsValid())
       return base::nullopt;
 
     return config;
@@ -225,7 +237,6 @@
 
     if (new_effective_config) {
       DCHECK(new_effective_config.value().IsValid());
-      DCHECK(!new_effective_config.value().unhandled_options);
 
       std::unique_ptr<DnsSocketPool> socket_pool(
           new_effective_config.value().randomize_ports
diff --git a/net/dns/dns_client_unittest.cc b/net/dns/dns_client_unittest.cc
index a2531ff2..e6519ee 100644
--- a/net/dns/dns_client_unittest.cc
+++ b/net/dns/dns_client_unittest.cc
@@ -55,8 +55,11 @@
     return config;
   }
 
-  DnsConfig ValidConfigWithDoh() {
-    DnsConfig config = BasicValidConfig();
+  DnsConfig ValidConfigWithDoh(bool doh_only) {
+    DnsConfig config;
+    if (!doh_only) {
+      config = BasicValidConfig();
+    }
     config.dns_over_https_servers = {DnsConfig::DnsOverHttpsServerConfig(
         "www.doh.com", true /* use_post */)};
     return config;
@@ -125,7 +128,7 @@
 
 TEST_F(DnsClientTest, InsecureNotEnabled) {
   client_->SetInsecureEnabled(false);
-  client_->SetSystemConfig(ValidConfigWithDoh());
+  client_->SetSystemConfig(ValidConfigWithDoh(false /* doh_only */));
 
   EXPECT_TRUE(client_->CanUseSecureDnsTransactions());
   EXPECT_TRUE(
@@ -134,14 +137,35 @@
   EXPECT_TRUE(client_->FallbackFromInsecureTransactionPreferred());
 
   EXPECT_THAT(client_->GetEffectiveConfig(),
-              testing::Pointee(ValidConfigWithDoh()));
+              testing::Pointee(ValidConfigWithDoh(false /* doh_only */)));
   EXPECT_TRUE(client_->GetHosts());
   EXPECT_TRUE(client_->GetTransactionFactory());
-  EXPECT_EQ(client_->GetCurrentSession()->config(), ValidConfigWithDoh());
+  EXPECT_EQ(client_->GetCurrentSession()->config(),
+            ValidConfigWithDoh(false /* doh_only */));
+}
+
+TEST_F(DnsClientTest, UnhandledOptions) {
+  client_->SetInsecureEnabled(true);
+  DnsConfig config = ValidConfigWithDoh(false /* doh_only */);
+  config.unhandled_options = true;
+  client_->SetSystemConfig(config);
+
+  EXPECT_TRUE(client_->CanUseSecureDnsTransactions());
+  EXPECT_TRUE(
+      client_->FallbackFromSecureTransactionPreferred(&resolve_context_));
+  EXPECT_FALSE(client_->CanUseInsecureDnsTransactions());
+  EXPECT_TRUE(client_->FallbackFromInsecureTransactionPreferred());
+
+  DnsConfig expected_config = config;
+  expected_config.nameservers.clear();
+  EXPECT_THAT(client_->GetEffectiveConfig(), testing::Pointee(expected_config));
+  EXPECT_TRUE(client_->GetHosts());
+  EXPECT_TRUE(client_->GetTransactionFactory());
+  EXPECT_EQ(client_->GetCurrentSession()->config(), expected_config);
 }
 
 TEST_F(DnsClientTest, CanUseSecureDnsTransactions_ProbeSuccess) {
-  client_->SetSystemConfig(ValidConfigWithDoh());
+  client_->SetSystemConfig(ValidConfigWithDoh(true /* doh_only */));
   resolve_context_.InvalidateCaches(client_->GetCurrentSession());
 
   EXPECT_TRUE(client_->CanUseSecureDnsTransactions());
@@ -158,7 +182,7 @@
 
 TEST_F(DnsClientTest, DnsOverTlsActive) {
   client_->SetInsecureEnabled(true);
-  DnsConfig config = ValidConfigWithDoh();
+  DnsConfig config = ValidConfigWithDoh(false /* doh_only */);
   config.dns_over_tls_active = true;
   client_->SetSystemConfig(config);
 
@@ -176,7 +200,7 @@
 
 TEST_F(DnsClientTest, AllAllowed) {
   client_->SetInsecureEnabled(true);
-  client_->SetSystemConfig(ValidConfigWithDoh());
+  client_->SetSystemConfig(ValidConfigWithDoh(false /* doh_only */));
   resolve_context_.InvalidateCaches(client_->GetCurrentSession());
   resolve_context_.RecordServerSuccess(0u /* server_index */,
                                        true /* is_doh_server */,
@@ -189,15 +213,16 @@
   EXPECT_FALSE(client_->FallbackFromInsecureTransactionPreferred());
 
   EXPECT_THAT(client_->GetEffectiveConfig(),
-              testing::Pointee(ValidConfigWithDoh()));
+              testing::Pointee(ValidConfigWithDoh(false /* doh_only */)));
   EXPECT_TRUE(client_->GetHosts());
   EXPECT_TRUE(client_->GetTransactionFactory());
-  EXPECT_EQ(client_->GetCurrentSession()->config(), ValidConfigWithDoh());
+  EXPECT_EQ(client_->GetCurrentSession()->config(),
+            ValidConfigWithDoh(false /* doh_only */));
 }
 
 TEST_F(DnsClientTest, FallbackFromInsecureTransactionPreferred_Failures) {
   client_->SetInsecureEnabled(true);
-  client_->SetSystemConfig(ValidConfigWithDoh());
+  client_->SetSystemConfig(ValidConfigWithDoh(false /* doh_only */));
 
   for (int i = 0; i < DnsClient::kMaxInsecureFallbackFailures; ++i) {
     EXPECT_TRUE(client_->CanUseSecureDnsTransactions());
diff --git a/net/dns/dns_config.h b/net/dns/dns_config.h
index e1b01ae1..367ffc1 100644
--- a/net/dns/dns_config.h
+++ b/net/dns/dns_config.h
@@ -46,7 +46,9 @@
   // Value only contains the number of hosts rather than the full list.
   std::unique_ptr<base::Value> ToValue() const;
 
-  bool IsValid() const { return !nameservers.empty(); }
+  bool IsValid() const {
+    return !nameservers.empty() || !dns_over_https_servers.empty();
+  }
 
   struct NET_EXPORT DnsOverHttpsServerConfig {
     DnsOverHttpsServerConfig(const std::string& server_template, bool use_post);
diff --git a/net/dns/host_resolver_manager_unittest.cc b/net/dns/host_resolver_manager_unittest.cc
index 5e539574..1e34ddc 100644
--- a/net/dns/host_resolver_manager_unittest.cc
+++ b/net/dns/host_resolver_manager_unittest.cc
@@ -7425,6 +7425,26 @@
   EXPECT_EQ(expected_doh_servers, fetched_config->dns_over_https_servers);
 }
 
+TEST_F(HostResolverManagerDnsTest,
+       DohMappingUnhandledOptionsIneligibleForUpgrade) {
+  // Use a real DnsClient to test config-handling behavior.
+  AlwaysFailSocketFactory socket_factory;
+  auto client =
+      DnsClient::CreateClientForTesting(nullptr /* net_log */, &socket_factory,
+                                        base::BindRepeating(&base::RandInt));
+  DnsClient* client_ptr = client.get();
+  resolver_->SetDnsClientForTesting(std::move(client));
+
+  // Create a DnsConfig containing IP addresses associated with Cloudflare,
+  // SafeBrowsing family filter, SafeBrowsing security filter, and other IPs
+  // not associated with hardcoded DoH services.
+  DnsConfig original_config = CreateUpgradableDnsConfig();
+  original_config.unhandled_options = true;
+  ChangeDnsConfig(original_config);
+
+  EXPECT_FALSE(client_ptr->GetEffectiveConfig());
+}
+
 TEST_F(HostResolverManagerDnsTest, DohMappingWithExclusion) {
   // Use a real DnsClient to test config-handling behavior.
   AlwaysFailSocketFactory socket_factory;
@@ -7480,6 +7500,38 @@
             fetched_config->dns_over_https_servers);
 }
 
+TEST_F(HostResolverManagerDnsTest,
+       DohMappingUnhandledOptionsAndTemplateSpecified) {
+  // Use a real DnsClient to test config-handling behavior.
+  AlwaysFailSocketFactory socket_factory;
+  auto client =
+      DnsClient::CreateClientForTesting(nullptr /* net_log */, &socket_factory,
+                                        base::BindRepeating(&base::RandInt));
+  DnsClient* client_ptr = client.get();
+  resolver_->SetDnsClientForTesting(std::move(client));
+
+  // Create a DnsConfig containing IP addresses associated with Cloudflare,
+  // SafeBrowsing family filter, SafeBrowsing security filter, and other IPs
+  // not associated with hardcoded DoH services.
+  DnsConfig original_config = CreateUpgradableDnsConfig();
+  original_config.unhandled_options = true;
+  ChangeDnsConfig(original_config);
+
+  // If the overrides contains DoH servers, no DoH upgrade should be attempted.
+  DnsConfigOverrides overrides;
+  const std::vector<DnsConfig::DnsOverHttpsServerConfig>
+      dns_over_https_servers_overrides = {
+          DnsConfig::DnsOverHttpsServerConfig("doh.server.override.com", true)};
+  overrides.dns_over_https_servers = dns_over_https_servers_overrides;
+  resolver_->SetDnsConfigOverrides(overrides);
+  const DnsConfig* fetched_config = client_ptr->GetEffectiveConfig();
+  EXPECT_TRUE(fetched_config->nameservers.empty());
+  EXPECT_FALSE(client_ptr->CanUseInsecureDnsTransactions());
+  EXPECT_EQ(dns_over_https_servers_overrides,
+            fetched_config->dns_over_https_servers);
+  EXPECT_TRUE(client_ptr->CanUseSecureDnsTransactions());
+}
+
 TEST_F(HostResolverManagerDnsTest, DohMappingWithAutomaticDot) {
   // Use a real DnsClient to test config-handling behavior.
   AlwaysFailSocketFactory socket_factory;
diff --git a/net/http/transport_security_state_static.json b/net/http/transport_security_state_static.json
index 48d52d7..bd1497b5 100644
--- a/net/http/transport_security_state_static.json
+++ b/net/http/transport_security_state_static.json
@@ -1317,7 +1317,6 @@
     { "name": "reserve-online.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "riesenmagnete.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "rosenkeller.org", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "salaervergleich.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "secuvera.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "siammedia.co", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "simplystudio.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -2998,7 +2997,6 @@
     { "name": "playkh.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "portalplatform.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "petrachuk.ru", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "p8r.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "patterson.mp", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "pothe.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "robertglastra.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -4407,7 +4405,6 @@
     { "name": "neel.ch", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "netlocal.ru", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "nicoborghuis.nl", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "nidux.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "niho.jp", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "nodebrewery.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "nope.website", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -7080,7 +7077,6 @@
     { "name": "dbx.ovh", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "deeprecce.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "denisjean.fr", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "devstaff.gr", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "dick.red", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "dinge.xyz", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "distinctivephotography.com.au", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -8611,7 +8607,6 @@
     { "name": "ekbanden.nl", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "ekokontakt.cz", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "ekostecki.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "el-soul.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "elanguest.pl", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "elanguest.ro", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "elanguest.ru", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -10975,7 +10970,6 @@
     { "name": "montonicms.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "modernapprenticeships.org", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "mottvd.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "modernibytovytextil.cz", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "mor.cloud", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "mon-partage.fr", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "moonraptor.co.uk", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -11599,7 +11593,6 @@
     { "name": "devtub.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "dgpot.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "diasp.org", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "diavo.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "digired.xyz", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "directreal.sk", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "djangoproject.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -12069,8 +12062,6 @@
     { "name": "thriveapproach.co.uk", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "tierarztpraxis-bogenhausen.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "tjs.me", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "tobias-picha.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "tobias-weidhase.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "tocaro.im", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "toursandtransfers.it", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "tracetracker.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -12143,7 +12134,6 @@
     { "name": "xroot.org", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "yarcom.ru", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "yinfor.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "yoga-prive.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "yooooex.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "youngandunited.nl", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "yoyoost.duckdns.org", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -12214,7 +12204,6 @@
     { "name": "aiois.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "airbnbopen.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "airhelp.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "airnow.gov", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "akpwebdesign.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "alexberts.ch", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "algolia.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -12894,7 +12883,6 @@
     { "name": "k2mts.org", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "kabeuchi.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "kadmec.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "kaiusaltd.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "kanar.nl", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "kartec.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "kasadara.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -13450,7 +13438,6 @@
     { "name": "slimmerbouwen.be", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "smartest-trading.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "smartrade.tech", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "snapfinance.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "snow.dog", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "sokolkarvina.cz", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "sol-computers.es", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -13655,7 +13642,6 @@
     { "name": "vietnamhost.vn", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "vigrey.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "vinciconps4.it", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "viserproject.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "visibox.nl", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "vitastic.nl", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "voter-info.uk", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -13948,7 +13934,6 @@
     { "name": "bountyfactory.io", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "bluex.im", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "bravz.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "bp-wahl.at", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "brb.city", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "bowntycdn.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "bru6.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -14346,10 +14331,7 @@
     { "name": "guhenry3.tk", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "goetemp.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "hash-archive.org", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "graceful-project.eu", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "greboid.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "glotter.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "greboid.co.uk", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "hearty.ga", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "greensdictofslang.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "gogoodyear.eu", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -14756,7 +14738,6 @@
     { "name": "natsumihoshino.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "mobifinans.ru", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "mitarbeiter-pc.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "nchangfong.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "mitsukabose.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "manantial.mx", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "meronberry.jp", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -15076,7 +15057,6 @@
     { "name": "sequiturs.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "significados.com.br", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "shux.pro", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "skyline.link", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "selectary.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "sciencesolutions.eu", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "sanguoxiu.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -16637,7 +16617,6 @@
     { "name": "uptogood.org", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "unblocked.uno", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "trior.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "terra.by", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "v2ex.us", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "unfuddle.cn", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "utilitarian.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -16696,7 +16675,6 @@
     { "name": "wingos.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "vksportphoto.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "vrijstaandhuis-in-zwartewaterland-kopen.nl", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "weltverschwoerung.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "wormdisk.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "ukrigging.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "wardow.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -17717,7 +17695,6 @@
     { "name": "internetovehazardnihry.cz", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "intercom.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "informatik-handwerk.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "inetpub.cn", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "instruktor.io", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "interchanges.io", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "intvonline.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -17942,7 +17919,6 @@
     { "name": "libble.eu", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "liftie.info", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "lidl-shop.nl", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "lighttp.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "lianye.in", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "linss.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "localnetwork.nz", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -19799,7 +19775,6 @@
     { "name": "fruition.co.jp", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "fixtectools.co.za", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "frankenlehrmittel.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "freeslots.guru", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "freedomvote.nl", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "fpersona.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "fedo.moe", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -20738,7 +20713,6 @@
     { "name": "projectnom.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "queryplayground.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "project-rune.tech", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "mrning.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "pollet-ghys.be", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "quantumwebs.co", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "projectsecretidentity.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -21325,7 +21299,6 @@
     { "name": "vyvybean.cf", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "vehicleuplift.co.uk", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "vegis.ro", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "whisperlab.org", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "tipiakers.club", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "walterlynnmosley.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "wifipineapple.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -26730,7 +26703,6 @@
     { "name": "daktarisys.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "cspeti.hu", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "datacubed.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "creaescola.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "cursuri-de-actorie.ro", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "darkfire.ch", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "cyberspace.community", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -26752,7 +26724,6 @@
     { "name": "delahrzolder.nl", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "cutelariafiveladeouro.com.br", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "dcw.io", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "dctxf.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "danotage.tv", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "dbyz.co.uk", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "deniszczuk.pl", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -27357,7 +27328,6 @@
     { "name": "hqq.tv", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "hotelmadhuwanvihar.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "huahinpropertylisting.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "hotel-le-vaisseau.ch", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "humpen.se", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "ibsglobal.co.za", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "i-red.info", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -27567,7 +27537,6 @@
     { "name": "kieranweightman.me", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "kickedmycat.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "justgalak.org", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "kazy111.info", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "jstelecom.com.br", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "kevinmorssink.nl", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "kenalsworld.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -28085,7 +28054,6 @@
     { "name": "nsfw-story.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "nopaste.xyz", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "notboring.co.uk", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "needstyle.ru", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "menanwc.org", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "northernhamsterclub.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "nordinfo.fi", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -28128,7 +28096,6 @@
     { "name": "okay.cf", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "novaopcaofestas.com.br", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "optimumwebdesigns.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "nupef.org.br", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "omertabeyond.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "oldtimer-trifft-flugplatz.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "orderessay.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -28467,7 +28434,6 @@
     { "name": "rognhaugen.no", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "rukhaiyar.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "robinevandenbos.nl", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "rets.org.br", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "roadtopgm.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "rester-autonome-chez-soi.ch", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "renlen.nl", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -28748,7 +28714,6 @@
     { "name": "sugarandcloth.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "stytt.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "steamscore.info", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "shota.vip", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "submelon.tech", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "strategiclivingblog.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "stijnodink.nl", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -29597,7 +29562,6 @@
     { "name": "bonami.pl", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "bogobeats.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "boueki.org", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "bitace.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "bonami.ro", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "bonami.sk", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "bonaccorso.eu", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -29605,7 +29569,6 @@
     { "name": "booox.pw", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "biomodra.cz", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "breadandlife.org", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "bitroll.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "bitvegas.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "bituptick.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "braviskindenjeugd.nl", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -30110,7 +30073,6 @@
     { "name": "hangcapnach.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "halletienne.fr", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "hexxagon.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "germanticz.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "hbbet.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "golser.info", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "fashionoutfits24.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -30350,7 +30312,6 @@
     { "name": "letterbox-online.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "lilycms.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "lemonrockbiketours.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "liehuojun.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "logymedia.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "kyusyu.org", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "linkmauve.fr", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -33068,7 +33029,6 @@
     { "name": "eliyah.co.il", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "elosuite.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "elyasweb.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "emailtools.io", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "emma-o.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "emojiengine.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "emolafarm.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -33919,7 +33879,6 @@
     { "name": "sharingcode.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "shaunharker.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "shelleystoybox.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "shopods.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "showroom.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "sicilianbalm.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "sieulog.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -34414,7 +34373,6 @@
     { "name": "eb-net.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "eco-derattizzazione.it", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "eco-work.it", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "edanni.io", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "edgetalk.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "edv-kohls.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "eerlijktransport.nl", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -35391,7 +35349,6 @@
     { "name": "drezzy.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "drillnation.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "drive.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "drjenafernandez.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "dtbouncycastles.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "dtuaarsfest.dk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "duckyubuntu.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -37323,7 +37280,6 @@
     { "name": "inkvisual.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "isfriday.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "isotope.gov", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "isotopes.gov", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "isz.no", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "it-jobbank.dk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "itfensi.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -39339,7 +39295,6 @@
     { "name": "posijson.stream", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "powerserg.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "powersergholdings.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "powertothebuilder.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "pozlife.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "praktijkdevecht.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "pritchett.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -40137,7 +40092,6 @@
     { "name": "giebel.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "gig-raiffeisen.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "gigabitz.pw", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "gkimanyar.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "glasdon.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "glenshere.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "gobranding.com.vn", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -40162,7 +40116,6 @@
     { "name": "haixihui.cn", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "halliday.work", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "handmadeshoes.pe", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "hanyibo.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "hazukilab.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "hcaz.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "heatershop.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -42020,7 +41973,6 @@
     { "name": "rightnetworks.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ristoviitanen.fi", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "rittis.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "robottip.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "rodafe.sk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "routercncperu.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "rpus.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -45288,7 +45240,6 @@
     { "name": "kotonoha.cafe", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "kovuthehusky.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ksk-agentur.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "kuops.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lacetsfun.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ladenzeile.at", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ladenzeile.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -45431,8 +45382,6 @@
     { "name": "parodesigns.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "partiwatch.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "pashminacachemire.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "pci-dss.hu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "pcidss.hu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "penslabyrinth.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "performancehealth.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "perthtrains.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -45973,7 +45922,6 @@
     { "name": "hellomouse.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "henrilammers.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "herrkaschke.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "hexadecimal.tech", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "heywood.cloud", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "hitomecha.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "hlinformatics.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -46059,7 +46007,6 @@
     { "name": "larky.top", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lavanderia.roma.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lcbizsolutions.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "led.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "leelaylay.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "letreview.ph", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lindholmen.club", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -46667,7 +46614,6 @@
     { "name": "fallingapart.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "famfi.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "fancy.org.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "faradji.nu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "fart.wtf", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "fbigame.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "fegame.mobi", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -49135,7 +49081,6 @@
     { "name": "skinmodo.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "sleeping.town", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "sleestak.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "sluciaconstruccion.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "smallcloudsolutions.co.za", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "smaltimento.roma.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "smart-media-gmbh.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -51398,7 +51343,6 @@
     { "name": "stitchinprogress.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "stma.is", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "stmarthachurch.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "streamelements.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "stromaci.sk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "stromzivota.sk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "studiobergaminloja.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -52171,7 +52115,6 @@
     { "name": "equallyy.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "esrhd.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "esrinfo.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "essex.cc", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "estudiarparaser.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "esurety.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "etax.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -53975,7 +53918,6 @@
     { "name": "frownonline.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "frpg.gov", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "fs-g.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "fsg.one", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "futaba-works.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "g-ds.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "gaengler.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -58165,7 +58107,6 @@
     { "name": "hotelneptundalmatien.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "hotelsolinebrela.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "houselocal.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "howesky.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "howtomovetheneedle.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "hro.to", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "hsts.ovh", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -58488,7 +58429,6 @@
     { "name": "tallinnsec.ee", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "tallinnsex.ee", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "tarot-cartas.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "tcit.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "tea.in.th", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "techforthepeople.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "technicalramblings.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -59656,7 +59596,6 @@
     { "name": "radiopleer.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "radiumcode.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "raid-runners.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "raiffeisenzeitung.at", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "railduction.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "rallypodium.be", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "randewoo.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -60161,7 +60100,6 @@
     { "name": "helptasker.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "helptasker.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "hgpowerglue.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "huabianwa.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "hyperd.sh", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ibsociety.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ictoniolopisa.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -60902,7 +60840,6 @@
     { "name": "mainhattan-handwerker.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "malibumodas.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "mantuo.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "manwish.cn", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "markhoodwrites.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "marron-dietrecipe.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "masterpassword.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -63012,7 +62949,6 @@
     { "name": "viku.fi", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "washoedems.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "wb2288.cc", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "webandsun.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "well-around-the-world.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "westcoastheatingair.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "weyhmueller.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -64288,7 +64224,6 @@
     { "name": "artplasticsurgeons.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "arx8x.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ashridgetrees.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "askyourdentist.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "atahualpa.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "attuned.se", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "atuendomr.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -64300,7 +64235,6 @@
     { "name": "aznaetelivy.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "b2families.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "bachkhoa.net.vn", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "badedesign.no", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ban.moe", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "batiskaf.ua", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "batteryboys.ca", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -69167,7 +69101,6 @@
     { "name": "discountpokale.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "distortmotion.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "divisasexpress.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "dnsge.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "dnswarden.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "dockflow.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "docnhanh.vn", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -69314,7 +69247,6 @@
     { "name": "octovpn.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "olafwalther.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "optiker-gilde.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "organicnature.dk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "outdoortrip.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "p6729.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "pacalzheimer.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -69452,7 +69384,6 @@
     { "name": "zenways.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "zhujiceping.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "znakcomstva.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "zoso.ro", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "zz6729.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "071k8.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "076k8.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -70659,7 +70590,6 @@
     { "name": "twin-tails.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "tytod.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ubytovanihyncice.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "ukari.hokkaido.jp", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "unblocked.lc", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "up2mark.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "uyen.party", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -72916,7 +72846,6 @@
     { "name": "97737.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "97738.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "97739.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "988316.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "99994048.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "999b58.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "99n13.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -73248,7 +73177,6 @@
     { "name": "bozhok.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "brainobeat.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "brainshare.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "branode.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "brasiltopnews.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "bratunaconline.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "bravica.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -73500,7 +73428,6 @@
     { "name": "disconnect.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "distancelove.ml", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "dities.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "divistart.online", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "dixi.ml", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "dj16888.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "dj16888a.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -74015,7 +73942,6 @@
     { "name": "loveismystyle.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lucarautti.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ludolust.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "luizlopes.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lukaszuk.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lukaszuk.pl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lukezweb.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -74055,24 +73981,7 @@
     { "name": "mauriceje.ga", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "maveeranpasupathi.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "max00365.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "max0365.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "max11365.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "max1365.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "max22365.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "max2365.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "max33365.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "max3365.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "max4365.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "max44365.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "max5365.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "max55365.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "max6365.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "max66365.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "max7365.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "max77365.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "max8365.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "max88365.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "max9365.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "maxclean.ml", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "mayito.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "mayre-idol.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -74789,7 +74698,6 @@
     { "name": "tula-news.ga", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "tuneotune.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "tunisiapress.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "turbosuflantecluj.ro", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "turciya.cf", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "turkey-portal.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "turkishhackers.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -81190,7 +81098,6 @@
     { "name": "penconsultants.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "philipdeussen.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "philipdeussen.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "philo.shop", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "pj1100.cc", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "planhub.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "platform39.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -85198,7 +85105,6 @@
     { "name": "brownsgroup.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "buddhismedia.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "buitenposter.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "burningmarket.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "buscoterapia.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "bywin9.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "campbellkennedy.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -86781,7 +86687,6 @@
     { "name": "bfdz.ink", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "bghope.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "blockchainbulteni.com.tr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "blogcast.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "blogthetindung.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "bluemail24.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "bmbfiltration.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -89030,7 +88935,6 @@
     { "name": "xtom.su", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "xtom.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "yarnsub.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "yourikeakitcheninstaller.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "youronly.one", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "yourtimetravel.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "yu-mug.jp", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -89085,7 +88989,6 @@
     { "name": "airlinesreservation.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "akaal.pw", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "alteralife.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "am8.ag", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "amorgosrentandgo.gr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ankitha.in", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "arbavere.ee", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -89124,7 +89027,6 @@
     { "name": "blogsked.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "bogurl.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "bolico.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "britishacademy.us", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "bthub.site", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "burzcast.ro", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "car-forums.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -89221,16 +89123,6 @@
     { "name": "katom.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "keilycosmetics.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "kernel.nz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "kf8949.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "kf8950.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "kf8951.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "kf8952.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "kf8953.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "kf8954.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "kf8955.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "kf8956.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "kf8957.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "kf8958.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "kiesmedia.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "kiwee.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "klacki.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -89533,7 +89425,6 @@
     { "name": "diamondyacca.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "diesicheremail.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "digipolis.gent", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "dogsdailylife.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "dominionedge.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "douzer.earth", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "dreamingwolf.sk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -89870,22 +89761,9 @@
     { "name": "zohra.ninja", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "zondervanacademic.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "zuzannastrycharska.pl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "0007552.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "0017552.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "0037552.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "0047552.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "0057552.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "0067552.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "0077552.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "0087552.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "0097552.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "0117552.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "0127552.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "0137552.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "1004233.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "111ttt.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "1me.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "2227552.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "2277bet.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "235551.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "24gazette.ga", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -89898,27 +89776,13 @@
     { "name": "4233065.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "4233068.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "4233069.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "4447552.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "4cavaleiros.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "4dropping.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "5557552.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "6004233.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "611121.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "611125.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "622283.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "633362.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "64stacks.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "655591.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "7552001.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "7552002.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "7552005.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "7552006.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "7552008.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "7552009.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "7552010.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "7552011.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "7552012.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "7552013.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "abcorporate-aviation.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "abcorporate-aviation.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "adventurealpinetreks.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -90843,11 +90707,6 @@
     { "name": "jordandirections.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "joseluishenriquez.cl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "k811111.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "k8cf003.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "k8vn001.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "k8vn003.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "k8vn008.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "k8vn010.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "kaiva.cl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "kalhufvudet.se", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "kangutingo.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -91036,7 +90895,6 @@
     { "name": "xtom.support", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "xyj22.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "yachtcita.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "yalacoins.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "yatai18.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "yayou.ag", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "yn.org.nz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -91079,8 +90937,6 @@
     { "name": "153ks.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "155ks.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "157ks.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "158ks.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "159ks.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "170kb.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "175ks.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "184kb.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -91118,7 +90974,6 @@
     { "name": "8851ks.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "88k66.vip", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "88kb.app", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "918hi.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "999ks.app", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "9articles.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "a-bicycleshop.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -91272,7 +91127,6 @@
     { "name": "binairy.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "binairy.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "bingle.nu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "bingoela.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "bioteebook.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "birosuli.hu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "bitcoinheaders.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -91842,7 +91696,6 @@
     { "name": "koelingmonitor.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "koenmartens.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "kokoro-singsong.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "korkortonline.se", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "kranbearys.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "krazy.net.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ks01.cc", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -92382,7 +92235,6 @@
     { "name": "thgstardragon.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "thietkenoithatshop.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "thomas717.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "tierschutz-niederrhein.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "tiffany.life", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "tilellit.pro", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "tiltedscalescollective.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -92482,7 +92334,6 @@
     { "name": "whitecleatbeat.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "widwap.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "wiki24.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "wikipedia-mirror.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "wildfirexpeditions.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "wireless4now.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "wiwi.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -92646,7 +92497,6 @@
     { "name": "77018vip.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "777234567.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "7fon.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "8153d.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "8173d.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "8226d.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "8815d.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -92820,7 +92670,6 @@
     { "name": "brightlingseamusicfest.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "brightonhoney.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "brindespegassus.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "brolic.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "brun.rocks", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "brunodomingos.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "brutdecom.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -93226,7 +93075,6 @@
     { "name": "klusservice-utrecht.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "knabstrup-autoophug.dk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "knapp.pro", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "koalarong.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "koltiva.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "kommotiv.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "kontist.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -95369,7 +95217,6 @@
     { "name": "birdsnow.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "blanx.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "blaskapellehohenpoelz.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "bmyjacks.cn", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "bookaway.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "bou.ke", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "brownandjoseph.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -95842,7 +95689,6 @@
     { "name": "visse-if.dk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "vns6654.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "vosges-tourisme.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "votegreece.gr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "w123.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "warmteshop.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "wb-cw.tech", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -95941,7 +95787,6 @@
     { "name": "cheapdesigners.website", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "chnlib.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "chou-chinois.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "cimen.club", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "cleanway.dk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "clownbox.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "congenio.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -96060,11 +95905,6 @@
     { "name": "kong.ink", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "kraeuterland.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "krasnodar.one", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "ks178.vip", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "ks187.vip", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "ks500.vip", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "ks700.vip", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "ks800.vip", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ksm.co.in", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "kuhlecloud.co.za", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "laharilais.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -96178,7 +96018,6 @@
     { "name": "quiz.biz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "raymundo.doctor", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "rcsda.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "reparaturcafe-pfullendorf.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "replica.plus", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "republicasantabanana.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "resslovaci.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -96265,7 +96104,6 @@
     { "name": "ygets.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "zenisi.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "zhengouwu.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "zl8862.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "002.ro", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "0514.chat", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "0chan.club", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -96498,7 +96336,6 @@
     { "name": "signal34.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "simplesend.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "skidzun.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "sofpedia.ro", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "sorocabacopos.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "streamlineaudio.co.za", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "strily.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -96590,6 +96427,1133 @@
     { "name": "you-livetv.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "yuweetek.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "zakbk.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "0km.top", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "0x2a.ninja", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "1211bet.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "1266bet.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "1277bet.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "1299bet.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "171365a.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "171365b.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "171365c.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "171365d.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "171365e.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "171365f.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "171365g.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "171365h.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "171365i.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "171365j.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "171365k.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "171365m.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "171365n.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "171365p.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "171365q.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "171365r.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "171365s.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "171365t.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "171365u.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "171365v.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "171365w.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "171365x.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "171365y.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "171365z.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "1clic1.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "2122bet.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "2322bet.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "233.land", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "233.services", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "233image.land", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "2366bet.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "2377bet.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "2399bet.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "24webservice.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "30for30podcasts.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "3133bet.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "3233bet.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "3433bet.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "3455bet.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "3466bet.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "3477bet.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "36588800.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "36588811.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "36588822.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "365888321.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "36588833.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "365888432.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "3658884321.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "36588844.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "365888543.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "3658885432.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "36588855.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "365888654.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "3658886543.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "36588866.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "365888765.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "3658887654.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "365888876.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "3658888765.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "365888987.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "3658889876.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "365a1.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "3aa365.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "3bb365.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "3cc365.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "3dd365.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "3ee365.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "3ff365.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "3gg365.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "3hh365.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "3ii365.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "3jj365.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "3niu11.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "3niu12.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "3niu13.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "3niu15.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "3niu17.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "3niu18.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "3niu19.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "3zzbet.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "40daysforlifepensacola.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "4144bet.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "4344bet.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "4544bet.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "5155bet.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "5455bet.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "5611bet.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "5622bet.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "5633bet.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "7177bet.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "7877bet.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "8699bet.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "8799bet.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "8966bet.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "8977bet.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "90splease.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "91idy.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "9877bet.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "abi-kurs.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "abilympics.org.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "abogadosjaca.es", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "abwatches.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "accretivetechno.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "adamscampcolorado.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "addictedtotravel.pl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "adgame.live", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "advancedsepticandpumping.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "advantageroofer.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "advasa.jp", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "afbrtn.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "affiliatemarketingplan4beginners.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "after40.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "agellonia.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ai.ls", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "aircraftnoisemodel.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "akademie-multimedii.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "aktuelleprospekte.at", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "alabn.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "alexpostnikov.by", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "alhs-archives.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "alinecordeiro.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "alliaancebiotech.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "alpinedentalhealth.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "am2digital.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "amasea.yachts", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ambersoftware.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "animate.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "animepahe.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ankureurope.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ao3fan.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ao3fans.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ao3unlock.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "app3w.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "appbot.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "aqlivia.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "archives.gov", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "arclookup.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "arroyoins.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "asml.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "assured.se", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "atomictag.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "austinmobilestorage.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "australianhimalayanfoundation.org.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "aviapic.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "aviapic.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "aviapic.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "aviapic.info", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "aviapic.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "aviapic.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "awesomelifedeals.today", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "axelr.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "b979365.cn", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "b979365.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "b979365.vip", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "babyvillagegt.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bachelorampel.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "backporchartists.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "banimarket.by", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "basamadco.ir", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "basementdoctorwestvirginia.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "basementdoctorwv.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bastawholesale.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "baukebies.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bdmusic25.us", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bearcreekcubschildcare.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "beggintime.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "belevida.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "beryko.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bestlawyernear.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bet365bet2020.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bet365vip2020.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bet3xx.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bet3zz.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bewegingdenk.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "beyondthepitch.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bikealways.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bingohalls.ca", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "birhayalikiinsan.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bitcoinmakesense.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bitradius.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bixelo.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "blackishtv.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bo-rad.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bocada.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "borzaresearch.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bpsis.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "brasilwear.biz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bridgesinbelize.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "britofootball.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "broilertrade.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "brokerstalk.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "brunnenhof-welze.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "brunoarruda.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bs3xy.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bsystem.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bt121.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bt121.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "butlist.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "byskafasi.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "caalmn.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "campanhamamypoko.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cannon.org.cn", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cap-adrenaline.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "carforme.gr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "caryl2twenty.info", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "castlabs.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "catsoft.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "caucasusandmercury.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cavisson.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cbdoils.llc", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cdfnature2019.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "celestialdental.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cellohealth.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "challahuakbar.rocks", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "chalu.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "chaoxi.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "chateauderoncourt.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cherysunzhang.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "chiakhoakhoinghiep.vn", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "chibiotaku.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "chinaicpower.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "christoph.media", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cibdol.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ciginsurance.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cirurgicaexpress.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "claaruba.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "clam.network", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cleova.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "clinicadentalacacias.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cm-terrasdebouro.pt", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "coachoutlettvb.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "colleenfaulknernovels.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "coloradoroofingservice.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "communityaligned.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "computernetwork.be", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "comunicate.digital", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "comuniclick.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "conhecendocosmeticos.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "constru-vegas.com.mx", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "consumera.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cookiepedia.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "coolspring8.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cospi.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "courtneybearse.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "crushthelsatexam.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "crushthepmexam.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cryptolocalatm.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "crypton.asia", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "csdcareerday.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cubia.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cubocell.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cumulogranite.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "d4h.live", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "dallastxdivorce.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "danieldavies.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "dannywall.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "danux.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "decoinn.com.tw", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "dedirten.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "delivr.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "desertfiredesigns.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "designtrc.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "devslash.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "devynfreet.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "diablo-2.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "diamondcontent.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "digital-insure.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "digitalgeek.social", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "dimism.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "directfitnesssolutions.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "discus.fish", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "distribuidoradecierres.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "dn-inc.biz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "dosei.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "dotya.ml", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "down-load.dynu.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "downloadmoremousepad.ml", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "drakeexcavating.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "drjart.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "dwz.wtf", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "dx365.biz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "dzz.by", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac010.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac020.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac021.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac022.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac023.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac024.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac025.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac027.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac028.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac029.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0310.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0311.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0312.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0313.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0314.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0315.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0316.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0317.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0318.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0319.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0335.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0350.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0351.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0352.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0353.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0354.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0355.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0356.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0357.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0358.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0359.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0370.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0371.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0372.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0373.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0374.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0375.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0376.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0377.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0378.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0379.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0391.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0392.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0393.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0394.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0395.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0396.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0398.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0410.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0411.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0412.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0413.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0414.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0415.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0416.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0417.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0418.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0419.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0421.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0427.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0429.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0431.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0432.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0433.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0434.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0435.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0436.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0437.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0438.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0439.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0440.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0450.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0451.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0452.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0453.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0454.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0455.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0456.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0457.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0458.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0459.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0470.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0471.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0472.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0473.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0474.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0475.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0476.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0477.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0478.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0479.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0482.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0483.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0510.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0511.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0512.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0513.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0514.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0515.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0516.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0517.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0518.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0519.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0523.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0530.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0531.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0532.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0533.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0534.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0535.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0536.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0537.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0538.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0539.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0550.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0551.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0552.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0553.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0554.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0555.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0556.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0557.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0558.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0559.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0561.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0562.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0563.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0564.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0565.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0566.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0570.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0571.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0572.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0573.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0574.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0575.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0576.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0577.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0578.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0579.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0580.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0591.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0592.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0593.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0594.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0595.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0596.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0597.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0598.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0599.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0660.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0661.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0662.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0663.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0691.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0692.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0701.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0710.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0711.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0712.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0713.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0714.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0715.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0716.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0717.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0718.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0719.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0722.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0724.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0728.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0730.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0731.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0732.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0733.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0734.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0735.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0736.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0737.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0738.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0739.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0743.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0744.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0745.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0746.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0751.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0752.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0753.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0754.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0755.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0756.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0757.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0758.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0759.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0760.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0762.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0763.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0765.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0766.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0768.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0769.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0770.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0771.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0772.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0773.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0774.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0775.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0776.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0777.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0778.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0779.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0790.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0791.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0792.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0793.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0794.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0795.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0796.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0797.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0798.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0799.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0810.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0811.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0812.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0813.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0814.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0816.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0817.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0818.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0819.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0826.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0827.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0830.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0831.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0832.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0833.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0834.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0835.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0836.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0837.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0838.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0839.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0840.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0851.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0852.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0853.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0854.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0855.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0856.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0857.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0858.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0859.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0870.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0871.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0872.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0873.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0874.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0875.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0876.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0877.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0878.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0879.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0881.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0883.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0886.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0887.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0888.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0890.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0891.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0898.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0899.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0910.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0911.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0912.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0913.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0914.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0915.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0916.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0917.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0930.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0931.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0932.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0933.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0934.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0935.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0936.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0937.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0938.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0941.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0943.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0951.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0952.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0953.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0954.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0971.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0972.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0973.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0974.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0975.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0976.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0977.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac0991.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac444.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eac555.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "echelle-escamotable.info", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "edgezzz.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ee-koolitus.ee", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "een-eenvoudige-test-voor-de-maximum-lengte-van-een-nederlandse.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eldiariodemof.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "elegance-lingerie.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "elektrolang.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "elevatedconstructionltd.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "elitephysiotherapy.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "emiliehouse.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "encircled.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "enghero.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ennioporrino.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "enstroga.at", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "entryscape.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "envirobizcollective.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "erico-hm.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "escortsontop.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "esiefektivs.lv", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "essethon.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "estumarca.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eviadc.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "evitacion.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "exoticspecialist.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "explorecams.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "extrememusclepump.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "f-csc.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "faeriebabe.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "familiebies.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "familyframeworks.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "feiromo.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "fico.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "fiorenzaperfumhome.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "firenze.net.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "fiseon.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "fitfocusau.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "fklegal.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "flawlessbiometrics.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "floryceblanchery.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "flyabe.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "flylvia.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "flyn43.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "flyxll.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "fmjd64.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "foiremobile.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "fontanaseiyo.jp", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "forrestwalkbarbershop.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "foundationmaintenance.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "freedom-substitute.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "fridaypulse.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "friedli.info", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "fussballtransfers.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "galexlee.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "gameshack.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "garciam.gt", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "gasull-claramunt.cat", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "gbsmiedzyrzecz.pl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "generalcustomshop.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ggradio.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ghienlamdep.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "globalshares.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "gofoodservice.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "gokazakhstan.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "gokyrgyzstan.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "gonepal.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "gotajikistan.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "goturkmenistan.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "gpselect.com.tw", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "grceurope.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "greavessports.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "greenderma.in", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "greenearthlawns.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "grenoblepartners.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "growthinbusiness.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "gruber-software.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "gruener-salon-bochum.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "gutterguardcharlotte.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "guttershutter.biz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "habibitravels.com.ng", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "hae.sh", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "haizrulamrie.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "hby.cx", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "heckmann.photos", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "hermodesign.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "heroesdelplaneta.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "herogaming.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "herold.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "herold.space", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "hetfundament.team", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "hjf.com.ar", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "hjwcarpentry.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "hobindesign.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "holmesworkholding.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "honeycombcreative.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "hooperlabs.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "host4us.cc", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "hostathome.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "hosuto.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "hoteldahu.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "how2recycle.info", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "howdybikes.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "hqblog.cn", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ibq.life", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ichibanfansub.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "icterra.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "idf64.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ienekolife.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ifanqiang.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "illogical-gaming.at", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "imro.ie", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "indahstay.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "inflowphysio.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "infosecsw.ca", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ingereck.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "insomniac.ro", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "interactiveliterature.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "iorgroup.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "iqbalmauludy.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "iqos.com.ua", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "isustain.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ithot.ro", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "itsm.tools", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "itworks.nyc", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "itzlive.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "jabagly.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "jachtbouw.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "jackmcgregor.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "jan.su", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "jc2.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "jejakbocah.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "jettenbommelaer.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "jettenjachtbouw.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "joico.by", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "jonaskruckenberg.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "josephrichard.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "jrfortune.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "julianporras.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "jumibow.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "kebo.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "keepsafe.live", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "kerb-grossauheim.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "keywaysconsulting.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "kfoundation.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "kibickas.lt", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "kidsdj.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "killerwebsites.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "kivalitaconsulting.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "kluzza.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "koizumidesign.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "kooky.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "koperek.pl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "krajzlinsky.info", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "kuechler.info", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "kuklavrost.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "kunc.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "kyosyo-jungle.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "laarroceriacolombiana.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "lammertbies.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "lammertbies.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "laube-school.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "lazownik.pl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "lbet365.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "lcb1.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ldiesel.ca", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "leblancq.ca", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "legale-services.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "lekkerleben.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "lesborgestv.cat", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "leticia.com.tw", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "letiziamx.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "letsdevelop.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "lfcnsv.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "libcip.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "libcmodbus.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "libcrc.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "libfins.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "libhttp.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "libpdf.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "libscpi.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "lig.ink", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "linocomm.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "linocomm.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "linocomm.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "linomass.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "linomass.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "linoplan.be", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "linoplan.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "linoplan.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "linoplan.dk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "linoplan.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "linoplan.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "linoplan.info", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "linoplan.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "linoplan.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "linoscan.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "linoscan.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "linoskin.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "linoskin.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "linostor.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "linostor.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "linotrac.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "linotrac.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "liongueststudios.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "lovecode.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "lsj.world", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "luksusy.pl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "lunademiel.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "luxplay.com.tw", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "lvee.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "lvfc.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "machkovi.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "madeincana.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "magazineflex.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mailsupport.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "manbetx1998.live", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mangacdn.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "manufacturingsupportgroup.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "maocular.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mapausenaturelle.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "marketingconcafe.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "matthewthode.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "maxcleaning.be", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "maxley.yachts", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mdinashop.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "medart-media.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "medcartoon.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "metasolutions.se", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "meww.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mh.com.fj", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mihu233.com.cn", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mindbuild.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ministryofinternet.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "minpex.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "miodziki.pl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mk.gov.tr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mkt7.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "moec.top", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mokamelhaa.ir", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "money-spell.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "moreyalta.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mossan.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "msa.bank", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "msaludasuhogar.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mt-caza.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mtabriz.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mudareganhar.pt", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "muhafazakarkiralikvilla.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mullerkappers.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "multifest.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mundodasfechaduras.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mwms.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mychicken.info", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mychicken.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "myfsb.bank", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "myfutureself.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mynook.info", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mypet24.ch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mytefl.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "narec.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "natusvita.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "navdigital.cl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "nawt.pl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "neilhosting.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "new10.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "newagehoops.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "newway.ie", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "nexalis.co.za", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "nextvery.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "nhadatpho.com.vn", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "nibrasfashion.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "nickfarr.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "nintendoreporters.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "nkuxlogistics.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "noirpvp.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "nothinux.id", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "nsu.pw", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "nzguns.co.nz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "oceanofpdf.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "offeryep.info", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ohiobrewweek.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "omniga.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "onetrust.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "onmyside.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "openbsdhosting.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "opencache.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "opera.im", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "opussystems.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "opzich.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "osteria-italiana-nsv.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "osugiving.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ourgame.ie", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "oxidized.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "pao.moe", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "parfumersha.by", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "parkbee.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "parkersbarbershop.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "partitioningjohannesburg.co.za", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "pascoaselecta.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "pastebin.bet", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "pauljmartinez.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "payamazizi.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "pcmr.rocks", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "pd2bans.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "peddie.institute", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "penthack.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "pepta.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "pereceh.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "perrotts.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "pgui.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "phonearena.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "photography-edu.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "pic2pat.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "pic2pat.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "pictopat.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "pictopat.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "pimentalove.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "pinkplay.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "pizzahut.co.in", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "pollies.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ponte-camp.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "poolheatingsolutionswa.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "porzellantreff.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "powersolusa.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "poynter.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "prestigeworldwidepr.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "primocourtreporting.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "protidhoni.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "psau.edu.sa", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "pueblosamerica.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "puhudefu.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "qalikay.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "qb928.com.tw", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "qmradio.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "qualitydns.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "quora.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "qwords.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "radiationserviceswa.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "rajeshkochhar.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "rawfitco.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "rebellecare.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "rebeportillo.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "regaltheatre.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "remiz.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "retiradinerodetutarjetadecredito.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "revolutionenkommer.dk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "rfp-rechtsanwaelte.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "rfs-zbpe.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "riannespiercingshop.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "rib-leipzig.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "richardfaber.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ripper.store", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "rocketdashboard.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "rodolphe-lebrun.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "rohanisuhadi.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "roidsstore.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "rootfor.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "royal83.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "royal84.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "rsa-services.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "russandol.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "sabrinajoiasatacado.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "sagame88vip.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "satena.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "savoir.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "schmiggywibblits.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "scoopgalleries.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "secard.cc", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "secs.london", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "selectiveasia.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "sembrando-amor.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "sergeenko.by", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "sherijames.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "shoplogcap.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "signature.in.th", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "simmonshome.cloud", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "simonzoellner.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "simple-test-to-demonstrate-the-maximum-length-of-a-domain-name.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "simple-test-to-demonstrate-the-maximum-length-of-a-domain-name.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "simple-test-to-demonstrate-the-maximum-length-of-a-domain-name.international", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "simplyfitperth.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "simplyheadwear.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "simplypromo.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "simplyuniforms.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "sintas.lt", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "skoroff.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "sleepig.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "smartrentacar.ro", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "smartsprouts.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "snatti.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "snorkelaroundtheworld.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "snowdrop.moe", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "snowy.ink", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "soderparr.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "solisboutique.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "solutionsaddles.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "spieka.info", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "splashstoretw.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "spleis.no", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "sqalogic.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "stacisezeptat.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "stanglwirt.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "stickme.be", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "strategicemailservices.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "streamanimetv.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "strokesb.store", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "strousberg.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "stuartcrawford.nz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "subiacotram.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "summit-humanpotential.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "supercombinata.by", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "swiftirc.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "syrea.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "szuflady.pl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "taiwanteama.com.tw", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "talonro.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tamatoyaku.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tamronhallshow.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tarchive.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tate.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tccb.gov.tr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tcybert.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tcyoung.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "teaks.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tempotem.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "teppich-frisch.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "teppichfrisch.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "teqip-pms.gov.in", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tespent.cn", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "testmock.in", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "teutonia-grossenlueder.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "teutonia08.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "thatsucks.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "the-race.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "theblueinnovations.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "theboardroomsubi.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "thefabulouswomen.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "thefastmode.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "theglobalreport.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "theluxonomist.es", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "theowlclub.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "theptpractitioner.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "therivercrosswarwick.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "therudes.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "thisismit.ch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tiendamultimarca.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "timberjoineryperth.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tinakay-photography.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tinyhousebarat.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tixtips.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tldplaza.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tojeit.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tomorrowhealth.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tomove.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "toplockshop.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "topsnow.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "townforge.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tr34.ro", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "trainingsecke.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "translation.qa", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "travelphotographycourse.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "treering.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tribeda.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tricityhelpline.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tryramp.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tuttopappagalli.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "typica.com.tw", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "une-bonne-nouvelle.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "uniqueazaw.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "uniteinhealth.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "unlisted.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "unmade.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "uoe.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "uploadyourtestament.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ups-yahweh.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "upstaa.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "us2uplumbing.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "usashopex.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "uspesnyprvnacek-demo.herokuapp.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "v-studio.by", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "v.pn", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "valoriani.by", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "vandeth.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "vault647.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "vavra.us", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "vazgaming.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "vdb-it.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "veritalife.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "viacheslavpleshkov.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "vincehut.top", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "vinumenu.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "vipkit.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "viralcreate.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "visitationbvm.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "visualvolta.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "vitalhealthandbeauty.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "vnpem.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "vnpem.store", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "vuelosabajoprecio.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "vybavzahradu.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "vyskocil.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "wastewaterservicesltd.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "wc64.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "weatherforyou.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "web-3.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "websec.nu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "websitesbywordpress.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "weldonconstruction.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "wessalicious.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "wikimirror.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "wilco-s.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "wildmine.su", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "wohao.ga", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "wowbabykids.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "wp-bootstrap.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "wsrn.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "wuminhao.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "www.gov.scot", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "xfinityapparel.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "xn--80adianadstvnice3evh.xn--90ais", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "xn--80afdc7a1a8m.kyiv.ua", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "xn--gi8h6v.ml", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "xn--nda.fit", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "xn--s-pia8o.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "xnxx54.info", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "xrptipbot-stats.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "xumm.community", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "yavip8088.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "yifymovietorrent.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "youreitbranding.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "youtubelet.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "zanquan.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "zhabagly.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "zhuji.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "zhunlink.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "zthc.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "zwemclub-rob.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     // END OF 1-YEAR BULK HSTS ENTRIES
 
     // Only eTLD+1 domains can be submitted automatically to hstspreload.org,
diff --git a/net/quic/bidirectional_stream_quic_impl_unittest.cc b/net/quic/bidirectional_stream_quic_impl_unittest.cc
index 5d0b64d..4f63630 100644
--- a/net/quic/bidirectional_stream_quic_impl_unittest.cc
+++ b/net/quic/bidirectional_stream_quic_impl_unittest.cc
@@ -2516,6 +2516,12 @@
 // QuicChromiumClientSession::Handle::ReleaseStream() is called, there is no
 // crash. Regression test for crbug.com/754823.
 TEST_P(BidirectionalStreamQuicImplTest, ReleaseStreamFails) {
+  if (VersionHasIetfQuicFrames(version_.transport_version)) {
+    // Re-enable after crbug.com/1060765 is fixed.
+    // Session should be configured before creating streams.
+    return;
+  }
+
   SetRequest("GET", "/", DEFAULT_PRIORITY);
   Initialize();
 
diff --git a/net/quic/quic_flags_list.h b/net/quic/quic_flags_list.h
index 39055de8..3a40eaed 100644
--- a/net/quic/quic_flags_list.h
+++ b/net/quic/quic_flags_list.h
@@ -395,3 +395,9 @@
     bool,
     FLAGS_quic_reloadable_flag_quic_bbr2_add_ack_height_to_queueing_threshold,
     false)
+
+// If true, send PING when PTO skips packet number and there is no data to send.
+QUIC_FLAG(
+    bool,
+    FLAGS_quic_reloadable_flag_quic_send_ping_when_pto_skips_packet_number,
+    true)
diff --git a/remoting/host/it2me/it2me_host.cc b/remoting/host/it2me/it2me_host.cc
index 378751d..70ea532 100644
--- a/remoting/host/it2me/it2me_host.cc
+++ b/remoting/host/it2me/it2me_host.cc
@@ -506,7 +506,7 @@
 
 void It2MeHost::ValidateConnectionDetails(
     const std::string& original_remote_jid,
-    const ValidationResultCallback& result_callback) {
+    ValidationResultCallback result_callback) {
   DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread());
 
   // First ensure the JID we received is valid.
@@ -515,8 +515,8 @@
                                 /*resource=*/nullptr)) {
     LOG(ERROR) << "Rejecting incoming connection from " << original_remote_jid
                << ": Invalid JID.";
-    result_callback.Run(
-        protocol::ValidatingAuthenticator::Result::ERROR_INVALID_ACCOUNT);
+    std::move(result_callback)
+        .Run(protocol::ValidatingAuthenticator::Result::ERROR_INVALID_ACCOUNT);
     DisconnectOnNetworkThread();
     return;
   }
@@ -524,8 +524,8 @@
 
   if (client_username.empty()) {
     LOG(ERROR) << "Invalid user name passed in: " << remote_jid;
-    result_callback.Run(
-        protocol::ValidatingAuthenticator::Result::ERROR_INVALID_ACCOUNT);
+    std::move(result_callback)
+        .Run(protocol::ValidatingAuthenticator::Result::ERROR_INVALID_ACCOUNT);
     DisconnectOnNetworkThread();
     return;
   }
@@ -543,7 +543,7 @@
     if (!matched) {
       LOG(ERROR) << "Rejecting incoming connection from " << remote_jid
                  << ": Domain not allowed.";
-      result_callback.Run(ValidationResult::ERROR_INVALID_ACCOUNT);
+      std::move(result_callback).Run(ValidationResult::ERROR_INVALID_ACCOUNT);
       DisconnectOnNetworkThread();
       return;
     }
@@ -554,7 +554,8 @@
   if (state_ != kReceivedAccessCode) {
     DCHECK_EQ(kConnecting, state_);
     LOG(ERROR) << "Received too many connection requests.";
-    result_callback.Run(ValidationResult::ERROR_TOO_MANY_CONNECTIONS);
+    std::move(result_callback)
+        .Run(ValidationResult::ERROR_TOO_MANY_CONNECTIONS);
     DisconnectOnNetworkThread();
     return;
   }
@@ -570,28 +571,28 @@
         host_context_->ui_task_runner(),
         confirmation_dialog_factory_->Create()));
     confirmation_dialog_proxy_->Show(
-        client_username, base::Bind(&It2MeHost::OnConfirmationResult,
-                                    base::Unretained(this), result_callback));
+        client_username,
+        base::Bind(&It2MeHost::OnConfirmationResult, base::Unretained(this),
+                   base::Passed(std::move(result_callback))));
   } else {
     base::ThreadTaskRunnerHandle::Get()->PostTask(
         FROM_HERE,
-        base::BindOnce(result_callback, ValidationResult::SUCCESS));
+        base::BindOnce(std::move(result_callback), ValidationResult::SUCCESS));
   }
 }
 
-void It2MeHost::OnConfirmationResult(
-    const ValidationResultCallback& result_callback,
-    It2MeConfirmationDialog::Result result) {
+void It2MeHost::OnConfirmationResult(ValidationResultCallback result_callback,
+                                     It2MeConfirmationDialog::Result result) {
   DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread());
 
   connecting_jid_.clear();
   switch (result) {
     case It2MeConfirmationDialog::Result::OK:
-      result_callback.Run(ValidationResult::SUCCESS);
+      std::move(result_callback).Run(ValidationResult::SUCCESS);
       break;
 
     case It2MeConfirmationDialog::Result::CANCEL:
-      result_callback.Run(ValidationResult::ERROR_REJECTED_BY_USER);
+      std::move(result_callback).Run(ValidationResult::ERROR_REJECTED_BY_USER);
       DisconnectOnNetworkThread();
       break;
   }
diff --git a/remoting/host/it2me/it2me_host.h b/remoting/host/it2me/it2me_host.h
index 5cc8180..152710bf 100644
--- a/remoting/host/it2me/it2me_host.h
+++ b/remoting/host/it2me/it2me_host.h
@@ -142,7 +142,7 @@
 
   // Processes the result of the confirmation dialog.
   void OnConfirmationResult(
-      const protocol::ValidatingAuthenticator::ResultCallback& result_callback,
+      protocol::ValidatingAuthenticator::ResultCallback result_callback,
       It2MeConfirmationDialog::Result result);
 
   // Task posted to the network thread from Connect().
@@ -169,7 +169,7 @@
   // connection should be accepted or rejected.
   void ValidateConnectionDetails(
       const std::string& remote_jid,
-      const protocol::ValidatingAuthenticator::ResultCallback& result_callback);
+      protocol::ValidatingAuthenticator::ResultCallback result_callback);
 
   // Caller supplied fields.
   std::unique_ptr<ChromotingHostContext> host_context_;
diff --git a/remoting/host/pam_authorization_factory_posix.cc b/remoting/host/pam_authorization_factory_posix.cc
index f58f9740..f60ee53 100644
--- a/remoting/host/pam_authorization_factory_posix.cc
+++ b/remoting/host/pam_authorization_factory_posix.cc
@@ -21,15 +21,15 @@
 namespace {
 class PamAuthorizer : public protocol::Authenticator {
  public:
-  PamAuthorizer(std::unique_ptr<protocol::Authenticator> underlying);
+  explicit PamAuthorizer(std::unique_ptr<protocol::Authenticator> underlying);
   ~PamAuthorizer() override;
 
-  // protocol::Authenticator interface.
+  // protocol::Authenticator:
   State state() const override;
   bool started() const override;
   RejectionReason rejection_reason() const override;
   void ProcessMessage(const jingle_xmpp::XmlElement* message,
-                      const base::Closure& resume_callback) override;
+                      base::OnceClosure resume_callback) override;
   std::unique_ptr<jingle_xmpp::XmlElement> GetNextMessage() override;
   const std::string& GetAuthKey() const override;
   std::unique_ptr<protocol::ChannelAuthenticator> CreateChannelAuthenticator()
@@ -38,7 +38,7 @@
  private:
   void MaybeCheckLocalLogin();
   bool IsLocalLoginAllowed();
-  void OnMessageProcessed(const base::Closure& resume_callback);
+  void OnMessageProcessed(base::OnceClosure resume_callback);
 
   static int PamConversation(int num_messages,
                              const struct pam_message** messages,
@@ -79,16 +79,17 @@
 }
 
 void PamAuthorizer::ProcessMessage(const jingle_xmpp::XmlElement* message,
-                                   const base::Closure& resume_callback) {
+                                   base::OnceClosure resume_callback) {
   // |underlying_| is owned, so Unretained() is safe here.
-  underlying_->ProcessMessage(message, base::Bind(
-      &PamAuthorizer::OnMessageProcessed,
-      base::Unretained(this), resume_callback));
+  underlying_->ProcessMessage(
+      message,
+      base::BindOnce(&PamAuthorizer::OnMessageProcessed, base::Unretained(this),
+                     base::Passed(std::move(resume_callback))));
 }
 
-void PamAuthorizer::OnMessageProcessed(const base::Closure& resume_callback) {
+void PamAuthorizer::OnMessageProcessed(base::OnceClosure resume_callback) {
   MaybeCheckLocalLogin();
-  resume_callback.Run();
+  std::move(resume_callback).Run();
 }
 
 std::unique_ptr<jingle_xmpp::XmlElement> PamAuthorizer::GetNextMessage() {
diff --git a/remoting/host/token_validator_base.cc b/remoting/host/token_validator_base.cc
index e9d5890..0448e88f0 100644
--- a/remoting/host/token_validator_base.cc
+++ b/remoting/host/token_validator_base.cc
@@ -127,12 +127,12 @@
 // TokenValidator interface.
 void TokenValidatorBase::ValidateThirdPartyToken(
     const std::string& token,
-    const base::Callback<void(
-        const std::string& shared_secret)>& on_token_validated) {
+    base::OnceCallback<void(const std::string& shared_secret)>
+        on_token_validated) {
   DCHECK(!request_);
   DCHECK(!on_token_validated.is_null());
 
-  on_token_validated_ = on_token_validated;
+  on_token_validated_ = std::move(on_token_validated);
   token_ = token;
   StartValidateRequest(token);
 }
@@ -178,7 +178,7 @@
   retrying_request_ = false;
   std::string shared_token = ProcessResponse(net_result);
   request_.reset();
-  on_token_validated_.Run(shared_token);
+  std::move(on_token_validated_).Run(shared_token);
 }
 
 void TokenValidatorBase::OnReceivedRedirect(
diff --git a/remoting/host/token_validator_base.h b/remoting/host/token_validator_base.h
index b79e33f..a5f90ff 100644
--- a/remoting/host/token_validator_base.h
+++ b/remoting/host/token_validator_base.h
@@ -36,7 +36,7 @@
   // TokenValidator interface.
   void ValidateThirdPartyToken(
       const std::string& token,
-      const base::Callback<void(const std::string& shared_secret)>&
+      base::OnceCallback<void(const std::string& shared_secret)>
           on_token_validated) override;
 
   const GURL& token_url() const override;
@@ -85,7 +85,8 @@
   // needs to be retried.
   std::string token_;
 
-  base::Callback<void(const std::string& shared_secret)> on_token_validated_;
+  base::OnceCallback<void(const std::string& shared_secret)>
+      on_token_validated_;
 
   base::WeakPtrFactory<TokenValidatorBase> weak_factory_{this};
 
diff --git a/remoting/protocol/authenticator.h b/remoting/protocol/authenticator.h
index 60933f8..9e1d82c 100644
--- a/remoting/protocol/authenticator.h
+++ b/remoting/protocol/authenticator.h
@@ -119,7 +119,7 @@
   // finished. The implementation must guarantee that |resume_callback| is not
   // called after the Authenticator is destroyed.
   virtual void ProcessMessage(const jingle_xmpp::XmlElement* message,
-                              const base::Closure& resume_callback) = 0;
+                              base::OnceClosure resume_callback) = 0;
 
   // Must be called when in MESSAGE_READY state. Returns next
   // authentication message that needs to be sent to the peer.
diff --git a/remoting/protocol/authenticator_test_base.cc b/remoting/protocol/authenticator_test_base.cc
index e3313c1..6c533094 100644
--- a/remoting/protocol/authenticator_test_base.cc
+++ b/remoting/protocol/authenticator_test_base.cc
@@ -111,10 +111,11 @@
   ASSERT_NE(Authenticator::MESSAGE_READY, sender->state());
 
   ASSERT_EQ(Authenticator::WAITING_MESSAGE, receiver->state());
-  receiver->ProcessMessage(message.get(), base::Bind(
-      &AuthenticatorTestBase::ContinueAuthExchangeWith,
-      base::Unretained(receiver), base::Unretained(sender),
-      receiver->started(), sender->started()));
+  receiver->ProcessMessage(
+      message.get(),
+      base::BindOnce(&AuthenticatorTestBase::ContinueAuthExchangeWith,
+                     base::Unretained(receiver), base::Unretained(sender),
+                     receiver->started(), sender->started()));
 }
 
 void AuthenticatorTestBase::RunChannelAuth(bool expected_fail) {
diff --git a/remoting/protocol/fake_authenticator.cc b/remoting/protocol/fake_authenticator.cc
index 631c83c..8a84bf3 100644
--- a/remoting/protocol/fake_authenticator.cc
+++ b/remoting/protocol/fake_authenticator.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// const  Copyright (c) 2012 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.
 
@@ -160,7 +160,7 @@
 }
 
 void FakeAuthenticator::ProcessMessage(const jingle_xmpp::XmlElement* message,
-                                       const base::Closure& resume_callback) {
+                                       base::OnceClosure resume_callback) {
   EXPECT_EQ(WAITING_MESSAGE, state());
   std::string id =
       message->TextNamed(jingle_xmpp::QName(kChromotingXmlNamespace, "id"));
@@ -181,10 +181,10 @@
 
   ++messages_;
   if (messages_ == pause_message_index_) {
-    resume_closure_ = resume_callback;
+    resume_closure_ = std::move(resume_callback);
     return;
   }
-  resume_callback.Run();
+  std::move(resume_callback).Run();
 }
 
 std::unique_ptr<jingle_xmpp::XmlElement> FakeAuthenticator::GetNextMessage() {
diff --git a/remoting/protocol/fake_authenticator.h b/remoting/protocol/fake_authenticator.h
index 6441b1d8..deb89d5 100644
--- a/remoting/protocol/fake_authenticator.h
+++ b/remoting/protocol/fake_authenticator.h
@@ -98,7 +98,7 @@
   bool started() const override;
   RejectionReason rejection_reason() const override;
   void ProcessMessage(const jingle_xmpp::XmlElement* message,
-                      const base::Closure& resume_callback) override;
+                      base::OnceClosure resume_callback) override;
   std::unique_ptr<jingle_xmpp::XmlElement> GetNextMessage() override;
   const std::string& GetAuthKey() const override;
   std::unique_ptr<ChannelAuthenticator> CreateChannelAuthenticator()
@@ -117,7 +117,7 @@
   int messages_till_started_ = 0;
 
   int pause_message_index_ = -1;
-  base::Closure resume_closure_;
+  base::OnceClosure resume_closure_;
 
   std::string auth_key_;
 
diff --git a/remoting/protocol/it2me_host_authenticator_factory.cc b/remoting/protocol/it2me_host_authenticator_factory.cc
index e99e908..ce51dee 100644
--- a/remoting/protocol/it2me_host_authenticator_factory.cc
+++ b/remoting/protocol/it2me_host_authenticator_factory.cc
@@ -20,11 +20,11 @@
     const std::string& local_cert,
     scoped_refptr<RsaKeyPair> key_pair,
     const std::string& access_code_hash,
-    const ValidatingAuthenticator::ValidationCallback& callback)
+    ValidatingAuthenticator::ValidationCallback callback)
     : local_cert_(local_cert),
       key_pair_(key_pair),
       access_code_hash_(access_code_hash),
-      validation_callback_(callback) {}
+      validation_callback_(std::move(callback)) {}
 
 It2MeHostAuthenticatorFactory::~It2MeHostAuthenticatorFactory() = default;
 
@@ -38,7 +38,7 @@
           nullptr));
 
   return std::make_unique<ValidatingAuthenticator>(
-      remote_jid, validation_callback_, std::move(authenticator));
+      remote_jid, std::move(validation_callback_), std::move(authenticator));
 }
 
 }  // namespace protocol
diff --git a/remoting/protocol/it2me_host_authenticator_factory.h b/remoting/protocol/it2me_host_authenticator_factory.h
index 1be5683..84dc45c 100644
--- a/remoting/protocol/it2me_host_authenticator_factory.h
+++ b/remoting/protocol/it2me_host_authenticator_factory.h
@@ -28,7 +28,7 @@
       const std::string& local_cert,
       scoped_refptr<RsaKeyPair> key_pair,
       const std::string& access_code,
-      const ValidatingAuthenticator::ValidationCallback& callback);
+      ValidatingAuthenticator::ValidationCallback callback);
   ~It2MeHostAuthenticatorFactory() override;
 
   // AuthenticatorFactory interface.
diff --git a/remoting/protocol/jingle_session.cc b/remoting/protocol/jingle_session.cc
index 676d5426..ffbdb2d 100644
--- a/remoting/protocol/jingle_session.cc
+++ b/remoting/protocol/jingle_session.cc
@@ -279,9 +279,10 @@
 
   DCHECK_EQ(authenticator_->state(), Authenticator::WAITING_MESSAGE);
   // |authenticator_| is owned, so Unretained() is safe here.
-  authenticator_->ProcessMessage(first_auth_message, base::Bind(
-      &JingleSession::ContinueAcceptIncomingConnection,
-      base::Unretained(this)));
+  authenticator_->ProcessMessage(
+      first_auth_message,
+      base::BindOnce(&JingleSession::ContinueAcceptIncomingConnection,
+                     base::Unretained(this)));
 }
 
 void JingleSession::ContinueAcceptIncomingConnection() {
@@ -587,8 +588,8 @@
 
   DCHECK(authenticator_->state() == Authenticator::WAITING_MESSAGE);
   authenticator_->ProcessMessage(
-      auth_message, base::Bind(&JingleSession::ProcessAuthenticationStep,
-                               base::Unretained(this)));
+      auth_message, base::BindOnce(&JingleSession::ProcessAuthenticationStep,
+                                   base::Unretained(this)));
 }
 
 void JingleSession::OnSessionInfo(std::unique_ptr<JingleMessage> message,
@@ -611,8 +612,9 @@
   reply_callback.Run(JingleMessageReply::NONE);
 
   authenticator_->ProcessMessage(
-      message->info.get(), base::Bind(&JingleSession::ProcessAuthenticationStep,
-                                      base::Unretained(this)));
+      message->info.get(),
+      base::BindOnce(&JingleSession::ProcessAuthenticationStep,
+                     base::Unretained(this)));
 }
 
 void JingleSession::OnTransportInfo(std::unique_ptr<JingleMessage> message,
diff --git a/remoting/protocol/negotiating_authenticator_base.cc b/remoting/protocol/negotiating_authenticator_base.cc
index 11e3c14..4d2b377 100644
--- a/remoting/protocol/negotiating_authenticator_base.cc
+++ b/remoting/protocol/negotiating_authenticator_base.cc
@@ -95,7 +95,7 @@
 
 void NegotiatingAuthenticatorBase::ProcessMessageInternal(
     const jingle_xmpp::XmlElement* message,
-    const base::Closure& resume_callback) {
+    base::OnceClosure resume_callback) {
   DCHECK_EQ(state_, PROCESSING_MESSAGE);
 
   if (current_authenticator_->state() == WAITING_MESSAGE) {
@@ -104,15 +104,16 @@
     // |current_authenticator_| is owned, so Unretained() is safe here.
     current_authenticator_->ProcessMessage(
         message, base::Bind(&NegotiatingAuthenticatorBase::UpdateState,
-                            base::Unretained(this), resume_callback));
+                            base::Unretained(this),
+                            base::Passed(std::move(resume_callback))));
   } else {
     // Otherwise, just discard the message.
-    UpdateState(resume_callback);
+    UpdateState(std::move(resume_callback));
   }
 }
 
 void NegotiatingAuthenticatorBase::UpdateState(
-    const base::Closure& resume_callback) {
+    base::OnceClosure resume_callback) {
   DCHECK_EQ(state_, PROCESSING_MESSAGE);
 
   // After the underlying authenticator finishes processing the message, the
@@ -127,7 +128,7 @@
   if (state_ == REJECTED)
     rejection_reason_ = current_authenticator_->rejection_reason();
 
-  resume_callback.Run();
+  std::move(resume_callback).Run();
 }
 
 std::unique_ptr<jingle_xmpp::XmlElement>
diff --git a/remoting/protocol/negotiating_authenticator_base.h b/remoting/protocol/negotiating_authenticator_base.h
index 67e1db6..0002c16 100644
--- a/remoting/protocol/negotiating_authenticator_base.h
+++ b/remoting/protocol/negotiating_authenticator_base.h
@@ -102,7 +102,7 @@
   // Calls |current_authenticator_| to process |message|, passing the supplied
   // |resume_callback|.
   void ProcessMessageInternal(const jingle_xmpp::XmlElement* message,
-                              const base::Closure& resume_callback);
+                              base::OnceClosure resume_callback);
 
  protected:
   friend class NegotiatingAuthenticatorTest;
@@ -127,7 +127,7 @@
 
   // Updates |state_| to reflect the current underlying authenticator state.
   // |resume_callback| is called after the state is updated.
-  void UpdateState(const base::Closure& resume_callback);
+  void UpdateState(base::OnceClosure resume_callback);
 
   // Gets the next message from |current_authenticator_|, if any, and fills in
   // the 'method' tag with |current_method_|.
diff --git a/remoting/protocol/negotiating_client_authenticator.cc b/remoting/protocol/negotiating_client_authenticator.cc
index ba9c00fc..dd0e93a 100644
--- a/remoting/protocol/negotiating_client_authenticator.cc
+++ b/remoting/protocol/negotiating_client_authenticator.cc
@@ -49,7 +49,7 @@
 
 void NegotiatingClientAuthenticator::ProcessMessage(
     const jingle_xmpp::XmlElement* message,
-    const base::Closure& resume_callback) {
+    base::OnceClosure resume_callback) {
   DCHECK_EQ(state(), WAITING_MESSAGE);
   state_ = PROCESSING_MESSAGE;
 
@@ -64,7 +64,7 @@
         std::find(methods_.begin(), methods_.end(), method) == methods_.end()) {
       state_ = REJECTED;
       rejection_reason_ = PROTOCOL_ERROR;
-      resume_callback.Run();
+      std::move(resume_callback).Run();
       return;
     }
 
@@ -72,14 +72,15 @@
     method_set_by_host_ = true;
 
     // Copy the message since the authenticator may process it asynchronously.
-    base::Closure callback = base::Bind(
-        &NegotiatingAuthenticatorBase::ProcessMessageInternal,
-        base::Unretained(this), base::Owned(new jingle_xmpp::XmlElement(*message)),
-        resume_callback);
-    CreateAuthenticatorForCurrentMethod(WAITING_MESSAGE, callback);
+    CreateAuthenticatorForCurrentMethod(
+        WAITING_MESSAGE,
+        base::BindOnce(&NegotiatingAuthenticatorBase::ProcessMessageInternal,
+                       base::Unretained(this),
+                       base::Owned(new jingle_xmpp::XmlElement(*message)),
+                       base::Passed(std::move(resume_callback))));
     return;
   }
-  ProcessMessageInternal(message, resume_callback);
+  ProcessMessageInternal(message, std::move(resume_callback));
 }
 
 std::unique_ptr<jingle_xmpp::XmlElement>
@@ -123,7 +124,7 @@
 
 void NegotiatingClientAuthenticator::CreateAuthenticatorForCurrentMethod(
     Authenticator::State preferred_initial_state,
-    const base::Closure& resume_callback) {
+    base::OnceClosure resume_callback) {
   DCHECK_EQ(state(), PROCESSING_MESSAGE);
   DCHECK(current_method_ != Method::INVALID);
   switch (current_method_) {
@@ -135,7 +136,7 @@
       current_authenticator_.reset(new ThirdPartyClientAuthenticator(
           base::Bind(&V2Authenticator::CreateForClient),
           config_.fetch_third_party_token_callback));
-      resume_callback.Run();
+      std::move(resume_callback).Run();
       break;
 
     case Method::THIRD_PARTY_SPAKE2_CURVE25519:
@@ -143,7 +144,7 @@
           base::Bind(&Spake2Authenticator::CreateForClient, local_id_,
                      remote_id_),
           config_.fetch_third_party_token_callback));
-      resume_callback.Run();
+      std::move(resume_callback).Run();
       break;
 
     case Method::PAIRED_SPAKE2_P224: {
@@ -151,7 +152,8 @@
           new PairingClientAuthenticator(
               config_, base::Bind(&V2Authenticator::CreateForClient));
       current_authenticator_ = base::WrapUnique(pairing_authenticator);
-      pairing_authenticator->Start(preferred_initial_state, resume_callback);
+      pairing_authenticator->Start(preferred_initial_state,
+                                   std::move(resume_callback));
       break;
     }
 
@@ -161,7 +163,8 @@
               config_, base::Bind(&Spake2Authenticator::CreateForClient,
                                   local_id_, remote_id_));
       current_authenticator_ = base::WrapUnique(pairing_authenticator);
-      pairing_authenticator->Start(preferred_initial_state, resume_callback);
+      pairing_authenticator->Start(preferred_initial_state,
+                                   std::move(resume_callback));
       break;
     }
 
@@ -173,7 +176,7 @@
           base::Bind(
               &NegotiatingClientAuthenticator::CreateSharedSecretAuthenticator,
               weak_factory_.GetWeakPtr(), preferred_initial_state,
-              resume_callback));
+              base::Passed(std::move(resume_callback))));
       break;
   }
 }
@@ -193,7 +196,7 @@
 
 void NegotiatingClientAuthenticator::CreateSharedSecretAuthenticator(
     Authenticator::State initial_state,
-    const base::Closure& resume_callback,
+    base::OnceClosure resume_callback,
     const std::string& shared_secret) {
   std::string shared_secret_hash =
       (current_method_ == Method::SHARED_SECRET_PLAIN_SPAKE2_P224)
@@ -207,7 +210,7 @@
     current_authenticator_ =
         V2Authenticator::CreateForClient(shared_secret_hash, initial_state);
   }
-  resume_callback.Run();
+  std::move(resume_callback).Run();
 }
 
 bool NegotiatingClientAuthenticator::is_paired() {
diff --git a/remoting/protocol/negotiating_client_authenticator.h b/remoting/protocol/negotiating_client_authenticator.h
index f138ebb..af00212 100644
--- a/remoting/protocol/negotiating_client_authenticator.h
+++ b/remoting/protocol/negotiating_client_authenticator.h
@@ -29,9 +29,9 @@
       const ClientAuthenticationConfig& config);
   ~NegotiatingClientAuthenticator() override;
 
-  // Overriden from Authenticator.
+  // NegotiatingAuthenticatorBase:
   void ProcessMessage(const jingle_xmpp::XmlElement* message,
-                      const base::Closure& resume_callback) override;
+                      base::OnceClosure resume_callback) override;
   std::unique_ptr<jingle_xmpp::XmlElement> GetNextMessage() override;
 
  private:
@@ -41,7 +41,7 @@
   // |resume_callback| is called after |current_authenticator_| is set.
   void CreateAuthenticatorForCurrentMethod(
       Authenticator::State preferred_initial_state,
-      const base::Closure& resume_callback);
+      base::OnceClosure resume_callback);
 
   // If possible, create a preferred authenticator ready to send an
   // initial message optimistically to the host. The host is free to
@@ -57,7 +57,7 @@
   // Creates a shared-secret authenticator in state |initial_state| with the
   // given |shared_secret|, then runs |resume_callback|.
   void CreateSharedSecretAuthenticator(Authenticator::State initial_state,
-                                       const base::Closure& resume_callback,
+                                       base::OnceClosure resume_callback,
                                        const std::string& shared_secret);
 
   bool is_paired();
diff --git a/remoting/protocol/negotiating_host_authenticator.cc b/remoting/protocol/negotiating_host_authenticator.cc
index 6191619..609f442f 100644
--- a/remoting/protocol/negotiating_host_authenticator.cc
+++ b/remoting/protocol/negotiating_host_authenticator.cc
@@ -79,7 +79,7 @@
 
 void NegotiatingHostAuthenticator::ProcessMessage(
     const jingle_xmpp::XmlElement* message,
-    const base::Closure& resume_callback) {
+    base::OnceClosure resume_callback) {
   DCHECK_EQ(state(), WAITING_MESSAGE);
   state_ = PROCESSING_MESSAGE;
 
@@ -95,7 +95,7 @@
   if (current_method_ != Method::INVALID && method != current_method_) {
     state_ = REJECTED;
     rejection_reason_ = PROTOCOL_ERROR;
-    resume_callback.Run();
+    std::move(resume_callback).Run();
     return;
   }
 
@@ -112,7 +112,7 @@
       // Message contains neither method nor supported-methods attributes.
       state_ = REJECTED;
       rejection_reason_ = PROTOCOL_ERROR;
-      resume_callback.Run();
+      std::move(resume_callback).Run();
       return;
     }
 
@@ -136,15 +136,16 @@
       // Failed to find a common auth method.
       state_ = REJECTED;
       rejection_reason_ = PROTOCOL_ERROR;
-      resume_callback.Run();
+      std::move(resume_callback).Run();
       return;
     }
 
     // Drop the current message because we've chosen a different method.
     current_method_ = method;
-    CreateAuthenticator(MESSAGE_READY,
-                        base::Bind(&NegotiatingHostAuthenticator::UpdateState,
-                                   base::Unretained(this), resume_callback));
+    CreateAuthenticator(
+        MESSAGE_READY,
+        base::BindOnce(&NegotiatingHostAuthenticator::UpdateState,
+                       base::Unretained(this), std::move(resume_callback)));
     return;
   }
 
@@ -155,15 +156,15 @@
     // Copy the message since the authenticator may process it asynchronously.
     CreateAuthenticator(
         WAITING_MESSAGE,
-        base::Bind(&NegotiatingAuthenticatorBase::ProcessMessageInternal,
-                   base::Unretained(this),
-                   base::Owned(new jingle_xmpp::XmlElement(*message)),
-                   resume_callback));
+        base::BindOnce(&NegotiatingAuthenticatorBase::ProcessMessageInternal,
+                       base::Unretained(this),
+                       base::Owned(new jingle_xmpp::XmlElement(*message)),
+                       std::move(resume_callback)));
     return;
   }
 
   // If the client is using the host's current method, just process the message.
-  ProcessMessageInternal(message, resume_callback);
+  ProcessMessageInternal(message, std::move(resume_callback));
 }
 
 std::unique_ptr<jingle_xmpp::XmlElement>
@@ -173,7 +174,7 @@
 
 void NegotiatingHostAuthenticator::CreateAuthenticator(
     Authenticator::State preferred_initial_state,
-    const base::Closure& resume_callback) {
+    base::OnceClosure resume_callback) {
   DCHECK(current_method_ != Method::INVALID);
 
   switch(current_method_) {
@@ -187,7 +188,7 @@
                      local_key_pair_),
           token_validator_factory_->CreateTokenValidator(local_id_,
                                                          remote_id_)));
-      resume_callback.Run();
+      std::move(resume_callback).Run();
       break;
 
     case Method::THIRD_PARTY_SPAKE2_CURVE25519:
@@ -196,7 +197,7 @@
                      local_cert_, local_key_pair_),
           token_validator_factory_->CreateTokenValidator(local_id_,
                                                          remote_id_)));
-      resume_callback.Run();
+      std::move(resume_callback).Run();
       break;
 
     case Method::PAIRED_SPAKE2_P224: {
@@ -207,7 +208,7 @@
               shared_secret_hash_);
       current_authenticator_.reset(pairing_authenticator);
       pairing_authenticator->Initialize(client_id_, preferred_initial_state,
-                                        resume_callback);
+                                        std::move(resume_callback));
       break;
     }
 
@@ -220,7 +221,7 @@
               shared_secret_hash_);
       current_authenticator_.reset(pairing_authenticator);
       pairing_authenticator->Initialize(client_id_, preferred_initial_state,
-                                        resume_callback);
+                                        std::move(resume_callback));
       break;
     }
 
@@ -228,7 +229,7 @@
       current_authenticator_ = Spake2Authenticator::CreateForHost(
           local_id_, remote_id_, local_cert_, local_key_pair_,
           shared_secret_hash_, preferred_initial_state);
-      resume_callback.Run();
+      std::move(resume_callback).Run();
       break;
 
     case Method::SHARED_SECRET_PLAIN_SPAKE2_P224:
@@ -236,7 +237,7 @@
       current_authenticator_ = V2Authenticator::CreateForHost(
           local_cert_, local_key_pair_, shared_secret_hash_,
           preferred_initial_state);
-      resume_callback.Run();
+      std::move(resume_callback).Run();
       break;
   }
 }
diff --git a/remoting/protocol/negotiating_host_authenticator.h b/remoting/protocol/negotiating_host_authenticator.h
index 9ede079..c02d91e2 100644
--- a/remoting/protocol/negotiating_host_authenticator.h
+++ b/remoting/protocol/negotiating_host_authenticator.h
@@ -49,9 +49,9 @@
       scoped_refptr<RsaKeyPair> key_pair,
       scoped_refptr<TokenValidatorFactory> token_validator_factory);
 
-  // Overriden from Authenticator.
+  // NegotiatingAuthenticatorBase:
   void ProcessMessage(const jingle_xmpp::XmlElement* message,
-                      const base::Closure& resume_callback) override;
+                      base::OnceClosure resume_callback) override;
   std::unique_ptr<jingle_xmpp::XmlElement> GetNextMessage() override;
 
  private:
@@ -64,7 +64,7 @@
   // |current_authenticator_|. Authenticators that can be started in either
   // state will be created in |preferred_initial_state|.
   void CreateAuthenticator(Authenticator::State preferred_initial_state,
-                           const base::Closure& resume_callback);
+                           base::OnceClosure resume_callback);
 
   std::string local_id_;
   std::string remote_id_;
diff --git a/remoting/protocol/pairing_authenticator_base.cc b/remoting/protocol/pairing_authenticator_base.cc
index 0db2bc2..f73dcbc 100644
--- a/remoting/protocol/pairing_authenticator_base.cc
+++ b/remoting/protocol/pairing_authenticator_base.cc
@@ -45,7 +45,7 @@
 
 void PairingAuthenticatorBase::ProcessMessage(
     const jingle_xmpp::XmlElement* message,
-    const base::Closure& resume_callback) {
+    base::OnceClosure resume_callback) {
   DCHECK_EQ(state(), WAITING_MESSAGE);
 
   // The client authenticator creates the underlying authenticator in the ctor
@@ -58,10 +58,11 @@
     using_paired_secret_ = false;
     spake2_authenticator_.reset();
     CreateSpakeAuthenticatorWithPin(
-        WAITING_MESSAGE, base::Bind(&PairingAuthenticatorBase::ProcessMessage,
-                                    weak_factory_.GetWeakPtr(),
-                                    base::Owned(new jingle_xmpp::XmlElement(*message)),
-                                    resume_callback));
+        WAITING_MESSAGE,
+        base::Bind(&PairingAuthenticatorBase::ProcessMessage,
+                   weak_factory_.GetWeakPtr(),
+                   base::Owned(new jingle_xmpp::XmlElement(*message)),
+                   base::Passed(std::move(resume_callback))));
     return;
   }
 
@@ -72,7 +73,8 @@
   spake2_authenticator_->ProcessMessage(
       message,
       base::Bind(&PairingAuthenticatorBase::CheckForFailedSpakeExchange,
-                 weak_factory_.GetWeakPtr(), resume_callback));
+                 weak_factory_.GetWeakPtr(),
+                 base::Passed(std::move(resume_callback))));
 }
 
 std::unique_ptr<jingle_xmpp::XmlElement> PairingAuthenticatorBase::GetNextMessage() {
@@ -114,7 +116,7 @@
 }
 
 void PairingAuthenticatorBase::CheckForFailedSpakeExchange(
-    const base::Closure& resume_callback) {
+    base::OnceClosure resume_callback) {
   // If the SPAKE exchange failed due to invalid credentials, and those
   // credentials were the paired secret, then notify the peer that the
   // PIN-less connection failed and retry using the PIN.
@@ -124,11 +126,11 @@
     using_paired_secret_ = false;
     error_message_ = "invalid-shared-secret";
     spake2_authenticator_.reset();
-    CreateSpakeAuthenticatorWithPin(MESSAGE_READY, resume_callback);
+    CreateSpakeAuthenticatorWithPin(MESSAGE_READY, std::move(resume_callback));
     return;
   }
 
-  resume_callback.Run();
+  std::move(resume_callback).Run();
 }
 
 }  // namespace protocol
diff --git a/remoting/protocol/pairing_authenticator_base.h b/remoting/protocol/pairing_authenticator_base.h
index 6e9bbc7..7c463f7 100644
--- a/remoting/protocol/pairing_authenticator_base.h
+++ b/remoting/protocol/pairing_authenticator_base.h
@@ -47,7 +47,7 @@
   bool started() const override;
   RejectionReason rejection_reason() const override;
   void ProcessMessage(const jingle_xmpp::XmlElement* message,
-                      const base::Closure& resume_callback) override;
+                      base::OnceClosure resume_callback) override;
   std::unique_ptr<jingle_xmpp::XmlElement> GetNextMessage() override;
   const std::string& GetAuthKey() const override;
   std::unique_ptr<ChannelAuthenticator> CreateChannelAuthenticator()
@@ -58,7 +58,7 @@
   // for the PIN first if necessary.
   virtual void CreateSpakeAuthenticatorWithPin(
       State initial_state,
-      const base::Closure& resume_callback) = 0;
+      base::OnceClosure resume_callback) = 0;
 
   // A non-fatal error message that derived classes should set in order to
   // cause the peer to be notified that pairing has failed and that it should
@@ -78,7 +78,7 @@
   // Helper methods for ProcessMessage() and GetNextMessage().
   void MaybeAddErrorMessage(jingle_xmpp::XmlElement* message);
   bool HasErrorMessage(const jingle_xmpp::XmlElement* message) const;
-  void CheckForFailedSpakeExchange(const base::Closure& resume_callback);
+  void CheckForFailedSpakeExchange(base::OnceClosure resume_callback);
 
   base::WeakPtrFactory<PairingAuthenticatorBase> weak_factory_{this};
 
diff --git a/remoting/protocol/pairing_client_authenticator.cc b/remoting/protocol/pairing_client_authenticator.cc
index c5cc8ba..83db0f69 100644
--- a/remoting/protocol/pairing_client_authenticator.cc
+++ b/remoting/protocol/pairing_client_authenticator.cc
@@ -23,17 +23,17 @@
 PairingClientAuthenticator::~PairingClientAuthenticator() = default;
 
 void PairingClientAuthenticator::Start(State initial_state,
-                                       const base::Closure& resume_callback) {
+                                       base::OnceClosure resume_callback) {
   if (client_auth_config_.pairing_client_id.empty() ||
       client_auth_config_.pairing_secret.empty()) {
     // Send pairing error to make it clear that PIN is being used instead of
     // pairing secret.
     error_message_ = "not-paired";
     using_paired_secret_ = false;
-    CreateSpakeAuthenticatorWithPin(initial_state, resume_callback);
+    CreateSpakeAuthenticatorWithPin(initial_state, std::move(resume_callback));
   } else {
     StartPaired(initial_state);
-    resume_callback.Run();
+    std::move(resume_callback).Run();
   }
 }
 
@@ -54,25 +54,24 @@
 
 void PairingClientAuthenticator::CreateSpakeAuthenticatorWithPin(
     State initial_state,
-    const base::Closure& resume_callback) {
+    base::OnceClosure resume_callback) {
   DCHECK(!waiting_for_pin_);
   waiting_for_pin_ = true;
   client_auth_config_.fetch_secret_callback.Run(
-      true,
-      base::Bind(&PairingClientAuthenticator::OnPinFetched,
-                 weak_factory_.GetWeakPtr(), initial_state, resume_callback));
+      true, base::Bind(&PairingClientAuthenticator::OnPinFetched,
+                       weak_factory_.GetWeakPtr(), initial_state,
+                       base::Passed(std::move(resume_callback))));
 }
 
-void PairingClientAuthenticator::OnPinFetched(
-    State initial_state,
-    const base::Closure& resume_callback,
-    const std::string& pin) {
+void PairingClientAuthenticator::OnPinFetched(State initial_state,
+                                              base::OnceClosure resume_callback,
+                                              const std::string& pin) {
   DCHECK(waiting_for_pin_);
   DCHECK(!spake2_authenticator_);
   waiting_for_pin_ = false;
   spake2_authenticator_ = create_base_authenticator_callback_.Run(
       GetSharedSecretHash(client_auth_config_.host_id, pin), initial_state);
-  resume_callback.Run();
+  std::move(resume_callback).Run();
 }
 
 }  // namespace protocol
diff --git a/remoting/protocol/pairing_client_authenticator.h b/remoting/protocol/pairing_client_authenticator.h
index 61dd5bb..58fb63b4 100644
--- a/remoting/protocol/pairing_client_authenticator.h
+++ b/remoting/protocol/pairing_client_authenticator.h
@@ -28,7 +28,7 @@
   // initialize the authenticator synchronously in
   // NegotiatingClientAuthentitcator, while Start() may be executed
   // asynchronously to fetch the PIN.
-  void Start(State initial_state, const base::Closure& resume_callback);
+  void Start(State initial_state, base::OnceClosure resume_callback);
   void StartPaired(State initial_state);
 
   // Authenticator interface.
@@ -38,10 +38,10 @@
   // PairingAuthenticatorBase overrides.
   void CreateSpakeAuthenticatorWithPin(
       State initial_state,
-      const base::Closure& resume_callback) override;
+      base::OnceClosure resume_callback) override;
 
   void OnPinFetched(State initial_state,
-                    const base::Closure& resume_callback,
+                    base::OnceClosure resume_callback,
                     const std::string& pin);
 
   ClientAuthenticationConfig client_auth_config_;
diff --git a/remoting/protocol/pairing_host_authenticator.cc b/remoting/protocol/pairing_host_authenticator.cc
index 3ee4b32b..40ef17a5 100644
--- a/remoting/protocol/pairing_host_authenticator.cc
+++ b/remoting/protocol/pairing_host_authenticator.cc
@@ -24,7 +24,7 @@
 void PairingHostAuthenticator::Initialize(
     const std::string& client_id,
     Authenticator::State preferred_initial_state,
-    const base::Closure& resume_callback) {
+    base::OnceClosure resume_callback) {
   DCHECK(!spake2_authenticator_);
 
   if (client_id.empty()) {
@@ -32,7 +32,7 @@
     error_message_ = "client-id-unknown";
     spake2_authenticator_ =
         create_base_authenticator_callback_.Run(pin_, MESSAGE_READY);
-    resume_callback.Run();
+    std::move(resume_callback).Run();
     return;
   }
 
@@ -41,7 +41,7 @@
   pairing_registry_->GetPairing(
       client_id, base::Bind(&PairingHostAuthenticator::InitializeWithPairing,
                             weak_factory_.GetWeakPtr(), preferred_initial_state,
-                            resume_callback));
+                            base::Passed(std::move(resume_callback))));
 }
 
 PairingHostAuthenticator::~PairingHostAuthenticator() = default;
@@ -65,15 +65,15 @@
 
 void PairingHostAuthenticator::CreateSpakeAuthenticatorWithPin(
     State initial_state,
-    const base::Closure& resume_callback) {
+    base::OnceClosure resume_callback) {
   spake2_authenticator_ =
       create_base_authenticator_callback_.Run(pin_, initial_state);
-  resume_callback.Run();
+  std::move(resume_callback).Run();
 }
 
 void PairingHostAuthenticator::InitializeWithPairing(
     Authenticator::State preferred_initial_state,
-    const base::Closure& resume_callback,
+    base::OnceClosure resume_callback,
     PairingRegistry::Pairing pairing) {
   DCHECK(waiting_for_paired_secret_);
   waiting_for_paired_secret_ = false;
@@ -90,7 +90,7 @@
     spake2_authenticator_ = create_base_authenticator_callback_.Run(
         pairing_secret, preferred_initial_state);
   }
-  resume_callback.Run();
+  std::move(resume_callback).Run();
 }
 
 }  // namespace protocol
diff --git a/remoting/protocol/pairing_host_authenticator.h b/remoting/protocol/pairing_host_authenticator.h
index 4c72b2ad..37ff943 100644
--- a/remoting/protocol/pairing_host_authenticator.h
+++ b/remoting/protocol/pairing_host_authenticator.h
@@ -27,7 +27,7 @@
   // |preferred_initial_state|.
   void Initialize(const std::string& client_id,
                   Authenticator::State preferred_initial_state,
-                  const base::Closure& resume_callback);
+                  base::OnceClosure resume_callback);
 
   // Authenticator interface.
   State state() const override;
@@ -37,12 +37,12 @@
   // PairingAuthenticatorBase overrides.
   void CreateSpakeAuthenticatorWithPin(
       State initial_state,
-      const base::Closure& resume_callback) override;
+      base::OnceClosure resume_callback) override;
 
   // Continue initializing once the pairing information for the client id has
   // been received.
   void InitializeWithPairing(Authenticator::State preferred_initial_state,
-                             const base::Closure& resume_callback,
+                             base::OnceClosure resume_callback,
                              PairingRegistry::Pairing pairing);
 
   // Protocol state.
diff --git a/remoting/protocol/protocol_mock_objects.h b/remoting/protocol/protocol_mock_objects.h
index 464cba4..561a25ce 100644
--- a/remoting/protocol/protocol_mock_objects.h
+++ b/remoting/protocol/protocol_mock_objects.h
@@ -53,7 +53,7 @@
   MOCK_CONST_METHOD0(CreateChannelAuthenticatorPtr, ChannelAuthenticator*());
   MOCK_METHOD2(ProcessMessage,
                void(const jingle_xmpp::XmlElement* message,
-                    const base::Closure& resume_callback));
+                    base::OnceClosure resume_callback));
   MOCK_METHOD0(GetNextMessagePtr, jingle_xmpp::XmlElement*());
 
   std::unique_ptr<ChannelAuthenticator> CreateChannelAuthenticator()
diff --git a/remoting/protocol/rejecting_authenticator.cc b/remoting/protocol/rejecting_authenticator.cc
index 05c3dc7c..dd22249 100644
--- a/remoting/protocol/rejecting_authenticator.cc
+++ b/remoting/protocol/rejecting_authenticator.cc
@@ -33,10 +33,10 @@
 
 void RejectingAuthenticator::ProcessMessage(
     const jingle_xmpp::XmlElement* message,
-    const base::Closure& resume_callback) {
+    base::OnceClosure resume_callback) {
   DCHECK_EQ(state_, WAITING_MESSAGE);
   state_ = REJECTED;
-  resume_callback.Run();
+  std::move(resume_callback).Run();
 }
 
 std::unique_ptr<jingle_xmpp::XmlElement> RejectingAuthenticator::GetNextMessage() {
diff --git a/remoting/protocol/rejecting_authenticator.h b/remoting/protocol/rejecting_authenticator.h
index 8e17bcb..fbfac82c 100644
--- a/remoting/protocol/rejecting_authenticator.h
+++ b/remoting/protocol/rejecting_authenticator.h
@@ -24,7 +24,7 @@
   bool started() const override;
   RejectionReason rejection_reason() const override;
   void ProcessMessage(const jingle_xmpp::XmlElement* message,
-                      const base::Closure& resume_callback) override;
+                      base::OnceClosure resume_callback) override;
   std::unique_ptr<jingle_xmpp::XmlElement> GetNextMessage() override;
   const std::string& GetAuthKey() const override;
   std::unique_ptr<ChannelAuthenticator> CreateChannelAuthenticator()
diff --git a/remoting/protocol/spake2_authenticator.cc b/remoting/protocol/spake2_authenticator.cc
index 40528c9..49a64d6 100644
--- a/remoting/protocol/spake2_authenticator.cc
+++ b/remoting/protocol/spake2_authenticator.cc
@@ -151,9 +151,9 @@
 }
 
 void Spake2Authenticator::ProcessMessage(const jingle_xmpp::XmlElement* message,
-                                         const base::Closure& resume_callback) {
+                                         base::OnceClosure resume_callback) {
   ProcessMessageInternal(message);
-  resume_callback.Run();
+  std::move(resume_callback).Run();
 }
 
 void Spake2Authenticator::ProcessMessageInternal(
diff --git a/remoting/protocol/spake2_authenticator.h b/remoting/protocol/spake2_authenticator.h
index 78a204a..30d4122 100644
--- a/remoting/protocol/spake2_authenticator.h
+++ b/remoting/protocol/spake2_authenticator.h
@@ -47,7 +47,7 @@
   bool started() const override;
   RejectionReason rejection_reason() const override;
   void ProcessMessage(const jingle_xmpp::XmlElement* message,
-                      const base::Closure& resume_callback) override;
+                      base::OnceClosure resume_callback) override;
   std::unique_ptr<jingle_xmpp::XmlElement> GetNextMessage() override;
   const std::string& GetAuthKey() const override;
   std::unique_ptr<ChannelAuthenticator> CreateChannelAuthenticator()
diff --git a/remoting/protocol/third_party_authenticator_base.cc b/remoting/protocol/third_party_authenticator_base.cc
index 1279b23..8a76f83 100644
--- a/remoting/protocol/third_party_authenticator_base.cc
+++ b/remoting/protocol/third_party_authenticator_base.cc
@@ -54,16 +54,16 @@
 
 void ThirdPartyAuthenticatorBase::ProcessMessage(
     const jingle_xmpp::XmlElement* message,
-    const base::Closure& resume_callback) {
+    base::OnceClosure resume_callback) {
   DCHECK_EQ(state(), WAITING_MESSAGE);
 
   if (token_state_ == WAITING_MESSAGE) {
-    ProcessTokenMessage(message, resume_callback);
+    ProcessTokenMessage(message, std::move(resume_callback));
   } else {
     DCHECK_EQ(token_state_, ACCEPTED);
     DCHECK(underlying_);
     DCHECK_EQ(underlying_->state(), WAITING_MESSAGE);
-    underlying_->ProcessMessage(message, resume_callback);
+    underlying_->ProcessMessage(message, std::move(resume_callback));
   }
 }
 
diff --git a/remoting/protocol/third_party_authenticator_base.h b/remoting/protocol/third_party_authenticator_base.h
index ef342a7..63548fe 100644
--- a/remoting/protocol/third_party_authenticator_base.h
+++ b/remoting/protocol/third_party_authenticator_base.h
@@ -40,7 +40,7 @@
   bool started() const override;
   RejectionReason rejection_reason() const override;
   void ProcessMessage(const jingle_xmpp::XmlElement* message,
-                      const base::Closure& resume_callback) override;
+                      base::OnceClosure resume_callback) override;
   std::unique_ptr<jingle_xmpp::XmlElement> GetNextMessage() override;
   const std::string& GetAuthKey() const override;
   std::unique_ptr<ChannelAuthenticator> CreateChannelAuthenticator()
@@ -55,14 +55,12 @@
   explicit ThirdPartyAuthenticatorBase(State initial_state);
 
   // Gives the message to the underlying authenticator for processing.
-  void ProcessUnderlyingMessage(
-      const jingle_xmpp::XmlElement* message,
-      const base::Closure& resume_callback);
+  void ProcessUnderlyingMessage(const jingle_xmpp::XmlElement* message,
+                                base::OnceClosure resume_callback);
 
   // Processes the token-related elements of the message.
-  virtual void ProcessTokenMessage(
-      const jingle_xmpp::XmlElement* message,
-      const base::Closure& resume_callback) = 0;
+  virtual void ProcessTokenMessage(const jingle_xmpp::XmlElement* message,
+                                   base::OnceClosure resume_callback) = 0;
 
   // Adds the token related XML elements to the message.
   virtual void AddTokenElements(jingle_xmpp::XmlElement* message) = 0;
diff --git a/remoting/protocol/third_party_authenticator_unittest.cc b/remoting/protocol/third_party_authenticator_unittest.cc
index d3bddc7..c63343d 100644
--- a/remoting/protocol/third_party_authenticator_unittest.cc
+++ b/remoting/protocol/third_party_authenticator_unittest.cc
@@ -76,9 +76,9 @@
 
     void ValidateThirdPartyToken(
         const std::string& token,
-        const TokenValidatedCallback& token_validated_callback) override {
+        TokenValidatedCallback token_validated_callback) override {
       ASSERT_FALSE(token_validated_callback.is_null());
-      on_token_validated_ = token_validated_callback;
+      on_token_validated_ = std::move(token_validated_callback);
     }
 
     void OnTokenValidated(const std::string& shared_secret) {
@@ -93,7 +93,8 @@
    private:
     GURL token_url_;
     std::string token_scope_;
-    base::Callback<void(const std::string& shared_secret)> on_token_validated_;
+    base::OnceCallback<void(const std::string& shared_secret)>
+        on_token_validated_;
   };
 
  public:
diff --git a/remoting/protocol/third_party_client_authenticator.cc b/remoting/protocol/third_party_client_authenticator.cc
index f5218d5..486bfd4 100644
--- a/remoting/protocol/third_party_client_authenticator.cc
+++ b/remoting/protocol/third_party_client_authenticator.cc
@@ -29,7 +29,7 @@
 
 void ThirdPartyClientAuthenticator::ProcessTokenMessage(
     const jingle_xmpp::XmlElement* message,
-    const base::Closure& resume_callback) {
+    base::OnceClosure resume_callback) {
   std::string token_url = message->TextNamed(kTokenUrlTag);
   std::string token_scope = message->TextNamed(kTokenScopeTag);
 
@@ -38,7 +38,7 @@
         "missing token verification URL or scope.";
     token_state_ = REJECTED;
     rejection_reason_ = PROTOCOL_ERROR;
-    resume_callback.Run();
+    std::move(resume_callback).Run();
     return;
   }
 
@@ -47,7 +47,8 @@
   fetch_token_callback_.Run(
       token_url, token_scope,
       base::Bind(&ThirdPartyClientAuthenticator::OnThirdPartyTokenFetched,
-                 weak_factory_.GetWeakPtr(), resume_callback));
+                 weak_factory_.GetWeakPtr(),
+                 base::Passed(std::move(resume_callback))));
 }
 
 void ThirdPartyClientAuthenticator::AddTokenElements(
@@ -62,7 +63,7 @@
 }
 
 void ThirdPartyClientAuthenticator::OnThirdPartyTokenFetched(
-    const base::Closure& resume_callback,
+    base::OnceClosure resume_callback,
     const std::string& third_party_token,
     const std::string& shared_secret) {
   token_ = third_party_token;
@@ -74,7 +75,7 @@
     underlying_ =
         create_base_authenticator_callback_.Run(shared_secret, MESSAGE_READY);
   }
-  resume_callback.Run();
+  std::move(resume_callback).Run();
 }
 
 }  // namespace protocol
diff --git a/remoting/protocol/third_party_client_authenticator.h b/remoting/protocol/third_party_client_authenticator.h
index 01a9c6f..436b4167 100644
--- a/remoting/protocol/third_party_client_authenticator.h
+++ b/remoting/protocol/third_party_client_authenticator.h
@@ -38,11 +38,11 @@
  protected:
   // ThirdPartyAuthenticator implementation.
   void ProcessTokenMessage(const jingle_xmpp::XmlElement* message,
-                           const base::Closure& resume_callback) override;
+                           base::OnceClosure resume_callback) override;
   void AddTokenElements(jingle_xmpp::XmlElement* message) override;
 
  private:
-  void OnThirdPartyTokenFetched(const base::Closure& resume_callback,
+  void OnThirdPartyTokenFetched(base::OnceClosure resume_callback,
                                 const std::string& third_party_token,
                                 const std::string& shared_secret);
 
diff --git a/remoting/protocol/third_party_host_authenticator.cc b/remoting/protocol/third_party_host_authenticator.cc
index 2aa4601..73d2d37 100644
--- a/remoting/protocol/third_party_host_authenticator.cc
+++ b/remoting/protocol/third_party_host_authenticator.cc
@@ -28,14 +28,14 @@
 
 void ThirdPartyHostAuthenticator::ProcessTokenMessage(
     const jingle_xmpp::XmlElement* message,
-    const base::Closure& resume_callback) {
+    base::OnceClosure resume_callback) {
   // Host has already sent the URL and expects a token from the client.
   std::string token = message->TextNamed(kTokenTag);
   if (token.empty()) {
     LOG(ERROR) << "Third-party authentication protocol error: missing token.";
     token_state_ = REJECTED;
     rejection_reason_ = PROTOCOL_ERROR;
-    resume_callback.Run();
+    std::move(resume_callback).Run();
     return;
   }
 
@@ -45,11 +45,12 @@
   // message into the callback, so that OnThirdPartyTokenValidated can give it
   // to the underlying SPAKE authenticator that will be created.
   // |token_validator_| is owned, so Unretained() is safe here.
-  token_validator_->ValidateThirdPartyToken(token, base::Bind(
-          &ThirdPartyHostAuthenticator::OnThirdPartyTokenValidated,
-          base::Unretained(this),
-          base::Owned(new jingle_xmpp::XmlElement(*message)),
-          resume_callback));
+  token_validator_->ValidateThirdPartyToken(
+      token,
+      base::BindOnce(&ThirdPartyHostAuthenticator::OnThirdPartyTokenValidated,
+                     base::Unretained(this),
+                     base::Owned(new jingle_xmpp::XmlElement(*message)),
+                     std::move(resume_callback)));
 }
 
 void ThirdPartyHostAuthenticator::AddTokenElements(
@@ -71,12 +72,12 @@
 
 void ThirdPartyHostAuthenticator::OnThirdPartyTokenValidated(
     const jingle_xmpp::XmlElement* message,
-    const base::Closure& resume_callback,
+    base::OnceClosure resume_callback,
     const std::string& shared_secret) {
   if (shared_secret.empty()) {
     token_state_ = REJECTED;
     rejection_reason_ = INVALID_CREDENTIALS;
-    resume_callback.Run();
+    std::move(resume_callback).Run();
     return;
   }
 
@@ -84,7 +85,7 @@
   token_state_ = ACCEPTED;
   underlying_ =
       create_base_authenticator_callback_.Run(shared_secret, WAITING_MESSAGE);
-  underlying_->ProcessMessage(message, resume_callback);
+  underlying_->ProcessMessage(message, std::move(resume_callback));
 }
 
 }  // namespace protocol
diff --git a/remoting/protocol/third_party_host_authenticator.h b/remoting/protocol/third_party_host_authenticator.h
index 2c7677a..8162e25 100644
--- a/remoting/protocol/third_party_host_authenticator.h
+++ b/remoting/protocol/third_party_host_authenticator.h
@@ -38,12 +38,12 @@
  protected:
   // ThirdPartyAuthenticator implementation.
   void ProcessTokenMessage(const jingle_xmpp::XmlElement* message,
-                           const base::Closure& resume_callback) override;
+                           base::OnceClosure resume_callback) override;
   void AddTokenElements(jingle_xmpp::XmlElement* message) override;
 
  private:
   void OnThirdPartyTokenValidated(const jingle_xmpp::XmlElement* message,
-                                  const base::Closure& resume_callback,
+                                  base::OnceClosure resume_callback,
                                   const std::string& shared_secret);
 
   CreateBaseAuthenticatorCallback create_base_authenticator_callback_;
diff --git a/remoting/protocol/token_validator.h b/remoting/protocol/token_validator.h
index f6250113..703fafe 100644
--- a/remoting/protocol/token_validator.h
+++ b/remoting/protocol/token_validator.h
@@ -25,10 +25,10 @@
   // authentication finishes. |shared_secret| should be used by the host to
   // create a V2Authenticator. In case of failure, the callback is called with
   // an empty |shared_secret|.
-  typedef base::Callback<void(
-      const std::string& shared_secret)> TokenValidatedCallback;
+  typedef base::OnceCallback<void(const std::string& shared_secret)>
+      TokenValidatedCallback;
 
-  virtual ~TokenValidator() {}
+  virtual ~TokenValidator() = default;
 
   // Validates |token| with the server and exchanges it for a |shared_secret|.
   // |token_validated_callback| is called when the host authentication ends,
@@ -36,7 +36,7 @@
   // The request is canceled if this object is destroyed.
   virtual void ValidateThirdPartyToken(
       const std::string& token,
-      const TokenValidatedCallback& token_validated_callback) = 0;
+      TokenValidatedCallback token_validated_callback) = 0;
 
   // URL sent to the client, to be used by its |TokenFetcher| to get a token.
   virtual const GURL& token_url() const = 0;
diff --git a/remoting/protocol/v2_authenticator.cc b/remoting/protocol/v2_authenticator.cc
index 0cfee2d..419ea07b 100644
--- a/remoting/protocol/v2_authenticator.cc
+++ b/remoting/protocol/v2_authenticator.cc
@@ -84,9 +84,9 @@
 }
 
 void V2Authenticator::ProcessMessage(const jingle_xmpp::XmlElement* message,
-                                     const base::Closure& resume_callback) {
+                                     base::OnceClosure resume_callback) {
   ProcessMessageInternal(message);
-  resume_callback.Run();
+  std::move(resume_callback).Run();
 }
 
 void V2Authenticator::ProcessMessageInternal(const jingle_xmpp::XmlElement* message) {
diff --git a/remoting/protocol/v2_authenticator.h b/remoting/protocol/v2_authenticator.h
index a87483d9..d78cb40 100644
--- a/remoting/protocol/v2_authenticator.h
+++ b/remoting/protocol/v2_authenticator.h
@@ -43,7 +43,7 @@
   bool started() const override;
   RejectionReason rejection_reason() const override;
   void ProcessMessage(const jingle_xmpp::XmlElement* message,
-                      const base::Closure& resume_callback) override;
+                      base::OnceClosure resume_callback) override;
   std::unique_ptr<jingle_xmpp::XmlElement> GetNextMessage() override;
   const std::string& GetAuthKey() const override;
   std::unique_ptr<ChannelAuthenticator> CreateChannelAuthenticator()
diff --git a/remoting/protocol/validating_authenticator.cc b/remoting/protocol/validating_authenticator.cc
index 359b842..83a246f0 100644
--- a/remoting/protocol/validating_authenticator.cc
+++ b/remoting/protocol/validating_authenticator.cc
@@ -23,10 +23,10 @@
 
 ValidatingAuthenticator::ValidatingAuthenticator(
     const std::string& remote_jid,
-    const ValidationCallback& validation_callback,
+    ValidationCallback validation_callback,
     std::unique_ptr<Authenticator> current_authenticator)
     : remote_jid_(remote_jid),
-      validation_callback_(validation_callback),
+      validation_callback_(std::move(validation_callback)),
       current_authenticator_(std::move(current_authenticator)) {
   DCHECK(!remote_jid_.empty());
   DCHECK(validation_callback_);
@@ -59,13 +59,14 @@
 
 void ValidatingAuthenticator::ProcessMessage(
     const jingle_xmpp::XmlElement* message,
-    const base::Closure& resume_callback) {
+    base::OnceClosure resume_callback) {
   DCHECK_EQ(state_, WAITING_MESSAGE);
   state_ = PROCESSING_MESSAGE;
 
   current_authenticator_->ProcessMessage(
       message, base::Bind(&ValidatingAuthenticator::UpdateState,
-                          weak_factory_.GetWeakPtr(), resume_callback));
+                          weak_factory_.GetWeakPtr(),
+                          base::Passed(std::move(resume_callback))));
 }
 
 std::unique_ptr<jingle_xmpp::XmlElement> ValidatingAuthenticator::GetNextMessage() {
@@ -82,14 +83,14 @@
   return result;
 }
 
-void ValidatingAuthenticator::OnValidateComplete(const base::Closure& callback,
+void ValidatingAuthenticator::OnValidateComplete(base::OnceClosure callback,
                                                  Result validation_result) {
   // Map |rejection_reason_| to a known reason, set |state_| to REJECTED and
   // notify the listener of the connection error via the callback.
   switch (validation_result) {
     case Result::SUCCESS:
       state_ = ACCEPTED;
-      callback.Run();
+      std::move(callback).Run();
       return;
 
     case Result::ERROR_INVALID_CREDENTIALS:
@@ -110,11 +111,10 @@
   }
 
   state_ = Authenticator::REJECTED;
-  callback.Run();
+  std::move(callback).Run();
 }
 
-void ValidatingAuthenticator::UpdateState(
-    const base::Closure& resume_callback) {
+void ValidatingAuthenticator::UpdateState(base::OnceClosure resume_callback) {
   DCHECK_EQ(state_, PROCESSING_MESSAGE);
 
   // Update our current state before running |resume_callback|.
@@ -129,11 +129,13 @@
 
   if (state_ == ACCEPTED) {
     state_ = PROCESSING_MESSAGE;
-    validation_callback_.Run(
-        remote_jid_, base::Bind(&ValidatingAuthenticator::OnValidateComplete,
-                                weak_factory_.GetWeakPtr(), resume_callback));
+    std::move(validation_callback_)
+        .Run(remote_jid_,
+             base::Bind(&ValidatingAuthenticator::OnValidateComplete,
+                        weak_factory_.GetWeakPtr(),
+                        base::Passed(std::move(resume_callback))));
   } else {
-    resume_callback.Run();
+    std::move(resume_callback).Run();
   }
 }
 
diff --git a/remoting/protocol/validating_authenticator.h b/remoting/protocol/validating_authenticator.h
index 198324a7..ecaa4e2e 100644
--- a/remoting/protocol/validating_authenticator.h
+++ b/remoting/protocol/validating_authenticator.h
@@ -31,14 +31,14 @@
     ERROR_TOO_MANY_CONNECTIONS
   };
 
-  typedef base::Callback<void(Result validation_result)> ResultCallback;
+  typedef base::OnceCallback<void(Result validation_result)> ResultCallback;
 
-  typedef base::Callback<void(const std::string& remote_jid,
-                              const ResultCallback& callback)>
+  typedef base::OnceCallback<void(const std::string& remote_jid,
+                                  ResultCallback callback)>
       ValidationCallback;
 
   ValidatingAuthenticator(const std::string& remote_jid,
-                          const ValidationCallback& validation_callback,
+                          ValidationCallback validation_callback,
                           std::unique_ptr<Authenticator> current_authenticator);
   ~ValidatingAuthenticator() override;
 
@@ -50,17 +50,17 @@
   std::unique_ptr<ChannelAuthenticator> CreateChannelAuthenticator()
       const override;
   void ProcessMessage(const jingle_xmpp::XmlElement* message,
-                      const base::Closure& resume_callback) override;
+                      base::OnceClosure resume_callback) override;
   std::unique_ptr<jingle_xmpp::XmlElement> GetNextMessage() override;
 
  private:
   // Checks |result|.  If the connection was rejected, |state_| and
   // |rejection_reason_| are updated.  |callback| is always run.
-  void OnValidateComplete(const base::Closure& callback, Result result);
+  void OnValidateComplete(base::OnceClosure callback, Result result);
 
   // Updates |state_| to reflect the current underlying authenticator state.
   // |resume_callback| is called after the state is updated.
-  void UpdateState(const base::Closure& resume_callback);
+  void UpdateState(base::OnceClosure resume_callback);
 
   // The JID of the remote user.
   std::string remote_jid_;
diff --git a/remoting/protocol/validating_authenticator_unittest.cc b/remoting/protocol/validating_authenticator_unittest.cc
index 6677901..4379c3f0 100644
--- a/remoting/protocol/validating_authenticator_unittest.cc
+++ b/remoting/protocol/validating_authenticator_unittest.cc
@@ -38,7 +38,7 @@
 ACTION_TEMPLATE(InvokeCallbackArgument,
                 HAS_1_TEMPLATE_PARAMS(int, k),
                 AND_0_VALUE_PARAMS()) {
-  std::get<k>(args).Run();
+  std::move(const_cast<base::OnceClosure&>(std::get<k>(args))).Run();
 }
 
 }  // namespace
@@ -48,9 +48,8 @@
   ValidatingAuthenticatorTest();
   ~ValidatingAuthenticatorTest() override;
 
-  void ValidateCallback(
-      const std::string& remote_jid,
-      const ValidatingAuthenticator::ResultCallback& callback);
+  void ValidateCallback(const std::string& remote_jid,
+                        ValidatingAuthenticator::ResultCallback callback);
 
  protected:
   // testing::Test overrides.
@@ -88,9 +87,9 @@
 
 void ValidatingAuthenticatorTest::ValidateCallback(
     const std::string& remote_jid,
-    const ValidatingAuthenticator::ResultCallback& callback) {
+    ValidatingAuthenticator::ResultCallback callback) {
   validate_complete_called_ = true;
-  callback.Run(validation_result_);
+  std::move(callback).Run(validation_result_);
 }
 
 void ValidatingAuthenticatorTest::SetUp() {
@@ -98,8 +97,9 @@
   std::unique_ptr<Authenticator> authenticator(mock_authenticator_);
 
   validating_authenticator_.reset(new ValidatingAuthenticator(
-      kRemoteTestJid, base::Bind(&ValidatingAuthenticatorTest::ValidateCallback,
-                                 base::Unretained(this)),
+      kRemoteTestJid,
+      base::BindOnce(&ValidatingAuthenticatorTest::ValidateCallback,
+                     base::Unretained(this)),
       std::move(authenticator)));
 }
 
diff --git a/services/data_decoder/BUILD.gn b/services/data_decoder/BUILD.gn
index 45ff0f1..fa455db 100644
--- a/services/data_decoder/BUILD.gn
+++ b/services/data_decoder/BUILD.gn
@@ -14,6 +14,8 @@
     "web_bundle_parser.h",
     "web_bundle_parser_factory.cc",
     "web_bundle_parser_factory.h",
+    "web_bundler.cc",
+    "web_bundler.h",
     "xml_parser.cc",
     "xml_parser.h",
   ]
diff --git a/services/data_decoder/data_decoder_service.cc b/services/data_decoder/data_decoder_service.cc
index 837a7c5..f841ce4 100644
--- a/services/data_decoder/data_decoder_service.cc
+++ b/services/data_decoder/data_decoder_service.cc
@@ -16,6 +16,7 @@
 #include "services/data_decoder/json_parser_impl.h"
 #include "services/data_decoder/public/mojom/image_decoder.mojom.h"
 #include "services/data_decoder/web_bundle_parser_factory.h"
+#include "services/data_decoder/web_bundler.h"
 #include "services/data_decoder/xml_parser.h"
 
 #if defined(OS_CHROMEOS)
@@ -78,6 +79,16 @@
   }
 }
 
+void DataDecoderService::BindWebBundler(
+    mojo::PendingReceiver<mojom::WebBundler> receiver) {
+  if (web_bundler_binder_) {
+    web_bundler_binder_.Run(std::move(receiver));
+  } else {
+    mojo::MakeSelfOwnedReceiver(std::make_unique<WebBundler>(),
+                                std::move(receiver));
+  }
+}
+
 #ifdef OS_CHROMEOS
 void DataDecoderService::BindBleScanParser(
     mojo::PendingReceiver<mojom::BleScanParser> receiver) {
diff --git a/services/data_decoder/data_decoder_service.h b/services/data_decoder/data_decoder_service.h
index dd73495..b399900 100644
--- a/services/data_decoder/data_decoder_service.h
+++ b/services/data_decoder/data_decoder_service.h
@@ -14,6 +14,7 @@
 #include "services/data_decoder/public/mojom/image_decoder.mojom.h"
 #include "services/data_decoder/public/mojom/json_parser.mojom.h"
 #include "services/data_decoder/public/mojom/web_bundle_parser.mojom.h"
+#include "services/data_decoder/public/mojom/web_bundler.mojom.h"
 #include "services/data_decoder/public/mojom/xml_parser.mojom.h"
 
 #ifdef OS_CHROMEOS
@@ -54,6 +55,14 @@
     web_bundle_parser_factory_binder_ = binder;
   }
 
+  // Configures the service to use |binder| to bind WebBundler in subsequent
+  // BindWebBundler() calls.
+  void SetWebBundlerBinderForTesting(
+      base::RepeatingCallback<void(mojo::PendingReceiver<mojom::WebBundler>)>
+          binder) {
+    web_bundler_binder_ = binder;
+  }
+
  private:
   // mojom::DataDecoderService implementation:
   void BindImageDecoder(
@@ -63,6 +72,8 @@
   void BindXmlParser(mojo::PendingReceiver<mojom::XmlParser> receiver) override;
   void BindWebBundleParserFactory(
       mojo::PendingReceiver<mojom::WebBundleParserFactory> receiver) override;
+  void BindWebBundler(
+      mojo::PendingReceiver<mojom::WebBundler> receiver) override;
 
 #ifdef OS_CHROMEOS
   void BindBleScanParser(
@@ -78,6 +89,8 @@
   base::RepeatingCallback<void(
       mojo::PendingReceiver<mojom::WebBundleParserFactory>)>
       web_bundle_parser_factory_binder_;
+  base::RepeatingCallback<void(mojo::PendingReceiver<mojom::WebBundler>)>
+      web_bundler_binder_;
 
   DISALLOW_COPY_AND_ASSIGN(DataDecoderService);
 };
diff --git a/services/data_decoder/public/mojom/BUILD.gn b/services/data_decoder/public/mojom/BUILD.gn
index 89ce98c8..ee6240f 100644
--- a/services/data_decoder/public/mojom/BUILD.gn
+++ b/services/data_decoder/public/mojom/BUILD.gn
@@ -10,10 +10,12 @@
     "image_decoder.mojom",
     "json_parser.mojom",
     "web_bundle_parser.mojom",
+    "web_bundler.mojom",
     "xml_parser.mojom",
   ]
 
   public_deps = [
+    ":mojom_resource_snapshot_for_web_bundle",
     "//mojo/public/mojom/base",
     "//skia/public/mojom",
     "//ui/gfx/geometry/mojom",
diff --git a/services/data_decoder/public/mojom/data_decoder_service.mojom b/services/data_decoder/public/mojom/data_decoder_service.mojom
index 420b6d1..120ba37 100644
--- a/services/data_decoder/public/mojom/data_decoder_service.mojom
+++ b/services/data_decoder/public/mojom/data_decoder_service.mojom
@@ -6,6 +6,7 @@
 
 import "services/data_decoder/public/mojom/image_decoder.mojom";
 import "services/data_decoder/public/mojom/json_parser.mojom";
+import "services/data_decoder/public/mojom/web_bundler.mojom";
 import "services/data_decoder/public/mojom/web_bundle_parser.mojom";
 import "services/data_decoder/public/mojom/xml_parser.mojom";
 
@@ -27,6 +28,9 @@
   BindWebBundleParserFactory(
       pending_receiver<WebBundleParserFactory> receiver);
 
+  // Binds an interface which can be used to generate a Web Bundle.
+  BindWebBundler(pending_receiver<WebBundler> receiver);
+
   // Binds an interface which can be used to parse raw BLE advertising packet
   // data.
   [EnableIf=is_chromeos]
diff --git a/services/data_decoder/public/mojom/web_bundler.mojom b/services/data_decoder/public/mojom/web_bundler.mojom
new file mode 100644
index 0000000..879e0f7b
--- /dev/null
+++ b/services/data_decoder/public/mojom/web_bundler.mojom
@@ -0,0 +1,24 @@
+// 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.
+
+module data_decoder.mojom;
+
+import "services/data_decoder/public/mojom/resource_snapshot_for_web_bundle.mojom";
+import "mojo/public/mojom/base/file.mojom";
+
+enum WebBundlerError {
+  kOK,
+  kNotImplemented,
+  kFileOpenFailed,
+  kWebBundlerConnectionError,
+};
+
+// Bundler interface to generate a web bundle from snapshots.
+interface WebBundler {
+  // Generates a web bundle from |snapshots| and writes to the passed |file|.
+  Generate(
+      array<pending_remote<ResourceSnapshotForWebBundle>> snapshots,
+      mojo_base.mojom.File file)
+    => (uint64 file_size, WebBundlerError error);
+};
diff --git a/services/data_decoder/web_bundler.cc b/services/data_decoder/web_bundler.cc
new file mode 100644
index 0000000..dd4537f
--- /dev/null
+++ b/services/data_decoder/web_bundler.cc
@@ -0,0 +1,19 @@
+// 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 "services/data_decoder/web_bundler.h"
+
+namespace data_decoder {
+
+void WebBundler::Generate(
+    std::vector<mojo::PendingRemote<mojom::ResourceSnapshotForWebBundle>>
+        snapshots,
+    base::File file,
+    GenerateCallback callback) {
+  // The Web Bundle generation logic is not implemented yet.
+  // TODO(crbug.com/1040752): Implement this.
+  std::move(callback).Run(0, mojom::WebBundlerError::kNotImplemented);
+}
+
+}  // namespace data_decoder
diff --git a/services/data_decoder/web_bundler.h b/services/data_decoder/web_bundler.h
new file mode 100644
index 0000000..36be214
--- /dev/null
+++ b/services/data_decoder/web_bundler.h
@@ -0,0 +1,37 @@
+// 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 SERVICES_DATA_DECODER_WEB_BUNDLER_H_
+#define SERVICES_DATA_DECODER_WEB_BUNDLER_H_
+
+#include <vector>
+
+#include "base/files/file.h"
+#include "mojo/public/cpp/base/big_buffer.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "services/data_decoder/public/mojom/resource_snapshot_for_web_bundle.mojom.h"
+#include "services/data_decoder/public/mojom/web_bundler.mojom.h"
+
+namespace data_decoder {
+
+class WebBundler : public mojom::WebBundler {
+ public:
+  WebBundler() = default;
+  ~WebBundler() override = default;
+
+  WebBundler(const WebBundler&) = delete;
+  WebBundler& operator=(const WebBundler&) = delete;
+
+ private:
+  // mojom::WebBundler implementation.
+  void Generate(
+      std::vector<mojo::PendingRemote<mojom::ResourceSnapshotForWebBundle>>
+          snapshots,
+      base::File file,
+      GenerateCallback callback) override;
+};
+
+}  // namespace data_decoder
+
+#endif  // SERVICES_DATA_DECODER_WEB_BUNDLER_H_
diff --git a/testing/buildbot/OWNERS b/testing/buildbot/OWNERS
index 575b4d3..db4513d 100644
--- a/testing/buildbot/OWNERS
+++ b/testing/buildbot/OWNERS
@@ -3,13 +3,15 @@
 
 file://infra/OWNERS
 
+bsheedy@chromium.org
 jam@chromium.org
 jochen@chromium.org
 machenbach@chromium.org
 sky@chromium.org
 
 per-file *chromium.perf*.json=crouleau@chromium.org
-per-file chromium.perf.json=simonhatch@chromium.org
+per-file *chromium.perf*.json=wenbinzhang@google.com
+per-file *chromium.perf*.json=hypan@google.com
 per-file chromium.webrtc.json=phoglund@chromium.org
 per-file chromium.webrtc.fyi.json=phoglund@chromium.org
 
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index a64891b3..89ccdda 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -12235,15 +12235,460 @@
   "ios-simulator-code-coverage": {
     "gtest_tests": [
       {
+        "args": [
+          "--platform",
+          "iPhone X",
+          "--version",
+          "13.3",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}"
+        ],
         "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
         },
+        "name": "boringssl_crypto_tests_iPhone X 13.3",
         "swarming": {
-          "can_use_on_swarming_builders": true
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "os": "Mac-10.14.6"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_11c29",
+              "path": "Xcode.app"
+            }
+          ],
+          "service_account": "ios-isolated-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "boringssl_crypto_tests",
+        "test_target": "//third_party/boringssl:boringssl_crypto_tests"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone X",
+          "--version",
+          "13.3",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "boringssl_ssl_tests_iPhone X 13.3",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "os": "Mac-10.14.6"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_11c29",
+              "path": "Xcode.app"
+            }
+          ],
+          "service_account": "ios-isolated-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "boringssl_ssl_tests",
+        "test_target": "//third_party/boringssl:boringssl_ssl_tests"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone X",
+          "--version",
+          "13.3",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "crypto_unittests_iPhone X 13.3",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "os": "Mac-10.14.6"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_11c29",
+              "path": "Xcode.app"
+            }
+          ],
+          "service_account": "ios-isolated-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "crypto_unittests",
+        "test_target": "//crypto:crypto_unittests"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone X",
+          "--version",
+          "13.3",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "google_apis_unittests_iPhone X 13.3",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "os": "Mac-10.14.6"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_11c29",
+              "path": "Xcode.app"
+            }
+          ],
+          "service_account": "ios-isolated-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "google_apis_unittests",
+        "test_target": "//google_apis:google_apis_unittests"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone X",
+          "--version",
+          "13.3",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_components_unittests_iPhone X 13.3",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "os": "Mac-10.14.6"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_11c29",
+              "path": "Xcode.app"
+            }
+          ],
+          "service_account": "ios-isolated-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "ios_components_unittests",
         "test_target": "//ios/components:ios_components_unittests"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone X",
+          "--version",
+          "13.3",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_net_unittests_iPhone X 13.3",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "os": "Mac-10.14.6"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_11c29",
+              "path": "Xcode.app"
+            }
+          ],
+          "service_account": "ios-isolated-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "ios_net_unittests",
+        "test_target": "//ios/net:ios_net_unittests"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone X",
+          "--version",
+          "13.3",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_remoting_unittests_iPhone X 13.3",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "os": "Mac-10.14.6"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_11c29",
+              "path": "Xcode.app"
+            }
+          ],
+          "service_account": "ios-isolated-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "ios_remoting_unittests",
+        "test_target": "//remoting/ios:ios_remoting_unittests"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone X",
+          "--version",
+          "13.3",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_testing_unittests_iPhone X 13.3",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "os": "Mac-10.14.6"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_11c29",
+              "path": "Xcode.app"
+            }
+          ],
+          "service_account": "ios-isolated-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "ios_testing_unittests",
+        "test_target": "//ios/testing:ios_testing_unittests"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone X",
+          "--version",
+          "13.3",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "net_unittests_iPhone X 13.3",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "os": "Mac-10.14.6"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_11c29",
+              "path": "Xcode.app"
+            }
+          ],
+          "service_account": "ios-isolated-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "net_unittests",
+        "test_target": "//net:net_unittests"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone X",
+          "--version",
+          "13.3",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "services_unittests_iPhone X 13.3",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "os": "Mac-10.14.6"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_11c29",
+              "path": "Xcode.app"
+            }
+          ],
+          "service_account": "ios-isolated-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "services_unittests",
+        "test_target": "//services:services_unittests"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone X",
+          "--version",
+          "13.3",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "sql_unittests_iPhone X 13.3",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "os": "Mac-10.14.6"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_11c29",
+              "path": "Xcode.app"
+            }
+          ],
+          "service_account": "ios-isolated-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "sql_unittests",
+        "test_target": "//sql:sql_unittests"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone X",
+          "--version",
+          "13.3",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "url_unittests_iPhone X 13.3",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "os": "Mac-10.14.6"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_11c29",
+              "path": "Xcode.app"
+            }
+          ],
+          "service_account": "ios-isolated-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "url_unittests",
+        "test_target": "//url:url_unittests"
       }
     ]
   },
diff --git a/testing/buildbot/filters/gpu.skiarenderer_vulkan_content_browsertests.filter b/testing/buildbot/filters/gpu.skiarenderer_vulkan_content_browsertests.filter
index 9791d6f..0778fa6d5 100644
--- a/testing/buildbot/filters/gpu.skiarenderer_vulkan_content_browsertests.filter
+++ b/testing/buildbot/filters/gpu.skiarenderer_vulkan_content_browsertests.filter
@@ -1,4 +1 @@
-# FeatureList Flakes. https://crbug.com/1012372
--NavigationBrowserTest.HistoryBackCancelPendingNavigationUserGesture/1
--SitePerProcessBrowserTest.*
--TextFragmentAnchorBrowserTest.DisabledOnScriptNavigation/0
+# TODO(jonross): Remove once Finch completes.
\ No newline at end of file
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl
index ab14ba4..f18ffa9 100644
--- a/testing/buildbot/test_suites.pyl
+++ b/testing/buildbot/test_suites.pyl
@@ -2639,10 +2639,6 @@
       },
     },
 
-    'ios_code_coverage_gtests': {
-      'ios_components_unittests': {},
-    },
-
     'ios_common_tests': {
       'boringssl_crypto_tests': {
         'merge': {
@@ -4734,7 +4730,6 @@
   ##############################################################################
 
   'matrix_compound_suites': {
-
     'ios13_beta_simulator_gtests': {
       'ios_common_tests': {
         'variants': [
@@ -4777,6 +4772,13 @@
           'SIM_IPAD_AIR_2_12_4',
         ],
       },
-    }
+    },
+    'ios_code_coverage_gtests': {
+      'ios_common_tests': {
+        'variants': [
+          'SIM_IPHONE_X_13_3',
+        ],
+      },
+     },
   }
 }
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl
index 82ed6199..69def86 100644
--- a/testing/buildbot/waterfalls.pyl
+++ b/testing/buildbot/waterfalls.pyl
@@ -1776,6 +1776,13 @@
         },
       },
       'ios-simulator-code-coverage': {
+        'mixins': [
+          'ios_swarming_account',
+          'mac_10.14',
+          'mac_toolchain',
+          'out_dir_arg',
+          'xcode_11c29'
+        ],
         'test_suites': {
           'gtest_tests': 'ios_code_coverage_gtests',
         },
diff --git a/third_party/blink/public/platform/platform.h b/third_party/blink/public/platform/platform.h
index 16cf044..c7bc680 100644
--- a/third_party/blink/public/platform/platform.h
+++ b/third_party/blink/public/platform/platform.h
@@ -88,7 +88,7 @@
 }
 
 namespace viz {
-class ContextProvider;
+class RasterContextProvider;
 }
 
 namespace blink {
@@ -526,7 +526,7 @@
     return nullptr;
   }
 
-  virtual viz::ContextProvider* SharedMainThreadContextProvider() {
+  virtual viz::RasterContextProvider* SharedMainThreadContextProvider() {
     return nullptr;
   }
 
diff --git a/third_party/blink/renderer/bindings/core/v8/generated_code_helper.cc b/third_party/blink/renderer/bindings/core/v8/generated_code_helper.cc
index f8a086c..05332ac8 100644
--- a/third_party/blink/renderer/bindings/core/v8/generated_code_helper.cc
+++ b/third_party/blink/renderer/bindings/core/v8/generated_code_helper.cc
@@ -226,7 +226,7 @@
 }
 
 ExecutionContext* ExecutionContextFromV8Wrappable(const Node* node) {
-  return node->GetDocument().ToExecutionContext();
+  return node->GetExecutionContext();
 }
 
 ExecutionContext* ExecutionContextFromV8Wrappable(const Range* range) {
diff --git a/third_party/blink/renderer/build/scripts/gperf.py b/third_party/blink/renderer/build/scripts/gperf.py
index 4890b2d..73dd62ad 100644
--- a/third_party/blink/renderer/build/scripts/gperf.py
+++ b/third_party/blink/renderer/build/scripts/gperf.py
@@ -40,6 +40,13 @@
         # https://savannah.gnu.org/bugs/index.php?53029
         gperf_output = gperf_output.replace('/*FALLTHROUGH*/',
                                             '  FALLTHROUGH;')
+        # -Wpointer-to-int-cast warns about casting pointers to smaller ints
+        # Replace {(int)(long)&(foo), bar} with
+        # {static_cast<int>(reinterpret_cast<uintptr_t>(&(foo)), bar}
+        gperf_output = re.sub(
+            r'\(int\)\(long\)(.*?),',
+            r'static_cast<int>(reinterpret_cast<uintptr_t>(\1)),',
+            gperf_output)
         script = 'third_party/blink/renderer/build/scripts/gperf.py'
         return '// Generated by %s\n' % script + gperf_output
     except OSError:
diff --git a/third_party/blink/renderer/core/DEPS b/third_party/blink/renderer/core/DEPS
index 4bb2892..49ab19bae 100644
--- a/third_party/blink/renderer/core/DEPS
+++ b/third_party/blink/renderer/core/DEPS
@@ -26,6 +26,7 @@
     "+cc/animation/scroll_offset_animations.h",
     "+cc/animation/scroll_offset_animation_curve.h",
     "+cc/animation/scroll_state.h",
+    "+cc/animation/scroll_timeline.h",
     "+cc/base/region.h",
     "+cc/input/browser_controls_state.h",
     "+cc/input/event_listener_properties.h",
diff --git a/third_party/blink/renderer/core/animation/animation.cc b/third_party/blink/renderer/core/animation/animation.cc
index 971b17a5..3b9ed49 100644
--- a/third_party/blink/renderer/core/animation/animation.cc
+++ b/third_party/blink/renderer/core/animation/animation.cc
@@ -43,6 +43,7 @@
 #include "third_party/blink/renderer/core/animation/keyframe_effect.h"
 #include "third_party/blink/renderer/core/animation/pending_animations.h"
 #include "third_party/blink/renderer/core/animation/scroll_timeline.h"
+#include "third_party/blink/renderer/core/animation/scroll_timeline_util.h"
 #include "third_party/blink/renderer/core/css/properties/css_property_ref.h"
 #include "third_party/blink/renderer/core/css/resolver/style_resolver.h"
 #include "third_party/blink/renderer/core/css/style_change_reason.h"
@@ -1826,7 +1827,6 @@
   if (Platform::Current()->IsThreadedAnimationEnabled() &&
       !compositor_animation_) {
     compositor_animation_ = CompositorAnimationHolder::Create(this);
-    DCHECK(compositor_animation_);
     AttachCompositorTimeline();
   }
 
@@ -1844,23 +1844,59 @@
 }
 
 void Animation::AttachCompositorTimeline() {
-  if (compositor_animation_) {
-    CompositorAnimationTimeline* timeline =
-        timeline_ ? To<DocumentTimeline>(*timeline_).CompositorTimeline()
-                  : nullptr;
-    if (timeline)
-      timeline->AnimationAttached(*this);
-  }
+  DCHECK(compositor_animation_);
+
+  // Register ourselves on the compositor timeline. This will cause our cc-side
+  // animation animation to be registered.
+  CompositorAnimationTimeline* compositor_timeline =
+      timeline_ ? timeline_->EnsureCompositorTimeline() : nullptr;
+  if (!compositor_timeline)
+    return;
+
+  compositor_timeline->AnimationAttached(*this);
+  if (compositor_timeline->GetAnimationTimeline()->IsScrollTimeline())
+    document_->AttachCompositorTimeline(compositor_timeline);
 }
 
 void Animation::DetachCompositorTimeline() {
-  if (compositor_animation_) {
-    CompositorAnimationTimeline* timeline =
-        timeline_ ? To<DocumentTimeline>(*timeline_).CompositorTimeline()
-                  : nullptr;
-    if (timeline)
-      timeline->AnimationDestroyed(*this);
+  DCHECK(compositor_animation_);
+
+  CompositorAnimationTimeline* compositor_timeline =
+      timeline_ ? timeline_->CompositorTimeline() : nullptr;
+  if (!compositor_timeline)
+    return;
+
+  compositor_timeline->AnimationDestroyed(*this);
+
+  if (compositor_timeline->GetAnimationTimeline()->IsScrollTimeline())
+    document_->DetachCompositorTimeline(compositor_timeline);
+}
+
+void Animation::UpdateCompositorScrollTimeline() {
+  if (!compositor_animation_ || !timeline_)
+    return;
+  Node* scroll_source = To<ScrollTimeline>(*timeline_).ResolvedScrollSource();
+  LayoutBox* box = scroll_source ? scroll_source->GetLayoutBox() : nullptr;
+
+  base::Optional<double> start_scroll_offset;
+  base::Optional<double> end_scroll_offset;
+  if (box) {
+    double current_offset;
+    double max_offset;
+    To<ScrollTimeline>(*timeline_)
+        .GetCurrentAndMaxOffset(box, current_offset, max_offset);
+
+    double resolved_start_scroll_offset = 0;
+    double resolved_end_scroll_offset = max_offset;
+    To<ScrollTimeline>(*timeline_)
+        .ResolveScrollStartAndEnd(box, max_offset, resolved_start_scroll_offset,
+                                  resolved_end_scroll_offset);
+    start_scroll_offset = resolved_start_scroll_offset;
+    end_scroll_offset = resolved_end_scroll_offset;
   }
+  compositor_animation_->GetAnimation()->UpdateScrollTimeline(
+      scroll_timeline_util::GetCompositorScrollElementId(scroll_source),
+      start_scroll_offset, end_scroll_offset);
 }
 
 void Animation::AttachCompositedLayers() {
diff --git a/third_party/blink/renderer/core/animation/animation.h b/third_party/blink/renderer/core/animation/animation.h
index 5253c82c..02d4b0ab 100644
--- a/third_party/blink/renderer/core/animation/animation.h
+++ b/third_party/blink/renderer/core/animation/animation.h
@@ -280,6 +280,11 @@
   }
   bool ReplaceStateActive() const { return replace_state_ == kActive; }
 
+  // TODO(yigu): This is a reverse dependency between AnimationTimeline and
+  // Animation. We should move the update logic once snapshotting is
+  // implemented. https://crbug.com/1060578.
+  void UpdateCompositorScrollTimeline();
+
  protected:
   DispatchEventResult DispatchEventInternal(Event&) override;
   void AddedEventListener(const AtomicString& event_type,
diff --git a/third_party/blink/renderer/core/animation/animation_timeline.cc b/third_party/blink/renderer/core/animation/animation_timeline.cc
index 3a67e258..78a16ee8 100644
--- a/third_party/blink/renderer/core/animation/animation_timeline.cc
+++ b/third_party/blink/renderer/core/animation/animation_timeline.cc
@@ -97,7 +97,15 @@
 void AnimationTimeline::ServiceAnimations(TimingUpdateReason reason) {
   TRACE_EVENT0("blink", "AnimationTimeline::serviceAnimations");
 
-  last_current_time_internal_ = CurrentTimeInternal();
+  auto current_time_internal = CurrentTimeInternal();
+  bool maybe_update_compositor_scroll_timeline = false;
+
+  if (IsScrollTimeline() &&
+      last_current_time_internal_ != current_time_internal) {
+    maybe_update_compositor_scroll_timeline = true;
+  }
+
+  last_current_time_internal_ = current_time_internal;
 
   HeapVector<Member<Animation>> animations;
   animations.ReserveInitialCapacity(animations_needing_update_.size());
@@ -107,8 +115,12 @@
   std::sort(animations.begin(), animations.end(), CompareAnimations);
 
   for (Animation* animation : animations) {
-    if (!animation->Update(reason))
+    if (!animation->Update(reason)) {
       animations_needing_update_.erase(animation);
+      continue;
+    }
+    if (maybe_update_compositor_scroll_timeline)
+      animation->UpdateCompositorScrollTimeline();
   }
 
   DCHECK_EQ(outdated_animation_count_, 0U);
diff --git a/third_party/blink/renderer/core/animation/animation_timeline.h b/third_party/blink/renderer/core/animation/animation_timeline.h
index 1a0a55e..20fac27 100644
--- a/third_party/blink/renderer/core/animation/animation_timeline.h
+++ b/third_party/blink/renderer/core/animation/animation_timeline.h
@@ -8,6 +8,7 @@
 #include "third_party/blink/renderer/core/animation/animation.h"
 #include "third_party/blink/renderer/core/animation/animation_effect.h"
 #include "third_party/blink/renderer/core/core_export.h"
+#include "third_party/blink/renderer/platform/animation/compositor_animation_timeline.h"
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
 
 namespace blink {
@@ -69,6 +70,11 @@
     return animations_;
   }
 
+  CompositorAnimationTimeline* CompositorTimeline() const {
+    return compositor_timeline_.get();
+  }
+  virtual CompositorAnimationTimeline* EnsureCompositorTimeline() = 0;
+
   void Trace(Visitor*) override;
 
  protected:
@@ -83,6 +89,8 @@
   // All animations attached to this timeline.
   HeapHashSet<WeakMember<Animation>> animations_;
 
+  std::unique_ptr<CompositorAnimationTimeline> compositor_timeline_;
+
   base::Optional<base::TimeDelta> last_current_time_internal_;
 };
 
diff --git a/third_party/blink/renderer/core/animation/document_animations_test.cc b/third_party/blink/renderer/core/animation/document_animations_test.cc
index c67f99b3..195ae86 100644
--- a/third_party/blink/renderer/core/animation/document_animations_test.cc
+++ b/third_party/blink/renderer/core/animation/document_animations_test.cc
@@ -32,6 +32,7 @@
   MOCK_METHOD1(ServiceAnimations, void(TimingUpdateReason));
   MOCK_CONST_METHOD0(AnimationsNeedingUpdateCount, wtf_size_t());
   MOCK_METHOD0(ScheduleNextService, void());
+  MOCK_METHOD0(EnsureCompositorTimeline, CompositorAnimationTimeline*());
 
   void Trace(Visitor* visitor) override { AnimationTimeline::Trace(visitor); }
 
diff --git a/third_party/blink/renderer/core/animation/document_timeline.cc b/third_party/blink/renderer/core/animation/document_timeline.cc
index c6675c40..106bf2c 100644
--- a/third_party/blink/renderer/core/animation/document_timeline.cc
+++ b/third_party/blink/renderer/core/animation/document_timeline.cc
@@ -35,7 +35,6 @@
 #include "third_party/blink/renderer/core/animation/animation_clock.h"
 #include "third_party/blink/renderer/core/animation/animation_effect.h"
 #include "third_party/blink/renderer/core/loader/document_loader.h"
-#include "third_party/blink/renderer/platform/animation/compositor_animation_timeline.h"
 #include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
 
 namespace blink {
@@ -86,7 +85,7 @@
   else
     timing_ = timing;
   if (Platform::Current()->IsThreadedAnimationEnabled())
-    compositor_timeline_ = std::make_unique<CompositorAnimationTimeline>();
+    EnsureCompositorTimeline();
 
   DCHECK(document);
 }
@@ -230,6 +229,14 @@
     animation->InvalidateKeyframeEffect(tree_scope);
 }
 
+CompositorAnimationTimeline* DocumentTimeline::EnsureCompositorTimeline() {
+  if (compositor_timeline_)
+    return compositor_timeline_.get();
+
+  compositor_timeline_ = std::make_unique<CompositorAnimationTimeline>();
+  return compositor_timeline_.get();
+}
+
 void DocumentTimeline::Trace(Visitor* visitor) {
   visitor->Trace(timing_);
   AnimationTimeline::Trace(visitor);
diff --git a/third_party/blink/renderer/core/animation/document_timeline.h b/third_party/blink/renderer/core/animation/document_timeline.h
index c7f8c6d..87a65e0 100644
--- a/third_party/blink/renderer/core/animation/document_timeline.h
+++ b/third_party/blink/renderer/core/animation/document_timeline.h
@@ -43,7 +43,6 @@
 class Animation;
 class AnimationEffect;
 class DocumentTimelineOptions;
-class CompositorAnimationTimeline;
 
 // DocumentTimeline is constructed and owned by Document, and tied to its
 // lifecycle.
@@ -90,13 +89,11 @@
   void SetPlaybackRate(double);
   double PlaybackRate() const;
 
-  CompositorAnimationTimeline* CompositorTimeline() const {
-    return compositor_timeline_.get();
-  }
-
   void ResetForTesting();
   void SetTimingForTesting(PlatformTiming* timing);
 
+  CompositorAnimationTimeline* EnsureCompositorTimeline() override;
+
   void Trace(Visitor*) override;
 
  protected:
@@ -119,8 +116,6 @@
 
   Member<PlatformTiming> timing_;
 
-  std::unique_ptr<CompositorAnimationTimeline> compositor_timeline_;
-
   class DocumentTimelineTiming final : public PlatformTiming {
    public:
     DocumentTimelineTiming(DocumentTimeline* timeline)
diff --git a/third_party/blink/renderer/core/animation/scroll_timeline.cc b/third_party/blink/renderer/core/animation/scroll_timeline.cc
index 1d9fa53..06e29628 100644
--- a/third_party/blink/renderer/core/animation/scroll_timeline.cc
+++ b/third_party/blink/renderer/core/animation/scroll_timeline.cc
@@ -5,6 +5,7 @@
 #include "third_party/blink/renderer/core/animation/scroll_timeline.h"
 
 #include "third_party/blink/renderer/bindings/core/v8/v8_scroll_timeline_options.h"
+#include "third_party/blink/renderer/core/animation/scroll_timeline_util.h"
 #include "third_party/blink/renderer/core/css/css_to_length_conversion_data.h"
 #include "third_party/blink/renderer/core/css/parser/css_parser_context.h"
 #include "third_party/blink/renderer/core/css/parser/css_tokenizer.h"
@@ -367,4 +368,13 @@
   return it != set.end() && it->value > 0;
 }
 
+CompositorAnimationTimeline* ScrollTimeline::EnsureCompositorTimeline() {
+  if (compositor_timeline_)
+    return compositor_timeline_.get();
+
+  compositor_timeline_ = std::make_unique<CompositorAnimationTimeline>(
+      scroll_timeline_util::ToCompositorScrollTimeline(this));
+  return compositor_timeline_.get();
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/animation/scroll_timeline.h b/third_party/blink/renderer/core/animation/scroll_timeline.h
index a359e43..d265614 100644
--- a/third_party/blink/renderer/core/animation/scroll_timeline.h
+++ b/third_party/blink/renderer/core/animation/scroll_timeline.h
@@ -89,6 +89,8 @@
   void AnimationAttached(Animation*) override;
   void AnimationDetached(Animation*) override;
 
+  CompositorAnimationTimeline* EnsureCompositorTimeline() override;
+
   void Trace(Visitor*) override;
 
   static bool HasActiveScrollTimeline(Node* node);
diff --git a/third_party/blink/renderer/core/animation/scroll_timeline_util.cc b/third_party/blink/renderer/core/animation/scroll_timeline_util.cc
index c694177..eb95f9b 100644
--- a/third_party/blink/renderer/core/animation/scroll_timeline_util.cc
+++ b/third_party/blink/renderer/core/animation/scroll_timeline_util.cc
@@ -51,7 +51,7 @@
     end_scroll_offset = resolved_end_scroll_offset;
   }
 
-  return base::MakeRefCounted<CompositorScrollTimeline>(
+  return CompositorScrollTimeline::Create(
       element_id, orientation, start_scroll_offset, end_scroll_offset,
       time_range.GetAsDouble(), scroll_timeline->GetFillMode());
 }
diff --git a/third_party/blink/renderer/core/animation/scroll_timeline_util.h b/third_party/blink/renderer/core/animation/scroll_timeline_util.h
index 497bbea..c17846b 100644
--- a/third_party/blink/renderer/core/animation/scroll_timeline_util.h
+++ b/third_party/blink/renderer/core/animation/scroll_timeline_util.h
@@ -8,12 +8,15 @@
 #include <memory>
 
 #include "base/optional.h"
+#include "cc/animation/scroll_timeline.h"
 #include "third_party/blink/renderer/core/animation/scroll_timeline.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/platform/animation/compositor_animation.h"
 
 namespace blink {
 
+using CompositorScrollTimeline = cc::ScrollTimeline;
+
 class AnimationTimeline;
 class ComputedStyle;
 class Node;
diff --git a/third_party/blink/renderer/core/css/css_page_rule_test.cc b/third_party/blink/renderer/core/css/css_page_rule_test.cc
index 51be9ee..98332f4 100644
--- a/third_party/blink/renderer/core/css/css_page_rule_test.cc
+++ b/third_party/blink/renderer/core/css/css_page_rule_test.cc
@@ -6,7 +6,7 @@
 
 #include "third_party/blink/renderer/core/css/css_rule_list.h"
 #include "third_party/blink/renderer/core/css/css_test_helpers.h"
-#include "third_party/blink/renderer/core/dom/document.h"
+#include "third_party/blink/renderer/core/testing/null_execution_context.h"
 
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -36,25 +36,22 @@
 
   auto* page_rule = To<CSSPageRule>(sheet.CssRules()->item(0));
   EXPECT_EQ(":left", page_rule->selectorText());
+  auto* context = MakeGarbageCollected<NullExecutionContext>();
 
   // set invalid page selector.
-  page_rule->setSelectorText(sheet.GetDocument().ToExecutionContext(),
-                             ":hover");
+  page_rule->setSelectorText(context, ":hover");
   EXPECT_EQ(":left", page_rule->selectorText());
 
   // set invalid page selector.
-  page_rule->setSelectorText(sheet.GetDocument().ToExecutionContext(),
-                             "right { bla");
+  page_rule->setSelectorText(context, "right { bla");
   EXPECT_EQ(":left", page_rule->selectorText());
 
   // set page pseudo class selector.
-  page_rule->setSelectorText(sheet.GetDocument().ToExecutionContext(),
-                             ":right");
+  page_rule->setSelectorText(context, ":right");
   EXPECT_EQ(":right", page_rule->selectorText());
 
   // set page type selector.
-  page_rule->setSelectorText(sheet.GetDocument().ToExecutionContext(),
-                             "namedpage");
+  page_rule->setSelectorText(context, "namedpage");
   EXPECT_EQ("namedpage", page_rule->selectorText());
 }
 
diff --git a/third_party/blink/renderer/core/css/css_property_value_set.cc b/third_party/blink/renderer/core/css/css_property_value_set.cc
index e1488e5..94d083cc 100644
--- a/third_party/blink/renderer/core/css/css_property_value_set.cc
+++ b/third_party/blink/renderer/core/css/css_property_value_set.cc
@@ -33,6 +33,7 @@
 #include "third_party/blink/renderer/core/style_property_shorthand.h"
 #include "third_party/blink/renderer/platform/heap/heap.h"
 #include "third_party/blink/renderer/platform/instrumentation/use_counter.h"
+#include "third_party/blink/renderer/platform/wtf/size_assertions.h"
 #include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
 
 #ifndef NDEBUG
@@ -656,11 +657,9 @@
 // See the function above if you need to update this.
 struct SameSizeAsCSSPropertyValueSet final
     : public GarbageCollected<SameSizeAsCSSPropertyValueSet> {
-  unsigned bitfield;
+  uint32_t bitfield;
 };
-static_assert(sizeof(CSSPropertyValueSet) ==
-                  sizeof(SameSizeAsCSSPropertyValueSet),
-              "CSSPropertyValueSet should stay small");
+ASSERT_SIZE(CSSPropertyValueSet, SameSizeAsCSSPropertyValueSet);
 
 #ifndef NDEBUG
 void CSSPropertyValueSet::ShowStyle() {
diff --git a/third_party/blink/renderer/core/css/css_property_value_set.h b/third_party/blink/renderer/core/css/css_property_value_set.h
index 44bcdab..303fc5d 100644
--- a/third_party/blink/renderer/core/css/css_property_value_set.h
+++ b/third_party/blink/renderer/core/css/css_property_value_set.h
@@ -144,23 +144,22 @@
  protected:
   enum { kMaxArraySize = (1 << 28) - 1 };
 
-  CSSPropertyValueSet(CSSParserMode css_parser_mode)
-      : css_parser_mode_(css_parser_mode), is_mutable_(true), array_size_(0) {}
+  explicit CSSPropertyValueSet(CSSParserMode css_parser_mode)
+      : array_size_(0), css_parser_mode_(css_parser_mode), is_mutable_(true) {}
 
   CSSPropertyValueSet(CSSParserMode css_parser_mode,
                       unsigned immutable_array_size)
-      : css_parser_mode_(css_parser_mode), is_mutable_(false) {
-    // Avoid min()/max() from std here in the header, because that would require
-    // inclusion of <algorithm>, which is slow to compile.
-    if (immutable_array_size < unsigned(kMaxArraySize))
-      array_size_ = immutable_array_size;
-    else
-      array_size_ = unsigned(kMaxArraySize);
-  }
+      // Avoid min()/max() from std here in the header, because that would
+      // require inclusion of <algorithm>, which is slow to compile.
+      : array_size_((immutable_array_size < unsigned(kMaxArraySize))
+                        ? immutable_array_size
+                        : unsigned(kMaxArraySize)),
+        css_parser_mode_(css_parser_mode),
+        is_mutable_(false) {}
 
-  unsigned css_parser_mode_ : 3;
-  const unsigned is_mutable_ : 1;
-  unsigned array_size_ : 28;
+  const uint32_t array_size_ : 28;
+  const uint32_t css_parser_mode_ : 3;
+  const uint32_t is_mutable_ : 1;
 
   friend class PropertySetCSSStyleDeclaration;
   DISALLOW_COPY_AND_ASSIGN(CSSPropertyValueSet);
diff --git a/third_party/blink/renderer/core/css/css_value.cc b/third_party/blink/renderer/core/css/css_value.cc
index adc44ca..d849ce27 100644
--- a/third_party/blink/renderer/core/css/css_value.cc
+++ b/third_party/blink/renderer/core/css/css_value.cc
@@ -80,7 +80,7 @@
 namespace blink {
 
 struct SameSizeAsCSSValue final : public GarbageCollected<SameSizeAsCSSValue> {
-  uint32_t bitfields;
+  char bitfields[sizeof(uint16_t) + sizeof(uint8_t)];
 };
 ASSERT_SIZE(CSSValue, SameSizeAsCSSValue);
 
diff --git a/third_party/blink/renderer/core/css/css_value.h b/third_party/blink/renderer/core/css/css_value.h
index 29eac1b6..db205280 100644
--- a/third_party/blink/renderer/core/css/css_value.h
+++ b/third_party/blink/renderer/core/css/css_value.h
@@ -190,7 +190,6 @@
   ~CSSValue() = default;
 
  protected:
-  static const size_t kClassTypeBits = 6;
   enum ClassType {
     kNumericLiteralClass,
     kMathFunctionClass,
@@ -272,8 +271,8 @@
 
   explicit CSSValue(ClassType class_type)
       : numeric_literal_unit_type_(0),
-        value_list_separator_(kSpaceSeparator),
         is_non_negative_math_function_(false),
+        value_list_separator_(kSpaceSeparator),
         allows_negative_percentage_reference_(false),
         class_type_(class_type) {}
 
@@ -283,18 +282,30 @@
  protected:
   // The bits in this section are only used by specific subclasses but kept here
   // to maximize struct packing.
+  // The bits are ordered and split into groups to such that from the
+  // perspective of each subclass, each field is a separate memory location.
+  // Using NOLINT here allows to use uint8_t as bitfield type which reduces
+  // size of CSSValue from 4 bytes to 3 bytes.
 
   // CSSNumericLiteralValue bits:
-  unsigned numeric_literal_unit_type_ : 7;  // CSSPrimitiveValue::UnitType
+  // This field hold CSSPrimitiveValue::UnitType.
+  uint8_t numeric_literal_unit_type_ : 7;  // NOLINT
 
-  unsigned value_list_separator_ : kValueListSeparatorBits;
+  // CSSMathFunctionValue:
+  uint8_t is_non_negative_math_function_ : 1;  // NOLINT
 
-  // CSSMathFunctionValue
-  unsigned is_non_negative_math_function_ : 1;
-  unsigned allows_negative_percentage_reference_ : 1;
+  // Force a new memory location. This will make TSAN treat the 2 fields above
+  // this line as a separate memory location than the 2 fields below it.
+  char : 0;
+
+  // CSSNumericLiteralValue bits:
+  uint8_t value_list_separator_ : kValueListSeparatorBits;  // NOLINT
+
+  // CSSMathFunctionValue:
+  uint8_t allows_negative_percentage_reference_ : 1;  // NOLINT
 
  private:
-  const unsigned class_type_ : kClassTypeBits;  // ClassType
+  const uint8_t class_type_;  // ClassType
 };
 
 template <typename CSSValueType, wtf_size_t inlineCapacity>
diff --git a/third_party/blink/renderer/core/css/parser/css_parser_context.cc b/third_party/blink/renderer/core/css/parser/css_parser_context.cc
index 2ef82af..fd784878 100644
--- a/third_party/blink/renderer/core/css/parser/css_parser_context.cc
+++ b/third_party/blink/renderer/core/css/parser/css_parser_context.cc
@@ -267,7 +267,7 @@
     if (!LayoutAnimationsPolicy::AffectedCSSProperties().Contains(&property))
       continue;
     LayoutAnimationsPolicy::ReportViolation(property,
-                                            *document_->ToExecutionContext());
+                                            *document_->GetExecutionContext());
   }
 }
 
diff --git a/third_party/blink/renderer/core/css/properties/css_parsing_utils.cc b/third_party/blink/renderer/core/css/properties/css_parsing_utils.cc
index 7a9413f8..91d1b11 100644
--- a/third_party/blink/renderer/core/css/properties/css_parsing_utils.cc
+++ b/third_party/blink/renderer/core/css/properties/css_parsing_utils.cc
@@ -2617,14 +2617,14 @@
     return nullptr;
   if (token.Id() == CSSValueID::kNone)
     return css_property_parser_helpers::ConsumeIdent(range);
-  const Document* document = context.GetDocument();
+  const auto* execution_context = context.GetExecutionContext();
   CSSPropertyID unresolved_property =
-      token.ParseAsUnresolvedCSSPropertyID(document->ToExecutionContext());
+      token.ParseAsUnresolvedCSSPropertyID(execution_context);
   if (unresolved_property != CSSPropertyID::kInvalid &&
       unresolved_property != CSSPropertyID::kVariable) {
 #if DCHECK_IS_ON()
     DCHECK(CSSProperty::Get(resolveCSSPropertyID(unresolved_property))
-               .IsWebExposed(document->ToExecutionContext()));
+               .IsWebExposed(execution_context));
 #endif
     range.ConsumeIncludingWhitespace();
     return MakeGarbageCollected<CSSCustomIdentValue>(unresolved_property);
diff --git a/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc b/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc
index d525ce5..27203a0 100644
--- a/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc
+++ b/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc
@@ -7821,12 +7821,12 @@
     if (range.Peek().GetType() != kIdentToken)
       return nullptr;
     CSSPropertyID unresolved_property = UnresolvedCSSPropertyID(
-        context.GetDocument()->ToExecutionContext(), range.Peek().Value());
+        context.GetExecutionContext(), range.Peek().Value());
     if (unresolved_property != CSSPropertyID::kInvalid &&
         unresolved_property != CSSPropertyID::kVariable) {
 #if DCHECK_IS_ON()
       DCHECK(CSSProperty::Get(resolveCSSPropertyID(unresolved_property))
-                 .IsWebExposed(context.GetDocument()->ToExecutionContext()));
+                 .IsWebExposed(context.GetExecutionContext()));
 #endif
       // Now "all" is used by both CSSValue and CSSPropertyValue.
       // Need to return nullptr when currentValue is CSSPropertyID::kAll.
diff --git a/third_party/blink/renderer/core/css/style_rule.h b/third_party/blink/renderer/core/css/style_rule.h
index 4d1a1b01..a4a1146 100644
--- a/third_party/blink/renderer/core/css/style_rule.h
+++ b/third_party/blink/renderer/core/css/style_rule.h
@@ -91,7 +91,7 @@
   CSSRule* CreateCSSOMWrapper(CSSStyleSheet* parent_sheet,
                               CSSRule* parent_rule) const;
 
-  const unsigned type_ : 5;
+  const uint8_t type_;
 };
 
 // A single rule from a stylesheet. Contains a selector list (one or more
diff --git a/third_party/blink/renderer/core/css/webxr_overlay.css b/third_party/blink/renderer/core/css/webxr_overlay.css
index 64b33b1..d4f65fb7 100644
--- a/third_party/blink/renderer/core/css/webxr_overlay.css
+++ b/third_party/blink/renderer/core/css/webxr_overlay.css
@@ -2,6 +2,9 @@
     /* force a transparent background */
     background: rgba(0,0,0,0) !important;
 
+    /* act as containing block for descendants */
+    contain: paint !important;
+
     /* the following styling is identical to :fullscreen */
     position: fixed !important;
     top: 0 !important;
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc
index e7dbc7c..ce7801d 100644
--- a/third_party/blink/renderer/core/dom/document.cc
+++ b/third_party/blink/renderer/core/dom/document.cc
@@ -229,7 +229,6 @@
 #include "third_party/blink/renderer/core/inspector/inspector_issue.h"
 #include "third_party/blink/renderer/core/inspector/inspector_issue_storage.h"
 #include "third_party/blink/renderer/core/inspector/inspector_trace_events.h"
-#include "third_party/blink/renderer/core/inspector/main_thread_debugger.h"
 #include "third_party/blink/renderer/core/intersection_observer/element_intersection_observer_data.h"
 #include "third_party/blink/renderer/core/intersection_observer/intersection_observer_controller.h"
 #include "third_party/blink/renderer/core/intersection_observer/intersection_observer_entry.h"
@@ -656,7 +655,6 @@
                    DocumentClassFlags document_classes)
     : ContainerNode(nullptr, kCreateDocument),
       TreeScope(*this),
-      ExecutionContext(V8PerIsolateData::MainThreadIsolate()),
       evaluate_media_queries_on_style_recalc_(false),
       pending_sheet_layout_(kNoLayoutWithPendingSheets),
       window_agent_factory_(initializer.GetWindowAgentFactory()),
@@ -758,10 +756,9 @@
               HeapHashMap<int, Member<ContentSecurityPolicy>>>()),
       permission_service_(this->ToExecutionContext()),
       font_preload_manager_(*this) {
-  // TODO(crbug.com/1029822): SecurityContextInit will eventually not be
-  // passed to the Document constructor. These will need to move.
   security_initializer.ApplyPendingDataToDocument(*this);
-  GetSecurityContext().GetOriginTrialContext()->BindExecutionContext(this);
+  GetOriginTrialContext()->BindExecutionContext(GetExecutionContext());
+
   if (frame_) {
     pending_fp_headers_ = security_initializer.FeaturePolicyHeader();
     pending_dp_headers_ = initializer.GetDocumentPolicy();
@@ -941,12 +938,10 @@
   return domWindow()->location();
 }
 
-bool Document::ShouldInstallV8Extensions() const {
-  return frame_->Client()->AllowScriptExtensions();
-}
-
 ContentSecurityPolicy* Document::GetContentSecurityPolicyForWorld() {
   v8::Isolate* isolate = GetIsolate();
+  if (!isolate)
+    return GetContentSecurityPolicy();
   v8::HandleScope handle_scope(isolate);
   v8::Local<v8::Context> v8_context = isolate->GetCurrentContext();
 
@@ -973,6 +968,48 @@
   return policy;
 }
 
+// static
+Document& Document::From(ExecutionContext& context) {
+  SECURITY_DCHECK(context.IsDocument());
+  return *static_cast<LocalDOMWindow&>(context).document();
+}
+
+// static
+const Document& Document::From(const ExecutionContext& context) {
+  SECURITY_DCHECK(context.IsDocument());
+  return *static_cast<const LocalDOMWindow&>(context).document();
+}
+
+ExecutionContext* Document::ToExecutionContext() {
+  return domWindow();
+}
+
+const ExecutionContext* Document::ToExecutionContext() const {
+  return domWindow();
+}
+
+bool Document::FeatureEnabled(OriginTrialFeature feature) const {
+  return GetOriginTrialContext()->IsFeatureEnabled(feature);
+}
+
+void Document::CountFeaturePolicyUsage(mojom::WebFeature feature) {
+  UseCounter::Count(*this, feature);
+}
+
+bool Document::FeaturePolicyFeatureObserved(
+    mojom::blink::FeaturePolicyFeature feature) {
+  wtf_size_t feature_index = static_cast<wtf_size_t>(feature);
+  if (parsed_feature_policies_.size() == 0) {
+    parsed_feature_policies_.resize(
+        static_cast<wtf_size_t>(mojom::blink::FeaturePolicyFeature::kMaxValue) +
+        1);
+  } else if (parsed_feature_policies_[feature_index]) {
+    return true;
+  }
+  parsed_feature_policies_[feature_index] = true;
+  return false;
+}
+
 const SecurityOrigin* Document::GetSecurityOrigin() const {
   return GetSecurityContext().GetSecurityOrigin();
 }
@@ -994,47 +1031,50 @@
 }
 
 PublicURLManager& Document::GetPublicURLManager() {
-  return ExecutionContext::GetPublicURLManager();
+  DCHECK(GetExecutionContext());
+  return GetExecutionContext()->GetPublicURLManager();
 }
 
 bool Document::IsContextPaused() const {
-  return ExecutionContext::IsContextPaused();
+  return GetExecutionContext() ? GetExecutionContext()->IsContextPaused()
+                               : false;
 }
 
 bool Document::IsContextDestroyed() const {
-  return ExecutionContext::IsContextDestroyed();
+  return GetExecutionContext() ? GetExecutionContext()->IsContextDestroyed()
+                               : true;
 }
 
 ContentSecurityPolicyDelegate& Document::GetContentSecurityPolicyDelegate() {
-  return ExecutionContext::GetContentSecurityPolicyDelegate();
+  return GetExecutionContext()->GetContentSecurityPolicyDelegate();
 }
 
 SecureContextMode Document::GetSecureContextMode() const {
-  return ExecutionContext::GetSecureContextMode();
+  return GetSecurityContext().GetSecureContextMode();
 }
 
 bool Document::IsSecureContext() const {
-  return ExecutionContext::IsSecureContext();
+  return GetExecutionContext()->IsSecureContext();
 }
 
 bool Document::IsSecureContext(String& error_message) const {
-  return ExecutionContext::IsSecureContext(error_message);
+  return GetExecutionContext()->IsSecureContext(error_message);
 }
 
 void Document::SetReferrerPolicy(network::mojom::ReferrerPolicy policy) {
-  ExecutionContext::SetReferrerPolicy(policy);
+  GetExecutionContext()->SetReferrerPolicy(policy);
 }
 
 v8::Isolate* Document::GetIsolate() const {
-  return ExecutionContext::GetIsolate();
+  return GetExecutionContext() ? GetExecutionContext()->GetIsolate() : nullptr;
 }
 
 Agent* Document::GetAgent() const {
-  return ExecutionContext::GetAgent();
+  return GetSecurityContext().GetAgent();
 }
 
 OriginTrialContext* Document::GetOriginTrialContext() const {
-  return ExecutionContext::GetOriginTrialContext();
+  return MasterDocument().GetSecurityContext().GetOriginTrialContext();
 }
 
 void Document::SetSecureContextModeForTesting(SecureContextMode mode) {
@@ -1045,8 +1085,9 @@
                                 ReportOptions report_on_failure,
                                 const String& message,
                                 const String& source_file) const {
-  return ExecutionContext::IsFeatureEnabled(feature, report_on_failure, message,
-                                            source_file);
+  return GetExecutionContext() &&
+         GetExecutionContext()->IsFeatureEnabled(feature, report_on_failure,
+                                                 message, source_file);
 }
 
 bool Document::IsFeatureEnabled(mojom::blink::FeaturePolicyFeature feature,
@@ -1054,16 +1095,18 @@
                                 ReportOptions report_on_failure,
                                 const String& message,
                                 const String& source_file) const {
-  return ExecutionContext::IsFeatureEnabled(
-      feature, threshold_value, report_on_failure, message, source_file);
+  return GetExecutionContext() &&
+         GetExecutionContext()->IsFeatureEnabled(
+             feature, threshold_value, report_on_failure, message, source_file);
 }
 
 bool Document::IsFeatureEnabled(mojom::blink::DocumentPolicyFeature feature,
                                 ReportOptions report_option,
                                 const String& message,
                                 const String& source_file) {
-  return ExecutionContext::IsFeatureEnabled(feature, report_option, message,
-                                            source_file);
+  return GetExecutionContext() &&
+         GetExecutionContext()->IsFeatureEnabled(feature, report_option,
+                                                 message, source_file);
 }
 
 bool Document::IsFeatureEnabled(mojom::blink::DocumentPolicyFeature feature,
@@ -1071,12 +1114,13 @@
                                 ReportOptions report_option,
                                 const String& message,
                                 const String& source_file) {
-  return ExecutionContext::IsFeatureEnabled(
-      feature, threshold_value, report_option, message, source_file);
+  return GetExecutionContext() &&
+         GetExecutionContext()->IsFeatureEnabled(
+             feature, threshold_value, report_option, message, source_file);
 }
 
 String Document::addressSpaceForBindings() const {
-  return ExecutionContext::addressSpaceForBindings();
+  return GetExecutionContext()->addressSpaceForBindings();
 }
 
 void Document::ChildrenChanged(const ChildrenChange& change) {
@@ -2906,16 +2950,42 @@
     // don't have a good place that has access to its local root's FrameWidget.
     // TODO(dcheng): If we create FrameWidget before Frame then we could move
     // this to Document::Initialize().
-    if (Platform::Current()->IsThreadedAnimationEnabled() &&
-        GetSettings()->GetAcceleratedCompositingEnabled()) {
-      GetPage()->GetChromeClient().AttachCompositorAnimationTimeline(
-          Timeline().CompositorTimeline(), GetFrame());
-    }
+    AttachCompositorTimeline(Timeline().CompositorTimeline());
   }
 
   Markers().InvalidateRectsForAllTextMatchMarkers();
 }
 
+void Document::AttachCompositorTimeline(
+    CompositorAnimationTimeline* timeline) const {
+  if (!Platform::Current()->IsThreadedAnimationEnabled() ||
+      !GetSettings()->GetAcceleratedCompositingEnabled())
+    return;
+
+  if (timeline->GetAnimationTimeline()->IsScrollTimeline() &&
+      timeline->GetAnimationTimeline()->animation_host())
+    return;
+
+  GetPage()->GetChromeClient().AttachCompositorAnimationTimeline(timeline,
+                                                                 GetFrame());
+}
+
+void Document::DetachCompositorTimeline(
+    CompositorAnimationTimeline* timeline) const {
+  if (!Platform::Current()->IsThreadedAnimationEnabled() ||
+      !GetSettings()->GetAcceleratedCompositingEnabled())
+    return;
+
+  // This requires detaching all animations from timeline first before detaching
+  // timeline.
+  if (timeline->GetAnimationTimeline()->IsScrollTimeline() &&
+      timeline->GetAnimationTimeline()->HasAnimation())
+    return;
+
+  GetPage()->GetChromeClient().DetachCompositorAnimationTimeline(timeline,
+                                                                 GetFrame());
+}
+
 void Document::ClearFocusedElementSoon() {
   if (!clear_focused_element_timer_.IsActive())
     clear_focused_element_timer_.StartOneShot(base::TimeDelta(), FROM_HERE);
@@ -3123,10 +3193,9 @@
   // ExecutionContextLifecycleObserver::contextDestroyed wouldn't be fired.
   network_state_observer_ = MakeGarbageCollected<NetworkStateObserver>(*this);
 
-  // Check for frame_ so we only attach execution contexts with its own
-  // scheduler.
+  // Check for frame_ so we only attach documents with its own scheduler.
   if (frame_)
-    GetAgent()->AttachExecutionContext(this);
+    GetAgent()->AttachDocument(this);
 }
 
 void Document::Shutdown() {
@@ -3202,11 +3271,7 @@
   http_refresh_scheduler_->Cancel();
   CancelFormSubmissions();
 
-  if (Platform::Current()->IsThreadedAnimationEnabled() &&
-      GetSettings()->GetAcceleratedCompositingEnabled()) {
-    GetPage()->GetChromeClient().DetachCompositorAnimationTimeline(
-        Timeline().CompositorTimeline(), GetFrame());
-  }
+  DetachCompositorTimeline(Timeline().CompositorTimeline());
 
   if (frame_->IsLocalRoot())
     GetPage()->GetChromeClient().AttachRootLayer(nullptr, frame_.Get());
@@ -3281,22 +3346,12 @@
   // TODO(crbug.com/729196): Trace why LocalFrameView::DetachFromLayout crashes.
   CHECK(!View()->IsAttached());
 
-  // Check for frame_ so we only detach execution contexts with its own
-  // scheduler.
+  // Check for frame_ so we only detach documents with its own scheduler.
   // TODO(bokan): Can this happen? |frame_| is dereferenced above and CHECKed
   // at top.
   if (frame_)
-    GetAgent()->DetachExecutionContext(this);
+    GetAgent()->DetachDocument(this);
 
-  // TODO(haraken): Call contextDestroyed() before we start any disruptive
-  // operations.
-  // TODO(haraken): Currently we call notifyContextDestroyed() only in
-  // Document::detachLayoutTree(), which means that we don't call
-  // notifyContextDestroyed() for a document that doesn't get detached.
-  // If such a document has any observer, the observer won't get
-  // a contextDestroyed() notification. This can happen for a document
-  // created by DOMImplementation::createDocument().
-  ExecutionContext::NotifyContextDestroyed();
   // TODO(crbug.com/729196): Trace why LocalFrameView::DetachFromLayout crashes.
   CHECK(!View()->IsAttached());
 
@@ -4346,12 +4401,12 @@
 
   DCHECK(parser_);
   PerformanceMonitor::ReportGenericViolation(
-      this, PerformanceMonitor::kDiscouragedAPIUse,
+      domWindow(), PerformanceMonitor::kDiscouragedAPIUse,
       "Avoid using document.write(). "
       "https://developers.google.com/web/updates/2016/08/"
       "removing-document-write",
       base::TimeDelta(), nullptr);
-  probe::BreakableLocation(this, "Document.write");
+  probe::BreakableLocation(domWindow(), "Document.write");
   parser_->insert(text);
 }
 
@@ -4399,7 +4454,7 @@
 }
 
 bool Document::IsTrustedTypesEnabledForDoc() const {
-  return ExecutionContext::RequireTrustedTypes();
+  return GetExecutionContext()->RequireTrustedTypes();
 }
 
 void Document::write(v8::Isolate* isolate,
@@ -4418,14 +4473,6 @@
           exception_state);
 }
 
-EventTarget* Document::ErrorEventTarget() {
-  return domWindow();
-}
-
-void Document::ExceptionThrown(ErrorEvent* event) {
-  MainThreadDebugger::Instance()->ExceptionThrown(this, event);
-}
-
 KURL Document::urlForBinding() const {
   if (!Url().IsNull()) {
     return Url();
@@ -4629,13 +4676,6 @@
   return GetFrame() ? GetFrame()->Loader().UserAgent() : String();
 }
 
-void Document::DisableEval(const String& error_message) {
-  if (!GetFrame())
-    return;
-
-  GetFrame()->GetScriptController().DisableEval(error_message);
-}
-
 void Document::DidLoadAllImports() {
   if (!HaveScriptBlockingStylesheetsLoaded())
     return;
@@ -4768,17 +4808,8 @@
 }
 
 network::mojom::ReferrerPolicy Document::GetReferrerPolicy() const {
-  network::mojom::ReferrerPolicy policy = ExecutionContext::GetReferrerPolicy();
-  // For srcdoc documents without their own policy, walk up the frame
-  // tree to find the document that is either not a srcdoc or doesn't
-  // have its own policy. This algorithm is defined in
-  // https://html.spec.whatwg.org/C/#set-up-a-window-environment-settings-object.
-  if (!frame_ || policy != network::mojom::ReferrerPolicy::kDefault ||
-      !IsSrcdocDocument()) {
-    return policy;
-  }
-  LocalFrame* frame = To<LocalFrame>(frame_->Tree().Parent());
-  return frame->GetDocument()->GetReferrerPolicy();
+  return GetExecutionContext() ? GetExecutionContext()->GetReferrerPolicy()
+                               : network::mojom::ReferrerPolicy::kDefault;
 }
 
 MouseEventWithHitTestResults Document::PerformMouseEventHitTest(
@@ -6503,11 +6534,8 @@
 }
 
 Document* Document::ContextDocument() const {
-  if (context_document_)
-    return context_document_;
-  if (frame_)
-    return const_cast<Document*>(this);
-  return nullptr;
+  return context_document_ ? context_document_.Get()
+                           : const_cast<Document*>(this);
 }
 
 Attr* Document::createAttribute(const AtomicString& name,
@@ -6694,7 +6722,7 @@
     // Forward origin trial freeze policy to the corresponding frame object in
     // the resource coordinator.
     if (auto* document_resource_coordinator = GetResourceCoordinator())
-      SetOriginTrialFreezePolicy(document_resource_coordinator, this);
+      SetOriginTrialFreezePolicy(document_resource_coordinator, domWindow());
   }
 
   // Schedule dropping of the ElementDataCache. We keep it alive for a while
@@ -6962,7 +6990,6 @@
 
 void Document::InitSecurityContext(const DocumentInit& initializer) {
   DCHECK(GetSecurityOrigin());
-
   // If the CSP was provided by the DocumentLoader or is from ImportsController
   // it doesn't need to be bound right now. ImportsController takes a reference
   // to a master document's CSP which is already bound. Document construction
@@ -7009,7 +7036,7 @@
   // main world's CSP (such as for privileged isolated worlds). See
   // https://crbug.com/811528.
   if (IsSandboxed(mojom::blink::WebSandboxFlags::kScripts) &&
-      !ContentSecurityPolicy::ShouldBypassMainWorld(this)) {
+      !ContentSecurityPolicy::ShouldBypassMainWorld(domWindow())) {
     // FIXME: This message should be moved off the console once a solution to
     // https://bugs.webkit.org/show_bug.cgi?id=103274 exists.
     if (reason == kAboutToExecuteScript) {
@@ -7074,10 +7101,6 @@
   return true;
 }
 
-bool Document::IsContextThread() const {
-  return IsMainThread();
-}
-
 void Document::UpdateFocusAppearanceAfterLayout() {
   update_focus_appearance_after_layout_ = true;
 }
@@ -7171,61 +7194,18 @@
   return *resize_observer_controller_;
 }
 
-static void RunAddConsoleMessageTask(mojom::ConsoleMessageSource source,
-                                     mojom::ConsoleMessageLevel level,
-                                     const String& message,
-                                     Document* document,
-                                     bool discard_duplicates) {
-  auto* console_message =
-      MakeGarbageCollected<ConsoleMessage>(source, level, message);
-  document->AddConsoleMessage(console_message, discard_duplicates);
-}
-
-void Document::AddConsoleMessageImpl(ConsoleMessage* console_message,
-                                     bool discard_duplicates) {
-  if (!IsContextThread()) {
-    PostCrossThreadTask(
-        *GetTaskRunner(TaskType::kInternalInspector), FROM_HERE,
-        CrossThreadBindOnce(
-            &RunAddConsoleMessageTask, console_message->Source(),
-            console_message->Level(), console_message->Message(),
-            WrapCrossThreadPersistent(this), discard_duplicates));
-    return;
-  }
-
-  if (!frame_) {
-    if (imports_controller_) {
-      imports_controller_->Master()->GetFrame()->Console().AddMessage(
-          console_message);
-    }
-    return;
-  }
-
-  if (console_message->Location()->IsUnknown()) {
-    // TODO(dgozman): capture correct location at call places instead.
-    unsigned line_number = 0;
-    if (!IsInDocumentWrite() && GetScriptableDocumentParser()) {
-      ScriptableDocumentParser* parser = GetScriptableDocumentParser();
-      if (parser->IsParsingAtLineNumber())
-        line_number = parser->LineNumber().OneBasedInt();
-    }
-    Vector<DOMNodeId> nodes(console_message->Nodes());
-    console_message = MakeGarbageCollected<ConsoleMessage>(
-        console_message->Source(), console_message->Level(),
-        console_message->Message(),
-        std::make_unique<SourceLocation>(Url().GetString(), line_number, 0,
-                                         nullptr));
-    console_message->SetNodes(frame_, std::move(nodes));
-  }
-
-  frame_->Console().AddMessage(console_message, discard_duplicates);
+void Document::AddConsoleMessage(ConsoleMessage* message,
+                                 bool discard_duplicates) {
+  // Don't let non-attached Documents spam the console.
+  if (domWindow())
+    domWindow()->AddConsoleMessage(message, discard_duplicates);
 }
 
 void Document::AddConsoleMessageImpl(mojom::ConsoleMessageSource source,
                                      mojom::ConsoleMessageLevel level,
                                      const String& message,
                                      bool discard_duplicates) {
-  AddConsoleMessageImpl(
+  AddConsoleMessage(
       MakeGarbageCollected<ConsoleMessage>(source, level, message),
       discard_duplicates);
 }
@@ -7237,7 +7217,8 @@
     return;
   }
 
-  page->GetInspectorIssueStorage().AddInspectorIssue(this, issue);
+  page->GetInspectorIssueStorage().AddInspectorIssue(GetExecutionContext(),
+                                                     issue);
 }
 
 void Document::AddToTopLayer(Element* element, const Element* before) {
@@ -7386,11 +7367,10 @@
 
 ScriptedIdleTaskController& Document::EnsureScriptedIdleTaskController() {
   if (!scripted_idle_task_controller_) {
-    scripted_idle_task_controller_ = ScriptedIdleTaskController::Create(this);
-    // We need to make sure that we don't start up the idle controller if we
-    // don't have an attached frame and if execution context is destroyed.
-    if (!frame_ || !frame_->IsAttached() ||
-        ExecutionContext::IsContextDestroyed()) {
+    scripted_idle_task_controller_ =
+        ScriptedIdleTaskController::Create(domWindow());
+    // We need to make sure that we don't start up if we're detached.
+    if (!domWindow() || domWindow()->IsContextDestroyed()) {
       scripted_idle_task_controller_->ContextLifecycleStateChanged(
           mojom::FrameLifecycleState::kFrozen);
     }
@@ -8223,7 +8203,6 @@
   Supplementable<Document>::Trace(visitor);
   TreeScope::Trace(visitor);
   ContainerNode::Trace(visitor);
-  ExecutionContext::Trace(visitor);
 }
 
 void Document::RecordUkmOutliveTimeAfterShutdown(int outlive_time_count) {
@@ -8377,7 +8356,7 @@
       ReportType::kFeaturePolicyViolation, Url().GetString(), body);
 
   // Send the feature policy violation report to any ReportingObservers.
-  auto* reporting_context = ReportingContext::From(this);
+  auto* reporting_context = ReportingContext::From(domWindow());
   reporting_context->QueueReport(report);
 
   // TODO(iclelland): Report something different in report-only mode
@@ -8591,26 +8570,6 @@
       show_dialog);
 }
 
-const Document* DocumentForTrustedTypes(const Document* doc) {
-  // The Trusted Type factory & friends are stored on the window. For
-  // programmatically created docs (like createHTMLDocument) let's use the one
-  // from the context document.
-  DCHECK(doc);
-  while (doc->ContextDocument() && !doc->ExecutingWindow())
-    doc = doc->ContextDocument();
-  return doc;
-}
-
-TrustedTypePolicyFactory* Document::GetTrustedTypes() const {
-  const Document* doc = DocumentForTrustedTypes(this);
-  return doc->ExecutingWindow() ? doc->ExecutingWindow()->trustedTypes()
-                                : nullptr;
-}
-
-bool Document::RequireTrustedTypes() const {
-  return DocumentForTrustedTypes(this)->ExecutionContext::RequireTrustedTypes();
-}
-
 void Document::ColorSchemeChanged() {
   UpdateForcedColors();
   GetStyleEngine().ColorSchemeChanged();
diff --git a/third_party/blink/renderer/core/dom/document.h b/third_party/blink/renderer/core/dom/document.h
index d7fc039..9dcb8b0 100644
--- a/third_party/blink/renderer/core/dom/document.h
+++ b/third_party/blink/renderer/core/dom/document.h
@@ -98,6 +98,7 @@
 class CanvasFontCache;
 class ChromeClient;
 class Comment;
+class CompositorAnimationTimeline;
 class ComputedAccessibleNode;
 class DisplayLockContext;
 class ElementIntersectionObserverData;
@@ -269,16 +270,11 @@
 // of a tree of DOM nodes, generally resulting from the parsing of an markup
 // (typically, HTML) resource. It provides both the content to be displayed to
 // the user in a frame and an execution context for JavaScript code.
-// TODO(crbug.com/1029822): Virtual inheritance is used here temporarily to
-// enable moving ExecutionContext from Document to LocalDOMWindow. This allows
-// Document's inheritance of ExecutionContext to be hidden, while still allowing
-// Document to inherit from some of ExecutionContext's parent classes publicly.
 class CORE_EXPORT Document : public ContainerNode,
                              public TreeScope,
-                             public virtual ConsoleLogger,
-                             public virtual UseCounter,
-                             public virtual FeaturePolicyParserDelegate,
-                             private ExecutionContext,
+                             public ConsoleLogger,
+                             public UseCounter,
+                             public FeaturePolicyParserDelegate,
                              public Supplementable<Document> {
   DEFINE_WRAPPERTYPEINFO();
   USING_GARBAGE_COLLECTED_MIXIN(Document);
@@ -309,13 +305,42 @@
 
   using TreeScope::getElementById;
 
-  // ExecutionContext overrides:
-  bool IsDocument() const final { return true; }
-  bool ShouldInstallV8Extensions() const final;
-  ContentSecurityPolicy* GetContentSecurityPolicyForWorld() override;
+  // TODO(crbug.com/1029822) Former ExecutionContext overrides. Most of these
+  // should move to LocalDOMWindow.
+  ContentSecurityPolicy* GetContentSecurityPolicyForWorld();
+  LocalDOMWindow* ExecutingWindow() const;
+  String UserAgent() const;
+  // TODO(https://crbug.com/880986): Implement Document's HTTPS state in more
+  // spec-conformant way.
+  HttpsState GetHttpsState() const {
+    return CalculateHttpsState(GetSecurityOrigin());
+  }
+  bool CanExecuteScripts(ReasonForCallingCanExecuteScripts);
+  String OutgoingReferrer() const;
+  network::mojom::ReferrerPolicy GetReferrerPolicy() const;
+  CoreProbeSink* GetProbeSink();
+  BrowserInterfaceBrokerProxy& GetBrowserInterfaceBroker();
+  FrameOrWorkerScheduler* GetScheduler();
+  void CountPotentialFeaturePolicyViolation(
+      mojom::blink::FeaturePolicyFeature) const;
+  void ReportFeaturePolicyViolation(
+      mojom::blink::FeaturePolicyFeature,
+      mojom::FeaturePolicyDisposition,
+      const String& message = g_empty_string,
+      // If source_file is set to empty string,
+      // current JS file would be used as source_file instead.
+      const String& source_file = g_empty_string) const;
 
-  SecurityContext& GetSecurityContext() final { return security_context_; }
-  const SecurityContext& GetSecurityContext() const final {
+  // FeaturePolicyParserDelegate override
+  // TODO(crbug.com/1029822) FeaturePolicyParserDelegate overrides, these
+  // should migrate to LocalDOMWindow.
+  bool FeatureEnabled(OriginTrialFeature) const override;
+  void CountFeaturePolicyUsage(mojom::WebFeature feature) override;
+  bool FeaturePolicyFeatureObserved(
+      mojom::blink::FeaturePolicyFeature feature) override;
+
+  SecurityContext& GetSecurityContext() { return security_context_; }
+  const SecurityContext& GetSecurityContext() const {
     return security_context_;
   }
 
@@ -325,22 +350,16 @@
   // inherited from Node, or domWindow().
   // Downcasts will cast to a LocalDOMWindow, then use
   // LocalDOMWindow::document() if the Document is what is actually needed.
-  ExecutionContext* ToExecutionContext() { return this; }
-  const ExecutionContext* ToExecutionContext() const { return this; }
+  ExecutionContext* ToExecutionContext();
+  const ExecutionContext* ToExecutionContext() const;
   static Document* From(ExecutionContext* context) {
     return context ? &From(*context) : nullptr;
   }
-  static Document& From(ExecutionContext& context) {
-    SECURITY_DCHECK(context.IsDocument());
-    return static_cast<Document&>(context);
-  }
+  static Document& From(ExecutionContext& context);
   static const Document* From(const ExecutionContext* context) {
     return context ? &From(*context) : nullptr;
   }
-  static const Document& From(const ExecutionContext& context) {
-    SECURITY_DCHECK(context.IsDocument());
-    return static_cast<const Document&>(context);
-  }
+  static const Document& From(const ExecutionContext& context);
   static Document* DynamicFrom(ExecutionContext* context) {
     return context && context->IsDocument() ? From(context) : nullptr;
   }
@@ -424,9 +443,6 @@
 
   ViewportData& GetViewportData() const { return *viewport_data_; }
 
-  String OutgoingReferrer() const override;
-  network::mojom::ReferrerPolicy GetReferrerPolicy() const override;
-
   void SetDoctype(DocumentType*);
   DocumentType* doctype() const { return doc_type_.Get(); }
 
@@ -585,7 +601,6 @@
     return saw_elements_in_known_namespaces_;
   }
 
-  bool CanExecuteScripts(ReasonForCallingCanExecuteScripts) override;
   bool IsScriptExecutionReady() const {
     return HaveImportsLoaded() && HaveScriptBlockingStylesheetsLoaded();
   }
@@ -685,9 +700,7 @@
                                   int& margin_bottom,
                                   int& margin_left);
 
-  ResourceFetcher* Fetcher() const override { return fetcher_.Get(); }
-
-  using ExecutionContext::NotifyContextDestroyed;
+  ResourceFetcher* Fetcher() const { return fetcher_.Get(); }
 
   void Initialize();
   virtual void Shutdown();
@@ -809,7 +822,7 @@
 
   // Return the document URL, or an empty URL if it's unavailable.
   // This is not an implementation of web-exposed Document.prototype.URL.
-  const KURL& Url() const final { return url_; }
+  const KURL& Url() const { return url_; }
   void SetURL(const KURL&);
 
   // Bind the url to document.url, if unavailable bind to about:blank.
@@ -820,7 +833,7 @@
 
   // Document base URL.
   // https://html.spec.whatwg.org/C/#document-base-url
-  const KURL& BaseURL() const final;
+  const KURL& BaseURL() const;
   void SetBaseURLOverride(const KURL&);
   const KURL& BaseURLOverride() const { return base_url_override_; }
   KURL ValidBaseElementURL() const;
@@ -834,7 +847,7 @@
   // Creates URL based on passed relative url and this documents base URL.
   // Depending on base URL value it is possible that parent document
   // base URL will be used instead. Uses CompleteURLWithOverride internally.
-  KURL CompleteURL(const String&) const final;
+  KURL CompleteURL(const String&) const;
   // Creates URL based on passed relative url and passed base URL override.
   KURL CompleteURLWithOverride(const String&,
                                const KURL& base_url_override) const;
@@ -843,15 +856,6 @@
   // the document which created it.
   static bool ShouldInheritSecurityOriginFromOwner(const KURL&);
 
-  String UserAgent() const final;
-  void DisableEval(const String& error_message) final;
-
-  // TODO(https://crbug.com/880986): Implement Document's HTTPS state in more
-  // spec-conformant way.
-  HttpsState GetHttpsState() const final {
-    return CalculateHttpsState(GetSecurityOrigin());
-  }
-
   CSSStyleSheet& ElementSheet();
 
   virtual DocumentParser* CreateParser();
@@ -1301,9 +1305,6 @@
   void SetContainsPlugins() { contains_plugins_ = true; }
   bool ContainsPlugins() const { return contains_plugins_; }
 
-  bool IsContextThread() const final;
-  bool IsJSExecutionForbidden() const final { return false; }
-
   void EnqueueResizeEvent();
   void EnqueueScrollEventForNode(Node*);
   void EnqueueScrollEndEventForNode(Node*);
@@ -1359,9 +1360,6 @@
 
   ScriptedAnimationController& GetScriptedAnimationController();
 
-  EventTarget* ErrorEventTarget() final;
-  void ExceptionThrown(ErrorEvent*) final;
-
   void InitDNSPrefetch();
 
   bool IsInDocumentWrite() const { return write_recursion_depth_ > 0; }
@@ -1421,6 +1419,9 @@
     return *worklet_animation_controller_;
   }
 
+  void AttachCompositorTimeline(CompositorAnimationTimeline*) const;
+  void DetachCompositorTimeline(CompositorAnimationTimeline*) const;
+
   void AddToTopLayer(Element*, const Element* before = nullptr);
   void RemoveFromTopLayer(Element*);
   const HeapVector<Member<Element>>& TopLayerElements() const {
@@ -1439,17 +1440,13 @@
   void DidAssociateFormControl(Element*);
 
   void AddConsoleMessage(ConsoleMessage* message,
-                         bool discard_duplicates = false) {
-    AddConsoleMessageImpl(message, discard_duplicates);
-  }
-  void AddConsoleMessageImpl(ConsoleMessage*, bool discard_duplicates) final;
+                         bool discard_duplicates = false);
   void AddConsoleMessageImpl(mojom::ConsoleMessageSource,
                              mojom::ConsoleMessageLevel,
                              const String& message,
                              bool discard_duplicates) final;
   void AddInspectorIssue(InspectorIssue*);
 
-  LocalDOMWindow* ExecutingWindow() const final;
   LocalFrame* ExecutingFrame();
 
   DocumentLifecycle& Lifecycle() { return lifecycle_; }
@@ -1529,10 +1526,6 @@
   // text field in a non-secure context.
   void MaybeQueueSendDidEditFieldInInsecureContext();
 
-  CoreProbeSink* GetProbeSink() final;
-
-  BrowserInterfaceBrokerProxy& GetBrowserInterfaceBroker() final;
-
   // May return nullptr when PerformanceManager instrumentation is disabled.
   DocumentResourceCoordinator* GetResourceCoordinator();
 
@@ -1562,10 +1555,7 @@
   // attempts (both successful and not successful) by the page.
   FontMatchingMetrics* GetFontMatchingMetrics();
 
-  // May return nullptr.
-  FrameOrWorkerScheduler* GetScheduler() override;
-
-  scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunner(TaskType) override;
+  scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunner(TaskType);
 
   void RecordUkmOutliveTimeAfterShutdown(int outlive_time_count);
 
@@ -1624,16 +1614,6 @@
     return window_agent_factory_;
   }
 
-  void CountPotentialFeaturePolicyViolation(
-      mojom::blink::FeaturePolicyFeature) const override;
-  void ReportFeaturePolicyViolation(
-      mojom::blink::FeaturePolicyFeature,
-      mojom::FeaturePolicyDisposition,
-      const String& message = g_empty_string,
-      // If source_file is set to empty string,
-      // current JS file would be used as source_file instead.
-      const String& source_file = g_empty_string) const override;
-
   void IncrementNumberOfCanvases();
 
   void ProcessJavaScriptUrl(const KURL&, network::mojom::CSPDisposition);
@@ -1701,9 +1681,6 @@
   // inside a cross-process frame (MimeHandlerView).
   void SetShowBeforeUnloadDialog(bool show_dialog);
 
-  TrustedTypePolicyFactory* GetTrustedTypes() const override;
-  bool RequireTrustedTypes() const override;
-
   void ColorSchemeChanged();
 
   // A new vision deficiency is being emulated through DevTools.
@@ -2307,6 +2284,11 @@
   // DocumentLoader.
   DocumentPolicy::FeatureState pending_dp_headers_;
 
+  // Tracks which feature policies have already been parsed, so as not to count
+  // them multiple times.
+  // The size of this vector is 0 until FeaturePolicyFeatureObserved is called.
+  Vector<bool> parsed_feature_policies_;
+
   AtomicString override_last_modified_;
 
   // Map from isolated world IDs to their ContentSecurityPolicy instances.
diff --git a/third_party/blink/renderer/core/dom/element.cc b/third_party/blink/renderer/core/dom/element.cc
index 0a353465..73f1f33 100644
--- a/third_party/blink/renderer/core/dom/element.cc
+++ b/third_party/blink/renderer/core/dom/element.cc
@@ -931,11 +931,11 @@
   // NOTE: AnyAttributeMatches in selector_checker.cc currently assumes that all
   // lazy attributes have a null namespace.  If that ever changes we'll need to
   // fix that code.
-  if (GetElementData()->style_attribute_is_dirty_) {
+  if (GetElementData()->style_attribute_is_dirty()) {
     DCHECK(IsStyledElement());
     SynchronizeStyleAttributeInternal();
   }
-  if (GetElementData()->animated_svg_attributes_are_dirty_)
+  if (GetElementData()->animated_svg_attributes_are_dirty())
     To<SVGElement>(this)->SynchronizeAnimatedSVGAttribute(AnyQName());
 }
 
@@ -943,12 +943,12 @@
   if (!GetElementData())
     return;
   if (UNLIKELY(name == html_names::kStyleAttr &&
-               GetElementData()->style_attribute_is_dirty_)) {
+               GetElementData()->style_attribute_is_dirty())) {
     DCHECK(IsStyledElement());
     SynchronizeStyleAttributeInternal();
     return;
   }
-  if (UNLIKELY(GetElementData()->animated_svg_attributes_are_dirty_)) {
+  if (UNLIKELY(GetElementData()->animated_svg_attributes_are_dirty())) {
     // See comment in the AtomicString version of SynchronizeAttribute()
     // also.
     To<SVGElement>(this)->SynchronizeAnimatedSVGAttribute(name);
@@ -960,13 +960,13 @@
   // you don't have a full QualifiedName, e.g when called from DOM API.
   if (!GetElementData())
     return;
-  if (GetElementData()->style_attribute_is_dirty_ &&
+  if (GetElementData()->style_attribute_is_dirty() &&
       LowercaseIfNecessary(local_name) == html_names::kStyleAttr.LocalName()) {
     DCHECK(IsStyledElement());
     SynchronizeStyleAttributeInternal();
     return;
   }
-  if (GetElementData()->animated_svg_attributes_are_dirty_) {
+  if (GetElementData()->animated_svg_attributes_are_dirty()) {
     // We're not passing a namespace argument on purpose. SVGNames::*Attr are
     // defined w/o namespaces as well.
 
@@ -2444,7 +2444,7 @@
     if (name == html_names::kStyleAttr) {
       StyleAttributeChanged(params.new_value, params.reason);
     } else if (IsPresentationAttribute(name)) {
-      GetElementData()->presentation_attribute_style_is_dirty_ = true;
+      GetElementData()->SetPresentationAttributeStyleIsDirty(true);
       SetNeedsStyleRecalc(kLocalStyleChange,
                           StyleChangeReasonForTracing::FromAttribute(name));
     } else if (RuntimeEnabledFeatures::InvisibleDOMEnabled() &&
@@ -4071,7 +4071,7 @@
   wtf_size_t index = GetElementData()->Attributes().FindIndex(local_name);
   if (index == kNotFound) {
     if (UNLIKELY(local_name == html_names::kStyleAttr) &&
-        GetElementData()->style_attribute_is_dirty_ && IsStyledElement())
+        GetElementData()->style_attribute_is_dirty() && IsStyledElement())
       RemoveAllInlineStyleProperties();
     return;
   }
@@ -6059,8 +6059,8 @@
 void Element::SynchronizeStyleAttributeInternal() const {
   DCHECK(IsStyledElement());
   DCHECK(GetElementData());
-  DCHECK(GetElementData()->style_attribute_is_dirty_);
-  GetElementData()->style_attribute_is_dirty_ = false;
+  DCHECK(GetElementData()->style_attribute_is_dirty());
+  GetElementData()->SetStyleAttributeIsDirty(false);
   const CSSPropertyValueSet* inline_style = InlineStyle();
   const_cast<Element*>(this)->SetSynchronizedLazyAttribute(
       html_names::kStyleAttr,
@@ -6153,7 +6153,7 @@
     SetInlineStyleFromString(new_style_string);
   }
 
-  GetElementData()->style_attribute_is_dirty_ = false;
+  GetElementData()->SetStyleAttributeIsDirty(false);
 
   SetNeedsStyleRecalc(kLocalStyleChange,
                       StyleChangeReasonForTracing::Create(
@@ -6253,7 +6253,7 @@
   // ShareableElementData doesn't store presentation attribute style, so make
   // sure we have a UniqueElementData.
   UniqueElementData& element_data = EnsureUniqueElementData();
-  element_data.presentation_attribute_style_is_dirty_ = false;
+  element_data.SetPresentationAttributeStyleIsDirty(false);
   element_data.presentation_attribute_style_ =
       ComputePresentationAttributeStyle(*this);
 }
@@ -6463,7 +6463,7 @@
 
 void Element::InvalidateStyleAttribute() {
   DCHECK(GetElementData());
-  GetElementData()->style_attribute_is_dirty_ = true;
+  GetElementData()->SetStyleAttributeIsDirty(true);
   SetNeedsStyleRecalc(kLocalStyleChange,
                       StyleChangeReasonForTracing::Create(
                           style_change_reason::kInlineCSSStyleMutated));
diff --git a/third_party/blink/renderer/core/dom/element.h b/third_party/blink/renderer/core/dom/element.h
index 2bc022d..8f06e4a 100644
--- a/third_party/blink/renderer/core/dom/element.h
+++ b/third_party/blink/renderer/core/dom/element.h
@@ -1330,7 +1330,7 @@
 inline const CSSPropertyValueSet* Element::PresentationAttributeStyle() {
   if (!GetElementData())
     return nullptr;
-  if (GetElementData()->presentation_attribute_style_is_dirty_)
+  if (GetElementData()->presentation_attribute_style_is_dirty())
     UpdatePresentationAttributeStyle();
   // Need to call elementData() again since updatePresentationAttributeStyle()
   // might swap it with a UniqueElementData.
diff --git a/third_party/blink/renderer/core/dom/element_data.cc b/third_party/blink/renderer/core/dom/element_data.cc
index c64ec79..cb990d55 100644
--- a/third_party/blink/renderer/core/dom/element_data.cc
+++ b/third_party/blink/renderer/core/dom/element_data.cc
@@ -52,27 +52,27 @@
 }
 
 ElementData::ElementData()
-    : is_unique_(true),
-      array_size_(0),
-      presentation_attribute_style_is_dirty_(false),
-      style_attribute_is_dirty_(false),
-      animated_svg_attributes_are_dirty_(false) {}
+    : bit_field_(IsUniqueFlag::encode(true) | ArraySize::encode(0) |
+                 PresentationAttributeStyleIsDirty::encode(false) |
+                 StyleAttributeIsDirty::encode(false) |
+                 AnimatedSvgAttributesAreDirty::encode(false)) {}
 
 ElementData::ElementData(unsigned array_size)
-    : is_unique_(false),
-      array_size_(array_size),
-      presentation_attribute_style_is_dirty_(false),
-      style_attribute_is_dirty_(false),
-      animated_svg_attributes_are_dirty_(false) {}
+    : bit_field_(IsUniqueFlag::encode(false) | ArraySize::encode(array_size) |
+                 PresentationAttributeStyleIsDirty::encode(false) |
+                 StyleAttributeIsDirty::encode(false) |
+                 AnimatedSvgAttributesAreDirty::encode(false)) {}
 
 ElementData::ElementData(const ElementData& other, bool is_unique)
-    : is_unique_(is_unique),
-      array_size_(is_unique ? 0 : other.Attributes().size()),
-      presentation_attribute_style_is_dirty_(
-          other.presentation_attribute_style_is_dirty_),
-      style_attribute_is_dirty_(other.style_attribute_is_dirty_),
-      animated_svg_attributes_are_dirty_(
-          other.animated_svg_attributes_are_dirty_),
+    : bit_field_(
+          IsUniqueFlag::encode(is_unique) |
+          ArraySize::encode(is_unique ? 0 : other.Attributes().size()) |
+          PresentationAttributeStyleIsDirty::encode(
+              other.bit_field_.get<PresentationAttributeStyleIsDirty>()) |
+          StyleAttributeIsDirty::encode(
+              other.bit_field_.get<StyleAttributeIsDirty>()) |
+          AnimatedSvgAttributesAreDirty::encode(
+              other.bit_field_.get<AnimatedSvgAttributesAreDirty>())),
       class_names_(other.class_names_),
       id_for_style_resolution_(other.id_for_style_resolution_) {
   // NOTE: The inline style is copied by the subclass copy constructor since we
@@ -111,12 +111,10 @@
 }
 
 void ElementData::Trace(Visitor* visitor) {
-  if (is_unique_) {
-    DCHECK(DynamicTo<UniqueElementData>(this));
-    To<UniqueElementData>(this)->TraceAfterDispatch(visitor);
+  if (bit_field_.get_concurrently<IsUniqueFlag>()) {
+    static_cast<UniqueElementData*>(this)->TraceAfterDispatch(visitor);
   } else {
-    DCHECK(DynamicTo<ShareableElementData>(this));
-    To<ShareableElementData>(this)->TraceAfterDispatch(visitor);
+    static_cast<ShareableElementData*>(this)->TraceAfterDispatch(visitor);
   }
 }
 
@@ -126,12 +124,12 @@
 
 ShareableElementData::ShareableElementData(const Vector<Attribute>& attributes)
     : ElementData(attributes.size()) {
-  for (unsigned i = 0; i < array_size_; ++i)
+  for (unsigned i = 0; i < bit_field_.get<ArraySize>(); ++i)
     new (&attribute_array_[i]) Attribute(attributes[i]);
 }
 
 ShareableElementData::~ShareableElementData() {
-  for (unsigned i = 0; i < array_size_; ++i)
+  for (unsigned i = 0; i < bit_field_.get<ArraySize>(); ++i)
     attribute_array_[i].~Attribute();
 }
 
@@ -143,7 +141,7 @@
     inline_style_ = other.inline_style_->ImmutableCopyIfNeeded();
   }
 
-  for (unsigned i = 0; i < array_size_; ++i)
+  for (unsigned i = 0; i < bit_field_.get<ArraySize>(); ++i)
     new (&attribute_array_[i]) Attribute(other.attribute_vector_.at(i));
 }
 
diff --git a/third_party/blink/renderer/core/dom/element_data.h b/third_party/blink/renderer/core/dom/element_data.h
index 0164868f..bda3030 100644
--- a/third_party/blink/renderer/core/dom/element_data.h
+++ b/third_party/blink/renderer/core/dom/element_data.h
@@ -37,6 +37,7 @@
 #include "third_party/blink/renderer/core/dom/attribute_collection.h"
 #include "third_party/blink/renderer/core/dom/space_split_string.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/wtf/bit_field.h"
 #include "third_party/blink/renderer/platform/wtf/casting.h"
 #include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
 
@@ -78,23 +79,55 @@
 
   bool IsEquivalent(const ElementData* other) const;
 
-  bool IsUnique() const { return is_unique_; }
+  bool IsUnique() const { return bit_field_.get<IsUniqueFlag>(); }
 
   void TraceAfterDispatch(blink::Visitor*) const;
   void Trace(Visitor*);
 
  protected:
+  using BitField = WTF::ConcurrentlyReadBitField<uint32_t>;
+  using IsUniqueFlag =
+      BitField::DefineFirstValue<bool, 1, WTF::BitFieldValueConstness::kConst>;
+  using ArraySize = IsUniqueFlag::
+      DefineNextValue<uint32_t, 28, WTF::BitFieldValueConstness::kConst>;
+  using PresentationAttributeStyleIsDirty = ArraySize::DefineNextValue<bool, 1>;
+  using StyleAttributeIsDirty =
+      PresentationAttributeStyleIsDirty::DefineNextValue<bool, 1>;
+  using AnimatedSvgAttributesAreDirty =
+      StyleAttributeIsDirty::DefineNextValue<bool, 1>;
+
   ElementData();
   explicit ElementData(unsigned array_size);
   ElementData(const ElementData&, bool is_unique);
 
-  // Keep the type in a bitfield instead of using virtual destructors to avoid
-  // adding a vtable.
-  const unsigned is_unique_ : 1;
-  unsigned array_size_ : 28;
-  mutable unsigned presentation_attribute_style_is_dirty_ : 1;
-  mutable unsigned style_attribute_is_dirty_ : 1;
-  mutable unsigned animated_svg_attributes_are_dirty_ : 1;
+  bool presentation_attribute_style_is_dirty() const {
+    return bit_field_.get<PresentationAttributeStyleIsDirty>();
+  }
+  bool style_attribute_is_dirty() const {
+    return bit_field_.get<StyleAttributeIsDirty>();
+  }
+  bool animated_svg_attributes_are_dirty() const {
+    return bit_field_.get<AnimatedSvgAttributesAreDirty>();
+  }
+
+  // Following 3 fields are meant to be mutable and can change even when const.
+  void SetPresentationAttributeStyleIsDirty(
+      bool presentation_attribute_style_is_dirty) const {
+    const_cast<BitField*>(&bit_field_)
+        ->set<PresentationAttributeStyleIsDirty>(
+            presentation_attribute_style_is_dirty);
+  }
+  void SetStyleAttributeIsDirty(bool style_attribute_is_dirty) const {
+    const_cast<BitField*>(&bit_field_)
+        ->set<StyleAttributeIsDirty>(style_attribute_is_dirty);
+  }
+  void SetAnimatedSvgAttributesAreDirty(
+      bool animated_svg_attributes_are_dirty) const {
+    const_cast<BitField*>(&bit_field_)
+        ->set<AnimatedSvgAttributesAreDirty>(animated_svg_attributes_are_dirty);
+  }
+
+  BitField bit_field_;
 
   mutable Member<CSSPropertyValueSet> inline_style_;
   mutable SpaceSplitString class_names_;
@@ -105,6 +138,8 @@
   friend class ShareableElementData;
   friend class UniqueElementData;
   friend class SVGElement;
+  friend struct DowncastTraits<UniqueElementData>;
+  friend struct DowncastTraits<ShareableElementData>;
 
   UniqueElementData* MakeUniqueCopy() const;
 };
@@ -138,7 +173,9 @@
 
 template <>
 struct DowncastTraits<ShareableElementData> {
-  static bool AllowFrom(const ElementData& data) { return !data.IsUnique(); }
+  static bool AllowFrom(const ElementData& data) {
+    return !data.bit_field_.get<ElementData::IsUniqueFlag>();
+  }
 };
 
 #if defined(COMPILER_MSVC)
@@ -174,12 +211,14 @@
 
 template <>
 struct DowncastTraits<UniqueElementData> {
-  static bool AllowFrom(const ElementData& data) { return data.IsUnique(); }
+  static bool AllowFrom(const ElementData& data) {
+    return data.bit_field_.get<ElementData::IsUniqueFlag>();
+  }
 };
 
 inline const CSSPropertyValueSet* ElementData::PresentationAttributeStyle()
     const {
-  if (!is_unique_)
+  if (!bit_field_.get<IsUniqueFlag>())
     return nullptr;
   return To<UniqueElementData>(this)->presentation_attribute_style_.Get();
 }
@@ -191,7 +230,7 @@
 }
 
 inline AttributeCollection ShareableElementData::Attributes() const {
-  return AttributeCollection(attribute_array_, array_size_);
+  return AttributeCollection(attribute_array_, bit_field_.get<ArraySize>());
 }
 
 inline AttributeCollection UniqueElementData::Attributes() const {
diff --git a/third_party/blink/renderer/core/dom/node.cc b/third_party/blink/renderer/core/dom/node.cc
index 83507aeb..7cbcc09 100644
--- a/third_party/blink/renderer/core/dom/node.cc
+++ b/third_party/blink/renderer/core/dom/node.cc
@@ -2613,7 +2613,7 @@
 
 ExecutionContext* Node::GetExecutionContext() const {
   if (auto* document = GetDocument().ContextDocument())
-    return document->ToExecutionContext();
+    return document->domWindow();
   return nullptr;
 }
 
diff --git a/third_party/blink/renderer/core/dom/node_rare_data.cc b/third_party/blink/renderer/core/dom/node_rare_data.cc
index 43337f3..50a72f9d 100644
--- a/third_party/blink/renderer/core/dom/node_rare_data.cc
+++ b/third_party/blink/renderer/core/dom/node_rare_data.cc
@@ -78,8 +78,8 @@
 }
 
 void NodeData::Trace(Visitor* visitor) {
-  if (is_rare_data_) {
-    if (is_element_rare_data_)
+  if (bit_field_.get_concurrently<IsRareData>()) {
+    if (bit_field_.get_concurrently<IsElementRareData>())
       static_cast<ElementRareData*>(this)->TraceAfterDispatch(visitor);
     else
       static_cast<NodeRareData*>(this)->TraceAfterDispatch(visitor);
@@ -117,7 +117,7 @@
 }
 
 void NodeRareData::FinalizeGarbageCollectedObject() {
-  if (is_element_rare_data_)
+  if (bit_field_.get<IsElementRareData>())
     static_cast<ElementRareData*>(this)->~ElementRareData();
   else
     this->~NodeRareData();
diff --git a/third_party/blink/renderer/core/dom/node_rare_data.h b/third_party/blink/renderer/core/dom/node_rare_data.h
index 0c3b213..f38a337 100644
--- a/third_party/blink/renderer/core/dom/node_rare_data.h
+++ b/third_party/blink/renderer/core/dom/node_rare_data.h
@@ -25,6 +25,7 @@
 #include "base/macros.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
 #include "third_party/blink/renderer/platform/heap/heap.h"
+#include "third_party/blink/renderer/platform/wtf/bit_field.h"
 #include "third_party/blink/renderer/platform/wtf/hash_set.h"
 
 namespace blink {
@@ -72,9 +73,9 @@
   NodeData(bool is_rare_data, bool is_element_rare_data)
       : connected_frame_count_(0),
         element_flags_(0),
-        restyle_flags_(0),
-        is_element_rare_data_(is_element_rare_data),
-        is_rare_data_(is_rare_data) {
+        bit_field_(RestyleFlags::encode(0) |
+                   IsElementRareData::encode(is_element_rare_data) |
+                   IsRareData::encode(is_rare_data)) {
     DCHECK(!is_element_rare_data || is_rare_data);
   }
   void Trace(Visitor*);
@@ -87,15 +88,17 @@
   };
 
  protected:
-  // The top 4 fields belong to NodeRareData. They are located here to conserve
-  // space and avoid increase in size of NodeRareData (without locating the
-  // fields here, is_rare_data_ will be padded thus increasing the size of
-  // NodeRareData by 8 bytes).
-  unsigned connected_frame_count_ : kConnectedFrameCountBits;
-  unsigned element_flags_ : kNumberOfElementFlags;
-  unsigned restyle_flags_ : kNumberOfDynamicRestyleFlags;
-  const unsigned is_element_rare_data_ : 1;
-  const unsigned is_rare_data_ : 1;
+  using BitField = WTF::ConcurrentlyReadBitField<uint16_t>;
+  using RestyleFlags =
+      BitField::DefineFirstValue<uint16_t, kNumberOfDynamicRestyleFlags>;
+  using IsElementRareData = RestyleFlags::
+      DefineNextValue<bool, 1, WTF::BitFieldValueConstness::kConst>;
+  using IsRareData = IsElementRareData::
+      DefineNextValue<bool, 1, WTF::BitFieldValueConstness::kConst>;
+
+  uint16_t connected_frame_count_ : kConnectedFrameCountBits;
+  uint16_t element_flags_ : kNumberOfElementFlags;
+  BitField bit_field_;
 };
 
 class GC_PLUGIN_IGNORE("Manual dispatch implemented in NodeData.")
@@ -165,7 +168,7 @@
     return *mutation_observer_data_;
   }
 
-  unsigned ConnectedSubframeCount() const { return connected_frame_count_; }
+  uint16_t ConnectedSubframeCount() const { return connected_frame_count_; }
   void IncrementConnectedSubframeCount();
   void DecrementConnectedSubframeCount() {
     DCHECK(connected_frame_count_);
@@ -173,25 +176,27 @@
   }
 
   bool HasElementFlag(ElementFlags mask) const {
-    return element_flags_ & static_cast<unsigned>(mask);
+    return element_flags_ & static_cast<uint16_t>(mask);
   }
   void SetElementFlag(ElementFlags mask, bool value) {
-    element_flags_ = (element_flags_ & ~static_cast<unsigned>(mask)) |
-                     (-(int32_t)value & static_cast<unsigned>(mask));
+    element_flags_ =
+        (element_flags_ & ~static_cast<uint16_t>(mask)) |
+        (-static_cast<uint16_t>(value) & static_cast<uint16_t>(mask));
   }
   void ClearElementFlag(ElementFlags mask) {
-    element_flags_ &= ~static_cast<unsigned>(mask);
+    element_flags_ &= ~static_cast<uint16_t>(mask);
   }
 
   bool HasRestyleFlag(DynamicRestyleFlags mask) const {
-    return restyle_flags_ & static_cast<unsigned>(mask);
+    return bit_field_.get<RestyleFlags>() & static_cast<uint16_t>(mask);
   }
   void SetRestyleFlag(DynamicRestyleFlags mask) {
-    restyle_flags_ |= static_cast<unsigned>(mask);
-    CHECK(restyle_flags_);
+    bit_field_.set<RestyleFlags>(bit_field_.get<RestyleFlags>() |
+                                 static_cast<uint16_t>(mask));
+    CHECK(bit_field_.get<RestyleFlags>());
   }
-  bool HasRestyleFlags() const { return restyle_flags_; }
-  void ClearRestyleFlags() { restyle_flags_ = 0; }
+  bool HasRestyleFlags() const { return bit_field_.get<RestyleFlags>(); }
+  void ClearRestyleFlags() { bit_field_.set<RestyleFlags>(0); }
 
   void TraceAfterDispatch(blink::Visitor*) const;
   void FinalizeGarbageCollectedObject();
diff --git a/third_party/blink/renderer/core/editing/spellcheck/idle_spell_check_controller_test.cc b/third_party/blink/renderer/core/editing/spellcheck/idle_spell_check_controller_test.cc
index e0f8e8d..fb13b4c 100644
--- a/third_party/blink/renderer/core/editing/spellcheck/idle_spell_check_controller_test.cc
+++ b/third_party/blink/renderer/core/editing/spellcheck/idle_spell_check_controller_test.cc
@@ -7,6 +7,7 @@
 #include "third_party/blink/renderer/core/dom/element.h"
 #include "third_party/blink/renderer/core/editing/spellcheck/spell_check_test_base.h"
 #include "third_party/blink/renderer/core/editing/spellcheck/spell_checker.h"
+#include "third_party/blink/renderer/core/frame/local_dom_window.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/core/html/html_object_element.h"
 
@@ -115,25 +116,25 @@
 
 TEST_F(IdleSpellCheckControllerTest, DetachWhenInactive) {
   TransitTo(State::kInactive);
-  GetDocument().Shutdown();
+  GetFrame().DomWindow()->FrameDestroyed();
   EXPECT_EQ(State::kInactive, IdleChecker().GetState());
 }
 
 TEST_F(IdleSpellCheckControllerTest, DetachWhenHotModeRequested) {
   TransitTo(State::kHotModeRequested);
-  GetDocument().Shutdown();
+  GetFrame().DomWindow()->FrameDestroyed();
   EXPECT_EQ(State::kInactive, IdleChecker().GetState());
 }
 
 TEST_F(IdleSpellCheckControllerTest, DetachWhenColdModeTimerStarted) {
   TransitTo(State::kColdModeTimerStarted);
-  GetDocument().Shutdown();
+  GetFrame().DomWindow()->FrameDestroyed();
   EXPECT_EQ(State::kInactive, IdleChecker().GetState());
 }
 
 TEST_F(IdleSpellCheckControllerTest, DetachWhenColdModeRequested) {
   TransitTo(State::kColdModeRequested);
-  GetDocument().Shutdown();
+  GetFrame().DomWindow()->FrameDestroyed();
   EXPECT_EQ(State::kInactive, IdleChecker().GetState());
 }
 
diff --git a/third_party/blink/renderer/core/editing/suggestion/text_suggestion_controller_test.cc b/third_party/blink/renderer/core/editing/suggestion/text_suggestion_controller_test.cc
index d6d51da..d2a6e5a 100644
--- a/third_party/blink/renderer/core/editing/suggestion/text_suggestion_controller_test.cc
+++ b/third_party/blink/renderer/core/editing/suggestion/text_suggestion_controller_test.cc
@@ -12,6 +12,7 @@
 #include "third_party/blink/renderer/core/editing/spellcheck/spell_checker.h"
 #include "third_party/blink/renderer/core/editing/testing/editing_test_base.h"
 #include "third_party/blink/renderer/core/editing/visible_selection.h"
+#include "third_party/blink/renderer/core/frame/local_dom_window.h"
 
 using ui::mojom::ImeTextSpanThickness;
 using ui::mojom::ImeTextSpanUnderlineStyle;
@@ -469,7 +470,7 @@
 
 TEST_F(TextSuggestionControllerTest, CallbackHappensAfterDocumentDestroyed) {
   LocalFrame& frame = *GetDocument().GetFrame();
-  GetDocument().Shutdown();
+  frame.DomWindow()->FrameDestroyed();
 
   // Shouldn't crash
   frame.GetTextSuggestionController().SuggestionMenuTimeoutCallback(0);
diff --git a/third_party/blink/renderer/core/execution_context/agent.cc b/third_party/blink/renderer/core/execution_context/agent.cc
index d4684a5..d25e979 100644
--- a/third_party/blink/renderer/core/execution_context/agent.cc
+++ b/third_party/blink/renderer/core/execution_context/agent.cc
@@ -4,8 +4,8 @@
 
 #include "third_party/blink/renderer/core/execution_context/agent.h"
 
+#include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/dom/mutation_observer.h"
-#include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/platform/scheduler/public/event_loop.h"
 
 namespace blink {
@@ -21,12 +21,12 @@
 
 void Agent::Trace(Visitor* visitor) {}
 
-void Agent::AttachExecutionContext(ExecutionContext* execution_context) {
-  event_loop_->AttachScheduler(execution_context->GetScheduler());
+void Agent::AttachDocument(Document* document) {
+  event_loop_->AttachScheduler(document->GetScheduler());
 }
 
-void Agent::DetachExecutionContext(ExecutionContext* execution_context) {
-  event_loop_->DetachScheduler(execution_context->GetScheduler());
+void Agent::DetachDocument(Document* document) {
+  event_loop_->DetachScheduler(document->GetScheduler());
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/execution_context/agent.h b/third_party/blink/renderer/core/execution_context/agent.h
index 4c85074..8adc23a 100644
--- a/third_party/blink/renderer/core/execution_context/agent.h
+++ b/third_party/blink/renderer/core/execution_context/agent.h
@@ -19,7 +19,7 @@
 class EventLoop;
 }
 
-class ExecutionContext;
+class Document;
 
 // Corresponding spec concept is:
 // https://html.spec.whatwg.org/C#integration-with-the-javascript-agent-formalism
@@ -45,8 +45,8 @@
 
   virtual void Trace(Visitor*);
 
-  void AttachExecutionContext(ExecutionContext*);
-  void DetachExecutionContext(ExecutionContext*);
+  void AttachDocument(Document*);
+  void DetachDocument(Document*);
 
   const base::UnguessableToken& cluster_id() const { return cluster_id_; }
 
diff --git a/third_party/blink/renderer/core/execution_context/execution_context.h b/third_party/blink/renderer/core/execution_context/execution_context.h
index c475c8a8..f25509b9 100644
--- a/third_party/blink/renderer/core/execution_context/execution_context.h
+++ b/third_party/blink/renderer/core/execution_context/execution_context.h
@@ -90,7 +90,7 @@
 
 // An environment in which script can execute. This class exposes the common
 // properties of script execution environments on the web (i.e, common between
-// script executing in a document and script executing in a worker), such as:
+// script executing in a window and script executing in a worker), such as:
 //
 // - a base URL for the resolution of relative URLs
 // - a security context that defines the privileges associated with the
@@ -101,25 +101,20 @@
 //   been closed permanently
 // - a console logging facility for debugging
 //
-// Typically, the ExecutionContext is an instance of Document or of
+// Typically, the ExecutionContext is an instance of LocalDOMWindow or of
 // WorkerOrWorkletGlobalScope.
 //
 // Note that this is distinct from the notion of a ScriptState or v8::Context,
 // which are associated with a single script context (with a single global
 // object). For example, there are separate JavaScript globals for "main world"
 // script written by a web author and an "isolated world" content script written
-// by an extension developer, but these share an ExecutionContext (the document)
+// by an extension developer, but these share an ExecutionContext (the window)
 // in common.
-// TODO(crbug.com/1029822): Virtual inheritance is used here temporarily to
-// enable moving ExecutionContext from Document to LocalDOMWindow. This allows
-// Document's inheritance of ExecutionContext to be hidden, while still allowing
-// Document to inherit from some of ExecutionContext's parent classes publicly.
-class CORE_EXPORT ExecutionContext
-    : public Supplementable<ExecutionContext>,
-      public ContextLifecycleNotifier,
-      public virtual ConsoleLogger,
-      public virtual UseCounter,
-      public virtual FeaturePolicyParserDelegate {
+class CORE_EXPORT ExecutionContext : public Supplementable<ExecutionContext>,
+                                     public ContextLifecycleNotifier,
+                                     public ConsoleLogger,
+                                     public UseCounter,
+                                     public FeaturePolicyParserDelegate {
   MERGE_GARBAGE_COLLECTED_MIXINS();
 
  public:
diff --git a/third_party/blink/renderer/core/execution_context/security_context.cc b/third_party/blink/renderer/core/execution_context/security_context.cc
index b5f7ed3..0d20182 100644
--- a/third_party/blink/renderer/core/execution_context/security_context.cc
+++ b/third_party/blink/renderer/core/execution_context/security_context.cc
@@ -72,7 +72,8 @@
       context_type_(context_type),
       agent_(init.GetAgent()),
       secure_context_mode_(init.GetSecureContextMode()),
-      origin_trial_context_(init.GetOriginTrialContext()) {}
+      origin_trial_context_(init.GetOriginTrialContext()),
+      bind_csp_immediately_(init.BindCSPImmediately()) {}
 
 void SecurityContext::Trace(Visitor* visitor) {
   visitor->Trace(content_security_policy_);
@@ -133,8 +134,6 @@
 
 void SecurityContext::SetFeaturePolicy(
     std::unique_ptr<FeaturePolicy> feature_policy) {
-  // This method should be called before a FeaturePolicy has been created.
-  DCHECK(!feature_policy_);
   feature_policy_ = std::move(feature_policy);
 }
 
diff --git a/third_party/blink/renderer/core/execution_context/security_context.h b/third_party/blink/renderer/core/execution_context/security_context.h
index 2a12264..5931045 100644
--- a/third_party/blink/renderer/core/execution_context/security_context.h
+++ b/third_party/blink/renderer/core/execution_context/security_context.h
@@ -197,6 +197,8 @@
     secure_context_mode_ = mode;
   }
 
+  bool BindCSPImmediately() const { return bind_csp_immediately_; }
+
  protected:
   mojom::blink::WebSandboxFlags sandbox_flags_;
   scoped_refptr<SecurityOrigin> security_origin_;
@@ -216,6 +218,7 @@
   Member<Agent> agent_;
   SecureContextMode secure_context_mode_;
   Member<OriginTrialContext> origin_trial_context_;
+  bool bind_csp_immediately_ = false;
   DISALLOW_COPY_AND_ASSIGN(SecurityContext);
 };
 
diff --git a/third_party/blink/renderer/core/execution_context/security_context_init.cc b/third_party/blink/renderer/core/execution_context/security_context_init.cc
index 3cd9752..d945488f 100644
--- a/third_party/blink/renderer/core/execution_context/security_context_init.cc
+++ b/third_party/blink/renderer/core/execution_context/security_context_init.cc
@@ -89,10 +89,6 @@
 }
 
 void SecurityContextInit::ApplyPendingDataToDocument(Document& document) const {
-  if (BindCSPImmediately()) {
-    document.GetContentSecurityPolicy()->BindToDelegate(
-        document.GetContentSecurityPolicyDelegate());
-  }
   for (auto feature : feature_count_)
     UseCounter::Count(document, feature);
   for (auto feature : parsed_feature_policies_)
diff --git a/third_party/blink/renderer/core/exported/web_scoped_window_focus_allowed_indicator_test.cc b/third_party/blink/renderer/core/exported/web_scoped_window_focus_allowed_indicator_test.cc
index 15a8fcc..be2e44f 100644
--- a/third_party/blink/renderer/core/exported/web_scoped_window_focus_allowed_indicator_test.cc
+++ b/third_party/blink/renderer/core/exported/web_scoped_window_focus_allowed_indicator_test.cc
@@ -33,12 +33,14 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/web/web_document.h"
 #include "third_party/blink/renderer/core/dom/document.h"
+#include "third_party/blink/renderer/core/testing/dummy_page_holder.h"
 #include "third_party/blink/renderer/platform/heap/heap.h"
 
 namespace blink {
 
 TEST(WebScopedWindowFocusAllowedIndicatorTest, Basic) {
-  Persistent<Document> document = MakeGarbageCollected<Document>();
+  auto dummy = std::make_unique<DummyPageHolder>();
+  auto* document = &dummy->GetDocument();
   WebDocument web_document(document);
 
   EXPECT_FALSE(document->ToExecutionContext()->IsWindowInteractionAllowed());
diff --git a/third_party/blink/renderer/core/feature_policy/policy_test.cc b/third_party/blink/renderer/core/feature_policy/policy_test.cc
index 26b725db..0630be0a 100644
--- a/third_party/blink/renderer/core/feature_policy/policy_test.cc
+++ b/third_party/blink/renderer/core/feature_policy/policy_test.cc
@@ -8,8 +8,8 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/renderer/core/dom/document.h"
-#include "third_party/blink/renderer/core/dom/document_init.h"
 #include "third_party/blink/renderer/core/feature_policy/feature_policy_parser.h"
+#include "third_party/blink/renderer/core/testing/dummy_page_holder.h"
 #include "third_party/blink/renderer/platform/weborigin/security_origin.h"
 
 namespace blink {
@@ -25,18 +25,27 @@
 class PolicyTest : public testing::Test {
  public:
   void SetUp() override {
-    DocumentInit init =
-        DocumentInit::Create()
-            .WithOriginToCommit(SecurityOrigin::CreateFromString(kSelfOrigin))
-            .WithFeaturePolicyHeader(
-                "fullscreen *; payment 'self'; midi 'none'; camera 'self' "
-                "https://example.com https://example.net");
-    document_ = MakeGarbageCollected<Document>(init);
+    page_holder_ = std::make_unique<DummyPageHolder>();
+
+    auto origin = SecurityOrigin::CreateFromString(kSelfOrigin);
+    Vector<String> messages;
+    auto feature_policy = FeaturePolicy::CreateFromParentPolicy(
+        nullptr, ParsedFeaturePolicy(), origin->ToUrlOrigin());
+    auto header = FeaturePolicyParser::ParseHeader(
+        "fullscreen *; payment 'self'; midi 'none'; camera 'self' "
+        "https://example.com https://example.net",
+        origin.get(), &messages);
+    feature_policy->SetHeaderPolicy(header);
+
+    auto& security_context = page_holder_->GetDocument().GetSecurityContext();
+    security_context.SetSecurityOriginForTesting(origin);
+    security_context.SetFeaturePolicy(std::move(feature_policy));
   }
 
   DOMFeaturePolicy* GetPolicy() const { return policy_; }
 
  protected:
+  std::unique_ptr<DummyPageHolder> page_holder_;
   Persistent<Document> document_;
   Persistent<DOMFeaturePolicy> policy_;
 };
@@ -45,7 +54,8 @@
  public:
   void SetUp() override {
     PolicyTest::SetUp();
-    policy_ = MakeGarbageCollected<DOMDocumentPolicy>(document_);
+    policy_ =
+        MakeGarbageCollected<DOMDocumentPolicy>(&page_holder_->GetDocument());
   }
 };
 
@@ -54,7 +64,7 @@
   void SetUp() override {
     PolicyTest::SetUp();
     policy_ = MakeGarbageCollected<IFramePolicy>(
-        document_, ParsedFeaturePolicy(),
+        &page_holder_->GetDocument(), ParsedFeaturePolicy(),
         SecurityOrigin::CreateFromString(kSelfOrigin));
   }
 };
diff --git a/third_party/blink/renderer/core/frame/csp/content_security_policy_test.cc b/third_party/blink/renderer/core/frame/csp/content_security_policy_test.cc
index 4696ca2..e6206ef3 100644
--- a/third_party/blink/renderer/core/frame/csp/content_security_policy_test.cc
+++ b/third_party/blink/renderer/core/frame/csp/content_security_policy_test.cc
@@ -79,21 +79,22 @@
                           ContentSecurityPolicySource::kHTTP);
     EXPECT_EQ(test.expected_policy, csp->GetInsecureRequestPolicy());
 
-    DocumentInit init = DocumentInit::Create()
-                            .WithOriginToCommit(secure_origin)
-                            .WithURL(secure_url);
-    auto* document = MakeGarbageCollected<Document>(init);
-    csp->BindToDelegate(document->GetContentSecurityPolicyDelegate());
+    auto dummy = std::make_unique<DummyPageHolder>();
+    dummy->GetDocument().SetURL(secure_url);
+    auto& security_context = dummy->GetDocument().GetSecurityContext();
+    security_context.SetSecurityOriginForTesting(secure_origin);
+
+    csp->BindToDelegate(
+        dummy->GetDocument().GetContentSecurityPolicyDelegate());
     EXPECT_EQ(test.expected_policy,
-              document->GetSecurityContext().GetInsecureRequestPolicy());
+              security_context.GetInsecureRequestPolicy());
     bool expect_upgrade =
         (test.expected_policy &
          mojom::blink::InsecureRequestPolicy::kUpgradeInsecureRequests) !=
         mojom::blink::InsecureRequestPolicy::kLeaveInsecureRequestsAlone;
-    EXPECT_EQ(
-        expect_upgrade,
-        document->GetSecurityContext().InsecureNavigationsToUpgrade().Contains(
-            document->Url().Host().Impl()->GetHash()));
+    EXPECT_EQ(expect_upgrade,
+              security_context.InsecureNavigationsToUpgrade().Contains(
+                  dummy->GetDocument().Url().Host().Impl()->GetHash()));
   }
 
   // Report-Only
@@ -717,8 +718,9 @@
   WTF::OrdinalNumber context_line;
 
   // We need document for HTMLScriptElement tests.
-  DocumentInit init = DocumentInit::Create().WithOriginToCommit(secure_origin);
-  auto* document = MakeGarbageCollected<Document>(init);
+  auto dummy = std::make_unique<DummyPageHolder>();
+  auto& document = dummy->GetDocument();
+  document.GetSecurityContext().SetSecurityOriginForTesting(secure_origin);
 
   for (const auto& test : cases) {
     SCOPED_TRACE(testing::Message() << "Policy: `" << test.policy
@@ -726,12 +728,12 @@
 
     unsigned expected_reports = test.allowed ? 0u : 1u;
     auto* element = MakeGarbageCollected<HTMLScriptElement>(
-        *document, CreateElementFlags::ByParser());
+        document, CreateElementFlags::ByParser());
 
     // Enforce 'script-src'
     Persistent<ContentSecurityPolicy> policy =
         MakeGarbageCollected<ContentSecurityPolicy>();
-    policy->BindToDelegate(document->GetContentSecurityPolicyDelegate());
+    policy->BindToDelegate(document.GetContentSecurityPolicyDelegate());
     policy->DidReceiveHeader(String("script-src ") + test.policy,
                              ContentSecurityPolicyType::kEnforce,
                              ContentSecurityPolicySource::kHTTP);
@@ -743,7 +745,7 @@
 
     // Enforce 'style-src'
     policy = MakeGarbageCollected<ContentSecurityPolicy>();
-    policy->BindToDelegate(document->GetContentSecurityPolicyDelegate());
+    policy->BindToDelegate(document.GetContentSecurityPolicyDelegate());
     policy->DidReceiveHeader(String("style-src ") + test.policy,
                              ContentSecurityPolicyType::kEnforce,
                              ContentSecurityPolicySource::kHTTP);
@@ -755,7 +757,7 @@
 
     // Report 'script-src'
     policy = MakeGarbageCollected<ContentSecurityPolicy>();
-    policy->BindToDelegate(document->GetContentSecurityPolicyDelegate());
+    policy->BindToDelegate(document.GetContentSecurityPolicyDelegate());
     policy->DidReceiveHeader(String("script-src ") + test.policy,
                              ContentSecurityPolicyType::kReport,
                              ContentSecurityPolicySource::kHTTP);
@@ -766,7 +768,7 @@
 
     // Report 'style-src'
     policy = MakeGarbageCollected<ContentSecurityPolicy>();
-    policy->BindToDelegate(document->GetContentSecurityPolicyDelegate());
+    policy->BindToDelegate(document.GetContentSecurityPolicyDelegate());
     policy->DidReceiveHeader(String("style-src ") + test.policy,
                              ContentSecurityPolicyType::kReport,
                              ContentSecurityPolicySource::kHTTP);
diff --git a/third_party/blink/renderer/core/frame/csp/execution_context_csp_delegate.cc b/third_party/blink/renderer/core/frame/csp/execution_context_csp_delegate.cc
index 91cd284..186a0034 100644
--- a/third_party/blink/renderer/core/frame/csp/execution_context_csp_delegate.cc
+++ b/third_party/blink/renderer/core/frame/csp/execution_context_csp_delegate.cc
@@ -12,6 +12,7 @@
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/core/execution_context/security_context.h"
 #include "third_party/blink/renderer/core/frame/csp/csp_violation_report_body.h"
+#include "third_party/blink/renderer/core/frame/local_dom_window.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/core/frame/local_frame_client.h"
 #include "third_party/blink/renderer/core/frame/report.h"
@@ -241,7 +242,8 @@
 }
 
 Document* ExecutionContextCSPDelegate::GetDocument() {
-  return Document::DynamicFrom(execution_context_.Get());
+  auto* window = DynamicTo<LocalDOMWindow>(execution_context_.Get());
+  return window ? window->document() : nullptr;
 }
 
 void ExecutionContextCSPDelegate::DispatchViolationEventInternal(
diff --git a/third_party/blink/renderer/core/frame/csp/source_list_directive_test.cc b/third_party/blink/renderer/core/frame/csp/source_list_directive_test.cc
index 2444240..55cca68 100644
--- a/third_party/blink/renderer/core/frame/csp/source_list_directive_test.cc
+++ b/third_party/blink/renderer/core/frame/csp/source_list_directive_test.cc
@@ -5,10 +5,9 @@
 #include "third_party/blink/renderer/core/frame/csp/source_list_directive.h"
 
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/blink/renderer/core/dom/document.h"
-#include "third_party/blink/renderer/core/dom/document_init.h"
 #include "third_party/blink/renderer/core/frame/csp/content_security_policy.h"
 #include "third_party/blink/renderer/core/frame/csp/csp_source.h"
+#include "third_party/blink/renderer/core/testing/null_execution_context.h"
 #include "third_party/blink/renderer/platform/heap/heap.h"
 #include "third_party/blink/renderer/platform/loader/fetch/resource_request.h"
 #include "third_party/blink/renderer/platform/weborigin/kurl.h"
@@ -33,23 +32,19 @@
 
   void SetUp() override {
     KURL secure_url("https://example.test/image.png");
-    scoped_refptr<SecurityOrigin> secure_origin(
+    context = MakeGarbageCollected<NullExecutionContext>();
+    context->GetSecurityContext().SetSecurityOrigin(
         SecurityOrigin::Create(secure_url));
-    DocumentInit init =
-        DocumentInit::Create().WithOriginToCommit(secure_origin);
-    document = MakeGarbageCollected<Document>(init);
-    csp->BindToDelegate(document->GetContentSecurityPolicyDelegate());
+    csp->BindToDelegate(context->GetContentSecurityPolicyDelegate());
   }
 
   ContentSecurityPolicy* SetUpWithOrigin(const String& origin) {
     KURL secure_url(origin);
-    scoped_refptr<SecurityOrigin> secure_origin(
+    auto* context = MakeGarbageCollected<NullExecutionContext>();
+    context->GetSecurityContext().SetSecurityOrigin(
         SecurityOrigin::Create(secure_url));
-    DocumentInit init =
-        DocumentInit::Create().WithOriginToCommit(secure_origin);
-    auto* document = MakeGarbageCollected<Document>(init);
     auto* csp = MakeGarbageCollected<ContentSecurityPolicy>();
-    csp->BindToDelegate(document->GetContentSecurityPolicyDelegate());
+    csp->BindToDelegate(context->GetContentSecurityPolicyDelegate());
     return csp;
   }
 
@@ -60,7 +55,7 @@
   }
 
   Persistent<ContentSecurityPolicy> csp;
-  Persistent<Document> document;
+  Persistent<ExecutionContext> context;
 };
 
 TEST_F(SourceListDirectiveTest, BasicMatchingNone) {
diff --git a/third_party/blink/renderer/core/frame/local_dom_window.cc b/third_party/blink/renderer/core/frame/local_dom_window.cc
index 690a02e..c3364ba 100644
--- a/third_party/blink/renderer/core/frame/local_dom_window.cc
+++ b/third_party/blink/renderer/core/frame/local_dom_window.cc
@@ -30,6 +30,7 @@
 #include <utility>
 
 #include "cc/input/snap_selection_strategy.h"
+#include "third_party/blink/public/common/browser_interface_broker_proxy.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/platform/task_type.h"
 #include "third_party/blink/public/platform/web_screen_info.h"
@@ -55,6 +56,7 @@
 #include "third_party/blink/renderer/core/dom/events/event_dispatch_forbidden_scope.h"
 #include "third_party/blink/renderer/core/dom/events/scoped_event_queue.h"
 #include "third_party/blink/renderer/core/dom/frame_request_callback_collection.h"
+#include "third_party/blink/renderer/core/dom/scriptable_document_parser.h"
 #include "third_party/blink/renderer/core/dom/scripted_idle_task_controller.h"
 #include "third_party/blink/renderer/core/editing/editor.h"
 #include "third_party/blink/renderer/core/editing/frame_selection.h"
@@ -62,6 +64,8 @@
 #include "third_party/blink/renderer/core/events/message_event.h"
 #include "third_party/blink/renderer/core/events/pop_state_event.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context_lifecycle_observer.h"
+#include "third_party/blink/renderer/core/execution_context/security_context_init.h"
+#include "third_party/blink/renderer/core/execution_context/window_agent.h"
 #include "third_party/blink/renderer/core/frame/bar_prop.h"
 #include "third_party/blink/renderer/core/frame/csp/content_security_policy.h"
 #include "third_party/blink/renderer/core/frame/dom_visual_viewport.h"
@@ -85,6 +89,7 @@
 #include "third_party/blink/renderer/core/input/event_handler.h"
 #include "third_party/blink/renderer/core/inspector/console_message.h"
 #include "third_party/blink/renderer/core/inspector/inspector_trace_events.h"
+#include "third_party/blink/renderer/core/inspector/main_thread_debugger.h"
 #include "third_party/blink/renderer/core/layout/adjust_for_absolute_zoom.h"
 #include "third_party/blink/renderer/core/layout/layout_view.h"
 #include "third_party/blink/renderer/core/loader/appcache/application_cache.h"
@@ -106,8 +111,10 @@
 #include "third_party/blink/renderer/platform/bindings/microtask.h"
 #include "third_party/blink/renderer/platform/heap/heap.h"
 #include "third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h"
+#include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h"
 #include "third_party/blink/renderer/platform/timer.h"
 #include "third_party/blink/renderer/platform/weborigin/security_origin.h"
+#include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h"
 
 namespace blink {
 
@@ -213,6 +220,7 @@
 
 LocalDOMWindow::LocalDOMWindow(LocalFrame& frame)
     : DOMWindow(frame),
+      ExecutionContext(V8PerIsolateData::MainThreadIsolate()),
       visualViewport_(MakeGarbageCollected<DOMVisualViewport>(this)),
       unused_preloads_timer_(frame.GetTaskRunner(TaskType::kInternalDefault),
                              this,
@@ -264,6 +272,174 @@
   return blink::ToLocalDOMWindow(script_state->GetContext());
 }
 
+bool LocalDOMWindow::IsContextThread() const {
+  return IsMainThread();
+}
+
+bool LocalDOMWindow::ShouldInstallV8Extensions() const {
+  return GetFrame()->Client()->AllowScriptExtensions();
+}
+
+ContentSecurityPolicy* LocalDOMWindow::GetContentSecurityPolicyForWorld() {
+  return document()->GetContentSecurityPolicyForWorld();
+}
+
+const KURL& LocalDOMWindow::Url() const {
+  return document()->Url();
+}
+
+const KURL& LocalDOMWindow::BaseURL() const {
+  return document()->BaseURL();
+}
+
+KURL LocalDOMWindow::CompleteURL(const String& url) const {
+  return document()->CompleteURL(url);
+}
+
+void LocalDOMWindow::DisableEval(const String& error_message) {
+  if (GetFrame())
+    GetFrame()->GetScriptController().DisableEval(error_message);
+}
+
+String LocalDOMWindow::UserAgent() const {
+  return document()->UserAgent();
+}
+
+HttpsState LocalDOMWindow::GetHttpsState() const {
+  return document()->GetHttpsState();
+}
+
+ResourceFetcher* LocalDOMWindow::Fetcher() const {
+  return document()->Fetcher();
+}
+
+SecurityContext& LocalDOMWindow::GetSecurityContext() {
+  return document()->GetSecurityContext();
+}
+
+const SecurityContext& LocalDOMWindow::GetSecurityContext() const {
+  return document()->GetSecurityContext();
+}
+
+bool LocalDOMWindow::CanExecuteScripts(
+    ReasonForCallingCanExecuteScripts reason) {
+  return document()->CanExecuteScripts(reason);
+}
+
+void LocalDOMWindow::ExceptionThrown(ErrorEvent* event) {
+  MainThreadDebugger::Instance()->ExceptionThrown(this, event);
+}
+
+String LocalDOMWindow::OutgoingReferrer() const {
+  return document()->OutgoingReferrer();
+}
+
+network::mojom::ReferrerPolicy LocalDOMWindow::GetReferrerPolicy() const {
+  network::mojom::ReferrerPolicy policy = ExecutionContext::GetReferrerPolicy();
+  // For srcdoc documents without their own policy, walk up the frame
+  // tree to find the document that is either not a srcdoc or doesn't
+  // have its own policy. This algorithm is defined in
+  // https://html.spec.whatwg.org/C/#set-up-a-window-environment-settings-object.
+  if (!GetFrame() || policy != network::mojom::ReferrerPolicy::kDefault ||
+      !document()->IsSrcdocDocument()) {
+    return policy;
+  }
+  LocalFrame* frame = To<LocalFrame>(GetFrame()->Tree().Parent());
+  return frame->DomWindow()->GetReferrerPolicy();
+}
+
+CoreProbeSink* LocalDOMWindow::GetProbeSink() {
+  return document()->GetProbeSink();
+}
+
+BrowserInterfaceBrokerProxy& LocalDOMWindow::GetBrowserInterfaceBroker() {
+  if (!GetFrame())
+    return GetEmptyBrowserInterfaceBroker();
+
+  return GetFrame()->GetBrowserInterfaceBroker();
+}
+
+FrameOrWorkerScheduler* LocalDOMWindow::GetScheduler() {
+  return GetFrame() ? GetFrame()->GetFrameScheduler() : nullptr;
+}
+
+scoped_refptr<base::SingleThreadTaskRunner> LocalDOMWindow::GetTaskRunner(
+    TaskType type) {
+  return document()->GetTaskRunner(type);
+}
+
+void LocalDOMWindow::CountPotentialFeaturePolicyViolation(
+    mojom::blink::FeaturePolicyFeature feature) const {
+  document()->CountPotentialFeaturePolicyViolation(feature);
+}
+
+void LocalDOMWindow::ReportFeaturePolicyViolation(
+    mojom::blink::FeaturePolicyFeature feature,
+    mojom::FeaturePolicyDisposition disposition,
+    const String& message,
+    const String& source_file) const {
+  document()->ReportFeaturePolicyViolation(feature, disposition, message,
+                                           source_file);
+}
+
+static void RunAddConsoleMessageTask(mojom::ConsoleMessageSource source,
+                                     mojom::ConsoleMessageLevel level,
+                                     const String& message,
+                                     LocalDOMWindow* window,
+                                     bool discard_duplicates) {
+  window->AddConsoleMessageImpl(
+      MakeGarbageCollected<ConsoleMessage>(source, level, message),
+      discard_duplicates);
+}
+
+void LocalDOMWindow::AddConsoleMessageImpl(ConsoleMessage* console_message,
+                                           bool discard_duplicates) {
+  if (!IsContextThread()) {
+    PostCrossThreadTask(
+        *GetTaskRunner(TaskType::kInternalInspector), FROM_HERE,
+        CrossThreadBindOnce(
+            &RunAddConsoleMessageTask, console_message->Source(),
+            console_message->Level(), console_message->Message(),
+            WrapCrossThreadPersistent(this), discard_duplicates));
+    return;
+  }
+
+  if (!GetFrame())
+    return;
+
+  if (document() && console_message->Location()->IsUnknown()) {
+    // TODO(dgozman): capture correct location at call places instead.
+    unsigned line_number = 0;
+    if (!document()->IsInDocumentWrite() &&
+        document()->GetScriptableDocumentParser()) {
+      ScriptableDocumentParser* parser =
+          document()->GetScriptableDocumentParser();
+      if (parser->IsParsingAtLineNumber())
+        line_number = parser->LineNumber().OneBasedInt();
+    }
+    Vector<DOMNodeId> nodes(console_message->Nodes());
+    console_message = MakeGarbageCollected<ConsoleMessage>(
+        console_message->Source(), console_message->Level(),
+        console_message->Message(),
+        std::make_unique<SourceLocation>(Url().GetString(), line_number, 0,
+                                         nullptr));
+    console_message->SetNodes(GetFrame(), std::move(nodes));
+  }
+
+  GetFrame()->Console().AddMessage(console_message, discard_duplicates);
+}
+
+void LocalDOMWindow::CountUse(mojom::WebFeature feature) {
+  if (!GetFrame())
+    return;
+  if (auto* loader = GetFrame()->Loader().GetDocumentLoader())
+    loader->CountUse(feature);
+}
+
+void LocalDOMWindow::CountDeprecation(mojom::WebFeature feature) {
+  document()->CountDeprecation(feature);
+}
+
 Document* LocalDOMWindow::InstallNewDocument(const DocumentInit& init,
                                              bool force_xhtml) {
   DCHECK_EQ(init.GetFrame(), GetFrame());
@@ -273,6 +449,13 @@
   document_ = CreateDocument(init, force_xhtml);
   document_->Initialize();
 
+  // The CSP delegate doesn't have access to all of the state it needs until
+  // document_ is set.
+  if (GetSecurityContext().BindCSPImmediately()) {
+    GetSecurityContext().GetContentSecurityPolicy()->BindToDelegate(
+        GetContentSecurityPolicyDelegate());
+  }
+
   if (!GetFrame())
     return document_;
 
@@ -407,7 +590,7 @@
 }
 
 ExecutionContext* LocalDOMWindow::GetExecutionContext() const {
-  return document_->ToExecutionContext();
+  return const_cast<LocalDOMWindow*>(this);
 }
 
 const LocalDOMWindow* LocalDOMWindow::ToLocalDOMWindow() const {
@@ -424,6 +607,7 @@
 }
 
 void LocalDOMWindow::FrameDestroyed() {
+  NotifyContextDestroyed();
   RemoveAllEventListeners();
   DisconnectFromFrame();
 }
@@ -435,7 +619,6 @@
 
 void LocalDOMWindow::Reset() {
   DCHECK(document());
-  DCHECK(document()->IsContextDestroyed());
   FrameDestroyed();
 
   screen_ = nullptr;
@@ -1651,6 +1834,7 @@
   visitor->Trace(event_listener_observers_);
   visitor->Trace(trusted_types_);
   DOMWindow::Trace(visitor);
+  ExecutionContext::Trace(visitor);
   Supplementable<LocalDOMWindow>::Trace(visitor);
 }
 
diff --git a/third_party/blink/renderer/core/frame/local_dom_window.h b/third_party/blink/renderer/core/frame/local_dom_window.h
index 729a2762..b0d44f8e 100644
--- a/third_party/blink/renderer/core/frame/local_dom_window.h
+++ b/third_party/blink/renderer/core/frame/local_dom_window.h
@@ -83,6 +83,7 @@
 // Note: if you're thinking of returning something DOM-related by reference,
 // please ping dcheng@chromium.org first. You probably don't want to do that.
 class CORE_EXPORT LocalDOMWindow final : public DOMWindow,
+                                         public ExecutionContext,
                                          public Supplementable<LocalDOMWindow> {
   USING_GARBAGE_COLLECTED_MIXIN(LocalDOMWindow);
   USING_PRE_FINALIZER(LocalDOMWindow, Dispose);
@@ -107,6 +108,53 @@
 
   void Trace(Visitor*) override;
 
+  // ExecutionContext overrides:
+  // TODO(crbug.com/1029822): Most of these just call in to Document, but should
+  // move entirely here.
+  bool IsDocument() const final { return true; }
+  bool IsContextThread() const final;
+  bool ShouldInstallV8Extensions() const final;
+  ContentSecurityPolicy* GetContentSecurityPolicyForWorld() final;
+  const KURL& Url() const final;
+  const KURL& BaseURL() const final;
+  KURL CompleteURL(const String&) const final;
+  void DisableEval(const String& error_message) final;
+  LocalDOMWindow* ExecutingWindow() const final {
+    // TODO(crbug.com/1029822): This const_cast is gross.
+    return const_cast<LocalDOMWindow*>(this);
+  }
+  String UserAgent() const final;
+  HttpsState GetHttpsState() const final;
+  ResourceFetcher* Fetcher() const final;
+  SecurityContext& GetSecurityContext() final;
+  const SecurityContext& GetSecurityContext() const final;
+  bool CanExecuteScripts(ReasonForCallingCanExecuteScripts) final;
+  void ExceptionThrown(ErrorEvent*) final;
+  EventTarget* ErrorEventTarget() final { return this; }
+  String OutgoingReferrer() const final;
+  network::mojom::ReferrerPolicy GetReferrerPolicy() const final;
+  CoreProbeSink* GetProbeSink() final;
+  BrowserInterfaceBrokerProxy& GetBrowserInterfaceBroker() final;
+  FrameOrWorkerScheduler* GetScheduler() final;
+  scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunner(TaskType) final;
+  TrustedTypePolicyFactory* GetTrustedTypes() const final {
+    return trustedTypes();
+  }
+  void CountPotentialFeaturePolicyViolation(
+      mojom::blink::FeaturePolicyFeature) const final;
+  void ReportFeaturePolicyViolation(
+      mojom::blink::FeaturePolicyFeature,
+      mojom::FeaturePolicyDisposition,
+      const String& message = g_empty_string,
+      // If source_file is set to empty string,
+      // current JS file would be used as source_file instead.
+      const String& source_file = g_empty_string) const final;
+  void AddConsoleMessageImpl(ConsoleMessage*, bool discard_duplicates) final;
+
+  // UseCounter orverrides:
+  void CountUse(mojom::WebFeature feature) final;
+  void CountDeprecation(mojom::WebFeature feature) final;
+
   Document* InstallNewDocument(const DocumentInit&, bool force_xhtml);
 
   // EventTarget overrides:
@@ -381,6 +429,9 @@
 
 template <>
 struct DowncastTraits<LocalDOMWindow> {
+  static bool AllowFrom(const ExecutionContext& context) {
+    return context.IsDocument();
+  }
   static bool AllowFrom(const DOMWindow& window) {
     return window.IsLocalDOMWindow();
   }
diff --git a/third_party/blink/renderer/core/frame/local_frame.cc b/third_party/blink/renderer/core/frame/local_frame.cc
index 8bba7e3..b1538af 100644
--- a/third_party/blink/renderer/core/frame/local_frame.cc
+++ b/third_party/blink/renderer/core/frame/local_frame.cc
@@ -455,6 +455,7 @@
   // frames, and those event handlers might start a new subresource load in this
   // frame which should be stopped by Detach.
   loader_.Detach();
+  DomWindow()->FrameDestroyed();
   GetDocument()->Shutdown();
 
   if (content_capture_manager_) {
@@ -500,8 +501,6 @@
 
   GetEventHandlerRegistry().DidRemoveAllEventHandlers(*DomWindow());
 
-  DomWindow()->FrameDestroyed();
-
   probe::FrameDetachedFromParent(this);
 
   supplements_.clear();
@@ -604,11 +603,10 @@
 }
 
 void LocalFrame::SetDOMWindow(LocalDOMWindow* dom_window) {
-  if (dom_window)
-    GetScriptController().ClearWindowProxy();
-
+  DCHECK(dom_window);
   if (this->DomWindow())
     this->DomWindow()->Reset();
+  GetScriptController().ClearWindowProxy();
   dom_window_ = dom_window;
 }
 
@@ -1785,7 +1783,7 @@
 }
 
 void LocalFrame::ForciblyPurgeV8Memory() {
-  GetDocument()->NotifyContextDestroyed();
+  DomWindow()->NotifyContextDestroyed();
 
   WindowProxyManager* window_proxy_manager = GetWindowProxyManager();
   window_proxy_manager->ClearForV8MemoryPurge();
diff --git a/third_party/blink/renderer/core/html/forms/html_select_element.cc b/third_party/blink/renderer/core/html/forms/html_select_element.cc
index db3dfe9d..1ed5c0a0 100644
--- a/third_party/blink/renderer/core/html/forms/html_select_element.cc
+++ b/third_party/blink/renderer/core/html/forms/html_select_element.cc
@@ -554,46 +554,6 @@
   active_selection_end_ = option;
 }
 
-void HTMLSelectElement::UpdateListBoxSelection(bool deselect_other_options,
-                                               bool scroll) {
-  DCHECK(GetLayoutObject());
-  DCHECK(!UsesMenuList() || is_multiple_);
-
-  int active_selection_anchor_index =
-      active_selection_anchor_ ? active_selection_anchor_->index() : -1;
-  int active_selection_end_index =
-      active_selection_end_ ? active_selection_end_->index() : -1;
-  int start =
-      std::min(active_selection_anchor_index, active_selection_end_index);
-  int end = std::max(active_selection_anchor_index, active_selection_end_index);
-
-  int i = 0;
-  for (auto* const option : GetOptionList()) {
-    if (option->IsDisabledFormControl() || !option->GetLayoutObject()) {
-      ++i;
-      continue;
-    }
-    if (i >= start && i <= end) {
-      option->SetSelectedState(active_selection_state_);
-      option->SetDirty(true);
-    } else if (deselect_other_options ||
-               i >= static_cast<int>(
-                        cached_state_for_active_selection_.size())) {
-      option->SetSelectedState(false);
-      option->SetDirty(true);
-    } else {
-      option->SetSelectedState(cached_state_for_active_selection_[i]);
-    }
-    ++i;
-  }
-
-  select_type_->UpdateMultiSelectFocus();
-  SetNeedsValidityCheck();
-  if (scroll)
-    ScrollToSelection();
-  NotifyFormStateChanged();
-}
-
 void HTMLSelectElement::ListBoxOnChange() {
   DCHECK(!UsesMenuList());
 
diff --git a/third_party/blink/renderer/core/html/forms/html_select_element.h b/third_party/blink/renderer/core/html/forms/html_select_element.h
index 2cec83d..52643d57 100644
--- a/third_party/blink/renderer/core/html/forms/html_select_element.h
+++ b/third_party/blink/renderer/core/html/forms/html_select_element.h
@@ -257,7 +257,6 @@
   wtf_size_t SearchOptionsForValue(const String&,
                                    wtf_size_t list_index_start,
                                    wtf_size_t list_index_end) const;
-  void UpdateListBoxSelection(bool deselect_other_options, bool scroll = true);
   void SetIndexToSelectOnCancel(int list_index);
   void SetSuggestedOption(HTMLOptionElement*);
 
diff --git a/third_party/blink/renderer/core/html/forms/select_type.cc b/third_party/blink/renderer/core/html/forms/select_type.cc
index f818d1a..ea90ceb 100644
--- a/third_party/blink/renderer/core/html/forms/select_type.cc
+++ b/third_party/blink/renderer/core/html/forms/select_type.cc
@@ -608,12 +608,13 @@
   explicit ListBoxSelectType(HTMLSelectElement& select) : SelectType(select) {}
   bool DefaultEventHandler(const Event& event) override;
   void DidSetSuggestedOption(HTMLOptionElement* option) override;
-  void UpdateMultiSelectFocus() override;
   void SelectAll() override;
 
  private:
   HTMLOptionElement* NextSelectableOptionPageAway(HTMLOptionElement*,
                                                   SkipDirection) const;
+  // Update :-internal-multi-select-focus state of selected OPTIONs.
+  void UpdateMultiSelectFocus();
   void ToggleSelection(HTMLOptionElement& option);
   enum class SelectionMode {
     kDeselectOthers,
@@ -622,6 +623,7 @@
   };
   void UpdateSelectedState(HTMLOptionElement* clicked_option,
                            SelectionMode mode);
+  void UpdateListBoxSelection(bool deselect_other_options, bool scroll = true);
 
   bool is_in_non_contiguous_selection_ = false;
 };
@@ -708,11 +710,11 @@
             return false;
 
           select_->SetActiveSelectionEnd(option);
-          select_->UpdateListBoxSelection(false);
+          UpdateListBoxSelection(false);
         } else {
           select_->SetActiveSelectionAnchor(option);
           select_->SetActiveSelectionEnd(option);
-          select_->UpdateListBoxSelection(true);
+          UpdateListBoxSelection(true);
         }
       }
     }
@@ -838,7 +840,7 @@
       select_->ScrollToOption(end_option);
       if (select_new_item || is_in_non_contiguous_selection_) {
         if (select_new_item) {
-          select_->UpdateListBoxSelection(deselect_others);
+          UpdateListBoxSelection(deselect_others);
           select_->ListBoxOnChange();
         }
         UpdateMultiSelectFocus();
@@ -911,7 +913,7 @@
   select_->SetActiveSelectionAnchor(NextSelectableOption(nullptr));
   select_->SetActiveSelectionEnd(PreviousSelectableOption(nullptr));
 
-  select_->UpdateListBoxSelection(false, false);
+  UpdateListBoxSelection(false, false);
   select_->ListBoxOnChange();
   select_->SetNeedsValidityCheck();
 }
@@ -988,7 +990,44 @@
     select_->SetActiveSelectionAnchor(clicked_option);
 
   select_->SetActiveSelectionEnd(clicked_option);
-  select_->UpdateListBoxSelection(mode != SelectionMode::kNotChangeOthers);
+  UpdateListBoxSelection(mode != SelectionMode::kNotChangeOthers);
+}
+
+void ListBoxSelectType::UpdateListBoxSelection(bool deselect_other_options,
+                                               bool scroll) {
+  DCHECK(select_->GetLayoutObject());
+  HTMLOptionElement* const anchor_option = select_->active_selection_anchor_;
+  HTMLOptionElement* const end_option = select_->active_selection_end_;
+  const int anchor_index = anchor_option ? anchor_option->index() : -1;
+  const int end_index = end_option ? end_option->index() : -1;
+  const int start = std::min(anchor_index, end_index);
+  const int end = std::max(anchor_index, end_index);
+
+  int i = 0;
+  for (auto* const option : select_->GetOptionList()) {
+    if (option->IsDisabledFormControl() || !option->GetLayoutObject()) {
+      ++i;
+      continue;
+    }
+    if (i >= start && i <= end) {
+      option->SetSelectedState(select_->active_selection_state_);
+      option->SetDirty(true);
+    } else if (deselect_other_options ||
+               i >= static_cast<int>(
+                        select_->cached_state_for_active_selection_.size())) {
+      option->SetSelectedState(false);
+      option->SetDirty(true);
+    } else {
+      option->SetSelectedState(select_->cached_state_for_active_selection_[i]);
+    }
+    ++i;
+  }
+
+  UpdateMultiSelectFocus();
+  select_->SetNeedsValidityCheck();
+  if (scroll)
+    select_->ScrollToSelection();
+  select_->NotifyFormStateChanged();
 }
 
 // ============================================================================
@@ -1041,8 +1080,6 @@
 
 void SelectType::MaximumOptionWidthMightBeChanged() const {}
 
-void SelectType::UpdateMultiSelectFocus() {}
-
 void SelectType::SelectAll() {
   NOTREACHED();
 }
diff --git a/third_party/blink/renderer/core/html/forms/select_type.h b/third_party/blink/renderer/core/html/forms/select_type.h
index 8b571315f..f781a00 100644
--- a/third_party/blink/renderer/core/html/forms/select_type.h
+++ b/third_party/blink/renderer/core/html/forms/select_type.h
@@ -44,9 +44,6 @@
   virtual const ComputedStyle* OptionStyle() const;
   virtual void MaximumOptionWidthMightBeChanged() const;
 
-  // Update :-internal-multi-select-focus state of selected OPTIONs.
-  virtual void UpdateMultiSelectFocus();
-
   virtual void SelectAll();
 
   virtual void ShowPopup();
diff --git a/third_party/blink/renderer/core/html/html_iframe_element_test.cc b/third_party/blink/renderer/core/html/html_iframe_element_test.cc
index 332e9f53..71f5cd5 100644
--- a/third_party/blink/renderer/core/html/html_iframe_element_test.cc
+++ b/third_party/blink/renderer/core/html/html_iframe_element_test.cc
@@ -9,6 +9,7 @@
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/dom/document_init.h"
 #include "third_party/blink/renderer/core/feature_policy/feature_policy_parser.h"
+#include "third_party/blink/renderer/core/testing/dummy_page_holder.h"
 #include "third_party/blink/renderer/platform/heap/heap.h"
 #include "third_party/blink/renderer/platform/wtf/text/string_utf8_adaptor.h"
 
@@ -25,23 +26,25 @@
 
   void SetUp() final {
     const KURL document_url("http://example.com");
-    DocumentInit init =
-        DocumentInit::Create()
-            .WithOriginToCommit(SecurityOrigin::Create(document_url))
-            .WithURL(document_url);
-    document_ = MakeGarbageCollected<Document>(init);
+    page_holder_ = std::make_unique<DummyPageHolder>(IntSize(800, 600));
+    document_ = &page_holder_->GetDocument();
+    document_->SetURL(document_url);
+    document_->GetSecurityContext().SetSecurityOriginForTesting(
+        SecurityOrigin::Create(document_url));
     frame_element_ = MakeGarbageCollected<HTMLIFrameElement>(*document_);
   }
 
   void TearDown() final {
     frame_element_.Clear();
     document_.Clear();
+    page_holder_.reset();
   }
 
  protected:
   const PolicyValue min_value = PolicyValue(false);
   const PolicyValue max_value = PolicyValue(true);
 
+  std::unique_ptr<DummyPageHolder> page_holder_;
   Persistent<Document> document_;
   Persistent<HTMLIFrameElement> frame_element_;
 };
diff --git a/third_party/blink/renderer/core/html/html_script_element.cc b/third_party/blink/renderer/core/html/html_script_element.cc
index cca567cd..e8f1a15 100644
--- a/third_party/blink/renderer/core/html/html_script_element.cc
+++ b/third_party/blink/renderer/core/html/html_script_element.cc
@@ -134,9 +134,8 @@
 void HTMLScriptElement::setInnerText(
     const StringOrTrustedScript& string_or_trusted_script,
     ExceptionState& exception_state) {
-  String value = TrustedTypesCheckForScript(string_or_trusted_script,
-                                            GetDocument().ToExecutionContext(),
-                                            exception_state);
+  String value = TrustedTypesCheckForScript(
+      string_or_trusted_script, GetExecutionContext(), exception_state);
   if (!exception_state.HadException()) {
     // https://w3c.github.io/webappsec-trusted-types/dist/spec/#setting-slot-values
     // On setting, the innerText [...] perform the regular steps, and then set
@@ -157,9 +156,8 @@
 void HTMLScriptElement::setTextContent(
     const StringOrTrustedScript& string_or_trusted_script,
     ExceptionState& exception_state) {
-  String value = TrustedTypesCheckForScript(string_or_trusted_script,
-                                            GetDocument().ToExecutionContext(),
-                                            exception_state);
+  String value = TrustedTypesCheckForScript(
+      string_or_trusted_script, GetExecutionContext(), exception_state);
   if (!exception_state.HadException()) {
     // https://w3c.github.io/webappsec-trusted-types/dist/spec/#setting-slot-values
     // On setting, [..] textContent [..] perform the regular steps, and then set
diff --git a/third_party/blink/renderer/core/html/media/html_media_element.cc b/third_party/blink/renderer/core/html/media/html_media_element.cc
index 89b47088..e5ecd5d 100644
--- a/third_party/blink/renderer/core/html/media/html_media_element.cc
+++ b/third_party/blink/renderer/core/html/media/html_media_element.cc
@@ -595,7 +595,7 @@
   // load event from within the destructor.
   old_document.DecrementLoadEventDelayCount();
 
-  SetExecutionContext(GetDocument().ToExecutionContext());
+  SetExecutionContext(GetExecutionContext());
   HTMLElement::DidMoveToNewDocument(old_document);
 }
 
diff --git a/third_party/blink/renderer/core/html/media/video_wake_lock.cc b/third_party/blink/renderer/core/html/media/video_wake_lock.cc
index f1d7438..7d10bf70 100644
--- a/third_party/blink/renderer/core/html/media/video_wake_lock.cc
+++ b/third_party/blink/renderer/core/html/media/video_wake_lock.cc
@@ -37,7 +37,7 @@
 }
 
 void VideoWakeLock::ElementDidMoveToNewDocument() {
-  SetExecutionContext(VideoElement().GetDocument().ToExecutionContext());
+  SetExecutionContext(VideoElement().GetExecutionContext());
 }
 
 void VideoWakeLock::PageVisibilityChanged() {
diff --git a/third_party/blink/renderer/core/html/media/video_wake_lock_test.cc b/third_party/blink/renderer/core/html/media/video_wake_lock_test.cc
index cb2f6f0..f58eada 100644
--- a/third_party/blink/renderer/core/html/media/video_wake_lock_test.cc
+++ b/third_party/blink/renderer/core/html/media/video_wake_lock_test.cc
@@ -12,6 +12,7 @@
 #include "third_party/blink/public/common/browser_interface_broker_proxy.h"
 #include "third_party/blink/public/mojom/picture_in_picture/picture_in_picture.mojom-blink.h"
 #include "third_party/blink/renderer/core/dom/events/event.h"
+#include "third_party/blink/renderer/core/frame/local_dom_window.h"
 #include "third_party/blink/renderer/core/frame/picture_in_picture_controller.h"
 #include "third_party/blink/renderer/core/html/media/html_media_test_helper.h"
 #include "third_party/blink/renderer/core/html/media/html_video_element.h"
@@ -159,7 +160,9 @@
         mojom::FrameLifecycleState::kRunning);
   }
 
-  void SimulateContextDestroyed() { GetDocument().NotifyContextDestroyed(); }
+  void SimulateContextDestroyed() {
+    GetFrame().DomWindow()->NotifyContextDestroyed();
+  }
 
   void SimulateNetworkState(HTMLMediaElement::NetworkState network_state) {
     video_->SetNetworkState(network_state);
diff --git a/third_party/blink/renderer/core/imagebitmap/image_bitmap_test.cc b/third_party/blink/renderer/core/imagebitmap/image_bitmap_test.cc
index f3b1a97..0622f5f 100644
--- a/third_party/blink/renderer/core/imagebitmap/image_bitmap_test.cc
+++ b/third_party/blink/renderer/core/imagebitmap/image_bitmap_test.cc
@@ -41,6 +41,7 @@
 #include "third_party/blink/renderer/core/html/html_image_element.h"
 #include "third_party/blink/renderer/core/html/media/html_video_element.h"
 #include "third_party/blink/renderer/core/loader/resource/image_resource_content.h"
+#include "third_party/blink/renderer/core/testing/dummy_page_holder.h"
 #include "third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.h"
 #include "third_party/blink/renderer/platform/graphics/canvas_resource_provider.h"
 #include "third_party/blink/renderer/platform/graphics/color_correction_test_utils.h"
@@ -104,8 +105,9 @@
 
 TEST_F(ImageBitmapTest, ImageResourceConsistency) {
   const ImageBitmapOptions* default_options = ImageBitmapOptions::Create();
+  auto dummy = std::make_unique<DummyPageHolder>(IntSize(800, 600));
   auto* image_element =
-      MakeGarbageCollected<HTMLImageElement>(*MakeGarbageCollected<Document>());
+      MakeGarbageCollected<HTMLImageElement>(dummy->GetDocument());
   sk_sp<SkColorSpace> src_rgb_color_space = SkColorSpace::MakeSRGB();
   SkImageInfo raster_image_info =
       SkImageInfo::MakeN32Premul(5, 5, src_rgb_color_space);
@@ -175,8 +177,8 @@
 // Verifies that ImageBitmaps constructed from HTMLImageElements hold a
 // reference to the original Image if the HTMLImageElement src is changed.
 TEST_F(ImageBitmapTest, ImageBitmapSourceChanged) {
-  auto* image =
-      MakeGarbageCollected<HTMLImageElement>(*MakeGarbageCollected<Document>());
+  auto dummy = std::make_unique<DummyPageHolder>(IntSize(800, 600));
+  auto* image = MakeGarbageCollected<HTMLImageElement>(dummy->GetDocument());
   sk_sp<SkColorSpace> src_rgb_color_space = SkColorSpace::MakeSRGB();
   SkImageInfo raster_image_info =
       SkImageInfo::MakeN32Premul(5, 5, src_rgb_color_space);
@@ -311,8 +313,9 @@
 }
 
 TEST_F(ImageBitmapTest, ImageBitmapColorSpaceConversionHTMLImageElement) {
+  auto dummy = std::make_unique<DummyPageHolder>(IntSize(800, 600));
   auto* image_element =
-      MakeGarbageCollected<HTMLImageElement>(*MakeGarbageCollected<Document>());
+      MakeGarbageCollected<HTMLImageElement>(dummy->GetDocument());
 
   SkPaint p;
   p.setColor(SK_ColorRED);
diff --git a/third_party/blink/renderer/core/layout/ng/ng_fieldset_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/ng_fieldset_layout_algorithm.cc
index f770adf6..470f1cf3 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_fieldset_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_fieldset_layout_algorithm.cc
@@ -30,7 +30,8 @@
 }
 
 scoped_refptr<const NGLayoutResult> NGFieldsetLayoutAlgorithm::Layout() {
-  // TODO(almaher): Finish fragmentation for legends.
+  // TODO(almaher): Make sure the border start is handled correctly during
+  // fragmentation.
 
   // Layout of a fieldset container consists of two parts: Create a child
   // fragment for the rendered legend (if any), and create a child fragment for
@@ -65,89 +66,131 @@
       container_builder_.SetIsInitialColumnBalancingPass();
   }
 
+  scoped_refptr<const NGBlockBreakToken> legend_break_token;
   scoped_refptr<const NGBlockBreakToken> content_break_token;
   bool has_seen_all_children = false;
   if (const auto* token = BreakToken()) {
     const auto child_tokens = token->ChildBreakTokens();
     if (wtf_size_t break_token_count = child_tokens.size()) {
-      scoped_refptr<const NGBlockBreakToken> child_token =
-          To<NGBlockBreakToken>(child_tokens[0]);
-      if (child_token) {
-        DCHECK(!child_token->InputNode().IsRenderedLegend());
-        content_break_token = child_token;
+      DCHECK_LE(break_token_count, 2u);
+      for (wtf_size_t break_token_idx = 0; break_token_idx < break_token_count;
+           break_token_idx++) {
+        scoped_refptr<const NGBlockBreakToken> child_token =
+            To<NGBlockBreakToken>(child_tokens[break_token_idx]);
+        if (child_token && child_token->InputNode().IsRenderedLegend()) {
+          DCHECK_EQ(break_token_idx, 0u);
+          legend_break_token = child_token;
+        } else {
+          content_break_token = child_token;
+        }
       }
-      // There shouldn't be any additional break tokens.
-      DCHECK_EQ(child_tokens.size(), 1u);
     }
-
     if (token->HasSeenAllChildren()) {
-      has_seen_all_children = true;
       container_builder_.SetHasSeenAllChildren();
+      has_seen_all_children = true;
     }
   }
 
-  // TODO(vmpstr): Skip child (including legend) layout for fieldset elements.
   NGBlockNode legend = Node().GetRenderedLegend();
-  if (!content_break_token && legend && !has_seen_all_children) {
+  bool legend_break = false;
+  bool block_start_padding_edge_adjusted = false;
+  bool legend_needs_layout =
+      legend && (legend_break_token || !IsResumingLayout(BreakToken()));
+
+  if (legend_needs_layout) {
     // Lay out the legend. While the fieldset container normally ignores its
     // padding, the legend is laid out within what would have been the content
     // box had the fieldset been a regular block with no weirdness.
     LogicalSize content_box_size =
         ShrinkAvailableSize(border_box_size, adjusted_border_padding);
-    auto legend_space =
-        CreateConstraintSpaceForLegend(legend, content_box_size);
-    auto result = legend.Layout(legend_space, BreakToken());
+    LogicalSize percentage_size = CalculateChildPercentageSize(
+        ConstraintSpace(), Node(), content_box_size);
+    NGBoxStrut legend_margins = ComputeMarginsFor(
+        legend.Style(), percentage_size.inline_size,
+        ConstraintSpace().GetWritingMode(), ConstraintSpace().Direction());
 
-    // TODO(layout-dev): Handle abortions caused by block fragmentation.
-    DCHECK_EQ(result->Status(), NGLayoutResult::kSuccess);
+    if (legend_break_token)
+      legend_margins.block_start = LayoutUnit();
 
-    const auto& physical_fragment = result->PhysicalFragment();
-    NGBoxStrut legend_margins =
-        ComputeMarginsFor(legend_space, legend.Style(), ConstraintSpace());
+    LogicalOffset legend_offset;
+    scoped_refptr<const NGLayoutResult> result;
+    LayoutUnit block_offset = legend_margins.block_start;
+    do {
+      auto legend_space = CreateConstraintSpaceForLegend(
+          legend, content_box_size, percentage_size, block_offset);
+      result = legend.Layout(legend_space, legend_break_token.get());
+
+      // TODO(layout-dev): Handle abortions caused by block fragmentation.
+      DCHECK_EQ(result->Status(), NGLayoutResult::kSuccess);
+
+      const auto& physical_fragment = result->PhysicalFragment();
+      legend_break = physical_fragment.BreakToken();
+
+      // We have already adjusted the legend block offset, no need to adjust
+      // again.
+      if (block_offset != legend_margins.block_start)
+        break;
+
+      LayoutUnit legend_margin_box_block_size =
+          NGFragment(writing_mode, physical_fragment).BlockSize() +
+          legend_margins.BlockSum();
+      LayoutUnit space_left =
+          borders.block_start - legend_margin_box_block_size;
+      if (space_left > LayoutUnit()) {
+        // If the border is the larger one, though, it will stay put at the
+        // border-box block-start edge of the fieldset. Then it's the legend
+        // that needs to be pushed. We'll center the margin box in this case, to
+        // make sure that both margins remain within the area occupied by the
+        // border also after adjustment.
+        block_offset += space_left / 2;
+        if (ConstraintSpace().HasBlockFragmentation())
+          continue;
+      } else {
+        // If the legend is larger than the width of the fieldset block-start
+        // border, the actual padding edge of the fieldset will be moved
+        // accordingly. This will be the block-start offset for the fieldset
+        // contents anonymous box.
+        block_start_padding_edge = legend_margin_box_block_size;
+        block_start_padding_edge_adjusted = true;
+      }
+      break;
+    } while (true);
+
     // If the margin box of the legend is at least as tall as the fieldset
-    // block-start border width, it will start at the block-start border edge of
-    // the fieldset. As a paint effect, the block-start border will be pushed so
-    // that the center of the border will be flush with the center of the
-    // border-box of the legend.
+    // block-start border width, it will start at the block-start border edge
+    // of the fieldset. As a paint effect, the block-start border will be
+    // pushed so that the center of the border will be flush with the center
+    // of the border-box of the legend.
     // TODO(mstensho): inline alignment
-    LogicalOffset legend_offset = LogicalOffset(
+    legend_offset = LogicalOffset(
         adjusted_border_padding.inline_start + legend_margins.inline_start,
-        legend_margins.block_start);
-    LayoutUnit legend_margin_box_block_size =
-        NGFragment(writing_mode, physical_fragment).BlockSize() +
-        legend_margins.BlockSum();
-    LayoutUnit space_left = borders.block_start - legend_margin_box_block_size;
-    if (space_left > LayoutUnit()) {
-      // If the border is the larger one, though, it will stay put at the
-      // border-box block-start edge of the fieldset. Then it's the legend that
-      // needs to be pushed. We'll center the margin box in this case, to make
-      // sure that both margins remain within the area occupied by the border
-      // also after adjustment.
-      legend_offset.block_offset += space_left / 2;
-    } else {
-      // If the legend is larger than the width of the fieldset block-start
-      // border, the actual padding edge of the fieldset will be moved
-      // accordingly. This will be the block-start offset for the fieldset
-      // contents anonymous box.
-      block_start_padding_edge = legend_margin_box_block_size;
-    }
+        block_offset);
 
-    container_builder_.AddChild(physical_fragment, legend_offset);
-    DCHECK(!physical_fragment.BreakToken());
+    container_builder_.AddResult(*result, legend_offset);
   }
 
   NGBoxStrut borders_with_legend = borders;
   borders_with_legend.block_start = block_start_padding_edge;
   LogicalSize adjusted_padding_box_size =
       ShrinkAvailableSize(border_box_size, borders_with_legend);
-  if (IsResumingLayout(BreakToken()))
+  if (content_break_token ||
+      (!block_start_padding_edge_adjusted && IsResumingLayout(BreakToken()))) {
     borders_with_legend.block_start = LayoutUnit();
+  }
   LayoutUnit intrinsic_block_size = borders_with_legend.BlockSum();
 
+  bool has_space_left = true;
+  if (ConstraintSpace().HasKnownFragmentainerBlockSize()) {
+    LayoutUnit space_left =
+        FragmentainerSpaceAtBfcStart(ConstraintSpace()) - intrinsic_block_size;
+    has_space_left = space_left > 0;
+  }
+
   // Proceed with normal fieldset children (excluding the rendered legend). They
   // all live inside an anonymous child box of the fieldset container.
   auto fieldset_content = Node().GetFieldsetContent();
-  if (fieldset_content && (content_break_token || !has_seen_all_children)) {
+  if (has_space_left && fieldset_content &&
+      (content_break_token || !has_seen_all_children)) {
     auto child_space = CreateConstraintSpaceForFieldsetContent(
         fieldset_content, adjusted_padding_box_size,
         borders_with_legend.block_start);
@@ -164,19 +207,20 @@
         NGFragment(writing_mode, physical_fragment).BlockSize();
     container_builder_.SetHasSeenAllChildren();
   }
+
+  LayoutUnit consumed_block_size =
+      BreakToken() ? BreakToken()->ConsumedBlockSize() : LayoutUnit();
   if (!fieldset_content) {
-    // There was no anonymous child to provide the padding, so we have to add it
-    // ourselves.
     container_builder_.SetHasSeenAllChildren();
+    // There was no anonymous child to provide the padding, so we have to add it
+    // ourselves. Subtract out the consumed block size to avoid over-calculating
+    // the block size when the fieldset's height is auto.
     intrinsic_block_size += padding.BlockSum();
   }
 
   intrinsic_block_size = ClampIntrinsicBlockSize(
       ConstraintSpace(), Node(), adjusted_border_padding, intrinsic_block_size);
 
-  LayoutUnit consumed_block_size =
-      BreakToken() ? BreakToken()->ConsumedBlockSize() : LayoutUnit();
-
   // Recompute the block-axis size now that we know our content size.
   border_box_size.block_size =
       ComputeBlockSizeForFragment(ConstraintSpace(), Style(), border_padding_,
@@ -189,10 +233,17 @@
   // contents, with the conjecture being that legend is part of the contents.
   // Thus, only do this adjustment if we do not contain size.
   if (!Node().ShouldApplySizeContainment()) {
-    LayoutUnit minimum_border_box_block_size =
-        borders_with_legend.BlockSum() + padding.BlockSum();
+    LayoutUnit minimum_border_box_block_size;
+    if (legend_needs_layout) {
+      minimum_border_box_block_size =
+          borders_with_legend.BlockSum() + padding.BlockSum();
+    }
+    // Similar to how we add the consumed block size to the intrinsic
+    // block size when calculating border_box_size.block_size, we also need to
+    // do so when the fieldset is adjusted to encompass the legend.
     border_box_size.block_size =
-        std::max(border_box_size.block_size, minimum_border_box_block_size);
+        std::max(border_box_size.block_size,
+                 minimum_border_box_block_size + consumed_block_size);
   }
 
   // TODO(almaher): end border and padding may overflow the parent
@@ -254,17 +305,23 @@
 const NGConstraintSpace
 NGFieldsetLayoutAlgorithm::CreateConstraintSpaceForLegend(
     NGBlockNode legend,
-    LogicalSize available_size) {
+    LogicalSize available_size,
+    LogicalSize percentage_size,
+    LayoutUnit block_offset) {
   NGConstraintSpaceBuilder builder(
       ConstraintSpace(), legend.Style().GetWritingMode(), /* is_new_fc */ true);
   SetOrthogonalFallbackInlineSizeIfNeeded(Style(), legend, &builder);
 
   builder.SetAvailableSize(available_size);
-  LogicalSize percentage_size =
-      CalculateChildPercentageSize(ConstraintSpace(), Node(), available_size);
   builder.SetPercentageResolutionSize(percentage_size);
   builder.SetIsShrinkToFit(legend.Style().LogicalWidth().IsAuto());
   builder.SetTextDirection(legend.Style().Direction());
+
+  if (ConstraintSpace().HasBlockFragmentation()) {
+    SetupFragmentation(ConstraintSpace(), legend, block_offset, &builder,
+                       /* is_new_fc */ true);
+    builder.SetEarlyBreakAppeal(container_builder_.BreakAppeal());
+  }
   return builder.ToConstraintSpace();
 }
 
diff --git a/third_party/blink/renderer/core/layout/ng/ng_fieldset_layout_algorithm.h b/third_party/blink/renderer/core/layout/ng/ng_fieldset_layout_algorithm.h
index b3f8f0e1..f4472e1 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_fieldset_layout_algorithm.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_fieldset_layout_algorithm.h
@@ -30,7 +30,9 @@
  private:
   const NGConstraintSpace CreateConstraintSpaceForLegend(
       NGBlockNode legend,
-      LogicalSize available_size);
+      LogicalSize available_size,
+      LogicalSize percentage_size,
+      LayoutUnit block_offset);
   const NGConstraintSpace CreateConstraintSpaceForFieldsetContent(
       NGBlockNode fieldset_content,
       LogicalSize padding_box_size,
diff --git a/third_party/blink/renderer/core/layout/ng/ng_fieldset_layout_algorithm_test.cc b/third_party/blink/renderer/core/layout/ng/ng_fieldset_layout_algorithm_test.cc
index 4979bb575..b06f877 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_fieldset_layout_algorithm_test.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_fieldset_layout_algorithm_test.cc
@@ -574,6 +574,45 @@
   ASSERT_FALSE(fragment->BreakToken());
 }
 
+// Tests that a fieldset with no content or padding will fragment if it reaches
+// the fragmentation line.
+TEST_F(NGFieldsetLayoutAlgorithmTest, FragmentationNoPadding) {
+  SetBodyInnerHTML(R"HTML(
+    <style>
+      #fieldset { margin:0; border:10px solid; padding:0px; width:100px; }
+    </style>
+    <fieldset id="fieldset"></fieldset>
+  )HTML");
+
+  LayoutUnit kFragmentainerSpaceAvailable(10);
+
+  NGBlockNode node(ToLayoutBox(GetLayoutObjectByElementId("fieldset")));
+  NGConstraintSpace space = ConstructBlockLayoutTestConstraintSpace(
+      WritingMode::kHorizontalTb, TextDirection::kLtr,
+      LogicalSize(LayoutUnit(1000), kIndefiniteSize), false,
+      node.CreatesNewFormattingContext(), kFragmentainerSpaceAvailable);
+
+  scoped_refptr<const NGPhysicalBoxFragment> fragment =
+      NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm(node, space);
+  ASSERT_FALSE(fragment->BreakToken()->IsFinished());
+
+  String dump = DumpFragmentTree(fragment.get());
+  String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
+  offset:unplaced size:120x10
+)DUMP";
+  EXPECT_EQ(expectation, dump);
+
+  fragment = NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm(
+      node, space, fragment->BreakToken());
+  ASSERT_FALSE(fragment->BreakToken());
+
+  dump = DumpFragmentTree(fragment.get());
+  expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
+  offset:unplaced size:120x10
+)DUMP";
+  EXPECT_EQ(expectation, dump);
+}
+
 // Tests that a fieldset with auto height will fragment when its content reaches
 // the fragmentation line.
 TEST_F(NGFieldsetLayoutAlgorithmTest, FieldsetContentFragmentationAutoHeight) {
@@ -588,7 +627,7 @@
         }
       </style>
       <fieldset id="fieldset">
-        <div id="child"></child>
+        <div id="child"></div>
       </fieldset>
   )HTML");
 
@@ -651,7 +690,7 @@
         }
       </style>
       <fieldset id="fieldset">
-        <div id="child"></child>
+        <div id="child"></div>
       </fieldset>
   )HTML");
 
@@ -700,5 +739,336 @@
   EXPECT_EQ(expectation, dump);
 }
 
+// Tests that a fieldset with auto height will fragment when its legend reaches
+// the fragmentation line.
+TEST_F(NGFieldsetLayoutAlgorithmTest, LegendFragmentationAutoHeight) {
+  SetBodyInnerHTML(R"HTML(
+      <!DOCTYPE html>
+      <style>
+        #fieldset {
+          border:3px solid; margin:0; padding:10px; width: 150px;
+        }
+        #legend {
+          padding:0px; margin:0; width: 50px; height: 500px;
+        }
+      </style>
+      <fieldset id="fieldset">
+        <legend id="legend"></legend>
+      </fieldset>
+  )HTML");
+
+  LayoutUnit kFragmentainerSpaceAvailable(200);
+
+  NGBlockNode node(ToLayoutBox(GetLayoutObjectByElementId("fieldset")));
+  NGConstraintSpace space = ConstructBlockLayoutTestConstraintSpace(
+      WritingMode::kHorizontalTb, TextDirection::kLtr,
+      LogicalSize(LayoutUnit(1000), kIndefiniteSize), false,
+      node.CreatesNewFormattingContext(), kFragmentainerSpaceAvailable);
+
+  scoped_refptr<const NGPhysicalBoxFragment> fragment =
+      NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm(node, space);
+  ASSERT_FALSE(fragment->BreakToken()->IsFinished());
+
+  String dump = DumpFragmentTree(fragment.get());
+  String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
+  offset:unplaced size:176x200
+    offset:13,0 size:50x200
+)DUMP";
+  EXPECT_EQ(expectation, dump);
+
+  fragment = NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm(
+      node, space, fragment->BreakToken());
+  ASSERT_FALSE(fragment->BreakToken()->IsFinished());
+
+  dump = DumpFragmentTree(fragment.get());
+  expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
+  offset:unplaced size:176x200
+    offset:13,0 size:50x200
+)DUMP";
+  EXPECT_EQ(expectation, dump);
+
+  fragment = NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm(
+      node, space, fragment->BreakToken());
+  ASSERT_FALSE(fragment->BreakToken());
+
+  dump = DumpFragmentTree(fragment.get());
+  expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
+  offset:unplaced size:176x123
+    offset:13,0 size:50x100
+    offset:3,100 size:170x20
+)DUMP";
+  EXPECT_EQ(expectation, dump);
+}
+
+// Tests that a fieldset with a set height will fragment when its legend
+// reaches the fragmentation line. The used height should also be extended to
+// encompass the legend.
+TEST_F(NGFieldsetLayoutAlgorithmTest, LegendFragmentation) {
+  SetBodyInnerHTML(R"HTML(
+      <!DOCTYPE html>
+      <style>
+        #fieldset {
+          border:3px solid; margin:0; padding:10px; width: 150px; height: 100px;
+        }
+        #legend {
+          padding:0px; margin:0; width: 50px; height: 500px;
+        }
+      </style>
+      <fieldset id="fieldset">
+        <legend id="legend"></legend>
+      </fieldset>
+  )HTML");
+
+  LayoutUnit kFragmentainerSpaceAvailable(200);
+
+  NGBlockNode node(ToLayoutBox(GetLayoutObjectByElementId("fieldset")));
+  NGConstraintSpace space = ConstructBlockLayoutTestConstraintSpace(
+      WritingMode::kHorizontalTb, TextDirection::kLtr,
+      LogicalSize(LayoutUnit(1000), kIndefiniteSize), false,
+      node.CreatesNewFormattingContext(), kFragmentainerSpaceAvailable);
+
+  scoped_refptr<const NGPhysicalBoxFragment> fragment =
+      NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm(node, space);
+  ASSERT_FALSE(fragment->BreakToken()->IsFinished());
+
+  String dump = DumpFragmentTree(fragment.get());
+  String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
+  offset:unplaced size:176x200
+    offset:13,0 size:50x200
+)DUMP";
+  EXPECT_EQ(expectation, dump);
+
+  fragment = NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm(
+      node, space, fragment->BreakToken());
+  ASSERT_FALSE(fragment->BreakToken()->IsFinished());
+
+  dump = DumpFragmentTree(fragment.get());
+  expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
+  offset:unplaced size:176x200
+    offset:13,0 size:50x200
+)DUMP";
+  EXPECT_EQ(expectation, dump);
+
+  fragment = NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm(
+      node, space, fragment->BreakToken());
+  ASSERT_FALSE(fragment->BreakToken());
+
+  dump = DumpFragmentTree(fragment.get());
+  expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
+  offset:unplaced size:176x123
+    offset:13,0 size:50x100
+    offset:3,100 size:170x23
+)DUMP";
+  EXPECT_EQ(expectation, dump);
+}
+
+// Tests that a fieldset with auto height will fragment when its legend/content
+// reaches the fragmentation line.
+TEST_F(NGFieldsetLayoutAlgorithmTest, LegendAndContentFragmentationAutoHeight) {
+  SetBodyInnerHTML(R"HTML(
+      <!DOCTYPE html>
+      <style>
+        #fieldset {
+          border:3px solid; margin:0; padding:10px; width: 150px;
+        }
+        #legend {
+          padding:0px; margin:0; width: 50px; height: 500px;
+        }
+        #child {
+          margin:0; width: 100px; height: 200px;
+        }
+      </style>
+      <fieldset id="fieldset">
+        <legend id="legend"></legend>
+        <div id="child"></div>
+      </fieldset>
+  )HTML");
+
+  LayoutUnit kFragmentainerSpaceAvailable(200);
+
+  NGBlockNode node(ToLayoutBox(GetLayoutObjectByElementId("fieldset")));
+  NGConstraintSpace space = ConstructBlockLayoutTestConstraintSpace(
+      WritingMode::kHorizontalTb, TextDirection::kLtr,
+      LogicalSize(LayoutUnit(1000), kIndefiniteSize), false,
+      node.CreatesNewFormattingContext(), kFragmentainerSpaceAvailable);
+
+  scoped_refptr<const NGPhysicalBoxFragment> fragment =
+      NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm(node, space);
+  ASSERT_FALSE(fragment->BreakToken()->IsFinished());
+
+  String dump = DumpFragmentTree(fragment.get());
+  String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
+  offset:unplaced size:176x200
+    offset:13,0 size:50x200
+)DUMP";
+  EXPECT_EQ(expectation, dump);
+
+  fragment = NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm(
+      node, space, fragment->BreakToken());
+  ASSERT_FALSE(fragment->BreakToken()->IsFinished());
+
+  dump = DumpFragmentTree(fragment.get());
+  expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
+  offset:unplaced size:176x200
+    offset:13,0 size:50x200
+)DUMP";
+  EXPECT_EQ(expectation, dump);
+
+  fragment = NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm(
+      node, space, fragment->BreakToken());
+  ASSERT_FALSE(fragment->BreakToken()->IsFinished());
+
+  dump = DumpFragmentTree(fragment.get());
+  expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
+  offset:unplaced size:176x200
+    offset:13,0 size:50x100
+    offset:3,100 size:170x100
+      offset:10,10 size:100x90
+)DUMP";
+  EXPECT_EQ(expectation, dump);
+
+  fragment = NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm(
+      node, space, fragment->BreakToken());
+  ASSERT_FALSE(fragment->BreakToken());
+
+  dump = DumpFragmentTree(fragment.get());
+  expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
+  offset:unplaced size:176x123
+    offset:3,0 size:170x120
+      offset:10,0 size:100x110
+)DUMP";
+  EXPECT_EQ(expectation, dump);
+}
+
+// Tests that a fieldset with a set height will fragment when its legend/content
+// reaches the fragmentation line.
+TEST_F(NGFieldsetLayoutAlgorithmTest, LegendAndContentFragmentation) {
+  SetBodyInnerHTML(R"HTML(
+      <!DOCTYPE html>
+      <style>
+        #fieldset {
+          border:3px solid; margin:0; padding:10px; width: 150px; height: 100px;
+        }
+        #legend {
+          padding:0px; margin:0; width: 50px; height: 500px;
+        }
+        #child {
+          margin:0; width: 100px; height: 200px;
+        }
+      </style>
+      <fieldset id="fieldset">
+        <legend id="legend"></legend>
+        <div id="child"></div>
+      </fieldset>
+  )HTML");
+
+  LayoutUnit kFragmentainerSpaceAvailable(200);
+
+  NGBlockNode node(ToLayoutBox(GetLayoutObjectByElementId("fieldset")));
+  NGConstraintSpace space = ConstructBlockLayoutTestConstraintSpace(
+      WritingMode::kHorizontalTb, TextDirection::kLtr,
+      LogicalSize(LayoutUnit(1000), kIndefiniteSize), false,
+      node.CreatesNewFormattingContext(), kFragmentainerSpaceAvailable);
+
+  scoped_refptr<const NGPhysicalBoxFragment> fragment =
+      NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm(node, space);
+  ASSERT_FALSE(fragment->BreakToken()->IsFinished());
+
+  String dump = DumpFragmentTree(fragment.get());
+  String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
+  offset:unplaced size:176x200
+    offset:13,0 size:50x200
+)DUMP";
+  EXPECT_EQ(expectation, dump);
+
+  fragment = NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm(
+      node, space, fragment->BreakToken());
+  ASSERT_FALSE(fragment->BreakToken()->IsFinished());
+
+  dump = DumpFragmentTree(fragment.get());
+  expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
+  offset:unplaced size:176x200
+    offset:13,0 size:50x200
+)DUMP";
+  EXPECT_EQ(expectation, dump);
+
+  fragment = NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm(
+      node, space, fragment->BreakToken());
+  ASSERT_FALSE(fragment->BreakToken()->IsFinished());
+
+  dump = DumpFragmentTree(fragment.get());
+  expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
+  offset:unplaced size:176x123
+    offset:13,0 size:50x100
+    offset:3,100 size:170x23
+      offset:10,10 size:100x90
+)DUMP";
+  EXPECT_EQ(expectation, dump);
+
+  fragment = NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm(
+      node, space, fragment->BreakToken());
+  ASSERT_FALSE(fragment->BreakToken());
+
+  dump = DumpFragmentTree(fragment.get());
+  expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
+  offset:unplaced size:176x0
+    offset:3,0 size:170x97
+      offset:10,0 size:100x110
+)DUMP";
+  EXPECT_EQ(expectation, dump);
+}
+
+// Tests fragmentation when a legend's child content overflows.
+TEST_F(NGFieldsetLayoutAlgorithmTest, LegendFragmentationWithOverflow) {
+  SetBodyInnerHTML(R"HTML(
+      <!DOCTYPE html>
+      <style>
+        fieldset, legend { margin:0; border:none; padding:0; }
+      </style>
+      <fieldset id="fieldset">
+        <legend style="height:30px;">
+          <div style="width:55px; height:150px;"></div>
+        </legend>
+        <div style="width:44px; height:150px;"></div>
+      </div>
+  )HTML");
+
+  LayoutUnit kFragmentainerSpaceAvailable(100);
+
+  NGBlockNode node(ToLayoutBox(GetLayoutObjectByElementId("fieldset")));
+  NGConstraintSpace space = ConstructBlockLayoutTestConstraintSpace(
+      WritingMode::kHorizontalTb, TextDirection::kLtr,
+      LogicalSize(LayoutUnit(1000), kIndefiniteSize), false,
+      node.CreatesNewFormattingContext(), kFragmentainerSpaceAvailable);
+
+  scoped_refptr<const NGPhysicalBoxFragment> fragment =
+      NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm(node, space);
+  ASSERT_FALSE(fragment->BreakToken()->IsFinished());
+
+  String dump = DumpFragmentTree(fragment.get());
+  String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
+  offset:unplaced size:1000x100
+    offset:0,0 size:55x30
+      offset:0,0 size:55x100
+    offset:0,30 size:1000x70
+      offset:0,0 size:44x70
+)DUMP";
+  EXPECT_EQ(expectation, dump);
+
+  fragment = NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm(
+      node, space, fragment->BreakToken());
+  ASSERT_FALSE(fragment->BreakToken());
+
+  dump = DumpFragmentTree(fragment.get());
+  expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
+  offset:unplaced size:1000x80
+    offset:0,0 size:55x0
+      offset:0,0 size:55x50
+    offset:0,0 size:1000x80
+      offset:0,0 size:44x80
+)DUMP";
+  EXPECT_EQ(expectation, dump);
+}
+
 }  // anonymous namespace
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/loader/resource/document_resource.cc b/third_party/blink/renderer/core/loader/resource/document_resource.cc
index fc65f1b..ea05b85 100644
--- a/third_party/blink/renderer/core/loader/resource/document_resource.cc
+++ b/third_party/blink/renderer/core/loader/resource/document_resource.cc
@@ -34,15 +34,20 @@
 
 namespace blink {
 
-DocumentResource* DocumentResource::FetchSVGDocument(FetchParameters& params,
-                                                     ResourceFetcher* fetcher,
-                                                     ResourceClient* client) {
+DocumentResource* DocumentResource::FetchSVGDocument(
+    FetchParameters& params,
+    const Document& context_document,
+    ResourceClient* client) {
   DCHECK_EQ(params.GetResourceRequest().GetMode(),
             network::mojom::RequestMode::kSameOrigin);
   params.SetRequestContext(mojom::RequestContextType::IMAGE);
   params.SetRequestDestination(network::mojom::RequestDestination::kImage);
-  return To<DocumentResource>(
-      fetcher->RequestResource(params, SVGDocumentResourceFactory(), client));
+  auto* resource =
+      To<DocumentResource>(context_document.Fetcher()->RequestResource(
+          params, SVGDocumentResourceFactory(), client));
+  if (!resource->document_ && !resource->context_document_)
+    resource->context_document_ = const_cast<Document*>(&context_document);
+  return resource;
 }
 
 DocumentResource::DocumentResource(
@@ -59,6 +64,7 @@
 
 void DocumentResource::Trace(Visitor* visitor) {
   visitor->Trace(document_);
+  visitor->Trace(context_document_);
   Resource::Trace(visitor);
 }
 
@@ -84,7 +90,9 @@
 Document* DocumentResource::CreateDocument(const KURL& url) {
   switch (GetType()) {
     case ResourceType::kSVGDocument:
-      return XMLDocument::CreateSVG(DocumentInit::Create().WithURL(url));
+      return XMLDocument::CreateSVG(
+          DocumentInit::Create().WithURL(url).WithContextDocument(
+              context_document_));
     default:
       // FIXME: We'll add more types to support HTMLImports.
       NOTREACHED();
diff --git a/third_party/blink/renderer/core/loader/resource/document_resource.h b/third_party/blink/renderer/core/loader/resource/document_resource.h
index 4ecff95..a468525 100644
--- a/third_party/blink/renderer/core/loader/resource/document_resource.h
+++ b/third_party/blink/renderer/core/loader/resource/document_resource.h
@@ -35,12 +35,11 @@
 
 class Document;
 class FetchParameters;
-class ResourceFetcher;
 
 class CORE_EXPORT DocumentResource final : public TextResource {
  public:
   static DocumentResource* FetchSVGDocument(FetchParameters&,
-                                            ResourceFetcher*,
+                                            const Document& context_document,
                                             ResourceClient*);
 
   DocumentResource(const ResourceRequest&,
@@ -74,6 +73,7 @@
   Document* CreateDocument(const KURL&);
 
   Member<Document> document_;
+  WeakMember<Document> context_document_;
 };
 
 template <>
diff --git a/third_party/blink/renderer/core/origin_trials/origin_trial_context.cc b/third_party/blink/renderer/core/origin_trials/origin_trial_context.cc
index b0ce142..67deb68 100644
--- a/third_party/blink/renderer/core/origin_trials/origin_trial_context.cc
+++ b/third_party/blink/renderer/core/origin_trials/origin_trial_context.cc
@@ -16,6 +16,7 @@
 #include "third_party/blink/renderer/bindings/core/v8/worker_or_worklet_script_controller.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
+#include "third_party/blink/renderer/core/frame/local_dom_window.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/core/origin_trials/origin_trials.h"
 #include "third_party/blink/renderer/core/workers/worklet_global_scope.h"
@@ -281,7 +282,8 @@
   // For the purposes of origin trials, we consider imported documents to be
   // part of the master document. Thus, check if the trial is enabled in the
   // master document and use that result.
-  auto* document = Document::DynamicFrom(context_.Get());
+  auto* window = DynamicTo<LocalDOMWindow>(context_.Get());
+  auto* document = window ? window->document() : nullptr;
   if (!document || !document->IsHTMLImport())
     return false;
 
diff --git a/third_party/blink/renderer/core/script/script_runner.h b/third_party/blink/renderer/core/script/script_runner.h
index 1b5ab6db..083622207 100644
--- a/third_party/blink/renderer/core/script/script_runner.h
+++ b/third_party/blink/renderer/core/script/script_runner.h
@@ -64,6 +64,10 @@
 
   static void MovePendingScript(Document&, Document&, ScriptLoader*);
 
+  void SetTaskRunnerForTesting(base::SingleThreadTaskRunner* task_runner) {
+    task_runner_ = task_runner;
+  }
+
   void Trace(Visitor*) override;
   const char* NameInHeapSnapshot() const override { return "ScriptRunner"; }
 
diff --git a/third_party/blink/renderer/core/script/script_runner_test.cc b/third_party/blink/renderer/core/script/script_runner_test.cc
index 66c1ca6..27fd139 100644
--- a/third_party/blink/renderer/core/script/script_runner_test.cc
+++ b/third_party/blink/renderer/core/script/script_runner_test.cc
@@ -10,6 +10,7 @@
 #include "third_party/blink/renderer/core/script/mock_script_element_base.h"
 #include "third_party/blink/renderer/core/script/pending_script.h"
 #include "third_party/blink/renderer/core/script/script.h"
+#include "third_party/blink/renderer/core/testing/dummy_page_holder.h"
 #include "third_party/blink/renderer/platform/bindings/runtime_call_stats.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
 #include "third_party/blink/renderer/platform/testing/testing_platform_support_with_mock_scheduler.h"
@@ -76,14 +77,16 @@
 
 class ScriptRunnerTest : public testing::Test {
  public:
-  ScriptRunnerTest() : document_(MakeGarbageCollected<Document>()) {}
+  ScriptRunnerTest()
+      : page_holder_(std::make_unique<DummyPageHolder>()),
+        document_(&page_holder_->GetDocument()) {}
 
   void SetUp() override {
-    // We have to create ScriptRunner after initializing platform, because we
-    // need Platform::current()->currentThread()->scheduler()->
-    // loadingTaskRunner() to be initialized before creating ScriptRunner to
-    // save it in constructor.
     script_runner_ = MakeGarbageCollected<ScriptRunner>(document_.Get());
+    // Give ScriptRunner a task runner that platform_ will pump in
+    // RunUntilIdle()/RunSingleTask().
+    script_runner_->SetTaskRunnerForTesting(
+        Thread::Current()->GetTaskRunner().get());
     RuntimeCallStats::SetRuntimeCallStatsForTesting();
   }
   void TearDown() override {
@@ -101,6 +104,7 @@
     script_runner_->QueueScriptForExecution(pending_script);
   }
 
+  std::unique_ptr<DummyPageHolder> page_holder_;
   Persistent<Document> document_;
   Persistent<ScriptRunner> script_runner_;
   WTF::Vector<int> order_;
diff --git a/third_party/blink/renderer/core/style/computed_style_test.cc b/third_party/blink/renderer/core/style/computed_style_test.cc
index 8978518..ee99144 100644
--- a/third_party/blink/renderer/core/style/computed_style_test.cc
+++ b/third_party/blink/renderer/core/style/computed_style_test.cc
@@ -464,9 +464,9 @@
 }
 
 TEST(ComputedStyleTest, CustomPropertiesEqual_Values) {
-  auto* document = MakeGarbageCollected<Document>();
-  css_test_helpers::RegisterProperty(*document, "--x", "<length>", "0px",
-                                     false);
+  auto dummy = std::make_unique<DummyPageHolder>(IntSize(0, 0));
+  css_test_helpers::RegisterProperty(dummy->GetDocument(), "--x", "<length>",
+                                     "0px", false);
 
   scoped_refptr<ComputedStyle> style1 = ComputedStyle::Create();
   scoped_refptr<ComputedStyle> style2 = ComputedStyle::Create();
@@ -494,9 +494,9 @@
 }
 
 TEST(ComputedStyleTest, CustomPropertiesEqual_Data) {
-  auto* document = MakeGarbageCollected<Document>();
-  css_test_helpers::RegisterProperty(*document, "--x", "<length>", "0px",
-                                     false);
+  auto dummy = std::make_unique<DummyPageHolder>(IntSize(0, 0));
+  css_test_helpers::RegisterProperty(dummy->GetDocument(), "--x", "<length>",
+                                     "0px", false);
 
   scoped_refptr<ComputedStyle> style1 = ComputedStyle::Create();
   scoped_refptr<ComputedStyle> style2 = ComputedStyle::Create();
diff --git a/third_party/blink/renderer/core/svg/svg_element.cc b/third_party/blink/renderer/core/svg/svg_element.cc
index 1bf5161..71de49e 100644
--- a/third_party/blink/renderer/core/svg/svg_element.cc
+++ b/third_party/blink/renderer/core/svg/svg_element.cc
@@ -180,7 +180,7 @@
 void SVGElement::SetWebAnimationsPending() {
   GetDocument().AccessSVGExtensions().AddWebAnimationsPendingSVGElement(*this);
   EnsureSVGRareData()->SetWebAnimatedAttributesDirty(true);
-  EnsureUniqueElementData().animated_svg_attributes_are_dirty_ = true;
+  EnsureUniqueElementData().SetAnimatedSvgAttributesAreDirty(true);
 }
 
 static bool IsSVGAttributeHandle(const PropertyHandle& property_handle) {
@@ -900,7 +900,7 @@
   // TODO(alancutter): Only mark attributes as dirty if their animation depends
   // on the underlying value.
   SvgRareData()->SetWebAnimatedAttributesDirty(true);
-  GetElementData()->animated_svg_attributes_are_dirty_ = true;
+  GetElementData()->SetAnimatedSvgAttributesAreDirty(true);
 }
 
 void SVGElement::EnsureAttributeAnimValUpdated() {
@@ -918,7 +918,7 @@
 void SVGElement::SynchronizeAnimatedSVGAttribute(
     const QualifiedName& name) const {
   if (!GetElementData() ||
-      !GetElementData()->animated_svg_attributes_are_dirty_)
+      !GetElementData()->animated_svg_attributes_are_dirty())
     return;
 
   // We const_cast here because we have deferred baseVal mutation animation
@@ -935,7 +935,7 @@
         (*it)->SynchronizeAttribute();
     }
 
-    GetElementData()->animated_svg_attributes_are_dirty_ = false;
+    GetElementData()->SetAnimatedSvgAttributesAreDirty(false);
   } else {
     SVGAnimatedPropertyBase* property = attribute_to_property_map_.at(name);
     if (property && property->NeedsSynchronizeAttribute())
diff --git a/third_party/blink/renderer/core/svg/svg_element.h b/third_party/blink/renderer/core/svg/svg_element.h
index a81b35c..c11a50ff 100644
--- a/third_party/blink/renderer/core/svg/svg_element.h
+++ b/third_party/blink/renderer/core/svg/svg_element.h
@@ -132,10 +132,10 @@
   virtual AffineTransform* AnimateMotionTransform() { return nullptr; }
 
   void InvalidateSVGAttributes() {
-    EnsureUniqueElementData().animated_svg_attributes_are_dirty_ = true;
+    EnsureUniqueElementData().SetAnimatedSvgAttributesAreDirty(true);
   }
   void InvalidateSVGPresentationAttributeStyle() {
-    EnsureUniqueElementData().presentation_attribute_style_is_dirty_ = true;
+    EnsureUniqueElementData().SetPresentationAttributeStyleIsDirty(true);
   }
 
   const HeapHashSet<WeakMember<SVGElement>>& InstancesForElement() const;
diff --git a/third_party/blink/renderer/core/svg/svg_resource.cc b/third_party/blink/renderer/core/svg/svg_resource.cc
index 9cf40b6..110d32b4 100644
--- a/third_party/blink/renderer/core/svg/svg_resource.cc
+++ b/third_party/blink/renderer/core/svg/svg_resource.cc
@@ -141,7 +141,7 @@
   params.MutableResourceRequest().SetMode(
       network::mojom::blink::RequestMode::kSameOrigin);
   resource_document_ =
-      DocumentResource::FetchSVGDocument(params, document.Fetcher(), this);
+      DocumentResource::FetchSVGDocument(params, document, this);
   target_ = ResolveTarget();
 }
 
@@ -156,7 +156,7 @@
   params.MutableResourceRequest().SetMode(
       network::mojom::blink::RequestMode::kSameOrigin);
   resource_document_ =
-      DocumentResource::FetchSVGDocument(params, document.Fetcher(), this);
+      DocumentResource::FetchSVGDocument(params, document, this);
   target_ = ResolveTarget();
 }
 
diff --git a/third_party/blink/renderer/core/svg/svg_use_element.cc b/third_party/blink/renderer/core/svg/svg_use_element.cc
index 1122d75a..1ee1e84 100644
--- a/third_party/blink/renderer/core/svg/svg_use_element.cc
+++ b/third_party/blink/renderer/core/svg/svg_use_element.cc
@@ -205,7 +205,7 @@
   FetchParameters params(ResourceRequest(element_url_), options);
   params.MutableResourceRequest().SetMode(
       network::mojom::RequestMode::kSameOrigin);
-  ResourceFetcher* fetcher = GetDocument().Fetcher();
+  auto* context_document = &GetDocument();
   if (base::FeatureList::IsEnabled(
           features::kHtmlImportsRequestInitiatorLock) &&
       GetDocument().ImportsController()) {
@@ -217,9 +217,9 @@
       ClearResource();
       return;
     }
-    fetcher = GetDocument().ContextDocument()->Fetcher();
+    context_document = GetDocument().ContextDocument();
   }
-  DocumentResource::FetchSVGDocument(params, fetcher, this);
+  DocumentResource::FetchSVGDocument(params, *context_document, this);
 }
 
 void SVGUseElement::SvgAttributeChanged(const QualifiedName& attr_name) {
diff --git a/third_party/blink/renderer/core/testing/internals.cc b/third_party/blink/renderer/core/testing/internals.cc
index f3bd3a4..45eaf70b 100644
--- a/third_party/blink/renderer/core/testing/internals.cc
+++ b/third_party/blink/renderer/core/testing/internals.cc
@@ -3509,12 +3509,13 @@
   resolver->Resolve(resource_load_priority);
 }
 
-String Internals::getDocumentAgentId(Document* document) {
+String Internals::getAgentId(DOMWindow* window) {
   // Sounds like there's no notion of "process ID" in Blink, but the main
   // thread's thread ID serves for that purpose.
   PlatformThreadId process_id = Thread::MainThread()->ThreadId();
 
-  uintptr_t agent_address = reinterpret_cast<uintptr_t>(document->GetAgent());
+  uintptr_t agent_address =
+      reinterpret_cast<uintptr_t>(To<LocalDOMWindow>(window)->GetAgent());
 
   // This serializes a pointer as a decimal number, which is a bit ugly, but
   // it works. Is there any utility to dump a number in a hexadecimal form?
diff --git a/third_party/blink/renderer/core/testing/internals.h b/third_party/blink/renderer/core/testing/internals.h
index 6891302c..587f1ef 100644
--- a/third_party/blink/renderer/core/testing/internals.h
+++ b/third_party/blink/renderer/core/testing/internals.h
@@ -595,7 +595,7 @@
 
   void setDeviceEmulationScale(float scale, ExceptionState&);
 
-  String getDocumentAgentId(Document*);
+  String getAgentId(DOMWindow*);
 
   void useMockOverlayScrollbars();
   bool overlayScrollbarsEnabled() const;
diff --git a/third_party/blink/renderer/core/testing/internals.idl b/third_party/blink/renderer/core/testing/internals.idl
index fc7e640..e363515 100644
--- a/third_party/blink/renderer/core/testing/internals.idl
+++ b/third_party/blink/renderer/core/testing/internals.idl
@@ -424,11 +424,11 @@
     // Sets the scale for devtools device emulation.
     [RaisesException] void setDeviceEmulationScale(float scale);
 
-    // Return a string that identifies |document|'s WindowAgent. You can use it
+    // Return a string that identifies |window|'s WindowAgent. You can use it
     // to distinguish different Agent instances (perhaps in a remote process).
     // The returned string is composed of the process ID and the memory address
     // of the Agent object.
-    DOMString getDocumentAgentId(Document document);
+    DOMString getAgentId(Window window);
 
     void useMockOverlayScrollbars();
     readonly attribute boolean overlayScrollbarsEnabled;
diff --git a/third_party/blink/renderer/core/timing/window_performance_test.cc b/third_party/blink/renderer/core/timing/window_performance_test.cc
index 2e6bcbdd..dccd01e 100644
--- a/third_party/blink/renderer/core/timing/window_performance_test.cc
+++ b/third_party/blink/renderer/core/timing/window_performance_test.cc
@@ -10,6 +10,7 @@
 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_testing.h"
 #include "third_party/blink/renderer/core/dom/document_init.h"
+#include "third_party/blink/renderer/core/execution_context/security_context_init.h"
 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
 #include "third_party/blink/renderer/core/frame/performance_monitor.h"
 #include "third_party/blink/renderer/core/loader/document_load_timing.h"
@@ -150,13 +151,7 @@
   EXPECT_TRUE(ObservingLongTasks());
 
   // Simulate navigation commit.
-  DocumentInit init =
-      DocumentInit::Create()
-          .WithDocumentLoader(GetFrame()->Loader().GetDocumentLoader())
-          .WithTypeFrom("text/html");
-  GetDocument()->Shutdown();
-  GetFrame()->SetDOMWindow(MakeGarbageCollected<LocalDOMWindow>(*GetFrame()));
-  GetFrame()->DomWindow()->InstallNewDocument(init, false);
+  GetFrame()->DomWindow()->FrameDestroyed();
 
   // m_performance is still alive, and should not crash when notified.
   SimulateDidProcessLongTask();
diff --git a/third_party/blink/renderer/core/trustedtypes/trusted_types_util.cc b/third_party/blink/renderer/core/trustedtypes/trusted_types_util.cc
index 00b6c91b..6531df0 100644
--- a/third_party/blink/renderer/core/trustedtypes/trusted_types_util.cc
+++ b/third_party/blink/renderer/core/trustedtypes/trusted_types_util.cc
@@ -273,7 +273,7 @@
                                 const Document* document,
                                 ExceptionState& exception_state) {
   return TrustedTypesCheckForHTML(
-      html, document ? document->ToExecutionContext() : nullptr,
+      html, document ? document->GetExecutionContext() : nullptr,
       exception_state);
 }
 
diff --git a/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc b/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc
index 3355157..24dc6bb 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc
@@ -2184,6 +2184,9 @@
   AddValidationMessageChild();
   AddAccessibleNodeChildren();
 
+  for (const auto& owned_child : owned_children)
+    AddChild(owned_child);
+
   for (const auto& child : children_) {
     if (!is_continuation && !child->CachedParentObject()) {
       // Never set continuations as a parent object. The first layout object
@@ -2191,9 +2194,6 @@
       child->SetParent(this);
     }
   }
-
-  for (const auto& owned_child : owned_children)
-    AddChild(owned_child);
 }
 
 bool AXLayoutObject::CanHaveChildren() const {
diff --git a/third_party/blink/renderer/modules/animationworklet/worklet_animation.cc b/third_party/blink/renderer/modules/animationworklet/worklet_animation.cc
index ac4a69d..823d9ac 100644
--- a/third_party/blink/renderer/modules/animationworklet/worklet_animation.cc
+++ b/third_party/blink/renderer/modules/animationworklet/worklet_animation.cc
@@ -618,16 +618,19 @@
     // offset information.
     compositor_animation_ = CompositorAnimation::CreateWorkletAnimation(
         id_, animator_name_, playback_rate_,
-        scroll_timeline_util::ToCompositorScrollTimeline(timeline_),
         std::move(options_), std::move(effect_timings_));
     compositor_animation_->SetAnimationDelegate(this);
   }
 
   // Register ourselves on the compositor timeline. This will cause our cc-side
   // animation animation to be registered.
-  if (CompositorAnimationTimeline* compositor_timeline =
-          document_->Timeline().CompositorTimeline())
+  CompositorAnimationTimeline* compositor_timeline =
+      timeline_ ? timeline_->EnsureCompositorTimeline() : nullptr;
+  if (compositor_timeline) {
     compositor_timeline->AnimationAttached(*this);
+    if (compositor_timeline->GetAnimationTimeline()->IsScrollTimeline())
+      document_->AttachCompositorTimeline(compositor_timeline);
+  }
 
   CompositorAnimations::AttachCompositedLayers(*GetEffect()->EffectTarget(),
                                                compositor_animation_.get());
@@ -694,11 +697,17 @@
   if (compositor_animation_ && compositor_animation_->IsElementAttached())
     compositor_animation_->DetachElement();
 
-  if (CompositorAnimationTimeline* compositor_timeline =
-          document_->Timeline().CompositorTimeline())
+  CompositorAnimationTimeline* compositor_timeline =
+      timeline_ ? timeline_->CompositorTimeline() : nullptr;
+  if (compositor_timeline)
     compositor_timeline->AnimationDestroyed(*this);
 
   if (compositor_animation_) {
+    if (compositor_timeline &&
+        compositor_timeline->GetAnimationTimeline()->IsScrollTimeline()) {
+      document_->DetachCompositorTimeline(compositor_timeline);
+    }
+
     compositor_animation_->SetAnimationDelegate(nullptr);
     compositor_animation_ = nullptr;
   }
@@ -753,8 +762,6 @@
 }
 
 void WorkletAnimation::UpdateCurrentTimeIfNeeded() {
-  DCHECK(play_state_ != Animation::kIdle && play_state_ != Animation::kUnset);
-
   bool is_timeline_active = IsTimelineActive();
   if (is_timeline_active != was_timeline_active_) {
     if (is_timeline_active) {
diff --git a/third_party/blink/renderer/modules/credentialmanager/credentials_container_test.cc b/third_party/blink/renderer/modules/credentialmanager/credentials_container_test.cc
index b93aa984..9b3c7c8 100644
--- a/third_party/blink/renderer/modules/credentialmanager/credentials_container_test.cc
+++ b/third_party/blink/renderer/modules/credentialmanager/credentials_container_test.cc
@@ -25,6 +25,7 @@
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/dom/dom_exception.h"
 #include "third_party/blink/renderer/core/frame/frame_test_helpers.h"
+#include "third_party/blink/renderer/core/frame/local_dom_window.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/core/testing/gc_object_liveness_observer.h"
 #include "third_party/blink/renderer/core/typed_arrays/dom_array_buffer.h"
@@ -181,7 +182,7 @@
       context.GetScriptState(), CredentialRequestOptions::Create());
   mock_credential_manager.WaitForCallToGet();
 
-  context.GetDocument()->Shutdown();
+  context.Frame()->DomWindow()->FrameDestroyed();
 
   mock_credential_manager.InvokeGetCallback();
   proxy->FlushCredentialManagerConnectionForTesting();
diff --git a/third_party/blink/renderer/modules/media_capabilities/BUILD.gn b/third_party/blink/renderer/modules/media_capabilities/BUILD.gn
index b05316c..e2f5250 100644
--- a/third_party/blink/renderer/modules/media_capabilities/BUILD.gn
+++ b/third_party/blink/renderer/modules/media_capabilities/BUILD.gn
@@ -15,7 +15,11 @@
     "worker_navigator_media_capabilities.cc",
     "worker_navigator_media_capabilities.h",
   ]
-  deps = [ "//third_party/blink/renderer/modules/mediarecorder" ]
+  deps = [
+    "//media",
+    "//media/learning/mojo:impl",
+    "//third_party/blink/renderer/modules/mediarecorder",
+  ]
 }
 
 fuzzable_proto_library("fuzzer_media_configuration_proto") {
diff --git a/third_party/blink/renderer/modules/media_capabilities/DEPS b/third_party/blink/renderer/modules/media_capabilities/DEPS
index 35cf7adf6..92da4401 100644
--- a/third_party/blink/renderer/modules/media_capabilities/DEPS
+++ b/third_party/blink/renderer/modules/media_capabilities/DEPS
@@ -1,10 +1,12 @@
 include_rules = [
-    "+media/base/mime_util.h",
-    "+media/base/supported_types.h",
-    "+media/base/video_codecs.h",
+    "+media/base",
     "+media/filters/stream_parser_factory.h",
+    "+media/learning/common",
+    "+media/learning/mojo/public/cpp/mojo_learning_task_controller.h",
+    "+media/learning/mojo/public/mojom/learning_task_controller.mojom-blink.h",
     "+media/mojo/mojom/media_types.mojom-blink.h",
     "+media/mojo/mojom/video_decode_perf_history.mojom-blink.h",
+    "+media/mojo/mojom/media_metrics_provider.mojom-blink.h",
     "-third_party/blink/renderer/modules",
     "+third_party/blink/renderer/modules/encryptedmedia",
     "+third_party/blink/renderer/modules/media_capabilities",
diff --git a/third_party/blink/renderer/modules/media_capabilities/media_capabilities.cc b/third_party/blink/renderer/modules/media_capabilities/media_capabilities.cc
index 7dce1150..717318e 100644
--- a/third_party/blink/renderer/modules/media_capabilities/media_capabilities.cc
+++ b/third_party/blink/renderer/modules/media_capabilities/media_capabilities.cc
@@ -5,12 +5,20 @@
 #include "third_party/blink/renderer/modules/media_capabilities/media_capabilities.h"
 
 #include <memory>
+#include <sstream>
 #include <utility>
 
+#include "base/feature_list.h"
+#include "base/metrics/field_trial_params.h"
 #include "base/optional.h"
+#include "media/base/media_switches.h"
 #include "media/base/mime_util.h"
 #include "media/base/supported_types.h"
 #include "media/filters/stream_parser_factory.h"
+#include "media/learning/common/media_learning_tasks.h"
+#include "media/learning/common/target_histogram.h"
+#include "media/learning/mojo/public/mojom/learning_task_controller.mojom-blink.h"
+#include "media/mojo/mojom/media_metrics_provider.mojom-blink.h"
 #include "media/mojo/mojom/media_types.mojom-blink.h"
 #include "third_party/blink/public/common/browser_interface_broker_proxy.h"
 #include "third_party/blink/public/mojom/feature_policy/feature_policy.mojom-blink.h"
@@ -59,11 +67,34 @@
 
 namespace {
 
+const double kLearningBadWindowThresholdDefault = 2;
+const double kLearningNnrThresholdDefault = 3;
+
 constexpr const char* kApplicationMimeTypePrefix = "application/";
 constexpr const char* kAudioMimeTypePrefix = "audio/";
 constexpr const char* kVideoMimeTypePrefix = "video/";
 constexpr const char* kCodecsMimeTypeParam = "codecs";
 
+// Gets parameters for kMediaLearningSmoothnessExperiment field trial. Will
+// provide sane defaults when field trial not enabled. Values of -1 indicate
+// predictions from a given task should be ignored.
+
+// static
+double GetLearningBadWindowThreshold() {
+  return base::GetFieldTrialParamByFeatureAsDouble(
+      media::kMediaLearningSmoothnessExperiment,
+      MediaCapabilities::kLearningBadWindowThresholdParamName,
+      kLearningBadWindowThresholdDefault);
+}
+
+// static
+double GetLearningNnrThreshold() {
+  return base::GetFieldTrialParamByFeatureAsDouble(
+      media::kMediaLearningSmoothnessExperiment,
+      MediaCapabilities::kLearningNnrThresholdParamName,
+      kLearningNnrThresholdDefault);
+}
+
 // Utility function that will create a MediaCapabilitiesDecodingInfo object with
 // all the values set to either true or false.
 MediaCapabilitiesDecodingInfo* CreateDecodingInfoWith(bool value) {
@@ -111,7 +142,7 @@
     // Query the client for smoothness and power efficiency of the video. It
     // will resolve the promise.
     std::move(get_perf_callback_)
-        .Run(std::move(resolver_),
+        .Run(resolver_.Get(),
              MakeGarbageCollected<MediaKeySystemAccess>(std::move(access)));
   }
 
@@ -363,16 +394,17 @@
 //                   console.
 bool IsVideoCodecValid(const String& mime_type,
                        const String& codec,
+                       media::VideoCodec* out_video_codec,
                        media::VideoCodecProfile* out_video_profile,
                        String* console_warning) {
-  media::VideoCodec video_codec = media::kUnknownVideoCodec;
   uint8_t video_level = 0;
   media::VideoColorSpace video_color_space;
   bool is_video_codec_ambiguous = true;
 
-  if (!media::ParseVideoCodecString(
-          mime_type.Ascii(), codec.Ascii(), &is_video_codec_ambiguous,
-          &video_codec, out_video_profile, &video_level, &video_color_space)) {
+  if (!media::ParseVideoCodecString(mime_type.Ascii(), codec.Ascii(),
+                                    &is_video_codec_ambiguous, out_video_codec,
+                                    out_video_profile, &video_level,
+                                    &video_color_space)) {
     *console_warning = StringView("Failed to parse video contentType: ") +
                        String{mime_type} + StringView("; codecs=") +
                        String{codec};
@@ -471,8 +503,29 @@
 
 }  // anonymous namespace
 
+const char MediaCapabilities::kLearningBadWindowThresholdParamName[] =
+    "bad_window_threshold";
+
+const char MediaCapabilities::kLearningNnrThresholdParamName[] =
+    "nnr_threshold";
+
 MediaCapabilities::MediaCapabilities() = default;
 
+void MediaCapabilities::Trace(blink::Visitor* visitor) {
+  visitor->Trace(pending_cb_map_);
+  ScriptWrappable::Trace(visitor);
+}
+
+MediaCapabilities::PendingCallbackState::PendingCallbackState(
+    ScriptPromiseResolver* resolver,
+    MediaKeySystemAccess* access)
+    : resolver(resolver), key_system_access(access) {}
+
+void MediaCapabilities::PendingCallbackState::Trace(blink::Visitor* visitor) {
+  visitor->Trace(key_system_access);
+  visitor->Trace(resolver);
+}
+
 ScriptPromise MediaCapabilities::decodingInfo(
     ScriptState* script_state,
     const MediaDecodingConfiguration* config,
@@ -504,21 +557,21 @@
   // Validation errors should return above.
   DCHECK(message.IsEmpty());
 
-  String audio_mime;
-  String audio_codec;
+  String audio_mime_str;
+  String audio_codec_str;
   if (config->hasAudio()) {
     DCHECK(config->audio()->hasContentType());
-    bool valid_content_type = ParseContentType(config->audio()->contentType(),
-                                               &audio_mime, &audio_codec);
+    bool valid_content_type = ParseContentType(
+        config->audio()->contentType(), &audio_mime_str, &audio_codec_str);
     DCHECK(valid_content_type);
   }
 
-  String video_mime;
-  String video_codec;
+  String video_mime_str;
+  String video_codec_str;
   if (config->hasVideo()) {
     DCHECK(config->video()->hasContentType());
-    bool valid_content_type = ParseContentType(config->video()->contentType(),
-                                               &video_mime, &video_codec);
+    bool valid_content_type = ParseContentType(
+        config->video()->contentType(), &video_mime_str, &video_codec_str);
     DCHECK(valid_content_type);
   }
 
@@ -526,8 +579,10 @@
   // that MSE support is not implied by EME support, so do it irrespective of
   // whether we have a KeySystem configuration.
   if (config->type() == "media-source") {
-    if ((config->hasAudio() && !CheckMseSupport(audio_mime, audio_codec)) ||
-        (config->hasVideo() && !CheckMseSupport(video_mime, video_codec))) {
+    if ((config->hasAudio() &&
+         !CheckMseSupport(audio_mime_str, audio_codec_str)) ||
+        (config->hasVideo() &&
+         !CheckMseSupport(video_mime_str, video_codec_str))) {
       // Unsupported EME queries should resolve with a null
       // MediaKeySystemAccess.
       return ScriptPromise::Cast(
@@ -536,12 +591,14 @@
     }
   }
 
+  media::VideoCodec video_codec = media::kUnknownVideoCodec;
   media::VideoCodecProfile video_profile = media::VIDEO_CODEC_PROFILE_UNKNOWN;
 
   if ((config->hasAudio() &&
-       !IsAudioCodecValid(audio_mime, audio_codec, &message)) ||
+       !IsAudioCodecValid(audio_mime_str, audio_codec_str, &message)) ||
       (config->hasVideo() &&
-       !IsVideoCodecValid(video_mime, video_codec, &video_profile, &message))) {
+       !IsVideoCodecValid(video_mime_str, video_codec_str, &video_codec,
+                          &video_profile, &message))) {
     DCHECK(!message.IsEmpty());
     if (ExecutionContext* execution_context =
             ExecutionContext::From(script_state)) {
@@ -560,14 +617,15 @@
   if (config->hasKeySystemConfiguration()) {
     // GetEmeSupport() will call the VideoDecodePerfHistory service after
     // receiving info about support for the configuration for encrypted content.
-    return GetEmeSupport(script_state, video_profile, config, exception_state);
+    return GetEmeSupport(script_state, video_codec, video_profile, config,
+                         exception_state);
   }
 
   bool audio_supported = true;
 
   if (config->hasAudio()) {
-    audio_supported =
-        IsAudioConfigurationSupported(config->audio(), audio_mime, audio_codec);
+    audio_supported = IsAudioConfigurationSupported(
+        config->audio(), audio_mime_str, audio_codec_str);
   }
 
   // No need to check video capabilities if video not included in configuration
@@ -582,8 +640,8 @@
   DCHECK(config->hasVideo());
 
   // Return early for unsupported configurations.
-  if (!IsVideoConfigurationSupported(config->video(), video_mime,
-                                     video_codec)) {
+  if (!IsVideoConfigurationSupported(config->video(), video_mime_str,
+                                     video_codec_str)) {
     return ScriptPromise::Cast(
         script_state, ToV8(CreateDecodingInfoWith(false), script_state));
   }
@@ -595,7 +653,8 @@
   // undefined. See comment above Promise() in script_promise_resolver.h
   ScriptPromise promise = resolver->Promise();
 
-  GetPerfInfo(video_profile, config->video(), resolver, nullptr /* access */);
+  GetPerfInfo(video_codec, video_profile, config->video(), resolver,
+              nullptr /* access */);
 
   return promise;
 }
@@ -669,7 +728,49 @@
   return promise;
 }
 
-bool MediaCapabilities::EnsureService(ExecutionContext* execution_context) {
+bool MediaCapabilities::EnsureLearningPredictors(
+    ExecutionContext* execution_context) {
+  DCHECK(execution_context && !execution_context->IsContextDestroyed());
+
+  // One or both of these will have been bound in an earlier pass.
+  if (bad_window_predictor_ || nnr_predictor_)
+    return true;
+
+  // MediaMetricsProvider currently only exposed via render frame.
+  // TODO(chcunningham): Expose in worker contexts pending outcome of
+  // media-learning experiments.
+  if (execution_context->IsWorkerGlobalScope())
+    return false;
+
+  scoped_refptr<base::SingleThreadTaskRunner> task_runner =
+      execution_context->GetTaskRunner(TaskType::kMediaElementEvent);
+
+  mojo::Remote<media::mojom::blink::MediaMetricsProvider> metrics_provider;
+  execution_context->GetBrowserInterfaceBroker().GetInterface(
+      metrics_provider.BindNewPipeAndPassReceiver(task_runner));
+
+  if (!metrics_provider)
+    return false;
+
+  if (GetLearningBadWindowThreshold() != -1.0) {
+    DCHECK_GE(GetLearningBadWindowThreshold(), 0);
+    metrics_provider->AcquireLearningTaskController(
+        media::learning::tasknames::kConsecutiveBadWindows,
+        bad_window_predictor_.BindNewPipeAndPassReceiver());
+  }
+
+  if (GetLearningNnrThreshold() != -1.0) {
+    DCHECK_GE(GetLearningNnrThreshold(), 0);
+    metrics_provider->AcquireLearningTaskController(
+        media::learning::tasknames::kConsecutiveNNRs,
+        nnr_predictor_.BindNewPipeAndPassReceiver());
+  }
+
+  return bad_window_predictor_ || nnr_predictor_;
+}
+
+bool MediaCapabilities::EnsurePerfHistoryService(
+    ExecutionContext* execution_context) {
   if (decode_history_service_)
     return true;
 
@@ -686,6 +787,7 @@
 
 ScriptPromise MediaCapabilities::GetEmeSupport(
     ScriptState* script_state,
+    media::VideoCodec video_codec,
     media::VideoCodecProfile video_profile,
     const MediaDecodingConfiguration* configuration,
     ExceptionState& exception_state) {
@@ -811,7 +913,8 @@
       MakeGarbageCollected<MediaCapabilitiesKeySystemAccessInitializer>(
           script_state, key_system_config->keySystem(), config_vector,
           WTF::Bind(&MediaCapabilities::GetPerfInfo, WrapPersistent(this),
-                    video_profile, WrapPersistent(configuration->video())));
+                    video_codec, video_profile,
+                    WrapPersistent(configuration->video())));
 
   // IMPORTANT: Acquire the promise before potentially synchronously resolving
   // it in the code that follows. Otherwise the promise returned to JS will be
@@ -825,7 +928,8 @@
   return promise;
 }
 
-void MediaCapabilities::GetPerfInfo(media::VideoCodecProfile video_profile,
+void MediaCapabilities::GetPerfInfo(media::VideoCodec video_codec,
+                                    media::VideoCodecProfile video_profile,
                                     const VideoConfiguration* video_config,
                                     ScriptPromiseResolver* resolver,
                                     MediaKeySystemAccess* access) {
@@ -849,11 +953,22 @@
     use_hw_secure_codecs = access->UseHardwareSecureCodecs();
   }
 
-  if (!EnsureService(execution_context)) {
-    resolver->Resolve(WrapPersistent(CreateDecodingInfoWith(false)));
+  if (!EnsurePerfHistoryService(execution_context)) {
+    resolver->Resolve(WrapPersistent(CreateDecodingInfoWith(true)));
     return;
   }
 
+  const int callback_id = CreateCallbackId();
+  pending_cb_map_.insert(
+      callback_id,
+      MakeGarbageCollected<MediaCapabilities::PendingCallbackState>(resolver,
+                                                                    access));
+
+  if (base::FeatureList::IsEnabled(media::kMediaLearningSmoothnessExperiment)) {
+    GetPerfInfo_ML(execution_context, callback_id, video_codec, video_profile,
+                   video_config->width(), video_config->framerate());
+  }
+
   media::mojom::blink::PredictionFeaturesPtr features =
       media::mojom::blink::PredictionFeatures::New(
           static_cast<media::mojom::blink::VideoCodecProfile>(video_profile),
@@ -861,28 +976,153 @@
           video_config->framerate(), key_system, use_hw_secure_codecs);
 
   decode_history_service_->GetPerfInfo(
-      std::move(features),
-      WTF::Bind(&MediaCapabilities::OnPerfInfo, WrapPersistent(this),
-                WrapPersistent(resolver), WrapPersistent(access)));
+      std::move(features), WTF::Bind(&MediaCapabilities::OnPerfHistoryInfo,
+                                     WrapPersistent(this), callback_id));
 }
 
-void MediaCapabilities::OnPerfInfo(ScriptPromiseResolver* resolver,
-                                   MediaKeySystemAccess* access,
-                                   bool is_smooth,
-                                   bool is_power_efficient) {
-  if (!resolver->GetExecutionContext() ||
-      resolver->GetExecutionContext()->IsContextDestroyed()) {
+void MediaCapabilities::GetPerfInfo_ML(ExecutionContext* execution_context,
+                                       int callback_id,
+                                       media::VideoCodec video_codec,
+                                       media::VideoCodecProfile video_profile,
+                                       int width,
+                                       double framerate) {
+  DCHECK(execution_context && !execution_context->IsContextDestroyed());
+  DCHECK(pending_cb_map_.Contains(callback_id));
+
+  if (!EnsureLearningPredictors(execution_context)) {
+    return;
+  }
+
+  // FRAGILE: Order here MUST match order in
+  // WebMediaPlayerImpl::UpdateSmoothnessHelper().
+  // TODO(chcunningham): refactor into something more robust.
+  Vector<media::learning::FeatureValue> ml_features(
+      {media::learning::FeatureValue(video_codec),
+       media::learning::FeatureValue(video_profile),
+       media::learning::FeatureValue(width),
+       media::learning::FeatureValue(framerate)});
+
+  if (bad_window_predictor_) {
+    bad_window_predictor_->PredictDistribution(
+        ml_features, WTF::Bind(&MediaCapabilities::OnBadWindowPrediction,
+                               WrapPersistent(this), callback_id));
+  }
+
+  if (nnr_predictor_) {
+    nnr_predictor_->PredictDistribution(
+        ml_features, WTF::Bind(&MediaCapabilities::OnNnrPrediction,
+                               WrapPersistent(this), callback_id));
+  }
+}
+
+void MediaCapabilities::ResolveCallbackIfReady(int callback_id) {
+  DCHECK(pending_cb_map_.Contains(callback_id));
+  PendingCallbackState* pending_cb = pending_cb_map_.at(callback_id);
+
+  if (!pending_cb->db_is_power_efficient.has_value())
+    return;
+  // Both db_* fields should be set simultaneously by the DB callback.
+  DCHECK(pending_cb->db_is_smooth.has_value());
+
+  if (nnr_predictor_ && !pending_cb->is_nnr_prediction_smooth.has_value())
+    return;
+
+  if (bad_window_predictor_ &&
+      !pending_cb->is_bad_window_prediction_smooth.has_value())
+    return;
+
+  if (!pending_cb->resolver->GetExecutionContext() ||
+      pending_cb->resolver->GetExecutionContext()->IsContextDestroyed()) {
+    // We're too late! Now that all the callbacks have provided state, its safe
+    // to erase the entry in the map.
+    pending_cb_map_.erase(callback_id);
     return;
   }
 
   Persistent<MediaCapabilitiesDecodingInfo> info(
       MediaCapabilitiesDecodingInfo::Create());
   info->setSupported(true);
-  info->setSmooth(is_smooth);
-  info->setPowerEfficient(is_power_efficient);
-  info->setKeySystemAccess(access);
+  info->setKeySystemAccess(pending_cb->key_system_access);
+  info->setPowerEfficient(*pending_cb->db_is_power_efficient);
 
-  resolver->Resolve(std::move(info));
+  // If ML experiment is running: AND available ML signals.
+  if (pending_cb->is_bad_window_prediction_smooth.has_value() ||
+      pending_cb->is_nnr_prediction_smooth.has_value()) {
+    info->setSmooth(
+        pending_cb->is_bad_window_prediction_smooth.value_or(true) &&
+        pending_cb->is_nnr_prediction_smooth.value_or(true));
+  } else {
+    // Use DB when ML experiment not running.
+    info->setSmooth(*pending_cb->db_is_smooth);
+  }
+
+  pending_cb->resolver->Resolve(std::move(info));
+  pending_cb_map_.erase(callback_id);
+}
+
+void MediaCapabilities::OnBadWindowPrediction(
+    int callback_id,
+    const base::Optional<::media::learning::TargetHistogram>& histogram) {
+  DCHECK(pending_cb_map_.Contains(callback_id));
+  PendingCallbackState* pending_cb = pending_cb_map_.at(callback_id);
+
+  std::stringstream histogram_log;
+  if (!histogram) {
+    // No data, so optimistically assume zero bad windows.
+    pending_cb->is_bad_window_prediction_smooth = true;
+    histogram_log << "none";
+  } else {
+    double histogram_average = histogram->Average();
+    pending_cb->is_bad_window_prediction_smooth =
+        histogram_average <= GetLearningBadWindowThreshold();
+    histogram_log << histogram_average;
+  }
+
+  DVLOG(2) << __func__ << " bad_win_avg:" << histogram_log.str()
+           << " smooth_threshold (<=):" << GetLearningBadWindowThreshold();
+
+  ResolveCallbackIfReady(callback_id);
+}
+
+void MediaCapabilities::OnNnrPrediction(
+    int callback_id,
+    const base::Optional<::media::learning::TargetHistogram>& histogram) {
+  DCHECK(pending_cb_map_.Contains(callback_id));
+  PendingCallbackState* pending_cb = pending_cb_map_.at(callback_id);
+
+  std::stringstream histogram_log;
+  if (!histogram) {
+    // No data, so optimistically assume zero NNRs
+    pending_cb->is_nnr_prediction_smooth = true;
+    histogram_log << "none";
+  } else {
+    double histogram_average = histogram->Average();
+    pending_cb->is_nnr_prediction_smooth =
+        histogram_average <= GetLearningNnrThreshold();
+    histogram_log << histogram_average;
+  }
+
+  DVLOG(2) << __func__ << " nnr_avg:" << histogram_log.str()
+           << " smooth_threshold (<=):" << GetLearningNnrThreshold();
+
+  ResolveCallbackIfReady(callback_id);
+}
+
+void MediaCapabilities::OnPerfHistoryInfo(int callback_id,
+                                          bool is_smooth,
+                                          bool is_power_efficient) {
+  DCHECK(pending_cb_map_.Contains(callback_id));
+  PendingCallbackState* pending_cb = pending_cb_map_.at(callback_id);
+
+  pending_cb->db_is_smooth = is_smooth;
+  pending_cb->db_is_power_efficient = is_power_efficient;
+
+  ResolveCallbackIfReady(callback_id);
+}
+
+int MediaCapabilities::CreateCallbackId() {
+  ++last_callback_id_;
+  return last_callback_id_;
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/media_capabilities/media_capabilities.h b/third_party/blink/renderer/modules/media_capabilities/media_capabilities.h
index 07b012e..cfb0b12 100644
--- a/third_party/blink/renderer/modules/media_capabilities/media_capabilities.h
+++ b/third_party/blink/renderer/modules/media_capabilities/media_capabilities.h
@@ -6,12 +6,15 @@
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIA_CAPABILITIES_MEDIA_CAPABILITIES_H_
 
 #include "media/base/video_codecs.h"  // for media::VideoCodecProfile
+#include "media/learning/mojo/public/cpp/mojo_learning_task_controller.h"
+#include "media/learning/mojo/public/mojom/learning_task_controller.mojom-blink.h"
 #include "media/mojo/mojom/video_decode_perf_history.mojom-blink.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_video_configuration.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
 #include "third_party/blink/renderer/platform/heap/visitor.h"
+#include "third_party/blink/renderer/platform/wtf/hash_map.h"
 
 namespace blink {
 
@@ -28,34 +31,108 @@
   DEFINE_WRAPPERTYPEINFO();
 
  public:
+  static const char kLearningBadWindowThresholdParamName[];
+  static const char kLearningNnrThresholdParamName[];
+
   MediaCapabilities();
 
+  void Trace(blink::Visitor* visitor) override;
+
   ScriptPromise decodingInfo(ScriptState*,
                              const MediaDecodingConfiguration*,
                              ExceptionState&);
   ScriptPromise encodingInfo(ScriptState*, const MediaEncodingConfiguration*);
 
  private:
-  // Binds to the VideoDecodePerfHistory service. Returns whether it was
+  // Stores pending callback state from and intermediate prediction values while
+  // we wait for all predictions to arrive.
+  class PendingCallbackState : public GarbageCollected<PendingCallbackState> {
+   public:
+    PendingCallbackState(ScriptPromiseResolver* resolver,
+                         MediaKeySystemAccess* access);
+    virtual void Trace(blink::Visitor* visitor);
+
+    Member<ScriptPromiseResolver> resolver;
+    Member<MediaKeySystemAccess> key_system_access;
+    base::Optional<bool> is_bad_window_prediction_smooth;
+    base::Optional<bool> is_nnr_prediction_smooth;
+    base::Optional<bool> db_is_smooth;
+    base::Optional<bool> db_is_power_efficient;
+  };
+
+  // Lazily binds remote LearningTaskControllers for ML smoothness predictions
+  // and returns whether binding succeeds. Returns true if it was already bound.
+  bool EnsureLearningPredictors(ExecutionContext*);
+
+  // Lazily binds to the VideoDecodePerfHistory service. Returns whether it was
   // successful. Returns true if it was already bound.
-  bool EnsureService(ExecutionContext*);
+  bool EnsurePerfHistoryService(ExecutionContext*);
 
   ScriptPromise GetEmeSupport(ScriptState*,
+                              media::VideoCodec,
                               media::VideoCodecProfile,
                               const MediaDecodingConfiguration*,
                               ExceptionState&);
-  void GetPerfInfo(media::VideoCodecProfile,
+  // Gets perf info from VideoDecodePerrHistory DB. Will optionally kick off
+  // parallel request to GetPerfInfo_ML() when learning experiment is enabled.
+  void GetPerfInfo(media::VideoCodec,
+                   media::VideoCodecProfile,
                    const VideoConfiguration*,
                    ScriptPromiseResolver*,
                    MediaKeySystemAccess*);
 
-  void OnPerfInfo(ScriptPromiseResolver*,
-                  MediaKeySystemAccess*,
-                  bool is_smooth,
-                  bool is_power_efficient);
+  // Gets ML perf predictions from remote LearingTaskControllers.
+  void GetPerfInfo_ML(ExecutionContext* execution_context,
+                      int callback_id,
+                      media::VideoCodec video_codec,
+                      media::VideoCodecProfile video_profile,
+                      int width,
+                      double framerate);
+
+  // Callback for perf info from the VideoDecodePerfHistory service.
+  void OnPerfHistoryInfo(int callback_id,
+                         bool is_smooth,
+                         bool is_power_efficient);
+
+  // Callback for predictions from |bad_window_predictor_|.
+  void OnBadWindowPrediction(
+      int callback_id,
+      const base::Optional<::media::learning::TargetHistogram>& histogram);
+
+  // Callback for predictions from |nnr_predictor_|.
+  void OnNnrPrediction(
+      int callback_id,
+      const base::Optional<::media::learning::TargetHistogram>& histogram);
+
+  // Resolves the callback with associated |callback_id| and removes it from the
+  // |pending_callback_map_|.
+  void ResolveCallbackIfReady(int callback_id);
+
+  // Creates a new (incremented) callback ID from |last_callback_id_| for
+  // mapping in |pending_cb_map_|.
+  int CreateCallbackId();
 
   mojo::Remote<media::mojom::blink::VideoDecodePerfHistory>
       decode_history_service_;
+
+  // Connection to a browser-process LearningTaskController for predicting the
+  // number of consecutive "bad" dropped frame windows during a playback. See
+  // media::SmoothnessHelper.
+  mojo::Remote<media::learning::mojom::blink::LearningTaskController>
+      bad_window_predictor_;
+
+  // Connects to a browser-process LearningTaskController for predicting the
+  // number of consecutive non-network re-buffers (NNRs). See
+  // media::SmoothnessHelper.
+  mojo::Remote<media::learning::mojom::blink::LearningTaskController>
+      nnr_predictor_;
+
+  // Holds the last key for callbacks in the map below. Incremented for each
+  // usage.
+  int last_callback_id_ = 0;
+
+  // Maps a callback ID to state for pending callbacks.
+  HeapHashMap<int, Member<PendingCallbackState>> pending_cb_map_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/media_controls/elements/media_control_loading_panel_element.cc b/third_party/blink/renderer/modules/media_controls/elements/media_control_loading_panel_element.cc
index 43c7ab3..9c47c4a 100644
--- a/third_party/blink/renderer/modules/media_controls/elements/media_control_loading_panel_element.cc
+++ b/third_party/blink/renderer/modules/media_controls/elements/media_control_loading_panel_element.cc
@@ -138,10 +138,10 @@
 
 void MediaControlLoadingPanelElement::SetAnimationIterationCount(
     const String& count_value) {
-  mask1_background_->style()->setProperty(GetDocument().ToExecutionContext(),
+  mask1_background_->style()->setProperty(GetExecutionContext(),
                                           kAnimationIterationCountName,
                                           count_value, "", ASSERT_NO_EXCEPTION);
-  mask2_background_->style()->setProperty(GetDocument().ToExecutionContext(),
+  mask2_background_->style()->setProperty(GetExecutionContext(),
                                           kAnimationIterationCountName,
                                           count_value, "", ASSERT_NO_EXCEPTION);
 }
diff --git a/third_party/blink/renderer/modules/mediastream/webmediaplayer_ms.cc b/third_party/blink/renderer/modules/mediastream/webmediaplayer_ms.cc
index 1c69c91..f40539f 100644
--- a/third_party/blink/renderer/modules/mediastream/webmediaplayer_ms.cc
+++ b/third_party/blink/renderer/modules/mediastream/webmediaplayer_ms.cc
@@ -821,7 +821,7 @@
 
   const scoped_refptr<media::VideoFrame> frame = compositor_->GetCurrentFrame();
 
-  viz::ContextProvider* provider = nullptr;
+  viz::RasterContextProvider* provider = nullptr;
   if (frame && frame->HasTextures()) {
     provider = Platform::Current()->SharedMainThreadContextProvider();
     // GPU Process crashed.
diff --git a/third_party/blink/renderer/modules/mediastream/webmediaplayer_ms_compositor.cc b/third_party/blink/renderer/modules/mediastream/webmediaplayer_ms_compositor.cc
index df01cf5..bd063b3 100644
--- a/third_party/blink/renderer/modules/mediastream/webmediaplayer_ms_compositor.cc
+++ b/third_party/blink/renderer/modules/mediastream/webmediaplayer_ms_compositor.cc
@@ -74,7 +74,7 @@
         frame->visible_rect().width(), frame->visible_rect().height()));
     cc::SkiaPaintCanvas paint_canvas(bitmap);
 
-    DCHECK(provider->ContextGL());
+    DCHECK(provider->RasterInterface());
     video_renderer->Copy(frame.get(), &paint_canvas, provider);
 
     SkPixmap pixmap;
diff --git a/third_party/blink/renderer/modules/native_file_system/file_system_file_handle.idl b/third_party/blink/renderer/modules/native_file_system/file_system_file_handle.idl
index 7148f72..c725cb9 100644
--- a/third_party/blink/renderer/modules/native_file_system/file_system_file_handle.idl
+++ b/third_party/blink/renderer/modules/native_file_system/file_system_file_handle.idl
@@ -10,7 +10,6 @@
     RuntimeEnabled=NativeFileSystem,
     ImplementedAs=NativeFileSystemFileHandle
 ] interface FileSystemFileHandle : FileSystemHandle {
-    [CallWith=ScriptState] Promise<FileSystemWriter> createWriter(optional FileSystemCreateWriterOptions options = {});
     [CallWith=ScriptState, RuntimeEnabled=WritableFileStream, RaisesException] Promise<FileSystemWritableFileStream> createWritable(optional FileSystemCreateWriterOptions options = {});
     [CallWith=ScriptState, RaisesException] Promise<File> getFile();
 };
diff --git a/third_party/blink/renderer/modules/native_file_system/native_file_system_file_handle.cc b/third_party/blink/renderer/modules/native_file_system/native_file_system_file_handle.cc
index 804d08d..cb6f5f1 100644
--- a/third_party/blink/renderer/modules/native_file_system/native_file_system_file_handle.cc
+++ b/third_party/blink/renderer/modules/native_file_system/native_file_system_file_handle.cc
@@ -71,8 +71,6 @@
     ScriptState* script_state,
     const FileSystemCreateWriterOptions* options,
     ExceptionState& exception_state) {
-  DCHECK(RuntimeEnabledFeatures::WritableFileStreamEnabled());
-
   if (!mojo_ptr_) {
     exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError, "");
     return ScriptPromise();
diff --git a/third_party/blink/renderer/modules/nfc/ndef_reader.cc b/third_party/blink/renderer/modules/nfc/ndef_reader.cc
index f8b1d00..2147253e 100644
--- a/third_party/blink/renderer/modules/nfc/ndef_reader.cc
+++ b/third_party/blink/renderer/modules/nfc/ndef_reader.cc
@@ -13,6 +13,7 @@
 #include "third_party/blink/renderer/core/dom/abort_signal.h"
 #include "third_party/blink/renderer/core/dom/dom_exception.h"
 #include "third_party/blink/renderer/core/events/error_event.h"
+#include "third_party/blink/renderer/core/frame/local_dom_window.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/modules/event_target_modules.h"
 #include "third_party/blink/renderer/modules/nfc/ndef_message.h"
@@ -214,7 +215,7 @@
 
 NFCProxy* NDEFReader::GetNfcProxy() const {
   DCHECK(GetExecutionContext());
-  return NFCProxy::From(*Document::From(GetExecutionContext()));
+  return NFCProxy::From(*To<LocalDOMWindow>(GetExecutionContext()));
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/nfc/ndef_writer.cc b/third_party/blink/renderer/modules/nfc/ndef_writer.cc
index eece9adb..5c3503e 100644
--- a/third_party/blink/renderer/modules/nfc/ndef_writer.cc
+++ b/third_party/blink/renderer/modules/nfc/ndef_writer.cc
@@ -12,6 +12,7 @@
 #include "third_party/blink/renderer/bindings/modules/v8/v8_ndef_write_options.h"
 #include "third_party/blink/renderer/core/dom/abort_signal.h"
 #include "third_party/blink/renderer/core/dom/dom_exception.h"
+#include "third_party/blink/renderer/core/frame/local_dom_window.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/modules/nfc/ndef_message.h"
 #include "third_party/blink/renderer/modules/nfc/nfc_type_converters.h"
@@ -157,7 +158,7 @@
   if (nfc_proxy_)
     return;
 
-  nfc_proxy_ = NFCProxy::From(*Document::From(GetExecutionContext()));
+  nfc_proxy_ = NFCProxy::From(*To<LocalDOMWindow>(GetExecutionContext()));
   DCHECK(nfc_proxy_);
 
   // Add the writer to proxy's writer list for mojo connection error
diff --git a/third_party/blink/renderer/modules/nfc/nfc_proxy.cc b/third_party/blink/renderer/modules/nfc/nfc_proxy.cc
index 9741abfb..320a28c2 100644
--- a/third_party/blink/renderer/modules/nfc/nfc_proxy.cc
+++ b/third_party/blink/renderer/modules/nfc/nfc_proxy.cc
@@ -7,6 +7,7 @@
 #include <utility>
 
 #include "third_party/blink/public/common/browser_interface_broker_proxy.h"
+#include "third_party/blink/renderer/core/frame/local_dom_window.h"
 #include "third_party/blink/renderer/core/page/page.h"
 #include "third_party/blink/renderer/modules/nfc/ndef_reader.h"
 #include "third_party/blink/renderer/modules/nfc/ndef_writer.h"
@@ -20,23 +21,23 @@
 const char NFCProxy::kSupplementName[] = "NFCProxy";
 
 // static
-NFCProxy* NFCProxy::From(Document& document) {
+NFCProxy* NFCProxy::From(LocalDOMWindow& window) {
   // https://w3c.github.io/web-nfc/#security-policies
   // WebNFC API must be only accessible from top level browsing context.
-  DCHECK(document.IsInMainFrame());
+  DCHECK(window.GetFrame()->IsMainFrame());
 
-  NFCProxy* nfc_proxy = Supplement<Document>::From<NFCProxy>(document);
+  NFCProxy* nfc_proxy = Supplement<LocalDOMWindow>::From<NFCProxy>(window);
   if (!nfc_proxy) {
-    nfc_proxy = MakeGarbageCollected<NFCProxy>(document);
-    Supplement<Document>::ProvideTo(document, nfc_proxy);
+    nfc_proxy = MakeGarbageCollected<NFCProxy>(window);
+    Supplement<LocalDOMWindow>::ProvideTo(window, nfc_proxy);
   }
   return nfc_proxy;
 }
 
 // NFCProxy
-NFCProxy::NFCProxy(Document& document)
-    : PageVisibilityObserver(document.GetPage()),
-      Supplement<Document>(document),
+NFCProxy::NFCProxy(LocalDOMWindow& window)
+    : PageVisibilityObserver(window.GetFrame()->GetPage()),
+      Supplement<LocalDOMWindow>(window),
       client_receiver_(this) {}
 
 NFCProxy::~NFCProxy() = default;
@@ -49,7 +50,7 @@
   visitor->Trace(writers_);
   visitor->Trace(readers_);
   PageVisibilityObserver::Trace(visitor);
-  Supplement<Document>::Trace(visitor);
+  Supplement<LocalDOMWindow>::Trace(visitor);
 }
 
 void NFCProxy::StartReading(NDEFReader* reader,
diff --git a/third_party/blink/renderer/modules/nfc/nfc_proxy.h b/third_party/blink/renderer/modules/nfc/nfc_proxy.h
index 3fe1997..91bbbaa 100644
--- a/third_party/blink/renderer/modules/nfc/nfc_proxy.h
+++ b/third_party/blink/renderer/modules/nfc/nfc_proxy.h
@@ -8,14 +8,14 @@
 #include "mojo/public/cpp/bindings/receiver.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "services/device/public/mojom/nfc.mojom-blink.h"
-#include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/page/page_visibility_observer.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
+#include "third_party/blink/renderer/platform/supplementable.h"
 #include "third_party/blink/renderer/platform/wtf/hash_map.h"
 
 namespace blink {
 
-class Document;
+class LocalDOMWindow;
 class NDEFScanOptions;
 class NDEFReader;
 class NDEFWriter;
@@ -24,16 +24,16 @@
 // to implementation of device::mojom::blink::NFC interface.
 class MODULES_EXPORT NFCProxy final : public GarbageCollected<NFCProxy>,
                                       public PageVisibilityObserver,
-                                      public Supplement<Document>,
+                                      public Supplement<LocalDOMWindow>,
                                       public device::mojom::blink::NFCClient {
   USING_GARBAGE_COLLECTED_MIXIN(NFCProxy);
   USING_PRE_FINALIZER(NFCProxy, Dispose);
 
  public:
   static const char kSupplementName[];
-  static NFCProxy* From(Document&);
+  static NFCProxy* From(LocalDOMWindow&);
 
-  explicit NFCProxy(Document&);
+  explicit NFCProxy(LocalDOMWindow&);
   ~NFCProxy() override;
 
   void Dispose();
diff --git a/third_party/blink/renderer/modules/nfc/nfc_proxy_test.cc b/third_party/blink/renderer/modules/nfc/nfc_proxy_test.cc
index 3c125fb..d0d81686 100644
--- a/third_party/blink/renderer/modules/nfc/nfc_proxy_test.cc
+++ b/third_party/blink/renderer/modules/nfc/nfc_proxy_test.cc
@@ -14,6 +14,7 @@
 #include "third_party/blink/public/common/browser_interface_broker_proxy.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_testing.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_ndef_scan_options.h"
+#include "third_party/blink/renderer/core/frame/local_dom_window.h"
 #include "third_party/blink/renderer/core/testing/page_test_base.h"
 #include "third_party/blink/renderer/modules/nfc/ndef_reader.h"
 #include "third_party/blink/renderer/modules/nfc/nfc_proxy.h"
@@ -162,14 +163,14 @@
 
   void SetUp() override {
     PageTestBase::SetUp(IntSize());
-    GetDocument().GetBrowserInterfaceBroker().SetBinderForTesting(
+    GetFrame().DomWindow()->GetBrowserInterfaceBroker().SetBinderForTesting(
         device::mojom::blink::NFC::Name_,
         WTF::BindRepeating(&FakeNfcService::BindRequest,
                            WTF::Unretained(nfc_service())));
   }
 
   void TearDown() override {
-    GetDocument().GetBrowserInterfaceBroker().SetBinderForTesting(
+    GetFrame().DomWindow()->GetBrowserInterfaceBroker().SetBinderForTesting(
         device::mojom::blink::NFC::Name_, {});
   }
 
@@ -180,12 +181,11 @@
 };
 
 TEST_F(NFCProxyTest, SuccessfulPath) {
-  auto& document = GetDocument();
-  auto* nfc_proxy = NFCProxy::From(document);
+  auto* window = GetFrame().DomWindow();
+  auto* nfc_proxy = NFCProxy::From(*window);
   auto* scan_options = NDEFScanOptions::Create();
   scan_options->setId(kFakeRecordId);
-  auto* reader =
-      MakeGarbageCollected<MockNDEFReader>(document.ToExecutionContext());
+  auto* reader = MakeGarbageCollected<MockNDEFReader>(window);
 
   {
     base::RunLoop loop;
@@ -234,12 +234,11 @@
 }
 
 TEST_F(NFCProxyTest, ErrorPath) {
-  auto& document = GetDocument();
-  auto* nfc_proxy = NFCProxy::From(document);
+  auto* window = GetFrame().DomWindow();
+  auto* nfc_proxy = NFCProxy::From(*window);
   auto* scan_options = NDEFScanOptions::Create();
   scan_options->setId(kFakeRecordId);
-  auto* reader =
-      MakeGarbageCollected<MockNDEFReader>(document.ToExecutionContext());
+  auto* reader = MakeGarbageCollected<MockNDEFReader>(window);
 
   // Make the fake NFC service return an error for the incoming watch request.
   nfc_service()->set_watch_error(device::mojom::blink::NDEFError::New(
diff --git a/third_party/blink/renderer/modules/presentation/presentation_connection.cc b/third_party/blink/renderer/modules/presentation/presentation_connection.cc
index b39444a1..4051bf5 100644
--- a/third_party/blink/renderer/modules/presentation/presentation_connection.cc
+++ b/third_party/blink/renderer/modules/presentation/presentation_connection.cc
@@ -385,9 +385,7 @@
 }
 
 ExecutionContext* PresentationConnection::GetExecutionContext() const {
-  if (!GetFrame())
-    return nullptr;
-  return GetFrame()->GetDocument()->ToExecutionContext();
+  return ExecutionContextLifecycleObserver::GetExecutionContext();
 }
 
 void PresentationConnection::AddedEventListener(
diff --git a/third_party/blink/renderer/modules/remoteplayback/remote_playback.cc b/third_party/blink/renderer/modules/remoteplayback/remote_playback.cc
index db7df4a..ce40804 100644
--- a/third_party/blink/renderer/modules/remoteplayback/remote_playback.cc
+++ b/third_party/blink/renderer/modules/remoteplayback/remote_playback.cc
@@ -103,7 +103,7 @@
 }
 
 ExecutionContext* RemotePlayback::GetExecutionContext() const {
-  return media_element_->GetDocument().ToExecutionContext();
+  return ExecutionContextLifecycleObserver::GetExecutionContext();
 }
 
 ScriptPromise RemotePlayback::watchAvailability(
@@ -284,6 +284,9 @@
     return kWatchAvailabilityNotSupported;
   }
 
+  if (!GetExecutionContext())
+    return kWatchAvailabilityNotSupported;
+
   int id;
   do {
     id = GetExecutionContext()->CircularSequentialID();
diff --git a/third_party/blink/renderer/modules/wake_lock/wake_lock_sentinel_test.cc b/third_party/blink/renderer/modules/wake_lock/wake_lock_sentinel_test.cc
index 0217adf..7c2b89a 100644
--- a/third_party/blink/renderer/modules/wake_lock/wake_lock_sentinel_test.cc
+++ b/third_party/blink/renderer/modules/wake_lock/wake_lock_sentinel_test.cc
@@ -11,6 +11,8 @@
 #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/dom/events/native_event_listener.h"
+#include "third_party/blink/renderer/core/frame/local_dom_window.h"
+#include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/modules/event_target_modules_names.h"
 #include "third_party/blink/renderer/modules/wake_lock/wake_lock.h"
 #include "third_party/blink/renderer/modules/wake_lock/wake_lock_manager.h"
@@ -112,7 +114,7 @@
   sentinel->addEventListener(event_type_names::kRelease, event_listener);
   EXPECT_TRUE(sentinel->HasPendingActivity());
 
-  context.GetDocument()->Shutdown();
+  context.Frame()->DomWindow()->FrameDestroyed();
 
   // If the method returns false the object can be GC'ed.
   EXPECT_FALSE(sentinel->HasPendingActivity());
diff --git a/third_party/blink/renderer/modules/wake_lock/wake_lock_test.cc b/third_party/blink/renderer/modules/wake_lock/wake_lock_test.cc
index ec31037..3e71152 100644
--- a/third_party/blink/renderer/modules/wake_lock/wake_lock_test.cc
+++ b/third_party/blink/renderer/modules/wake_lock/wake_lock_test.cc
@@ -8,6 +8,8 @@
 #include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
 #include "third_party/blink/renderer/core/dom/dom_exception.h"
+#include "third_party/blink/renderer/core/frame/local_dom_window.h"
+#include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/core/page/page.h"
 #include "third_party/blink/renderer/modules/wake_lock/wake_lock_test_utils.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
@@ -111,7 +113,7 @@
   // been cleared. We cannot check that the promises have been rejected because
   // ScriptPromiseResolver::Reject() will bail out if we no longer have a valid
   // execution context.
-  context.GetDocument()->Shutdown();
+  context.Frame()->DomWindow()->FrameDestroyed();
   screen_lock.WaitForCancelation();
   system_lock.WaitForCancelation();
 
diff --git a/third_party/blink/renderer/modules/webgl/webgl2_rendering_context_base.cc b/third_party/blink/renderer/modules/webgl/webgl2_rendering_context_base.cc
index b6f3369a..a8cae56 100644
--- a/third_party/blink/renderer/modules/webgl/webgl2_rendering_context_base.cc
+++ b/third_party/blink/renderer/modules/webgl/webgl2_rendering_context_base.cc
@@ -1006,70 +1006,6 @@
     ContextGL()->PixelStorei(GL_UNPACK_SKIP_IMAGES, unpack_skip_images_);
 }
 
-/* Texture objects */
-bool WebGL2RenderingContextBase::ValidateTexStorage(
-    const char* function_name,
-    GLenum target,
-    GLsizei levels,
-    GLenum internalformat,
-    GLsizei width,
-    GLsizei height,
-    GLsizei depth,
-    TexStorageType function_type) {
-  if (function_type == kTexStorageType2D) {
-    if (target != GL_TEXTURE_2D && target != GL_TEXTURE_CUBE_MAP) {
-      SynthesizeGLError(GL_INVALID_ENUM, function_name, "invalid 2D target");
-      return false;
-    }
-  } else {
-    if (target != GL_TEXTURE_3D && target != GL_TEXTURE_2D_ARRAY) {
-      SynthesizeGLError(GL_INVALID_ENUM, function_name, "invalid 3D target");
-      return false;
-    }
-  }
-
-  if (function_type == kTexStorageType3D && target != GL_TEXTURE_2D_ARRAY &&
-      compressed_texture_formats_etc2eac_.find(internalformat) !=
-          compressed_texture_formats_etc2eac_.end()) {
-    SynthesizeGLError(
-        GL_INVALID_OPERATION, function_name,
-        "target for ETC2/EAC internal formats must be TEXTURE_2D_ARRAY");
-    return false;
-  }
-
-  if (supported_internal_formats_storage_.find(internalformat) ==
-          supported_internal_formats_storage_.end() &&
-      (function_type == kTexStorageType2D &&
-       !compressed_texture_formats_.Contains(internalformat))) {
-    SynthesizeGLError(GL_INVALID_ENUM, function_name, "invalid internalformat");
-    return false;
-  }
-
-  if (width <= 0 || height <= 0 || depth <= 0) {
-    SynthesizeGLError(GL_INVALID_VALUE, function_name, "invalid dimensions");
-    return false;
-  }
-
-  if (levels <= 0) {
-    SynthesizeGLError(GL_INVALID_VALUE, function_name, "invalid levels");
-    return false;
-  }
-
-  if (target == GL_TEXTURE_3D) {
-    if (levels > log2(std::max(std::max(width, height), depth)) + 1) {
-      SynthesizeGLError(GL_INVALID_OPERATION, function_name, "to many levels");
-      return false;
-    }
-  } else {
-    if (levels > log2(std::max(width, height)) + 1) {
-      SynthesizeGLError(GL_INVALID_OPERATION, function_name, "to many levels");
-      return false;
-    }
-  }
-
-  return true;
-}
-
 void WebGL2RenderingContextBase::texImage2D(GLenum target,
                                             GLint level,
                                             GLint internalformat,
@@ -1678,9 +1614,7 @@
                                               GLenum internalformat,
                                               GLsizei width,
                                               GLsizei height) {
-  if (isContextLost() ||
-      !ValidateTexStorage("texStorage2D", target, levels, internalformat, width,
-                          height, 1, kTexStorageType2D))
+  if (isContextLost())
     return;
 
   ContextGL()->TexStorage2DEXT(target, levels, internalformat, width, height);
@@ -1692,9 +1626,7 @@
                                               GLsizei width,
                                               GLsizei height,
                                               GLsizei depth) {
-  if (isContextLost() ||
-      !ValidateTexStorage("texStorage3D", target, levels, internalformat, width,
-                          height, depth, kTexStorageType3D))
+  if (isContextLost())
     return;
 
   ContextGL()->TexStorage3D(target, levels, internalformat, width, height,
diff --git a/third_party/blink/renderer/modules/webgl/webgl2_rendering_context_base.h b/third_party/blink/renderer/modules/webgl/webgl2_rendering_context_base.h
index 56461979..1906f0e6 100644
--- a/third_party/blink/renderer/modules/webgl/webgl2_rendering_context_base.h
+++ b/third_party/blink/renderer/modules/webgl/webgl2_rendering_context_base.h
@@ -1004,14 +1004,6 @@
     kTexStorageType2D,
     kTexStorageType3D,
   };
-  bool ValidateTexStorage(const char*,
-                          GLenum,
-                          GLsizei,
-                          GLenum,
-                          GLsizei,
-                          GLsizei,
-                          GLsizei,
-                          TexStorageType);
 
   bool ValidateUniformBlockIndex(const char*, WebGLProgram*, GLuint);
 
@@ -1121,7 +1113,6 @@
   Member<WebGLTransformFeedback> default_transform_feedback_;
 
   GLenumHashSet supported_internal_formats_storage_;
-  GLenumHashSet compressed_texture_formats_etc2eac_;
 
   Member<WebGLBuffer> bound_copy_read_buffer_;
   Member<WebGLBuffer> bound_copy_write_buffer_;
diff --git a/third_party/blink/renderer/modules/xr/xr_hit_test_source.cc b/third_party/blink/renderer/modules/xr/xr_hit_test_source.cc
index 52d87a46..9e539e6 100644
--- a/third_party/blink/renderer/modules/xr/xr_hit_test_source.cc
+++ b/third_party/blink/renderer/modules/xr/xr_hit_test_source.cc
@@ -53,6 +53,7 @@
     last_frame_results_.push_back(*result);
   }
 }
+
 void XRHitTestSource::Trace(Visitor* visitor) {
   visitor->Trace(xr_session_);
   ScriptWrappable::Trace(visitor);
diff --git a/third_party/blink/renderer/modules/xr/xr_transient_input_hit_test_source.cc b/third_party/blink/renderer/modules/xr/xr_transient_input_hit_test_source.cc
index a1cf9da..a457b32 100644
--- a/third_party/blink/renderer/modules/xr/xr_transient_input_hit_test_source.cc
+++ b/third_party/blink/renderer/modules/xr/xr_transient_input_hit_test_source.cc
@@ -60,9 +60,12 @@
     if (!input_source)
       continue;
 
-    current_frame_results_.push_back(
-        MakeGarbageCollected<XRTransientInputHitTestResult>(
-            input_source, source_id_and_results.value));
+    // If the input source is not visible, ignore it.
+    if (input_source->IsVisible()) {
+      current_frame_results_.push_back(
+          MakeGarbageCollected<XRTransientInputHitTestResult>(
+              input_source, source_id_and_results.value));
+    }
   }
 }
 
diff --git a/third_party/blink/renderer/platform/animation/compositor_animation.cc b/third_party/blink/renderer/platform/animation/compositor_animation.cc
index 7a16e73d..f02f038 100644
--- a/third_party/blink/renderer/platform/animation/compositor_animation.cc
+++ b/third_party/blink/renderer/platform/animation/compositor_animation.cc
@@ -22,12 +22,10 @@
     cc::WorkletAnimationId worklet_animation_id,
     const String& name,
     double playback_rate,
-    scoped_refptr<CompositorScrollTimeline> scroll_timeline,
     std::unique_ptr<cc::AnimationOptions> options,
     std::unique_ptr<cc::AnimationEffectTimings> effect_timings) {
   return std::make_unique<CompositorAnimation>(cc::WorkletAnimation::Create(
-      worklet_animation_id, name.Utf8(), playback_rate,
-      std::move(scroll_timeline), std::move(options),
+      worklet_animation_id, name.Utf8(), playback_rate, std::move(options),
       std::move(effect_timings)));
 }
 
@@ -86,9 +84,8 @@
     base::Optional<cc::ElementId> element_id,
     base::Optional<double> start_scroll_offset,
     base::Optional<double> end_scroll_offset) {
-  cc::ToWorkletAnimation(animation_.get())
-      ->UpdateScrollTimeline(element_id, start_scroll_offset,
-                             end_scroll_offset);
+  animation_->UpdateScrollTimeline(element_id, start_scroll_offset,
+                                   end_scroll_offset);
 }
 
 void CompositorAnimation::UpdatePlaybackRate(double playback_rate) {
diff --git a/third_party/blink/renderer/platform/animation/compositor_animation.h b/third_party/blink/renderer/platform/animation/compositor_animation.h
index 771619c..348a448 100644
--- a/third_party/blink/renderer/platform/animation/compositor_animation.h
+++ b/third_party/blink/renderer/platform/animation/compositor_animation.h
@@ -12,7 +12,6 @@
 #include "base/optional.h"
 #include "cc/animation/animation.h"
 #include "cc/animation/animation_delegate.h"
-#include "cc/animation/scroll_timeline.h"
 #include "cc/animation/worklet_animation.h"
 #include "third_party/blink/renderer/platform/graphics/compositor_element_id.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
@@ -24,8 +23,6 @@
 
 namespace blink {
 
-using CompositorScrollTimeline = cc::ScrollTimeline;
-
 class CompositorAnimationDelegate;
 class CompositorKeyframeModel;
 
@@ -37,7 +34,6 @@
       cc::WorkletAnimationId,
       const String& name,
       double playback_rate,
-      scoped_refptr<CompositorScrollTimeline>,
       std::unique_ptr<cc::AnimationOptions>,
       std::unique_ptr<cc::AnimationEffectTimings> effect_timings);
 
diff --git a/third_party/blink/renderer/platform/animation/compositor_animation_timeline.cc b/third_party/blink/renderer/platform/animation/compositor_animation_timeline.cc
index eca78aec..3da0644a 100644
--- a/third_party/blink/renderer/platform/animation/compositor_animation_timeline.cc
+++ b/third_party/blink/renderer/platform/animation/compositor_animation_timeline.cc
@@ -15,6 +15,10 @@
     : animation_timeline_(cc::AnimationTimeline::Create(
           cc::AnimationIdProvider::NextTimelineId())) {}
 
+CompositorAnimationTimeline::CompositorAnimationTimeline(
+    scoped_refptr<cc::AnimationTimeline> timeline)
+    : animation_timeline_(timeline) {}
+
 CompositorAnimationTimeline::~CompositorAnimationTimeline() {
   // Detach timeline from host, otherwise it stays there (leaks) until
   // compositor shutdown.
diff --git a/third_party/blink/renderer/platform/animation/compositor_animation_timeline.h b/third_party/blink/renderer/platform/animation/compositor_animation_timeline.h
index b4e6b842..8307cd8 100644
--- a/third_party/blink/renderer/platform/animation/compositor_animation_timeline.h
+++ b/third_party/blink/renderer/platform/animation/compositor_animation_timeline.h
@@ -24,6 +24,7 @@
 
  public:
   CompositorAnimationTimeline();
+  explicit CompositorAnimationTimeline(scoped_refptr<cc::AnimationTimeline>);
   ~CompositorAnimationTimeline();
 
   cc::AnimationTimeline* GetAnimationTimeline() const;
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index 7e5fb5d..7994cba 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -318,6 +318,8 @@
       // NativeFileSystem is in Origin Trial, which doesn't support having
       // non-origin-trial-enabled features depend on it. https://crbug.com/1000486
       // depends_on: ["NativeFileSystem"]
+      origin_trial_feature_name: "NativeFileSystem2",
+      origin_trial_os: ["win", "macosx", "linux", "chromeos"],
     },
     {
       name: "CompositeAfterPaint",
@@ -1109,7 +1111,7 @@
       // on the command line (or via chrome://flags).
       name: "NativeFileSystem",
       status: {"Android": "test", "default": "experimental"},
-      origin_trial_feature_name: "NativeFileSystem",
+      origin_trial_feature_name: "NativeFileSystem2",
       origin_trial_os: ["win", "macosx", "linux", "chromeos"],
     },
     {
@@ -1928,6 +1930,8 @@
       // NativeFileSystem is in Origin Trial, which doesn't support having
       // non-origin-trial-enabled features depend on it. https://crbug.com/1000486
       // depends_on: ["NativeFileSystem"]
+      origin_trial_feature_name: "NativeFileSystem2",
+      origin_trial_os: ["win", "macosx", "linux", "chromeos"],
     },
     {
       name: "XSLT",
diff --git a/third_party/blink/renderer/platform/wtf/BUILD.gn b/third_party/blink/renderer/platform/wtf/BUILD.gn
index 058b9c0..de3e2eb 100644
--- a/third_party/blink/renderer/platform/wtf/BUILD.gn
+++ b/third_party/blink/renderer/platform/wtf/BUILD.gn
@@ -42,6 +42,7 @@
     "allocator/partitions.h",
     "assertions.cc",
     "assertions.h",
+    "bit_field.h",
     "bloom_filter.h",
     "casting.h",
     "conditional_destructor.h",
@@ -255,6 +256,7 @@
     "allocator/partitions_test.cc",
     "ascii_ctype_test.cc",
     "assertions_test.cc",
+    "bit_field_test.cc",
     "cross_thread_functional_test.cc",
     "decimal_test.cc",
     "deque_test.cc",
diff --git a/third_party/blink/renderer/platform/wtf/bit_field.h b/third_party/blink/renderer/platform/wtf/bit_field.h
new file mode 100644
index 0000000..e65e183
--- /dev/null
+++ b/third_party/blink/renderer/platform/wtf/bit_field.h
@@ -0,0 +1,153 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_BIT_FIELD_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_BIT_FIELD_H_
+
+#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
+
+namespace WTF {
+
+enum class BitFieldValueConstness {
+  kNonConst,
+  kConst,
+};
+
+namespace internal {
+
+template <class BitFieldType>
+class BitFieldBase;
+
+// Helper class for defining values in a bit field. This helper provides
+// utilities to read, write and update the value in the bit field.
+template <class ValueType,
+          size_t offset,
+          size_t size,
+          class BitFieldType,
+          BitFieldValueConstness is_const = BitFieldValueConstness::kNonConst>
+class BitFieldValue final {
+  static_assert(std::is_fundamental<ValueType>::value,
+                "Fields in a bit field must be of a primitive type.");
+  static_assert(std::is_fundamental<BitFieldType>::value,
+                "Bit fields must be of a primitive type.");
+  static_assert(std::is_unsigned<BitFieldType>::value,
+                "Bit field must be of an unsigned type");
+  static_assert(sizeof(ValueType) <= sizeof(BitFieldType),
+                "Value in bit field cannot be bigger than the bit field");
+  static_assert(
+      offset < 8 * sizeof(BitFieldType),
+      "Field offset in bit field must be smaller than the bit field size");
+  static_assert(
+      size < 8 * sizeof(BitFieldType),
+      "Field size in bit field must be smaller than the bit field size");
+  static_assert(offset + size <= 8 * sizeof(BitFieldType),
+                "Field in bit field cannot overflow the bit field");
+  static_assert(size > 0, "Bit field fields cannot have 0 size.");
+
+ public:
+  using Type = ValueType;
+
+  template <class OtherValueType,
+            int other_size,
+            BitFieldValueConstness other_is_const =
+                BitFieldValueConstness::kNonConst>
+  using DefineNextValue = BitFieldValue<OtherValueType,
+                                        offset + size,
+                                        other_size,
+                                        BitFieldType,
+                                        other_is_const>;
+
+  // Create a bit field with the given value.
+  static constexpr BitFieldType encode(ValueType value) {
+    DCHECK(is_valid(value));
+    return static_cast<BitFieldType>(value) << offset;
+  }
+
+  // Update a bit field with the given value.
+  static constexpr BitFieldType update(BitFieldType previous, ValueType value) {
+    return (previous & ~kMask) | encode(value);
+  }
+
+  // Read the value from the bit field.
+  static constexpr ValueType decode(BitFieldType value) {
+    return static_cast<ValueType>((value & kMask) >> offset);
+  }
+
+ private:
+  static constexpr BitFieldValueConstness kIsConst = is_const;
+
+  static constexpr BitFieldType kValidationMask =
+      (BitFieldType{1} << size) - BitFieldType{1};
+  static constexpr BitFieldType kMask = (kValidationMask) << offset;
+  static_assert(kMask != 0, "Mask in which all bits are 0 is not allowed.");
+  static_assert(~kMask != 0, "Mask in which all bits are 1 is not allowed.");
+
+  // Confirm that the provided value fits into the bit field.
+  static constexpr bool is_valid(ValueType value) {
+    return (static_cast<BitFieldType>(value) & ~kValidationMask) == 0;
+  }
+
+  friend class BitFieldBase<BitFieldType>;
+};
+
+}  // namespace internal
+
+// BitField intended to be used by a single thread.
+template <class BitFieldType>
+class WTF_EXPORT SingleThreadedBitField {
+  static_assert(std::is_fundamental<BitFieldType>::value,
+                "Bit fields must be of a primitive type.");
+  static_assert(std::is_unsigned<BitFieldType>::value,
+                "Bit field must be of an unsigned type");
+
+ public:
+  template <class Type,
+            int size,
+            BitFieldValueConstness is_const = BitFieldValueConstness::kNonConst>
+  using DefineFirstValue =
+      internal::BitFieldValue<Type, 0, size, BitFieldType, is_const>;
+
+  explicit SingleThreadedBitField() : SingleThreadedBitField(0) {}
+  explicit SingleThreadedBitField(BitFieldType bits) : bits_(bits) {}
+
+  template <typename Value>
+  typename Value::Type get() const {
+    return Value::decode(bits_);
+  }
+
+  template <typename Value>
+  void set(typename Value::Type value) {
+    bits_ = Value::update(bits_, value);
+  }
+
+ protected:
+  BitFieldType bits_;
+};
+
+// BitField that can be written by a single thread but read by multiple threads.
+template <class BitFieldType>
+class WTF_EXPORT ConcurrentlyReadBitField
+    : public SingleThreadedBitField<BitFieldType> {
+  using Base = SingleThreadedBitField<BitFieldType>;
+  using Base::bits_;
+
+ public:
+  explicit ConcurrentlyReadBitField() : Base(0) {}
+  explicit ConcurrentlyReadBitField(BitFieldType bits) : Base(bits) {}
+
+  template <typename Value>
+  typename Value::Type get_concurrently() const {
+    return Value::decode(AsAtomicPtr(&bits_)->load(std::memory_order_relaxed));
+  }
+
+  template <typename Value>
+  void set(typename Value::Type value) {
+    AsAtomicPtr(&bits_)->store(Value::update(bits_, value),
+                               std::memory_order_relaxed);
+  }
+};
+
+}  // namespace WTF
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_BIT_FIELD_H_
diff --git a/third_party/blink/renderer/platform/wtf/bit_field_test.cc b/third_party/blink/renderer/platform/wtf/bit_field_test.cc
new file mode 100644
index 0000000..795db25
--- /dev/null
+++ b/third_party/blink/renderer/platform/wtf/bit_field_test.cc
@@ -0,0 +1,105 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/wtf/bit_field.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace WTF {
+
+class BitFieldTest : public ::testing::Test {};
+
+TEST_F(BitFieldTest, BitFieldDefaultCtor) {
+  using BitField = SingleThreadedBitField<uint32_t>;
+  using Value1 = BitField::DefineFirstValue<uint32_t, 31>;
+  using Value2 = Value1::DefineNextValue<uint32_t, 1>;
+
+  SingleThreadedBitField<uint32_t> bit_field;
+  EXPECT_EQ(0u, bit_field.get<Value1>());
+  EXPECT_EQ(0u, bit_field.get<Value2>());
+}
+
+TEST_F(BitFieldTest, BitFieldCtor) {
+  using BitField = SingleThreadedBitField<uint32_t>;
+  using Value1 = BitField::DefineFirstValue<uint32_t, 31>;
+  using Value2 = Value1::DefineNextValue<uint32_t, 1>;
+
+  SingleThreadedBitField<uint32_t> bit_field(0xdeadbeef);
+  EXPECT_EQ(0x5eadbeefu, bit_field.get<Value1>());
+  EXPECT_EQ(1u, bit_field.get<Value2>());
+}
+
+TEST_F(BitFieldTest, SplitBitField) {
+  using BitField = SingleThreadedBitField<uint32_t>;
+  using Value1 = BitField::DefineFirstValue<uint16_t, 16>;
+  using Value2 = Value1::DefineNextValue<uint16_t, 8>;
+  using Value3 = Value2::DefineNextValue<uint16_t, 8>;
+
+  SingleThreadedBitField<uint32_t> bit_field(0xdeadbeef);
+  EXPECT_EQ(0xde, bit_field.get<Value3>());
+  EXPECT_EQ(0xad, bit_field.get<Value2>());
+  EXPECT_EQ(0xbeef, bit_field.get<Value1>());
+}
+
+TEST_F(BitFieldTest, BitFieldBits) {
+  using BitField = SingleThreadedBitField<uint8_t>;
+  using Value1 = BitField::DefineFirstValue<bool, 1>;
+  using Value2 = Value1::DefineNextValue<bool, 1>;
+  using Value3 = Value2::DefineNextValue<bool, 1>;
+  using Value4 = Value3::DefineNextValue<bool, 1>;
+  using Value5 = Value4::DefineNextValue<bool, 1>;
+  using Value6 = Value5::DefineNextValue<bool, 1>;
+  using Value7 = Value6::DefineNextValue<bool, 1>;
+  using Value8 = Value7::DefineNextValue<bool, 1>;
+
+  SingleThreadedBitField<uint32_t> bit_field(0b10101010);
+  EXPECT_FALSE(bit_field.get<Value1>());
+  EXPECT_TRUE(bit_field.get<Value2>());
+  EXPECT_FALSE(bit_field.get<Value3>());
+  EXPECT_TRUE(bit_field.get<Value4>());
+  EXPECT_FALSE(bit_field.get<Value5>());
+  EXPECT_TRUE(bit_field.get<Value6>());
+  EXPECT_FALSE(bit_field.get<Value7>());
+  EXPECT_TRUE(bit_field.get<Value8>());
+}
+
+TEST_F(BitFieldTest, BitFieldSetValue) {
+  using BitField = SingleThreadedBitField<uint32_t>;
+  using Value1 = BitField::DefineFirstValue<uint16_t, 16>;
+  using Value2 = Value1::DefineNextValue<uint16_t, 16>;
+
+  SingleThreadedBitField<uint32_t> bit_field;
+  CHECK_EQ(0u, bit_field.get<Value1>());
+  CHECK_EQ(0u, bit_field.get<Value2>());
+  bit_field.set<Value1>(1337);
+  EXPECT_EQ(1337u, bit_field.get<Value1>());
+  EXPECT_EQ(0u, bit_field.get<Value2>());
+}
+
+TEST_F(BitFieldTest, ConcurrentBitFieldGettersReturnTheSame) {
+  using BitField = SingleThreadedBitField<uint32_t>;
+  using Value1 = BitField::DefineFirstValue<uint16_t, 16>;
+  using Value2 = Value1::DefineNextValue<uint16_t, 16>;
+
+  ConcurrentlyReadBitField<uint32_t> bit_field(0xdeadbeef);
+  CHECK_EQ(0xbeef, bit_field.get<Value1>());
+  CHECK_EQ(0xdead, bit_field.get<Value2>());
+  EXPECT_EQ(bit_field.get_concurrently<Value1>(), bit_field.get<Value1>());
+  EXPECT_EQ(bit_field.get_concurrently<Value2>(), bit_field.get<Value2>());
+}
+
+TEST_F(BitFieldTest, ConcurrentBitFieldSetValue) {
+  using BitField = SingleThreadedBitField<uint32_t>;
+  using Value1 = BitField::DefineFirstValue<uint16_t, 16>;
+  using Value2 = Value1::DefineNextValue<uint16_t, 16>;
+
+  ConcurrentlyReadBitField<uint32_t> bit_field;
+  CHECK_EQ(0u, bit_field.get_concurrently<Value1>());
+  CHECK_EQ(0u, bit_field.get_concurrently<Value2>());
+  bit_field.set<Value1>(1337);
+  EXPECT_EQ(1337u, bit_field.get_concurrently<Value1>());
+  EXPECT_EQ(0u, bit_field.get_concurrently<Value2>());
+}
+
+}  // namespace WTF
diff --git a/third_party/blink/web_tests/FlagExpectations/use-gl=any b/third_party/blink/web_tests/FlagExpectations/use-gl=any
index e1e826d..496cee8e 100644
--- a/third_party/blink/web_tests/FlagExpectations/use-gl=any
+++ b/third_party/blink/web_tests/FlagExpectations/use-gl=any
@@ -36,3 +36,6 @@
 
 # Viewport image is the wrong size.
 crbug.com/1009718 images/huge-image-viewport-scale.html [ Skip ]
+
+# Input or Playback flaky
+crbug.com/1060773 [ Win ] media/video-controls-visibility-multimodal-mouse-after-touch.html [ Skip ]
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index e960afb1..0e28d84c 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -6567,6 +6567,9 @@
 
 # Crashes with DCHECK enabled, but not on normal Release builds.
 crbug.com/944449 external/wpt/scroll-animations/scroll-animation.html [ Pass Crash ]
+crbug.com/944449 virtual/threaded/external/wpt/scroll-animations/scroll-animation.html [ Pass Crash ]
+crbug.com/944449 external/wpt/scroll-animations/animation-with-display-none.html [ Pass Crash Timeout ]
+crbug.com/944449 virtual/threaded/external/wpt/scroll-animations/animation-with-display-none.html [ Pass Crash Timeout ]
 
 # Sheriff 2020-02-10
 crbug.com/1050306 [ Fuchsia ] virtual/gpu/fast/canvas/canvas-filter-frameless-document.html [ Crash Pass Timeout ]
diff --git a/third_party/blink/web_tests/VirtualTestSuites b/third_party/blink/web_tests/VirtualTestSuites
index 3f1380a..215a4d30 100644
--- a/third_party/blink/web_tests/VirtualTestSuites
+++ b/third_party/blink/web_tests/VirtualTestSuites
@@ -23,6 +23,7 @@
               "external/wpt/css/css-scroll-snap",
               "external/wpt/feature-policy/experimental-features",
               "external/wpt/offscreen-canvas/convert-to-blob",
+              "external/wpt/scroll-animations",
               "external/wpt/requestidlecallback",
               "external/wpt/web-animations/timing-model/animations",
               "fast/events/pointerevents/pinch",
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_7.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_7.json
index 71d9503..934917a 100644
--- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_7.json
+++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_7.json
@@ -6141,9 +6141,15 @@
      {}
     ]
    ],
-   "html/user-activation/activation-hierarchy-parent-manual.sub.tentative.html": [
+   "html/user-activation/activation-hierarchy-crossorigin-parent-manual.sub.html": [
     [
-     "html/user-activation/activation-hierarchy-parent-manual.sub.tentative.html",
+     "html/user-activation/activation-hierarchy-crossorigin-parent-manual.sub.html",
+     {}
+    ]
+   ],
+   "html/user-activation/activation-hierarchy-sameorigin-parent-manual.html": [
+    [
+     "html/user-activation/activation-hierarchy-sameorigin-parent-manual.html",
      {}
     ]
    ],
@@ -164977,6 +164983,9 @@
    "html/cross-origin-opener-policy/iframe-popup-same-origin-to-same-origin.https.html.headers": [
     []
    ],
+   "html/cross-origin-opener-policy/iframe-popup-same-origin-to-unsafe-none.https-expected.txt": [
+    []
+   ],
    "html/cross-origin-opener-policy/iframe-popup-same-origin-to-unsafe-none.https.html.headers": [
     []
    ],
@@ -171082,10 +171091,16 @@
    "html/tools/update_html5lib_tests.py": [
     []
    ],
-   "html/user-activation/resources/activation-hierarchy-child.sub.html": [
+   "html/user-activation/resources/activation-hierarchy-crossorigin-child.sub.html": [
     []
    ],
-   "html/user-activation/resources/activation-hierarchy-grandchild.html": [
+   "html/user-activation/resources/activation-hierarchy-crossorigin-grandchild.html": [
+    []
+   ],
+   "html/user-activation/resources/activation-hierarchy-sameorigin-child.html": [
+    []
+   ],
+   "html/user-activation/resources/activation-hierarchy-sameorigin-grandchild.html": [
     []
    ],
    "html/user-activation/resources/child-four.html": [
@@ -190060,9 +190075,6 @@
    "web-animations/animation-model/keyframe-effects/effect-value-iteration-composite-operation-expected.txt": [
     []
    ],
-   "web-animations/idlharness.window-expected.txt": [
-    []
-   ],
    "web-animations/interfaces/Animatable/animate-expected.txt": [
     []
    ],
@@ -190081,9 +190093,6 @@
    "web-animations/interfaces/Document/getAnimations-expected.txt": [
     []
    ],
-   "web-animations/interfaces/DocumentOrShadowRoot/getAnimations-expected.txt": [
-    []
-   ],
    "web-animations/interfaces/KeyframeEffect/constructor-expected.txt": [
     []
    ],
@@ -190831,6 +190840,9 @@
    "webgpu/suites/cts/canvas/context_creation.spec.js": [
     []
    ],
+   "webgpu/suites/cts/capability_info.js": [
+    []
+   ],
    "webgpu/suites/cts/command_buffer/basic.spec.js": [
     []
    ],
@@ -190858,9 +190870,6 @@
    "webgpu/suites/cts/fences.spec.js": [
     []
    ],
-   "webgpu/suites/cts/format_info.js": [
-    []
-   ],
    "webgpu/suites/cts/gpu_test.js": [
     []
    ],
@@ -501483,15 +501492,19 @@
    "support"
   ],
   "html/cross-origin-opener-policy/iframe-popup-same-origin-to-same-origin.https.html": [
-   "635f7ab3b872c49c436a5cfcb387e63100322ea3",
+   "a38376c7fcf6ddc548267cb45467390211736868",
    "testharness"
   ],
   "html/cross-origin-opener-policy/iframe-popup-same-origin-to-same-origin.https.html.headers": [
    "46ad58d83bf6e98913ca4c564b7acb8f19fa0093",
    "support"
   ],
+  "html/cross-origin-opener-policy/iframe-popup-same-origin-to-unsafe-none.https-expected.txt": [
+   "e4abbae75202f95adf4d641e4bb0e818f75f93f9",
+   "support"
+  ],
   "html/cross-origin-opener-policy/iframe-popup-same-origin-to-unsafe-none.https.html": [
-   "6589ad923c1fe2111f6a9865439284875d081b0f",
+   "4adb4130418ddb189443869f821fb2b235265b7d",
    "testharness"
   ],
   "html/cross-origin-opener-policy/iframe-popup-same-origin-to-unsafe-none.https.html.headers": [
@@ -508487,7 +508500,7 @@
    "testharness"
   ],
   "html/rendering/replaced-elements/attributes-for-embedded-content-and-images/img-aspect-ratio-lazy.tentative.html": [
-   "0a56539e0fd3e6d9184d18af5b910d391782612e",
+   "c058aa426de8d72b671f7eae5d36708d69c788c4",
    "testharness"
   ],
   "html/rendering/replaced-elements/attributes-for-embedded-content-and-images/img-aspect-ratio.html": [
@@ -518226,8 +518239,12 @@
    "a8eba38c60eeef7396c2725d26ea3c6e6609be7a",
    "testharness"
   ],
-  "html/user-activation/activation-hierarchy-parent-manual.sub.tentative.html": [
-   "7dd8018573db2808b7166cf7e94650a2a5a330bc",
+  "html/user-activation/activation-hierarchy-crossorigin-parent-manual.sub.html": [
+   "601b9003f7ea9ba9847366a9365a08139f810078",
+   "manual"
+  ],
+  "html/user-activation/activation-hierarchy-sameorigin-parent-manual.html": [
+   "a7f423eea56f0e6dd41ecf9a950137ef502fdf12",
    "manual"
   ],
   "html/user-activation/activation-thru-contextmenu-event-manual.html": [
@@ -518262,14 +518279,22 @@
    "4f03195f434742d80574c481092b307d4ad9108f",
    "manual"
   ],
-  "html/user-activation/resources/activation-hierarchy-child.sub.html": [
-   "ebccc8c0dbf9da8a95049712f0fdd641b66b629b",
+  "html/user-activation/resources/activation-hierarchy-crossorigin-child.sub.html": [
+   "6dbd46830442d5790207e1309bf4403a353c9c62",
    "support"
   ],
-  "html/user-activation/resources/activation-hierarchy-grandchild.html": [
+  "html/user-activation/resources/activation-hierarchy-crossorigin-grandchild.html": [
    "b9fe19a746ffbbe63740df67a77da8a894b573ae",
    "support"
   ],
+  "html/user-activation/resources/activation-hierarchy-sameorigin-child.html": [
+   "d54c7efc9bdcf2cc5c79fed6838729975bee4cfa",
+   "support"
+  ],
+  "html/user-activation/resources/activation-hierarchy-sameorigin-grandchild.html": [
+   "9d664e2356a87496416644d639598adbaa8c43dd",
+   "support"
+  ],
   "html/user-activation/resources/child-four.html": [
    "65d17f273e1868312f59c89f3f2d05bda759ae1a",
    "support"
@@ -575439,7 +575464,7 @@
    "testharness"
   ],
   "scroll-animations/scroll-timeline-phases.tentative.html": [
-   "0cc4e12864e209602b8c3ad57106deb3927e5be8",
+   "48ca749d24d5725acaea0981cada6383f0b3a45b",
    "testharness"
   ],
   "scroll-animations/setting-current-time.html": [
@@ -575451,7 +575476,7 @@
    "testharness"
   ],
   "scroll-animations/testcommon.js": [
-   "ca2596885669f24b5f170cf1b023cd3c3cc34b93",
+   "ab22ff64835e8c12fe0f2432060b79c44881bb5f",
    "support"
   ],
   "scroll-to-text-fragment/META.yml": [
@@ -579687,7 +579712,7 @@
    "testharness"
   ],
   "shadow-dom/slots-imperative-slot-api.tentative-expected.txt": [
-   "9dc356fbc25b5e57f21473f6ec8354500306e8fc",
+   "79d2a7d16fab5b6828df5c882112e473c1ba4ad3",
    "support"
   ],
   "shadow-dom/slots-imperative-slot-api.tentative.html": [
@@ -595398,10 +595423,6 @@
    "a33d6d4f24676356b59b6b431968d8486df50615",
    "testharness"
   ],
-  "web-animations/idlharness.window-expected.txt": [
-   "444ec5dc3f9614403c57a07bbd40e19c986274b7",
-   "support"
-  ],
   "web-animations/idlharness.window.js": [
    "c32016280e659fe8268c87b58e4956a1eb81b399",
    "testharness"
@@ -595522,12 +595543,8 @@
    "b8b4d74d5e536a111449fd0268424ac06a151b95",
    "testharness"
   ],
-  "web-animations/interfaces/DocumentOrShadowRoot/getAnimations-expected.txt": [
-   "d19858716bff9be02d84faf218d7631da7e0663f",
-   "support"
-  ],
   "web-animations/interfaces/DocumentOrShadowRoot/getAnimations.html": [
-   "5d6952bd3421a1d258976bbce91b9688c4668112",
+   "67addc05476fba7107fcc97b6783180093daf96f",
    "testharness"
   ],
   "web-animations/interfaces/DocumentTimeline/constructor.html": [
@@ -598347,7 +598364,7 @@
    "support"
   ],
   "webgpu/framework/version.js": [
-   "f8067d48eeb9a9942cd2797c57fbaa471044671f",
+   "0b58dd9b3c4430d337b4a6241f2345f4073a12b9",
    "support"
   ],
   "webgpu/runtime/helper/options.js": [
@@ -598390,12 +598407,16 @@
    "a693b002769ae9a1e951bc1888ca49ba393df7d0",
    "support"
   ],
+  "webgpu/suites/cts/capability_info.js": [
+   "10947ee1367e1d943e52a605fe868a06376badbe",
+   "support"
+  ],
   "webgpu/suites/cts/command_buffer/basic.spec.js": [
    "6762a563c56edafeb225df6af27bd4507825e56f",
    "support"
   ],
   "webgpu/suites/cts/command_buffer/compute/basic.spec.js": [
-   "598edea66ab3946b45548ca3cea1d8becd8aec55",
+   "536f7a9003eded24dced4ebc206c78472b26acb0",
    "support"
   ],
   "webgpu/suites/cts/command_buffer/copies.spec.js": [
@@ -598407,15 +598428,15 @@
    "support"
   ],
   "webgpu/suites/cts/command_buffer/render/rendering.spec.js": [
-   "7bb7c546b5842aa585dec89ec456ad78a4ccfc18",
+   "b26b9fbc859d5b2a5fbf79e9e1ca3f76cb02e455",
    "support"
   ],
   "webgpu/suites/cts/command_buffer/render/storeop.spec.js": [
-   "5bf6bd0116c80eea3082fab2f84522027755672b",
+   "1f758cc5c2ea74d9f2e36f5dd71d321c76cde822",
    "support"
   ],
   "webgpu/suites/cts/copyImageBitmapToTexture.spec.js": [
-   "b201bb49750c4f368fe6c39aa1462f1b026a3f69",
+   "42ad8507ec949c55c441cc3c2808ab8d6ebee7e5",
    "support"
   ],
   "webgpu/suites/cts/examples.spec.js": [
@@ -598426,12 +598447,8 @@
    "af6482fcab508705e884cd205cf31f7b9762990a",
    "support"
   ],
-  "webgpu/suites/cts/format_info.js": [
-   "5d54bbb8c51ecd39ddb4869ce29e46571726a0cc",
-   "support"
-  ],
   "webgpu/suites/cts/gpu_test.js": [
-   "236e9eaeb9d10923d840ff9cbe885a9d534e9230",
+   "7ab5320d6e95c8c3914c26db5f0cc7ee7a0b0843",
    "support"
   ],
   "webgpu/suites/cts/index.js": [
@@ -598439,31 +598456,31 @@
    "support"
   ],
   "webgpu/suites/cts/resource_init/sampled_texture_clear.spec.js": [
-   "66cee08addd60931f1192a6bf7bc97227b5b4130",
+   "a8fb1ad71d1ef0fa7030dd59a58733ad34fc0841",
    "support"
   ],
   "webgpu/suites/cts/validation/createBindGroup.spec.js": [
-   "22472f8dff2cf6b4d4206d7e29bc652a56466c4d",
+   "2f42a8b643d4905a5f15f3abc312e20ce569ecfa",
    "support"
   ],
   "webgpu/suites/cts/validation/createBindGroupLayout.spec.js": [
-   "22fa8f5d606e01841485d2567849db8dcd211168",
+   "b48db9598dd62fc933e14b08974c3aa4af6fbd63",
    "support"
   ],
   "webgpu/suites/cts/validation/createPipelineLayout.spec.js": [
-   "9d5394f925b6bec87b1ec7a01f9bd66aa994929e",
+   "0018c24e54075cf1551af3bbb1cb4d1977386907",
    "support"
   ],
   "webgpu/suites/cts/validation/createRenderPipeline.spec.js": [
-   "f6a555526559969c659c74a49d3035573fc013db",
+   "e4dd32af9e8de43bc29f8bd7ced45638d7a1e6a4",
    "support"
   ],
   "webgpu/suites/cts/validation/createTexture.spec.js": [
-   "08c6d1b50b596e32b780f08a7be6d370d0951a75",
+   "a7317a88f5badd9d65fc881a2a34a8cf652fdee0",
    "support"
   ],
   "webgpu/suites/cts/validation/createView.spec.js": [
-   "855d32a3f43990295513385ebbd161fa38d4f04a",
+   "a4fe7ac2a930ee9bec57528d743f119c8749b6fc",
    "support"
   ],
   "webgpu/suites/cts/validation/error_scope.spec.js": [
@@ -598479,7 +598496,7 @@
    "support"
   ],
   "webgpu/suites/cts/validation/render_pass.spec.js": [
-   "f788fb63d8d0cd0270516a9e51640cb95fdf4118",
+   "67f7bec6d66977fbb67539897d64ab0506a5f9f5",
    "support"
   ],
   "webgpu/suites/cts/validation/render_pass_descriptor.spec.js": [
@@ -598503,7 +598520,7 @@
    "support"
   ],
   "webgpu/suites/cts/validation/setVertexBuffer.spec.js": [
-   "ba22ee2d2a4e636e2b519003da669bbd33fead2d",
+   "47b861501296fbe517345a54ca7198e237cdf748",
    "support"
   ],
   "webgpu/suites/cts/validation/setViewport.spec.js": [
@@ -598515,7 +598532,7 @@
    "support"
   ],
   "webgpu/suites/cts/validation/vertex_state.spec.js": [
-   "cc33cde1decd5f42f006007e0e9381d50067babd",
+   "103a11265c573e7f2873da5893fb92aedcba14ee",
    "support"
   ],
   "webmessaging/Channel_postMessage_Blob.htm": [
diff --git a/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/iframe-popup-same-origin-to-same-origin.https.html b/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/iframe-popup-same-origin-to-same-origin.https.html
index 635f7ab..a38376c 100644
--- a/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/iframe-popup-same-origin-to-same-origin.https.html
+++ b/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/iframe-popup-same-origin-to-same-origin.https.html
@@ -8,18 +8,21 @@
 
 <div id=log></div>
 <script>
+// This document has COOP "same-origin". The popup has COOP "same-origin". Therefore there should
+// only be an opener and name if the frameOrigin and popupOrigin are same-origin with this document.
 [
-[SAME_ORIGIN, SAME_ORIGIN, "same-origin", true, true],
-[SAME_SITE, SAME_ORIGIN, "same-origin", false, false],
-[CROSS_ORIGIN, SAME_ORIGIN, "same-origin", false, false],
-[SAME_ORIGIN, SAME_SITE, "same-origin", false, false],
-[SAME_SITE, SAME_SITE, "same-origin", false, false],
-[CROSS_ORIGIN, SAME_SITE, "same-origin", false, false],
-[SAME_ORIGIN, CROSS_ORIGIN, "same-origin", false, false],
-[SAME_SITE, CROSS_ORIGIN, "same-origin", false, false],
-[CROSS_ORIGIN, CROSS_ORIGIN, "same-origin", false, false],
-].forEach( value => {
-    run_coop_test_iframe("same-origin", value[0], value[1], value[2], value[3], value[4]);
+[SAME_ORIGIN, SAME_ORIGIN, true],
+[SAME_SITE, SAME_ORIGIN, false],
+[CROSS_ORIGIN, SAME_ORIGIN, false],
+[SAME_ORIGIN, SAME_SITE, false],
+[SAME_SITE, SAME_SITE, false],
+[CROSS_ORIGIN, SAME_SITE, false],
+[SAME_ORIGIN, CROSS_ORIGIN, false],
+[SAME_SITE, CROSS_ORIGIN, false],
+[CROSS_ORIGIN, CROSS_ORIGIN, false],
+].forEach(([frameOrigin, popupOrigin, popupHasOpenerAndName]) => {
+    const testTitleStart = "same-origin";
+    const popupCOOP = "same-origin";
+    run_coop_test_iframe(testTitleStart, frameOrigin, popupOrigin, popupCOOP, popupHasOpenerAndName, popupHasOpenerAndName);
 });
-
-</script>
\ No newline at end of file
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/iframe-popup-same-origin-to-unsafe-none.https-expected.txt b/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/iframe-popup-same-origin-to-unsafe-none.https-expected.txt
new file mode 100644
index 0000000..e4abbae7
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/iframe-popup-same-origin-to-unsafe-none.https-expected.txt
@@ -0,0 +1,12 @@
+This is a testharness.js-based test.
+PASS same-origin with SAME_ORIGIN iframe opening popup a SAME_ORIGIN with COOP: 
+FAIL same-origin with SAME_SITE iframe opening popup a SAME_ORIGIN with COOP:  assert_equals: name expected "" but got "SAME_SITE_iframe_opening_SAME_ORIGIN_popup_with_coop_"
+FAIL same-origin with CROSS_ORIGIN iframe opening popup a SAME_ORIGIN with COOP:  assert_equals: name expected "" but got "CROSS_ORIGIN_iframe_opening_SAME_ORIGIN_popup_with_coop_"
+PASS same-origin with SAME_ORIGIN iframe opening popup a SAME_SITE with COOP: 
+FAIL same-origin with SAME_SITE iframe opening popup a SAME_SITE with COOP:  assert_equals: name expected "" but got "SAME_SITE_iframe_opening_SAME_SITE_popup_with_coop_"
+FAIL same-origin with CROSS_ORIGIN iframe opening popup a SAME_SITE with COOP:  assert_equals: name expected "" but got "CROSS_ORIGIN_iframe_opening_SAME_SITE_popup_with_coop_"
+PASS same-origin with SAME_ORIGIN iframe opening popup a CROSS_ORIGIN with COOP: 
+FAIL same-origin with SAME_SITE iframe opening popup a CROSS_ORIGIN with COOP:  assert_equals: name expected "" but got "SAME_SITE_iframe_opening_CROSS_ORIGIN_popup_with_coop_"
+FAIL same-origin with CROSS_ORIGIN iframe opening popup a CROSS_ORIGIN with COOP:  assert_equals: name expected "" but got "CROSS_ORIGIN_iframe_opening_CROSS_ORIGIN_popup_with_coop_"
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/iframe-popup-same-origin-to-unsafe-none.https.html b/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/iframe-popup-same-origin-to-unsafe-none.https.html
index 6589ad92..4adb4130 100644
--- a/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/iframe-popup-same-origin-to-unsafe-none.https.html
+++ b/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/iframe-popup-same-origin-to-unsafe-none.https.html
@@ -8,18 +8,22 @@
 
 <div id=log></div>
 <script>
+// This document has COOP "same-origin". The popup has no COOP. Therefore there should be no
+// opener or name.
 [
-[SAME_ORIGIN, SAME_ORIGIN, "", false, false],
-[SAME_SITE, SAME_ORIGIN, "", false, true],
-[CROSS_ORIGIN, SAME_ORIGIN, "", false, true],
-[SAME_ORIGIN, SAME_SITE, "", false, false],
-[SAME_SITE, SAME_SITE, "", false, true],
-[CROSS_ORIGIN, SAME_SITE, "", false, true],
-[SAME_ORIGIN, CROSS_ORIGIN, "", false, false],
-[SAME_SITE, CROSS_ORIGIN, "", false, true],
-[CROSS_ORIGIN, CROSS_ORIGIN, "", false, true],
-].forEach( value => {
-    run_coop_test_iframe("same-origin", value[0], value[1], value[2], value[3], value[4]);
+[SAME_ORIGIN, SAME_ORIGIN],
+[SAME_SITE, SAME_ORIGIN],
+[CROSS_ORIGIN, SAME_ORIGIN],
+[SAME_ORIGIN, SAME_SITE],
+[SAME_SITE, SAME_SITE],
+[CROSS_ORIGIN, SAME_SITE],
+[SAME_ORIGIN, CROSS_ORIGIN],
+[SAME_SITE, CROSS_ORIGIN],
+[CROSS_ORIGIN, CROSS_ORIGIN],
+].forEach(([frameOrigin, popupOrigin]) => {
+    const testTitleStart = "same-origin";
+    const popupCOOP = "";
+    const popupHasOpenerAndName = false;
+    run_coop_test_iframe(testTitleStart, frameOrigin, popupOrigin, popupCOOP, popupHasOpenerAndName, popupHasOpenerAndName);
 });
-
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/img-aspect-ratio-lazy.tentative.html b/third_party/blink/web_tests/external/wpt/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/img-aspect-ratio-lazy.tentative.html
index 0a56539e..c058aa4 100644
--- a/third_party/blink/web_tests/external/wpt/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/img-aspect-ratio-lazy.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/img-aspect-ratio-lazy.tentative.html
@@ -1,5 +1,6 @@
 <!doctype html>
 <title>Image width and height attributes are used to infer aspect-ratio for lazy-loaded images</title>
+<meta name="viewport" content="width=device-width">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <style>
diff --git a/third_party/blink/web_tests/external/wpt/native-file-system/idlharness.https.any-expected.txt b/third_party/blink/web_tests/external/wpt/native-file-system/idlharness.https.any-expected.txt
new file mode 100644
index 0000000..53542e69
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/native-file-system/idlharness.https.any-expected.txt
@@ -0,0 +1,57 @@
+This is a testharness.js-based test.
+Found 53 tests; 52 PASS, 1 FAIL, 0 TIMEOUT, 0 NOTRUN.
+PASS idl_test setup
+PASS idl_test validation
+PASS Partial interface Window: original interface defined
+PASS Partial interface Window: member names are unique
+PASS Partial interface FileSystemDirectoryHandle: original interface defined
+PASS Partial interface FileSystemDirectoryHandle: member names are unique
+PASS Partial interface Window[2]: member names are unique
+PASS Window includes GlobalEventHandlers: member names are unique
+PASS Window includes WindowEventHandlers: member names are unique
+PASS Window includes WindowOrWorkerGlobalScope: member names are unique
+PASS Window includes AnimationFrameProvider: member names are unique
+PASS Window includes WindowSessionStorage: member names are unique
+PASS Window includes WindowLocalStorage: member names are unique
+PASS FileSystemHandle interface: existence and properties of interface object
+PASS FileSystemHandle interface object length
+PASS FileSystemHandle interface object name
+PASS FileSystemHandle interface: existence and properties of interface prototype object
+PASS FileSystemHandle interface: existence and properties of interface prototype object's "constructor" property
+PASS FileSystemHandle interface: existence and properties of interface prototype object's @@unscopables property
+PASS FileSystemHandle interface: attribute isFile
+PASS FileSystemHandle interface: attribute isDirectory
+PASS FileSystemHandle interface: attribute name
+PASS FileSystemHandle interface: operation queryPermission(optional FileSystemHandlePermissionDescriptor)
+PASS FileSystemHandle interface: operation requestPermission(optional FileSystemHandlePermissionDescriptor)
+PASS FileSystemFileHandle interface: existence and properties of interface object
+PASS FileSystemFileHandle interface object length
+PASS FileSystemFileHandle interface object name
+PASS FileSystemFileHandle interface: existence and properties of interface prototype object
+PASS FileSystemFileHandle interface: existence and properties of interface prototype object's "constructor" property
+PASS FileSystemFileHandle interface: existence and properties of interface prototype object's @@unscopables property
+PASS FileSystemFileHandle interface: operation getFile()
+FAIL FileSystemFileHandle interface: operation createWriter(optional FileSystemCreateWriterOptions) assert_own_property: interface prototype object missing non-static operation expected property "createWriter" missing
+PASS FileSystemDirectoryHandle interface: existence and properties of interface object
+PASS FileSystemDirectoryHandle interface object length
+PASS FileSystemDirectoryHandle interface object name
+PASS FileSystemDirectoryHandle interface: existence and properties of interface prototype object
+PASS FileSystemDirectoryHandle interface: existence and properties of interface prototype object's "constructor" property
+PASS FileSystemDirectoryHandle interface: existence and properties of interface prototype object's @@unscopables property
+PASS FileSystemDirectoryHandle interface: operation getFile(USVString, optional FileSystemGetFileOptions)
+PASS FileSystemDirectoryHandle interface: operation getDirectory(USVString, optional FileSystemGetDirectoryOptions)
+PASS FileSystemDirectoryHandle interface: operation getEntries()
+PASS FileSystemDirectoryHandle interface: operation removeEntry(USVString, optional FileSystemRemoveOptions)
+PASS FileSystemDirectoryHandle interface: operation getSystemDirectory(GetSystemDirectoryOptions)
+PASS FileSystemWriter interface: existence and properties of interface object
+PASS FileSystemWriter interface object length
+PASS FileSystemWriter interface object name
+PASS FileSystemWriter interface: existence and properties of interface prototype object
+PASS FileSystemWriter interface: existence and properties of interface prototype object's "constructor" property
+PASS FileSystemWriter interface: existence and properties of interface prototype object's @@unscopables property
+PASS FileSystemWriter interface: operation write(unsigned long long, (BufferSource or Blob or USVString))
+PASS FileSystemWriter interface: operation truncate(unsigned long long)
+PASS FileSystemWriter interface: operation close()
+PASS Window interface: operation chooseFileSystemEntries(optional ChooseFileSystemEntriesOptions)
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/native-file-system/idlharness.https.any.worker-expected.txt b/third_party/blink/web_tests/external/wpt/native-file-system/idlharness.https.any.worker-expected.txt
new file mode 100644
index 0000000..dd0e88f
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/native-file-system/idlharness.https.any.worker-expected.txt
@@ -0,0 +1,57 @@
+This is a testharness.js-based test.
+Found 53 tests; 52 PASS, 1 FAIL, 0 TIMEOUT, 0 NOTRUN.
+PASS idl_test setup
+PASS idl_test validation
+PASS Partial interface Window: original interface defined
+PASS Partial interface Window: member names are unique
+PASS Partial interface FileSystemDirectoryHandle: original interface defined
+PASS Partial interface FileSystemDirectoryHandle: member names are unique
+PASS Partial interface Window[2]: member names are unique
+PASS Window includes GlobalEventHandlers: member names are unique
+PASS Window includes WindowEventHandlers: member names are unique
+PASS Window includes WindowOrWorkerGlobalScope: member names are unique
+PASS Window includes AnimationFrameProvider: member names are unique
+PASS Window includes WindowSessionStorage: member names are unique
+PASS Window includes WindowLocalStorage: member names are unique
+PASS FileSystemHandle interface: existence and properties of interface object
+PASS FileSystemHandle interface object length
+PASS FileSystemHandle interface object name
+PASS FileSystemHandle interface: existence and properties of interface prototype object
+PASS FileSystemHandle interface: existence and properties of interface prototype object's "constructor" property
+PASS FileSystemHandle interface: existence and properties of interface prototype object's @@unscopables property
+PASS FileSystemHandle interface: attribute isFile
+PASS FileSystemHandle interface: attribute isDirectory
+PASS FileSystemHandle interface: attribute name
+PASS FileSystemHandle interface: operation queryPermission(optional FileSystemHandlePermissionDescriptor)
+PASS FileSystemHandle interface: operation requestPermission(optional FileSystemHandlePermissionDescriptor)
+PASS FileSystemFileHandle interface: existence and properties of interface object
+PASS FileSystemFileHandle interface object length
+PASS FileSystemFileHandle interface object name
+PASS FileSystemFileHandle interface: existence and properties of interface prototype object
+PASS FileSystemFileHandle interface: existence and properties of interface prototype object's "constructor" property
+PASS FileSystemFileHandle interface: existence and properties of interface prototype object's @@unscopables property
+PASS FileSystemFileHandle interface: operation getFile()
+FAIL FileSystemFileHandle interface: operation createWriter(optional FileSystemCreateWriterOptions) assert_own_property: interface prototype object missing non-static operation expected property "createWriter" missing
+PASS FileSystemDirectoryHandle interface: existence and properties of interface object
+PASS FileSystemDirectoryHandle interface object length
+PASS FileSystemDirectoryHandle interface object name
+PASS FileSystemDirectoryHandle interface: existence and properties of interface prototype object
+PASS FileSystemDirectoryHandle interface: existence and properties of interface prototype object's "constructor" property
+PASS FileSystemDirectoryHandle interface: existence and properties of interface prototype object's @@unscopables property
+PASS FileSystemDirectoryHandle interface: operation getFile(USVString, optional FileSystemGetFileOptions)
+PASS FileSystemDirectoryHandle interface: operation getDirectory(USVString, optional FileSystemGetDirectoryOptions)
+PASS FileSystemDirectoryHandle interface: operation getEntries()
+PASS FileSystemDirectoryHandle interface: operation removeEntry(USVString, optional FileSystemRemoveOptions)
+PASS FileSystemDirectoryHandle interface: operation getSystemDirectory(GetSystemDirectoryOptions)
+PASS FileSystemWriter interface: existence and properties of interface object
+PASS FileSystemWriter interface object length
+PASS FileSystemWriter interface object name
+PASS FileSystemWriter interface: existence and properties of interface prototype object
+PASS FileSystemWriter interface: existence and properties of interface prototype object's "constructor" property
+PASS FileSystemWriter interface: existence and properties of interface prototype object's @@unscopables property
+PASS FileSystemWriter interface: operation write(unsigned long long, (BufferSource or Blob or USVString))
+PASS FileSystemWriter interface: operation truncate(unsigned long long)
+PASS FileSystemWriter interface: operation close()
+PASS Window interface: existence and properties of interface object
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/animation-ref.html b/third_party/blink/web_tests/external/wpt/scroll-animations/animation-ref.html
new file mode 100644
index 0000000..9158715
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/scroll-animations/animation-ref.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<title>Reference for Web Animation with scroll timeline tests</title>
+<style>
+  #box {
+    width: 100px;
+    height: 100px;
+    background-color: green;
+    transform: translate(0, 100px);
+    opacity: 0.5;
+    will-change: transform; /* force compositing */
+  }
+
+  #covered {
+    width: 100px;
+    height: 100px;
+    background-color: red;
+  }
+
+  #scroller {
+    overflow: auto;
+    height: 100px;
+    width: 100px;
+    will-change: transform; /* force compositing */
+  }
+
+  #contents {
+    height: 1000px;
+    width: 100%;
+  }
+</style>
+
+<div id="box"></div>
+<div id="covered"></div>
+<div id="scroller">
+  <div id="contents"></div>
+</div>
+
+<script>
+  window.addEventListener('load', function() {
+    // Move the scroller to halfway.
+    const scroller = document.getElementById("scroller");
+    const maxScroll = scroller.scrollHeight - scroller.clientHeight;
+    scroller.scrollTop = 0.5 * maxScroll;
+  });
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/animation-with-display-none.html b/third_party/blink/web_tests/external/wpt/scroll-animations/animation-with-display-none.html
new file mode 100644
index 0000000..310cb5f
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/scroll-animations/animation-with-display-none.html
@@ -0,0 +1,74 @@
+<html class="reftest-wait">
+<title>Scroll timeline with Web Animation and transition from display:none to display:block</title>
+<link rel="help" href="https://drafts.csswg.org/scroll-animations/">
+<meta name="assert" content="Scroll timeline should properly handle going from display:none to display:block">
+<link rel="match" href="animation-ref.html">
+
+<script src="/web-animations/testcommon.js"></script>
+<script src="/common/reftest-wait.js"></script>
+
+<style>
+  #box {
+    width: 100px;
+    height: 100px;
+    background-color: green;
+  }
+
+  #covered {
+    width: 100px;
+    height: 100px;
+    background-color: red;
+  }
+
+  #scroller {
+    overflow: auto;
+    height: 100px;
+    width: 100px;
+    will-change: transform; /* force compositing */
+  }
+
+  .removed {
+    display: none;
+  }
+
+  #contents {
+    height: 1000px;
+    width: 100%;
+  }
+</style>
+
+<div id="box"></div>
+<div id="covered"></div>
+<div id="scroller">
+  <div id="contents"></div>
+</div>
+
+<script>
+  const box = document.getElementById('box');
+  const effect = new KeyframeEffect(box,
+    [
+    { transform: 'translateY(0)', opacity: 1 },
+    { transform: 'translateY(200px)', opacity: 0 }
+    ], {
+      duration: 1000,
+    }
+  );
+
+  const scroller = document.getElementById('scroller');
+  scroller.classList.add('removed');
+  const timeline = new ScrollTimeline({ scrollSource: scroller, timeRange: 1000, orientation: 'block' });
+  const animation = new Animation(effect, timeline);
+  animation.play();
+
+  waitForAnimationFrames(2).then(_ => {
+    scroller.classList.remove('removed');
+    animation.ready.then(() => {
+      const maxScroll = scroller.scrollHeight - scroller.clientHeight;
+      scroller.scrollTop = 0.5 * maxScroll;
+
+      waitForAnimationFrames(2).then(_ => {
+        takeScreenshot();
+      });
+    });
+  });
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/animation-with-overflow-hidden-ref.html b/third_party/blink/web_tests/external/wpt/scroll-animations/animation-with-overflow-hidden-ref.html
new file mode 100644
index 0000000..c045f1a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/scroll-animations/animation-with-overflow-hidden-ref.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<title>Scroll timeline with Web Animation using a scroller with overflow hidden</title>
+<style>
+  #box {
+    width: 100px;
+    height: 100px;
+    background-color: green;
+    transform: translate(0, 100px);
+    opacity: 0.5;
+    will-change: transform; /* force compositing */
+  }
+
+  #covered {
+    width: 100px;
+    height: 100px;
+    background-color: red;
+  }
+
+  #scroller {
+    overflow: hidden;
+    height: 100px;
+    width: 100px;
+    will-change: transform; /* force compositing */
+  }
+
+  #contents {
+    height: 1000px;
+    width: 100%;
+  }
+</style>
+
+<div id="box"></div>
+<div id="covered"></div>
+<div id="scroller">
+  <div id="contents"></div>
+</div>
+
+<script>
+  window.addEventListener('load', function() {
+    // Move the scroller to halfway.
+    const scroller = document.getElementById("scroller");
+    const maxScroll = scroller.scrollHeight - scroller.clientHeight;
+    scroller.scrollTop = 0.5 * maxScroll;
+  });
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/animation-with-overflow-hidden.html b/third_party/blink/web_tests/external/wpt/scroll-animations/animation-with-overflow-hidden.html
new file mode 100644
index 0000000..118f682
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/scroll-animations/animation-with-overflow-hidden.html
@@ -0,0 +1,65 @@
+<html class="reftest-wait">
+<title>Scroll timeline with Web Animation using a scroller with overflow hidden</title>
+<link rel="help" href="https://drafts.csswg.org/scroll-animations/">
+<meta name="assert" content="Web animation correctly updates values when using a overflow: hidden on the scroller being used as the source for the ScrollTimeline">
+<link rel="match" href="animation-with-overflow-hidden-ref.html">
+
+<script src="/web-animations/testcommon.js"></script>
+<script src="/common/reftest-wait.js"></script>
+
+<style>
+  #box {
+    width: 100px;
+    height: 100px;
+    background-color: green;
+  }
+
+  #covered {
+    width: 100px;
+    height: 100px;
+    background-color: red;
+  }
+
+  #scroller {
+    overflow: hidden;
+    height: 100px;
+    width: 100px;
+  }
+
+  #contents {
+    height: 1000px;
+    width: 100%;
+  }
+</style>
+
+<div id="box"></div>
+<div id="covered"></div>
+<div id="scroller">
+  <div id="contents"></div>
+</div>
+
+<script>
+  const box = document.getElementById('box');
+  const effect = new KeyframeEffect(box,
+    [
+      {transform: 'translateY(0)', opacity: 1},
+      {transform: 'translateY(200px)', opacity: 0}
+    ], {
+      duration: 1000,
+    }
+  );
+
+  const scroller = document.getElementById('scroller');
+  const timeline = new ScrollTimeline({ scrollSource: scroller, timeRange: 1000, orientation: 'block' });
+  const animation = new Animation(effect, timeline);
+  animation.play();
+
+  animation.ready.then(() => {
+    // Move the scroller to the halfway point.
+    const maxScroll = scroller.scrollHeight - scroller.clientHeight;
+    scroller.scrollTop = 0.5 * maxScroll;
+    waitForAnimationFrames(2).then(_ => {
+      takeScreenshot();
+    });
+  });
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/animation-with-root-scroller-ref.html b/third_party/blink/web_tests/external/wpt/scroll-animations/animation-with-root-scroller-ref.html
new file mode 100644
index 0000000..92ec0f93
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/scroll-animations/animation-with-root-scroller-ref.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<title>Reference for Scroll timeline with Web Animation using the root scroller</title>
+<style>
+  html {
+    min-height: 100%;
+    min-width: 100%;
+    padding-bottom: 100px;
+    padding-right: 100px;
+  }
+
+  #box {
+    width: 100px;
+    height: 100px;
+    background-color: green;
+    transform: translate(0, 100px);
+    opacity: 0.5;
+    will-change: transform; /* force compositing */
+  }
+
+  #covered {
+    width: 100px;
+    height: 100px;
+    background-color: red;
+  }
+</style>
+
+<div id="box"></div>
+<div id="covered"></div>
+
+<script>
+  window.addEventListener('load', function() {
+    // Move the scroller to halfway.
+    const scroller = document.scrollingElement;
+    const maxScroll = scroller.scrollHeight - scroller.clientHeight;
+    scroller.scrollTop = 0.5 * maxScroll;
+  });
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/animation-with-root-scroller.html b/third_party/blink/web_tests/external/wpt/scroll-animations/animation-with-root-scroller.html
new file mode 100644
index 0000000..6748e22
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/scroll-animations/animation-with-root-scroller.html
@@ -0,0 +1,59 @@
+<html class="reftest-wait">
+<title>Scroll timeline with Web Animation using the root scroller</title>
+<link rel="help" href="https://drafts.csswg.org/scroll-animations/">
+<meta name="assert" content="Web animation correctly updates values when using the root scroller as the source for the ScrollTimeline">
+<link rel="match" href="animation-with-root-scroller-ref.html">
+
+<script src="/web-animations/testcommon.js"></script>
+<script src="/common/reftest-wait.js"></script>
+
+<style>
+  html {
+    min-height: 100%;
+    min-width: 100%;
+    padding-bottom: 100px;
+    padding-right: 100px;
+  }
+
+  #box {
+    width: 100px;
+    height: 100px;
+    background-color: green;
+  }
+
+  #covered {
+    width: 100px;
+    height: 100px;
+    background-color: red;
+  }
+</style>
+
+<div id="box"></div>
+<div id="covered"></div>
+
+<script>
+  const box = document.getElementById('box');
+  const effect = new KeyframeEffect(box,
+    [
+      {transform: 'translateY(0)', opacity: 1},
+      {transform: 'translateY(200px)', opacity: 0}
+    ], {
+      duration: 1000,
+    }
+  );
+
+  const scroller = document.scrollingElement;
+  const timeline = new ScrollTimeline({ scrollSource: scroller, timeRange: 1000, orientation: 'block' });
+  const animation = new Animation(effect, timeline);
+  animation.play();
+
+  animation.ready.then(() => {
+    // Move the scroller to the halfway point.
+    const maxScroll = scroller.scrollHeight - scroller.clientHeight;
+    scroller.scrollTop = 0.5 * maxScroll;
+
+    waitForAnimationFrames(2).then(_ => {
+      takeScreenshot();
+    });
+  });
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/animation-with-transform.html b/third_party/blink/web_tests/external/wpt/scroll-animations/animation-with-transform.html
new file mode 100644
index 0000000..b6ee964
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/scroll-animations/animation-with-transform.html
@@ -0,0 +1,67 @@
+<html class="reftest-wait">
+<title>Basic use of scroll timeline with Web Animation</title>
+<link rel="help" href="https://drafts.csswg.org/scroll-animations/">
+<meta name="assert" content="Should be able to use the scroll timeline to drive the animation timing">
+<link rel="match" href="animation-ref.html">
+
+<script src="/web-animations/testcommon.js"></script>
+<script src="/common/reftest-wait.js"></script>
+
+<style>
+  #box {
+    width: 100px;
+    height: 100px;
+    background-color: green;
+  }
+
+  #covered {
+    width: 100px;
+    height: 100px;
+    background-color: red;
+  }
+
+  #scroller {
+    overflow: auto;
+    height: 100px;
+    width: 100px;
+    will-change: transform; /* force compositing */
+  }
+
+  #contents {
+    height: 1000px;
+    width: 100%;
+  }
+</style>
+
+<div id="box"></div>
+<div id="covered"></div>
+<div id="scroller">
+  <div id="contents"></div>
+</div>
+
+<script>
+  const box = document.getElementById('box');
+  const effect = new KeyframeEffect(box,
+    [
+    { transform: 'translateY(0)', opacity: 1},
+    { transform: 'translateY(200px)', opacity: 0}
+    ], {
+      duration: 1000,
+    }
+  );
+
+  const scroller = document.getElementById('scroller');
+  const timeline = new ScrollTimeline({ scrollSource: scroller, timeRange: 1000, orientation: 'block' });
+  const animation = new Animation(effect, timeline);
+  animation.play();
+
+  animation.ready.then(() => {
+    // Move the scroller to the halfway point.
+    const maxScroll = scroller.scrollHeight - scroller.clientHeight;
+    scroller.scrollTop = 0.5 * maxScroll;
+
+    waitForAnimationFrames(2).then(_ => {
+        takeScreenshot();
+    });
+  });
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/two-animations-attach-to-same-scroll-timeline-cancel-one.html b/third_party/blink/web_tests/external/wpt/scroll-animations/two-animations-attach-to-same-scroll-timeline-cancel-one.html
new file mode 100644
index 0000000..1519e51c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/scroll-animations/two-animations-attach-to-same-scroll-timeline-cancel-one.html
@@ -0,0 +1,83 @@
+<html class="reftest-wait">
+<title>Scroll timeline shared by two animation, one gets cancelled</title>
+<link rel="help" href="https://drafts.csswg.org/scroll-animations/">
+<meta name="assert" content="Cancelling animations should not affect other
+                   animation that is attached to the same timeline.">
+<link rel="match" href="animation-ref.html">
+
+<script src="/web-animations/testcommon.js"></script>
+<script src="/common/reftest-wait.js"></script>
+
+<style>
+  #box {
+    width: 100px;
+    height: 100px;
+    background-color: green;
+  }
+
+  #covered {
+    width: 100px;
+    height: 100px;
+    background-color: red;
+  }
+
+  #scroller {
+    overflow: auto;
+    height: 100px;
+    width: 100px;
+    will-change: transform; /* force compositing */
+  }
+
+  #contents {
+    height: 1000px;
+    width: 100%;
+  }
+</style>
+
+<div id="box"></div>
+<div id="covered"></div>
+<div id="scroller">
+  <div id="contents"></div>
+</div>
+
+<script>
+  const box = document.getElementById('box');
+  const effect = new KeyframeEffect(box,
+    [
+    { transform: 'translateY(0)', opacity: 1},
+    { transform: 'translateY(200px)', opacity: 0}
+    ], {
+      duration: 1000,
+    }
+  );
+  const temporary_effect = new KeyframeEffect(box,
+    [
+    { transform: 'translateX(0)'},
+    { transform: 'translateX(200px)'}
+    ], {
+      duration: 1000,
+    }
+  );
+
+  const scroller = document.getElementById('scroller');
+  const timeline = new ScrollTimeline({ scrollSource: scroller, timeRange: 1000, orientation: 'block' });
+  const animation = new Animation(effect, timeline);
+  const temporary_animation = new Animation(temporary_effect, timeline);
+  animation.play();
+  temporary_animation.play();
+
+  Promise.all([animation.ready, temporary_animation.ready]).then(() => {
+    temporary_animation.cancel();
+    temporary_animation.ready.then(() => {
+      waitForAnimationFrames(2).then(_ => {
+        // Move the scroller to the halfway point.
+        const maxScroll = scroller.scrollHeight - scroller.clientHeight;
+        scroller.scrollTop = 0.5 * maxScroll;
+
+        waitForAnimationFrames(2).then(_ => {
+            takeScreenshot();
+        });
+      });
+    });
+  });
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/two-animations-attach-to-same-scroll-timeline.html b/third_party/blink/web_tests/external/wpt/scroll-animations/two-animations-attach-to-same-scroll-timeline.html
new file mode 100644
index 0000000..4b491a2
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/scroll-animations/two-animations-attach-to-same-scroll-timeline.html
@@ -0,0 +1,78 @@
+<html class="reftest-wait">
+<title>Scroll timeline shared by two animation</title>
+<link rel="help" href="https://drafts.csswg.org/scroll-animations/">
+<meta name="assert" content="Should be able to use the same scroll timeline to
+drive two animations">
+<link rel="match" href="animation-ref.html">
+
+<script src="/web-animations/testcommon.js"></script>
+<script src="/common/reftest-wait.js"></script>
+
+<style>
+  #box {
+    width: 100px;
+    height: 100px;
+    background-color: green;
+  }
+
+  #covered {
+    width: 100px;
+    height: 100px;
+    background-color: red;
+  }
+
+  #scroller {
+    overflow: auto;
+    height: 100px;
+    width: 100px;
+    will-change: transform; /* force compositing */
+  }
+
+  #contents {
+    height: 1000px;
+    width: 100%;
+  }
+</style>
+
+<div id="box"></div>
+<div id="covered"></div>
+<div id="scroller">
+  <div id="contents"></div>
+</div>
+
+<script>
+  const box = document.getElementById('box');
+  const transform_effect = new KeyframeEffect(box,
+    [
+    { transform: 'translateY(0)'},
+    { transform: 'translateY(200px)'}
+    ], {
+      duration: 1000,
+    }
+  );
+  const opacity_effect = new KeyframeEffect(box,
+    [
+    { opacity: 1},
+    { opacity: 0}
+    ], {
+      duration: 1000,
+    }
+  );
+
+  const scroller = document.getElementById('scroller');
+  const timeline = new ScrollTimeline({ scrollSource: scroller, timeRange: 1000, orientation: 'block' });
+  const transform_animation = new Animation(transform_effect, timeline);
+  transform_animation.play();
+  const opacity_animation = new Animation(opacity_effect, timeline);
+  opacity_animation.play();
+
+  Promise.all([transform_animation.ready, opacity_animation.ready]).then(() => {
+    // Move the scroller to the halfway point.
+    const maxScroll = scroller.scrollHeight - scroller.clientHeight;
+    scroller.scrollTop = 0.5 * maxScroll;
+
+    waitForAnimationFrames(2).then(_ => {
+        takeScreenshot();
+    });
+  });
+</script>
diff --git a/third_party/blink/web_tests/http/tests/activedomobject/media-expected.txt b/third_party/blink/web_tests/http/tests/activedomobject/media-expected.txt
index 5fe8d87..3120871 100644
--- a/third_party/blink/web_tests/http/tests/activedomobject/media-expected.txt
+++ b/third_party/blink/web_tests/http/tests/activedomobject/media-expected.txt
@@ -2,7 +2,7 @@
 
 Before Reparenting
 PASS: internals.contextLifecycleStateObserverObjectCount(document) should be '2' and is.
-PASS: internals.contextLifecycleStateObserverObjectCount(iframe) should be '5' and is.
+PASS: internals.contextLifecycleStateObserverObjectCount(iframe) should be '7' and is.
 After Reparenting
 PASS: internals.contextLifecycleStateObserverObjectCount(document) should be '5' and is.
-PASS: internals.contextLifecycleStateObserverObjectCount(iframe) should be '3' and is.
+PASS: internals.contextLifecycleStateObserverObjectCount(iframe) should be '5' and is.
diff --git a/third_party/blink/web_tests/http/tests/activedomobject/media.html b/third_party/blink/web_tests/http/tests/activedomobject/media.html
index 9523ef6..0803012a 100644
--- a/third_party/blink/web_tests/http/tests/activedomobject/media.html
+++ b/third_party/blink/web_tests/http/tests/activedomobject/media.html
@@ -11,7 +11,7 @@
 
         log('Before Reparenting');
         shouldBe('internals.contextLifecycleStateObserverObjectCount(document)', 2);
-        shouldBe('internals.contextLifecycleStateObserverObjectCount(iframe)', 5);
+        shouldBe('internals.contextLifecycleStateObserverObjectCount(iframe)', 7);
 
         document.body.appendChild(window.iframe.querySelector('video'));
 
@@ -21,7 +21,7 @@
         // to differ by one.
         log('After Reparenting');
         shouldBe('internals.contextLifecycleStateObserverObjectCount(document)', 5);
-        shouldBe('internals.contextLifecycleStateObserverObjectCount(iframe)', 3);
+        shouldBe('internals.contextLifecycleStateObserverObjectCount(iframe)', 5);
     }
 </script>
 </body>
diff --git a/third_party/blink/web_tests/http/tests/origin_trials/webexposed/native-file-system-origin-trial-interfaces.html b/third_party/blink/web_tests/http/tests/origin_trials/webexposed/native-file-system-origin-trial-interfaces.html
index 1a4372ae..4aeca62 100644
--- a/third_party/blink/web_tests/http/tests/origin_trials/webexposed/native-file-system-origin-trial-interfaces.html
+++ b/third_party/blink/web_tests/http/tests/origin_trials/webexposed/native-file-system-origin-trial-interfaces.html
@@ -1,9 +1,9 @@
-<!DOCTYPE html>
+<!DOCTYPE html>
 <meta charset="utf-8">
 <!-- Generate token with the command:
-generate_token.py http://127.0.0.1:8000 NativeFileSystem --expire-timestamp=2000000000
+generate_token.py http://127.0.0.1:8000 NativeFileSystem2 --expire-timestamp=2000000000
 -- -->
-<meta http-equiv="origin-trial" content="AonTJaP9wZXkdR+DdBwuBMICov7GjWH/biHmn3yiSHBYZttDKA7ZMlB+rfnf7ImSFpYeePoQsT3Fg6i5yxNRNQoAAABYeyJvcmlnaW4iOiAiaHR0cDovLzEyNy4wLjAuMTo4MDAwIiwgImZlYXR1cmUiOiAiTmF0aXZlRmlsZVN5c3RlbSIsICJleHBpcnkiOiAyMDAwMDAwMDAwfQ==">
+<meta http-equiv="origin-trial" content="AmmBxJTKsdbb26LLpdAVmIKBvVxU6fKf6UZDoMXiD+b9bULkOHzUTtZ4dNwHMhI7tZzO+tNgmWCzNK2MLoZsvAEAAABZeyJvcmlnaW4iOiAiaHR0cDovLzEyNy4wLjAuMTo4MDAwIiwgImZlYXR1cmUiOiAiTmF0aXZlRmlsZVN5c3RlbTIiLCAiZXhwaXJ5IjogMjAwMDAwMDAwMH0=">
 <title>Native File System API - interfaces exposed by origin trial</title>
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
@@ -12,7 +12,7 @@
 test(t => {
   OriginTrialsHelper.check_properties_exist(this, {
     'FileSystemHandle': ['isFile', 'isDirectory', 'name', 'queryPermission', 'requestPermission'],
-    'FileSystemFileHandle': ['getFile', 'createWriter'],
+    'FileSystemFileHandle': ['getFile', 'createWritable'],
     'FileSystemDirectoryHandle': ['getFile', 'getDirectory', 'getEntries', 'removeEntry'],
     'FileSystemWriter': ['write', 'truncate', 'close'],
     'global': ['chooseFileSystemEntries'],
diff --git a/third_party/blink/web_tests/http/tests/security/agent-equality.html b/third_party/blink/web_tests/http/tests/security/agent-equality.html
index 4e5a9673..40703bb1 100644
--- a/third_party/blink/web_tests/http/tests/security/agent-equality.html
+++ b/third_party/blink/web_tests/http/tests/security/agent-equality.html
@@ -164,9 +164,8 @@
       if (evt.data[0] !== 'same-origin detach test') {
         return;
       }
-      assert_equals(evt.data.length, 4);
+      assert_equals(evt.data.length, 3);
       assert_equals(evt.data[1], evt.data[2]);
-      assert_equals(evt.data[1], evt.data[3]);
       t.done();
     }));
 
diff --git a/third_party/blink/web_tests/http/tests/security/resources/agent-equality-after-detach.html b/third_party/blink/web_tests/http/tests/security/resources/agent-equality-after-detach.html
index 1668aa3..7a1818ab 100644
--- a/third_party/blink/web_tests/http/tests/security/resources/agent-equality-after-detach.html
+++ b/third_party/blink/web_tests/http/tests/security/resources/agent-equality-after-detach.html
@@ -14,15 +14,13 @@
 
 function run(evt) {
   let sub1 = document.getElementById('sub1');
-  let sub1_doc = sub1.contentDocument;
+  let sub1_window = sub1.contentWindow;
   sub1.remove();
-  let sub1_cloned_doc = sub1_doc.cloneNode();
 
   let message = [
     'same-origin detach test',
-    internals.getDocumentAgentId(document),
-    internals.getDocumentAgentId(sub1_doc),
-    internals.getDocumentAgentId(sub1_cloned_doc)
+    internals.getAgentId(window),
+    internals.getAgentId(sub1_window)
   ];
 
   window.parent.postMessage(message, '*');
diff --git a/third_party/blink/web_tests/http/tests/security/resources/agent-equality-cross-origin.html b/third_party/blink/web_tests/http/tests/security/resources/agent-equality-cross-origin.html
index 6a1c8ad..797c432 100644
--- a/third_party/blink/web_tests/http/tests/security/resources/agent-equality-cross-origin.html
+++ b/third_party/blink/web_tests/http/tests/security/resources/agent-equality-cross-origin.html
@@ -25,7 +25,7 @@
   let agentIdB = evt.data;
   let message = [
     'cross-origin test',
-    internals.getDocumentAgentId(document),
+    internals.getAgentId(window),
     agentIdB
   ];
   window.parent.postMessage(message, '*');
diff --git a/third_party/blink/web_tests/http/tests/security/resources/agent-equality-data-url.html b/third_party/blink/web_tests/http/tests/security/resources/agent-equality-data-url.html
index 3e0a3d82..b6c03bc2 100644
--- a/third_party/blink/web_tests/http/tests/security/resources/agent-equality-data-url.html
+++ b/third_party/blink/web_tests/http/tests/security/resources/agent-equality-data-url.html
@@ -4,7 +4,7 @@
 <title>data scheme test</title>
 </head>
 <body>
-<iframe src="data:text/html,&lt;script&gt;window.parent.postMessage(internals.getDocumentAgentId(document), '*');&lt;/script&gt;">
+<iframe src="data:text/html,&lt;script&gt;window.parent.postMessage(internals.getAgentId(window), '*');&lt;/script&gt;">
 </iframe>
 <script>
 // This test loads an iframe whose src attribute is a data: URL. It posts
@@ -19,7 +19,7 @@
   let iframeAgentId = evt.data;
   let message = [
     'data scheme test',
-    internals.getDocumentAgentId(document),
+    internals.getAgentId(window),
     iframeAgentId
   ];
   window.parent.postMessage(message, '*');
diff --git a/third_party/blink/web_tests/http/tests/security/resources/agent-equality-different-schemes.html b/third_party/blink/web_tests/http/tests/security/resources/agent-equality-different-schemes.html
index ebb1d8ca..dd747d4 100644
--- a/third_party/blink/web_tests/http/tests/security/resources/agent-equality-different-schemes.html
+++ b/third_party/blink/web_tests/http/tests/security/resources/agent-equality-different-schemes.html
@@ -24,7 +24,7 @@
   let agentIdB = evt.data;
   let message = [
     'different-schemes test',
-    internals.getDocumentAgentId(document),
+    internals.getAgentId(window),
     agentIdB
   ];
   window.parent.postMessage(message, '*');
diff --git a/third_party/blink/web_tests/http/tests/security/resources/agent-equality-report-to-grandparent.html b/third_party/blink/web_tests/http/tests/security/resources/agent-equality-report-to-grandparent.html
index fae92de2..928d7a8 100644
--- a/third_party/blink/web_tests/http/tests/security/resources/agent-equality-report-to-grandparent.html
+++ b/third_party/blink/web_tests/http/tests/security/resources/agent-equality-report-to-grandparent.html
@@ -5,7 +5,7 @@
 </head>
 <body>
 <script>
-window.parent.parent.postMessage(internals.getDocumentAgentId(document), '*');
+window.parent.parent.postMessage(internals.getAgentId(window), '*');
 </script>
 </body>
 </html>
diff --git a/third_party/blink/web_tests/http/tests/security/resources/agent-equality-report-to-parent.html b/third_party/blink/web_tests/http/tests/security/resources/agent-equality-report-to-parent.html
index d391bcc..a10f0091f 100644
--- a/third_party/blink/web_tests/http/tests/security/resources/agent-equality-report-to-parent.html
+++ b/third_party/blink/web_tests/http/tests/security/resources/agent-equality-report-to-parent.html
@@ -5,7 +5,7 @@
 </head>
 <body>
 <script>
-window.parent.postMessage(internals.getDocumentAgentId(document), '*');
+window.parent.postMessage(internals.getAgentId(window), '*');
 </script>
 </body>
 </html>
diff --git a/third_party/blink/web_tests/http/tests/security/resources/agent-equality-same-origin-different-ports.html b/third_party/blink/web_tests/http/tests/security/resources/agent-equality-same-origin-different-ports.html
index ac90cb7..63985e7 100644
--- a/third_party/blink/web_tests/http/tests/security/resources/agent-equality-same-origin-different-ports.html
+++ b/third_party/blink/web_tests/http/tests/security/resources/agent-equality-same-origin-different-ports.html
@@ -24,7 +24,7 @@
   let agentIdB = evt.data;
   let message = [
     'same-origin different-ports test',
-    internals.getDocumentAgentId(document),
+    internals.getAgentId(window),
     agentIdB
   ];
   window.parent.postMessage(message, '*');
diff --git a/third_party/blink/web_tests/http/tests/security/resources/agent-equality-same-origin.html b/third_party/blink/web_tests/http/tests/security/resources/agent-equality-same-origin.html
index cbf641c..b7c290c 100644
--- a/third_party/blink/web_tests/http/tests/security/resources/agent-equality-same-origin.html
+++ b/third_party/blink/web_tests/http/tests/security/resources/agent-equality-same-origin.html
@@ -20,9 +20,9 @@
 
   let message = [
     'same-origin test',
-    internals.getDocumentAgentId(document),
-    internals.getDocumentAgentId(sub1.contentDocument),
-    internals.getDocumentAgentId(sub2.contentDocument)
+    internals.getAgentId(window),
+    internals.getAgentId(sub1.contentWindow),
+    internals.getAgentId(sub2.contentWindow)
   ];
 
   window.parent.postMessage(message, '*');
diff --git a/third_party/blink/web_tests/http/tests/security/resources/agent-equality-skip-level-same-origin.html b/third_party/blink/web_tests/http/tests/security/resources/agent-equality-skip-level-same-origin.html
index 615017d..eea75e2e 100644
--- a/third_party/blink/web_tests/http/tests/security/resources/agent-equality-skip-level-same-origin.html
+++ b/third_party/blink/web_tests/http/tests/security/resources/agent-equality-skip-level-same-origin.html
@@ -27,7 +27,7 @@
   let agentIdC = evt.data;
   let message = [
     'skip-level same-origin test',
-    internals.getDocumentAgentId(document),
+    internals.getAgentId(window),
     agentIdC
   ];
   window.parent.postMessage(message, '*');
diff --git a/third_party/blink/web_tests/http/tests/security/resources/agent-equality-srcdoc.html b/third_party/blink/web_tests/http/tests/security/resources/agent-equality-srcdoc.html
index be921d5..f56769bd 100644
--- a/third_party/blink/web_tests/http/tests/security/resources/agent-equality-srcdoc.html
+++ b/third_party/blink/web_tests/http/tests/security/resources/agent-equality-srcdoc.html
@@ -4,7 +4,7 @@
 <title>srcdoc iframe test</title>
 </head>
 <body>
-<iframe srcdoc="&lt;script&gt;window.parent.postMessage(internals.getDocumentAgentId(document), '*');&lt;/script&gt;">
+<iframe srcdoc="&lt;script&gt;window.parent.postMessage(internals.getAgentId(window), '*');&lt;/script&gt;">
 </iframe>
 <script>
 // This test loads an iframe whose content is specified as its srcdoc
@@ -20,7 +20,7 @@
   let iframeAgentId = evt.data;
   let message = [
     'srcdoc iframe test',
-    internals.getDocumentAgentId(document),
+    internals.getAgentId(window),
     iframeAgentId
   ];
   window.parent.postMessage(message, '*');
diff --git a/third_party/blink/web_tests/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt b/third_party/blink/web_tests/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
index e84328d3c..598855e 100644
--- a/third_party/blink/web_tests/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
+++ b/third_party/blink/web_tests/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
@@ -540,7 +540,6 @@
     attribute @@toStringTag
     method constructor
     method createWritable
-    method createWriter
     method getFile
 interface FileSystemHandle
     attribute @@toStringTag
diff --git a/third_party/blink/web_tests/platform/linux/virtual/dark-color-scheme/details_summary/details-with-summary-dark-mode-expected.png b/third_party/blink/web_tests/platform/linux/virtual/dark-color-scheme/details_summary/details-with-summary-dark-mode-expected.png
new file mode 100644
index 0000000..1feaf25
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/dark-color-scheme/details_summary/details-with-summary-dark-mode-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/dark-color-scheme/details_summary/details-with-summary-dark-mode-expected.png b/third_party/blink/web_tests/platform/mac/virtual/dark-color-scheme/details_summary/details-with-summary-dark-mode-expected.png
new file mode 100644
index 0000000..51782647
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac/virtual/dark-color-scheme/details_summary/details-with-summary-dark-mode-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/dark-color-scheme/details_summary/details-with-summary-dark-mode-expected.png b/third_party/blink/web_tests/platform/win/virtual/dark-color-scheme/details_summary/details-with-summary-dark-mode-expected.png
new file mode 100644
index 0000000..4a67d77
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win/virtual/dark-color-scheme/details_summary/details-with-summary-dark-mode-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/dark-color-scheme/details_summary/details-with-summary-dark-mode.html b/third_party/blink/web_tests/virtual/dark-color-scheme/details_summary/details-with-summary-dark-mode.html
new file mode 100644
index 0000000..3d6ccf9
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/dark-color-scheme/details_summary/details-with-summary-dark-mode.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+
+<meta name="color-scheme" content="dark">
+
+<div style="color:white">
+    <details style="color-scheme: dark">
+        <summary>summary</summary>
+    </details>
+</div>
+
+<div style="color:white">
+    <details open style="color-scheme: dark">
+        <summary>summary 2</summary>
+        Example text
+    </details>
+</div>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/virtual/threaded/external/wpt/scroll-animations/README.txt b/third_party/blink/web_tests/virtual/threaded/external/wpt/scroll-animations/README.txt
new file mode 100644
index 0000000..2e5dfc92
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/threaded/external/wpt/scroll-animations/README.txt
@@ -0,0 +1,2 @@
+# This suite runs external/wpt/scroll-animations tests with threaded
+# compositing enabled (--enable-threaded-compositing flag).
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-dedicated-worker-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-dedicated-worker-expected.txt
index 6050628f..79aceb8 100644
--- a/third_party/blink/web_tests/webexposed/global-interface-listing-dedicated-worker-expected.txt
+++ b/third_party/blink/web_tests/webexposed/global-interface-listing-dedicated-worker-expected.txt
@@ -475,7 +475,6 @@
 [Worker]     attribute @@toStringTag
 [Worker]     method constructor
 [Worker]     method createWritable
-[Worker]     method createWriter
 [Worker]     method getFile
 [Worker] interface FileSystemHandle
 [Worker]     attribute @@toStringTag
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
index e26fe0b..02fbcb288 100644
--- a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
+++ b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
@@ -2440,7 +2440,6 @@
     attribute @@toStringTag
     method constructor
     method createWritable
-    method createWriter
     method getFile
 interface FileSystemHandle
     attribute @@toStringTag
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-shared-worker-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-shared-worker-expected.txt
index 41c0ae7..b188c71 100644
--- a/third_party/blink/web_tests/webexposed/global-interface-listing-shared-worker-expected.txt
+++ b/third_party/blink/web_tests/webexposed/global-interface-listing-shared-worker-expected.txt
@@ -470,7 +470,6 @@
 [Worker]     attribute @@toStringTag
 [Worker]     method constructor
 [Worker]     method createWritable
-[Worker]     method createWriter
 [Worker]     method getFile
 [Worker] interface FileSystemHandle
 [Worker]     attribute @@toStringTag
diff --git a/third_party/closure_compiler/externs/passwords_private.js b/third_party/closure_compiler/externs/passwords_private.js
index fd001e3..b6beb5c 100644
--- a/third_party/closure_compiler/externs/passwords_private.js
+++ b/third_party/closure_compiler/externs/passwords_private.js
@@ -57,6 +57,7 @@
   NO_PASSWORDS: 'NO_PASSWORDS',
   TOO_MANY_PASSWORDS: 'TOO_MANY_PASSWORDS',
   QUOTA_LIMIT: 'QUOTA_LIMIT',
+  TOO_MANY_PASSWORDS_AND_QUOTA_LIMIT: 'TOO_MANY_PASSWORDS_AND_QUOTA_LIMIT',
   OTHER_ERROR: 'OTHER_ERROR',
 };
 
diff --git a/third_party/crashpad/README.chromium b/third_party/crashpad/README.chromium
index 6a015f5..fbc4a6a 100644
--- a/third_party/crashpad/README.chromium
+++ b/third_party/crashpad/README.chromium
@@ -2,7 +2,7 @@
 Short Name: crashpad
 URL: https://crashpad.chromium.org/
 Version: unknown
-Revision: 3c573b54ae138c81c0224f22b38f5d439b56f9a1
+Revision: 1d75af9bf5918fa1c365a4ac696f038e6028a30b
 License: Apache 2.0
 License File: crashpad/LICENSE
 Security Critical: yes
diff --git a/third_party/crashpad/crashpad/BUILD.gn b/third_party/crashpad/crashpad/BUILD.gn
index cba955dd..3d0dc26 100644
--- a/third_party/crashpad/crashpad/BUILD.gn
+++ b/third_party/crashpad/crashpad/BUILD.gn
@@ -36,12 +36,12 @@
 if (crashpad_is_in_chromium || crashpad_is_in_fuchsia) {
   test("crashpad_tests") {
     deps = [
+      "client:client_test",
       "test:gmock_main",
       "test:test_test",
     ]
     if (!crashpad_is_ios) {
       deps += [
-        "client:client_test",
         "handler:handler_test",
         "minidump:minidump_test",
         "snapshot:snapshot_test",
@@ -138,9 +138,7 @@
     }
 
     package("crashpad_database_util") {
-      deps = [
-        "tools:crashpad_database_util",
-      ]
+      deps = [ "tools:crashpad_database_util" ]
 
       binaries = [
         {
@@ -156,9 +154,6 @@
       "client:client_test",
       "test:gmock_main",
     ]
-    if (crashpad_is_ios) {
-      deps -= [ "client:client_test" ]
-    }
   }
 
   test("crashpad_handler_test") {
@@ -212,8 +207,6 @@
 if (crashpad_is_ios) {
   group("ios_xcuitests") {
     testonly = true
-    deps = [
-      "test/ios:all_tests",
-    ]
+    deps = [ "test/ios:all_tests" ]
   }
 }
diff --git a/third_party/crashpad/crashpad/DEPS b/third_party/crashpad/crashpad/DEPS
index 21dc3f0..f7355b78 100644
--- a/third_party/crashpad/crashpad/DEPS
+++ b/third_party/crashpad/crashpad/DEPS
@@ -42,7 +42,7 @@
       '7bde79cc274d06451bf65ae82c012a5d3e476b5a',
   'crashpad/third_party/mini_chromium/mini_chromium':
       Var('chromium_git') + '/chromium/mini_chromium@' +
-      'f8f1182adb804675b2aa4fb3ce03f6f884fae474',
+      'c426ff98e1d9e9d59777fe8b883a5c0ceeca9ca3',
   'crashpad/third_party/libfuzzer/src':
       Var('chromium_git') + '/chromium/llvm-project/compiler-rt/lib/fuzzer.git@' +
       'fda403cf93ecb8792cb1d061564d89a6553ca020',
diff --git a/third_party/crashpad/crashpad/build/run_tests.py b/third_party/crashpad/crashpad/build/run_tests.py
index 44620fb..38473cd 100755
--- a/third_party/crashpad/crashpad/build/run_tests.py
+++ b/third_party/crashpad/crashpad/build/run_tests.py
@@ -437,7 +437,7 @@
 
 
 def _RunOnIOSTarget(binary_dir, test, is_xcuitest=False):
-  """Runs the given iOS |test| app on iPhone X with the default OS version."""
+  """Runs the given iOS |test| app on iPhone 8 with the default OS version."""
 
   def xctest(binary_dir, test):
     """Returns a dict containing the xctestrun data needed to run an
@@ -496,7 +496,7 @@
 
     subprocess.check_call(['xcodebuild', 'test-without-building',
                            '-xctestrun', xctestrun_path, '-destination',
-                           'platform=iOS Simulator,name=iPhone X'])
+                           'platform=iOS Simulator,name=iPhone 8'])
 
 # This script is primarily used from the waterfall so that the list of tests
 # that are run is maintained in-tree, rather than in a separate infrastructure
diff --git a/third_party/crashpad/crashpad/client/BUILD.gn b/third_party/crashpad/crashpad/client/BUILD.gn
index 4ec0386..476ceb2c 100644
--- a/third_party/crashpad/crashpad/client/BUILD.gn
+++ b/third_party/crashpad/crashpad/client/BUILD.gn
@@ -93,6 +93,11 @@
     cflags = [ "/wd4201" ]  # nonstandard extension used : nameless struct/union
   }
 
+  # TODO(justincohen): Temporary dependency to bring up the iOS client.
+  if (crashpad_is_ios) {
+    deps += [ "../snapshot" ]
+  }
+
   if (crashpad_is_linux || crashpad_is_android) {
     deps += [ "../third_party/lss" ]
   }
@@ -126,6 +131,17 @@
     sources += [ "crashpad_client_win_test.cc" ]
   }
 
+  if (crashpad_is_ios) {
+    sources += [ "crashpad_client_ios_test.cc" ]
+    sources -= [
+      "annotation_list_test.cc",
+      "annotation_test.cc",
+      "crash_report_database_test.cc",
+      "prune_crash_reports_test.cc",
+      "settings_test.cc",
+    ]
+  }
+
   if (crashpad_is_linux || crashpad_is_android) {
     sources += [ "crashpad_client_linux_test.cc" ]
   }
diff --git a/third_party/crashpad/crashpad/client/crashpad_client.h b/third_party/crashpad/crashpad/client/crashpad_client.h
index e0cd2f1..7a6a18a 100644
--- a/third_party/crashpad/crashpad/client/crashpad_client.h
+++ b/third_party/crashpad/crashpad/client/crashpad_client.h
@@ -436,6 +436,13 @@
   //! TODO(justincohen): This method will need to take database, metrics_dir,
   //! url and annotations eventually.
   bool StartCrashpadInProcessHandler();
+
+  // TODO(justincohen): This method is purely for bringing up iOS interfaces.
+  //! \brief Requests that the handler capture a dump even though there hasn't
+  //!     been a crash.
+  //!
+  //! A handler must have already been installed before calling this method.
+  static void DumpWithoutCrash();
 #endif
 
 #if defined(OS_MACOSX) || DOXYGEN
diff --git a/third_party/crashpad/crashpad/client/crashpad_client_ios.cc b/third_party/crashpad/crashpad/client/crashpad_client_ios.cc
index c19152b80..8e3fafa 100644
--- a/third_party/crashpad/crashpad/client/crashpad_client_ios.cc
+++ b/third_party/crashpad/crashpad/client/crashpad_client_ios.cc
@@ -19,6 +19,7 @@
 #include "base/logging.h"
 #include "base/strings/stringprintf.h"
 #include "client/client_argv_handling.h"
+#include "snapshot/ios/process_snapshot_ios.h"
 #include "util/posix/signals.h"
 
 namespace crashpad {
@@ -39,13 +40,21 @@
         HandleSignal, 0, &old_actions_, unhandled_signals);
   }
 
+  void HandleCrash(int signo, siginfo_t* siginfo, void* context) {
+    // TODO(justincohen): This is incomplete.
+    ProcessSnapshotIOS process_snapshot;
+    process_snapshot.Initialize();
+  }
+
  private:
   SignalHandler() = default;
 
   // The base implementation for all signal handlers, suitable for calling
   // directly to simulate signal delivery.
-  void HandleCrash(int signo, siginfo_t* siginfo, void* context) {
-    // Do Something.
+  void HandleCrashAndReraiseSignal(int signo,
+                                   siginfo_t* siginfo,
+                                   void* context) {
+    HandleCrash(signo, siginfo, context);
 
     // Always call system handler.
     Signals::RestoreHandlerAndReraiseSignalOnReturn(
@@ -54,7 +63,7 @@
 
   // The signal handler installed at OS-level.
   static void HandleSignal(int signo, siginfo_t* siginfo, void* context) {
-    Get()->HandleCrash(signo, siginfo, context);
+    Get()->HandleCrashAndReraiseSignal(signo, siginfo, context);
   }
 
   Signals::OldActions old_actions_ = {};
@@ -72,4 +81,11 @@
   return SignalHandler::Get()->Install(nullptr);
 }
 
+// static
+void CrashpadClient::DumpWithoutCrash() {
+  DCHECK(SignalHandler::Get());
+
+  siginfo_t siginfo = {};
+  SignalHandler::Get()->HandleCrash(siginfo.si_signo, &siginfo, nullptr);
+}
 }  // namespace crashpad
diff --git a/third_party/crashpad/crashpad/client/crashpad_client_ios_test.cc b/third_party/crashpad/crashpad/client/crashpad_client_ios_test.cc
new file mode 100644
index 0000000..6ed4579
--- /dev/null
+++ b/third_party/crashpad/crashpad/client/crashpad_client_ios_test.cc
@@ -0,0 +1,32 @@
+// Copyright 2020 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "client/crashpad_client.h"
+
+#include "gtest/gtest.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+// TODO(justincohen): This is a placeholder.
+TEST(CrashpadIOSClient, DumpWithoutCrash) {
+  crashpad::CrashpadClient client;
+  client.StartCrashpadInProcessHandler();
+  client.DumpWithoutCrash();
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace crashpad
diff --git a/third_party/crashpad/crashpad/compat/non_win/dbghelp.h b/third_party/crashpad/crashpad/compat/non_win/dbghelp.h
index 5ce88b8..ded912e7 100644
--- a/third_party/crashpad/crashpad/compat/non_win/dbghelp.h
+++ b/third_party/crashpad/crashpad/compat/non_win/dbghelp.h
@@ -214,7 +214,7 @@
     //!     `cpuid 0x80000001` `edx`.
     //!
     //! This field is only valid if #VendorId identifies the CPU vendor as
-    //! “AuthenticAMD”.
+    //! “AuthenticAMD” or "HygonGenuine".
     uint32_t AMDExtendedCpuFeatures;
   } X86CpuInfo;
 
diff --git a/third_party/crashpad/crashpad/handler/linux/cros_crash_report_exception_handler.cc b/third_party/crashpad/crashpad/handler/linux/cros_crash_report_exception_handler.cc
index b394d61..92bc93e 100644
--- a/third_party/crashpad/crashpad/handler/linux/cros_crash_report_exception_handler.cc
+++ b/third_party/crashpad/crashpad/handler/linux/cros_crash_report_exception_handler.cc
@@ -225,7 +225,7 @@
   AddUserExtensionStreams(user_stream_data_sources_, snapshot, &minidump);
 
   FileWriter file_writer;
-  if (!file_writer.OpenMemfd(base::FilePath("/tmp/minidump"))) {
+  if (!file_writer.OpenMemfd(base::FilePath("minidump"))) {
     Metrics::ExceptionCaptureResult(Metrics::CaptureResult::kOpenMemfdFailed);
     return false;
   }
diff --git a/third_party/crashpad/crashpad/minidump/minidump_system_info_writer.cc b/third_party/crashpad/crashpad/minidump/minidump_system_info_writer.cc
index 9bc013d..06aeecda 100644
--- a/third_party/crashpad/crashpad/minidump/minidump_system_info_writer.cc
+++ b/third_party/crashpad/crashpad/minidump/minidump_system_info_writer.cc
@@ -151,7 +151,7 @@
     SetCPUX86VersionAndFeatures(system_snapshot->CPUX86Signature(),
                                 system_snapshot->CPUX86Features() & 0xffffffff);
 
-    if (cpu_vendor == "AuthenticAMD") {
+    if (cpu_vendor == "AuthenticAMD" || cpu_vendor == "HygonGenuine") {
       SetCPUX86AMDExtendedFeatures(
           system_snapshot->CPUX86ExtendedFeatures() & 0xffffffff);
     }
diff --git a/third_party/crashpad/crashpad/snapshot/BUILD.gn b/third_party/crashpad/crashpad/snapshot/BUILD.gn
index f13616f6..32d69a4 100644
--- a/third_party/crashpad/crashpad/snapshot/BUILD.gn
+++ b/third_party/crashpad/crashpad/snapshot/BUILD.gn
@@ -108,6 +108,15 @@
     ]
   }
 
+  if (crashpad_is_ios) {
+    sources += [
+      "ios/module_snapshot_ios.cc",
+      "ios/module_snapshot_ios.h",
+      "ios/process_snapshot_ios.cc",
+      "ios/process_snapshot_ios.h",
+    ]
+  }
+
   if (crashpad_is_linux || crashpad_is_android) {
     set_sources_assignment_filter([])
     sources += [
@@ -230,6 +239,10 @@
     "../util",
   ]
 
+  if (crashpad_is_ios) {
+    deps -= [ "../client" ]
+  }
+
   if (crashpad_is_win) {
     cflags = [ "/wd4201" ]  # nonstandard extension used : nameless struct/union
     libs = [ "powrprof.lib" ]
diff --git a/third_party/crashpad/crashpad/snapshot/ios/module_snapshot_ios.cc b/third_party/crashpad/crashpad/snapshot/ios/module_snapshot_ios.cc
new file mode 100644
index 0000000..8b251eb
--- /dev/null
+++ b/third_party/crashpad/crashpad/snapshot/ios/module_snapshot_ios.cc
@@ -0,0 +1,238 @@
+// Copyright 2020 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "snapshot/ios/module_snapshot_ios.h"
+
+#include <mach-o/loader.h>
+#include <mach/mach.h>
+
+#include "base/files/file_path.h"
+#include "base/mac/mach_logging.h"
+#include "util/misc/from_pointer_cast.h"
+#include "util/misc/uuid.h"
+
+namespace crashpad {
+namespace internal {
+
+ModuleSnapshotIOS::ModuleSnapshotIOS()
+    : ModuleSnapshot(),
+      name_(),
+      address_(0),
+      size_(0),
+      timestamp_(0),
+      dylib_version_(0),
+      source_version_(0),
+      filetype_(0),
+      initialized_() {}
+
+ModuleSnapshotIOS::~ModuleSnapshotIOS() {}
+
+// static.
+const dyld_all_image_infos* ModuleSnapshotIOS::DyldAllImageInfo() {
+  task_dyld_info_data_t dyld_info;
+  mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT;
+
+  kern_return_t kr = task_info(mach_task_self(),
+                               TASK_DYLD_INFO,
+                               reinterpret_cast<task_info_t>(&dyld_info),
+                               &count);
+  if (kr != KERN_SUCCESS) {
+    MACH_LOG(WARNING, kr) << "task_info";
+    return 0;
+  }
+
+  return reinterpret_cast<dyld_all_image_infos*>(dyld_info.all_image_info_addr);
+}
+
+bool ModuleSnapshotIOS::InitializeDyld(const dyld_all_image_infos* images) {
+  INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
+
+  name_ = images->dyldPath;
+  address_ = FromPointerCast<uint64_t>(images->dyldImageLoadAddress);
+  return FinishInitialization();
+}
+
+bool ModuleSnapshotIOS::Initialize(const dyld_image_info* image) {
+  INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
+
+  name_ = image->imageFilePath;
+  address_ = FromPointerCast<uint64_t>(image->imageLoadAddress);
+  timestamp_ = image->imageFileModDate;
+  return FinishInitialization();
+}
+
+bool ModuleSnapshotIOS::FinishInitialization() {
+#ifndef ARCH_CPU_64_BITS
+#error Only 64-bit Mach-O is supported
+#endif
+  DCHECK(address_);
+  const mach_header_64* header =
+      reinterpret_cast<const mach_header_64*>(address_);
+  const load_command* command =
+      reinterpret_cast<const load_command*>(header + 1);
+  // Make sure that the basic load command structure doesn’t overflow the
+  // space allotted for load commands, as well as iterating through ncmds.
+  for (uint32_t cmd_index = 0, cumulative_cmd_size = 0;
+       cmd_index <= header->ncmds && cumulative_cmd_size < header->sizeofcmds;
+       ++cmd_index, cumulative_cmd_size += command->cmdsize) {
+    if (command->cmd == LC_SEGMENT_64) {
+      const segment_command_64* segment =
+          reinterpret_cast<const segment_command_64*>(command);
+      if (strcmp(segment->segname, SEG_TEXT) == 0) {
+        size_ = segment->vmsize;
+      }
+    } else if (command->cmd == LC_ID_DYLIB) {
+      const dylib_command* dylib =
+          reinterpret_cast<const dylib_command*>(command);
+      dylib_version_ = dylib->dylib.current_version;
+    } else if (command->cmd == LC_SOURCE_VERSION) {
+      const source_version_command* source_version =
+          reinterpret_cast<const source_version_command*>(command);
+      source_version_ = source_version->version;
+    } else if (command->cmd == LC_UUID) {
+      const uuid_command* uuid = reinterpret_cast<const uuid_command*>(command);
+      uuid_.InitializeFromBytes(uuid->uuid);
+    }
+
+    command = reinterpret_cast<const load_command*>(
+        reinterpret_cast<const uint8_t*>(command) + command->cmdsize);
+
+    // TODO(justincohen): Warn-able things:
+    // - Bad Mach-O magic (and give up trying to process the module)
+    // - Unrecognized Mach-O type
+    // - No SEG_TEXT
+    // - More than one SEG_TEXT
+    // - More than one LC_ID_DYLIB, LC_SOURCE_VERSION, or LC_UUID
+    // - No LC_ID_DYLIB in a dylib file
+    // - LC_ID_DYLIB in a non-dylib file
+    // And more optional:
+    // - Missing LC_UUID (although it leaves us with a big "?")
+    // - Missing LC_SOURCE_VERSION.
+  }
+
+  filetype_ = header->filetype;
+
+  INITIALIZATION_STATE_SET_VALID(initialized_);
+  return true;
+}
+
+std::string ModuleSnapshotIOS::Name() const {
+  INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+  return name_;
+}
+
+uint64_t ModuleSnapshotIOS::Address() const {
+  INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+  return address_;
+}
+
+uint64_t ModuleSnapshotIOS::Size() const {
+  INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+  return size_;
+}
+
+time_t ModuleSnapshotIOS::Timestamp() const {
+  INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+  return timestamp_;
+}
+
+void ModuleSnapshotIOS::FileVersion(uint16_t* version_0,
+                                    uint16_t* version_1,
+                                    uint16_t* version_2,
+                                    uint16_t* version_3) const {
+  INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+  if (filetype_ == MH_DYLIB) {
+    *version_0 = (dylib_version_ & 0xffff0000) >> 16;
+    *version_1 = (dylib_version_ & 0x0000ff00) >> 8;
+    *version_2 = (dylib_version_ & 0x000000ff);
+    *version_3 = 0;
+  } else {
+    *version_0 = 0;
+    *version_1 = 0;
+    *version_2 = 0;
+    *version_3 = 0;
+  }
+}
+
+void ModuleSnapshotIOS::SourceVersion(uint16_t* version_0,
+                                      uint16_t* version_1,
+                                      uint16_t* version_2,
+                                      uint16_t* version_3) const {
+  INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+  *version_0 = (source_version_ & 0xffff000000000000u) >> 48;
+  *version_1 = (source_version_ & 0x0000ffff00000000u) >> 32;
+  *version_2 = (source_version_ & 0x00000000ffff0000u) >> 16;
+  *version_3 = source_version_ & 0x000000000000ffffu;
+}
+
+ModuleSnapshot::ModuleType ModuleSnapshotIOS::GetModuleType() const {
+  INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+  switch (filetype_) {
+    case MH_EXECUTE:
+      return kModuleTypeExecutable;
+    case MH_DYLIB:
+      return kModuleTypeSharedLibrary;
+    case MH_DYLINKER:
+      return kModuleTypeDynamicLoader;
+    case MH_BUNDLE:
+      return kModuleTypeLoadableModule;
+    default:
+      return kModuleTypeUnknown;
+  }
+}
+
+void ModuleSnapshotIOS::UUIDAndAge(crashpad::UUID* uuid, uint32_t* age) const {
+  INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+  *uuid = uuid_;
+  *age = 0;
+}
+
+std::string ModuleSnapshotIOS::DebugFileName() const {
+  INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+  return base::FilePath(Name()).BaseName().value();
+}
+
+std::vector<uint8_t> ModuleSnapshotIOS::BuildID() const {
+  INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+  return std::vector<uint8_t>();
+}
+
+std::vector<std::string> ModuleSnapshotIOS::AnnotationsVector() const {
+  INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+  return std::vector<std::string>();
+}
+
+std::map<std::string, std::string> ModuleSnapshotIOS::AnnotationsSimpleMap()
+    const {
+  INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+  return std::map<std::string, std::string>();
+}
+
+std::vector<AnnotationSnapshot> ModuleSnapshotIOS::AnnotationObjects() const {
+  INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+  return std::vector<AnnotationSnapshot>();
+}
+
+std::set<CheckedRange<uint64_t>> ModuleSnapshotIOS::ExtraMemoryRanges() const {
+  INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+  return std::set<CheckedRange<uint64_t>>();
+}
+
+std::vector<const UserMinidumpStream*>
+ModuleSnapshotIOS::CustomMinidumpStreams() const {
+  return std::vector<const UserMinidumpStream*>();
+}
+
+}  // namespace internal
+}  // namespace crashpad
diff --git a/third_party/crashpad/crashpad/snapshot/ios/module_snapshot_ios.h b/third_party/crashpad/crashpad/snapshot/ios/module_snapshot_ios.h
new file mode 100644
index 0000000..7d0bbdc
--- /dev/null
+++ b/third_party/crashpad/crashpad/snapshot/ios/module_snapshot_ios.h
@@ -0,0 +1,112 @@
+// Copyright 2020 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_SNAPSHOT_IOS_MODULE_SNAPSHOT_IOS_H_
+#define CRASHPAD_SNAPSHOT_IOS_MODULE_SNAPSHOT_IOS_H_
+
+#include <mach-o/dyld_images.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "client/crashpad_info.h"
+#include "snapshot/crashpad_info_client_options.h"
+#include "snapshot/module_snapshot.h"
+#include "util/misc/initialization_state_dcheck.h"
+
+namespace crashpad {
+namespace internal {
+
+//! \brief A ModuleSnapshot of a code module (binary image) loaded into a
+//!     running (or crashed) process on an iOS system.
+class ModuleSnapshotIOS final : public ModuleSnapshot {
+ public:
+  ModuleSnapshotIOS();
+  ~ModuleSnapshotIOS() override;
+
+  // TODO(justincohen): This function is temporary, and will be broken into two
+  // parts.  One to do an in-process dump of all the relevant information, and
+  // two to initialize the snapshot after the in-process dump is loaded.
+  //! \brief Initializes the object.
+  //!
+  //! \param[in] image The mach-o image to be loaded.
+  //!
+  //! \return `true` if the snapshot could be created.
+  bool Initialize(const dyld_image_info* image);
+
+  // TODO(justincohen): This function is temporary, and will be broken into two
+  // parts.  One to do an in-process dump of all the relevant information, and
+  // two to initialize the snapshot after the in-process dump is loaded.
+  //! \brief Initializes the object specifically for the dyld module.
+  //!
+  //! \param[in] images The structure containing the necessary dyld information.
+  //!
+  //! \return `true` if the snapshot could be created.
+  bool InitializeDyld(const dyld_all_image_infos* images);
+
+  //! \brief Returns options from the module’s CrashpadInfo structure.
+  //!
+  //! \param[out] options Options set in the module’s CrashpadInfo structure.
+  void GetCrashpadOptions(CrashpadInfoClientOptions* options);
+
+  static const dyld_all_image_infos* DyldAllImageInfo();
+
+  // ModuleSnapshot:
+  std::string Name() const override;
+  uint64_t Address() const override;
+  uint64_t Size() const override;
+  time_t Timestamp() const override;
+  void FileVersion(uint16_t* version_0,
+                   uint16_t* version_1,
+                   uint16_t* version_2,
+                   uint16_t* version_3) const override;
+  void SourceVersion(uint16_t* version_0,
+                     uint16_t* version_1,
+                     uint16_t* version_2,
+                     uint16_t* version_3) const override;
+  ModuleType GetModuleType() const override;
+  void UUIDAndAge(UUID* uuid, uint32_t* age) const override;
+  std::string DebugFileName() const override;
+  std::vector<uint8_t> BuildID() const override;
+  std::vector<std::string> AnnotationsVector() const override;
+  std::map<std::string, std::string> AnnotationsSimpleMap() const override;
+  std::vector<AnnotationSnapshot> AnnotationObjects() const override;
+  std::set<CheckedRange<uint64_t>> ExtraMemoryRanges() const override;
+  std::vector<const UserMinidumpStream*> CustomMinidumpStreams() const override;
+
+ private:
+  // Gather the the module information based off of a mach_header_64 |address_|.
+  bool FinishInitialization();
+
+  std::string name_;
+  uint64_t address_;
+  uint64_t size_;
+  time_t timestamp_;
+  uint32_t dylib_version_;
+  uint64_t source_version_;
+  uint32_t filetype_;
+  UUID uuid_;
+  InitializationStateDcheck initialized_;
+
+  DISALLOW_COPY_AND_ASSIGN(ModuleSnapshotIOS);
+};
+
+}  // namespace internal
+}  // namespace crashpad
+
+#endif  // CRASHPAD_SNAPSHOT_IOS_MODULE_SNAPSHOT_IOS_H_
diff --git a/third_party/crashpad/crashpad/snapshot/ios/process_snapshot_ios.cc b/third_party/crashpad/crashpad/snapshot/ios/process_snapshot_ios.cc
new file mode 100644
index 0000000..666fc19
--- /dev/null
+++ b/third_party/crashpad/crashpad/snapshot/ios/process_snapshot_ios.cc
@@ -0,0 +1,162 @@
+// Copyright 2020 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "snapshot/ios/process_snapshot_ios.h"
+
+#include <mach-o/loader.h>
+#include <mach/mach.h>
+
+#include <utility>
+
+#include "base/logging.h"
+#include "base/mac/mach_logging.h"
+
+namespace crashpad {
+
+ProcessSnapshotIOS::ProcessSnapshotIOS()
+    : ProcessSnapshot(),
+      modules_(),
+      report_id_(),
+      client_id_(),
+      annotations_simple_map_(),
+      snapshot_time_(),
+      initialized_() {}
+
+ProcessSnapshotIOS::~ProcessSnapshotIOS() {}
+
+bool ProcessSnapshotIOS::Initialize() {
+  INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
+
+  if (gettimeofday(&snapshot_time_, nullptr) != 0) {
+    PLOG(ERROR) << "gettimeofday";
+    return false;
+  }
+
+  InitializeModules();
+
+  INITIALIZATION_STATE_SET_VALID(initialized_);
+  return true;
+}
+
+pid_t ProcessSnapshotIOS::ProcessID() const {
+  INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+  return getpid();
+}
+
+pid_t ProcessSnapshotIOS::ParentProcessID() const {
+  INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+  return 0;
+}
+
+void ProcessSnapshotIOS::SnapshotTime(timeval* snapshot_time) const {
+  INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+  *snapshot_time = snapshot_time_;
+}
+
+void ProcessSnapshotIOS::ProcessStartTime(timeval* start_time) const {
+  INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+}
+
+void ProcessSnapshotIOS::ProcessCPUTimes(timeval* user_time,
+                                         timeval* system_time) const {
+  INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+}
+
+void ProcessSnapshotIOS::ReportID(UUID* report_id) const {
+  INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+  *report_id = report_id_;
+}
+
+void ProcessSnapshotIOS::ClientID(UUID* client_id) const {
+  INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+  *client_id = client_id_;
+}
+
+const std::map<std::string, std::string>&
+ProcessSnapshotIOS::AnnotationsSimpleMap() const {
+  INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+  return annotations_simple_map_;
+}
+
+const SystemSnapshot* ProcessSnapshotIOS::System() const {
+  INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+  return nullptr;
+}
+
+std::vector<const ThreadSnapshot*> ProcessSnapshotIOS::Threads() const {
+  INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+  return std::vector<const ThreadSnapshot*>();
+}
+
+std::vector<const ModuleSnapshot*> ProcessSnapshotIOS::Modules() const {
+  INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+  std::vector<const ModuleSnapshot*> modules;
+  for (const auto& module : modules_) {
+    modules.push_back(module.get());
+  }
+  return modules;
+}
+
+std::vector<UnloadedModuleSnapshot> ProcessSnapshotIOS::UnloadedModules()
+    const {
+  INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+  return std::vector<UnloadedModuleSnapshot>();
+}
+
+const ExceptionSnapshot* ProcessSnapshotIOS::Exception() const {
+  INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+  return nullptr;
+}
+
+std::vector<const MemoryMapRegionSnapshot*> ProcessSnapshotIOS::MemoryMap()
+    const {
+  INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+  return std::vector<const MemoryMapRegionSnapshot*>();
+}
+
+std::vector<HandleSnapshot> ProcessSnapshotIOS::Handles() const {
+  INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+  return std::vector<HandleSnapshot>();
+}
+
+std::vector<const MemorySnapshot*> ProcessSnapshotIOS::ExtraMemory() const {
+  INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+  return std::vector<const MemorySnapshot*>();
+}
+
+const ProcessMemory* ProcessSnapshotIOS::Memory() const {
+  INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+  return nullptr;
+}
+
+void ProcessSnapshotIOS::InitializeModules() {
+  const dyld_all_image_infos* image_infos =
+      internal::ModuleSnapshotIOS::DyldAllImageInfo();
+
+  uint32_t image_count = image_infos->infoArrayCount;
+  const dyld_image_info* image_array = image_infos->infoArray;
+  for (uint32_t image_index = 0; image_index < image_count; ++image_index) {
+    const dyld_image_info* image = &image_array[image_index];
+    auto module = std::make_unique<internal::ModuleSnapshotIOS>();
+    if (module->Initialize(image)) {
+      modules_.push_back(std::move(module));
+    }
+  }
+  auto module = std::make_unique<internal::ModuleSnapshotIOS>();
+  if (module->InitializeDyld(image_infos)) {
+    modules_.push_back(std::move(module));
+  }
+}
+
+}  // namespace crashpad
diff --git a/third_party/crashpad/crashpad/snapshot/ios/process_snapshot_ios.h b/third_party/crashpad/crashpad/snapshot/ios/process_snapshot_ios.h
new file mode 100644
index 0000000..4ebcaf13
--- /dev/null
+++ b/third_party/crashpad/crashpad/snapshot/ios/process_snapshot_ios.h
@@ -0,0 +1,75 @@
+// Copyright 2020 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_SNAPSHOT_IOS_PROCESS_SNAPSHOT_IOS_H_
+#define CRASHPAD_SNAPSHOT_IOS_PROCESS_SNAPSHOT_IOS_H_
+
+#include <vector>
+
+#include "snapshot/ios/module_snapshot_ios.h"
+#include "snapshot/process_snapshot.h"
+#include "snapshot/unloaded_module_snapshot.h"
+
+namespace crashpad {
+
+//! \brief A ProcessSnapshot of a running (or crashed) process running on a
+//!     iphoneOS system.
+class ProcessSnapshotIOS final : public ProcessSnapshot {
+ public:
+  ProcessSnapshotIOS();
+  ~ProcessSnapshotIOS() override;
+
+  //! \brief Initializes the object.
+  //!
+  //! \return `true` if the snapshot could be created, `false` otherwise with
+  //!     an appropriate message logged.
+  bool Initialize();
+
+  // ProcessSnapshot:
+  pid_t ProcessID() const override;
+  pid_t ParentProcessID() const override;
+  void SnapshotTime(timeval* snapshot_time) const override;
+  void ProcessStartTime(timeval* start_time) const override;
+  void ProcessCPUTimes(timeval* user_time, timeval* system_time) const override;
+  void ReportID(UUID* report_id) const override;
+  void ClientID(UUID* client_id) const override;
+  const std::map<std::string, std::string>& AnnotationsSimpleMap()
+      const override;
+  const SystemSnapshot* System() const override;
+  std::vector<const ThreadSnapshot*> Threads() const override;
+  std::vector<const ModuleSnapshot*> Modules() const override;
+  std::vector<UnloadedModuleSnapshot> UnloadedModules() const override;
+  const ExceptionSnapshot* Exception() const override;
+  std::vector<const MemoryMapRegionSnapshot*> MemoryMap() const override;
+  std::vector<HandleSnapshot> Handles() const override;
+  std::vector<const MemorySnapshot*> ExtraMemory() const override;
+  const ProcessMemory* Memory() const override;
+
+ private:
+  // Initializes modules_ on behalf of Initialize().
+  void InitializeModules();
+
+  std::vector<std::unique_ptr<internal::ModuleSnapshotIOS>> modules_;
+  UUID report_id_;
+  UUID client_id_;
+  std::map<std::string, std::string> annotations_simple_map_;
+  timeval snapshot_time_;
+  InitializationStateDcheck initialized_;
+
+  DISALLOW_COPY_AND_ASSIGN(ProcessSnapshotIOS);
+};
+
+}  // namespace crashpad
+
+#endif  // CRASHPAD_SNAPSHOT_IOS_PROCESS_SNAPSHOT_IOS_H_
diff --git a/third_party/crashpad/crashpad/snapshot/linux/system_snapshot_linux_test.cc b/third_party/crashpad/crashpad/snapshot/linux/system_snapshot_linux_test.cc
index 46d3845f..f3013b5 100644
--- a/third_party/crashpad/crashpad/snapshot/linux/system_snapshot_linux_test.cc
+++ b/third_party/crashpad/crashpad/snapshot/linux/system_snapshot_linux_test.cc
@@ -77,7 +77,8 @@
 
   EXPECT_PRED1(
       [](std::string vendor) {
-        return vendor == "GenuineIntel" || vendor == "AuthenticAMD";
+        return vendor == "GenuineIntel" || vendor == "AuthenticAMD" ||
+               vendor == "HygonGenuine";
       },
       system.CPUVendor());
 
diff --git a/third_party/crashpad/crashpad/util/file/file_io.h b/third_party/crashpad/crashpad/util/file/file_io.h
index 3c956cc..6fa0f96d 100644
--- a/third_party/crashpad/crashpad/util/file/file_io.h
+++ b/third_party/crashpad/crashpad/util/file/file_io.h
@@ -399,16 +399,27 @@
                                    FilePermissions permissions);
 
 #if defined(OS_LINUX)
-//! \brief Wraps memfd_create(), logging an error if the operation fails.
-//!     Unlike other file open operations, this doesn't set `O_CLOEXEC`.
+//! \brief Opens an in-memory file for input and output.
 //!
-//! \return The newly opened FileHandle, or an invalid FileHandle on failure.
+//! This function first attempts to open the file with `memfd_create()`. If
+//! `memfd_create()` isn't supported by the kernel, this function next attempts
+//! to open a file using `O_TMPFILE`. If `O_TMPFILE` isn't supported, this
+//! function finally falls back to creating a file with a randomized name in
+//! `/tmp` and immediately `unlink()`ing it.
+//!
+//! Unlike other file open operations, this function doesn't set `O_CLOEXEC`.
+//!
+//! \param name A name associated with the file. This name does not indicate any
+//!     exact path and may not be used at all, depending on the strategy used to
+//!     create the file. The name should not contain any '/' characters.
+//! \return The newly opened FileHandle, or an invalid FileHandle on failure,
+//!     with a message logged.
 //!
 //! \sa ScopedFileHandle
 //! \sa LoggingOpenFileForRead
 //! \sa LoggingOpenFileForWrite
 //! \sa LoggingOpenFileForReadAndWrite
-FileHandle LoggingOpenMemFileForWrite(const base::FilePath& path);
+FileHandle LoggingOpenMemoryFileForReadAndWrite(const base::FilePath& name);
 #endif  // OS_LINUX
 
 //! \brief Wraps OpenFileForReadAndWrite(), logging an error if the operation
diff --git a/third_party/crashpad/crashpad/util/file/file_io_posix.cc b/third_party/crashpad/crashpad/util/file/file_io_posix.cc
index b72a48eb..91b252a 100644
--- a/third_party/crashpad/crashpad/util/file/file_io_posix.cc
+++ b/third_party/crashpad/crashpad/util/file/file_io_posix.cc
@@ -14,6 +14,7 @@
 
 #include "util/file/file_io.h"
 
+#include <errno.h>
 #include <fcntl.h>
 #include <sys/file.h>
 #include <sys/mman.h>
@@ -26,7 +27,9 @@
 #include "base/files/file_path.h"
 #include "base/logging.h"
 #include "base/posix/eintr_wrapper.h"
+#include "base/strings/stringprintf.h"
 #include "build/build_config.h"
+#include "util/misc/random_string.h"
 
 namespace crashpad {
 
@@ -98,13 +101,6 @@
            flags,
            permissions == FilePermissions::kWorldReadable ? 0644 : 0600));
 }
-
-#if defined(OS_LINUX)
-FileHandle OpenMemFileForOutput(const base::FilePath& path) {
-  return HANDLE_EINTR(memfd_create(path.value().c_str(), 0));
-}
-#endif
-
 }  // namespace
 
 namespace internal {
@@ -157,10 +153,49 @@
 }
 
 #if defined(OS_LINUX)
-FileHandle LoggingOpenMemFileForWrite(const base::FilePath& path) {
-  FileHandle fd = OpenMemFileForOutput(path);
-  PLOG_IF(ERROR, fd < 0) << "memfd_create " << path.value();
-  return fd;
+FileHandle LoggingOpenMemoryFileForReadAndWrite(const base::FilePath& name) {
+  DCHECK(name.value().find('/') == std::string::npos);
+
+  int result = HANDLE_EINTR(memfd_create(name.value().c_str(), 0));
+  if (result >= 0 || errno != ENOSYS) {
+    PLOG_IF(ERROR, result < 0) << "memfd_create";
+    return result;
+  }
+
+  const char* tmp = getenv("TMPDIR");
+  tmp = tmp ? tmp : "/tmp";
+
+  result = HANDLE_EINTR(open(tmp, O_RDWR | O_EXCL | O_TMPFILE, 0600));
+  if (result >= 0 ||
+      // These are the expected possible error codes indicating that O_TMPFILE
+      // doesn't have kernel or filesystem support. O_TMPFILE was added in Linux
+      // 3.11. Experimentation confirms that at least Linux 2.6.29 and Linux
+      // 3.10 set errno to EISDIR. EOPNOTSUPP is returned when the filesystem
+      // doesn't support O_TMPFILE. The man pages also mention ENOENT as an
+      // error code to check, but the language implies it would only occur when
+      // |tmp| is also an invalid directory. EINVAL is mentioned as a possible
+      // error code for any invalid values in flags, but O_TMPFILE isn't
+      // mentioned explicitly in this context and hasn't been observed in
+      // practice.
+      (errno != EISDIR && errno != EOPNOTSUPP)) {
+    PLOG_IF(ERROR, result < 0) << "open";
+    return result;
+  }
+
+  std::string path = base::StringPrintf("%s/%s.%d.%s",
+                                        tmp,
+                                        name.value().c_str(),
+                                        getpid(),
+                                        RandomString().c_str());
+  result = HANDLE_EINTR(open(path.c_str(), O_RDWR | O_CREAT | O_EXCL, 0600));
+  if (result < 0) {
+    PLOG(ERROR) << "open";
+    return result;
+  }
+  if (unlink(path.c_str()) != 0) {
+    PLOG(WARNING) << "unlink";
+  }
+  return result;
 }
 #endif
 
diff --git a/third_party/crashpad/crashpad/util/file/file_io_test.cc b/third_party/crashpad/crashpad/util/file/file_io_test.cc
index 0fdd25c..0efded8 100644
--- a/third_party/crashpad/crashpad/util/file/file_io_test.cc
+++ b/third_party/crashpad/crashpad/util/file/file_io_test.cc
@@ -473,6 +473,23 @@
   TestOpenFileForWrite(LoggingOpenFileForReadAndWrite);
 }
 
+#if defined(OS_LINUX)
+TEST(FileIO, LoggingOpenMemoryFileForReadAndWrite) {
+  ScopedFileHandle handle(
+      LoggingOpenMemoryFileForReadAndWrite(base::FilePath("memfile")));
+  ASSERT_TRUE(handle.is_valid());
+
+  static constexpr char kTestData[] = "somedata";
+  ASSERT_TRUE(LoggingWriteFile(handle.get(), kTestData, sizeof(kTestData)));
+
+  ASSERT_EQ(LoggingSeekFile(handle.get(), 0, SEEK_SET), 0);
+
+  char buffer[sizeof(kTestData)];
+  ASSERT_TRUE(LoggingReadFileExactly(handle.get(), buffer, sizeof(buffer)));
+  EXPECT_EQ(memcmp(buffer, kTestData, sizeof(buffer)), 0);
+}
+#endif  // OS_LINUX
+
 enum class ReadOrWrite : bool {
   kRead,
   kWrite,
diff --git a/third_party/crashpad/crashpad/util/file/file_writer.cc b/third_party/crashpad/crashpad/util/file/file_writer.cc
index de28575d..6dff975a 100644
--- a/third_party/crashpad/crashpad/util/file/file_writer.cc
+++ b/third_party/crashpad/crashpad/util/file/file_writer.cc
@@ -174,7 +174,7 @@
 #if defined(OS_LINUX)
 bool FileWriter::OpenMemfd(const base::FilePath& path) {
   CHECK(!file_.is_valid());
-  file_.reset(LoggingOpenMemFileForWrite(path));
+  file_.reset(LoggingOpenMemoryFileForReadAndWrite(path));
   if (!file_.is_valid()) {
     return false;
   }
diff --git a/third_party/crashpad/crashpad/util/file/file_writer.h b/third_party/crashpad/crashpad/util/file/file_writer.h
index 663adff..4b99b37 100644
--- a/third_party/crashpad/crashpad/util/file/file_writer.h
+++ b/third_party/crashpad/crashpad/util/file/file_writer.h
@@ -132,7 +132,7 @@
             FilePermissions permissions);
 
 #if defined(OS_LINUX)
-  //! \brief Wraps LoggingOpenMemFileForWrite().
+  //! \brief Wraps LoggingOpenMemoryFileForWrite().
   //!
   //! \return `true` if the operation succeeded, `false` if it failed, with an
   //!     error message logged.
diff --git a/tools/check_grd_for_unused_strings.py b/tools/check_grd_for_unused_strings.py
index 8223c429..d0b60da 100755
--- a/tools/check_grd_for_unused_strings.py
+++ b/tools/check_grd_for_unused_strings.py
@@ -129,7 +129,7 @@
   # If no GRD files were given, default them:
   if len(grd_files) == 0:
     ash_base_dir = os.path.join(src_dir, 'ash')
-    ash_components_dir = os.path.join(ash_base_dir, 'components')
+    ash_shortcut_viewer_dir = os.path.join(ash_base_dir, 'shortcut_viewer')
     chrome_dir = os.path.join(src_dir, 'chrome')
     chrome_app_dir = os.path.join(chrome_dir, 'app')
     chrome_app_res_dir = os.path.join(chrome_app_dir, 'resources')
@@ -140,7 +140,7 @@
     ui_chromeos_dir = os.path.join(ui_dir, 'chromeos')
     grd_files = [
       os.path.join(ash_base_dir, 'ash_strings.grd'),
-      os.path.join(ash_components_dir, 'ash_components_strings.grd'),
+      os.path.join(ash_shortcut_viewer_dir, 'ash_components_strings.grd'),
       os.path.join(chrome_app_dir, 'chromium_strings.grd'),
       os.path.join(chrome_app_dir, 'generated_resources.grd'),
       os.path.join(chrome_app_dir, 'google_chrome_strings.grd'),
diff --git a/tools/gritsettings/resource_ids.spec b/tools/gritsettings/resource_ids.spec
index 3f2d1cd..f8e27f6 100644
--- a/tools/gritsettings/resource_ids.spec
+++ b/tools/gritsettings/resource_ids.spec
@@ -466,7 +466,7 @@
   "ash/ash_strings.grd": {
     "messages": [3060],
   },
-  "ash/components/ash_components_strings.grd": {
+  "ash/shortcut_viewer/ash_components_strings.grd": {
     "messages": [3080],
   },
   "ash/keyboard/ui/keyboard_resources.grd": {
diff --git a/tools/gritsettings/translation_expectations.pyl b/tools/gritsettings/translation_expectations.pyl
index fce1864a..667b0b48a 100644
--- a/tools/gritsettings/translation_expectations.pyl
+++ b/tools/gritsettings/translation_expectations.pyl
@@ -25,7 +25,7 @@
       "android_webview/java/strings/android_webview_strings.grd",
       "android_webview/ui/aw_strings.grd",
       "ash/ash_strings.grd",
-      "ash/components/ash_components_strings.grd",
+      "ash/shortcut_viewer/ash_components_strings.grd",
       "chrome/android/features/vr/java/strings/android_chrome_vr_strings.grd",
       "chrome/android/features/media_router/java/strings/android_chrome_media_router_strings.grd",
       "chrome/android/features/keyboard_accessory/internal/java/strings/android_keyboard_accessory_strings.grd",
diff --git a/tools/mb/lib/validation.py b/tools/mb/lib/validation.py
index 137537fd..c8f3ea2 100644
--- a/tools/mb/lib/validation.py
+++ b/tools/mb/lib/validation.py
@@ -152,9 +152,15 @@
       elif config.startswith('//'):
         args = config
       else:
-        args = flatten_config(config_pool, mixin_pool, config)['gn_args']
+        flattened_config = flatten_config(config_pool, mixin_pool, config)
+        args = flattened_config['gn_args']
         if 'error' in args:
           continue
+        # Force the args_file into consideration when testing for duplicate
+        # configs.
+        args_file = flattened_config['args_file']
+        if args_file:
+          args += ' args_file=%s' % args_file
 
       evaled_to_source[args].add(config)
 
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl
index db83eb4..238df3a 100644
--- a/tools/mb/mb_config.pyl
+++ b/tools/mb/mb_config.pyl
@@ -14,13 +14,13 @@
   # below). MB uses this dict to look up which config to use for a given bot.
   'masters': {
     'chrome': {
-      'chromeos-arm-generic-cfi-thin-lto-chrome': 'cros_chrome_sdk_cfi_thin_lto',
-      'chromeos-betty-cfi-thin-lto-chrome': 'cros_chrome_sdk_cfi_thin_lto',
-      'chromeos-betty-chrome': 'cros_chrome_sdk_include_unwind_tables',
-      'chromeos-betty-pi-arc-cfi-thin-lto-chrome': 'cros_chrome_sdk_cfi_thin_lto',
-      'chromeos-betty-pi-arc-chrome': 'cros_chrome_sdk_include_unwind_tables',
-      'chromeos-eve-chrome': 'cros_chrome_sdk_include_unwind_tables',
-      'chromeos-kevin-chrome': 'cros_chrome_sdk_include_unwind_tables',
+      'chromeos-arm-generic-cfi-thin-lto-chrome': 'chromeos_arm-generic_cfi_thin_lto',
+      'chromeos-betty-cfi-thin-lto-chrome': 'chromeos_betty_cfi_thin_lto',
+      'chromeos-betty-chrome': 'chromeos_betty_include_unwind_tables',
+      'chromeos-betty-pi-arc-cfi-thin-lto-chrome': 'chromeos_betty-pi-arc_cfi_thin_lto',
+      'chromeos-betty-pi-arc-chrome': 'chromeos_betty-pi-arc_include_unwind_tables',
+      'chromeos-eve-chrome': 'chromeos_eve_include_unwind_tables',
+      'chromeos-kevin-chrome': 'chromeos_kevin_include_unwind_tables',
       # Don't include unwind tables for the remaining three builders since
       # they monitor binary size growth, which may be affected by the tables.
       'linux-chrome': 'official_goma',
@@ -96,13 +96,13 @@
     'chromium.chromiumos': {
       'Linux ChromiumOS Full': 'chromeos_with_codecs_release_bot',
 
-      'chromeos-amd64-generic-asan-rel': 'cros_chrome_sdk_asan',
-      'chromeos-amd64-generic-cfi-thin-lto-rel': 'cros_chrome_sdk_cfi_thin_lto',
-      'chromeos-amd64-generic-dbg': 'cros_chrome_sdk_dbg',
+      'chromeos-amd64-generic-asan-rel': 'chromeos_amd64-generic_asan',
+      'chromeos-amd64-generic-cfi-thin-lto-rel': 'chromeos_amd64-generic_cfi_thin_lto',
+      'chromeos-amd64-generic-dbg': 'chromeos_amd64-generic_dbg',
       'chromeos-amd64-generic-rel': 'chromeos_amd64-generic',
-      'chromeos-arm-generic-dbg': 'cros_chrome_sdk_dbg',
-      'chromeos-arm-generic-rel': 'cros_chrome_sdk',
-      'chromeos-kevin-rel': 'cros_chrome_sdk',
+      'chromeos-arm-generic-dbg': 'chromeos_arm-generic_dbg',
+      'chromeos-arm-generic-rel': 'chromeos_arm-generic',
+      'chromeos-kevin-rel': 'chromeos_kevin',
       'linux-chromeos-rel': 'chromeos_with_codecs_release_bot',
       'linux-chromeos-dbg': 'chromeos_with_codecs_debug_bot',
     },
@@ -231,8 +231,8 @@
       'android-code-coverage': 'gpu_tests_android_release_bot_minimal_symbols_arm64_fastbuild_java_coverage',
       'android-code-coverage-native': 'gpu_tests_android_release_bot_minimal_symbols_arm64_fastbuild_native_coverage',
       'android-mojo-webview-rel': 'android_release_bot_minimal_symbols_arm64',
-      'chromeos-amd64-generic-rel-vm-tests': 'cros_chrome_sdk_dcheck_always_on',
-      'chromeos-kevin-rel-hw-tests': 'cros_chrome_sdk',
+      'chromeos-amd64-generic-rel-vm-tests': 'chromeos_amd64-generic_dcheck_always_on',
+      'chromeos-kevin-rel-hw-tests': 'chromeos_kevin',
       'fuchsia-fyi-arm64-rel': 'release_bot_fuchsia_arm64',
       'fuchsia-fyi-x64-dbg': 'debug_bot_fuchsia',
       'fuchsia-fyi-x64-rel': 'release_bot_fuchsia',
@@ -304,10 +304,10 @@
       'mac-archive-rel-goma-canary-localoutputcache': 'release_bot_mac_strip_minimal_symbols',
       'mac-archive-rel-goma-latest-localoutputcache': 'release_bot_mac_strip_minimal_symbols',
 
-      'chromeos-amd64-generic-rel-goma-canary': 'cros_chrome_sdk',
-      'chromeos-amd64-generic-rel-goma-latest': 'cros_chrome_sdk',
-      'chromeos-amd64-generic-rel-goma-rbe-canary': 'cros_chrome_sdk',
-      'chromeos-amd64-generic-rel-goma-rbe-latest': 'cros_chrome_sdk',
+      'chromeos-amd64-generic-rel-goma-canary': 'chromeos_amd64-generic',
+      'chromeos-amd64-generic-rel-goma-latest': 'chromeos_amd64-generic',
+      'chromeos-amd64-generic-rel-goma-rbe-canary': 'chromeos_amd64-generic',
+      'chromeos-amd64-generic-rel-goma-rbe-latest': 'chromeos_amd64-generic',
       'android-archive-dbg-goma-canary': 'android_without_codecs_debug_bot',
       'android-archive-dbg-goma-latest': 'android_without_codecs_debug_bot',
       'android-archive-dbg-goma-rbe-ats-canary': 'android_without_codecs_debug_bot',
@@ -499,7 +499,7 @@
     'chromium.perf.fyi': {
       'android-cfi-builder-perf-fyi': 'official_goma_minimal_symbols_android_thin_lto_opt',
       'android_arm64-cfi-builder-perf-fyi': 'official_goma_minimal_symbols_android_thin_lto_opt_arm64',
-      'chromeos-kevin-builder-perf-fyi': 'cros_chrome_sdk',
+      'chromeos-kevin-builder-perf-fyi': 'chromeos_kevin',
     },
 
     'chromium.swangle': {
@@ -738,14 +738,15 @@
     },
 
     'tryserver.chrome': {
-      'chromeos-arm-generic-cfi-thin-lto-chrome': 'cros_chrome_sdk_cfi_thin_lto',
-      'chromeos-betty-cfi-thin-lto-chrome': 'cros_chrome_sdk_cfi_thin_lto',
-      'chromeos-betty-chrome': 'cros_chrome_sdk_include_unwind_tables',
-      'chromeos-betty-pi-arc-chrome': 'cros_chrome_sdk_include_unwind_tables',
-      'chromeos-eve-chrome': 'cros_chrome_sdk_include_unwind_tables',
-      'chromeos-eve-compile-chrome': 'cros_chrome_sdk_include_unwind_tables',
-      'chromeos-kevin-chrome': 'cros_chrome_sdk_include_unwind_tables',
-      'chromeos-kevin-compile-chrome': 'cros_chrome_sdk_include_unwind_tables',
+      'chromeos-arm-generic-cfi-thin-lto-chrome': 'chromeos_arm-generic_cfi_thin_lto',
+      'chromeos-betty-cfi-thin-lto-chrome': 'chromeos_betty_cfi_thin_lto',
+      'chromeos-betty-chrome': 'chromeos_betty_include_unwind_tables',
+      'chromeos-betty-pi-arc-cfi-thin-lto-chrome': 'chromeos_betty-pi-arc_cfi_thin_lto',
+      'chromeos-betty-pi-arc-chrome': 'chromeos_betty-pi-arc_include_unwind_tables',
+      'chromeos-eve-chrome': 'chromeos_eve_include_unwind_tables',
+      'chromeos-eve-compile-chrome': 'chromeos_eve_include_unwind_tables',
+      'chromeos-kevin-chrome': 'chromeos_kevin_include_unwind_tables',
+      'chromeos-kevin-compile-chrome': 'chromeos_kevin_include_unwind_tables',
       'linux-chrome': 'official_goma',
       'linux-chromeos-chrome': 'official_goma_chromeos_include_unwind_tables',
       'win_chrome_official': 'official_goma_x86',
@@ -755,13 +756,13 @@
     'tryserver.chromium.chromiumos': {
       # TODO(crbug.com/913750): Enable DCHECKS on the two amd64-generic bots
       # and two kevin bots when the PFQ has it enabled.
-      'chromeos-amd64-generic-cfi-thin-lto-rel': 'cros_chrome_sdk_cfi_thin_lto',
-      'chromeos-amd64-generic-dbg': 'cros_chrome_sdk_dbg',
+      'chromeos-amd64-generic-cfi-thin-lto-rel': 'chromeos_amd64-generic_cfi_thin_lto',
+      'chromeos-amd64-generic-dbg': 'chromeos_amd64-generic_dbg',
       'chromeos-amd64-generic-rel': 'chromeos_amd64-generic',
-      'chromeos-arm-generic-dbg': 'cros_chrome_sdk_dbg',
-      'chromeos-arm-generic-rel': 'cros_chrome_sdk_dcheck_always_on',
-      'chromeos-kevin-compile-rel': 'cros_chrome_sdk',
-      'chromeos-kevin-rel': 'cros_chrome_sdk',
+      'chromeos-arm-generic-dbg': 'chromeos_arm-generic_dbg',
+      'chromeos-arm-generic-rel': 'chromeos_arm-generic_dcheck_always_on',
+      'chromeos-kevin-compile-rel': 'chromeos_kevin',
+      'chromeos-kevin-rel': 'chromeos_kevin',
       'linux-chromeos-rel': 'chromeos_with_codecs_release_trybot_code_coverage',
       'linux-chromeos-compile-dbg': 'chromeos_with_codecs_debug_bot',
       'linux-chromeos-dbg': 'chromeos_with_codecs_debug_bot',
@@ -1348,10 +1349,70 @@
       'chromeos_device', 'amd64-generic',
     ],
 
+    'chromeos_amd64-generic_asan': [
+      'chromeos_device', 'amd64-generic', 'asan',
+    ],
+
+    'chromeos_amd64-generic_cfi_thin_lto': [
+      'chromeos_device', 'amd64-generic', 'cfi_full', 'thin_lto',
+    ],
+
+    'chromeos_amd64-generic_dcheck_always_on': [
+      'chromeos_device', 'amd64-generic', 'dcheck_always_on',
+    ],
+
+    'chromeos_amd64-generic_dbg': [
+      'chromeos_device', 'amd64-generic', 'debug',
+    ],
+
+    'chromeos_arm-generic': [
+      'chromeos_device', 'arm-generic',
+    ],
+
+    'chromeos_arm-generic_cfi_thin_lto': [
+      'chromeos_device', 'arm-generic', 'cfi_full', 'thin_lto',
+    ],
+
+    'chromeos_arm-generic_dcheck_always_on': [
+      'chromeos_device', 'arm-generic', 'dcheck_always_on',
+    ],
+
+    'chromeos_arm-generic_dbg': [
+      'chromeos_device', 'arm-generic', 'debug',
+    ],
+
     'chromeos_asan_lsan_fuzzer_v8_heap_release_bot': [
       'chromeos', 'asan', 'lsan', 'fuzzer', 'v8_heap', 'release_bot',
     ],
 
+    'chromeos_betty_cfi_thin_lto': [
+      'chromeos_device', 'betty', 'cfi_full', 'thin_lto',
+    ],
+
+    'chromeos_betty_include_unwind_tables': [
+      'chromeos_device', 'betty', 'include_unwind_tables',
+    ],
+
+    'chromeos_betty-pi-arc_cfi_thin_lto': [
+      'chromeos_device', 'betty-pi-arc', 'cfi_full', 'thin_lto',
+    ],
+
+    'chromeos_betty-pi-arc_include_unwind_tables': [
+      'chromeos_device', 'betty-pi-arc', 'include_unwind_tables',
+    ],
+
+    'chromeos_eve_include_unwind_tables': [
+      'chromeos_device', 'eve', 'include_unwind_tables',
+    ],
+
+    'chromeos_kevin': [
+      'chromeos_device', 'kevin',
+    ],
+
+    'chromeos_kevin_include_unwind_tables': [
+      'chromeos_device', 'kevin', 'include_unwind_tables',
+    ],
+
     'chromeos_msan_release_bot': [
       'chromeos', 'msan', 'release_bot',
     ],
@@ -1495,33 +1556,6 @@
       'codesearch',
     ],
 
-    # The 'cros_chrome_sdk_* configs are placeholders that indicate
-    # that the GN args are set by the `cros chrome-sdk`
-    # wrapper and need to be looked at specially.
-    'cros_chrome_sdk': [
-      'cros_chrome_sdk',
-    ],
-
-    'cros_chrome_sdk_asan': [
-      'cros_chrome_sdk', 'asan',
-    ],
-
-    'cros_chrome_sdk_dbg': [
-      'cros_chrome_sdk', 'debug',
-    ],
-
-    'cros_chrome_sdk_dcheck_always_on': [
-      'cros_chrome_sdk', 'dcheck_always_on',
-    ],
-
-    'cros_chrome_sdk_cfi_thin_lto': [
-      'cros_chrome_sdk', 'cfi_full', 'thin_lto',
-    ],
-
-    'cros_chrome_sdk_include_unwind_tables': [
-      'cros_chrome_sdk', 'include_unwind_tables',
-    ],
-
     'dawn_tests_release_trybot': [
       'dawn_tests', 'release_trybot',
     ],
@@ -2105,6 +2139,10 @@
       'gn_args': 'target_cpu="arm"',
     },
 
+    'arm-generic': {
+      'args_file': '//build/args/chromeos/arm-generic.gni',
+    },
+
     'arm64': {
       'gn_args': 'target_cpu="arm64"',
     },
@@ -2118,6 +2156,14 @@
       'gn_args': 'is_asan=true',
     },
 
+    'betty': {
+      'args_file': '//build/args/chromeos/betty.gni',
+    },
+
+    'betty-pi-arc': {
+      'args_file': '//build/args/chromeos/betty-pi-arc.gni',
+    },
+
     'cast': {
       'gn_args': 'is_chromecast=true'
     },
@@ -2201,18 +2247,6 @@
                   'enable_resource_whitelist_generation=false'),
     },
 
-    'cros_chrome_sdk': {
-      # This is used so that the cros chrome_sdk (simplechrome) builders
-      # can manage the list of GN args in their .ebuild
-      # files and just pass through the desired arguments, hence not
-      # really using MB. If a bot uses this mixin, we expect that
-      # both GYP_DEFINES and GN_ARGS are set in the environment,
-      # and that GYP_DEFINES has chromeos=1 and GN_ARGS has
-      # target_os="chromeos" in it.
-      'cros_passthrough': True,
-      'gn_args': 'ozone_platform_headless=true',
-    },
-
     'dawn_tests': {
       'gn_args': 'use_dawn=true',
     },
@@ -2269,6 +2303,10 @@
       'gn_args': 'skip_archive_compression=false',
     },
 
+    'eve': {
+      'args_file': '//build/args/chromeos/eve.gni',
+    },
+
     'java_warnings_as_errors': {
       'gn_args': ('java_warnings_as_errors=true'),
     },
@@ -2355,6 +2393,10 @@
       'gn_args': 'ios_use_goma_rbe=true'
     },
 
+    'kevin': {
+      'args_file': '//build/args/chromeos/kevin.gni',
+    },
+
     'libfuzzer': { 'gn_args': 'use_libfuzzer=true' },
 
     'link_jobs_32': {
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 45d89fd..696edd7b 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -58369,6 +58369,17 @@
              'Secure' directive (app targets &gt;= Android R)"/>
 </enum>
 
+<enum name="SecureDnsModeDetails">
+  <int value="0" label="Off, by user choice"/>
+  <int value="1" label="Off, by enterprise policy"/>
+  <int value="2" label="Off, due to managed environment"/>
+  <int value="3" label="Off, due to parental controls"/>
+  <int value="4" label="Automatic, by user choice"/>
+  <int value="5" label="Automatic, by enterprise policy"/>
+  <int value="6" label="Secure, by user choice"/>
+  <int value="7" label="Secure, by enterprise policy"/>
+</enum>
+
 <enum name="SecurityInterstitialDecision">
   <int value="0" label="SHOW"/>
   <int value="1" label="PROCEED"/>
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 8ecd7b0..3553bcb 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -86854,6 +86854,16 @@
   </summary>
 </histogram>
 
+<histogram name="Net.DNS.DnsConfig.SecureDnsMode" enum="SecureDnsModeDetails"
+    expires_after="2020-08-31">
+  <owner>dalyk@google.com</owner>
+  <owner>doh-core@google.com</owner>
+  <summary>
+    Records details of the secure DNS mode used to configure the host resolver
+    at construction of the network service.
+  </summary>
+</histogram>
+
 <histogram name="Net.DNS.DnsTask.ErrorBeforeFallback.Fast" enum="NetErrorCodes"
     expires_after="M77">
   <owner>pauljensen@chromium.org</owner>
@@ -87445,6 +87455,17 @@
   </summary>
 </histogram>
 
+<histogram name="Net.DNS.UpgradeConfig.Ineligible.UnhandledOptions"
+    enum="Boolean" expires_after="2020-08-31">
+  <owner>dalyk@google.com</owner>
+  <owner>doh-core@google.com</owner>
+  <summary>
+    True if upgrade to DoH was not attempted because of unhandled options in the
+    system config. False if upgrade to DoH was not attempted for some other
+    reason. Emitted each time the DnsConfig is updated.
+  </summary>
+</histogram>
+
 <histogram name="Net.DNS.UpgradeConfig.InsecureUpgradeSucceeded" enum="Boolean"
     expires_after="2020-08-31">
   <owner>dalyk@google.com</owner>
@@ -134766,6 +134787,16 @@
   </summary>
 </histogram>
 
+<histogram name="SafeBrowsing.Pref.Enhanced" enum="BooleanEnabled"
+    expires_after="M85">
+  <owner>bdea@chromium.org</owner>
+  <owner>chrome-safebrowsing-alerts@google.com</owner>
+  <summary>
+    Whether the Safe Browsing enhanced protection service is currently enabled.
+    Recorded for all non-Incognito profiles on profile startup.
+  </summary>
+</histogram>
+
 <histogram name="SafeBrowsing.Pref.Extended" enum="BooleanEnabled"
     expires_after="M85">
   <owner>vakh@chromium.org</owner>
@@ -191575,26 +191606,41 @@
 </histogram_suffixes>
 
 <histogram_suffixes name="OobeScreenName" separator=".">
+  <suffix name="Adb-sideloading" label=""/>
   <suffix name="App-downloading" label=""/>
+  <suffix name="App-launch-splash" label=""/>
   <suffix name="Arc_tos" label=""/>
   <suffix name="Assistant-optin-flow" label=""/>
   <suffix name="Auto-enrollment-check" label=""/>
+  <suffix name="Autolaunch" label=""/>
+  <suffix name="Debugging" label=""/>
   <suffix name="Demo-preferences" label=""/>
+  <suffix name="Demo-setup" label=""/>
+  <suffix name="Device-disabled" label=""/>
   <suffix name="Discover" label=""/>
+  <suffix name="Encryption-migration" label=""/>
   <suffix name="Enroll" label=""/>
+  <suffix name="Error-message" label=""/>
   <suffix name="Eula" label=""/>
   <suffix name="Fingerprint-setup" label=""/>
+  <suffix name="Gaia-signin" label=""/>
+  <suffix name="Gesture-navigation" label=""/>
   <suffix name="Hid-detection" label=""/>
   <suffix name="Image" label=""/>
+  <suffix name="Kiosk-enable" label=""/>
   <suffix name="Marketing-opt-in" label=""/>
   <suffix name="Multidevice-setup" label=""/>
   <suffix name="Network" label=""/>
   <suffix name="Network-selection" label=""/>
+  <suffix name="Packaged-license" label=""/>
   <suffix name="Recommend-apps" label=""/>
   <suffix name="Reset" label=""/>
+  <suffix name="Supervision-transition" label=""/>
   <suffix name="Sync-consent" label=""/>
   <suffix name="Tos" label=""/>
   <suffix name="Update" label=""/>
+  <suffix name="Update-required" label=""/>
+  <suffix name="UserBoard" label=""/>
   <suffix name="Wrong-hwid" label=""/>
   <affected-histogram name="OOBE.StepCompletionTime"/>
 </histogram_suffixes>
@@ -196587,6 +196633,7 @@
   <suffix name="MainThreadAnimation" label="Main-thread driven animation"/>
   <suffix name="PinchZoom" label="Pinch-to-zoom interaction"/>
   <suffix name="RAF" label="rAF callback driven animation"/>
+  <suffix name="ScrollbarScroll" label="Scrollbar driven scrolls"/>
   <suffix name="TouchScroll" label="Touchscreen driven interaction"/>
   <suffix name="Universal" label="All frame production"/>
   <suffix name="Video" label="Video playback"/>
@@ -196610,6 +196657,8 @@
   <affected-histogram
       name="Graphics.Smoothness.PercentDroppedFrames.MainThread"/>
   <affected-histogram
+      name="Graphics.Smoothness.PercentDroppedFrames.ScrollingThread"/>
+  <affected-histogram
       name="Graphics.Smoothness.PercentDroppedFrames.SlowerThread"/>
   <affected-histogram name="SingleThreadedCompositorLatency"/>
   <affected-histogram name="SingleThreadedCompositorLatency.MissedFrame"/>
@@ -196625,6 +196674,8 @@
   <suffix name="CompositorThread"
       label="The throughput of the compositor thread"/>
   <suffix name="MainThread" label="The throughput of the main thread"/>
+  <suffix name="ScrollingThread"
+      label="The throughput of the thread responsible for handling the thread"/>
   <suffix name="SlowerThread"
       label="The worse throughput of the main and the compositor thread"/>
   <affected-histogram name="Graphics.Smoothness.PercentDroppedFrames"/>
diff --git a/tools/perf/OWNERS b/tools/perf/OWNERS
index 01a63c53..6193811 100644
--- a/tools/perf/OWNERS
+++ b/tools/perf/OWNERS
@@ -1,10 +1,13 @@
 # Overall OWNERS
 crouleau@chromium.org
-#wenbinzhang@google.com  Uncomment when wenbinzhang@ becomes a committer.
+wenbinzhang@google.com
 johnchen@chromium.org  # Backup reviewer for when overall OWNERS are OOO.
 
-# Sharding, triggering, and builder configurations.
-#hypan@google.com  Uncomment when hypan@ becomes a committer.
+# For gtest perf tests, perf data upload, and functional tests
+bsheedy@chromium.org
+
+# Scheduling and builders.
+hypan@google.com
 
 # For changes related to ChromeOS.
 achuith@chromium.org
@@ -12,11 +15,7 @@
 # For expectations.config file format infrastructure changes.
 rmhasan@google.com
 
-# For gtest perf tests
-bsheedy@chromium.org
-
 # For system health stories.
-charliea@chromium.org
 eyaich@chromium.org
 sullivan@chromium.org
 
diff --git a/tools/perf/page_sets/rendering/throughput_test_cases.py b/tools/perf/page_sets/rendering/throughput_test_cases.py
index f6efbb0..d8dde78d 100644
--- a/tools/perf/page_sets/rendering/throughput_test_cases.py
+++ b/tools/perf/page_sets/rendering/throughput_test_cases.py
@@ -124,3 +124,38 @@
   SUPPORTED_PLATFORMS = platforms.ALL_PLATFORMS
   URL = ('file://../../../../chrome/test/data/perf/throughput_test_cases/'
          'main-animations-throughput.html?jank&offscreen#60')
+
+
+class ThroughputScrolling(ThroughputMetricStory):
+  ABSTRACT_STORY = True
+  URL = ('file://../../../../chrome/test/data/perf/throughput_test_cases/'
+         'throughput_scroll.html')
+  SPEED_IN_PIXELS_PER_SECOND = 5000
+  SELECTOR = 'undefined'
+
+  def RunPageInteractions(self, action_runner):
+    selector = self.SELECTOR
+    action_runner.WaitForElement(selector=selector)
+    with action_runner.CreateGestureInteraction('ScrollAction'):
+      action_runner.ScrollElement(selector=selector, direction='down')
+      action_runner.ScrollElement(selector=selector, direction='up')
+
+
+class ThroughputScrollingUncomposited(ThroughputScrolling):
+  BASE_NAME = 'throughput_scrolling_uncomposited'
+  SELECTOR = '.uncomposited'
+
+
+class ThroughputScrollingComposited(ThroughputScrolling):
+  BASE_NAME = 'throughput_scrolling_composited'
+  SELECTOR = '#composited'
+
+
+class ThroughputScrollingPassiveHandler(ThroughputScrolling):
+  BASE_NAME = 'throughput_scrolling_passive_handler'
+  SELECTOR = '#handler_passive'
+
+
+class ThroughputScrollingActiveHandler(ThroughputScrolling):
+  BASE_NAME = 'throughput_scrolling_active_handler'
+  SELECTOR = '#handler_active'
diff --git a/ui/accessibility/platform/ax_fragment_root_win.cc b/ui/accessibility/platform/ax_fragment_root_win.cc
index d88e4e6..96936c8 100644
--- a/ui/accessibility/platform/ax_fragment_root_win.cc
+++ b/ui/accessibility/platform/ax_fragment_root_win.cc
@@ -129,10 +129,26 @@
 
     *focus = nullptr;
 
-    gfx::NativeViewAccessible focused_element = GetDelegate()->GetFocus();
-    if (focused_element != nullptr) {
+    gfx::NativeViewAccessible focused_element = nullptr;
+
+    // GetFocus() can return a node at the root of a subtree, for example when
+    // transitioning from Views into web content. In such cases we want to
+    // continue drilling to retrieve the actual focused element.
+    AXPlatformNode* node_to_test = this;
+    do {
+      gfx::NativeViewAccessible test_result =
+          node_to_test->GetDelegate()->GetFocus();
+      if (test_result != nullptr && test_result != focused_element) {
+        focused_element = test_result;
+        node_to_test =
+            AXPlatformNode::FromNativeViewAccessible(focused_element);
+      } else {
+        node_to_test = nullptr;
+      }
+    } while (node_to_test);
+
+    if (focused_element)
       focused_element->QueryInterface(IID_PPV_ARGS(focus));
-    }
 
     return S_OK;
   }
diff --git a/ui/events/blink/input_handler_proxy.cc b/ui/events/blink/input_handler_proxy.cc
index 31d06ce..fe463d5 100644
--- a/ui/events/blink/input_handler_proxy.cc
+++ b/ui/events/blink/input_handler_proxy.cc
@@ -146,24 +146,19 @@
   return info;
 }
 
-enum ScrollingThreadStatus {
-  kScrollingOnCompositor = 0,
-  kScrollingOnCompositorBlockedOnMain = 1,
-  kScrollingOnMain = 2,
-  kMaxValue = kScrollingOnMain,
-};
-
-void RecordScrollingThread(bool scrolling_on_compositor_thread,
-                           bool blocked_on_main_thread_event_handler,
-                           blink::WebGestureDevice device) {
+cc::ScrollBeginThreadState RecordScrollingThread(
+    bool scrolling_on_compositor_thread,
+    bool blocked_on_main_thread_event_handler,
+    blink::WebGestureDevice device) {
   const char* kWheelHistogramName = "Renderer4.ScrollingThread.Wheel";
   const char* kTouchHistogramName = "Renderer4.ScrollingThread.Touch";
 
-  ScrollingThreadStatus status = kScrollingOnMain;
+  auto status = cc::ScrollBeginThreadState::kScrollingOnMain;
   if (scrolling_on_compositor_thread) {
-    status = blocked_on_main_thread_event_handler
-                 ? kScrollingOnCompositorBlockedOnMain
-                 : kScrollingOnCompositor;
+    status =
+        blocked_on_main_thread_event_handler
+            ? cc::ScrollBeginThreadState::kScrollingOnCompositorBlockedOnMain
+            : cc::ScrollBeginThreadState::kScrollingOnCompositor;
   }
 
   if (device == blink::WebGestureDevice::kTouchscreen) {
@@ -173,6 +168,7 @@
   } else {
     NOTREACHED();
   }
+  return status;
 }
 
 }  // namespace
@@ -567,13 +563,16 @@
           // Since a kScrollbarScroll is about to commence, ensure that any
           // existing ongoing scroll is ended.
           if (currently_active_gesture_device_.has_value()) {
-            DCHECK(currently_active_gesture_device_ !=
-                   blink::WebGestureDevice::kUninitialized);
+            DCHECK_NE(*currently_active_gesture_device_,
+                      blink::WebGestureDevice::kUninitialized);
             if (gesture_pinch_in_progress_)
               input_handler_->PinchGestureEnd(
                   gfx::ToFlooredPoint(mouse_event.PositionInWidget()), true);
-            if (handling_gesture_on_impl_thread_)
+            if (handling_gesture_on_impl_thread_) {
+              input_handler_->RecordScrollEnd(
+                  GestureScrollInputType(*currently_active_gesture_device_));
               InputHandlerScrollEnd();
+            }
           }
 
           // Generate GSB and GSU events and add them to the
@@ -709,8 +708,10 @@
   // wheel/touch events (i.e. were they preventDefaulted?).
   bool blocked_on_main_thread_handler = disposition == DID_NOT_HANDLE;
 
-  RecordScrollingThread(is_compositor_scroll, blocked_on_main_thread_handler,
-                        device);
+  auto scroll_start_state = RecordScrollingThread(
+      is_compositor_scroll, blocked_on_main_thread_handler, device);
+  input_handler_->RecordScrollBegin(GestureScrollInputType(device),
+                                    scroll_start_state);
 
   if (blocked_on_main_thread_handler) {
     // We should also collect main thread scrolling reasons if a scroll event
@@ -948,6 +949,8 @@
 InputHandlerProxy::EventDisposition InputHandlerProxy::HandleGestureScrollEnd(
     const WebGestureEvent& gesture_event) {
   TRACE_EVENT0("input", "InputHandlerProxy::HandleGestureScrollEnd");
+  input_handler_->RecordScrollEnd(
+      GestureScrollInputType(gesture_event.SourceDevice()));
 
   if (scroll_sequence_ignored_) {
     DCHECK(!currently_active_gesture_device_.has_value());
diff --git a/ui/events/blink/input_handler_proxy_unittest.cc b/ui/events/blink/input_handler_proxy_unittest.cc
index 08942898..5cb4da62 100644
--- a/ui/events/blink/input_handler_proxy_unittest.cc
+++ b/ui/events/blink/input_handler_proxy_unittest.cc
@@ -118,6 +118,10 @@
   MOCK_METHOD2(ScrollUpdate,
                cc::InputHandlerScrollResult(cc::ScrollState*, base::TimeDelta));
   MOCK_METHOD1(ScrollEnd, void(bool));
+  MOCK_METHOD2(RecordScrollBegin,
+               void(cc::ScrollInputType type,
+                    cc::ScrollBeginThreadState state));
+  MOCK_METHOD1(RecordScrollEnd, void(cc::ScrollInputType type));
   MOCK_METHOD0(ScrollingShouldSwitchtoMainThread, bool());
 
   std::unique_ptr<cc::SwapPromiseMonitor> CreateLatencyInfoSwapPromiseMonitor(
@@ -564,6 +568,11 @@
   // Touchpad initiates a scroll.
   EXPECT_CALL(mock_input_handler_, ScrollBegin(_, _))
       .WillOnce(testing::Return(kImplThreadScrollState));
+  EXPECT_CALL(
+      mock_input_handler_,
+      RecordScrollBegin(cc::ScrollInputType::kWheel,
+                        cc::ScrollBeginThreadState::kScrollingOnCompositor))
+      .Times(1);
 
   gesture_.SetType(WebInputEvent::kGestureScrollBegin);
   gesture_.SetSourceDevice(blink::WebGestureDevice::kTouchpad);
@@ -576,9 +585,17 @@
 
   // Before ScrollEnd for touchpad is fired, user starts a thumb drag. This is
   // expected to immediately end the touchpad scroll.
+  EXPECT_CALL(mock_input_handler_, RecordScrollEnd(cc::ScrollInputType::kWheel))
+      .Times(1);
   EXPECT_CALL(mock_input_handler_, ScrollEnd(true)).Times(1);
   EXPECT_CALL(mock_input_handler_, ScrollBegin(_, _))
       .WillOnce(testing::Return(kImplThreadScrollState));
+  // TODO(crbug.com/1060708): This should be called once.
+  EXPECT_CALL(
+      mock_input_handler_,
+      RecordScrollBegin(cc::ScrollInputType::kScrollbar,
+                        cc::ScrollBeginThreadState::kScrollingOnCompositor))
+      .Times(0);
   EXPECT_CALL(mock_input_handler_, ScrollUpdate(_, _)).Times(1);
   EXPECT_CALL(mock_input_handler_, ScrollingShouldSwitchtoMainThread())
       .WillOnce(testing::Return(false));
@@ -598,11 +615,17 @@
   gesture_.SetType(WebInputEvent::kGestureScrollEnd);
   gesture_.SetSourceDevice(blink::WebGestureDevice::kTouchpad);
   gesture_.data.scroll_update.delta_y = 0;
+  EXPECT_CALL(mock_input_handler_, RecordScrollEnd(cc::ScrollInputType::kWheel))
+      .Times(1);
   EXPECT_EQ(InputHandlerProxy::DROP_EVENT,
             HandleInputEventAndFlushEventQueue(mock_input_handler_,
                                                input_handler_.get(), gesture_));
+  VERIFY_AND_RESET_MOCKS();
 
   // The GSE from the scrollbar needs to be handled.
+  EXPECT_CALL(mock_input_handler_,
+              RecordScrollEnd(cc::ScrollInputType::kScrollbar))
+      .Times(1);
   EXPECT_CALL(mock_input_handler_, ScrollEnd(true)).Times(1);
   mouse_event.SetType(WebInputEvent::kMouseUp);
   EXPECT_EQ(InputHandlerProxy::DID_NOT_HANDLE,
@@ -728,6 +751,10 @@
 
   EXPECT_CALL(mock_input_handler_, ScrollBegin(_, _))
       .WillOnce(testing::Return(kImplThreadScrollState));
+  EXPECT_CALL(
+      mock_input_handler_,
+      RecordScrollBegin(_, cc::ScrollBeginThreadState::kScrollingOnCompositor))
+      .Times(1);
 
   gesture_.SetType(WebInputEvent::kGestureScrollBegin);
   EXPECT_EQ(expected_disposition_,
@@ -775,6 +802,7 @@
   gesture_.SetType(WebInputEvent::kGestureScrollEnd);
   gesture_.data.scroll_update.delta_y = 0;
   EXPECT_CALL(mock_input_handler_, ScrollEnd(true));
+  EXPECT_CALL(mock_input_handler_, RecordScrollEnd(_)).Times(1);
   EXPECT_EQ(expected_disposition_,
             HandleInputEventAndFlushEventQueue(mock_input_handler_,
                                                input_handler_.get(), gesture_));
@@ -792,6 +820,10 @@
 
   EXPECT_CALL(mock_input_handler_, ScrollBegin(_, _))
       .WillOnce(testing::Return(kMainThreadScrollState));
+  EXPECT_CALL(
+      mock_input_handler_,
+      RecordScrollBegin(_, cc::ScrollBeginThreadState::kScrollingOnMain))
+      .Times(1);
 
   gesture_.SetType(WebInputEvent::kGestureScrollBegin);
   EXPECT_EQ(expected_disposition_,
@@ -808,6 +840,7 @@
 
   gesture_.SetType(WebInputEvent::kGestureScrollEnd);
   gesture_.data.scroll_update.delta_y = 0;
+  EXPECT_CALL(mock_input_handler_, RecordScrollEnd(_)).Times(1);
   EXPECT_EQ(expected_disposition_,
             HandleInputEventWithLatencyInfo(input_handler_.get(), gesture_));
 
@@ -825,6 +858,10 @@
 
   EXPECT_CALL(mock_input_handler_, ScrollBegin(_, _))
       .WillOnce(testing::Return(kScrollIgnoredScrollState));
+  EXPECT_CALL(
+      mock_input_handler_,
+      RecordScrollBegin(_, cc::ScrollBeginThreadState::kScrollingOnMain))
+      .Times(1);
 
   gesture_.SetType(WebInputEvent::kGestureScrollBegin);
   EXPECT_EQ(expected_disposition_,
@@ -836,6 +873,7 @@
   // the main thread, either.
   expected_disposition_ = InputHandlerProxy::DROP_EVENT;
   gesture_.SetType(WebInputEvent::kGestureScrollEnd);
+  EXPECT_CALL(mock_input_handler_, RecordScrollEnd(_)).Times(1);
   EXPECT_EQ(expected_disposition_,
             HandleInputEventWithLatencyInfo(input_handler_.get(), gesture_));
 
@@ -850,6 +888,10 @@
   gesture_.SetType(WebInputEvent::kGestureScrollBegin);
   gesture_.data.scroll_begin.delta_hint_units =
       ui::ScrollGranularity::kScrollByPage;
+  EXPECT_CALL(
+      mock_input_handler_,
+      RecordScrollBegin(_, cc::ScrollBeginThreadState::kScrollingOnMain))
+      .Times(1);
   EXPECT_EQ(expected_disposition_,
             HandleInputEventWithLatencyInfo(input_handler_.get(), gesture_));
 
@@ -866,6 +908,7 @@
 
   gesture_.SetType(WebInputEvent::kGestureScrollEnd);
   gesture_.data.scroll_update.delta_y = 0;
+  EXPECT_CALL(mock_input_handler_, RecordScrollEnd(_)).Times(1);
   EXPECT_EQ(expected_disposition_,
             HandleInputEventWithLatencyInfo(input_handler_.get(), gesture_));
 
@@ -879,6 +922,10 @@
 
   EXPECT_CALL(mock_input_handler_, RootScrollBegin(_, _))
       .WillOnce(testing::Return(kImplThreadScrollState));
+  EXPECT_CALL(
+      mock_input_handler_,
+      RecordScrollBegin(_, cc::ScrollBeginThreadState::kScrollingOnCompositor))
+      .Times(1);
 
   gesture_.SetType(WebInputEvent::kGestureScrollBegin);
   gesture_.data.scroll_begin.target_viewport = true;
@@ -894,6 +941,10 @@
 
   EXPECT_CALL(mock_input_handler_, ScrollBegin(_, _))
       .WillOnce(testing::Return(kImplThreadScrollState));
+  EXPECT_CALL(
+      mock_input_handler_,
+      RecordScrollBegin(_, cc::ScrollBeginThreadState::kScrollingOnCompositor))
+      .Times(1);
 
   gesture_.SetType(WebInputEvent::kGestureScrollBegin);
   EXPECT_EQ(expected_disposition_,
@@ -940,6 +991,7 @@
   gesture_.SetType(WebInputEvent::kGestureScrollEnd);
   gesture_.data.scroll_end.inertial_phase =
       blink::WebGestureEvent::InertialPhaseState::kMomentum;
+  EXPECT_CALL(mock_input_handler_, RecordScrollEnd(_)).Times(0);
   EXPECT_CALL(mock_input_handler_, ScrollEnd(_)).Times(0);
   EXPECT_EQ(expected_disposition_,
             HandleInputEventAndFlushEventQueue(mock_input_handler_,
@@ -996,6 +1048,10 @@
 
   EXPECT_CALL(mock_input_handler_, ScrollBegin(_, _))
       .WillOnce(testing::Return(kMainThreadScrollState));
+  EXPECT_CALL(
+      mock_input_handler_,
+      RecordScrollBegin(_, cc::ScrollBeginThreadState::kScrollingOnMain))
+      .Times(1);
 
   gesture_.SetType(WebInputEvent::kGestureScrollBegin);
   EXPECT_EQ(expected_disposition_,
@@ -1072,6 +1128,7 @@
 
   gesture_.SetType(WebInputEvent::kGestureScrollEnd);
   gesture_.data.scroll_update.delta_y = 0;
+  EXPECT_CALL(mock_input_handler_, RecordScrollEnd(_)).Times(1);
   EXPECT_EQ(expected_disposition_,
             HandleInputEventWithLatencyInfo(input_handler_.get(), gesture_));
 
@@ -1085,6 +1142,10 @@
 
   EXPECT_CALL(mock_input_handler_, ScrollBegin(_, _))
       .WillOnce(testing::Return(kImplThreadScrollState));
+  EXPECT_CALL(
+      mock_input_handler_,
+      RecordScrollBegin(_, cc::ScrollBeginThreadState::kScrollingOnCompositor))
+      .Times(1);
 
   // HandleGestureScrollBegin will set gesture_scroll_on_impl_thread_.
   gesture_.SetType(WebInputEvent::kGestureScrollBegin);
@@ -1125,6 +1186,7 @@
   VERIFY_AND_RESET_MOCKS();
 
   gesture_.SetType(WebInputEvent::kGestureScrollEnd);
+  EXPECT_CALL(mock_input_handler_, RecordScrollEnd(_)).Times(1);
   EXPECT_EQ(expected_disposition_,
             HandleInputEventAndFlushEventQueue(mock_input_handler_,
                                                input_handler_.get(), gesture_));
@@ -1148,6 +1210,10 @@
 
   EXPECT_CALL(mock_input_handler_, ScrollBegin(_, _))
       .WillOnce(testing::Return(kImplThreadScrollState));
+  EXPECT_CALL(
+      mock_input_handler_,
+      RecordScrollBegin(_, cc::ScrollBeginThreadState::kScrollingOnCompositor))
+      .Times(1);
 
   gesture_.SetType(WebInputEvent::kGestureScrollBegin);
   EXPECT_EQ(expected_disposition_,
@@ -1161,6 +1227,7 @@
 
   EXPECT_CALL(mock_input_handler_, ScrollEnd(true));
   gesture_.SetType(WebInputEvent::kGestureScrollEnd);
+  EXPECT_CALL(mock_input_handler_, RecordScrollEnd(_)).Times(1);
   EXPECT_EQ(expected_disposition_,
             HandleInputEventWithLatencyInfo(input_handler_.get(), gesture_));
 
@@ -1179,6 +1246,10 @@
 
   EXPECT_CALL(mock_input_handler_, ScrollBegin(_, _))
       .WillOnce(testing::Return(kImplThreadScrollState));
+  EXPECT_CALL(
+      mock_input_handler_,
+      RecordScrollBegin(_, cc::ScrollBeginThreadState::kScrollingOnCompositor))
+      .Times(1);
 
   gesture_.SetType(WebInputEvent::kGestureScrollBegin);
   EXPECT_EQ(expected_disposition_,
@@ -1588,6 +1659,10 @@
 
   EXPECT_CALL(mock_input_handler_, ScrollBegin(_, _))
       .WillOnce(testing::Return(kImplThreadScrollState));
+  EXPECT_CALL(
+      mock_input_handler_,
+      RecordScrollBegin(_, cc::ScrollBeginThreadState::kScrollingOnCompositor))
+      .Times(1);
   EXPECT_CALL(mock_input_handler_, SetNeedsAnimateInput()).Times(1);
 
   HandleGestureEvent(WebInputEvent::kGestureScrollBegin);
@@ -1617,6 +1692,7 @@
   EXPECT_EQ(2ul, event_queue().front()->coalesced_count());
   EXPECT_EQ(1ul, event_disposition_recorder_.size());
 
+  EXPECT_CALL(mock_input_handler_, RecordScrollEnd(_)).Times(0);
   HandleGestureEvent(WebInputEvent::kGestureScrollEnd);
 
   // GestureScrollEnd will be queued.
@@ -1634,6 +1710,7 @@
   EXPECT_CALL(mock_input_handler_, ScrollEnd(true));
 
   // Dispatch all queued events.
+  EXPECT_CALL(mock_input_handler_, RecordScrollEnd(_)).Times(1);
   DeliverInputForBeginFrame();
   EXPECT_EQ(0ul, event_queue().size());
   // Should run callbacks for every original events.
@@ -1663,6 +1740,10 @@
   // Start scroll in the first frame.
   EXPECT_CALL(mock_input_handler_, ScrollBegin(_, _))
       .WillOnce(testing::Return(kImplThreadScrollState));
+  EXPECT_CALL(
+      mock_input_handler_,
+      RecordScrollBegin(_, cc::ScrollBeginThreadState::kScrollingOnCompositor))
+      .Times(1);
   EXPECT_CALL(mock_input_handler_, ScrollingShouldSwitchtoMainThread())
       .WillOnce(testing::Return(false));
   EXPECT_CALL(
@@ -1687,6 +1768,10 @@
   // Continue scroll in the second frame, pinch, then start another scroll.
   EXPECT_CALL(mock_input_handler_, ScrollBegin(_, _))
       .WillOnce(testing::Return(kImplThreadScrollState));
+  EXPECT_CALL(
+      mock_input_handler_,
+      RecordScrollBegin(_, cc::ScrollBeginThreadState::kScrollingOnCompositor))
+      .Times(1);
   EXPECT_CALL(mock_input_handler_, ScrollingShouldSwitchtoMainThread())
       .WillRepeatedly(testing::Return(false));
   EXPECT_CALL(
@@ -1701,6 +1786,7 @@
   EXPECT_CALL(mock_input_handler_,
               PinchGestureUpdate(0.7f, gfx::Point(13, 17)));
   EXPECT_CALL(mock_input_handler_, PinchGestureEnd(gfx::Point(), false));
+  EXPECT_CALL(mock_input_handler_, RecordScrollEnd(_)).Times(2);
 
   HandleGestureEvent(WebInputEvent::kGestureScrollUpdate, -30);
   HandleGestureEvent(WebInputEvent::kGestureScrollEnd);
@@ -1734,6 +1820,10 @@
 
   EXPECT_CALL(mock_input_handler_, ScrollBegin(_, _))
       .WillOnce(testing::Return(kImplThreadScrollState));
+  EXPECT_CALL(
+      mock_input_handler_,
+      RecordScrollBegin(_, cc::ScrollBeginThreadState::kScrollingOnCompositor))
+      .Times(1);
   EXPECT_CALL(mock_input_handler_, SetNeedsAnimateInput()).Times(1);
   EXPECT_CALL(mock_input_handler_, ScrollingShouldSwitchtoMainThread())
       .WillOnce(testing::Return(false));
@@ -1743,6 +1833,7 @@
                    _))
       .WillOnce(testing::Return(scroll_result_did_scroll_));
   EXPECT_CALL(mock_input_handler_, ScrollEnd(true));
+  EXPECT_CALL(mock_input_handler_, RecordScrollEnd(_)).Times(1);
 
   HandleGestureEvent(WebInputEvent::kGestureScrollBegin);
   tick_clock.Advance(base::TimeDelta::FromMicroseconds(10));
@@ -1766,6 +1857,10 @@
   // Start scroll in the first frame.
   EXPECT_CALL(mock_input_handler_, ScrollBegin(_, _))
       .WillOnce(testing::Return(kImplThreadScrollState));
+  EXPECT_CALL(
+      mock_input_handler_,
+      RecordScrollBegin(_, cc::ScrollBeginThreadState::kScrollingOnCompositor))
+      .Times(1);
   EXPECT_CALL(mock_input_handler_, SetNeedsAnimateInput()).Times(1);
 
   // GSUs and GPUs in one sequence should be coalesced into 1 GSU and 1 GPU.
@@ -1871,6 +1966,10 @@
 
   EXPECT_CALL(mock_input_handler_, ScrollBegin(_, _))
       .WillRepeatedly(testing::Return(kImplThreadScrollState));
+  EXPECT_CALL(
+      mock_input_handler_,
+      RecordScrollBegin(_, cc::ScrollBeginThreadState::kScrollingOnCompositor))
+      .Times(2);
   EXPECT_CALL(mock_input_handler_, SetNeedsAnimateInput())
       .Times(::testing::AtLeast(1));
   EXPECT_CALL(mock_input_handler_, ScrollingShouldSwitchtoMainThread())
@@ -1882,6 +1981,7 @@
       .WillRepeatedly(testing::Return(scroll_result_did_scroll_));
   EXPECT_CALL(mock_input_handler_, ScrollEnd(true))
       .Times(::testing::AtLeast(1));
+  EXPECT_CALL(mock_input_handler_, RecordScrollEnd(_)).Times(2);
 
   EXPECT_CALL(mock_input_handler_, PinchGestureBegin());
   EXPECT_CALL(mock_input_handler_, PinchGestureUpdate(_, _));
@@ -1953,6 +2053,10 @@
 
   EXPECT_CALL(mock_input_handler_, ScrollBegin(_, _))
       .WillRepeatedly(testing::Return(kImplThreadScrollState));
+  EXPECT_CALL(
+      mock_input_handler_,
+      RecordScrollBegin(_, cc::ScrollBeginThreadState::kScrollingOnCompositor))
+      .Times(2);
   EXPECT_CALL(mock_input_handler_, ScrollingShouldSwitchtoMainThread())
       .WillRepeatedly(testing::Return(false));
   EXPECT_CALL(
@@ -1984,6 +2088,7 @@
   EXPECT_EQ(2ul, event_disposition_recorder_.size());
 
   // Touchpad GSE will flush the queue.
+  EXPECT_CALL(mock_input_handler_, RecordScrollEnd(_)).Times(1);
   HandleGestureEventWithSourceDevice(WebInputEvent::kGestureScrollEnd,
                                      blink::WebGestureDevice::kTouchpad);
 
@@ -2015,6 +2120,10 @@
 
   EXPECT_CALL(mock_input_handler_, ScrollBegin(_, _))
       .WillOnce(testing::Return(kImplThreadScrollState));
+  EXPECT_CALL(
+      mock_input_handler_,
+      RecordScrollBegin(_, cc::ScrollBeginThreadState::kScrollingOnCompositor))
+      .Times(1);
   EXPECT_CALL(mock_input_handler_, SetNeedsAnimateInput()).Times(1);
   EXPECT_CALL(mock_input_handler_, ScrollingShouldSwitchtoMainThread())
       .WillOnce(testing::Return(false));
@@ -2023,6 +2132,7 @@
       ScrollUpdate(testing::Property(&cc::ScrollState::delta_y, testing::Gt(0)),
                    _))
       .WillOnce(testing::Return(scroll_result_did_scroll_));
+  EXPECT_CALL(mock_input_handler_, RecordScrollEnd(_)).Times(1);
   EXPECT_CALL(mock_input_handler_, ScrollEnd(true));
 
   HandleGestureEvent(WebInputEvent::kGestureScrollBegin);
@@ -2052,6 +2162,10 @@
   // scroll begin on main thread
   EXPECT_CALL(mock_input_handler_, ScrollBegin(_, _))
       .WillOnce(testing::Return(kMainThreadScrollState));
+  EXPECT_CALL(
+      mock_input_handler_,
+      RecordScrollBegin(_, cc::ScrollBeginThreadState::kScrollingOnMain))
+      .Times(1);
   EXPECT_CALL(mock_input_handler_, SetNeedsAnimateInput()).Times(2);
   EXPECT_CALL(mock_input_handler_, ScrollingShouldSwitchtoMainThread())
       .WillOnce(testing::Return(false));
@@ -2118,6 +2232,10 @@
   scroll_result_did_scroll_.did_scroll = true;
   EXPECT_CALL(mock_input_handler_, ScrollBegin(_, _))
       .WillOnce(testing::Return(kImplThreadScrollState));
+  EXPECT_CALL(
+      mock_input_handler_,
+      RecordScrollBegin(_, cc::ScrollBeginThreadState::kScrollingOnCompositor))
+      .Times(1);
   EXPECT_CALL(mock_input_handler_, SetNeedsAnimateInput()).Times(1);
   EXPECT_CALL(mock_input_handler_, ScrollingShouldSwitchtoMainThread())
       .WillOnce(testing::Return(false));
@@ -2149,6 +2267,10 @@
   EXPECT_CALL(mock_input_handler_, SetNeedsAnimateInput()).Times(1);
   EXPECT_CALL(mock_input_handler_, ScrollBegin(_, _))
       .WillOnce(testing::Return(kImplThreadScrollState));
+  EXPECT_CALL(
+      mock_input_handler_,
+      RecordScrollBegin(_, cc::ScrollBeginThreadState::kScrollingOnCompositor))
+      .Times(1);
   tick_clock.Advance(base::TimeDelta::FromMilliseconds(8));
   HandleGestureEvent(WebInputEvent::kGestureScrollBegin);
   DeliverInputForBeginFrame();
@@ -2165,6 +2287,10 @@
   scroll_result_did_scroll_.did_scroll = true;
   EXPECT_CALL(mock_input_handler_, ScrollBegin(_, _))
       .WillOnce(testing::Return(kImplThreadScrollState));
+  EXPECT_CALL(
+      mock_input_handler_,
+      RecordScrollBegin(_, cc::ScrollBeginThreadState::kScrollingOnCompositor))
+      .Times(1);
   EXPECT_CALL(mock_input_handler_, SetNeedsAnimateInput()).Times(2);
   EXPECT_CALL(mock_input_handler_, ScrollingShouldSwitchtoMainThread())
       .WillRepeatedly(testing::Return(false));
@@ -2277,6 +2403,10 @@
 
   EXPECT_CALL(mock_input_handler_, ScrollBegin(_, _))
       .WillOnce(testing::Return(kImplThreadScrollState));
+  EXPECT_CALL(
+      mock_input_handler_,
+      RecordScrollBegin(_, cc::ScrollBeginThreadState::kScrollingOnCompositor))
+      .Times(1);
   expected_disposition_ = InputHandlerProxy::DID_HANDLE;
   EXPECT_EQ(
       expected_disposition_,
@@ -2290,6 +2420,7 @@
           1)));
 
   EXPECT_CALL(mock_input_handler_, ScrollEnd(true));
+  EXPECT_CALL(mock_input_handler_, RecordScrollEnd(_)).Times(1);
   expected_disposition_ = InputHandlerProxy::DID_HANDLE;
   EXPECT_EQ(
       expected_disposition_,
@@ -2321,6 +2452,13 @@
 
   EXPECT_CALL(mock_input_handler_, ScrollBegin(_, _))
       .WillOnce(testing::Return(kImplThreadScrollState));
+  EXPECT_CALL(mock_input_handler_,
+              RecordScrollBegin(
+                  _, compositor_touch_action_enabled_
+                         ? cc::ScrollBeginThreadState::kScrollingOnCompositor
+                         : cc::ScrollBeginThreadState::
+                               kScrollingOnCompositorBlockedOnMain))
+      .Times(1);
   expected_disposition_ = InputHandlerProxy::DID_HANDLE;
   EXPECT_EQ(
       expected_disposition_,
@@ -2338,6 +2476,7 @@
           1)));
 
   EXPECT_CALL(mock_input_handler_, ScrollEnd(true));
+  EXPECT_CALL(mock_input_handler_, RecordScrollEnd(_)).Times(1);
   expected_disposition_ = InputHandlerProxy::DID_HANDLE;
   EXPECT_EQ(
       expected_disposition_,
@@ -2372,6 +2511,10 @@
 
   EXPECT_CALL(mock_input_handler_, ScrollBegin(_, _))
       .WillOnce(testing::Return(kMainThreadScrollState));
+  EXPECT_CALL(
+      mock_input_handler_,
+      RecordScrollBegin(_, cc::ScrollBeginThreadState::kScrollingOnMain))
+      .Times(1);
   expected_disposition_ = InputHandlerProxy::DID_NOT_HANDLE;
   EXPECT_EQ(expected_disposition_,
             HandleInputEventWithLatencyInfo(input_handler_.get(),
@@ -2390,6 +2533,7 @@
   // Handle touch end event so that input handler proxy is out of the state of
   // DID_NOT_HANDLE.
   expected_disposition_ = InputHandlerProxy::DID_NOT_HANDLE;
+  EXPECT_CALL(mock_input_handler_, RecordScrollEnd(_)).Times(1);
   EXPECT_EQ(expected_disposition_,
             HandleInputEventWithLatencyInfo(input_handler_.get(),
                                             gesture_scroll_end_));
@@ -2419,6 +2563,10 @@
 
   EXPECT_CALL(mock_input_handler_, ScrollBegin(_, _))
       .WillOnce(testing::Return(kMainThreadScrollState));
+  EXPECT_CALL(
+      mock_input_handler_,
+      RecordScrollBegin(_, cc::ScrollBeginThreadState::kScrollingOnMain))
+      .Times(1);
   expected_disposition_ = InputHandlerProxy::DID_NOT_HANDLE;
   EXPECT_EQ(expected_disposition_,
             HandleInputEventWithLatencyInfo(input_handler_.get(),
@@ -2433,6 +2581,7 @@
           1)));
 
   expected_disposition_ = InputHandlerProxy::DID_NOT_HANDLE;
+  EXPECT_CALL(mock_input_handler_, RecordScrollEnd(_)).Times(1);
   EXPECT_EQ(expected_disposition_,
             HandleInputEventWithLatencyInfo(input_handler_.get(),
                                             gesture_scroll_end_));
@@ -2441,6 +2590,10 @@
 TEST_P(InputHandlerProxyMainThreadScrollingReasonTest, WheelScrollHistogram) {
   // Firstly check if input handler can correctly record main thread scrolling
   // reasons.
+  EXPECT_CALL(
+      mock_input_handler_,
+      RecordScrollBegin(_, cc::ScrollBeginThreadState::kScrollingOnMain))
+      .Times(1);
   input_handler_->RecordMainThreadScrollingReasonsForTest(
       blink::WebGestureDevice::kTouchpad,
       cc::MainThreadScrollingReason::kHasBackgroundAttachmentFixedObjects |
@@ -2468,6 +2621,10 @@
   // scrolling on main is something else, and we only want to pay attention to
   // that reason. So we should only include this reason in the histogram when
   // its on its own.
+  EXPECT_CALL(
+      mock_input_handler_,
+      RecordScrollBegin(_, cc::ScrollBeginThreadState::kScrollingOnMain))
+      .Times(1);
   input_handler_->RecordMainThreadScrollingReasonsForTest(
       blink::WebGestureDevice::kTouchpad,
       cc::MainThreadScrollingReason::kHandlingScrollFromMainThread);
@@ -2509,6 +2666,10 @@
 
   EXPECT_CALL(mock_input_handler_, ScrollBegin(_, _))
       .WillOnce(testing::Return(kImplThreadScrollState));
+  EXPECT_CALL(
+      mock_input_handler_,
+      RecordScrollBegin(_, cc::ScrollBeginThreadState::kScrollingOnCompositor))
+      .Times(1);
   expected_disposition_ = InputHandlerProxy::DID_HANDLE;
   EXPECT_EQ(expected_disposition_,
             HandleInputEventWithLatencyInfo(input_handler_.get(),
@@ -2521,6 +2682,7 @@
           1)));
 
   EXPECT_CALL(mock_input_handler_, ScrollEnd(true));
+  EXPECT_CALL(mock_input_handler_, RecordScrollEnd(_)).Times(1);
   expected_disposition_ = InputHandlerProxy::DID_HANDLE;
   EXPECT_EQ(expected_disposition_,
             HandleInputEventWithLatencyInfo(input_handler_.get(),
@@ -2541,6 +2703,11 @@
 
   EXPECT_CALL(mock_input_handler_, ScrollBegin(_, _))
       .WillOnce(testing::Return(kImplThreadScrollState));
+  EXPECT_CALL(
+      mock_input_handler_,
+      RecordScrollBegin(
+          _, cc::ScrollBeginThreadState::kScrollingOnCompositorBlockedOnMain))
+      .Times(1);
   expected_disposition_ = InputHandlerProxy::DID_HANDLE;
   EXPECT_EQ(expected_disposition_,
             HandleInputEventWithLatencyInfo(input_handler_.get(),
@@ -2554,6 +2721,7 @@
           1)));
 
   EXPECT_CALL(mock_input_handler_, ScrollEnd(true));
+  EXPECT_CALL(mock_input_handler_, RecordScrollEnd(_)).Times(1);
   expected_disposition_ = InputHandlerProxy::DID_HANDLE;
   EXPECT_EQ(expected_disposition_,
             HandleInputEventWithLatencyInfo(input_handler_.get(),
@@ -2573,6 +2741,10 @@
 
   EXPECT_CALL(mock_input_handler_, ScrollBegin(_, _))
       .WillOnce(testing::Return(kMainThreadScrollState));
+  EXPECT_CALL(
+      mock_input_handler_,
+      RecordScrollBegin(_, cc::ScrollBeginThreadState::kScrollingOnMain))
+      .Times(1);
   expected_disposition_ = InputHandlerProxy::DID_NOT_HANDLE;
   EXPECT_EQ(expected_disposition_,
             HandleInputEventWithLatencyInfo(input_handler_.get(),
@@ -2586,6 +2758,7 @@
           1)));
 
   expected_disposition_ = InputHandlerProxy::DID_NOT_HANDLE;
+  EXPECT_CALL(mock_input_handler_, RecordScrollEnd(_)).Times(1);
   EXPECT_EQ(expected_disposition_,
             HandleInputEventWithLatencyInfo(input_handler_.get(),
                                             gesture_scroll_end_));
@@ -2607,6 +2780,10 @@
 
   EXPECT_CALL(mock_input_handler_, ScrollBegin(_, _))
       .WillOnce(testing::Return(kMainThreadScrollState));
+  EXPECT_CALL(
+      mock_input_handler_,
+      RecordScrollBegin(_, cc::ScrollBeginThreadState::kScrollingOnMain))
+      .Times(1);
   expected_disposition_ = InputHandlerProxy::DID_NOT_HANDLE;
   EXPECT_EQ(expected_disposition_,
             HandleInputEventWithLatencyInfo(input_handler_.get(),
@@ -2620,6 +2797,7 @@
           1)));
 
   expected_disposition_ = InputHandlerProxy::DID_NOT_HANDLE;
+  EXPECT_CALL(mock_input_handler_, RecordScrollEnd(_)).Times(1);
   EXPECT_EQ(expected_disposition_,
             HandleInputEventWithLatencyInfo(input_handler_.get(),
                                             gesture_scroll_end_));
diff --git a/ui/file_manager/file_manager/foreground/css/list.css b/ui/file_manager/file_manager/foreground/css/list.css
index 958cc09..be35b63 100644
--- a/ui/file_manager/file_manager/foreground/css/list.css
+++ b/ui/file_manager/file_manager/foreground/css/list.css
@@ -29,8 +29,8 @@
   display: inline-block;
 }
 
-html[class*='col-resize'] list,
-html[class*='col-resize'] grid {
+html.col-resize list,
+html.col-resize grid {
   overflow-y: hidden !important;
 }
 
diff --git a/ui/gl/egl_api_unittest.cc b/ui/gl/egl_api_unittest.cc
index 0813a174..eba8a60 100644
--- a/ui/gl/egl_api_unittest.cc
+++ b/ui/gl/egl_api_unittest.cc
@@ -50,7 +50,7 @@
       SetDisabledExtensionsEGL(disabled_extensions);
     }
     g_driver_egl.InitializeClientExtensionBindings();
-    GLSurfaceEGL::InitializeDisplay(EGL_DEFAULT_DISPLAY);
+    GLSurfaceEGL::InitializeDisplay(EGLDisplayPlatform(EGL_DEFAULT_DISPLAY));
     g_driver_egl.InitializeExtensionBindings();
   }
 
diff --git a/ui/gl/egl_bindings_autogen_mock.cc b/ui/gl/egl_bindings_autogen_mock.cc
index 5af1985..f6562ad 100644
--- a/ui/gl/egl_bindings_autogen_mock.cc
+++ b/ui/gl/egl_bindings_autogen_mock.cc
@@ -419,6 +419,21 @@
   return interface_->QueryDebugKHR(attribute, value);
 }
 
+const char* GL_BINDING_CALL
+MockEGLInterface::Mock_eglQueryDeviceStringEXT(EGLDeviceEXT device,
+                                               EGLint name) {
+  MakeEglMockFunctionUnique("eglQueryDeviceStringEXT");
+  return interface_->QueryDeviceStringEXT(device, name);
+}
+
+EGLBoolean GL_BINDING_CALL
+MockEGLInterface::Mock_eglQueryDevicesEXT(EGLint max_devices,
+                                          EGLDeviceEXT* devices,
+                                          EGLint* num_devices) {
+  MakeEglMockFunctionUnique("eglQueryDevicesEXT");
+  return interface_->QueryDevicesEXT(max_devices, devices, num_devices);
+}
+
 EGLBoolean GL_BINDING_CALL
 MockEGLInterface::Mock_eglQueryDisplayAttribANGLE(EGLDisplay dpy,
                                                   EGLint attribute,
@@ -725,6 +740,11 @@
     return reinterpret_cast<GLFunctionPointerType>(Mock_eglQueryContext);
   if (strcmp(name, "eglQueryDebugKHR") == 0)
     return reinterpret_cast<GLFunctionPointerType>(Mock_eglQueryDebugKHR);
+  if (strcmp(name, "eglQueryDeviceStringEXT") == 0)
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_eglQueryDeviceStringEXT);
+  if (strcmp(name, "eglQueryDevicesEXT") == 0)
+    return reinterpret_cast<GLFunctionPointerType>(Mock_eglQueryDevicesEXT);
   if (strcmp(name, "eglQueryDisplayAttribANGLE") == 0)
     return reinterpret_cast<GLFunctionPointerType>(
         Mock_eglQueryDisplayAttribANGLE);
diff --git a/ui/gl/egl_bindings_autogen_mock.h b/ui/gl/egl_bindings_autogen_mock.h
index c0432be..06f1117 100644
--- a/ui/gl/egl_bindings_autogen_mock.h
+++ b/ui/gl/egl_bindings_autogen_mock.h
@@ -186,6 +186,11 @@
                                                        EGLint* value);
 static EGLBoolean GL_BINDING_CALL Mock_eglQueryDebugKHR(EGLint attribute,
                                                         EGLAttrib* value);
+static const char* GL_BINDING_CALL
+Mock_eglQueryDeviceStringEXT(EGLDeviceEXT device, EGLint name);
+static EGLBoolean GL_BINDING_CALL Mock_eglQueryDevicesEXT(EGLint max_devices,
+                                                          EGLDeviceEXT* devices,
+                                                          EGLint* num_devices);
 static EGLBoolean GL_BINDING_CALL
 Mock_eglQueryDisplayAttribANGLE(EGLDisplay dpy,
                                 EGLint attribute,
diff --git a/ui/gl/generate_bindings.py b/ui/gl/generate_bindings.py
index 5dc81a5..6a89047 100755
--- a/ui/gl/generate_bindings.py
+++ b/ui/gl/generate_bindings.py
@@ -2531,6 +2531,17 @@
                  'client_extensions': ['EGL_KHR_debug'], }],
   'arguments': 'EGLint attribute, EGLAttrib* value', },
 { 'return_type': 'EGLBoolean',
+  'known_as': 'eglQueryDevicesEXT',
+  'versions': [{ 'name': 'eglQueryDevicesEXT',
+                 'client_extensions': ['EGL_EXT_device_enumeration'], }],
+  'arguments':
+      'EGLint max_devices, EGLDeviceEXT* devices, EGLint* num_devices', },
+{ 'return_type': 'const char *',
+  'known_as': 'eglQueryDeviceStringEXT',
+  'versions': [{ 'name': 'eglQueryDeviceStringEXT',
+                 'client_extensions': ['EGL_EXT_device_query'], }],
+  'arguments': 'EGLDeviceEXT device, EGLint name', },
+{ 'return_type': 'EGLBoolean',
   'versions': [{ 'name': 'eglQueryDisplayAttribANGLE',
                  'client_extensions': ['EGL_ANGLE_feature_control'] }],
   'arguments': 'EGLDisplay dpy, EGLint attribute, EGLAttrib* value' },
diff --git a/ui/gl/gl_bindings_api_autogen_egl.h b/ui/gl/gl_bindings_api_autogen_egl.h
index 94b446d..8628d7cd 100644
--- a/ui/gl/gl_bindings_api_autogen_egl.h
+++ b/ui/gl/gl_bindings_api_autogen_egl.h
@@ -160,6 +160,11 @@
                              EGLint attribute,
                              EGLint* value) override;
 EGLBoolean eglQueryDebugKHRFn(EGLint attribute, EGLAttrib* value) override;
+EGLBoolean eglQueryDevicesEXTFn(EGLint max_devices,
+                                EGLDeviceEXT* devices,
+                                EGLint* num_devices) override;
+const char* eglQueryDeviceStringEXTFn(EGLDeviceEXT device,
+                                      EGLint name) override;
 EGLBoolean eglQueryDisplayAttribANGLEFn(EGLDisplay dpy,
                                         EGLint attribute,
                                         EGLAttrib* value) override;
diff --git a/ui/gl/gl_bindings_autogen_egl.cc b/ui/gl/gl_bindings_autogen_egl.cc
index 858ecc32..2f1ed115 100644
--- a/ui/gl/gl_bindings_autogen_egl.cc
+++ b/ui/gl/gl_bindings_autogen_egl.cc
@@ -119,6 +119,12 @@
 
   ext.b_EGL_ANGLE_feature_control =
       gfx::HasExtension(extensions, "EGL_ANGLE_feature_control");
+  ext.b_EGL_EXT_device_base =
+      gfx::HasExtension(extensions, "EGL_EXT_device_base");
+  ext.b_EGL_EXT_device_enumeration =
+      gfx::HasExtension(extensions, "EGL_EXT_device_enumeration");
+  ext.b_EGL_EXT_device_query =
+      gfx::HasExtension(extensions, "EGL_EXT_device_query");
   ext.b_EGL_KHR_debug = gfx::HasExtension(extensions, "EGL_KHR_debug");
 
   if (ext.b_EGL_KHR_debug) {
@@ -137,6 +143,17 @@
         GetGLProcAddress("eglQueryDebugKHR"));
   }
 
+  if (ext.b_EGL_EXT_device_base || ext.b_EGL_EXT_device_enumeration) {
+    fn.eglQueryDevicesEXTFn = reinterpret_cast<eglQueryDevicesEXTProc>(
+        GetGLProcAddress("eglQueryDevicesEXT"));
+  }
+
+  if (ext.b_EGL_EXT_device_base || ext.b_EGL_EXT_device_query) {
+    fn.eglQueryDeviceStringEXTFn =
+        reinterpret_cast<eglQueryDeviceStringEXTProc>(
+            GetGLProcAddress("eglQueryDeviceStringEXT"));
+  }
+
   if (ext.b_EGL_ANGLE_feature_control) {
     fn.eglQueryDisplayAttribANGLEFn =
         reinterpret_cast<eglQueryDisplayAttribANGLEProc>(
@@ -682,6 +699,17 @@
   return driver_->fn.eglQueryDebugKHRFn(attribute, value);
 }
 
+EGLBoolean EGLApiBase::eglQueryDevicesEXTFn(EGLint max_devices,
+                                            EGLDeviceEXT* devices,
+                                            EGLint* num_devices) {
+  return driver_->fn.eglQueryDevicesEXTFn(max_devices, devices, num_devices);
+}
+
+const char* EGLApiBase::eglQueryDeviceStringEXTFn(EGLDeviceEXT device,
+                                                  EGLint name) {
+  return driver_->fn.eglQueryDeviceStringEXTFn(device, name);
+}
+
 EGLBoolean EGLApiBase::eglQueryDisplayAttribANGLEFn(EGLDisplay dpy,
                                                     EGLint attribute,
                                                     EGLAttrib* value) {
@@ -1202,6 +1230,19 @@
   return egl_api_->eglQueryDebugKHRFn(attribute, value);
 }
 
+EGLBoolean TraceEGLApi::eglQueryDevicesEXTFn(EGLint max_devices,
+                                             EGLDeviceEXT* devices,
+                                             EGLint* num_devices) {
+  TRACE_EVENT_BINARY_EFFICIENT0("gpu", "TraceEGLAPI::eglQueryDevicesEXT")
+  return egl_api_->eglQueryDevicesEXTFn(max_devices, devices, num_devices);
+}
+
+const char* TraceEGLApi::eglQueryDeviceStringEXTFn(EGLDeviceEXT device,
+                                                   EGLint name) {
+  TRACE_EVENT_BINARY_EFFICIENT0("gpu", "TraceEGLAPI::eglQueryDeviceStringEXT")
+  return egl_api_->eglQueryDeviceStringEXTFn(device, name);
+}
+
 EGLBoolean TraceEGLApi::eglQueryDisplayAttribANGLEFn(EGLDisplay dpy,
                                                      EGLint attribute,
                                                      EGLAttrib* value) {
@@ -1953,6 +1994,28 @@
   return result;
 }
 
+EGLBoolean LogEGLApi::eglQueryDevicesEXTFn(EGLint max_devices,
+                                           EGLDeviceEXT* devices,
+                                           EGLint* num_devices) {
+  GL_SERVICE_LOG("eglQueryDevicesEXT"
+                 << "(" << max_devices << ", "
+                 << static_cast<const void*>(devices) << ", "
+                 << static_cast<const void*>(num_devices) << ")");
+  EGLBoolean result =
+      egl_api_->eglQueryDevicesEXTFn(max_devices, devices, num_devices);
+  GL_SERVICE_LOG("GL_RESULT: " << result);
+  return result;
+}
+
+const char* LogEGLApi::eglQueryDeviceStringEXTFn(EGLDeviceEXT device,
+                                                 EGLint name) {
+  GL_SERVICE_LOG("eglQueryDeviceStringEXT"
+                 << "(" << device << ", " << name << ")");
+  const char* result = egl_api_->eglQueryDeviceStringEXTFn(device, name);
+  GL_SERVICE_LOG("GL_RESULT: " << result);
+  return result;
+}
+
 EGLBoolean LogEGLApi::eglQueryDisplayAttribANGLEFn(EGLDisplay dpy,
                                                    EGLint attribute,
                                                    EGLAttrib* value) {
diff --git a/ui/gl/gl_bindings_autogen_egl.h b/ui/gl/gl_bindings_autogen_egl.h
index 5ec31e2..546e2c9 100644
--- a/ui/gl/gl_bindings_autogen_egl.h
+++ b/ui/gl/gl_bindings_autogen_egl.h
@@ -196,6 +196,13 @@
                                                          EGLint* value);
 typedef EGLBoolean(GL_BINDING_CALL* eglQueryDebugKHRProc)(EGLint attribute,
                                                           EGLAttrib* value);
+typedef EGLBoolean(GL_BINDING_CALL* eglQueryDevicesEXTProc)(
+    EGLint max_devices,
+    EGLDeviceEXT* devices,
+    EGLint* num_devices);
+typedef const char*(GL_BINDING_CALL* eglQueryDeviceStringEXTProc)(
+    EGLDeviceEXT device,
+    EGLint name);
 typedef EGLBoolean(GL_BINDING_CALL* eglQueryDisplayAttribANGLEProc)(
     EGLDisplay dpy,
     EGLint attribute,
@@ -277,6 +284,9 @@
 
 struct ExtensionsEGL {
   bool b_EGL_ANGLE_feature_control;
+  bool b_EGL_EXT_device_base;
+  bool b_EGL_EXT_device_enumeration;
+  bool b_EGL_EXT_device_query;
   bool b_EGL_KHR_debug;
   bool b_EGL_ANDROID_blob_cache;
   bool b_EGL_ANDROID_get_frame_timestamps;
@@ -356,6 +366,8 @@
   eglQueryAPIProc eglQueryAPIFn;
   eglQueryContextProc eglQueryContextFn;
   eglQueryDebugKHRProc eglQueryDebugKHRFn;
+  eglQueryDevicesEXTProc eglQueryDevicesEXTFn;
+  eglQueryDeviceStringEXTProc eglQueryDeviceStringEXTFn;
   eglQueryDisplayAttribANGLEProc eglQueryDisplayAttribANGLEFn;
   eglQueryStreamKHRProc eglQueryStreamKHRFn;
   eglQueryStreamu64KHRProc eglQueryStreamu64KHRFn;
@@ -550,6 +562,11 @@
                                        EGLint attribute,
                                        EGLint* value) = 0;
   virtual EGLBoolean eglQueryDebugKHRFn(EGLint attribute, EGLAttrib* value) = 0;
+  virtual EGLBoolean eglQueryDevicesEXTFn(EGLint max_devices,
+                                          EGLDeviceEXT* devices,
+                                          EGLint* num_devices) = 0;
+  virtual const char* eglQueryDeviceStringEXTFn(EGLDeviceEXT device,
+                                                EGLint name) = 0;
   virtual EGLBoolean eglQueryDisplayAttribANGLEFn(EGLDisplay dpy,
                                                   EGLint attribute,
                                                   EGLAttrib* value) = 0;
@@ -689,6 +706,9 @@
 #define eglQueryAPI ::gl::g_current_egl_context->eglQueryAPIFn
 #define eglQueryContext ::gl::g_current_egl_context->eglQueryContextFn
 #define eglQueryDebugKHR ::gl::g_current_egl_context->eglQueryDebugKHRFn
+#define eglQueryDevicesEXT ::gl::g_current_egl_context->eglQueryDevicesEXTFn
+#define eglQueryDeviceStringEXT \
+  ::gl::g_current_egl_context->eglQueryDeviceStringEXTFn
 #define eglQueryDisplayAttribANGLE \
   ::gl::g_current_egl_context->eglQueryDisplayAttribANGLEFn
 #define eglQueryStreamKHR ::gl::g_current_egl_context->eglQueryStreamKHRFn
diff --git a/ui/gl/gl_mock_autogen_egl.h b/ui/gl/gl_mock_autogen_egl.h
index 78278ff7..bcdd2a3a 100644
--- a/ui/gl/gl_mock_autogen_egl.h
+++ b/ui/gl/gl_mock_autogen_egl.h
@@ -179,6 +179,12 @@
                         EGLint attribute,
                         EGLint* value));
 MOCK_METHOD2(QueryDebugKHR, EGLBoolean(EGLint attribute, EGLAttrib* value));
+MOCK_METHOD3(QueryDevicesEXT,
+             EGLBoolean(EGLint max_devices,
+                        EGLDeviceEXT* devices,
+                        EGLint* num_devices));
+MOCK_METHOD2(QueryDeviceStringEXT,
+             const char*(EGLDeviceEXT device, EGLint name));
 MOCK_METHOD3(QueryDisplayAttribANGLE,
              EGLBoolean(EGLDisplay dpy, EGLint attribute, EGLAttrib* value));
 MOCK_METHOD4(QueryStreamKHR,
diff --git a/ui/gl/gl_surface_egl.cc b/ui/gl/gl_surface_egl.cc
index ae2e63555..2bbf25b 100644
--- a/ui/gl/gl_surface_egl.cc
+++ b/ui/gl/gl_surface_egl.cc
@@ -166,7 +166,7 @@
 namespace {
 
 EGLDisplay g_egl_display = EGL_NO_DISPLAY;
-EGLNativeDisplayType g_native_display = EGL_DEFAULT_DISPLAY;
+EGLDisplayPlatform g_native_display(EGL_DEFAULT_DISPLAY);
 
 const char* g_egl_extensions = nullptr;
 bool g_egl_create_context_robustness_supported = false;
@@ -297,7 +297,7 @@
 }
 
 EGLDisplay GetPlatformANGLEDisplay(
-    EGLNativeDisplayType native_display,
+    EGLDisplayPlatform native_display,
     EGLenum platform_type,
     const std::vector<std::string>& enabled_features,
     const std::vector<std::string>& disabled_features,
@@ -336,18 +336,20 @@
           reinterpret_cast<EGLAttrib>(disabled_features_attribs.data()));
     }
   }
+  // TODO(dbehr) Add an attrib to Angle to pass EGL platform.
 
   display_attribs.push_back(EGL_NONE);
   // This is an EGL 1.5 function that we know ANGLE supports. It's used to pass
   // EGLAttribs (pointers) instead of EGLints into the display
-  return eglGetPlatformDisplay(EGL_PLATFORM_ANGLE_ANGLE,
-                               reinterpret_cast<void*>(native_display),
-                               &display_attribs[0]);
+  return eglGetPlatformDisplay(
+      EGL_PLATFORM_ANGLE_ANGLE,
+      reinterpret_cast<void*>(native_display.GetDisplay()),
+      &display_attribs[0]);
 }
 
 EGLDisplay GetDisplayFromType(
     DisplayType display_type,
-    EGLNativeDisplayType native_display,
+    EGLDisplayPlatform native_display,
     const std::vector<std::string>& enabled_angle_features,
     const std::vector<std::string>& disabled_angle_features,
     bool disable_all_angle_features) {
@@ -359,7 +361,13 @@
   switch (display_type) {
     case DEFAULT:
     case SWIFT_SHADER:
-      return eglGetDisplay(native_display);
+      if (native_display.GetPlatform() != 0) {
+        return eglGetPlatformDisplay(
+            native_display.GetPlatform(),
+            reinterpret_cast<void*>(native_display.GetDisplay()), nullptr);
+      } else {
+        return eglGetDisplay(native_display.GetDisplay());
+      }
     case ANGLE_D3D9:
       return GetPlatformANGLEDisplay(
           native_display, EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE,
@@ -545,7 +553,7 @@
 #if defined(USE_X11)
   // If we're using ANGLE_NULL, we may not have a display, in which case we
   // can't use XVisualManager.
-  if (g_native_display) {
+  if (g_native_display.GetDisplay() != EGL_DEFAULT_DISPLAY) {
     ui::XVisualManager::GetInstance()->ChooseVisualForWindow(
         true, nullptr, &buffer_size, nullptr, nullptr);
     alpha_size = buffer_size == 32 ? 8 : 0;
@@ -845,7 +853,7 @@
 }
 
 // static
-bool GLSurfaceEGL::InitializeOneOff(EGLNativeDisplayType native_display) {
+bool GLSurfaceEGL::InitializeOneOff(EGLDisplayPlatform native_display) {
   if (initialized_)
     return true;
 
@@ -1011,7 +1019,7 @@
 
 // static
 EGLNativeDisplayType GLSurfaceEGL::GetNativeDisplay() {
-  return g_native_display;
+  return g_native_display.GetDisplay();
 }
 
 // static
@@ -1081,8 +1089,7 @@
 // InitializeDisplay is necessary because the static binding code
 // needs a full Display init before it can query the Display extensions.
 // static
-EGLDisplay GLSurfaceEGL::InitializeDisplay(
-    EGLNativeDisplayType native_display) {
+EGLDisplay GLSurfaceEGL::InitializeDisplay(EGLDisplayPlatform native_display) {
   if (g_egl_display != EGL_NO_DISPLAY) {
     return g_egl_display;
   }
@@ -1501,8 +1508,8 @@
   // views::DesktopWindowTreeHostX11::InitX11Window back to None for the
   // XWindow associated to this surface after the first SwapBuffers has
   // happened, to avoid showing a weird white background while resizing.
-  if (g_native_display && !has_swapped_buffers_) {
-    XSetWindowBackgroundPixmap(g_native_display, window_, 0);
+  if (GetNativeDisplay() && !has_swapped_buffers_) {
+    XSetWindowBackgroundPixmap(GetNativeDisplay(), window_, 0);
     has_swapped_buffers_ = true;
   }
 #endif
diff --git a/ui/gl/gl_surface_egl.h b/ui/gl/gl_surface_egl.h
index e720b62..54f15b0 100644
--- a/ui/gl/gl_surface_egl.h
+++ b/ui/gl/gl_surface_egl.h
@@ -33,6 +33,25 @@
 
 namespace gl {
 
+class EGLDisplayPlatform {
+ public:
+  constexpr EGLDisplayPlatform()
+      : display_(EGL_DEFAULT_DISPLAY), platform_(0), valid_(false) {}
+  explicit constexpr EGLDisplayPlatform(EGLNativeDisplayType display,
+                                        int platform = 0)
+      : display_(display), platform_(platform), valid_(true) {}
+
+  bool Valid() const { return valid_; }
+  int GetPlatform() const { return platform_; }
+  EGLNativeDisplayType GetDisplay() const { return display_; }
+
+ private:
+  EGLNativeDisplayType display_;
+  // 0 for default, or EGL_PLATFORM_* enum.
+  int platform_;
+  bool valid_;
+};
+
 class GLSurfacePresentationHelper;
 
 // If adding a new type, also add it to EGLDisplayType in
@@ -74,12 +93,12 @@
   EGLConfig GetConfig() override;
   GLSurfaceFormat GetFormat() override;
 
-  static bool InitializeOneOff(EGLNativeDisplayType native_display);
+  static bool InitializeOneOff(EGLDisplayPlatform native_display);
   static bool InitializeOneOffForTesting();
   static bool InitializeExtensionSettingsOneOff();
   static void ShutdownOneOff();
   static EGLDisplay GetHardwareDisplay();
-  static EGLDisplay InitializeDisplay(EGLNativeDisplayType native_display);
+  static EGLDisplay InitializeDisplay(EGLDisplayPlatform native_display);
   static EGLNativeDisplayType GetNativeDisplay();
 
   // These aren't particularly tied to surfaces, but since we already
diff --git a/ui/gl/init/gl_initializer_android.cc b/ui/gl/init/gl_initializer_android.cc
index b4374dd..29af2d8 100644
--- a/ui/gl/init/gl_initializer_android.cc
+++ b/ui/gl/init/gl_initializer_android.cc
@@ -94,7 +94,8 @@
   switch (GetGLImplementation()) {
     case kGLImplementationEGLGLES2:
     case kGLImplementationEGLANGLE:
-      if (!GLSurfaceEGL::InitializeOneOff(EGL_DEFAULT_DISPLAY)) {
+      if (!GLSurfaceEGL::InitializeOneOff(
+              EGLDisplayPlatform(EGL_DEFAULT_DISPLAY))) {
         LOG(ERROR) << "GLSurfaceEGL::InitializeOneOff failed.";
         return false;
       }
diff --git a/ui/gl/init/gl_initializer_mac.cc b/ui/gl/init/gl_initializer_mac.cc
index e486fb0..0deebaf 100644
--- a/ui/gl/init/gl_initializer_mac.cc
+++ b/ui/gl/init/gl_initializer_mac.cc
@@ -177,7 +177,7 @@
     case kGLImplementationEGLGLES2:
     case kGLImplementationEGLANGLE:
     case kGLImplementationSwiftShaderGL:
-      if (!GLSurfaceEGL::InitializeOneOff(0)) {
+      if (!GLSurfaceEGL::InitializeOneOff(EGLDisplayPlatform(0))) {
         LOG(ERROR) << "GLSurfaceEGL::InitializeOneOff failed.";
         return false;
       }
diff --git a/ui/gl/init/gl_initializer_win.cc b/ui/gl/init/gl_initializer_win.cc
index ce25c5d..c62800c9 100644
--- a/ui/gl/init/gl_initializer_win.cc
+++ b/ui/gl/init/gl_initializer_win.cc
@@ -192,7 +192,7 @@
       break;
     case kGLImplementationSwiftShaderGL:
     case kGLImplementationEGLANGLE:
-      if (!GLSurfaceEGL::InitializeOneOff(GetDC(nullptr))) {
+      if (!GLSurfaceEGL::InitializeOneOff(EGLDisplayPlatform(GetDC(nullptr)))) {
         LOG(ERROR) << "GLSurfaceEGL::InitializeOneOff failed.";
         return false;
       }
diff --git a/ui/gl/init/gl_initializer_x11.cc b/ui/gl/init/gl_initializer_x11.cc
index 995d439..d9190a95 100644
--- a/ui/gl/init/gl_initializer_x11.cc
+++ b/ui/gl/init/gl_initializer_x11.cc
@@ -152,7 +152,8 @@
     case kGLImplementationSwiftShaderGL:
     case kGLImplementationEGLGLES2:
     case kGLImplementationEGLANGLE:
-      if (!GLSurfaceEGL::InitializeOneOff(gfx::GetXDisplay())) {
+      if (!GLSurfaceEGL::InitializeOneOff(
+              EGLDisplayPlatform(gfx::GetXDisplay()))) {
         LOG(ERROR) << "GLSurfaceEGL::InitializeOneOff failed.";
         return false;
       }
diff --git a/ui/ozone/common/gl_ozone_egl.h b/ui/ozone/common/gl_ozone_egl.h
index 7159f36b6..ec87683 100644
--- a/ui/ozone/common/gl_ozone_egl.h
+++ b/ui/ozone/common/gl_ozone_egl.h
@@ -9,6 +9,7 @@
 #include "base/macros.h"
 #include "third_party/khronos/EGL/eglplatform.h"
 #include "ui/gl/gl_implementation.h"
+#include "ui/gl/gl_surface_egl.h"
 #include "ui/ozone/public/gl_ozone.h"
 
 namespace ui {
@@ -41,9 +42,10 @@
       const gfx::Size& size) override = 0;
 
  protected:
-  // Returns native platform display handle. This is used to obtain the EGL
-  // display connection for the native display.
-  virtual EGLNativeDisplayType GetNativeDisplay() = 0;
+  // Returns native platform display handle and platform type as per
+  // EGL platform extensions.
+  // This is used to obtain the EGL display connection for the native display.
+  virtual gl::EGLDisplayPlatform GetNativeDisplay() = 0;
 
   // Sets up GL bindings for the native surface.
   virtual bool LoadGLES2Bindings(gl::GLImplementation implementation) = 0;
diff --git a/ui/ozone/platform/cast/gl_ozone_egl_cast.cc b/ui/ozone/platform/cast/gl_ozone_egl_cast.cc
index 446afea..36ae5fac 100644
--- a/ui/ozone/platform/cast/gl_ozone_egl_cast.cc
+++ b/ui/ozone/platform/cast/gl_ozone_egl_cast.cc
@@ -79,7 +79,7 @@
   DCHECK(get_display);
   DCHECK(terminate);
 
-  EGLDisplay display = get_display(GetNativeDisplay());
+  EGLDisplay display = get_display(GetNativeDisplay().GetDisplay());
   DCHECK_NE(display, EGL_NO_DISPLAY);
 
   EGLBoolean terminate_result = terminate(display);
@@ -100,9 +100,10 @@
   return gl::InitializeGLSurface(new gl::PbufferGLSurfaceEGL(size));
 }
 
-intptr_t GLOzoneEglCast::GetNativeDisplay() {
+gl::EGLDisplayPlatform GLOzoneEglCast::GetNativeDisplay() {
   CreateDisplayTypeAndWindowIfNeeded();
-  return reinterpret_cast<intptr_t>(display_type_);
+  return gl::EGLDisplayPlatform(
+      reinterpret_cast<EGLNativeDisplayType>(display_type_));
 }
 
 void GLOzoneEglCast::CreateDisplayTypeAndWindowIfNeeded() {
diff --git a/ui/ozone/platform/cast/gl_ozone_egl_cast.h b/ui/ozone/platform/cast/gl_ozone_egl_cast.h
index ace0cbc..79bcfd8 100644
--- a/ui/ozone/platform/cast/gl_ozone_egl_cast.h
+++ b/ui/ozone/platform/cast/gl_ozone_egl_cast.h
@@ -32,7 +32,7 @@
       gfx::AcceleratedWidget widget) override;
   scoped_refptr<gl::GLSurface> CreateOffscreenGLSurface(
       const gfx::Size& size) override;
-  intptr_t GetNativeDisplay() override;
+  gl::EGLDisplayPlatform GetNativeDisplay() override;
   bool LoadGLES2Bindings(gl::GLImplementation implementation) override;
 
   intptr_t GetNativeWindow();
diff --git a/ui/ozone/platform/drm/common/scoped_drm_types.cc b/ui/ozone/platform/drm/common/scoped_drm_types.cc
index e8b7941..ef22941 100644
--- a/ui/ozone/platform/drm/common/scoped_drm_types.cc
+++ b/ui/ozone/platform/drm/common/scoped_drm_types.cc
@@ -5,6 +5,7 @@
 #include "ui/ozone/platform/drm/common/scoped_drm_types.h"
 
 #include <stdint.h>  // required by xf86drmMode.h
+#include <xf86drm.h>
 #include <xf86drmMode.h>
 
 namespace ui {
@@ -55,4 +56,8 @@
   drmModeFreeFB(framebuffer);
 }
 
+void DrmVersionDeleter::operator()(drmVersion* version) const {
+  drmFreeVersion(version);
+}
+
 }  // namespace ui
diff --git a/ui/ozone/platform/drm/common/scoped_drm_types.h b/ui/ozone/platform/drm/common/scoped_drm_types.h
index 32f9dc9..22345a85 100644
--- a/ui/ozone/platform/drm/common/scoped_drm_types.h
+++ b/ui/ozone/platform/drm/common/scoped_drm_types.h
@@ -20,6 +20,7 @@
 typedef struct _drmModeAtomicReq drmModeAtomicReq;
 typedef struct _drmModePropertyBlob drmModePropertyBlobRes;
 typedef struct _drmModeRes drmModeRes;
+typedef struct _drmVersion drmVersion;
 typedef struct drm_color_lut drm_color_lut;
 typedef struct drm_color_ctm drm_color_ctm;
 
@@ -58,6 +59,9 @@
 struct DrmFramebufferDeleter {
   void operator()(drmModeFB* framebuffer) const;
 };
+struct DrmVersionDeleter {
+  void operator()(drmVersion* version) const;
+};
 
 typedef std::unique_ptr<drmModeRes, DrmResourcesDeleter> ScopedDrmResourcesPtr;
 typedef std::unique_ptr<drmModeConnector, DrmConnectorDeleter>
@@ -77,6 +81,7 @@
     ScopedDrmPropertyBlobPtr;
 typedef std::unique_ptr<drmModeFB, DrmFramebufferDeleter>
     ScopedDrmFramebufferPtr;
+typedef std::unique_ptr<drmVersion, DrmVersionDeleter> ScopedDrmVersionPtr;
 
 typedef std::unique_ptr<drm_color_lut, base::FreeDeleter> ScopedDrmColorLutPtr;
 typedef std::unique_ptr<drm_color_ctm, base::FreeDeleter> ScopedDrmColorCtmPtr;
diff --git a/ui/ozone/platform/drm/gpu/drm_gpu_util.cc b/ui/ozone/platform/drm/gpu/drm_gpu_util.cc
index 518916c..bc1ab154 100644
--- a/ui/ozone/platform/drm/gpu/drm_gpu_util.cc
+++ b/ui/ozone/platform/drm/gpu/drm_gpu_util.cc
@@ -4,8 +4,11 @@
 
 #include "ui/ozone/platform/drm/gpu/drm_gpu_util.h"
 
+#include <fcntl.h>
+#include <xf86drm.h>
 #include <xf86drmMode.h>
 
+#include "base/files/scoped_file.h"
 #include "base/trace_event/trace_event.h"
 #include "ui/display/types/gamma_ramp_rgb_entry.h"
 #include "ui/ozone/platform/drm/gpu/drm_device.h"
@@ -117,4 +120,23 @@
   return result;
 }
 
+bool IsDriverName(const char* device_file_name, const char* driver) {
+  base::ScopedFD fd(open(device_file_name, O_RDWR));
+  if (!fd.is_valid()) {
+    LOG(ERROR) << "Failed to open DRM device " << device_file_name;
+    return false;
+  }
+
+  ScopedDrmVersionPtr version(drmGetVersion(fd.get()));
+  if (!version) {
+    LOG(ERROR) << "Failed to query DRM version " << device_file_name;
+    return false;
+  }
+
+  if (strncmp(driver, version->name, version->name_len) == 0)
+    return true;
+
+  return false;
+}
+
 }  // namespace ui
diff --git a/ui/ozone/platform/drm/gpu/drm_gpu_util.h b/ui/ozone/platform/drm/gpu/drm_gpu_util.h
index 1972bef..ab04da74 100644
--- a/ui/ozone/platform/drm/gpu/drm_gpu_util.h
+++ b/ui/ozone/platform/drm/gpu/drm_gpu_util.h
@@ -36,6 +36,9 @@
     const std::vector<display::GammaRampRGBEntry>& lut_in,
     size_t desired_size);
 
+// Check DRM driver name match.
+bool IsDriverName(const char* device_file_name, const char* driver);
+
 }  // namespace ui
 
 #endif  // UI_OZONE_PLATFORM_DRM_GPU_DRM_GPU_UTIL_H_
diff --git a/ui/ozone/platform/drm/gpu/gbm_surface_factory.cc b/ui/ozone/platform/drm/gpu/gbm_surface_factory.cc
index fa32ffe..fcb45780 100644
--- a/ui/ozone/platform/drm/gpu/gbm_surface_factory.cc
+++ b/ui/ozone/platform/drm/gpu/gbm_surface_factory.cc
@@ -5,6 +5,7 @@
 #include "ui/ozone/platform/drm/gpu/gbm_surface_factory.h"
 
 #include <gbm.h>
+#include <xf86drm.h>
 
 #include <memory>
 #include <utility>
@@ -14,6 +15,7 @@
 #include "build/build_config.h"
 #include "third_party/khronos/EGL/egl.h"
 #include "ui/gfx/buffer_format_util.h"
+#include "ui/gfx/extension_set.h"
 #include "ui/gfx/linux/drm_util_linux.h"
 #include "ui/gfx/linux/gbm_defines.h"
 #include "ui/gfx/linux/scoped_gbm_device.h"
@@ -22,6 +24,7 @@
 #include "ui/ozone/common/egl_util.h"
 #include "ui/ozone/common/gl_ozone_egl.h"
 #include "ui/ozone/platform/drm/common/drm_util.h"
+#include "ui/ozone/platform/drm/gpu/drm_gpu_util.h"
 #include "ui/ozone/platform/drm/gpu/drm_thread_proxy.h"
 #include "ui/ozone/platform/drm/gpu/drm_window_proxy.h"
 #include "ui/ozone/platform/drm/gpu/gbm_overlay_surface.h"
@@ -84,7 +87,68 @@
   }
 
  protected:
-  intptr_t GetNativeDisplay() override { return EGL_DEFAULT_DISPLAY; }
+  gl::EGLDisplayPlatform GetNativeDisplay() override {
+    if (native_display_.Valid())
+      return native_display_;
+
+    // Default to null platform
+    native_display_ = gl::EGLDisplayPlatform(EGL_DEFAULT_DISPLAY);
+
+    gl::g_driver_egl.InitializeClientExtensionBindings();
+
+    const char* client_extensions_string =
+        eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);
+
+    gfx::ExtensionSet client_extensions =
+        client_extensions_string
+            ? gfx::MakeExtensionSet(client_extensions_string)
+            : gfx::ExtensionSet();
+
+    if (gfx::HasExtension(client_extensions, "EGL_MESA_platform_surfaceless")) {
+      native_display_ = gl::EGLDisplayPlatform(EGL_DEFAULT_DISPLAY,
+                                               EGL_PLATFORM_SURFACELESS_MESA);
+    }
+
+    if (!(gfx::HasExtension(client_extensions, "EGL_EXT_device_query") &&
+          gfx::HasExtension(client_extensions, "EGL_EXT_platform_device") &&
+          gfx::HasExtension(client_extensions, "EGL_EXT_device_enumeration"))) {
+      LOG(WARNING) << "Platform device extensions not found.";
+      return native_display_;
+    }
+
+    std::vector<EGLDeviceEXT> devices(DRM_MAX_MINOR, EGL_NO_DEVICE_EXT);
+    EGLDeviceEXT amdgpu_device = EGL_NO_DEVICE_EXT;
+    EGLDeviceEXT i915_device = EGL_NO_DEVICE_EXT;
+    EGLint num_devices = 0;
+
+    eglQueryDevicesEXT(DRM_MAX_MINOR, devices.data(), &num_devices);
+    devices.resize(num_devices);
+    for (EGLDeviceEXT device : devices) {
+      const char* filename =
+          eglQueryDeviceStringEXT(device, EGL_DRM_DEVICE_FILE_EXT);
+      if (!filename)  // Not a DRM device.
+        continue;
+      if (IsDriverName(filename, "amdgpu"))
+        amdgpu_device = device;
+      if (IsDriverName(filename, "i915"))
+        i915_device = device;
+    }
+
+    if (amdgpu_device != EGL_NO_DEVICE_EXT) {
+      native_display_ = gl::EGLDisplayPlatform(
+          reinterpret_cast<EGLNativeDisplayType>(amdgpu_device),
+          EGL_PLATFORM_DEVICE_EXT);
+    }
+
+    // If we also have Intel integrated, use it instead.
+    if (i915_device != EGL_NO_DEVICE_EXT) {
+      native_display_ = gl::EGLDisplayPlatform(
+          reinterpret_cast<EGLNativeDisplayType>(i915_device),
+          EGL_PLATFORM_DEVICE_EXT);
+    }
+
+    return native_display_;
+  }
 
   bool LoadGLES2Bindings(gl::GLImplementation impl) override {
     return LoadDefaultEGLGLES2Bindings(impl);
@@ -93,6 +157,7 @@
  private:
   GbmSurfaceFactory* surface_factory_;
   DrmThreadProxy* drm_thread_proxy_;
+  gl::EGLDisplayPlatform native_display_;
 
   DISALLOW_COPY_AND_ASSIGN(GLOzoneEGLGbm);
 };
diff --git a/ui/ozone/platform/headless/headless_surface_factory.cc b/ui/ozone/platform/headless/headless_surface_factory.cc
index 37bd9fc..fc1f0191 100644
--- a/ui/ozone/platform/headless/headless_surface_factory.cc
+++ b/ui/ozone/platform/headless/headless_surface_factory.cc
@@ -192,7 +192,9 @@
 
  protected:
   // GLOzoneEGL:
-  intptr_t GetNativeDisplay() override { return EGL_DEFAULT_DISPLAY; }
+  gl::EGLDisplayPlatform GetNativeDisplay() override {
+    return gl::EGLDisplayPlatform(EGL_DEFAULT_DISPLAY);
+  }
 
   bool LoadGLES2Bindings(gl::GLImplementation implementation) override {
     return LoadDefaultEGLGLES2Bindings(implementation);
diff --git a/ui/ozone/platform/scenic/scenic_surface_factory.cc b/ui/ozone/platform/scenic/scenic_surface_factory.cc
index 9820474a..940e843 100644
--- a/ui/ozone/platform/scenic/scenic_surface_factory.cc
+++ b/ui/ozone/platform/scenic/scenic_surface_factory.cc
@@ -52,8 +52,8 @@
         base::MakeRefCounted<gl::PbufferGLSurfaceEGL>(size));
   }
 
-  EGLNativeDisplayType GetNativeDisplay() override {
-    return EGL_DEFAULT_DISPLAY;
+  gl::EGLDisplayPlatform GetNativeDisplay() override {
+    return gl::EGLDisplayPlatform(EGL_DEFAULT_DISPLAY);
   }
 
  protected:
diff --git a/ui/ozone/platform/wayland/gpu/wayland_surface_factory.cc b/ui/ozone/platform/wayland/gpu/wayland_surface_factory.cc
index 8611c123..b8258fd 100644
--- a/ui/ozone/platform/wayland/gpu/wayland_surface_factory.cc
+++ b/ui/ozone/platform/wayland/gpu/wayland_surface_factory.cc
@@ -46,7 +46,7 @@
       const gfx::Size& size) override;
 
  protected:
-  intptr_t GetNativeDisplay() override;
+  gl::EGLDisplayPlatform GetNativeDisplay() override;
   bool LoadGLES2Bindings(gl::GLImplementation impl) override;
 
  private:
@@ -105,10 +105,11 @@
   }
 }
 
-intptr_t GLOzoneEGLWayland::GetNativeDisplay() {
+gl::EGLDisplayPlatform GLOzoneEGLWayland::GetNativeDisplay() {
   if (connection_)
-    return reinterpret_cast<intptr_t>(connection_->display());
-  return EGL_DEFAULT_DISPLAY;
+    return gl::EGLDisplayPlatform(
+        reinterpret_cast<EGLNativeDisplayType>(connection_->display()));
+  return gl::EGLDisplayPlatform(EGL_DEFAULT_DISPLAY);
 }
 
 bool GLOzoneEGLWayland::LoadGLES2Bindings(gl::GLImplementation impl) {
diff --git a/ui/ozone/platform/windows/windows_surface_factory.cc b/ui/ozone/platform/windows/windows_surface_factory.cc
index 11700e9..83b51fb 100644
--- a/ui/ozone/platform/windows/windows_surface_factory.cc
+++ b/ui/ozone/platform/windows/windows_surface_factory.cc
@@ -46,8 +46,8 @@
 
  protected:
   // GLOzoneEGL:
-  HDC GetNativeDisplay() override {
-    return GetWindowDC(nullptr);
+  gl::EGLDisplayPlatform GetNativeDisplay() override {
+    return gl::EGLDisplayPlatform(GetWindowDC(nullptr));
   }
 
   bool LoadGLES2Bindings(gl::GLImplementation implementation) override {
diff --git a/ui/ozone/platform/x11/x11_surface_factory.cc b/ui/ozone/platform/x11/x11_surface_factory.cc
index e2400a3..fa22890 100644
--- a/ui/ozone/platform/x11/x11_surface_factory.cc
+++ b/ui/ozone/platform/x11/x11_surface_factory.cc
@@ -55,8 +55,9 @@
 
  protected:
   // GLOzoneEGL:
-  intptr_t GetNativeDisplay() override {
-    return reinterpret_cast<intptr_t>(gfx::GetXDisplay());
+  gl::EGLDisplayPlatform GetNativeDisplay() override {
+    return gl::EGLDisplayPlatform(
+        reinterpret_cast<EGLNativeDisplayType>(gfx::GetXDisplay()));
   }
 
   bool LoadGLES2Bindings(gl::GLImplementation implementation) override {
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc b/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc
index 9662f19..7588eed 100644
--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc
+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc
@@ -211,7 +211,7 @@
   base::ThreadTaskRunnerHandle::Get()->PostTask(
       FROM_HERE, base::BindOnce(&DesktopWindowTreeHostPlatform::CloseNow,
                                 close_widget_factory_.GetWeakPtr()));
-}  // namespace views
+}
 
 void DesktopWindowTreeHostPlatform::CloseNow() {
   if (!platform_window())
diff --git a/url/android/native_java_unittests/src/org/chromium/url/GURLJavaTest.java b/url/android/native_java_unittests/src/org/chromium/url/GURLJavaTest.java
index 87661b692..0517ddf 100644
--- a/url/android/native_java_unittests/src/org/chromium/url/GURLJavaTest.java
+++ b/url/android/native_java_unittests/src/org/chromium/url/GURLJavaTest.java
@@ -116,10 +116,10 @@
         GURL url2 = new GURL("javascript:window.alert(\"hello,world\");");
         URI uri = new URI("filesystem:http://user:pass@google.com:21/blah#baz");
 
-        Assert.assertEquals(url1.getOrigin().getSpec(), kExpectedOrigin1);
-        Assert.assertEquals(url2.getOrigin().getSpec(), kExpectedOrigin2);
+        Assert.assertEquals(kExpectedOrigin1, url1.getOrigin().getSpec());
+        Assert.assertEquals(kExpectedOrigin2, url2.getOrigin().getSpec());
         URI origin = uri.getOrigin();
-        Assert.assertEquals(origin.getSpec(), kExpectedOrigin1);
+        Assert.assertEquals(kExpectedOrigin1, origin.getSpec());
     }
 
     @CalledByNativeJavaTest
diff --git a/weblayer/BUILD.gn b/weblayer/BUILD.gn
index f704e64..dead775 100644
--- a/weblayer/BUILD.gn
+++ b/weblayer/BUILD.gn
@@ -194,31 +194,6 @@
     "utility/content_utility_client_impl.h",
   ]
 
-  if (is_android) {
-    sources += [
-      "browser/android/exception_filter.cc",
-      "browser/android/exception_filter.h",
-      "browser/android/metrics/uma_utils.cc",
-      "browser/android/metrics/uma_utils.h",
-      "browser/android/metrics/weblayer_metrics_service_client.cc",
-      "browser/android/metrics/weblayer_metrics_service_client.h",
-      "browser/devtools_manager_delegate_android.cc",
-      "browser/devtools_manager_delegate_android.h",
-      "browser/devtools_server_android.cc",
-      "browser/devtools_server_android.h",
-      "browser/weblayer_impl_android.cc",
-      "renderer/url_loader_throttle_provider.cc",
-      "renderer/url_loader_throttle_provider.h",
-    ]
-  }
-
-  if (enable_captive_portal_detection) {
-    sources += [
-      "browser/captive_portal_service_factory.cc",
-      "browser/captive_portal_service_factory.h",
-    ]
-  }
-
   configs += [
     "//build/config:precompiled_headers",
 
@@ -306,6 +281,10 @@
   ]
 
   if (enable_captive_portal_detection) {
+    sources += [
+      "browser/captive_portal_service_factory.cc",
+      "browser/captive_portal_service_factory.h",
+    ]
     deps += [ "//components/captive_portal/content" ]
   }
 
@@ -318,10 +297,39 @@
 
   if (is_android) {
     sources += [
+      "app/jni_onload.cc",
+      "app/jni_onload.h",
+      "browser/android/exception_filter.cc",
+      "browser/android/exception_filter.h",
+      "browser/android/metrics/uma_utils.cc",
+      "browser/android/metrics/uma_utils.h",
+      "browser/android/metrics/weblayer_metrics_service_client.cc",
+      "browser/android/metrics/weblayer_metrics_service_client.h",
+      "browser/content_view_render_view.cc",
+      "browser/content_view_render_view.h",
+      "browser/devtools_manager_delegate_android.cc",
+      "browser/devtools_manager_delegate_android.h",
+      "browser/devtools_server_android.cc",
+      "browser/devtools_server_android.h",
+      "browser/download_callback_proxy.cc",
+      "browser/download_callback_proxy.h",
+      "browser/error_page_callback_proxy.cc",
+      "browser/error_page_callback_proxy.h",
+      "browser/fullscreen_callback_proxy.cc",
+      "browser/fullscreen_callback_proxy.h",
+      "browser/new_tab_callback_proxy.cc",
+      "browser/new_tab_callback_proxy.h",
+      "browser/tab_callback_proxy.cc",
+      "browser/tab_callback_proxy.h",
+      "browser/top_controls_container_view.cc",
+      "browser/top_controls_container_view.h",
+      "browser/weblayer_impl_android.cc",
       "common/crash_reporter/crash_keys.cc",
       "common/crash_reporter/crash_keys.h",
       "common/crash_reporter/crash_reporter_client.cc",
       "common/crash_reporter/crash_reporter_client.h",
+      "renderer/url_loader_throttle_provider.cc",
+      "renderer/url_loader_throttle_provider.h",
     ]
     deps += [
       "//android_webview:generate_aw_resources",
@@ -330,6 +338,7 @@
       "//components/autofill/android:provider",
       "//components/crash/android:crashpad_main",
       "//components/embedder_support/android:context_menu",
+      "//components/embedder_support/android:web_contents_delegate",
       "//components/embedder_support/android/metrics",
       "//components/javascript_dialogs",
       "//components/metrics",
@@ -338,8 +347,13 @@
       "//components/safe_browsing/content/renderer:throttles",
       "//components/safe_browsing/core/common",
       "//components/version_info/android:channel_getter",
+      "//services/resource_coordinator/public/cpp/memory_instrumentation:browser",
+      "//ui/android",
+      "//weblayer/browser/java:jni",
       "//weblayer/browser/safe_browsing:safe_browsing",
     ]
+  } else {
+    deps += [ "//ui/views/controls/webview" ]
   }
 
   if (enable_vulkan) {
@@ -363,36 +377,6 @@
     ]
   }
 
-  if (is_android) {
-    deps += [
-      "//components/embedder_support/android:web_contents_delegate",
-      "//services/resource_coordinator/public/cpp/memory_instrumentation:browser",
-      "//ui/android",
-      "//weblayer/browser/java:jni",
-    ]
-    sources += [
-      "app/jni_onload.cc",
-      "app/jni_onload.h",
-      "browser/content_view_render_view.cc",
-      "browser/content_view_render_view.h",
-      "browser/download_callback_proxy.cc",
-      "browser/download_callback_proxy.h",
-      "browser/error_page_callback_proxy.cc",
-      "browser/error_page_callback_proxy.h",
-      "browser/fullscreen_callback_proxy.cc",
-      "browser/fullscreen_callback_proxy.h",
-      "browser/new_tab_callback_proxy.cc",
-      "browser/new_tab_callback_proxy.h",
-      "browser/tab_callback_proxy.cc",
-      "browser/tab_callback_proxy.h",
-      "browser/top_controls_container_view.cc",
-      "browser/top_controls_container_view.h",
-    ]
-  }
-  if (!is_android) {
-    deps += [ "//ui/views/controls/webview" ]
-  }
-
   if (toolkit_views) {
     deps += [ "//ui/views" ]
   }
diff --git a/weblayer/browser/android/javatests/BUILD.gn b/weblayer/browser/android/javatests/BUILD.gn
index f4b46e8..f74dbdf 100644
--- a/weblayer/browser/android/javatests/BUILD.gn
+++ b/weblayer/browser/android/javatests/BUILD.gn
@@ -13,6 +13,7 @@
     "src/org/chromium/weblayer/test/CloseTabNewTabCallbackImpl.java",
     "src/org/chromium/weblayer/test/CrashReporterTest.java",
     "src/org/chromium/weblayer/test/DataClearingTest.java",
+    "src/org/chromium/weblayer/test/DowngradeTest.java",
     "src/org/chromium/weblayer/test/DownloadCallbackTest.java",
     "src/org/chromium/weblayer/test/ErrorPageCallbackTest.java",
     "src/org/chromium/weblayer/test/EventUtils.java",
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/BrowserFragmentLifecycleTest.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/BrowserFragmentLifecycleTest.java
index 9813413..a83a3a5 100644
--- a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/BrowserFragmentLifecycleTest.java
+++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/BrowserFragmentLifecycleTest.java
@@ -32,45 +32,42 @@
     public InstrumentationActivityTestRule mActivityTestRule =
             new InstrumentationActivityTestRule();
 
+    private Tab getTab() {
+        return TestThreadUtils.runOnUiThreadBlockingNoException(
+                () -> mActivityTestRule.getActivity().getTab());
+    }
+
     @Test
     @SmallTest
     public void successfullyLoadsUrlAfterRecreation() {
-        InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl("about:blank");
-        Tab tab = TestThreadUtils.runOnUiThreadBlockingNoException(() -> activity.getTab());
+        mActivityTestRule.launchShellWithUrl("about:blank");
         String url = "data:text,foo";
-        mActivityTestRule.navigateAndWait(tab, url, false);
+        mActivityTestRule.navigateAndWait(getTab(), url, false);
 
         mActivityTestRule.recreateActivity();
 
-        InstrumentationActivity newActivity = mActivityTestRule.getActivity();
-        tab = TestThreadUtils.runOnUiThreadBlockingNoException(() -> newActivity.getTab());
         url = "data:text,bar";
-        mActivityTestRule.navigateAndWait(tab, url, false);
+        mActivityTestRule.navigateAndWait(getTab(), url, false);
     }
 
     @Test
     @SmallTest
     public void restoreAfterRecreate() throws Throwable {
-        InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl("about:blank");
-        Tab tab = TestThreadUtils.runOnUiThreadBlockingNoException(() -> activity.getTab());
+        mActivityTestRule.launchShellWithUrl("about:blank");
         String url = "data:text,foo";
-        mActivityTestRule.navigateAndWait(tab, url, false);
+        mActivityTestRule.navigateAndWait(getTab(), url, false);
 
         mActivityTestRule.recreateActivity();
 
-        InstrumentationActivity newActivity = mActivityTestRule.getActivity();
-        waitForTabToFinishRestore(TestThreadUtils.runOnUiThreadBlocking(() -> {
-            return mActivityTestRule.getActivity().getTab();
-        }),
-                url);
+        waitForTabToFinishRestore(getTab(), url);
     }
 
     // https://crbug.com/1021041
     @Test
     @SmallTest
     public void handlesFragmentDestroyWhileNavigating() {
-        BoundedCountDownLatch latch = new BoundedCountDownLatch(1);
         InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl("about:blank");
+        BoundedCountDownLatch latch = new BoundedCountDownLatch(1);
         TestThreadUtils.runOnUiThreadBlocking(() -> {
             NavigationController navigationController = activity.getTab().getNavigationController();
             navigationController.registerNavigationCallback(new NavigationCallback() {
@@ -118,11 +115,10 @@
         extras.putString(InstrumentationActivity.EXTRA_PERSISTENCE_ID, "x");
         final String url = mActivityTestRule.getTestDataURL("simple_page.html");
         mActivityTestRule.launchShellWithUrl(url, extras);
+
         mActivityTestRule.recreateActivity();
 
-        InstrumentationActivity newActivity = mActivityTestRule.getActivity();
-        Assert.assertNotNull(newActivity);
-        Tab tab = TestThreadUtils.runOnUiThreadBlockingNoException(() -> newActivity.getTab());
+        Tab tab = getTab();
         Assert.assertNotNull(tab);
         waitForTabToFinishRestore(tab, url);
     }
@@ -148,7 +144,7 @@
         Bundle extras = new Bundle();
         extras.putString(InstrumentationActivity.EXTRA_PERSISTENCE_ID, "x");
         final String url = mActivityTestRule.getTestDataURL("simple_page.html");
-        InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(url, extras);
+        mActivityTestRule.launchShellWithUrl(url, extras);
         final String initialTabId = TestThreadUtils.runOnUiThreadBlocking(
                 () -> { return mActivityTestRule.getActivity().getTab().getGuid(); });
         Assert.assertNotNull(initialTabId);
@@ -156,8 +152,7 @@
 
         mActivityTestRule.recreateActivity();
 
-        InstrumentationActivity newActivity = mActivityTestRule.getActivity();
-        Tab tab = TestThreadUtils.runOnUiThreadBlockingNoException(() -> newActivity.getTab());
+        Tab tab = getTab();
         Assert.assertNotNull(tab);
         waitForTabToFinishRestore(tab, url);
         final String restoredTabId =
@@ -169,17 +164,16 @@
     @SmallTest
     public void restoreTabGuidAfterRecreate() throws Throwable {
         InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl("about:blank");
-        final Tab tab = TestThreadUtils.runOnUiThreadBlockingNoException(() -> activity.getTab());
-        final String initialTabId = TestThreadUtils.runOnUiThreadBlocking(
-                () -> { return mActivityTestRule.getActivity().getTab().getGuid(); });
+        final Tab tab = getTab();
+        final String initialTabId =
+                TestThreadUtils.runOnUiThreadBlocking(() -> { return tab.getGuid(); });
         String url = "data:text,foo";
         mActivityTestRule.navigateAndWait(tab, url, false);
 
         mActivityTestRule.recreateActivity();
 
-        InstrumentationActivity newActivity = mActivityTestRule.getActivity();
-        final Tab restoredTab =
-                TestThreadUtils.runOnUiThreadBlockingNoException(() -> newActivity.getTab());
+        final Tab restoredTab = getTab();
+        Assert.assertNotEquals(tab, restoredTab);
         waitForTabToFinishRestore(restoredTab, url);
         final String restoredTabId = TestThreadUtils.runOnUiThreadBlockingNoException(
                 () -> { return restoredTab.getGuid(); });
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/DowngradeTest.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/DowngradeTest.java
new file mode 100644
index 0000000..f04b5f66
--- /dev/null
+++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/DowngradeTest.java
@@ -0,0 +1,108 @@
+// 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.weblayer.test;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import static org.chromium.content_public.browser.test.util.TestThreadUtils.runOnUiThreadBlocking;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.support.test.filters.SmallTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.chromium.base.ContextUtils;
+import org.chromium.base.PathUtils;
+import org.chromium.weblayer.shell.InstrumentationActivity;
+
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * Tests that WebLayer version changes handle data correctly.
+ */
+@RunWith(WebLayerJUnit4ClassRunner.class)
+public class DowngradeTest {
+    public static final String PREF_LAST_VERSION_CODE =
+            "org.chromium.weblayer.last_version_code_used";
+
+    @Rule
+    public InstrumentationActivityTestRule mActivityTestRule =
+            new InstrumentationActivityTestRule();
+
+    // A test file in the app's data directory. This should never get deleted.
+    private File mAppFile;
+    // A test file in WebLayer's data directory. This should get deleted when we downgrade.
+    private File mWebLayerDataFile;
+
+    @Before
+    public void setUp() throws IOException, PackageManager.NameNotFoundException {
+        PathUtils.setPrivateDataDirectorySuffix("weblayer", "weblayer");
+        mWebLayerDataFile = new File(PathUtils.getDataDirectory(), "testWebLayerFile");
+        assertTrue(mWebLayerDataFile.createNewFile());
+
+        Context context = ContextUtils.getApplicationContext();
+        PackageManager packageManager = context.getPackageManager();
+        PackageInfo packageInfo = packageManager.getPackageInfo(context.getPackageName(), 0);
+        mAppFile = new File(packageInfo.applicationInfo.dataDir, "testAppFile");
+        assertTrue(mAppFile.createNewFile());
+    }
+
+    @After
+    public void tearDown() {
+        mAppFile.delete();
+        mWebLayerDataFile.delete();
+    }
+
+    @Test
+    @SmallTest
+    public void testDowngradeDeletesData() throws IOException {
+        SharedPreferences prefs = ContextUtils.getAppSharedPreferences();
+        prefs.edit().putInt(PREF_LAST_VERSION_CODE, 9999_000_00).apply();
+
+        InstrumentationActivity activity = mActivityTestRule.launchWithProfile("profile");
+        runOnUiThreadBlocking(
+                () -> { activity.loadWebLayerSync(ContextUtils.getApplicationContext()); });
+
+        assertFalse(mWebLayerDataFile.exists());
+        assertTrue(mAppFile.exists());
+    }
+
+    @Test
+    @SmallTest
+    public void testUnknownLastVersionKeepsData() throws IOException {
+        SharedPreferences prefs = ContextUtils.getAppSharedPreferences();
+        assertFalse(prefs.contains(PREF_LAST_VERSION_CODE));
+
+        InstrumentationActivity activity = mActivityTestRule.launchWithProfile("profile");
+        runOnUiThreadBlocking(
+                () -> { activity.loadWebLayerSync(ContextUtils.getApplicationContext()); });
+
+        assertTrue(mWebLayerDataFile.exists());
+        assertTrue(mAppFile.exists());
+    }
+
+    @Test
+    @SmallTest
+    public void testNewVersionKeepsData() {
+        SharedPreferences prefs = ContextUtils.getAppSharedPreferences();
+        prefs.edit().putInt(PREF_LAST_VERSION_CODE, 1_000_00).apply();
+
+        InstrumentationActivity activity = mActivityTestRule.launchWithProfile("profile");
+        runOnUiThreadBlocking(
+                () -> { activity.loadWebLayerSync(ContextUtils.getApplicationContext()); });
+
+        assertTrue(mWebLayerDataFile.exists());
+        assertTrue(mAppFile.exists());
+    }
+}
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/WebLayerImpl.java b/weblayer/browser/java/org/chromium/weblayer_private/WebLayerImpl.java
index 1ae937c7..698af96 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/WebLayerImpl.java
+++ b/weblayer/browser/java/org/chromium/weblayer_private/WebLayerImpl.java
@@ -6,6 +6,7 @@
 
 import android.content.Context;
 import android.content.Intent;
+import android.content.SharedPreferences;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.res.AssetManager;
@@ -26,6 +27,7 @@
 import org.chromium.base.CommandLine;
 import org.chromium.base.ContentUriUtils;
 import org.chromium.base.ContextUtils;
+import org.chromium.base.FileUtils;
 import org.chromium.base.Log;
 import org.chromium.base.PathUtils;
 import org.chromium.base.StrictModeContext;
@@ -78,6 +80,9 @@
     // signature requirements on the implementation, nor does it use the production code path to
     // load the code. Do not set this in production APKs!
     private static final String PACKAGE_MANIFEST_KEY = "org.chromium.weblayer.WebLayerPackage";
+    // SharedPreferences key storing the versionCode of the most recently loaded WebLayer library.
+    public static final String PREF_LAST_VERSION_CODE =
+            "org.chromium.weblayer.last_version_code_used";
 
     private final ProfileManager mProfileManager = new ProfileManager();
 
@@ -233,11 +238,10 @@
             }
         }
 
-        // Creating the Android shared preferences object causes I/O. Prewarm during
-        // initialization to avoid this occurring randomly later.
-        // TODO: Do this on a background thread.
+        // Creating the Android shared preferences object causes I/O.
         try (StrictModeContext ignored = StrictModeContext.allowDiskWrites()) {
-            ContextUtils.getAppSharedPreferences();
+            SharedPreferences prefs = ContextUtils.getAppSharedPreferences();
+            deleteDataIfPackageDowngrade(prefs, packageInfo);
         }
 
         DeviceUtils.addDeviceSpecificUserAgentSwitch();
@@ -457,6 +461,49 @@
         }
     }
 
+    private static void deleteDataIfPackageDowngrade(
+            SharedPreferences prefs, PackageInfo packageInfo) {
+        int previousVersion = prefs.getInt(PREF_LAST_VERSION_CODE, 0);
+        int currentVersion = packageInfo.versionCode;
+        if (getBranchFromVersionCode(currentVersion) < getBranchFromVersionCode(previousVersion)) {
+            // WebLayer was downgraded since the last run. Delete the data and cache directories.
+            File dataDir = new File(PathUtils.getDataDirectory());
+            Log.i(TAG,
+                    "WebLayer package downgraded from " + previousVersion + " to " + currentVersion
+                            + "; deleting contents of " + dataDir);
+            deleteDirectoryContents(dataDir);
+        }
+        if (previousVersion != currentVersion) {
+            prefs.edit().putInt(PREF_LAST_VERSION_CODE, currentVersion).apply();
+        }
+    }
+
+    /**
+     * Chromium versionCodes follow the scheme "BBBBPPPAX":
+     * BBBB: 4 digit branch number. It monotonically increases over time.
+     * PPP:  Patch number in the branch. It is padded with zeroes to the left. These three digits
+     *       may change their meaning in the future.
+     * A:    Architecture digit.
+     * X:    A digit to differentiate APKs for other reasons.
+     *
+     * @return The branch number of versionCode.
+     */
+    private static int getBranchFromVersionCode(int versionCode) {
+        return versionCode / 1_000_00;
+    }
+
+    private static void deleteDirectoryContents(File directory) {
+        File[] files = directory.listFiles();
+        if (files == null) {
+            return;
+        }
+        for (File file : files) {
+            if (!FileUtils.recursivelyDeleteFile(file, FileUtils.DELETE_ALL)) {
+                Log.w(TAG, "Failed to delete " + file);
+            }
+        }
+    }
+
     @NativeMethods
     interface Natives {
         void setRemoteDebuggingEnabled(boolean enabled);
diff --git a/weblayer/shell/android/BUILD.gn b/weblayer/shell/android/BUILD.gn
index d0144b3..85ad50a 100644
--- a/weblayer/shell/android/BUILD.gn
+++ b/weblayer/shell/android/BUILD.gn
@@ -27,6 +27,7 @@
 
   deps = [
     ":weblayer_shell_resources",
+    "//base:base_java",
     "//third_party/android_deps:android_support_v4_java",
     "//weblayer/public/java",
   ]
diff --git a/weblayer/shell/android/shell_apk/DEPS b/weblayer/shell/android/shell_apk/DEPS
index fd9e2a83..6b09699 100644
--- a/weblayer/shell/android/shell_apk/DEPS
+++ b/weblayer/shell/android/shell_apk/DEPS
@@ -1,4 +1,5 @@
 noparent = True
 include_rules = [
+  "+base/android/java",
   "+weblayer/public/java",
 ]
diff --git a/weblayer/shell/android/shell_apk/src/org/chromium/weblayer/shell/InstrumentationActivity.java b/weblayer/shell/android/shell_apk/src/org/chromium/weblayer/shell/InstrumentationActivity.java
index fa75249..b245ecf7 100644
--- a/weblayer/shell/android/shell_apk/src/org/chromium/weblayer/shell/InstrumentationActivity.java
+++ b/weblayer/shell/android/shell_apk/src/org/chromium/weblayer/shell/InstrumentationActivity.java
@@ -21,6 +21,7 @@
 import android.widget.LinearLayout;
 import android.widget.RelativeLayout;
 
+import org.chromium.base.ContextUtils;
 import org.chromium.weblayer.Browser;
 import org.chromium.weblayer.Profile;
 import org.chromium.weblayer.Tab;
@@ -149,7 +150,8 @@
 
     private void createWebLayerAsync() {
         try {
-            WebLayer.loadAsync(getApplicationContext(), webLayer -> onWebLayerReady());
+            // Get the Context from ContextUtils so tests get the wrapped version.
+            WebLayer.loadAsync(ContextUtils.getApplicationContext(), webLayer -> onWebLayerReady());
         } catch (UnsupportedVersionException e) {
             throw new RuntimeException("Failed to initialize WebLayer", e);
         }