diff --git a/AUTHORS b/AUTHORS
index 2e79a84..03f2b4b 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -72,6 +72,7 @@
 Andrew Boyarshin <andrew.boyarshin@gmail.com>
 Andrew Brampton <me@bramp.net>
 Andrew Hung <andrhung@amazon.com>
+Andrew Jorgensen <ajorgens@amazon.com>
 Andrew MacPherson <andrew.macpherson@soundtrap.com>
 Andrew Tulloch <andrew@tullo.ch>
 Anish Patankar <anish.p@samsung.com>
diff --git a/BUILD.gn b/BUILD.gn
index b3febd5..1c2b64e 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -67,6 +67,7 @@
     "//base:base_perftests",
     "//base:base_unittests",
     "//chrome/installer",
+    "//chrome/updater",
     "//net:net_unittests",
     "//skia:skia_unittests",
     "//sql:sql_unittests",
diff --git a/DEPS b/DEPS
index 900517d..d74bef9b9 100644
--- a/DEPS
+++ b/DEPS
@@ -121,11 +121,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': 'be6549a59d50b4f7368babd1092af4f49d9a51ec',
+  'skia_revision': '41169206c475f8c83542b565001ca830b5467b04',
   # 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': '22de82ca15b5c91bb975e86e0097dbf7a8af9ad4',
+  'v8_revision': 'dbd3d52d957384c31d02b6f6737078c661a49eef',
   # 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.
@@ -133,7 +133,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': '4b2e00f4d1801c6aa00cb58f21c29735eb355bb2',
+  'angle_revision': '82fddcb1f3e3daa72b5306fc2d1e324e4c90943c',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling build tools
   # and whatever else without interference from each other.
@@ -141,7 +141,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
-  'swiftshader_revision': '755d744023fff10ae1176224ad6ae419440e70e6',
+  'swiftshader_revision': 'ec061ddc31ca9e0a01d006cf197272c7d1db71cf',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
@@ -177,7 +177,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling HarfBuzz
   # and whatever else without interference from each other.
-  'harfbuzz_revision': '36fb2b4da9718a86978fa07c99ba4345f7ca9b4b',
+  'harfbuzz_revision': 'fe532923101586e316b300d419a337d357cd93da',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
@@ -229,7 +229,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'spv_tools_revision': 'cf011f990163ebb3c61252dea149cd9e1799bdf2',
+  'spv_tools_revision': '5d6b4c4b1b651ec0f129a106485b1e5bea87ef6c',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -249,7 +249,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': '1605d8b142ed791a195d0250992893bb8033fe01',
+  'quiche_revision': 'e72b8874b23a8219c68fd7f1d7db097fa4811114',
 }
 
 # Only these hosts are allowed for dependencies in this DEPS file.
@@ -679,7 +679,7 @@
 
   # Build tools for Chrome OS. Note: This depends on third_party/pyelftools.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '2900d34d5975cc467e393fcf26bad649706e858c',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '40cd93a533288d914a57a3b84e51ea2d4c92d5a8',
       'condition': 'checkout_linux',
   },
 
@@ -1036,7 +1036,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' +  'acd0b962902304bc70cc265855ce7cec29cabcc5',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' +  '6d7323f8cd3be2199641075e5a842c62b6a3a65b',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + 'ac0d98b5cee6c024b0cffeb4f8f45b6fc5ccdb78',
@@ -1199,7 +1199,7 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + 'db52df17f0d012983dc281e4864c71485a86bd0e',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '3d02384487f85c0915f0ef379781ba3a2f765b3f',
+    Var('webrtc_git') + '/src.git' + '@' + '949f0fdc10bf36a0c8379d4b378ac2036675756e',
 
   'src/third_party/xdg-utils': {
       'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d',
@@ -1240,7 +1240,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@86579d2a3a9fff1ed85e36e814d592d9471bd070',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@3d585408ab5716892c840ca4abc93ef0f5ebbde5',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/android_webview/tools/system_webview_shell/test/data/webexposed/not-webview-exposed.txt b/android_webview/tools/system_webview_shell/test/data/webexposed/not-webview-exposed.txt
index 853e086a..4b79bfb0 100644
--- a/android_webview/tools/system_webview_shell/test/data/webexposed/not-webview-exposed.txt
+++ b/android_webview/tools/system_webview_shell/test/data/webexposed/not-webview-exposed.txt
@@ -54,15 +54,20 @@
     getter remote                      # crbug.com/521319
 
 # permissions API (crbug.com/490120), presentation API (crbug.com/521319),
-# share API (crbug.com/765923) and custom scheme handlers (crbug.com/589502)
-# not supported in webview
+# share API (crbug.com/765923), custom scheme handlers (crbug.com/589502)
+# and media session API (crbug.com/925997) are not supported in webview.
 interface Navigator
+    getter mediaSession                # crbug.com/925997
     getter permissions                 # crbug.com/490120
     getter presentation                # crbug.com/521319
     method registerProtocolHandler     # crbug.com/589502
     method unregisterProtocolHandler   # crbug.com/589502
     method share                       # crbug.com/765923
 
+# Media Session API is not enabled in Android WebView (crbug.com/925997)
+interface MediaMetadata
+interface MediaSession
+
 # notifications not yet supported in webview, crbug.com/551446
 interface ServiceWorkerRegistration : EventTarget
     getter pushManager
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index afa1de18..1e2f0bd 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -154,6 +154,7 @@
     "accessibility/touch_exploration_manager.h",
     "animation/animation_change_type.h",
     "app_list/app_list_controller_impl.cc",
+    "app_list/app_list_controller_observer.h",
     "app_list/app_list_presenter_delegate_impl.cc",
     "app_list/app_list_presenter_delegate_impl.h",
     "app_list/home_launcher_gesture_handler.cc",
diff --git a/ash/app_list/app_list_controller_impl.cc b/ash/app_list/app_list_controller_impl.cc
index 5197605..65d9134 100644
--- a/ash/app_list/app_list_controller_impl.cc
+++ b/ash/app_list/app_list_controller_impl.cc
@@ -7,6 +7,7 @@
 #include <utility>
 #include <vector>
 
+#include "ash/app_list/app_list_controller_observer.h"
 #include "ash/app_list/app_list_presenter_delegate_impl.h"
 #include "ash/app_list/home_launcher_gesture_handler.h"
 #include "ash/app_list/model/app_list_folder_item.h"
@@ -833,16 +834,45 @@
     client_->GetNavigableContentsFactory(std::move(request));
 }
 
-void AppListControllerImpl::OnVisibilityChanged(bool visible) {
-  if (client_)
-    client_->OnAppListVisibilityChanged(visible);
+void AppListControllerImpl::AddObserver(AppListControllerObserver* observer) {
+  observers_.AddObserver(observer);
 }
 
-void AppListControllerImpl::OnTargetVisibilityChanged(bool visible) {
+void AppListControllerImpl::RemoveObserver(
+    AppListControllerObserver* observer) {
+  observers_.RemoveObserver(observer);
+}
+
+void AppListControllerImpl::NotifyAppListVisibilityChanged(bool visible,
+                                                           int64_t display_id) {
+  // Notify chrome of visibility changes.
+  if (client_)
+    client_->OnAppListVisibilityChanged(visible);
+
+  for (auto& observer : observers_)
+    observer.OnAppListVisibilityChanged(visible, display_id);
+}
+
+void AppListControllerImpl::NotifyAppListTargetVisibilityChanged(bool visible) {
+  // Notify chrome of target visibility changes.
   if (client_)
     client_->OnAppListTargetVisibilityChanged(visible);
 }
 
+void AppListControllerImpl::NotifyHomeLauncherTargetPositionChanged(
+    bool showing,
+    int64_t display_id) {
+  for (auto& observer : observers_)
+    observer.OnHomeLauncherTargetPositionChanged(showing, display_id);
+}
+
+void AppListControllerImpl::NotifyHomeLauncherAnimationComplete(
+    bool shown,
+    int64_t display_id) {
+  for (auto& observer : observers_)
+    observer.OnHomeLauncherAnimationComplete(shown, display_id);
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // Private used only:
 
diff --git a/ash/app_list/app_list_controller_impl.h b/ash/app_list/app_list_controller_impl.h
index 2a0d2ae..71b207b 100644
--- a/ash/app_list/app_list_controller_impl.h
+++ b/ash/app_list/app_list_controller_impl.h
@@ -26,6 +26,7 @@
 #include "ash/shell_observer.h"
 #include "ash/wallpaper/wallpaper_controller_observer.h"
 #include "ash/wm/tablet_mode/tablet_mode_observer.h"
+#include "base/observer_list.h"
 #include "components/sync/model/string_ordinal.h"
 #include "mojo/public/cpp/bindings/binding_set.h"
 #include "mojo/public/cpp/bindings/interface_ptr_set.h"
@@ -37,6 +38,7 @@
 
 namespace ash {
 
+class AppListControllerObserver;
 class HomeLauncherGestureHandler;
 
 // Ash's AppListController owns the AppListModel and implements interface
@@ -183,8 +185,17 @@
   void GetNavigableContentsFactory(
       content::mojom::NavigableContentsFactoryRequest request) override;
 
-  void OnVisibilityChanged(bool visible);
-  void OnTargetVisibilityChanged(bool visible);
+  void AddObserver(AppListControllerObserver* observer);
+  void RemoveObserver(AppListControllerObserver* obsever);
+
+  // AppList visibility announcements are for clamshell mode AppList.
+  void NotifyAppListVisibilityChanged(bool visible, int64_t display_id);
+  void NotifyAppListTargetVisibilityChanged(bool visible);
+
+  // HomeLauncher visibility announcements are for tablet mode AppList.
+  void NotifyHomeLauncherTargetPositionChanged(bool showing,
+                                               int64_t display_id);
+  void NotifyHomeLauncherAnimationComplete(bool shown, int64_t display_id);
 
   void FlushForTesting();
 
@@ -279,6 +290,8 @@
   // Whether we're currently in a window dragging process.
   bool in_window_dragging_ = false;
 
+  base::ObserverList<AppListControllerObserver> observers_;
+
   DISALLOW_COPY_AND_ASSIGN(AppListControllerImpl);
 };
 
diff --git a/ash/app_list/app_list_controller_observer.h b/ash/app_list/app_list_controller_observer.h
new file mode 100644
index 0000000..227d384
--- /dev/null
+++ b/ash/app_list/app_list_controller_observer.h
@@ -0,0 +1,28 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_APP_LIST_APP_LIST_CONTROLLER_OBSERVER_H_
+#define ASH_APP_LIST_APP_LIST_CONTROLLER_OBSERVER_H_
+
+#include "ash/ash_export.h"
+#include "base/observer_list_types.h"
+
+namespace ash {
+
+class ASH_EXPORT AppListControllerObserver : public base::CheckedObserver {
+ public:
+  // Called when the AppList is shown or dismissed.
+  virtual void OnAppListVisibilityChanged(bool shown, int64_t display_id) {}
+  // Called when the HomeLauncher has started to be dragged, or a positional
+  // animation has begin.
+  virtual void OnHomeLauncherTargetPositionChanged(bool showing,
+                                                   int64_t display_id) {}
+  // Called when the HomeLauncher positional animation has completed.
+  virtual void OnHomeLauncherAnimationComplete(bool shown, int64_t display_id) {
+  }
+};
+
+}  // namespace ash
+
+#endif  // ASH_APP_LIST_APP_LIST_CONTROLLER_OBSERVER_H_
diff --git a/ash/app_list/app_list_presenter_delegate_impl.cc b/ash/app_list/app_list_presenter_delegate_impl.cc
index 1bc721e..36761d5 100644
--- a/ash/app_list/app_list_presenter_delegate_impl.cc
+++ b/ash/app_list/app_list_presenter_delegate_impl.cc
@@ -164,18 +164,13 @@
   return ash::Shell::Get()->GetRootWindowForDisplayId(display_id);
 }
 
-void AppListPresenterDelegateImpl::OnVisibilityChanged(
-    bool visible,
-    aura::Window* root_window) {
-  // Notify Chrome the visibility change.
-  controller_->OnVisibilityChanged(visible);
-  // Notify Ash the visibility change
-  ash::Shell::Get()->NotifyAppListVisibilityChanged(visible, root_window);
+void AppListPresenterDelegateImpl::OnVisibilityChanged(bool visible,
+                                                       int64_t display_id) {
+  controller_->NotifyAppListVisibilityChanged(visible, display_id);
 }
 
 void AppListPresenterDelegateImpl::OnTargetVisibilityChanged(bool visible) {
-  // Notify Chrome the target visibility change.
-  controller_->OnTargetVisibilityChanged(visible);
+  controller_->NotifyAppListTargetVisibilityChanged(visible);
 }
 
 void AppListPresenterDelegateImpl::OnDisplayMetricsChanged(
diff --git a/ash/app_list/app_list_presenter_delegate_impl.h b/ash/app_list/app_list_presenter_delegate_impl.h
index dafd3ec..f1acd599 100644
--- a/ash/app_list/app_list_presenter_delegate_impl.h
+++ b/ash/app_list/app_list_presenter_delegate_impl.h
@@ -61,7 +61,7 @@
   app_list::AppListViewDelegate* GetAppListViewDelegate() override;
   bool GetOnScreenKeyboardShown() override;
   aura::Window* GetRootWindowForDisplayId(int64_t display_id) override;
-  void OnVisibilityChanged(bool visible, aura::Window* root_window) override;
+  void OnVisibilityChanged(bool visible, int64_t display_id) override;
   void OnTargetVisibilityChanged(bool visible) override;
 
   // DisplayObserver overrides:
diff --git a/ash/app_list/home_launcher_gesture_handler.cc b/ash/app_list/home_launcher_gesture_handler.cc
index 38fcc6f..ff5151d7 100644
--- a/ash/app_list/home_launcher_gesture_handler.cc
+++ b/ash/app_list/home_launcher_gesture_handler.cc
@@ -293,6 +293,11 @@
   mode_ = mode;
   last_event_location_ = base::make_optional(location);
 
+  if (mode != Mode::kNone) {
+    app_list_controller_->NotifyHomeLauncherTargetPositionChanged(
+        mode == Mode::kSlideUpToShow /*showing*/, display_.id());
+  }
+
   UpdateWindows(0.0, /*animate=*/false);
   return true;
 }
@@ -434,6 +439,8 @@
 void HomeLauncherGestureHandler::OnImplicitAnimationsCompleted() {
   float app_list_opacity = 1.f;
   const bool is_final_state_show = IsFinalStateShow();
+  app_list_controller_->NotifyHomeLauncherAnimationComplete(
+      is_final_state_show /*shown*/, display_.id());
   if (Shell::Get()->overview_controller()->IsSelecting()) {
     if (overview_active_on_gesture_start_ && is_final_state_show) {
       // Exit overview if event is released on the top half. This will also
@@ -502,9 +509,13 @@
   UpdateWindows(is_final_state_show ? 1.0 : 0.0, /*animate=*/true);
 
   if (!is_final_state_show && mode_ == Mode::kSlideDownToHide) {
+    app_list_controller_->NotifyHomeLauncherTargetPositionChanged(
+        false /*showing*/, display_.id());
     base::RecordAction(
         base::UserMetricsAction("AppList_HomeLauncherToMRUWindow"));
   } else if (is_final_state_show && mode_ == Mode::kSlideUpToShow) {
+    app_list_controller_->NotifyHomeLauncherTargetPositionChanged(
+        true /*showing*/, display_.id());
     base::RecordAction(
         base::UserMetricsAction("AppList_CurrentWindowToHomeLauncher"));
   }
diff --git a/ash/app_list/home_launcher_gesture_handler_unittest.cc b/ash/app_list/home_launcher_gesture_handler_unittest.cc
index 516baa8..115e0fae 100644
--- a/ash/app_list/home_launcher_gesture_handler_unittest.cc
+++ b/ash/app_list/home_launcher_gesture_handler_unittest.cc
@@ -5,7 +5,9 @@
 #include "ash/app_list/home_launcher_gesture_handler.h"
 
 #include "ash/app_list/app_list_controller_impl.h"
+#include "ash/public/cpp/shelf_types.h"
 #include "ash/shelf/shelf.h"
+#include "ash/shelf/shelf_layout_manager.h"
 #include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
 #include "ash/wm/overview/overview_controller.h"
@@ -333,6 +335,60 @@
   EXPECT_TRUE(wm::GetWindowState(window2.get())->IsMinimized());
 }
 
+// Tests that the shelf background is transparent when the home launcher is
+// dragged, and does not shift to opaque until the home launcher is completely
+// hidden from view and the finger is released.
+TEST_F(HomeLauncherGestureHandlerTest, TransparentShelfWileDragging) {
+  UpdateDisplay("400x456");
+
+  auto window = CreateWindowForTesting();
+  ASSERT_TRUE(window->IsVisible());
+  ASSERT_EQ(ShelfBackgroundType::SHELF_BACKGROUND_MAXIMIZED,
+            AshTestBase::GetPrimaryShelf()
+                ->shelf_layout_manager()
+                ->GetShelfBackgroundType());
+
+  // Begin to show the home launcher, the shelf should become transparent.
+  DoPress(Mode::kSlideUpToShow);
+  EXPECT_EQ(ShelfBackgroundType::SHELF_BACKGROUND_DEFAULT,
+            AshTestBase::GetPrimaryShelf()
+                ->shelf_layout_manager()
+                ->GetShelfBackgroundType());
+
+  // Fling up to complete showing the home launcher, the shelf should remain
+  // transparent.
+  GetGestureHandler()->OnScrollEvent(gfx::Point(0, 300), -10.f);
+  GetGestureHandler()->OnReleaseEvent(gfx::Point(0, 300));
+  EXPECT_EQ(ShelfBackgroundType::SHELF_BACKGROUND_DEFAULT,
+            AshTestBase::GetPrimaryShelf()
+                ->shelf_layout_manager()
+                ->GetShelfBackgroundType());
+
+  // Begin to hide the home launcher, the background should still be
+  // transparent.
+  DoPress(Mode::kSlideDownToHide);
+  EXPECT_EQ(ShelfBackgroundType::SHELF_BACKGROUND_DEFAULT,
+            AshTestBase::GetPrimaryShelf()
+                ->shelf_layout_manager()
+                ->GetShelfBackgroundType());
+
+  // Fling down to hide the home launcher, the shelf should still be
+  // transparent.
+  GetGestureHandler()->OnScrollEvent(gfx::Point(0, 100), -10.f);
+  EXPECT_EQ(ShelfBackgroundType::SHELF_BACKGROUND_DEFAULT,
+            AshTestBase::GetPrimaryShelf()
+                ->shelf_layout_manager()
+                ->GetShelfBackgroundType());
+
+  // The shelf should transition to opauqe when the gesture sequence has
+  // completed.
+  GetGestureHandler()->OnReleaseEvent(gfx::Point(0, 300));
+  EXPECT_EQ(ShelfBackgroundType::SHELF_BACKGROUND_MAXIMIZED,
+            AshTestBase::GetPrimaryShelf()
+                ->shelf_layout_manager()
+                ->GetShelfBackgroundType());
+}
+
 class HomeLauncherModeGestureHandlerTest
     : public HomeLauncherGestureHandlerTest,
       public testing::WithParamInterface<Mode> {
diff --git a/ash/app_list/presenter/app_list_presenter_delegate.h b/ash/app_list/presenter/app_list_presenter_delegate.h
index 46269680..8d746a7 100644
--- a/ash/app_list/presenter/app_list_presenter_delegate.h
+++ b/ash/app_list/presenter/app_list_presenter_delegate.h
@@ -77,7 +77,7 @@
   virtual aura::Window* GetRootWindowForDisplayId(int64_t display_id) = 0;
 
   // Called when the app list visibility changes.
-  virtual void OnVisibilityChanged(bool visible, aura::Window* root_window) = 0;
+  virtual void OnVisibilityChanged(bool visible, int64_t display_id) = 0;
 
   // Called when the app list target visibility changes.
   virtual void OnTargetVisibilityChanged(bool visible) = 0;
diff --git a/ash/app_list/presenter/app_list_presenter_impl.cc b/ash/app_list/presenter/app_list_presenter_impl.cc
index 193d21f..dfc5c1f6 100644
--- a/ash/app_list/presenter/app_list_presenter_impl.cc
+++ b/ash/app_list/presenter/app_list_presenter_impl.cc
@@ -352,8 +352,7 @@
   last_display_id_ = display_id;
 
   // Notify the Shell and its observers of the app list visibility change.
-  delegate_->OnVisibilityChanged(
-      visible, delegate_->GetRootWindowForDisplayId(display_id));
+  delegate_->OnVisibilityChanged(visible, display_id);
 }
 
 void AppListPresenterImpl::NotifyTargetVisibilityChanged(bool visible) {
diff --git a/ash/app_list/presenter/app_list_presenter_impl.h b/ash/app_list/presenter/app_list_presenter_impl.h
index ddb902ba..0fa947d 100644
--- a/ash/app_list/presenter/app_list_presenter_impl.h
+++ b/ash/app_list/presenter/app_list_presenter_impl.h
@@ -121,6 +121,9 @@
 
   void NotifyVisibilityChanged(bool visible, int64_t display_id);
   void NotifyTargetVisibilityChanged(bool visible);
+  void NotifyHomeLauncherTargetPositionChanged(bool showing,
+                                               int64_t display_id);
+  void NotifyHomeLauncherAnimationComplete(bool shown, int64_t display_id);
 
   // aura::client::FocusChangeObserver overrides:
   void OnWindowFocused(aura::Window* gained_focus,
diff --git a/ash/app_list/presenter/app_list_presenter_impl_unittest.cc b/ash/app_list/presenter/app_list_presenter_impl_unittest.cc
index 561b333..f10257c 100644
--- a/ash/app_list/presenter/app_list_presenter_impl_unittest.cc
+++ b/ash/app_list/presenter/app_list_presenter_impl_unittest.cc
@@ -72,7 +72,7 @@
   aura::Window* GetRootWindowForDisplayId(int64_t display_id) override {
     return container_->GetRootWindow();
   }
-  void OnVisibilityChanged(bool visible, aura::Window* root_window) override {}
+  void OnVisibilityChanged(bool visible, int64_t display_id) override {}
   void OnTargetVisibilityChanged(bool visible) override {}
 
  private:
diff --git a/ash/shelf/app_list_button.cc b/ash/shelf/app_list_button.cc
index 68c956f..4518a40e 100644
--- a/ash/shelf/app_list_button.cc
+++ b/ash/shelf/app_list_button.cc
@@ -35,6 +35,7 @@
 #include "components/account_id/account_id.h"
 #include "ui/accessibility/ax_node_data.h"
 #include "ui/base/l10n/l10n_util.h"
+#include "ui/display/screen.h"
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/scoped_canvas.h"
 #include "ui/views/animation/flood_fill_ink_drop_ripple.h"
@@ -62,7 +63,7 @@
 AppListButton::AppListButton(ShelfView* shelf_view, Shelf* shelf)
     : ShelfControlButton(shelf_view), shelf_(shelf) {
   DCHECK(shelf_);
-  Shell::Get()->AddShellObserver(this);
+  Shell::Get()->app_list_controller()->AddObserver(this);
   Shell::Get()->session_controller()->AddObserver(this);
 
   Shell::Get()->voice_interaction_controller()->AddLocalObserver(this);
@@ -81,7 +82,10 @@
 }
 
 AppListButton::~AppListButton() {
-  Shell::Get()->RemoveShellObserver(this);
+  // AppListController is destroyed early when Shell is being destroyed, it may
+  // not exist.
+  if (Shell::Get()->app_list_controller())
+    Shell::Get()->app_list_controller()->RemoveObserver(this);
   Shell::Get()->session_controller()->RemoveObserver(this);
   Shell::Get()->voice_interaction_controller()->RemoveLocalObserver(this);
 }
@@ -233,12 +237,12 @@
   }
 }
 
-void AppListButton::OnAppListVisibilityChanged(bool shown,
-                                               aura::Window* root_window) {
-  aura::Window* window = GetWidget() ? GetWidget()->GetNativeWindow() : nullptr;
-  if (!window || window->GetRootWindow() != root_window)
+void AppListButton::OnAppListVisibilityChanged(bool shown, int64_t display_id) {
+  if (display::Screen::GetScreen()
+          ->GetDisplayNearestWindow(GetWidget()->GetNativeWindow())
+          .id() != display_id) {
     return;
-
+  }
   if (shown)
     OnAppListShown();
   else
diff --git a/ash/shelf/app_list_button.h b/ash/shelf/app_list_button.h
index c5f4e7c9..7efd923 100644
--- a/ash/shelf/app_list_button.h
+++ b/ash/shelf/app_list_button.h
@@ -7,12 +7,12 @@
 
 #include <memory>
 
+#include "ash/app_list/app_list_controller_observer.h"
 #include "ash/ash_export.h"
 #include "ash/public/cpp/assistant/default_voice_interaction_observer.h"
 #include "ash/public/interfaces/voice_interaction_controller.mojom.h"
 #include "ash/session/session_observer.h"
 #include "ash/shelf/shelf_control_button.h"
-#include "ash/shell_observer.h"
 #include "base/macros.h"
 #include "third_party/skia/include/core/SkColor.h"
 
@@ -28,7 +28,7 @@
 
 // Button used for the AppList icon on the shelf.
 class ASH_EXPORT AppListButton : public ShelfControlButton,
-                                 public ShellObserver,
+                                 public AppListControllerObserver,
                                  public SessionObserver,
                                  public DefaultVoiceInteractionObserver {
  public:
@@ -49,9 +49,8 @@
   void PaintButtonContents(gfx::Canvas* canvas) override;
 
  private:
-  // ShellObserver:
-  void OnAppListVisibilityChanged(bool shown,
-                                  aura::Window* root_window) override;
+  // AppListControllerObserver:
+  void OnAppListVisibilityChanged(bool shown, int64_t display_id) override;
 
   // mojom::VoiceInteractionObserver:
   void OnVoiceInteractionStatusChanged(
diff --git a/ash/shelf/shelf_layout_manager.cc b/ash/shelf/shelf_layout_manager.cc
index f2a7305..278ca44 100644
--- a/ash/shelf/shelf_layout_manager.cc
+++ b/ash/shelf/shelf_layout_manager.cc
@@ -91,6 +91,11 @@
 }
 
 bool IsTabletModeEnabled() {
+  // Shell could be destroying. Shell destroys TabletModeController before
+  // closing all windows.
+  if (!Shell::Get()->tablet_mode_controller())
+    return false;
+
   return Shell::Get()
       ->tablet_mode_controller()
       ->IsTabletModeWindowManagerEnabled();
@@ -174,6 +179,7 @@
   DCHECK(shelf_widget_);
   DCHECK(shelf_);
   Shell::Get()->AddShellObserver(this);
+  Shell::Get()->app_list_controller()->AddObserver(this);
   Shell::Get()->lock_state_controller()->AddObserver(this);
   Shell::Get()->activation_client()->AddObserver(this);
   Shell::Get()->locale_update_controller()->AddObserver(this);
@@ -194,6 +200,10 @@
   Shell::Get()->locale_update_controller()->RemoveObserver(this);
   Shell::Get()->RemoveShellObserver(this);
   Shell::Get()->lock_state_controller()->RemoveObserver(this);
+  // AppListController is destroyed early when Shell is being destroyed, it may
+  // not exist.
+  if (Shell::Get()->app_list_controller())
+    Shell::Get()->app_list_controller()->RemoveObserver(this);
 }
 
 void ShelfLayoutManager::PrepareForShutdown() {
@@ -457,10 +467,16 @@
     return SHELF_BACKGROUND_LOGIN;
   }
 
-  // If the app list is active and the home launcher is not shown, hide the
-  // shelf background to prevent overlap.
-  if (is_app_list_visible_ && !IsHomeLauncherEnabledInTabletMode())
-    return SHELF_BACKGROUND_APP_LIST;
+  if (is_app_list_visible_) {
+    if (!IsHomeLauncherEnabledInTabletMode())
+      return SHELF_BACKGROUND_APP_LIST;
+
+    // In tablet mode, the app list(now referred to as Home Launcher) is always
+    // visible. If the Home Launcher is either fullscreen or being animated or
+    // dragged, show the transparent background.
+    if (is_home_launcher_shown_ || is_home_launcher_target_position_shown_)
+      return SHELF_BACKGROUND_DEFAULT;
+  }
 
   if (state_.visibility_state != SHELF_AUTO_HIDE &&
       state_.window_state == wm::WORKSPACE_WINDOW_STATE_MAXIMIZED) {
@@ -528,18 +544,7 @@
   UpdateVisibilityState();
 }
 
-void ShelfLayoutManager::OnAppListVisibilityChanged(bool shown,
-                                                    aura::Window* root_window) {
-  // Shell may be under destruction.
-  if (!shelf_widget_ || !shelf_widget_->GetNativeWindow())
-    return;
 
-  if (shelf_widget_->GetNativeWindow()->GetRootWindow() != root_window)
-    return;
-
-  is_app_list_visible_ = shown;
-  MaybeUpdateShelfBackground(AnimationChangeType::IMMEDIATE);
-}
 
 void ShelfLayoutManager::OnOverviewModeStartingAnimationComplete(
     bool canceled) {
@@ -560,6 +565,46 @@
   MaybeUpdateShelfBackground(AnimationChangeType::ANIMATE);
 }
 
+void ShelfLayoutManager::OnAppListVisibilityChanged(bool shown,
+                                                    int64_t display_id) {
+  // Shell may be under destruction.
+  if (!shelf_widget_ || !shelf_widget_->GetNativeWindow())
+    return;
+
+  if (display_.id() != display_id)
+    return;
+
+  is_app_list_visible_ = shown;
+  MaybeUpdateShelfBackground(AnimationChangeType::IMMEDIATE);
+}
+
+void ShelfLayoutManager::OnHomeLauncherTargetPositionChanged(
+    bool showing,
+    int64_t display_id) {
+  // Shell may be under destruction.
+  if (!shelf_widget_ || !shelf_widget_->GetNativeWindow())
+    return;
+
+  if (display_.id() != display_id)
+    return;
+
+  is_home_launcher_target_position_shown_ = showing;
+  MaybeUpdateShelfBackground(AnimationChangeType::IMMEDIATE);
+}
+
+void ShelfLayoutManager::OnHomeLauncherAnimationComplete(bool shown,
+                                                         int64_t display_id) {
+  // Shell may be under destruction.
+  if (!shelf_widget_ || !shelf_widget_->GetNativeWindow())
+    return;
+
+  if (display_.id() != display_id)
+    return;
+
+  is_home_launcher_shown_ = shown;
+  MaybeUpdateShelfBackground(AnimationChangeType::IMMEDIATE);
+}
+
 void ShelfLayoutManager::OnWindowActivated(ActivationReason reason,
                                            aura::Window* gained_active,
                                            aura::Window* lost_active) {
diff --git a/ash/shelf/shelf_layout_manager.h b/ash/shelf/shelf_layout_manager.h
index 2b7ddf2e..395edfb 100644
--- a/ash/shelf/shelf_layout_manager.h
+++ b/ash/shelf/shelf_layout_manager.h
@@ -7,6 +7,7 @@
 
 #include <memory>
 
+#include "ash/app_list/app_list_controller_observer.h"
 #include "ash/ash_export.h"
 #include "ash/public/cpp/shelf_types.h"
 #include "ash/session/session_observer.h"
@@ -49,7 +50,8 @@
 // closely with ShelfLayoutManager.
 // On mus, widget bounds management is handled by the window manager.
 class ASH_EXPORT ShelfLayoutManager
-    : public ShellObserver,
+    : public AppListControllerObserver,
+      public ShellObserver,
       public ::wm::ActivationChangeObserver,
       public keyboard::KeyboardControllerObserver,
       public LockStateObserver,
@@ -153,13 +155,17 @@
   // ShellObserver:
   void OnShelfAutoHideBehaviorChanged(aura::Window* root_window) override;
   void OnPinnedStateChanged(aura::Window* pinned_window) override;
-  void OnAppListVisibilityChanged(bool shown,
-                                  aura::Window* root_window) override;
   void OnOverviewModeStartingAnimationComplete(bool canceled) override;
   void OnOverviewModeEndingAnimationComplete(bool canceled) override;
   void OnSplitViewModeStarted() override;
   void OnSplitViewModeEnded() override;
 
+  // AppListControllerObserver:
+  void OnAppListVisibilityChanged(bool shown, int64_t display_id) override;
+  void OnHomeLauncherTargetPositionChanged(bool showing,
+                                           int64_t display_id) override;
+  void OnHomeLauncherAnimationComplete(bool shown, int64_t display_id) override;
+
   // wm::ActivationChangeObserver:
   void OnWindowActivated(ActivationReason reason,
                          aura::Window* gained_active,
@@ -396,6 +402,14 @@
   // OnAppListVisibilityChanged.
   bool is_app_list_visible_ = false;
 
+  // Whether the HomeLauncher is being dragged to, or animating to fullscreen.
+  // This is maintained by OnHomeLauncherTargetPositionChanged.
+  bool is_home_launcher_target_position_shown_ = false;
+
+  // Whether the HomeLauncher is shown. This is maintained by
+  // OnHomeLauncherAnimationComplete.
+  bool is_home_launcher_shown_ = false;
+
   base::OneShotTimer auto_hide_timer_;
 
   // Whether the mouse was over the shelf when the auto hide timer started.
diff --git a/ash/shelf/shelf_layout_manager_unittest.cc b/ash/shelf/shelf_layout_manager_unittest.cc
index be1a3336..5140c31 100644
--- a/ash/shelf/shelf_layout_manager_unittest.cc
+++ b/ash/shelf/shelf_layout_manager_unittest.cc
@@ -867,17 +867,18 @@
 
   // The home launcher gesture handler should not be handling any window
   // initially.
-  ShelfLayoutManager* manager = GetShelfLayoutManager();
   HomeLauncherGestureHandler* gesture_handler =
       Shell::Get()->app_list_controller()->home_launcher_gesture_handler();
   ASSERT_TRUE(gesture_handler);
   ASSERT_FALSE(gesture_handler->GetWindow1());
 
   // Tests that after scrolling up on the shelf, the home launcher gesture
-  // handler will be acting on |window|.
+  // handler will be acting on |window|, and the shelf becomes transparent.
+  ShelfLayoutManager* manager = GetShelfLayoutManager();
   manager->ProcessGestureEvent(
       create_scroll_event(ui::ET_GESTURE_SCROLL_BEGIN, -1.f));
   EXPECT_EQ(window.get(), gesture_handler->GetWindow1());
+  EXPECT_EQ(SHELF_BACKGROUND_DEFAULT, GetShelfWidget()->GetBackgroundType());
   if (autohide_shelf) {
     // Auto-hide shelf should keep visible after scrolling up on it.
     EXPECT_EQ(SHELF_AUTO_HIDE_SHOWN, shelf->GetAutoHideState());
@@ -889,9 +890,12 @@
   manager->ProcessGestureEvent(
       create_scroll_event(ui::ET_GESTURE_SCROLL_UPDATE, -1.f));
   EXPECT_EQ(window.get(), gesture_handler->GetWindow1());
+  EXPECT_EQ(SHELF_BACKGROUND_DEFAULT, GetShelfWidget()->GetBackgroundType());
+
   manager->ProcessGestureEvent(
       create_scroll_event(ui::ET_GESTURE_SCROLL_UPDATE, 1.f));
   EXPECT_EQ(window.get(), gesture_handler->GetWindow1());
+  EXPECT_EQ(SHELF_BACKGROUND_DEFAULT, GetShelfWidget()->GetBackgroundType());
   if (autohide_shelf)
     EXPECT_EQ(SHELF_AUTO_HIDE_SHOWN, shelf->GetAutoHideState());
 
@@ -1936,9 +1940,7 @@
 TEST_F(ShelfLayoutManagerTest, SwipingOnShelfIfAppListOpened) {
   Shelf* shelf = GetPrimaryShelf();
   ShelfLayoutManager* layout_manager = GetShelfLayoutManager();
-  aura::Window* root_window =
-      RootWindowController::ForTargetRootWindow()->GetRootWindow();
-  layout_manager->OnAppListVisibilityChanged(true, root_window);
+  layout_manager->OnAppListVisibilityChanged(true, GetPrimaryDisplayId());
   EXPECT_EQ(SHELF_ALIGNMENT_BOTTOM, shelf->alignment());
   EXPECT_EQ(SHELF_AUTO_HIDE_BEHAVIOR_NEVER, shelf->auto_hide_behavior());
   EXPECT_EQ(SHELF_VISIBLE, shelf->GetVisibilityState());
diff --git a/ash/shell.cc b/ash/shell.cc
index ba07f4b47..e80bee1d 100644
--- a/ash/shell.cc
+++ b/ash/shell.cc
@@ -642,12 +642,6 @@
   g_is_browser_process_with_mash = true;
 }
 
-void Shell::NotifyAppListVisibilityChanged(bool visible,
-                                           aura::Window* root_window) {
-  for (auto& observer : shell_observers_)
-    observer.OnAppListVisibilityChanged(visible, root_window);
-}
-
 ////////////////////////////////////////////////////////////////////////////////
 // Shell, private:
 
diff --git a/ash/shell.h b/ash/shell.h
index 2625c1d..52f5027 100644
--- a/ash/shell.h
+++ b/ash/shell.h
@@ -645,8 +645,6 @@
   // Used to provide better error messages for Shell::Get() under mash.
   static void SetIsBrowserProcessWithMash();
 
-  void NotifyAppListVisibilityChanged(bool visible, aura::Window* root_window);
-
  private:
   FRIEND_TEST_ALL_PREFIXES(ExtendedDesktopTest, TestCursor);
   FRIEND_TEST_ALL_PREFIXES(WindowManagerTest, MouseEventCursors);
diff --git a/ash/shell_observer.h b/ash/shell_observer.h
index 32a9c59..3164330 100644
--- a/ash/shell_observer.h
+++ b/ash/shell_observer.h
@@ -20,9 +20,6 @@
 
 class ASH_EXPORT ShellObserver {
  public:
-  // Called when the AppList is shown or dismissed.
-  virtual void OnAppListVisibilityChanged(bool shown,
-                                          aura::Window* root_window) {}
 
   // Called when a casting session is started or stopped.
   virtual void OnCastingSessionStartedOrStopped(bool started) {}
diff --git a/ash/wm/workspace/backdrop_controller.cc b/ash/wm/workspace/backdrop_controller.cc
index a7135085..bfb4185 100644
--- a/ash/wm/workspace/backdrop_controller.cc
+++ b/ash/wm/workspace/backdrop_controller.cc
@@ -74,6 +74,7 @@
   DCHECK(container_);
   Shell::Get()->AddShellObserver(this);
   Shell::Get()->accessibility_controller()->AddObserver(this);
+  Shell::Get()->app_list_controller()->AddObserver(this);
   Shell::Get()->wallpaper_controller()->AddObserver(this);
 }
 
@@ -81,6 +82,10 @@
   Shell::Get()->accessibility_controller()->RemoveObserver(this);
   Shell::Get()->wallpaper_controller()->RemoveObserver(this);
   Shell::Get()->RemoveShellObserver(this);
+  // AppListController is destroyed early when Shell is being destroyed, it may
+  // not exist.
+  if (Shell::Get()->app_list_controller())
+    Shell::Get()->app_list_controller()->RemoveObserver(this);
   // TODO(oshima): animations won't work right with mus:
   // http://crbug.com/548396.
   Hide();
@@ -172,11 +177,6 @@
     backdrop_window_->ClearProperty(aura::client::kAnimationsDisabledKey);
 }
 
-void BackdropController::OnAppListVisibilityChanged(bool shown,
-                                                    aura::Window* root_window) {
-  UpdateBackdrop();
-}
-
 void BackdropController::OnSplitViewModeStarting() {
   Shell::Get()->split_view_controller()->AddObserver(this);
 }
@@ -185,6 +185,11 @@
   Shell::Get()->split_view_controller()->RemoveObserver(this);
 }
 
+void BackdropController::OnAppListVisibilityChanged(bool shown,
+                                                    int64_t display_id) {
+  UpdateBackdrop();
+}
+
 void BackdropController::OnAccessibilityStatusChanged() {
   UpdateBackdrop();
 }
diff --git a/ash/wm/workspace/backdrop_controller.h b/ash/wm/workspace/backdrop_controller.h
index 33a35c4a..0bb2c41 100644
--- a/ash/wm/workspace/backdrop_controller.h
+++ b/ash/wm/workspace/backdrop_controller.h
@@ -8,6 +8,7 @@
 #include <memory>
 
 #include "ash/accessibility/accessibility_observer.h"
+#include "ash/app_list/app_list_controller_observer.h"
 #include "ash/shell_observer.h"
 #include "ash/wallpaper/wallpaper_controller_observer.h"
 #include "ash/wm/splitview/split_view_controller.h"
@@ -43,8 +44,9 @@
 // 1) Has a aura::client::kHasBackdrop property = true.
 // 2) BackdropDelegate::HasBackdrop(aura::Window* window) returns true.
 // 3) Active ARC window when the spoken feedback is enabled.
-class BackdropController : public ShellObserver,
-                           public AccessibilityObserver,
+class BackdropController : public AccessibilityObserver,
+                           public AppListControllerObserver,
+                           public ShellObserver,
                            public SplitViewController::Observer,
                            public WallpaperControllerObserver {
  public:
@@ -71,11 +73,12 @@
   void OnOverviewModeStarting() override;
   void OnOverviewModeEnding(OverviewSession* overview_session) override;
   void OnOverviewModeEndingAnimationComplete(bool canceled) override;
-  void OnAppListVisibilityChanged(bool shown,
-                                  aura::Window* root_window) override;
   void OnSplitViewModeStarting() override;
   void OnSplitViewModeEnded() override;
 
+  // AppListControllerObserver:
+  void OnAppListVisibilityChanged(bool shown, int64_t display_id) override;
+
   // AccessibilityObserver:
   void OnAccessibilityStatusChanged() override;
 
diff --git a/base/BUILD.gn b/base/BUILD.gn
index 07c1693..14c3849 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -2976,12 +2976,12 @@
       "android/java/src/org/chromium/base/EventLog.java",
       "android/java/src/org/chromium/base/FieldTrialList.java",
       "android/java/src/org/chromium/base/FileUtils.java",
-      "android/java/src/org/chromium/base/GcStateAssert.java",
       "android/java/src/org/chromium/base/ImportantFileWriterAndroid.java",
       "android/java/src/org/chromium/base/IntStringCallback.java",
       "android/java/src/org/chromium/base/JNIUtils.java",
       "android/java/src/org/chromium/base/JavaExceptionReporter.java",
       "android/java/src/org/chromium/base/JavaHandlerThread.java",
+      "android/java/src/org/chromium/base/LifetimeAssert.java",
       "android/java/src/org/chromium/base/LocaleUtils.java",
       "android/java/src/org/chromium/base/Log.java",
       "android/java/src/org/chromium/base/MemoryPressureListener.java",
@@ -3137,6 +3137,7 @@
       "test/android/javatests/src/org/chromium/base/test/BaseChromiumAndroidJUnitRunner.java",
       "test/android/javatests/src/org/chromium/base/test/BaseChromiumRunnerCommon.java",
       "test/android/javatests/src/org/chromium/base/test/BaseTestResult.java",
+      "test/android/javatests/src/org/chromium/base/test/LifetimeAssertRule.java",
       "test/android/javatests/src/org/chromium/base/test/ScreenshotOnFailureStatement.java",
       "test/android/javatests/src/org/chromium/base/test/SetUpTestRule.java",
       "test/android/javatests/src/org/chromium/base/test/SetUpStatement.java",
@@ -3228,7 +3229,7 @@
       "android/junit/src/org/chromium/base/AnimationFrameTimeHistogramTest.java",
       "android/junit/src/org/chromium/base/ApplicationStatusTest.java",
       "android/junit/src/org/chromium/base/DiscardableReferencePoolTest.java",
-      "android/junit/src/org/chromium/base/GcStateAssertTest.java",
+      "android/junit/src/org/chromium/base/LifetimeAssertTest.java",
       "android/junit/src/org/chromium/base/LogTest.java",
       "android/junit/src/org/chromium/base/NonThreadSafeTest.java",
       "android/junit/src/org/chromium/base/PiiEliderTest.java",
diff --git a/base/android/java/src/org/chromium/base/GcStateAssert.java b/base/android/java/src/org/chromium/base/GcStateAssert.java
deleted file mode 100644
index b4128c9..0000000
--- a/base/android/java/src/org/chromium/base/GcStateAssert.java
+++ /dev/null
@@ -1,119 +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.base;
-
-import android.support.annotation.VisibleForTesting;
-
-import java.lang.ref.PhantomReference;
-import java.lang.ref.ReferenceQueue;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Set;
-
-/**
- * Used to assert that clean-up logic has been run before an object is GC'ed.
- *
- * Class is a no-op withen DCHECK_IS_ON=false, and is entirely removed by
- * proguard (enforced via -checkdiscard).
- *
- * Usage:
- * class MyClassWithCleanup {
- *     private final mGcStateAssert = GcStateAssert.create(this);
- *
- *     public void destroy() {
- *         // If mGcStateAssert is GC'ed before this is called, it will throw an exception
- *         // with a stack trace showing the stack during GcStateAssert.create().
- *         GcStateAssert.setSafeToGc(mGcStateAssert, true);
- *     }
- * }
- */
-public class GcStateAssert {
-    interface TestHook {
-        void onCleaned(WrappedReference ref, String msg);
-    }
-
-    // Used only for unit test.
-    static TestHook sTestHook;
-
-    @VisibleForTesting
-    final WrappedReference mWrapper;
-
-    @VisibleForTesting
-    static class WrappedReference extends PhantomReference<Object> {
-        boolean mSafeToGc;
-        final Class<?> mTargetClass;
-        final Throwable mCreationException;
-
-        public WrappedReference(Object target, boolean safeToGc) {
-            super(target, sReferenceQueue);
-            mSafeToGc = safeToGc;
-            mTargetClass = target.getClass();
-            // Create an exception to capture stack trace of when object was created.
-            mCreationException = new RuntimeException();
-            sActiveWrappers.add(this);
-        }
-
-        private static ReferenceQueue<Object> sReferenceQueue = new ReferenceQueue<>();
-        private static Set<WrappedReference> sActiveWrappers =
-                Collections.synchronizedSet(new HashSet<>());
-
-        static {
-            new Thread("GcStateAssertQueue") {
-                {
-                    setDaemon(true);
-                    start();
-                }
-
-                @Override
-                public void run() {
-                    while (true) {
-                        try {
-                            // This sleeps until a wrapper is available.
-                            WrappedReference wrapper = (WrappedReference) sReferenceQueue.remove();
-                            sActiveWrappers.remove(wrapper);
-                            if (!wrapper.mSafeToGc) {
-                                String msg = String.format(
-                                        "Object of type %s was GC'ed without cleanup. Refer to "
-                                                + "\"Caused by\" for where object was created.",
-                                        wrapper.mTargetClass.getName());
-                                if (sTestHook != null) {
-                                    sTestHook.onCleaned(wrapper, msg);
-                                } else {
-                                    throw new RuntimeException(msg, wrapper.mCreationException);
-                                }
-                            } else if (sTestHook != null) {
-                                sTestHook.onCleaned(wrapper, null);
-                            }
-                        } catch (InterruptedException e) {
-                            throw new RuntimeException(e);
-                        }
-                    }
-                }
-            };
-        }
-    }
-
-    private GcStateAssert(WrappedReference wrapper) {
-        mWrapper = wrapper;
-    }
-
-    public static GcStateAssert create(Object target) {
-        return create(target, false);
-    }
-
-    public static GcStateAssert create(Object target, boolean safeToGc) {
-        if (!BuildConfig.DCHECK_IS_ON) {
-            return null;
-        }
-        return new GcStateAssert(new WrappedReference(target, safeToGc));
-    }
-
-    public static void setSafeToGc(GcStateAssert asserter, boolean value) {
-        if (BuildConfig.DCHECK_IS_ON) {
-            // asserter is never null when DCHECK_IS_ON.
-            asserter.mWrapper.mSafeToGc = value;
-        }
-    }
-}
diff --git a/base/android/java/src/org/chromium/base/LifetimeAssert.java b/base/android/java/src/org/chromium/base/LifetimeAssert.java
new file mode 100644
index 0000000..207c984
--- /dev/null
+++ b/base/android/java/src/org/chromium/base/LifetimeAssert.java
@@ -0,0 +1,158 @@
+// 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.base;
+
+import android.support.annotation.VisibleForTesting;
+
+import java.lang.ref.PhantomReference;
+import java.lang.ref.ReferenceQueue;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Used to assert that clean-up logic has been run before an object is GC'ed.
+ *
+ * Class is a no-op withen DCHECK_IS_ON=false, and is entirely removed by
+ * proguard (enforced via -checkdiscard).
+ *
+ * Usage:
+ * class MyClassWithCleanup {
+ *     private final mLifetimeAssert = LifetimeAssert.create(this);
+ *
+ *     public void destroy() {
+ *         // If mLifetimeAssert is GC'ed before this is called, it will throw an exception
+ *         // with a stack trace showing the stack during LifetimeAssert.create().
+ *         LifetimeAssert.setSafeToGc(mLifetimeAssert, true);
+ *     }
+ * }
+ */
+public class LifetimeAssert {
+    interface TestHook {
+        void onCleaned(WrappedReference ref, String msg);
+    }
+
+    /**
+     * Thrown for failed assertions.
+     */
+    static class LifetimeAssertException extends RuntimeException {
+        LifetimeAssertException(String msg, Throwable causedBy) {
+            super(msg, causedBy);
+        }
+    }
+
+    /**
+     * For capturing where objects were created.
+     */
+    private static class CreationException extends RuntimeException {
+        CreationException() {
+            super("vvv This is where object was created. vvv");
+        }
+    }
+
+    // Used only for unit test.
+    static TestHook sTestHook;
+
+    @VisibleForTesting
+    final WrappedReference mWrapper;
+
+    @VisibleForTesting
+    static class WrappedReference extends PhantomReference<Object> {
+        boolean mSafeToGc;
+        final Class<?> mTargetClass;
+        final CreationException mCreationException;
+
+        public WrappedReference(
+                Object target, CreationException creationException, boolean safeToGc) {
+            super(target, sReferenceQueue);
+            mCreationException = creationException;
+            mSafeToGc = safeToGc;
+            mTargetClass = target.getClass();
+            sActiveWrappers.add(this);
+        }
+
+        private static ReferenceQueue<Object> sReferenceQueue = new ReferenceQueue<>();
+        private static Set<WrappedReference> sActiveWrappers =
+                Collections.synchronizedSet(new HashSet<>());
+
+        static {
+            new Thread("GcStateAssertQueue") {
+                {
+                    setDaemon(true);
+                    start();
+                }
+
+                @Override
+                public void run() {
+                    while (true) {
+                        try {
+                            // This sleeps until a wrapper is available.
+                            WrappedReference wrapper = (WrappedReference) sReferenceQueue.remove();
+                            sActiveWrappers.remove(wrapper);
+                            if (!wrapper.mSafeToGc) {
+                                String msg = String.format(
+                                        "Object of type %s was GC'ed without cleanup. Refer to "
+                                                + "\"Caused by\" for where object was created.",
+                                        wrapper.mTargetClass.getName());
+                                if (sTestHook != null) {
+                                    sTestHook.onCleaned(wrapper, msg);
+                                } else {
+                                    throw new LifetimeAssertException(
+                                            msg, wrapper.mCreationException);
+                                }
+                            } else if (sTestHook != null) {
+                                sTestHook.onCleaned(wrapper, null);
+                            }
+                        } catch (InterruptedException e) {
+                            throw new RuntimeException(e);
+                        }
+                    }
+                }
+            };
+        }
+    }
+
+    private LifetimeAssert(WrappedReference wrapper) {
+        mWrapper = wrapper;
+    }
+
+    public static LifetimeAssert create(Object target) {
+        if (!BuildConfig.DCHECK_IS_ON) {
+            return null;
+        }
+        return new LifetimeAssert(new WrappedReference(target, new CreationException(), false));
+    }
+
+    public static LifetimeAssert create(Object target, boolean safeToGc) {
+        if (!BuildConfig.DCHECK_IS_ON) {
+            return null;
+        }
+        return new LifetimeAssert(new WrappedReference(target, new CreationException(), safeToGc));
+    }
+
+    public static void setSafeToGc(LifetimeAssert asserter, boolean value) {
+        if (BuildConfig.DCHECK_IS_ON) {
+            // asserter is never null when DCHECK_IS_ON.
+            asserter.mWrapper.mSafeToGc = value;
+        }
+    }
+
+    public static void assertAllInstancesDestroyedForTesting() throws LifetimeAssertException {
+        if (!BuildConfig.DCHECK_IS_ON) {
+            return;
+        }
+        synchronized (WrappedReference.sActiveWrappers) {
+            for (WrappedReference ref : WrappedReference.sActiveWrappers) {
+                if (!ref.mSafeToGc) {
+                    String msg = String.format(
+                            "Object of type %s was not destroyed after test completed. Refer to "
+                                    + "\"Caused by\" for where object was created.",
+                            ref.mTargetClass.getName());
+                    throw new LifetimeAssertException(msg, ref.mCreationException);
+                }
+            }
+        }
+    }
+}
diff --git a/base/android/java/src/org/chromium/base/task/DefaultTaskExecutor.java b/base/android/java/src/org/chromium/base/task/DefaultTaskExecutor.java
index cbb534d..0c6ea796b 100644
--- a/base/android/java/src/org/chromium/base/task/DefaultTaskExecutor.java
+++ b/base/android/java/src/org/chromium/base/task/DefaultTaskExecutor.java
@@ -43,8 +43,11 @@
             // Caching TaskRunners only for common TaskTraits.
             TaskRunner runner = mTraitsToRunnerMap.get(taskTraits);
             if (runner == null) {
-                runner = createTaskRunner(taskTraits);
-                mTraitsToRunnerMap.put(taskTraits, runner);
+                TaskRunnerImpl runnerImpl = new TaskRunnerImpl(taskTraits);
+                // Disable destroy() check since object will live forever.
+                runnerImpl.disableLifetimeCheck();
+                mTraitsToRunnerMap.put(taskTraits, runnerImpl);
+                runner = runnerImpl;
             }
             runner.postDelayedTask(task, delay);
         }
diff --git a/base/android/java/src/org/chromium/base/task/TaskRunnerImpl.java b/base/android/java/src/org/chromium/base/task/TaskRunnerImpl.java
index 68df5e2a..d672f08e 100644
--- a/base/android/java/src/org/chromium/base/task/TaskRunnerImpl.java
+++ b/base/android/java/src/org/chromium/base/task/TaskRunnerImpl.java
@@ -7,7 +7,7 @@
 import android.support.annotation.Nullable;
 import android.util.Pair;
 
-import org.chromium.base.GcStateAssert;
+import org.chromium.base.LifetimeAssert;
 import org.chromium.base.TraceEvent;
 import org.chromium.base.annotations.JNINamespace;
 
@@ -29,8 +29,7 @@
     protected long mNativeTaskRunnerAndroid;
     protected final Runnable mRunPreNativeTaskClosure = this::runPreNativeTask;
     private boolean mIsDestroying;
-    private final GcStateAssert mGcStateAssert = GcStateAssert.create(this, true);
-
+    private final LifetimeAssert mLifetimeAssert = LifetimeAssert.create(this);
     @Nullable
     protected LinkedList<Runnable> mPreNativeTasks = new LinkedList<>();
     @Nullable
@@ -60,13 +59,20 @@
     @Override
     public void destroy() {
         synchronized (mLock) {
-            GcStateAssert.setSafeToGc(mGcStateAssert, true);
+            LifetimeAssert.setSafeToGc(mLifetimeAssert, true);
             if (mNativeTaskRunnerAndroid != 0) nativeDestroy(mNativeTaskRunnerAndroid);
             mNativeTaskRunnerAndroid = 0;
             mIsDestroying = true;
         }
     }
 
+    /**
+     * Set this for instances that are cached between tests.
+     */
+    void disableLifetimeCheck() {
+        LifetimeAssert.setSafeToGc(mLifetimeAssert, true);
+    }
+
     @Override
     public void postTask(Runnable task) {
         postDelayedTask(task, 0);
@@ -124,7 +130,6 @@
     public void initNativeTaskRunner() {
         synchronized (mLock) {
             if (mPreNativeTasks != null) {
-                GcStateAssert.setSafeToGc(mGcStateAssert, false);
                 mNativeTaskRunnerAndroid =
                         nativeInit(mTaskRunnerType, mTaskTraits.mPrioritySetExplicitly,
                                 mTaskTraits.mPriority, mTaskTraits.mMayBlock,
diff --git a/base/android/javatests/src/org/chromium/base/task/PostTaskTest.java b/base/android/javatests/src/org/chromium/base/task/PostTaskTest.java
index dc33bbd..f414726 100644
--- a/base/android/javatests/src/org/chromium/base/task/PostTaskTest.java
+++ b/base/android/javatests/src/org/chromium/base/task/PostTaskTest.java
@@ -8,8 +8,6 @@
 import static org.hamcrest.collection.IsIterableContainingInOrder.contains;
 import static org.junit.Assert.assertNotNull;
 
-import android.annotation.TargetApi;
-import android.os.Build;
 import android.support.test.filters.SmallTest;
 
 import org.junit.Test;
@@ -17,7 +15,6 @@
 
 import org.chromium.base.test.BaseJUnit4ClassRunner;
 import org.chromium.base.test.task.SchedulerTestHelpers;
-import org.chromium.base.test.util.MinAndroidSdkLevel;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -32,8 +29,6 @@
  * TaskSchedulerTest.java
  */
 @RunWith(BaseJUnit4ClassRunner.class)
-@MinAndroidSdkLevel(23)
-@TargetApi(Build.VERSION_CODES.M)
 public class PostTaskTest {
     @Test
     @SmallTest
@@ -68,6 +63,7 @@
         // A SingleThreadTaskRunner with default traits will run in the native thread pool
         // and tasks posted won't run until after the native library has loaded.
         assertNotNull(taskQueue);
+        taskQueue.destroy();
     }
 
     @Test
@@ -75,10 +71,14 @@
     public void testCreateSequencedTaskRunner() throws Exception {
         TaskRunner taskQueue = PostTask.createSequencedTaskRunner(new TaskTraits());
         List<Integer> orderList = new ArrayList<>();
-        SchedulerTestHelpers.postRecordOrderTask(taskQueue, orderList, 1);
-        SchedulerTestHelpers.postRecordOrderTask(taskQueue, orderList, 2);
-        SchedulerTestHelpers.postRecordOrderTask(taskQueue, orderList, 3);
-        SchedulerTestHelpers.postTaskAndBlockUntilRun(taskQueue);
+        try {
+            SchedulerTestHelpers.postRecordOrderTask(taskQueue, orderList, 1);
+            SchedulerTestHelpers.postRecordOrderTask(taskQueue, orderList, 2);
+            SchedulerTestHelpers.postRecordOrderTask(taskQueue, orderList, 3);
+            SchedulerTestHelpers.postTaskAndBlockUntilRun(taskQueue);
+        } finally {
+            taskQueue.destroy();
+        }
 
         assertThat(orderList, contains(1, 2, 3));
     }
diff --git a/base/android/javatests/src/org/chromium/base/task/SequencedTaskRunnerImplTest.java b/base/android/javatests/src/org/chromium/base/task/SequencedTaskRunnerImplTest.java
index a2d693af..2efd36d 100644
--- a/base/android/javatests/src/org/chromium/base/task/SequencedTaskRunnerImplTest.java
+++ b/base/android/javatests/src/org/chromium/base/task/SequencedTaskRunnerImplTest.java
@@ -33,11 +33,14 @@
     public void testPreNativeTasksRunInOrder() {
         TaskRunner taskQueue = new SequencedTaskRunnerImpl(new TaskTraits());
         List<Integer> orderList = new ArrayList<>();
-        SchedulerTestHelpers.postRecordOrderTask(taskQueue, orderList, 1);
-        SchedulerTestHelpers.postRecordOrderTask(taskQueue, orderList, 2);
-        SchedulerTestHelpers.postRecordOrderTask(taskQueue, orderList, 3);
-        SchedulerTestHelpers.postTaskAndBlockUntilRun(taskQueue);
-
+        try {
+            SchedulerTestHelpers.postRecordOrderTask(taskQueue, orderList, 1);
+            SchedulerTestHelpers.postRecordOrderTask(taskQueue, orderList, 2);
+            SchedulerTestHelpers.postRecordOrderTask(taskQueue, orderList, 3);
+            SchedulerTestHelpers.postTaskAndBlockUntilRun(taskQueue);
+        } finally {
+            taskQueue.destroy();
+        }
         assertThat(orderList, contains(1, 2, 3));
     }
 }
diff --git a/base/android/javatests/src/org/chromium/base/task/SingleThreadTaskRunnerImplTest.java b/base/android/javatests/src/org/chromium/base/task/SingleThreadTaskRunnerImplTest.java
index 8756841b5..ee29c4a 100644
--- a/base/android/javatests/src/org/chromium/base/task/SingleThreadTaskRunnerImplTest.java
+++ b/base/android/javatests/src/org/chromium/base/task/SingleThreadTaskRunnerImplTest.java
@@ -7,8 +7,6 @@
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.collection.IsIterableContainingInOrder.contains;
 
-import android.annotation.TargetApi;
-import android.os.Build;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.Looper;
@@ -22,7 +20,6 @@
 
 import org.chromium.base.test.BaseJUnit4ClassRunner;
 import org.chromium.base.test.task.SchedulerTestHelpers;
-import org.chromium.base.test.util.MinAndroidSdkLevel;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -36,8 +33,6 @@
  * TaskSchedulerTest.java
  */
 @RunWith(BaseJUnit4ClassRunner.class)
-@MinAndroidSdkLevel(23)
-@TargetApi(Build.VERSION_CODES.M)
 public class SingleThreadTaskRunnerImplTest {
     @Before
     public void setUp() throws Exception {
@@ -47,7 +42,6 @@
     }
 
     @After
-    @TargetApi(Build.VERSION_CODES.LOLLIPOP_MR1)
     public void tearDown() throws InterruptedException {
         Looper looper = mHandlerThread.getLooper();
         if (looper != null) {
@@ -67,6 +61,7 @@
         SchedulerTestHelpers.postRecordOrderTask(taskQueue, orderList, 1);
         SchedulerTestHelpers.postRecordOrderTask(taskQueue, orderList, 2);
         SchedulerTestHelpers.postRecordOrderTask(taskQueue, orderList, 3);
+        taskQueue.destroy();
 
         SchedulerTestHelpers.preNativeRunUntilIdle(mHandlerThread);
         assertThat(orderList, contains(1, 2, 3));
@@ -78,12 +73,20 @@
         // The handler created during test setup belongs to a different thread.
         SingleThreadTaskRunner taskQueue =
                 new SingleThreadTaskRunnerImpl(mHandler, new TaskTraits());
-        Assert.assertFalse(taskQueue.belongsToCurrentThread());
+        try {
+            Assert.assertFalse(taskQueue.belongsToCurrentThread());
+        } finally {
+            taskQueue.destroy();
+        }
 
         // We create a handler belonging to current thread.
         Looper.prepare();
         SingleThreadTaskRunner taskQueueCurrentThread =
                 new SingleThreadTaskRunnerImpl(new Handler(), new TaskTraits());
-        Assert.assertTrue(taskQueueCurrentThread.belongsToCurrentThread());
+        try {
+            Assert.assertTrue(taskQueueCurrentThread.belongsToCurrentThread());
+        } finally {
+            taskQueueCurrentThread.destroy();
+        }
     }
 }
diff --git a/base/android/javatests/src/org/chromium/base/task/TaskRunnerImplTest.java b/base/android/javatests/src/org/chromium/base/task/TaskRunnerImplTest.java
index 3676ace..c8c0de7 100644
--- a/base/android/javatests/src/org/chromium/base/task/TaskRunnerImplTest.java
+++ b/base/android/javatests/src/org/chromium/base/task/TaskRunnerImplTest.java
@@ -28,6 +28,10 @@
         TaskRunner taskQueue = new TaskRunnerImpl(new TaskTraits());
 
         // This should not time out.
-        SchedulerTestHelpers.postTaskAndBlockUntilRun(taskQueue);
+        try {
+            SchedulerTestHelpers.postTaskAndBlockUntilRun(taskQueue);
+        } finally {
+            taskQueue.destroy();
+        }
     }
 }
diff --git a/base/android/junit/src/org/chromium/base/GcStateAssertTest.java b/base/android/junit/src/org/chromium/base/LifetimeAssertTest.java
similarity index 71%
rename from base/android/junit/src/org/chromium/base/GcStateAssertTest.java
rename to base/android/junit/src/org/chromium/base/LifetimeAssertTest.java
index 1548070..f1a5464 100644
--- a/base/android/junit/src/org/chromium/base/GcStateAssertTest.java
+++ b/base/android/junit/src/org/chromium/base/LifetimeAssertTest.java
@@ -14,19 +14,19 @@
 import org.chromium.base.test.BaseRobolectricTestRunner;
 
 /**
- * junit tests for {@link GcStateAssert}.
+ * junit tests for {@link LifetimeAssert}.
  */
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
-public class GcStateAssertTest {
+public class LifetimeAssertTest {
     private static class TestClass {
         // Put assert inside of a test class to mirror typical api usage.
-        final GcStateAssert mGcStateAssert = GcStateAssert.create(this);
+        final LifetimeAssert mLifetimeAssert = LifetimeAssert.create(this);
     }
 
     private final Object mLock = new Object();
     private TestClass mTestClass;
-    private GcStateAssert.WrappedReference mTargetRef;
+    private LifetimeAssert.WrappedReference mTargetRef;
     private boolean mFound;
     private String mHookMessage;
 
@@ -36,10 +36,10 @@
             return;
         }
         mTestClass = new TestClass();
-        mTargetRef = mTestClass.mGcStateAssert.mWrapper;
+        mTargetRef = mTestClass.mLifetimeAssert.mWrapper;
         mFound = false;
         mHookMessage = null;
-        GcStateAssert.sTestHook = (ref, msg) -> {
+        LifetimeAssert.sTestHook = (ref, msg) -> {
             if (ref == mTargetRef) {
                 synchronized (mLock) {
                     mFound = true;
@@ -55,7 +55,7 @@
         if (!BuildConfig.DCHECK_IS_ON) {
             return;
         }
-        GcStateAssert.sTestHook = null;
+        LifetimeAssert.sTestHook = null;
     }
 
     private void runTest(boolean setSafe) {
@@ -65,7 +65,7 @@
 
         synchronized (mLock) {
             if (setSafe) {
-                GcStateAssert.setSafeToGc(mTestClass.mGcStateAssert, true);
+                LifetimeAssert.setSafeToGc(mTestClass.mLifetimeAssert, true);
             }
             // Null out field to make reference GC'able.
             mTestClass = null;
@@ -95,4 +95,17 @@
     public void testUnsafeGc() {
         runTest(false);
     }
+
+    @Test
+    public void testAssertAllInstancesDestroyedForTesting() {
+        try {
+            LifetimeAssert.assertAllInstancesDestroyedForTesting();
+            Assert.fail();
+        } catch (LifetimeAssert.LifetimeAssertException e) {
+            // Expected.
+        }
+        LifetimeAssert.setSafeToGc(mTestClass.mLifetimeAssert, true);
+        // Should no longer throw.
+        LifetimeAssert.assertAllInstancesDestroyedForTesting();
+    }
 }
diff --git a/base/android/proguard/dcheck_is_off.flags b/base/android/proguard/dcheck_is_off.flags
index b256f83..258e8ba 100644
--- a/base/android/proguard/dcheck_is_off.flags
+++ b/base/android/proguard/dcheck_is_off.flags
@@ -4,5 +4,5 @@
 
 # Contains flags that are applied only when ENABLE_DCHECK=false.
 
-# Ensure that GcStateAssert is fully optimized away.
--checkdiscard class org.chromium.base.GcStateAssert$WrappedReference { *; }
+# Ensure that LifetimeAssert implementation is not reachable in release.
+-checkdiscard class org.chromium.base.LifetimeAssert$WrappedReference { *; }
diff --git a/base/files/file_descriptor_watcher_posix.cc b/base/files/file_descriptor_watcher_posix.cc
index 74257be..3a8cd4bf 100644
--- a/base/files/file_descriptor_watcher_posix.cc
+++ b/base/files/file_descriptor_watcher_posix.cc
@@ -5,42 +5,29 @@
 #include "base/files/file_descriptor_watcher_posix.h"
 
 #include "base/bind.h"
+#include "base/callback_helpers.h"
 #include "base/lazy_instance.h"
 #include "base/memory/ptr_util.h"
 #include "base/message_loop/message_loop_current.h"
 #include "base/message_loop/message_pump_for_io.h"
 #include "base/sequenced_task_runner.h"
 #include "base/single_thread_task_runner.h"
+#include "base/synchronization/waitable_event.h"
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "base/threading/thread_checker.h"
 #include "base/threading/thread_local.h"
+#include "base/threading/thread_restrictions.h"
 
 namespace base {
 
 namespace {
 
-// MessageLoopForIO used to watch file descriptors for which callbacks are
-// registered from a given thread.
+// Per-thread FileDescriptorWatcher registration.
 LazyInstance<ThreadLocalPointer<FileDescriptorWatcher>>::Leaky tls_fd_watcher =
     LAZY_INSTANCE_INITIALIZER;
 
 }  // namespace
 
-FileDescriptorWatcher::Controller::~Controller() {
-  DCHECK(sequence_checker_.CalledOnValidSequence());
-
-  // Delete |watcher_| on the IO thread task runner.
-  //
-  // If the MessageLoopForIO is deleted before Watcher::StartWatching() runs,
-  // |watcher_| is leaked. If the MessageLoopForIO is deleted after
-  // Watcher::StartWatching() runs but before the DeleteSoon task runs,
-  // |watcher_| is deleted from Watcher::WillDestroyCurrentMessageLoop().
-  io_thread_task_runner_->DeleteSoon(FROM_HERE, watcher_.release());
-
-  // Since WeakPtrs are invalidated by the destructor, RunCallback() won't be
-  // invoked after this returns.
-}
-
 class FileDescriptorWatcher::Controller::Watcher
     : public MessagePumpForIO::FdWatcher,
       public MessageLoopCurrent::DestructionObserver {
@@ -60,7 +47,7 @@
   // MessageLoopCurrent::DestructionObserver:
   void WillDestroyCurrentMessageLoop() override;
 
-  // The MessageLoopForIO's watch handle (stops the watch when destroyed).
+  // The MessagePumpForIO's watch handle (stops the watch when destroyed).
   MessagePumpForIO::FdWatchController fd_watch_controller_;
 
   // Runs tasks on the sequence on which this was instantiated (i.e. the
@@ -68,7 +55,9 @@
   const scoped_refptr<SequencedTaskRunner> callback_task_runner_ =
       SequencedTaskRunnerHandle::Get();
 
-  // The Controller that created this Watcher.
+  // The Controller that created this Watcher. This WeakPtr is bound to the
+  // |controller_| thread and can only be used by this Watcher to post back to
+  // |callback_task_runner_|.
   WeakPtr<Controller> controller_;
 
   // Whether this Watcher is notified when |fd_| becomes readable or writable
@@ -79,11 +68,11 @@
   const int fd_;
 
   // Except for the constructor, every method of this class must run on the same
-  // MessageLoopForIO thread.
+  // MessagePumpForIO thread.
   ThreadChecker thread_checker_;
 
   // Whether this Watcher was registered as a DestructionObserver on the
-  // MessageLoopForIO thread.
+  // MessagePumpForIO thread.
   bool registered_as_destruction_observer_ = false;
 
   DISALLOW_COPY_AND_ASSIGN(Watcher);
@@ -110,13 +99,10 @@
   DCHECK(thread_checker_.CalledOnValidThread());
   DCHECK(MessageLoopCurrentForIO::IsSet());
 
-  if (!MessageLoopCurrentForIO::Get()->WatchFileDescriptor(
-          fd_, false, mode_, &fd_watch_controller_, this)) {
-    // TODO(wez): Ideally we would [D]CHECK here, or propagate the failure back
-    // to the caller, but there is no guarantee that they haven't already
-    // closed |fd_| on another thread, so the best we can do is Debug-log.
-    DLOG(ERROR) << "Failed to watch fd=" << fd_;
-  }
+  const bool watch_success =
+      MessageLoopCurrentForIO::Get()->WatchFileDescriptor(
+          fd_, false, mode_, &fd_watch_controller_, this);
+  DCHECK(watch_success) << "Failed to watch fd=" << fd_;
 
   if (!registered_as_destruction_observer_) {
     MessageLoopCurrentForIO::Get()->AddDestructionObserver(this);
@@ -150,11 +136,19 @@
     WillDestroyCurrentMessageLoop() {
   DCHECK(thread_checker_.CalledOnValidThread());
 
-  // A Watcher is owned by a Controller. When the Controller is deleted, it
-  // transfers ownership of the Watcher to a delete task posted to the
-  // MessageLoopForIO. If the MessageLoopForIO is deleted before the delete task
-  // runs, the following line takes care of deleting the Watcher.
-  delete this;
+  if (callback_task_runner_->RunsTasksInCurrentSequence()) {
+    // |controller_| can be accessed directly when Watcher runs on the same
+    // thread.
+    controller_->watcher_.reset();
+  } else {
+    // If the Watcher and the Controller live on different threads, delete
+    // |this| synchronously. Pending tasks bound to an unretained Watcher* will
+    // not run since this loop is dead. The associated Controller still
+    // technically owns this via unique_ptr but it never uses it directly and
+    // will ultimately send it to this thread for deletion (and that also  won't
+    // run since the loop being dead).
+    delete this;
+  }
 }
 
 FileDescriptorWatcher::Controller::Controller(MessagePumpForIO::Mode mode,
@@ -170,14 +164,68 @@
   StartWatching();
 }
 
+FileDescriptorWatcher::Controller::~Controller() {
+  DCHECK(sequence_checker_.CalledOnValidSequence());
+
+  if (io_thread_task_runner_->BelongsToCurrentThread()) {
+    // If the MessagePumpForIO and the Controller live on the same thread.
+    watcher_.reset();
+  } else {
+    // Synchronously wait until |watcher_| is deleted on the MessagePumpForIO
+    // thread. This ensures that the file descriptor is never accessed after
+    // this destructor returns.
+    //
+    // Use a ScopedClosureRunner to ensure that |done| is signalled even if the
+    // thread doesn't run any more tasks (if PostTask returns true, it means
+    // that the task was queued, but it doesn't mean that a RunLoop will run the
+    // task before the queue is deleted).
+    //
+    // We considered associating "generations" to file descriptors to avoid the
+    // synchronous wait. For example, if the IO thread gets a "cancel" for fd=6,
+    // generation=1 after getting a "start watching" for fd=6, generation=2, it
+    // can ignore the "Cancel". However, "generations" didn't solve this race:
+    //
+    // T1 (client) Start watching fd = 6 with WatchReadable()
+    //             Stop watching fd = 6
+    //             Close fd = 6
+    //             Open a new file, fd = 6 gets reused.
+    // T2 (io)     Watcher::StartWatching()
+    //               Incorrectly starts watching fd = 6 which now refers to a
+    //               different file than when WatchReadable() was called.
+    WaitableEvent done;
+    io_thread_task_runner_->PostTask(
+        FROM_HERE, BindOnce(
+                       [](Watcher* watcher, ScopedClosureRunner closure) {
+                         // Since |watcher| is a raw pointer, it isn't deleted
+                         // if this callback is deleted before it gets to run.
+                         delete watcher;
+                         // |closure| runs at the end of this scope.
+                       },
+                       Unretained(watcher_.release()),
+                       ScopedClosureRunner(BindOnce(&WaitableEvent::Signal,
+                                                    Unretained(&done)))));
+    ScopedAllowBaseSyncPrimitivesOutsideBlockingScope allow;
+    done.Wait();
+  }
+
+  // Since WeakPtrs are invalidated by the destructor, any pending RunCallback()
+  // won't be invoked after this returns.
+}
+
 void FileDescriptorWatcher::Controller::StartWatching() {
   DCHECK(sequence_checker_.CalledOnValidSequence());
-  // It is safe to use Unretained() below because |watcher_| can only be deleted
-  // by a delete task posted to |io_thread_task_runner_| by this
-  // Controller's destructor. Since this delete task hasn't been posted yet, it
-  // can't run before the task posted below.
-  io_thread_task_runner_->PostTask(
-      FROM_HERE, BindOnce(&Watcher::StartWatching, Unretained(watcher_.get())));
+  if (io_thread_task_runner_->BelongsToCurrentThread()) {
+    // If the MessagePumpForIO and the Controller live on the same thread.
+    watcher_->StartWatching();
+  } else {
+    // It is safe to use Unretained() below because |watcher_| can only be
+    // deleted by a delete task posted to |io_thread_task_runner_| by this
+    // Controller's destructor. Since this delete task hasn't been posted yet,
+    // it can't run before the task posted below.
+    io_thread_task_runner_->PostTask(
+        FROM_HERE,
+        BindOnce(&Watcher::StartWatching, Unretained(watcher_.get())));
+  }
 }
 
 void FileDescriptorWatcher::Controller::RunCallback() {
diff --git a/base/files/file_descriptor_watcher_posix.h b/base/files/file_descriptor_watcher_posix.h
index 49902764..eccae13 100644
--- a/base/files/file_descriptor_watcher_posix.h
+++ b/base/files/file_descriptor_watcher_posix.h
@@ -84,9 +84,10 @@
 
   // Registers |io_thread_task_runner| to watch file descriptors for which
   // callbacks are registered from the current thread via WatchReadable() or
-  // WatchWritable(). |io_thread_task_runner| may run on another thread.
-  // |io_thread_task_runner| must post tasks to a thread which runs
-  // a MessagePumpForIO.
+  // WatchWritable(). |io_thread_task_runner| must post tasks to a thread which
+  // runs a MessagePumpForIO. If it is not the current thread, it must be highly
+  // responsive (i.e. not used to run other expensive tasks such as potentially
+  // blocking I/O) since ~Controller waits for a task posted to it.
   explicit FileDescriptorWatcher(
       scoped_refptr<SingleThreadTaskRunner> io_thread_task_runner);
   ~FileDescriptorWatcher();
@@ -97,7 +98,8 @@
   // sequence). To call these methods, a FileDescriptorWatcher must have been
   // instantiated on the current thread and SequencedTaskRunnerHandle::IsSet()
   // must return true (these conditions are met at least on all TaskScheduler
-  // threads as well as on threads backed by a MessageLoopForIO).
+  // threads as well as on threads backed by a MessageLoopForIO). |fd| must
+  // outlive the returned Controller.
   static std::unique_ptr<Controller> WatchReadable(int fd,
                                                    const Closure& callback);
   static std::unique_ptr<Controller> WatchWritable(int fd,
diff --git a/base/test/android/javatests/src/org/chromium/base/test/BaseJUnit4ClassRunner.java b/base/test/android/javatests/src/org/chromium/base/test/BaseJUnit4ClassRunner.java
index 2b818be..7c203f86 100644
--- a/base/test/android/javatests/src/org/chromium/base/test/BaseJUnit4ClassRunner.java
+++ b/base/test/android/javatests/src/org/chromium/base/test/BaseJUnit4ClassRunner.java
@@ -174,7 +174,7 @@
      */
     @CallSuper
     protected List<TestRule> getDefaultTestRules() {
-        return Collections.emptyList();
+        return Collections.singletonList(new LifetimeAssertRule());
     }
 
     /**
diff --git a/base/test/android/javatests/src/org/chromium/base/test/LifetimeAssertRule.java b/base/test/android/javatests/src/org/chromium/base/test/LifetimeAssertRule.java
new file mode 100644
index 0000000..aeb3bda
--- /dev/null
+++ b/base/test/android/javatests/src/org/chromium/base/test/LifetimeAssertRule.java
@@ -0,0 +1,19 @@
+// 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.base.test;
+
+import org.junit.rules.ExternalResource;
+
+import org.chromium.base.LifetimeAssert;
+
+/**
+ * Ensures that all object instances that use LifetimeAssert are destroyed.
+ */
+public class LifetimeAssertRule extends ExternalResource {
+    @Override
+    protected void after() {
+        LifetimeAssert.assertAllInstancesDestroyedForTesting();
+    }
+}
diff --git a/base/test/android/javatests/src/org/chromium/base/test/task/SchedulerTestHelpers.java b/base/test/android/javatests/src/org/chromium/base/test/task/SchedulerTestHelpers.java
index 89ac39c9..47b043c 100644
--- a/base/test/android/javatests/src/org/chromium/base/test/task/SchedulerTestHelpers.java
+++ b/base/test/android/javatests/src/org/chromium/base/test/task/SchedulerTestHelpers.java
@@ -4,14 +4,11 @@
 
 package org.chromium.base.test.task;
 
-import android.annotation.TargetApi;
-import android.os.Build;
 import android.os.Handler;
 import android.os.HandlerThread;
-import android.os.MessageQueue;
+import android.os.Looper;
 
 import org.chromium.base.task.TaskRunner;
-import org.chromium.base.test.util.MinAndroidSdkLevel;
 
 import java.util.List;
 import java.util.concurrent.atomic.AtomicBoolean;
@@ -19,8 +16,6 @@
 /**
  * Collection of helpers for testing the java PostTask.
  */
-@MinAndroidSdkLevel(23)
-@TargetApi(Build.VERSION_CODES.M)
 public class SchedulerTestHelpers {
     public static void postRecordOrderTask(
             TaskRunner taskQueue, List<Integer> orderList, int order) {
@@ -120,23 +115,19 @@
      * Waits until the looper's MessageQueue becomes idle.
      */
     public static void preNativeRunUntilIdle(HandlerThread handlerThread) {
-        final MessageQueue messageQueue = handlerThread.getLooper().getQueue();
-        // This API was added in sdk level 23.
-        if (messageQueue.isIdle()) {
-            return;
-        }
         final Object lock = new Object();
         final AtomicBoolean taskExecuted = new AtomicBoolean();
-        messageQueue.addIdleHandler(new MessageQueue.IdleHandler() {
-            @Override
-            public boolean queueIdle() {
+
+        new Handler(handlerThread.getLooper()).post(() -> {
+            Looper.myQueue().addIdleHandler(() -> {
                 synchronized (lock) {
                     taskExecuted.set(true);
                     lock.notify();
                 }
                 return false;
-            }
+            });
         });
+
         synchronized (lock) {
             try {
                 while (!taskExecuted.get()) {
diff --git a/base/test/android/junit/src/org/chromium/base/test/BaseRobolectricTestRunner.java b/base/test/android/junit/src/org/chromium/base/test/BaseRobolectricTestRunner.java
index b2ca201..38c6368 100644
--- a/base/test/android/junit/src/org/chromium/base/test/BaseRobolectricTestRunner.java
+++ b/base/test/android/junit/src/org/chromium/base/test/BaseRobolectricTestRunner.java
@@ -12,6 +12,7 @@
 import org.chromium.base.ApplicationStatus;
 import org.chromium.base.CommandLine;
 import org.chromium.base.ContextUtils;
+import org.chromium.base.LifetimeAssert;
 import org.chromium.testing.local.LocalRobolectricTestRunner;
 
 import java.lang.reflect.Method;
@@ -35,6 +36,7 @@
         @Override
         public void afterTest(Method method) {
             ApplicationStatus.destroyForJUnitTests();
+            LifetimeAssert.assertAllInstancesDestroyedForTesting();
             super.afterTest(method);
         }
     }
diff --git a/base/threading/thread_restrictions.h b/base/threading/thread_restrictions.h
index 10b2456..d3693135 100644
--- a/base/threading/thread_restrictions.h
+++ b/base/threading/thread_restrictions.h
@@ -254,6 +254,7 @@
 }
 
 class AdjustOOMScoreHelper;
+class FileDescriptorWatcher;
 class GetAppOutputScopedAllowBaseSyncPrimitives;
 class MessageLoopImpl;
 class ScopedAllowThreadRecallForStackSamplingProfiler;
@@ -419,6 +420,7 @@
       AwFormDatabaseService;  // http://crbug.com/904431
   friend class android_webview::CookieManager;
   friend class audio::OutputDevice;
+  friend class base::FileDescriptorWatcher;
   friend class base::MessageLoopImpl;
   friend class base::ScopedAllowThreadRecallForStackSamplingProfiler;
   friend class base::StackSamplingProfiler;
diff --git a/cc/paint/image_transfer_cache_entry.cc b/cc/paint/image_transfer_cache_entry.cc
index 3a9b9781..2500e4e 100644
--- a/cc/paint/image_transfer_cache_entry.cc
+++ b/cc/paint/image_transfer_cache_entry.cc
@@ -4,12 +4,20 @@
 
 #include "cc/paint/image_transfer_cache_entry.h"
 
+#include <utility>
+#include <vector>
+
 #include "base/bind_helpers.h"
 #include "base/logging.h"
 #include "base/numerics/checked_math.h"
 #include "cc/paint/paint_op_reader.h"
 #include "cc/paint/paint_op_writer.h"
+#include "third_party/skia/include/core/SkColorSpace.h"
+#include "third_party/skia/include/core/SkImage.h"
+#include "third_party/skia/include/core/SkImageInfo.h"
+#include "third_party/skia/include/core/SkPixmap.h"
 #include "third_party/skia/include/gpu/GrContext.h"
+#include "third_party/skia/include/gpu/GrTypes.h"
 
 namespace cc {
 namespace {
@@ -133,6 +141,31 @@
 ServiceImageTransferCacheEntry& ServiceImageTransferCacheEntry::operator=(
     ServiceImageTransferCacheEntry&& other) = default;
 
+bool ServiceImageTransferCacheEntry::BuildFromDecodedData(
+    GrContext* context,
+    base::span<const uint8_t> decoded_image,
+    size_t row_bytes,
+    const SkImageInfo& image_info,
+    bool needs_mips,
+    sk_sp<SkColorSpace> target_color_space) {
+  context_ = context;
+  has_mips_ = needs_mips;
+  size_ = image_info.computeByteSize(row_bytes);
+  if (size_ == SIZE_MAX)
+    return false;
+  DCHECK_EQ(size_, decoded_image.size());
+
+  uint32_t width;
+  uint32_t height;
+  if (!base::CheckedNumeric<int>(image_info.width()).AssignIfValid(&width) ||
+      !base::CheckedNumeric<int>(image_info.height()).AssignIfValid(&height)) {
+    return false;
+  }
+
+  return MakeSkImage(SkPixmap(image_info, decoded_image.data(), row_bytes),
+                     width, height, target_color_space);
+}
+
 size_t ServiceImageTransferCacheEntry::CachedSize() const {
   return size_;
 }
@@ -186,18 +219,26 @@
   // this as the worst case scenario is visual corruption.
   SkPixmap pixmap(image_info, const_cast<const void*>(pixel_data),
                   image_info.minRowBytes());
+  return MakeSkImage(pixmap, width, height, target_color_space);
+}
+
+bool ServiceImageTransferCacheEntry::MakeSkImage(
+    const SkPixmap& pixmap,
+    uint32_t width,
+    uint32_t height,
+    sk_sp<SkColorSpace> target_color_space) {
+  DCHECK(context_);
 
   // Depending on whether the pixmap will fit in a GPU texture, either create
   // a software or GPU SkImage.
-  uint32_t max_size = context->maxTextureSize();
+  uint32_t max_size = context_->maxTextureSize();
   fits_on_gpu_ = width <= max_size && height <= max_size;
   if (fits_on_gpu_) {
     sk_sp<SkImage> image = SkImage::MakeFromRaster(pixmap, nullptr, nullptr);
     if (!image)
       return false;
-    image_ =
-        MakeTextureImage(context, std::move(image), target_color_space,
-                         needs_mips ? GrMipMapped::kYes : GrMipMapped::kNo);
+    image_ = MakeTextureImage(context_, std::move(image), target_color_space,
+                              has_mips_ ? GrMipMapped::kYes : GrMipMapped::kNo);
   } else {
     sk_sp<SkImage> original =
         SkImage::MakeFromRaster(pixmap, [](const void*, void*) {}, nullptr);
diff --git a/cc/paint/image_transfer_cache_entry.h b/cc/paint/image_transfer_cache_entry.h
index 7b563c4..5385c5b 100644
--- a/cc/paint/image_transfer_cache_entry.h
+++ b/cc/paint/image_transfer_cache_entry.h
@@ -5,12 +5,19 @@
 #ifndef CC_PAINT_IMAGE_TRANSFER_CACHE_ENTRY_H_
 #define CC_PAINT_IMAGE_TRANSFER_CACHE_ENTRY_H_
 
-#include <vector>
+#include <stddef.h>
+#include <stdint.h>
 
 #include "base/atomic_sequence_num.h"
 #include "base/containers/span.h"
 #include "cc/paint/transfer_cache_entry.h"
-#include "third_party/skia/include/core/SkImage.h"
+#include "third_party/skia/include/core/SkRefCnt.h"
+
+class GrContext;
+class SkColorSpace;
+class SkImage;
+struct SkImageInfo;
+class SkPixmap;
 
 namespace cc {
 
@@ -53,6 +60,16 @@
   ServiceImageTransferCacheEntry& operator=(
       ServiceImageTransferCacheEntry&& other);
 
+  // Populates this entry using |decoded_image| described by |row_bytes| and
+  // |image_info|. The image is uploaded to the GPU if its dimensions are both
+  // at most |context_|->maxTextureSize().
+  bool BuildFromDecodedData(GrContext* context,
+                            base::span<const uint8_t> decoded_image,
+                            size_t row_bytes,
+                            const SkImageInfo& image_info,
+                            bool needs_mips,
+                            sk_sp<SkColorSpace> target_color_space);
+
   // ServiceTransferCacheEntry implementation:
   size_t CachedSize() const final;
   bool Deserialize(GrContext* context, base::span<const uint8_t> data) final;
@@ -64,7 +81,12 @@
   void EnsureMips();
 
  private:
-  GrContext* context_;
+  bool MakeSkImage(const SkPixmap& pixmap,
+                   uint32_t width,
+                   uint32_t height,
+                   sk_sp<SkColorSpace> target_color_space);
+
+  GrContext* context_ = nullptr;
   sk_sp<SkImage> image_;
   bool has_mips_ = false;
   size_t size_ = 0;
diff --git a/cc/paint/paint_image.cc b/cc/paint/paint_image.cc
index dfdcc1fd..08f0012 100644
--- a/cc/paint/paint_image.cc
+++ b/cc/paint/paint_image.cc
@@ -154,10 +154,10 @@
 
 SkISize PaintImage::GetSupportedDecodeSize(
     const SkISize& requested_size) const {
-  // TODO(vmpstr): If this image is using subset_rect, then we don't support
-  // decoding to any scale other than the original. See the comment in Decode()
-  // explaining this in more detail.
-  if (paint_image_generator_ && subset_rect_.IsEmpty())
+  // TODO(vmpstr): In some cases we do not support decoding to any other
+  // size than the original. See the comment in CanDecodeFromGenerator()
+  // for more detail.
+  if (CanDecodeFromGenerator())
     return paint_image_generator_->GetSupportedDecodeSize(requested_size);
   return SkISize::Make(width(), height());
 }
@@ -167,38 +167,61 @@
                         sk_sp<SkColorSpace> color_space,
                         size_t frame_index,
                         GeneratorClientId client_id) const {
-  // We only support decode to supported decode size.
-  DCHECK(info->dimensions() == GetSupportedDecodeSize(info->dimensions()));
-
   // We don't support SkImageInfo's with color spaces on them. Color spaces
   // should always be passed via the |color_space| arg.
   DCHECK(!info->colorSpace());
 
-  // TODO(vmpstr): If we're using a subset_rect_ then the info specifies the
-  // requested size relative to the subset. However, the generator isn't aware
-  // of this subsetting and would need a size that is relative to the original
-  // image size. We could still implement this case, but we need to convert the
-  // requested size into the space of the original image. For now, fallback to
-  // DecodeFromSkImage().
-  if (paint_image_generator_ && subset_rect_.IsEmpty())
+  // We only support decode to supported decode size.
+  DCHECK(info->dimensions() == GetSupportedDecodeSize(info->dimensions()));
+
+  // TODO(vmpstr): In some cases we do not support decoding to any other
+  // size than the original. See the comment in CanDecodeFromGenerator()
+  // for more detail. For now, fallback to DecodeFromSkImage().
+  if (CanDecodeFromGenerator()) {
     return DecodeFromGenerator(memory, info, std::move(color_space),
                                frame_index, client_id);
+  }
   return DecodeFromSkImage(memory, info, std::move(color_space), frame_index,
                            client_id);
 }
 
+bool PaintImage::DecodeYuv(void* planes[SkYUVASizeInfo::kMaxCount],
+                           size_t frame_index,
+                           GeneratorClientId client_id,
+                           const SkYUVASizeInfo& yuva_size_info) const {
+  SkYUVAIndex indices[SkYUVAIndex::kIndexCount];
+  // Passing nullptr for the SkYUVASizeInfo forces IsYuv to create and fill out
+  // a temporary object instead because |yuva_size_info| is const.
+  bool is_yuv = IsYuv(nullptr, indices);
+  DCHECK(is_yuv);
+  DCHECK(CanDecodeFromGenerator());
+  const uint32_t lazy_pixel_ref = unique_id();
+  return paint_image_generator_->GetYUVA8Planes(yuva_size_info, indices, planes,
+                                                frame_index, lazy_pixel_ref);
+}
+
 bool PaintImage::DecodeFromGenerator(void* memory,
                                      SkImageInfo* info,
                                      sk_sp<SkColorSpace> color_space,
                                      size_t frame_index,
                                      GeneratorClientId client_id) const {
-  DCHECK(subset_rect_.IsEmpty());
-
+  DCHECK(CanDecodeFromGenerator());
   // First convert the info to have the requested color space, since the decoder
   // will convert this for us.
   *info = info->makeColorSpace(std::move(color_space));
+  const uint32_t lazy_pixel_ref = unique_id();
   return paint_image_generator_->GetPixels(*info, memory, info->minRowBytes(),
-                                           frame_index, client_id, unique_id());
+                                           frame_index, client_id,
+                                           lazy_pixel_ref);
+}
+
+// TODO(vmpstr): If we're using a subset_rect_ then the info specifies the
+// requested size relative to the subset. However, the generator isn't aware
+// of this subsetting and would need a size that is relative to the original
+// image size. We could still implement this case, but we need to convert the
+// requested size into the space of the original image.
+bool PaintImage::CanDecodeFromGenerator() const {
+  return paint_image_generator_ && subset_rect_.IsEmpty();
 }
 
 bool PaintImage::DecodeFromSkImage(void* memory,
@@ -249,6 +272,23 @@
   return kUnknown_SkColorType;
 }
 
+bool PaintImage::IsYuv(SkYUVASizeInfo* yuva_size_info,
+                       SkYUVAIndex* plane_indices) const {
+  SkYUVASizeInfo temp_yuva_size_info;
+  SkYUVAIndex temp_plane_indices[SkYUVAIndex::kIndexCount];
+  if (!yuva_size_info) {
+    yuva_size_info = &temp_yuva_size_info;
+  }
+  if (!plane_indices) {
+    plane_indices = temp_plane_indices;
+  }
+  // We pass nullptr for color_space because QueryYUVA8 hardcodes it to
+  // kJPEG_SkYUVColorSpace when it should be kRec601_SkYUVColorSpace for WebP.
+  return CanDecodeFromGenerator() &&
+         paint_image_generator_->QueryYUVA8(yuva_size_info, plane_indices,
+                                            nullptr /* color_space */);
+}
+
 const std::vector<FrameMetadata>& PaintImage::GetFrameMetadata() const {
   DCHECK_EQ(animation_type_, AnimationType::ANIMATED);
   DCHECK(paint_image_generator_);
@@ -298,7 +338,7 @@
       << " animation_type_: " << static_cast<int>(animation_type_)
       << " completion_state_: " << static_cast<int>(completion_state_)
       << " subset_rect_: " << subset_rect_.ToString()
-      << " is_multipart_: " << is_multipart_;
+      << " is_multipart_: " << is_multipart_ << " is YUV: " << IsYuv();
   return str.str();
 }
 
diff --git a/cc/paint/paint_image.h b/cc/paint/paint_image.h
index c71fe72..33202446 100644
--- a/cc/paint/paint_image.h
+++ b/cc/paint/paint_image.h
@@ -15,6 +15,8 @@
 #include "cc/paint/paint_export.h"
 #include "cc/paint/paint_worklet_input.h"
 #include "third_party/skia/include/core/SkImage.h"
+#include "third_party/skia/include/core/SkYUVAIndex.h"
+#include "third_party/skia/include/core/SkYUVASizeInfo.h"
 #include "ui/gfx/geometry/rect.h"
 
 namespace cc {
@@ -146,10 +148,10 @@
   // GetSupportedDecodeSize(size).
   SkISize GetSupportedDecodeSize(const SkISize& requested_size) const;
 
-  // Decode the image into the given memory for the given SkImageInfo.
+  // Decode the image into RGBX into the given memory for the given SkImageInfo.
   // - Size in |info| must be supported.
   // - The amount of memory allocated must be at least
-  //   |info|.minRowBytes() * |info|.height().
+  //   |info|.minRowBytes() * |info|.height()
   // Returns true on success and false on failure. Updates |info| to match the
   // requested color space, if provided.
   // Note that for non-lazy images this will do a copy or readback if the image
@@ -160,6 +162,25 @@
               size_t frame_index,
               GeneratorClientId client_id) const;
 
+  // Decode the image into YUV into |planes| for the given SkYUVASizeInfo.
+  //  - Elements of the |planes| array are pointers to some underlying memory
+  //    for each plane. It is assumed to have been split up by a call to
+  //    SkYUVASizeInfo::computePlanes with the given |yuva_size_info|.
+  //  - The amount of memory allocated must be at least
+  //    |yuva_size_info|.computeTotalBytes(), though there are places in the
+  //    code that assume YUV420 without alpha because it is currently the only
+  //    subsampling supported for direct YUV rendering.
+  //  - The dimensions of YUV planes are tracked in |yuva_size_info|.
+  //    This struct is initialized by QueryYUVA8 in calls to
+  //    PaintImage::IsYuv(), including within this method.
+  //  - The |frame_index| parameter will be passed along to
+  //    ImageDecoder::DecodeToYUV but for multi-frame YUV support, ImageDecoder
+  //    needs a separate YUV frame buffer cache.
+  bool DecodeYuv(void* planes[SkYUVASizeInfo::kMaxCount],
+                 size_t frame_index,
+                 GeneratorClientId client_id,
+                 const SkYUVASizeInfo& yuva_size_info) const;
+
   Id stable_id() const { return id_; }
   const sk_sp<SkImage>& GetSkImage() const;
   AnimationType animation_type() const { return animation_type_; }
@@ -202,6 +223,12 @@
     return paint_worklet_input_ ? nullptr : GetSkImage()->colorSpace();
   }
 
+  // Returns whether this image will be decoded and rendered from YUV data
+  // and fills out plane size and plane index information respectively in
+  // |yuva_size_info| and |plane_indices|, if provided.
+  bool IsYuv(SkYUVASizeInfo* yuva_size_info = nullptr,
+             SkYUVAIndex* plane_indices = nullptr) const;
+
   // Returns the color type of this image.
   SkColorType GetColorType() const;
 
@@ -234,6 +261,8 @@
   friend class ScopedRasterFlags;
   friend class PaintOpReader;
 
+  bool CanDecodeFromGenerator() const;
+
   bool DecodeFromGenerator(void* memory,
                            SkImageInfo* info,
                            sk_sp<SkColorSpace> color_space,
diff --git a/cc/tiles/gpu_image_decode_cache.cc b/cc/tiles/gpu_image_decode_cache.cc
index 1e7eabde..bebf3fb55 100644
--- a/cc/tiles/gpu_image_decode_cache.cc
+++ b/cc/tiles/gpu_image_decode_cache.cc
@@ -29,6 +29,7 @@
 #include "third_party/skia/include/core/SkCanvas.h"
 #include "third_party/skia/include/core/SkRefCnt.h"
 #include "third_party/skia/include/core/SkSurface.h"
+#include "third_party/skia/include/core/SkYUVAIndex.h"
 #include "third_party/skia/include/gpu/GrBackendSurface.h"
 #include "third_party/skia/include/gpu/GrContext.h"
 #include "third_party/skia/include/gpu/GrTexture.h"
@@ -159,19 +160,65 @@
   return false;
 }
 
+void SetYuvPixmapsFromSizeInfo(SkPixmap* pixmap_y,
+                               SkPixmap* pixmap_u,
+                               SkPixmap* pixmap_v,
+                               const SkYUVASizeInfo& yuva_size_info,
+                               void* planes[SkYUVASizeInfo::kMaxCount],
+                               const SkImageInfo& info,
+                               void* memory_ptr) {
+  DCHECK(pixmap_y);
+  DCHECK(pixmap_u);
+  DCHECK(pixmap_v);
+  const size_t y_width = yuva_size_info.fWidthBytes[SkYUVAIndex::kY_Index];
+  const size_t y_height = yuva_size_info.fSizes[SkYUVAIndex::kY_Index].height();
+  const size_t u_width = yuva_size_info.fWidthBytes[SkYUVAIndex::kU_Index];
+  const size_t u_height = yuva_size_info.fSizes[SkYUVAIndex::kU_Index].height();
+  const size_t v_width = yuva_size_info.fWidthBytes[SkYUVAIndex::kV_Index];
+  const size_t v_height = yuva_size_info.fSizes[SkYUVAIndex::kV_Index].height();
+  const SkImageInfo y_decode_info =
+      info.makeColorType(kGray_8_SkColorType).makeWH(y_width, y_height);
+  const SkImageInfo u_decode_info = y_decode_info.makeWH(u_width, u_height);
+  const SkImageInfo v_decode_info = y_decode_info.makeWH(v_width, v_height);
+  yuva_size_info.computePlanes(memory_ptr, planes);
+  pixmap_y->reset(y_decode_info, planes[SkYUVAIndex::kY_Index],
+                  y_decode_info.minRowBytes());
+  pixmap_u->reset(u_decode_info, planes[SkYUVAIndex::kU_Index],
+                  u_decode_info.minRowBytes());
+  pixmap_v->reset(v_decode_info, planes[SkYUVAIndex::kV_Index],
+                  v_decode_info.minRowBytes());
+}
+
 // Draws and scales the provided |draw_image| into the |target_pixmap|. If the
 // draw/scale can be done directly, calls directly into PaintImage::Decode.
 // if not, decodes to a compatible temporary pixmap and then converts that into
 // the |target_pixmap|.
+//
+// For RGBX decoding, the default, the parameters |pixmap_y|,
+// |pixmap_u|, and |pixmap_v| are NULL. Otherwise, the pixmaps share a
+// contiguous block of allocated backing memory. If scaling needs to happen,
+// it is done individually for each plane.
+//
+// The |do_yuv_decode| parameter indicates whether YUV decoding can and should
+// be done, which is a combination of the underlying data requesting YUV and the
+// cache mode (i.e. OOP-R or not) supporting it.
 bool DrawAndScaleImage(const DrawImage& draw_image,
                        SkPixmap* target_pixmap,
-                       PaintImage::GeneratorClientId client_id) {
+                       PaintImage::GeneratorClientId client_id,
+                       const bool do_yuv_decode,
+                       SkPixmap* pixmap_y = nullptr,
+                       SkPixmap* pixmap_u = nullptr,
+                       SkPixmap* pixmap_v = nullptr) {
   // We will pass color_space explicitly to PaintImage::Decode, so pull it out
   // of the pixmap and populate a stand-alone value.
-  // note: To pull colorspace out of the pixmap, we create a new pixmap with
+  // Note: To pull colorspace out of the pixmap, we create a new pixmap with
   // null colorspace but the same memory pointer.
+  // The backing memory for |pixmap| has been allocated based on
+  // image_data->size, so it is correct for YUV even if the other parameters
+  // for |pixmap| do not quite make sense for YUV (e.g. rowBytes).
   SkPixmap pixmap(target_pixmap->info().makeColorSpace(nullptr),
                   target_pixmap->writable_addr(), target_pixmap->rowBytes());
+  uint8_t* data_ptr = reinterpret_cast<uint8_t*>(pixmap.writable_addr());
   sk_sp<SkColorSpace> color_space = target_pixmap->info().refColorSpace();
 
   const PaintImage& paint_image = draw_image.paint_image();
@@ -180,14 +227,28 @@
       pixmap.bounds().size();
   const bool is_nearest_neighbor =
       draw_image.filter_quality() == kNone_SkFilterQuality;
-
+  SkImageInfo info = pixmap.info();
+  SkYUVASizeInfo yuva_size_info;
+  if (do_yuv_decode) {
+    const bool yuva_info_initialized = paint_image.IsYuv(&yuva_size_info);
+    DCHECK(yuva_info_initialized);
+  }
   SkISize supported_size =
       paint_image.GetSupportedDecodeSize(pixmap.bounds().size());
   // We can directly decode into target pixmap if we are doing an original
   // decode or we are decoding to scale without nearest neighbor filtering.
-  const bool can_directly_decode = is_original_decode || !is_nearest_neighbor;
+  // Although the JPEG decoder supports decoding to scale, we have not yet
+  // implemented YUV + decoding to scale, so we skip it for YUV.
+  const bool can_directly_decode =
+      is_original_decode || (!is_nearest_neighbor && !do_yuv_decode);
   if (supported_size == pixmap.bounds().size() && can_directly_decode) {
-    SkImageInfo info = pixmap.info();
+    if (do_yuv_decode) {
+      void* planes[SkYUVASizeInfo::kMaxCount];
+      SetYuvPixmapsFromSizeInfo(pixmap_y, pixmap_u, pixmap_v, yuva_size_info,
+                                planes, info, pixmap.writable_addr());
+      return paint_image.DecodeYuv(planes, draw_image.frame_index(), client_id,
+                                   yuva_size_info);
+    }
     return paint_image.Decode(pixmap.writable_addr(), &info, color_space,
                               draw_image.frame_index(), client_id);
   }
@@ -197,13 +258,26 @@
   // original size if nearest neighbor quality is requested.
   // Step 2: Scale to |pixmap| size. If decoded image is half float backed and
   // the device does not support image resize, decode to N32 color type and
-  // convert to F16 afterward.
-  SkISize decode_size =
-      is_nearest_neighbor
-          ? SkISize::Make(paint_image.width(), paint_image.height())
-          : supported_size;
-  SkImageInfo decode_info =
-      pixmap.info().makeWH(decode_size.width(), decode_size.height());
+  // convert to F16 afterward. If doing YUV decoding, use an assumption of
+  // YUV420 and the dimensions of |pixmap|. Resizing happens on a plane-by-plane
+  // basis.
+  SkImageInfo decode_info;
+  if (do_yuv_decode) {
+    const size_t yuva_bytes = yuva_size_info.computeTotalBytes();
+    if (SkImageInfo::ByteSizeOverflowed(yuva_bytes)) {
+      return false;
+    }
+    // We temporarily abuse the dimensions of the pixmap to ensure we allocate
+    // the proper number of bytes, but the actual plane dimensions are stored in
+    // |yuva_size_info| and accessed within PaintImage::DecodeYuv() and below.
+    decode_info = info.makeColorType(kGray_8_SkColorType).makeWH(yuva_bytes, 1);
+  } else {
+    SkISize decode_size =
+        is_nearest_neighbor
+            ? SkISize::Make(paint_image.width(), paint_image.height())
+            : supported_size;
+    decode_info = info.makeWH(decode_size.width(), decode_size.height());
+  }
   SkFilterQuality filter_quality = CalculateDesiredFilterQuality(draw_image);
   bool decode_to_f16_using_n32_intermediate =
       decode_info.colorType() == kRGBA_F16_SkColorType &&
@@ -214,16 +288,58 @@
   SkBitmap decode_bitmap;
   if (!decode_bitmap.tryAllocPixels(decode_info))
     return false;
+
   SkPixmap decode_pixmap = decode_bitmap.pixmap();
-  if (!paint_image.Decode(decode_pixmap.writable_addr(), &decode_info,
-                          color_space, draw_image.frame_index(), client_id)) {
-    return false;
+  void* planes[SkYUVASizeInfo::kMaxCount];
+  if (do_yuv_decode) {
+    yuva_size_info.computePlanes(decode_pixmap.writable_addr(), planes);
   }
+  bool initial_decode_failed =
+      do_yuv_decode ? !paint_image.DecodeYuv(planes, draw_image.frame_index(),
+                                             client_id, yuva_size_info)
+                    : !paint_image.Decode(decode_pixmap.writable_addr(),
+                                          &decode_info, color_space,
+                                          draw_image.frame_index(), client_id);
+  if (initial_decode_failed)
+    return false;
 
   if (decode_to_f16_using_n32_intermediate) {
     return ImageDecodeCacheUtils::ScaleToHalfFloatPixmapUsingN32Intermediate(
         decode_pixmap, &pixmap, filter_quality);
   }
+  if (do_yuv_decode) {
+    SkPixmap unscaled_pixmap_y;
+    SkPixmap unscaled_pixmap_u;
+    SkPixmap unscaled_pixmap_v;
+    void* planes[SkYUVASizeInfo::kMaxCount];
+    SetYuvPixmapsFromSizeInfo(&unscaled_pixmap_y, &unscaled_pixmap_u,
+                              &unscaled_pixmap_v, yuva_size_info, planes,
+                              decode_info, decode_pixmap.writable_addr());
+
+    // Assumes YUV420 and splits decode_pixmap into pixmaps for each plane.
+    // TODO(crbug.com/915972): Fix this assumption.
+    const SkImageInfo y_info_scaled = info.makeColorType(kGray_8_SkColorType);
+    const size_t uv_width_scaled = (y_info_scaled.width() + 1) / 2;
+    const size_t uv_height_scaled = (y_info_scaled.height() + 1) / 2;
+    const SkImageInfo uv_info_scaled =
+        y_info_scaled.makeWH(uv_width_scaled, uv_height_scaled);
+    const size_t y_plane_bytes = y_info_scaled.computeMinByteSize();
+    const size_t u_plane_bytes = uv_info_scaled.computeMinByteSize();
+    DCHECK(!SkImageInfo::ByteSizeOverflowed(y_plane_bytes));
+    DCHECK(!SkImageInfo::ByteSizeOverflowed(u_plane_bytes));
+
+    pixmap_y->reset(y_info_scaled, data_ptr, y_info_scaled.minRowBytes());
+    pixmap_u->reset(uv_info_scaled, data_ptr + y_plane_bytes,
+                    uv_info_scaled.minRowBytes());
+    pixmap_v->reset(uv_info_scaled, data_ptr + y_plane_bytes + u_plane_bytes,
+                    uv_info_scaled.minRowBytes());
+
+    const bool all_planes_scaled_successfully =
+        unscaled_pixmap_y.scalePixels(*pixmap_y, filter_quality) &&
+        unscaled_pixmap_u.scalePixels(*pixmap_u, filter_quality) &&
+        unscaled_pixmap_v.scalePixels(*pixmap_v, filter_quality);
+    return all_planes_scaled_successfully;
+  }
   return decode_pixmap.scalePixels(pixmap, filter_quality);
 }
 
@@ -509,6 +625,26 @@
   OnSetLockedData(out_of_raster);
 }
 
+void GpuImageDecodeCache::DecodedImageData::SetLockedData(
+    std::unique_ptr<base::DiscardableMemory> data,
+    sk_sp<SkImage> image_y,
+    sk_sp<SkImage> image_u,
+    sk_sp<SkImage> image_v,
+    bool out_of_raster) {
+  DCHECK(data);
+  DCHECK(!data_);
+  DCHECK(image_y);
+  DCHECK(image_u);
+  DCHECK(image_v);
+  DCHECK(!image_yuv_planes_);
+  data_ = std::move(data);
+  image_yuv_planes_ = std::array<sk_sp<SkImage>, SkYUVASizeInfo::kMaxCount>();
+  image_yuv_planes_->at(SkYUVAIndex::kY_Index) = std::move(image_y);
+  image_yuv_planes_->at(SkYUVAIndex::kU_Index) = std::move(image_u);
+  image_yuv_planes_->at(SkYUVAIndex::kV_Index) = std::move(image_v);
+  OnSetLockedData(out_of_raster);
+}
+
 void GpuImageDecodeCache::DecodedImageData::SetBitmapImage(
     sk_sp<SkImage> image) {
   DCHECK(is_bitmap_backed_);
@@ -519,15 +655,24 @@
 void GpuImageDecodeCache::DecodedImageData::ResetBitmapImage() {
   DCHECK(is_bitmap_backed_);
   image_ = nullptr;
+  image_yuv_planes_.reset();
   OnUnlock();
 }
 
 void GpuImageDecodeCache::DecodedImageData::ResetData() {
   if (data_) {
-    DCHECK(image_);
+    if (is_yuv()) {
+      DCHECK(image_yuv_planes_);
+      DCHECK(image_yuv_planes_->at(SkYUVAIndex::kY_Index));
+      DCHECK(image_yuv_planes_->at(SkYUVAIndex::kU_Index));
+      DCHECK(image_yuv_planes_->at(SkYUVAIndex::kV_Index));
+    } else {
+      DCHECK(image_);
+    }
     ReportUsageStats();
   }
   image_ = nullptr;
+  image_yuv_planes_.reset();
   data_ = nullptr;
   OnResetData();
 }
@@ -547,9 +692,13 @@
 GpuImageDecodeCache::UploadedImageData::UploadedImageData() = default;
 GpuImageDecodeCache::UploadedImageData::~UploadedImageData() {
   DCHECK(!image());
+  DCHECK(!image_yuv_planes_);
+  DCHECK(!gl_plane_ids_);
 }
 
-void GpuImageDecodeCache::UploadedImageData::SetImage(sk_sp<SkImage> image) {
+void GpuImageDecodeCache::UploadedImageData::SetImage(
+    sk_sp<SkImage> image,
+    bool represents_yuv_image) {
   DCHECK(mode_ == Mode::kNone);
   DCHECK(!image_);
   DCHECK(!transfer_cache_id_);
@@ -557,11 +706,40 @@
 
   mode_ = Mode::kSkImage;
   image_ = std::move(image);
-  if (image_->isTextureBacked())
+  // Calling isTexturedBacked() on the YUV SkImage would flatten it to RGB.
+  if (!represents_yuv_image && image_->isTextureBacked()) {
     gl_id_ = GlIdFromSkImage(image_.get());
+  } else {
+    gl_id_ = -1;
+  }
   OnSetLockedData(false /* out_of_raster */);
 }
 
+void GpuImageDecodeCache::UploadedImageData::SetYuvImage(
+    sk_sp<SkImage> y_image_input,
+    sk_sp<SkImage> u_image_input,
+    sk_sp<SkImage> v_image_input) {
+  DCHECK(!image_yuv_planes_);
+  DCHECK(!gl_plane_ids_);
+  DCHECK(!transfer_cache_id_);
+  DCHECK(y_image_input);
+  DCHECK(u_image_input);
+  DCHECK(v_image_input);
+
+  mode_ = Mode::kSkImage;
+  image_yuv_planes_ = std::array<sk_sp<SkImage>, SkYUVASizeInfo::kMaxCount>();
+  image_yuv_planes_->at(SkYUVAIndex::kY_Index) = std::move(y_image_input);
+  image_yuv_planes_->at(SkYUVAIndex::kU_Index) = std::move(u_image_input);
+  image_yuv_planes_->at(SkYUVAIndex::kV_Index) = std::move(v_image_input);
+  if (y_image()->isTextureBacked() && u_image()->isTextureBacked() &&
+      v_image()->isTextureBacked()) {
+    gl_plane_ids_ = std::array<GrGLuint, SkYUVASizeInfo::kMaxCount>();
+    gl_plane_ids_->at(SkYUVAIndex::kY_Index) = GlIdFromSkImage(y_image().get());
+    gl_plane_ids_->at(SkYUVAIndex::kU_Index) = GlIdFromSkImage(u_image().get());
+    gl_plane_ids_->at(SkYUVAIndex::kV_Index) = GlIdFromSkImage(v_image().get());
+  }
+}
+
 void GpuImageDecodeCache::UploadedImageData::SetTransferCacheId(uint32_t id) {
   DCHECK(mode_ == Mode::kNone);
   DCHECK(!image_);
@@ -575,9 +753,10 @@
 void GpuImageDecodeCache::UploadedImageData::Reset() {
   if (mode_ != Mode::kNone)
     ReportUsageStats();
-
   mode_ = Mode::kNone;
   image_ = nullptr;
+  image_yuv_planes_.reset();
+  gl_plane_ids_.reset();
   gl_id_ = 0;
   transfer_cache_id_.reset();
   OnResetData();
@@ -591,14 +770,14 @@
                         usage_stats_.first_lock_wasted);
 }
 
-GpuImageDecodeCache::ImageData::ImageData(
-    PaintImage::Id paint_image_id,
-    DecodedDataMode mode,
-    size_t size,
-    SkFilterQuality quality,
-    int upload_scale_mip_level,
-    bool needs_mips,
-    bool is_bitmap_backed)
+GpuImageDecodeCache::ImageData::ImageData(PaintImage::Id paint_image_id,
+                                          DecodedDataMode mode,
+                                          size_t size,
+                                          SkFilterQuality quality,
+                                          int upload_scale_mip_level,
+                                          bool needs_mips,
+                                          bool is_bitmap_backed,
+                                          bool is_yuv_format)
     : paint_image_id(paint_image_id),
       mode(mode),
       size(size),
@@ -606,6 +785,7 @@
       upload_scale_mip_level(upload_scale_mip_level),
       needs_mips(needs_mips),
       is_bitmap_backed(is_bitmap_backed),
+      is_yuv(is_yuv_format),
       decode(is_bitmap_backed) {}
 
 GpuImageDecodeCache::ImageData::~ImageData() {
@@ -627,7 +807,14 @@
 bool GpuImageDecodeCache::ImageData::HasUploadedData() const {
   switch (mode) {
     case DecodedDataMode::kGpu:
-      return !!upload.image();
+      // upload.image() stores the result of MakeFromYUVATextures
+      if (upload.image()) {
+        // TODO(915968): Be smarter about being able to re-upload planes
+        // selectively if only some get deleted from under us.
+        DCHECK(!is_yuv || upload.has_yuv_planes());
+        return true;
+      }
+      return false;
     case DecodedDataMode::kTransferCache:
       return !!upload.transfer_cache_id();
     case DecodedDataMode::kCpu:
@@ -1048,11 +1235,20 @@
     if (image_data->HasUploadedData() &&
         image_data->mode == DecodedDataMode::kGpu) {
       size_t discardable_size = image_data->size;
+      auto* context_support = context_->ContextSupport();
       // If the discardable system has deleted this out from under us, log a
       // size of 0 to match software discardable.
-      if (context_->ContextSupport()
-              ->ThreadsafeDiscardableTextureIsDeletedForTracing(
-                  image_data->upload.gl_id())) {
+      if (image_data->is_yuv &&
+          context_support->ThreadsafeDiscardableTextureIsDeletedForTracing(
+              image_data->upload.gl_y_id()) &&
+          context_support->ThreadsafeDiscardableTextureIsDeletedForTracing(
+              image_data->upload.gl_u_id()) &&
+          context_support->ThreadsafeDiscardableTextureIsDeletedForTracing(
+              image_data->upload.gl_v_id())) {
+        discardable_size = 0;
+      } else if (context_support
+                     ->ThreadsafeDiscardableTextureIsDeletedForTracing(
+                         image_data->upload.gl_id())) {
         discardable_size = 0;
       }
 
@@ -1069,12 +1265,19 @@
       dump->AddScalar("locked_size", MemoryAllocatorDump::kUnitsBytes,
                       locked_size);
 
-      // Create a global shred GUID to associate this data with its GPU
-      // process counterpart.
-      MemoryAllocatorDumpGuid guid = gl::GetGLTextureClientGUIDForTracing(
-          context_->ContextSupport()->ShareGroupTracingGUID(),
-          image_data->upload.gl_id());
-
+      // TODO(crbug.com/919296): Dump additional plane information for YUV.
+      // Create globally shared GUID(s) to associate this data with its
+      // GPU process counterpart.
+      MemoryAllocatorDumpGuid guid;
+      if (image_data->is_yuv) {  // Choose luma plane for identifying texture.
+        guid = gl::GetGLTextureClientGUIDForTracing(
+            context_->ContextSupport()->ShareGroupTracingGUID(),
+            image_data->upload.gl_y_id());
+      } else {
+        guid = gl::GetGLTextureClientGUIDForTracing(
+            context_->ContextSupport()->ShareGroupTracingGUID(),
+            image_data->upload.gl_id());
+      }
       // kImportance is somewhat arbitrary - we chose 3 to be higher than the
       // value used in the GPU process (1), and Skia (2), causing us to appear
       // as the owner in memory traces.
@@ -1439,7 +1642,12 @@
 
   if (image_data->is_bitmap_backed) {
     DCHECK(!draw_image.paint_image().IsLazyGenerated());
-    image_data->decode.SetBitmapImage(draw_image.paint_image().GetSkImage());
+    if (image_data->is_yuv) {
+      DLOG(ERROR) << "YUV + Bitmap is unknown and unimplemented!";
+      NOTREACHED();
+    } else {
+      image_data->decode.SetBitmapImage(draw_image.paint_image().GetSkImage());
+    }
     return;
   }
 
@@ -1455,43 +1663,83 @@
   image_data->decode.ResetData();
   std::unique_ptr<base::DiscardableMemory> backing_memory;
   sk_sp<SkImage> image;
+  // These are used only for decoding into YUV.
+  sk_sp<SkImage> image_y;
+  sk_sp<SkImage> image_u;
+  sk_sp<SkImage> image_v;
   {
     base::AutoUnlock unlock(lock_);
     backing_memory = base::DiscardableMemoryAllocator::GetInstance()
                          ->AllocateLockedDiscardableMemory(image_data->size);
+    sk_sp<SkColorSpace> color_space =
+        ColorSpaceForImageDecode(draw_image, image_data->mode);
+    auto release_proc = [](const void*, void*) {};
     SkImageInfo image_info = CreateImageInfoForDrawImage(
         draw_image, image_data->upload_scale_mip_level);
     SkPixmap pixmap(image_info, backing_memory->data(),
                     image_info.minRowBytes());
 
     // Set |pixmap| to the desired colorspace to decode into.
-    pixmap.setColorSpace(
-        ColorSpaceForImageDecode(draw_image, image_data->mode));
-    if (!DrawAndScaleImage(draw_image, &pixmap, generator_client_id_)) {
-      DLOG(ERROR) << "DrawAndScaleImage failed.";
-      backing_memory->Unlock();
-      backing_memory.reset();
-    } else {
-      image =
-          SkImage::MakeFromRaster(pixmap, [](const void*, void*) {}, nullptr);
+    pixmap.setColorSpace(color_space);
+
+    if (image_data->is_yuv) {
+      DLOG(WARNING) << "GpuImageDecodeCache wants to do YUV decoding/rendering";
+      SkPixmap pixmap_y;
+      SkPixmap pixmap_u;
+      SkPixmap pixmap_v;
+      if (!DrawAndScaleImage(draw_image, &pixmap, generator_client_id_,
+                             image_data->is_yuv, &pixmap_y, &pixmap_u,
+                             &pixmap_v)) {
+        DLOG(ERROR) << "DrawAndScaleImage failed.";
+        backing_memory->Unlock();
+        backing_memory.reset();
+      }
+      image_y = SkImage::MakeFromRaster(pixmap_y, release_proc, nullptr);
+      image_u = SkImage::MakeFromRaster(pixmap_u, release_proc, nullptr);
+      image_v = SkImage::MakeFromRaster(pixmap_v, release_proc, nullptr);
+    } else {  // RGBX decoding is the default path.
+      if (!DrawAndScaleImage(draw_image, &pixmap, generator_client_id_,
+                             image_data->is_yuv)) {
+        LOG(ERROR) << "DrawAndScaleImage failed.";
+        backing_memory->Unlock();
+        backing_memory.reset();
+      } else {
+        image = SkImage::MakeFromRaster(pixmap, release_proc, nullptr);
+      }
     }
   }
 
   if (image_data->decode.data()) {
-    DCHECK(image_data->decode.image());
     // An at-raster task decoded this before us. Ingore our decode.
+    if (image_data->is_yuv) {
+      DCHECK(image_data->decode.y_image());
+      DCHECK(image_data->decode.u_image());
+      DCHECK(image_data->decode.v_image());
+    } else {
+      DCHECK(image_data->decode.image());
+    }
     return;
   }
 
   if (!backing_memory) {
     DCHECK(!image);
+    DCHECK(!image_y);
+    DCHECK(!image_u);
+    DCHECK(!image_v);
     // If |backing_memory| was not populated, we had a non-decodable image.
     image_data->decode.decode_failure = true;
     return;
   }
 
-  image_data->decode.SetLockedData(std::move(backing_memory), std::move(image),
-                                   task_type == TaskType::kOutOfRaster);
+  if (image_data->is_yuv) {
+    image_data->decode.SetLockedData(
+        std::move(backing_memory), std::move(image_y), std::move(image_u),
+        std::move(image_v), task_type == TaskType::kOutOfRaster);
+  } else {
+    image_data->decode.SetLockedData(std::move(backing_memory),
+                                     std::move(image),
+                                     task_type == TaskType::kOutOfRaster);
+  }
 }
 
 void GpuImageDecodeCache::UploadImageIfNecessary(const DrawImage& draw_image,
@@ -1530,9 +1778,16 @@
 
   sk_sp<SkColorSpace> color_space =
       SupportsColorSpaceConversion() ? target_color_space_ : nullptr;
+  // For RGBX decoding, sometimes color conversion happens during the decode, so
+  // |decode.image()| has the most up-to-date colorspace. For YUV this is not
+  // the case, so we look at |paint_image|.
+  // TODO(crbug.com/911246): Currently we avoid YUV decoding for images with an
+  // ICC profile, so |decoded_target_colorspace| will be nullptr in those cases.
+  SkColorSpace* decoded_target_colorspace =
+      image_data->is_yuv ? draw_image.paint_image().color_space()
+                         : image_data->decode.image()->colorSpace();
   if (color_space &&
-      SkColorSpace::Equals(color_space.get(),
-                           image_data->decode.image()->colorSpace())) {
+      SkColorSpace::Equals(color_space.get(), decoded_target_colorspace)) {
     color_space = nullptr;
   }
 
@@ -1567,18 +1822,109 @@
   // If we reached this point, we are in the CPU/GPU path (not transfer cache).
   DCHECK(!use_transfer_cache_);
 
-  // Grab a reference to our decoded image. For the kCpu path, we will use this
-  // directly as our "uploaded" data.
+  // Grab a reference to our decoded image. For the kCpu path, we will use
+  // this directly as our "uploaded" data.
   sk_sp<SkImage> uploaded_image = image_data->decode.image();
   image_data->decode.mark_used();
+  GrMipMapped image_needs_mips =
+      image_data->needs_mips ? GrMipMapped::kYes : GrMipMapped::kNo;
 
+  if (image_data->is_yuv) {
+    // Grab a reference to our decoded image. For the kCpu path, we will use
+    // this directly as our "uploaded" data.
+    sk_sp<SkImage> uploaded_y_image = image_data->decode.y_image();
+    sk_sp<SkImage> uploaded_u_image = image_data->decode.u_image();
+    sk_sp<SkImage> uploaded_v_image = image_data->decode.v_image();
+
+    image_data->decode.mark_used();
+
+    // For kGpu, we upload and color convert (if necessary).
+    if (image_data->mode == DecodedDataMode::kGpu) {
+      DCHECK(!use_transfer_cache_);
+      base::AutoUnlock unlock(lock_);
+
+      // WebP documentation says to use Rec 601 for converting to RGB.
+      // TODO(crbug.com/915707): Change QueryYUVA8 to set the colorspace based
+      // on image type.
+      SkYUVColorSpace yuva_color_space =
+          SkYUVColorSpace::kRec601_SkYUVColorSpace;
+      SkColorSpace* decoded_target_colorspace =
+          draw_image.paint_image().color_space();
+
+      uploaded_y_image = uploaded_y_image->makeTextureImage(
+          context_->GrContext(), nullptr /* colorspace */, image_needs_mips);
+      uploaded_u_image = uploaded_u_image->makeTextureImage(
+          context_->GrContext(), nullptr /* colorspace */, image_needs_mips);
+      uploaded_v_image = uploaded_v_image->makeTextureImage(
+          context_->GrContext(), nullptr /* colorspace */, image_needs_mips);
+      if (!uploaded_y_image || !uploaded_u_image || !uploaded_v_image)
+        return;
+      size_t image_width = uploaded_y_image->width();
+      size_t image_height = uploaded_y_image->height();
+      uploaded_image = CreateImageFromYUVATexturesInternal(
+          uploaded_y_image.get(), uploaded_u_image.get(),
+          uploaded_v_image.get(), image_width, image_height, &yuva_color_space,
+          sk_ref_sp(decoded_target_colorspace));
+    }
+
+    // At-raster may have decoded this while we were unlocked. If so, ignore our
+    // result.
+    if (image_data->HasUploadedData()) {
+      if (uploaded_image) {
+        DCHECK(uploaded_y_image);
+        DCHECK(uploaded_u_image);
+        DCHECK(uploaded_v_image);
+        // We do not call DeleteSkImageAndPreventCaching for |uploaded_image|
+        // because calls to getBackendTexture will flatten the YUV planes to
+        // an RGB texture only to immediately delete it.
+        DeleteSkImageAndPreventCaching(context_, std::move(uploaded_y_image));
+        DeleteSkImageAndPreventCaching(context_, std::move(uploaded_u_image));
+        DeleteSkImageAndPreventCaching(context_, std::move(uploaded_v_image));
+      }
+      return;
+    }
+
+    // TODO(crbug.com/740737): |uploaded_image| is sometimes null in certain
+    // context-lost situations, so it is handled with an early out.
+    if (!uploaded_image || !uploaded_y_image || !uploaded_u_image ||
+        !uploaded_v_image)
+      return;
+
+    uploaded_y_image = TakeOwnershipOfSkImageBacking(
+        context_->GrContext(), std::move(uploaded_y_image));
+    uploaded_u_image = TakeOwnershipOfSkImageBacking(
+        context_->GrContext(), std::move(uploaded_u_image));
+    uploaded_v_image = TakeOwnershipOfSkImageBacking(
+        context_->GrContext(), std::move(uploaded_v_image));
+
+    image_data->upload.SetImage(std::move(uploaded_image), image_data->is_yuv);
+    image_data->upload.SetYuvImage(std::move(uploaded_y_image),
+                                   std::move(uploaded_u_image),
+                                   std::move(uploaded_v_image));
+
+    // If we have a new GPU-backed image, initialize it for use in the GPU
+    // discardable system.
+    if (image_data->mode == DecodedDataMode::kGpu) {
+      // Notify the discardable system of the planes so they will count against
+      // budgets.
+      context_->ContextGL()->InitializeDiscardableTextureCHROMIUM(
+          image_data->upload.gl_y_id());
+      context_->ContextGL()->InitializeDiscardableTextureCHROMIUM(
+          image_data->upload.gl_u_id());
+      context_->ContextGL()->InitializeDiscardableTextureCHROMIUM(
+          image_data->upload.gl_v_id());
+    }
+    // YUV decoding ends.
+    return;
+  }
+
+  // RGBX decoding is below.
   // For kGpu, we upload and color convert (if necessary).
   if (image_data->mode == DecodedDataMode::kGpu) {
     DCHECK(!use_transfer_cache_);
     base::AutoUnlock unlock(lock_);
-    uploaded_image = MakeTextureImage(
-        context_, std::move(uploaded_image), color_space,
-        image_data->needs_mips ? GrMipMapped::kYes : GrMipMapped::kNo);
+    uploaded_image = MakeTextureImage(context_, std::move(uploaded_image),
+                                      color_space, image_needs_mips);
   }
 
   // At-raster may have decoded this while we were unlocked. If so, ignore our
@@ -1598,8 +1944,10 @@
 
   // TODO(crbug.com/740737): uploaded_image is sometimes null in certain
   // context-lost situations.
-  if (!uploaded_image)
+  if (!uploaded_image) {
+    LOG(WARNING) << "TODO(crbug.com/740737): Context was lost. Early out.";
     return;
+  }
 
   image_data->upload.SetImage(std::move(uploaded_image));
 
@@ -1636,6 +1984,7 @@
   }
 
   size_t data_size = image_info.computeMinByteSize();
+  DCHECK(!SkImageInfo::ByteSizeOverflowed(data_size));
 
   // We need to cache the result of color conversion on the cpu if the image
   // will be color converted during the decode.
@@ -1654,10 +2003,26 @@
   const bool is_bitmap_backed = !draw_image.paint_image().IsLazyGenerated() &&
                                 upload_scale_mip_level == 0 &&
                                 !cache_color_conversion_on_cpu;
-  return base::WrapRefCounted(
-      new ImageData(draw_image.paint_image().stable_id(), mode, data_size,
-                    CalculateDesiredFilterQuality(draw_image),
-                    upload_scale_mip_level, needs_mips, is_bitmap_backed));
+  SkYUVASizeInfo temp_yuva_size_info;
+  const bool is_yuv = draw_image.paint_image().IsYuv(&temp_yuva_size_info) &&
+                      mode == DecodedDataMode::kGpu;
+
+  // TODO(crbug.com/910276): Change after alpha support.
+  // TODO(crbug.com/915972): Remove YUV420 assumption.
+  if (is_yuv) {
+    // We can't use |temp_yuva_size_info| because it doesn't know about
+    // any scaling based on mip levels that |image_info| does incorporate.
+    size_t y_size_bytes = image_info.width() * image_info.height();
+    size_t u_size_bytes =
+        ((image_info.width() + 1) / 2) * ((image_info.height() + 1) / 2);
+    size_t v_size_bytes = u_size_bytes;
+    data_size = y_size_bytes + u_size_bytes + v_size_bytes;
+  }
+
+  return base::WrapRefCounted(new ImageData(
+      draw_image.paint_image().stable_id(), mode, data_size,
+      CalculateDesiredFilterQuality(draw_image), upload_scale_mip_level,
+      needs_mips, is_bitmap_backed, is_yuv));
 }
 
 void GpuImageDecodeCache::WillAddCacheEntry(const DrawImage& draw_image) {
@@ -1713,8 +2078,16 @@
 void GpuImageDecodeCache::DeleteImage(ImageData* image_data) {
   if (image_data->HasUploadedData()) {
     DCHECK(!image_data->upload.is_locked());
-    if (image_data->mode == DecodedDataMode::kGpu)
-      images_pending_deletion_.push_back(image_data->upload.image());
+    if (image_data->mode == DecodedDataMode::kGpu) {
+      if (image_data->is_yuv) {
+        images_pending_deletion_.push_back(image_data->upload.y_image());
+        images_pending_deletion_.push_back(image_data->upload.u_image());
+        images_pending_deletion_.push_back(image_data->upload.v_image());
+        yuv_images_pending_deletion_.push_back(image_data->upload.image());
+      } else {
+        images_pending_deletion_.push_back(image_data->upload.image());
+      }
+    }
     if (image_data->mode == DecodedDataMode::kTransferCache)
       ids_pending_deletion_.push_back(*image_data->upload.transfer_cache_id());
   }
@@ -1724,7 +2097,13 @@
 void GpuImageDecodeCache::UnlockImage(ImageData* image_data) {
   DCHECK(image_data->HasUploadedData());
   if (image_data->mode == DecodedDataMode::kGpu) {
-    images_pending_unlock_.push_back(image_data->upload.image().get());
+    if (image_data->is_yuv) {
+      images_pending_unlock_.push_back(image_data->upload.y_image().get());
+      images_pending_unlock_.push_back(image_data->upload.u_image().get());
+      images_pending_unlock_.push_back(image_data->upload.v_image().get());
+    } else {
+      images_pending_unlock_.push_back(image_data->upload.image().get());
+    }
   } else {
     DCHECK(image_data->mode == DecodedDataMode::kTransferCache);
     ids_pending_unlock_.push_back(*image_data->upload.transfer_cache_id());
@@ -1734,8 +2113,22 @@
   // If we were holding onto an unmipped image for defering deletion, do it now
   // it is guarenteed to have no-refs.
   auto unmipped_image = image_data->upload.take_unmipped_image();
-  if (unmipped_image)
-    images_pending_deletion_.push_back(std::move(unmipped_image));
+  if (unmipped_image) {
+    if (image_data->is_yuv) {
+      auto unmipped_y_image = image_data->upload.take_unmipped_y_image();
+      auto unmipped_u_image = image_data->upload.take_unmipped_u_image();
+      auto unmipped_v_image = image_data->upload.take_unmipped_v_image();
+      DCHECK(unmipped_y_image);
+      DCHECK(unmipped_u_image);
+      DCHECK(unmipped_v_image);
+      images_pending_deletion_.push_back(std::move(unmipped_y_image));
+      images_pending_deletion_.push_back(std::move(unmipped_u_image));
+      images_pending_deletion_.push_back(std::move(unmipped_v_image));
+      yuv_images_pending_deletion_.push_back(std::move(unmipped_image));
+    } else {
+      images_pending_deletion_.push_back(std::move(unmipped_image));
+    }
+  }
 }
 
 // We always run pending operations in the following order:
@@ -1746,6 +2139,10 @@
 // As this can be run at-raster, to unlock/delete an image that was just used,
 // we need to call GlIdFromSkImage, which flushes pending IO on the image,
 // rather than just using a cached GL ID.
+// YUV images are handled slightly differently because they are backed by
+// texture images but are not themselves registered with the discardable memory
+// system. We wait to delete the pointer to a YUV image until we have a context
+// lock and its textures have been deleted.
 void GpuImageDecodeCache::RunPendingContextThreadOperations() {
   CheckContextLockAcquiredIfNecessary();
   lock_.AssertAcquired();
@@ -1768,6 +2165,8 @@
   }
   ids_pending_unlock_.clear();
 
+  yuv_images_pending_deletion_.clear();
+
   for (auto& image : images_pending_deletion_) {
     uint32_t texture_id = GlIdFromSkImage(image.get());
     if (context_->ContextGL()->LockDiscardableTextureCHROMIUM(texture_id)) {
@@ -1812,30 +2211,60 @@
       data->upload.OnLock();
       return true;
     }
-  } else if (have_context_lock == HaveContextLock::kYes &&
-             context_->ContextGL()->LockDiscardableTextureCHROMIUM(
-                 data->upload.gl_id())) {
-    DCHECK(!use_transfer_cache_);
-    DCHECK(data->mode == DecodedDataMode::kGpu);
+  } else if (have_context_lock == HaveContextLock::kYes) {
+    auto* gl_context = context_->ContextGL();
     // If |have_context_lock|, we can immediately lock the image and send
     // the lock command to the GPU process.
-    data->upload.OnLock();
-    return true;
-  } else if (context_->ContextSupport()
-                 ->ThreadSafeShallowLockDiscardableTexture(
-                     data->upload.gl_id())) {
-    DCHECK(!use_transfer_cache_);
-    DCHECK(data->mode == DecodedDataMode::kGpu);
-    // If !|have_context_lock|, we use ThreadsafeShallowLockDiscardableTexture.
-    // This takes a reference to the image, ensuring that it can't be deleted
-    // by the service, but delays sending a lock command over the command
-    // buffer. This command must be sent before the image is used, but is now
-    // guaranteed to succeed. We will send this command via
-    // CompleteLockDiscardableTextureOnContextThread in UploadImageIfNecessary,
-    // which is guaranteed to run before the texture is used.
-    data->upload.OnLock();
-    images_pending_complete_lock_.push_back(data->upload.image().get());
-    return true;
+    // TODO(crbug.com/914622): Add Chrome GL extension to upload texture array.
+    if (data->is_yuv &&
+        gl_context->LockDiscardableTextureCHROMIUM(data->upload.gl_y_id()) &&
+        gl_context->LockDiscardableTextureCHROMIUM(data->upload.gl_u_id()) &&
+        gl_context->LockDiscardableTextureCHROMIUM(data->upload.gl_v_id())) {
+      DCHECK(!use_transfer_cache_);
+      DCHECK(data->mode == DecodedDataMode::kGpu);
+      data->upload.OnLock();
+      return true;
+    } else if (!(data->is_yuv) && gl_context->LockDiscardableTextureCHROMIUM(
+                                      data->upload.gl_id())) {
+      DCHECK(!use_transfer_cache_);
+      DCHECK(data->mode == DecodedDataMode::kGpu);
+      data->upload.OnLock();
+      return true;
+    }
+  } else {
+    // If !|have_context_lock|, we use
+    // ThreadsafeShallowLockDiscardableTexture. This takes a reference to the
+    // image, ensuring that it can't be deleted by the service, but delays
+    // sending a lock command over the command buffer. This command must be
+    // sent before the image is used, but is now guaranteed to succeed. We
+    // will send this command via
+    // CompleteLockDiscardableTextureOnContextThread in
+    // UploadImageIfNecessary, which is guaranteed to run before the texture
+    // is used.
+    auto* context_support = context_->ContextSupport();
+    if (data->is_yuv &&
+        context_support->ThreadSafeShallowLockDiscardableTexture(
+            data->upload.gl_y_id()) &&
+        context_support->ThreadSafeShallowLockDiscardableTexture(
+            data->upload.gl_u_id()) &&
+        context_support->ThreadSafeShallowLockDiscardableTexture(
+            data->upload.gl_v_id())) {
+      DCHECK(!use_transfer_cache_);
+      DCHECK(data->mode == DecodedDataMode::kGpu);
+      data->upload.OnLock();
+      images_pending_complete_lock_.push_back(data->upload.y_image().get());
+      images_pending_complete_lock_.push_back(data->upload.u_image().get());
+      images_pending_complete_lock_.push_back(data->upload.v_image().get());
+      return true;
+    } else if (!(data->is_yuv) &&
+               context_support->ThreadSafeShallowLockDiscardableTexture(
+                   data->upload.gl_id())) {
+      DCHECK(!use_transfer_cache_);
+      DCHECK(data->mode == DecodedDataMode::kGpu);
+      data->upload.OnLock();
+      images_pending_complete_lock_.push_back(data->upload.image().get());
+      return true;
+    }
   }
 
   // Couldn't lock, abandon the image.
@@ -1977,6 +2406,47 @@
   context_->GetLock()->AssertAcquired();
 }
 
+sk_sp<SkImage> GpuImageDecodeCache::CreateImageFromYUVATexturesInternal(
+    const SkImage* uploaded_y_image,
+    const SkImage* uploaded_u_image,
+    const SkImage* uploaded_v_image,
+    const size_t image_width,
+    const size_t image_height,
+    const SkYUVColorSpace* yuva_color_space,
+    sk_sp<SkColorSpace> decoded_color_space) const {
+  DCHECK(uploaded_y_image);
+  DCHECK(uploaded_u_image);
+  DCHECK(uploaded_v_image);
+  DCHECK(yuva_color_space);
+  GrSurfaceOrigin origin_temp = kTopLeft_GrSurfaceOrigin;
+  GrBackendTexture yuv_textures[3]{};
+  yuv_textures[0] = uploaded_y_image->getBackendTexture(false);
+  yuv_textures[1] = uploaded_u_image->getBackendTexture(false);
+  yuv_textures[2] = uploaded_v_image->getBackendTexture(false);
+
+  SkYUVAIndex indices[SkYUVAIndex::kIndexCount];
+  indices[SkYUVAIndex::kY_Index] = {0, SkColorChannel::kR};
+  indices[SkYUVAIndex::kU_Index] = {1, SkColorChannel::kR};
+  indices[SkYUVAIndex::kV_Index] = {2, SkColorChannel::kR};
+  indices[SkYUVAIndex::kA_Index] = {-1, SkColorChannel::kR};
+
+  sk_sp<SkColorSpace> target_color_space =
+      SupportsColorSpaceConversion() ? target_color_space_ : nullptr;
+  if (target_color_space && SkColorSpace::Equals(target_color_space.get(),
+                                                 decoded_color_space.get())) {
+    target_color_space = nullptr;
+  }
+
+  sk_sp<SkImage> yuva_image = SkImage::MakeFromYUVATextures(
+      context_->GrContext(), *yuva_color_space, yuv_textures, indices,
+      SkISize::Make(image_width, image_height), origin_temp,
+      std::move(decoded_color_space));
+
+  if (target_color_space)
+    return yuva_image->makeColorSpace(target_color_space);
+  return yuva_image;
+}
+
 void GpuImageDecodeCache::UpdateMipsIfNeeded(const DrawImage& draw_image,
                                              ImageData* image_data) {
   CheckContextLockAcquiredIfNecessary();
@@ -1998,8 +2468,90 @@
       image_data->mode != DecodedDataMode::kGpu)
     return;
 
-  // Need to generate mips. Take a reference on the image we're about to delete,
-  // delaying deletion.
+  if (image_data->is_yuv) {
+    // Need to generate mips. Take a reference on the planes we're about to
+    // delete, delaying deletion.
+    // TODO(crbug.com/910276): Change after alpha support.
+    sk_sp<SkImage> previous_y_image = image_data->upload.y_image();
+    sk_sp<SkImage> previous_u_image = image_data->upload.u_image();
+    sk_sp<SkImage> previous_v_image = image_data->upload.v_image();
+
+    // Generate a new image from the previous, adding mips.
+    sk_sp<SkImage> image_y_with_mips = previous_y_image->makeTextureImage(
+        context_->GrContext(), nullptr, GrMipMapped::kYes);
+    sk_sp<SkImage> image_u_with_mips = previous_u_image->makeTextureImage(
+        context_->GrContext(), nullptr, GrMipMapped::kYes);
+    sk_sp<SkImage> image_v_with_mips = previous_v_image->makeTextureImage(
+        context_->GrContext(), nullptr, GrMipMapped::kYes);
+
+    // Handle lost context.
+    if (!image_y_with_mips || !image_u_with_mips || !image_v_with_mips)
+      return;
+
+    // No need to do anything if mipping this image results in the same
+    // textures. Deleting it below will result in lifetime issues.
+    // We expect that if one plane mips the same, the others should as well.
+    if (GlIdFromSkImage(image_y_with_mips.get()) ==
+            image_data->upload.gl_y_id() &&
+        GlIdFromSkImage(image_u_with_mips.get()) ==
+            image_data->upload.gl_u_id() &&
+        GlIdFromSkImage(image_v_with_mips.get()) ==
+            image_data->upload.gl_v_id())
+      return;
+
+    // Skia owns our new image planes, take ownership.
+    sk_sp<SkImage> image_y_with_mips_owned = TakeOwnershipOfSkImageBacking(
+        context_->GrContext(), std::move(image_y_with_mips));
+    sk_sp<SkImage> image_u_with_mips_owned = TakeOwnershipOfSkImageBacking(
+        context_->GrContext(), std::move(image_u_with_mips));
+    sk_sp<SkImage> image_v_with_mips_owned = TakeOwnershipOfSkImageBacking(
+        context_->GrContext(), std::move(image_v_with_mips));
+
+    // Handle lost context
+    if (!image_y_with_mips_owned || !image_u_with_mips_owned ||
+        !image_v_with_mips_owned)
+      return;
+
+    // WebP documentation says to use Rec 601 for converting to RGB.
+    // TODO(crbug.com/915707): Change QueryYUVA8 to set the colorspace based
+    // on image type.
+    SkYUVColorSpace yuva_color_space = SkYUVColorSpace::kRec601_SkYUVColorSpace;
+    size_t width = image_y_with_mips_owned->width();
+    size_t height = image_y_with_mips_owned->height();
+    SkColorSpace* decoded_color_space = draw_image.paint_image().color_space();
+    sk_sp<SkImage> yuv_image_with_mips_owned =
+        CreateImageFromYUVATexturesInternal(
+            image_y_with_mips_owned.get(), image_u_with_mips_owned.get(),
+            image_v_with_mips_owned.get(), width, height, &yuva_color_space,
+            sk_ref_sp(decoded_color_space));
+    // In case of lost context
+    if (!yuv_image_with_mips_owned)
+      return;
+
+    // The previous images might be in the in-use cache, potentially held
+    // externally. We must defer deleting them until the entry is unlocked.
+    image_data->upload.set_unmipped_image(image_data->upload.image());
+    image_data->upload.set_unmipped_yuv_images(image_data->upload.y_image(),
+                                               image_data->upload.u_image(),
+                                               image_data->upload.v_image());
+
+    // Set the new image on the cache.
+    image_data->upload.Reset();
+    image_data->upload.SetImage(std::move(yuv_image_with_mips_owned));
+    image_data->upload.SetYuvImage(std::move(image_y_with_mips_owned),
+                                   std::move(image_u_with_mips_owned),
+                                   std::move(image_v_with_mips_owned));
+    context_->ContextGL()->InitializeDiscardableTextureCHROMIUM(
+        image_data->upload.gl_y_id());
+    context_->ContextGL()->InitializeDiscardableTextureCHROMIUM(
+        image_data->upload.gl_u_id());
+    context_->ContextGL()->InitializeDiscardableTextureCHROMIUM(
+        image_data->upload.gl_v_id());
+    return;  // End YUV mip mapping.
+  }
+  // Begin RGBX mip mapping.
+  // Need to generate mips. Take a reference on the image we're about to
+  // delete, delaying deletion.
   sk_sp<SkImage> previous_image = image_data->upload.image();
 
   // Generate a new image from the previous, adding mips.
diff --git a/cc/tiles/gpu_image_decode_cache.h b/cc/tiles/gpu_image_decode_cache.h
index a167341..c5fb68c 100644
--- a/cc/tiles/gpu_image_decode_cache.h
+++ b/cc/tiles/gpu_image_decode_cache.h
@@ -7,6 +7,7 @@
 
 #include <memory>
 #include <unordered_map>
+#include <utility>
 #include <vector>
 
 #include "base/containers/mru_cache.h"
@@ -17,6 +18,7 @@
 #include "cc/cc_export.h"
 #include "cc/tiles/image_decode_cache.h"
 #include "third_party/skia/include/core/SkRefCnt.h"
+#include "third_party/skia/include/core/SkYUVAIndex.h"
 #include "third_party/skia/include/gpu/gl/GrGLTypes.h"
 
 namespace viz {
@@ -195,6 +197,8 @@
     scoped_refptr<TileTask> task;
 
    protected:
+    using YUVSkImages = std::array<sk_sp<SkImage>, SkYUVASizeInfo::kMaxCount>;
+
     struct UsageStats {
       int lock_count = 1;
       bool used = false;
@@ -220,6 +224,11 @@
     void SetLockedData(std::unique_ptr<base::DiscardableMemory> data,
                        sk_sp<SkImage> image,
                        bool out_of_raster);
+    void SetLockedData(std::unique_ptr<base::DiscardableMemory> data,
+                       sk_sp<SkImage> image_y,
+                       sk_sp<SkImage> image_u,
+                       sk_sp<SkImage> image_v,
+                       bool out_of_raster);
     void ResetData();
     base::DiscardableMemory* data() const { return data_.get(); }
 
@@ -231,6 +240,20 @@
       return image_;
     }
 
+    sk_sp<SkImage> y_image() const {
+      return plane_image_internal(SkYUVAIndex::kY_Index);
+    }
+
+    sk_sp<SkImage> u_image() const {
+      return plane_image_internal(SkYUVAIndex::kU_Index);
+    }
+
+    sk_sp<SkImage> v_image() const {
+      return plane_image_internal(SkYUVAIndex::kV_Index);
+    }
+
+    bool is_yuv() const { return image_yuv_planes_.has_value(); }
+
     // Test-only functions.
     sk_sp<SkImage> ImageForTesting() const { return image_; }
 
@@ -242,9 +265,18 @@
    private:
     void ReportUsageStats() const;
 
+    sk_sp<SkImage> plane_image_internal(const size_t plane_id) const {
+      DCHECK(is_locked());
+      DCHECK(image_yuv_planes_);
+      DCHECK_GT(image_yuv_planes_->size(), plane_id)
+          << "Requested reference to a plane_id that is not set";
+      return image_yuv_planes_->at(plane_id);
+    }
+
     const bool is_bitmap_backed_;
     std::unique_ptr<base::DiscardableMemory> data_;
-    sk_sp<SkImage> image_;
+    sk_sp<SkImage> image_;  // RGBX (or null in YUV decode path)
+    base::Optional<YUVSkImages> image_yuv_planes_;
   };
 
   // Stores the GPU-side image and supporting fields.
@@ -252,7 +284,12 @@
     UploadedImageData();
     ~UploadedImageData();
 
-    void SetImage(sk_sp<SkImage> image);
+    // If |represents_yuv_image| is true, the method knows not to check for a
+    // texture ID for |image|, which would inadvertently flatten it to RGB.
+    void SetImage(sk_sp<SkImage> image, bool represents_yuv_image = false);
+    void SetYuvImage(sk_sp<SkImage> y_image_input,
+                     sk_sp<SkImage> u_image_input,
+                     sk_sp<SkImage> v_image_input);
     void SetTransferCacheId(uint32_t id);
     void Reset();
 
@@ -261,11 +298,51 @@
       DCHECK(mode_ == Mode::kSkImage || mode_ == Mode::kNone);
       return image_;
     }
+    const sk_sp<SkImage>& y_image() const {
+      return plane_image_internal(SkYUVAIndex::kY_Index);
+    }
+    const sk_sp<SkImage>& u_image() const {
+      return plane_image_internal(SkYUVAIndex::kU_Index);
+    }
+    const sk_sp<SkImage>& v_image() const {
+      return plane_image_internal(SkYUVAIndex::kV_Index);
+    }
     GrGLuint gl_id() const {
       DCHECK(mode_ == Mode::kSkImage || mode_ == Mode::kNone);
       return gl_id_;
     }
 
+    GrGLuint gl_y_id() const {
+      return gl_plane_id_internal(SkYUVAIndex::kY_Index);
+    }
+    GrGLuint gl_u_id() const {
+      return gl_plane_id_internal(SkYUVAIndex::kU_Index);
+    }
+    GrGLuint gl_v_id() const {
+      return gl_plane_id_internal(SkYUVAIndex::kV_Index);
+    }
+
+    // We consider an image to be valid YUV if all planes are non-null.
+    bool has_yuv_planes() const {
+      static_assert(SkYUVAIndex::kLast_Index == SkYUVAIndex::kA_Index,
+                    "Alpha plane isn't last in the YUV plane array");
+      if (!image_yuv_planes_) {
+        return false;
+      }
+      auto yuv_planes_rstart = image_yuv_planes_->crbegin() + !is_alpha_;
+      auto yuv_planes_rend = image_yuv_planes_->crend();
+      // Iterates from end to beginning, skipping alpha plane (verified to be
+      // last) if the image is not alpha.
+      bool has_existing_planes = std::any_of(yuv_planes_rstart, yuv_planes_rend,
+                                             [](auto& it) { return it; });
+      bool has_null_planes = std::any_of(yuv_planes_rstart, yuv_planes_rend,
+                                         [](auto& it) { return !it; });
+      if (has_existing_planes && has_null_planes) {
+        DLOG(ERROR) << "Image has a mix of null and decoded planes";
+      }
+      return has_existing_planes && !has_null_planes;
+    }
+
     // If in transfer cache mode.
     base::Optional<uint32_t> transfer_cache_id() const {
       DCHECK(mode_ == Mode::kTransferCache || mode_ == Mode::kNone);
@@ -280,6 +357,37 @@
       return std::move(unmipped_image_);
     }
 
+    void set_unmipped_yuv_images(sk_sp<SkImage> y_image,
+                                 sk_sp<SkImage> u_image,
+                                 sk_sp<SkImage> v_image) {
+      if (!unmipped_yuv_images_) {
+        unmipped_yuv_images_ = YUVSkImages();
+      }
+      unmipped_yuv_images_->at(SkYUVAIndex::kY_Index) = std::move(y_image);
+      unmipped_yuv_images_->at(SkYUVAIndex::kU_Index) = std::move(u_image);
+      unmipped_yuv_images_->at(SkYUVAIndex::kV_Index) = std::move(v_image);
+    }
+
+    sk_sp<SkImage> take_unmipped_y_image() {
+      return take_unmipped_yuv_image_internal(SkYUVAIndex::kY_Index);
+    }
+
+    sk_sp<SkImage> take_unmipped_u_image() {
+      return take_unmipped_yuv_image_internal(SkYUVAIndex::kU_Index);
+    }
+
+    sk_sp<SkImage> take_unmipped_v_image() {
+      return take_unmipped_yuv_image_internal(SkYUVAIndex::kV_Index);
+    }
+
+    sk_sp<SkImage> take_unmipped_yuv_image_internal(const size_t plane_id) {
+      DCHECK(!is_locked_);
+      if (unmipped_yuv_images_ && unmipped_yuv_images_->size() > plane_id) {
+        return std::move(unmipped_yuv_images_->at(plane_id));
+      }
+      return nullptr;
+    }
+
    private:
     // Used for internal DCHECKs only.
     enum class Mode {
@@ -290,18 +398,43 @@
 
     void ReportUsageStats() const;
 
+    const sk_sp<SkImage>& plane_image_internal(const size_t plane_id) const {
+      DCHECK(mode_ == Mode::kSkImage || mode_ == Mode::kNone);
+      DCHECK(image_yuv_planes_);
+      DCHECK_GT(image_yuv_planes_->size(), plane_id)
+          << "Requested reference to a plane_id that is not set";
+      return image_yuv_planes_->at(plane_id);
+    }
+
+    GrGLuint gl_plane_id_internal(const size_t plane_id) const {
+      DCHECK(mode_ == Mode::kSkImage || mode_ == Mode::kNone);
+      DCHECK(gl_plane_ids_);
+      DCHECK_GT(gl_plane_ids_->size(), plane_id)
+          << "Requested GL id for a plane texture that is not uploaded";
+      return gl_plane_ids_->at(plane_id);
+    }
+
     Mode mode_ = Mode::kNone;
 
     // Used if |mode_| == kSkImage.
     // May be null if image not yet uploaded / prepared.
     sk_sp<SkImage> image_;
+    base::Optional<YUVSkImages> image_yuv_planes_;
+    // TODO(crbug/910276): Change after alpha support.
+    bool is_alpha_ = false;
     GrGLuint gl_id_ = 0;
+    base::Optional<std::array<GrGLuint, SkYUVASizeInfo::kMaxCount>>
+        gl_plane_ids_;
 
     // Used if |mode_| == kTransferCache.
     base::Optional<uint32_t> transfer_cache_id_;
 
-    // The original un-mipped image, retained until it can be safely deleted.
+    // The original un-mipped image, for RGBX, or the representative image
+    // backed by three planes for YUV. It is retained until it can be safely
+    // deleted.
     sk_sp<SkImage> unmipped_image_;
+    // Used for YUV decoding and null otherwise.
+    base::Optional<YUVSkImages> unmipped_yuv_images_;
   };
 
   struct ImageData : public base::RefCountedThreadSafe<ImageData> {
@@ -311,7 +444,8 @@
               SkFilterQuality quality,
               int upload_scale_mip_level,
               bool needs_mips,
-              bool is_bitmap_backed);
+              bool is_bitmap_backed,
+              bool is_yuv_format);
 
     bool IsGpuOrTransferCache() const;
     bool HasUploadedData() const;
@@ -324,6 +458,7 @@
     int upload_scale_mip_level;
     bool needs_mips = false;
     bool is_bitmap_backed;
+    bool is_yuv;
     bool is_budgeted = false;
 
     // If true, this image is no longer in our |persistent_cache_| and will be
@@ -407,6 +542,14 @@
   void DecodeImageIfNecessary(const DrawImage& draw_image,
                               ImageData* image_data,
                               TaskType task_type);
+  sk_sp<SkImage> CreateImageFromYUVATexturesInternal(
+      const SkImage* uploaded_y_image,
+      const SkImage* uploaded_u_image,
+      const SkImage* uploaded_v_image,
+      const size_t image_width,
+      const size_t image_height,
+      const SkYUVColorSpace* yuva_color_space,
+      sk_sp<SkColorSpace> decoded_color_space) const;
 
   scoped_refptr<GpuImageDecodeCache::ImageData> CreateImageData(
       const DrawImage& image);
@@ -510,6 +653,9 @@
   std::vector<SkImage*> images_pending_complete_lock_;
   std::vector<SkImage*> images_pending_unlock_;
   std::vector<sk_sp<SkImage>> images_pending_deletion_;
+  // Images that are backed by planar textures must be handled differently
+  // to avoid inadvertently flattening to RGB and creating additional textures.
+  std::vector<sk_sp<SkImage>> yuv_images_pending_deletion_;
   const sk_sp<SkColorSpace> target_color_space_;
 
   std::vector<uint32_t> ids_pending_unlock_;
diff --git a/chrome/android/java/monochrome_public_apk.proguard_flags.expected b/chrome/android/java/monochrome_public_apk.proguard_flags.expected
index 9241b68d..f6f3783 100644
--- a/chrome/android/java/monochrome_public_apk.proguard_flags.expected
+++ b/chrome/android/java/monochrome_public_apk.proguard_flags.expected
@@ -254,8 +254,8 @@
 
 # Contains flags that are applied only when ENABLE_DCHECK=false.
 
-# Ensure that GcStateAssert is fully optimized away.
--checkdiscard class org.chromium.base.GcStateAssert$WrappedReference { *; }
+# Ensure that LifetimeAssert implementation is not reachable in release.
+-checkdiscard class org.chromium.base.LifetimeAssert$WrappedReference { *; }
 
 ################################################################################
 # ../../base/android/proguard/enable_obfuscation.flags
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
index 601613f..6b85d55 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
@@ -271,6 +271,8 @@
     public static final String PAY_WITH_GOOGLE_V1 = "PayWithGoogleV1";
     public static final String PASSWORDS_KEYBOARD_ACCESSORY = "PasswordsKeyboardAccessory";
     public static final String PERMISSION_DELEGATION = "PermissionDelegation";
+    public static final String PER_METHOD_CAN_MAKE_PAYMENT_QUOTA =
+            "WebPaymentsPerMethodCanMakePaymentQuota";
     public static final String PREDICTIVE_PREFETCHING_ALLOWED_ON_ALL_CONNECTION_TYPES =
             "PredictivePrefetchingAllowedOnAllConnectionTypes";
     public static final String PROGRESS_BAR_THROTTLE = "ProgressBarThrottle";
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/PersonalDataManager.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/PersonalDataManager.java
index ee1aed5a..3b0e1f9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/PersonalDataManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/PersonalDataManager.java
@@ -11,6 +11,7 @@
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.JNINamespace;
 import org.chromium.chrome.R;
+import org.chromium.chrome.browser.ChromeFeatureList;
 import org.chromium.chrome.browser.ResourceId;
 import org.chromium.chrome.browser.preferences.MainPreferences;
 import org.chromium.chrome.browser.preferences.Pref;
@@ -634,10 +635,12 @@
      */
     public ArrayList<AutofillProfile> getBillingAddressesToSuggest() {
         ThreadUtils.assertOnUiThread();
+        boolean includeOrganizationInLabel =
+                ChromeFeatureList.isEnabled(ChromeFeatureList.AUTOFILL_ENABLE_COMPANY_NAME);
         return getProfilesWithLabels(
-                nativeGetProfileLabelsToSuggest(
-                        mPersonalDataManagerAndroid, true /* includeNameInLabel */,
-                        false /* includeOrganizationInLabel */, false /* includeCountryInLabel */),
+                nativeGetProfileLabelsToSuggest(mPersonalDataManagerAndroid,
+                        true /* includeNameInLabel */, includeOrganizationInLabel,
+                        false /* includeCountryInLabel */),
                 nativeGetProfileGUIDsToSuggest(mPersonalDataManagerAndroid));
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantBottomBarCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantBottomBarCoordinator.java
index 046e227..94f49ee0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantBottomBarCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantBottomBarCoordinator.java
@@ -4,6 +4,7 @@
 
 package org.chromium.chrome.browser.autofill_assistant;
 
+import android.content.Context;
 import android.support.design.widget.BottomSheetBehavior;
 import android.support.design.widget.CoordinatorLayout;
 import android.util.DisplayMetrics;
@@ -12,7 +13,6 @@
 import android.view.ViewGroup;
 import android.widget.LinearLayout;
 
-import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.autofill_assistant.carousel.AssistantCarouselCoordinator;
 import org.chromium.chrome.browser.autofill_assistant.details.AssistantDetailsCoordinator;
 import org.chromium.chrome.browser.autofill_assistant.header.AssistantHeaderCoordinator;
@@ -51,15 +51,15 @@
     private final AssistantPaymentRequestCoordinator mPaymentRequestCoordinator;
     private final AssistantCarouselCoordinator mCarouselCoordinator;
 
-    AssistantBottomBarCoordinator(ChromeActivity activity, WebContents webContents,
-            View assistantView, AssistantModel model) {
+    AssistantBottomBarCoordinator(
+            Context context, WebContents webContents, View assistantView, AssistantModel model) {
         mBottomBarView = assistantView.findViewById(
                 org.chromium.chrome.autofill_assistant.R.id.autofill_assistant_bottombar);
         mSwipeIndicatorView = mBottomBarView.findViewById(
                 org.chromium.chrome.autofill_assistant.R.id.swipe_indicator);
         mBottomBarBehavior = BottomSheetBehavior.from(mBottomBarView);
 
-        DisplayMetrics displayMetrics = activity.getResources().getDisplayMetrics();
+        DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
         mChildrenHorizontalMargin = (int) TypedValue.applyDimension(
                 TypedValue.COMPLEX_UNIT_DIP, CHILDREN_HORIZONTAL_MARGIN_DP, displayMetrics);
         mDetailsOnlyVerticalMargin = (int) TypedValue.applyDimension(
@@ -72,10 +72,10 @@
 
         // Instantiate child components.
         mHeaderCoordinator =
-                new AssistantHeaderCoordinator(activity, mBottomBarView, model.getHeaderModel());
-        mDetailsCoordinator = new AssistantDetailsCoordinator(activity, model.getDetailsModel());
-        mPaymentRequestCoordinator = new AssistantPaymentRequestCoordinator(activity, webContents);
-        mCarouselCoordinator = new AssistantCarouselCoordinator(activity, model.getCarouselModel());
+                new AssistantHeaderCoordinator(context, mBottomBarView, model.getHeaderModel());
+        mDetailsCoordinator = new AssistantDetailsCoordinator(context, model.getDetailsModel());
+        mPaymentRequestCoordinator = new AssistantPaymentRequestCoordinator(context, webContents);
+        mCarouselCoordinator = new AssistantCarouselCoordinator(context, model.getCarouselModel());
 
         // Add child views to bottom bar.
         mBottomBarView.addView(mDetailsCoordinator.getView());
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/details/AssistantDetailsCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/details/AssistantDetailsCoordinator.java
index 646d14c0..e1647dd 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/details/AssistantDetailsCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/details/AssistantDetailsCoordinator.java
@@ -4,12 +4,12 @@
 
 package org.chromium.chrome.browser.autofill_assistant.details;
 
+import android.content.Context;
 import android.support.annotation.Nullable;
 import android.view.LayoutInflater;
 import android.view.View;
 
 import org.chromium.chrome.autofill_assistant.R;
-import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.autofill_assistant.details.AssistantDetailsViewBinder.ViewHolder;
 import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
 
@@ -21,11 +21,11 @@
     private Runnable mOnVisibilityChanged;
     private final View mView;
 
-    public AssistantDetailsCoordinator(ChromeActivity activity, AssistantDetailsModel model) {
-        mView = LayoutInflater.from(activity).inflate(
+    public AssistantDetailsCoordinator(Context context, AssistantDetailsModel model) {
+        mView = LayoutInflater.from(context).inflate(
                 R.layout.autofill_assistant_details, /* root= */ null);
-        ViewHolder viewHolder = new ViewHolder(activity, mView);
-        AssistantDetailsViewBinder viewBinder = new AssistantDetailsViewBinder(activity);
+        ViewHolder viewHolder = new ViewHolder(context, mView);
+        AssistantDetailsViewBinder viewBinder = new AssistantDetailsViewBinder(context);
         PropertyModelChangeProcessor.create(model, viewHolder, viewBinder);
 
         // Details view is initially hidden.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestCoordinator.java
index 9be5c33..2cc62203 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestCoordinator.java
@@ -4,13 +4,13 @@
 
 package org.chromium.chrome.browser.autofill_assistant.payment;
 
+import android.content.Context;
 import android.support.annotation.Nullable;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.LinearLayout;
 
 import org.chromium.base.Promise;
-import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.autofill_assistant.payment.AutofillAssistantPaymentRequest.SelectedPaymentInformation;
 import org.chromium.content_public.browser.WebContents;
 import org.chromium.payments.mojom.PaymentOptions;
@@ -28,13 +28,13 @@
 
     private Promise<SelectedPaymentInformation> mCurrentPromise;
 
-    public AssistantPaymentRequestCoordinator(ChromeActivity activity, WebContents webContents) {
+    public AssistantPaymentRequestCoordinator(Context context, WebContents webContents) {
         mWebContents = webContents;
         assert webContents != null;
 
         // TODO(crbug.com/806868): Remove this.
-        mView = new LinearLayout(activity);
-        mView.addView(new View(activity));
+        mView = new LinearLayout(context);
+        mView.addView(new View(context));
 
         // Payment request is initially hidden.
         setVisible(false);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/StackLayoutBase.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/StackLayoutBase.java
index 10654f5..8c645fa 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/StackLayoutBase.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/StackLayoutBase.java
@@ -4,13 +4,12 @@
 
 package org.chromium.chrome.browser.compositor.layouts.phone;
 
-import static org.chromium.chrome.browser.compositor.layouts.ChromeAnimation.AnimatableAnimation.createAnimation;
-
 import android.content.Context;
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.os.SystemClock;
 import android.support.annotation.IntDef;
+import android.util.Pair;
 import android.view.ViewGroup;
 import android.view.ViewGroup.LayoutParams;
 import android.widget.FrameLayout;
@@ -22,8 +21,7 @@
 import org.chromium.chrome.browser.ChromeFeatureList;
 import org.chromium.chrome.browser.compositor.LayerTitleCache;
 import org.chromium.chrome.browser.compositor.animation.CompositorAnimator;
-import org.chromium.chrome.browser.compositor.layouts.ChromeAnimation;
-import org.chromium.chrome.browser.compositor.layouts.ChromeAnimation.Animatable;
+import org.chromium.chrome.browser.compositor.animation.FloatProperty;
 import org.chromium.chrome.browser.compositor.layouts.Layout;
 import org.chromium.chrome.browser.compositor.layouts.LayoutManager;
 import org.chromium.chrome.browser.compositor.layouts.LayoutRenderHost;
@@ -58,19 +56,53 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Comparator;
+import java.util.Iterator;
 import java.util.List;
 
 /**
  * Base class for layouts that show one or more stacks of tabs.
  */
-public abstract class StackLayoutBase extends Layout implements Animatable {
-    @IntDef({Property.INNER_MARGIN_PERCENT, Property.STACK_SNAP, Property.STACK_OFFSET_Y_PERCENT})
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface Property {
-        int INNER_MARGIN_PERCENT = 0;
-        int STACK_SNAP = 1;
-        int STACK_OFFSET_Y_PERCENT = 2;
-    }
+public abstract class StackLayoutBase extends Layout {
+    private static final FloatProperty<StackLayoutBase> INNER_MARGIN_PERCENT =
+            new FloatProperty<StackLayoutBase>("INNER_MARGIN_PERCENT") {
+                @Override
+                public void setValue(StackLayoutBase layoutBase, float v) {
+                    layoutBase.mInnerMarginPercent = v;
+                }
+
+                @Override
+                public Float get(StackLayoutBase layoutBase) {
+                    return layoutBase.mInnerMarginPercent;
+                }
+            };
+
+    private static final FloatProperty<StackLayoutBase> STACK_OFFSET_Y_PERCENT =
+            new FloatProperty<StackLayoutBase>("STACK_OFFSET_Y_PERCENT") {
+                @Override
+                public void setValue(StackLayoutBase layoutBase, float v) {
+                    layoutBase.mStackOffsetYPercent = v;
+                }
+
+                @Override
+                public Float get(StackLayoutBase layoutBase) {
+                    return layoutBase.mStackOffsetYPercent;
+                }
+            };
+
+    private static final FloatProperty<StackLayoutBase> STACK_SNAP =
+            new FloatProperty<StackLayoutBase>("STACK_SNAP") {
+                @Override
+                public void setValue(StackLayoutBase layoutBase, float v) {
+                    layoutBase.setStackSnap(v);
+                }
+
+                @Override
+                public Float get(StackLayoutBase layoutBase) {
+                    return layoutBase.mRenderedScrollOffset == layoutBase.mScrollIndexOffset
+                            ? layoutBase.mRenderedScrollOffset
+                            : null;
+                }
+            };
 
     @IntDef({DragDirection.NONE, DragDirection.HORIZONTAL, DragDirection.VERTICAL})
     @Retention(RetentionPolicy.SOURCE)
@@ -194,7 +226,8 @@
 
     private StackLayoutGestureHandler mGestureHandler;
 
-    private ChromeAnimation<Animatable> mLayoutAnimations;
+    private final ArrayList<Pair<CompositorAnimator, FloatProperty>> mLayoutAnimations =
+            new ArrayList<>();
 
     private class StackLayoutGestureHandler implements GestureHandler {
         @Override
@@ -349,6 +382,16 @@
     }
 
     /**
+     * Sets the stack stap value.
+     *
+     * @param v Value to set.
+     */
+    private void setStackSnap(float v) {
+        mRenderedScrollOffset = v;
+        mScrollIndexOffset = v;
+    }
+
+    /**
      * Whether or not the HorizontalTabSwitcherAndroid flag (which enables the new horizontal tab
      * switcher in both portrait and landscape mode) is enabled.
      */
@@ -646,16 +689,13 @@
     @Override
     public boolean onUpdateAnimation(long time, boolean jumpToEnd) {
         boolean animationsWasDone = true;
-        if (mLayoutAnimations != null) {
+        if (!mLayoutAnimations.isEmpty()) {
             if (jumpToEnd) {
-                animationsWasDone = mLayoutAnimations.finished();
-                mLayoutAnimations.updateAndFinish();
+                forceAnimationToFinish();
             } else {
-                animationsWasDone = mLayoutAnimations.update(time);
+                animationsWasDone = !isLayoutAnimating();
             }
-
             if (animationsWasDone || jumpToEnd) {
-                mLayoutAnimations = null;
                 onAnimationFinished();
             }
         }
@@ -775,23 +815,23 @@
 
     protected void startMarginAnimation(boolean enter, boolean showMargin) {
         // Any outstanding animations must be cancelled to avoid race condition.
-        cancelAnimation(this, Property.INNER_MARGIN_PERCENT);
+        cancelAnimation(INNER_MARGIN_PERCENT);
 
         float start = mInnerMarginPercent;
         float end = enter && showMargin ? 1.0f : 0.0f;
         if (start != end) {
-            addToAnimation(this, Property.INNER_MARGIN_PERCENT, start, end, 200, 0);
+            addToAnimation(INNER_MARGIN_PERCENT, start, end, 200, 0);
         }
     }
 
     private void startYOffsetAnimation(boolean enter) {
         // Any outstanding animations must be cancelled to avoid race condition.
-        cancelAnimation(this, Property.STACK_OFFSET_Y_PERCENT);
+        cancelAnimation(STACK_OFFSET_Y_PERCENT);
 
         float start = mStackOffsetYPercent;
         float end = enter ? 1.f : 0.f;
         if (start != end) {
-            addToAnimation(this, Property.STACK_OFFSET_Y_PERCENT, start, end, 300, 0);
+            addToAnimation(STACK_OFFSET_Y_PERCENT, start, end, 300, 0);
         }
     }
 
@@ -1132,7 +1172,7 @@
      * @param delta The amount to scroll by.
      */
     private void scrollStacks(float delta) {
-        cancelAnimation(this, Property.STACK_SNAP);
+        cancelAnimation(STACK_SNAP);
         float fullDistance = getFullScrollDistance();
         mScrollIndexOffset += MathUtils.flipSignIf(delta / fullDistance,
                 !isUsingHorizontalLayout() && LocalizationUtils.isLayoutRtl());
@@ -1160,16 +1200,16 @@
      * incognito to non-incognito, which leaves the up event in the incognito side.
      */
     private void finishScrollStacks() {
-        cancelAnimation(this, Property.STACK_SNAP);
+        cancelAnimation(STACK_SNAP);
         final int currentModelIndex = getTabStackIndex();
         float delta = Math.abs(currentModelIndex + mRenderedScrollOffset);
         float target = -currentModelIndex;
         if (delta != 0) {
             long duration = FLING_MIN_DURATION
                     + (long) Math.abs(delta * getFullScrollDistance() / mFlingSpeed);
-            addToAnimation(this, Property.STACK_SNAP, mRenderedScrollOffset, target, duration, 0);
+            addToAnimation(STACK_SNAP, mRenderedScrollOffset, target, duration, 0);
         } else {
-            setProperty(Property.STACK_SNAP, target);
+            setStackSnap(target);
             onAnimationFinished();
         }
     }
@@ -1488,30 +1528,6 @@
     }
 
     /**
-     * Sets properties for animations.
-     * @param prop The property to update
-     * @param p New value of the property
-     */
-    @Override
-    public void setProperty(@Property int prop, float p) {
-        switch (prop) {
-            case Property.STACK_SNAP:
-                mRenderedScrollOffset = p;
-                mScrollIndexOffset = p;
-                break;
-            case Property.INNER_MARGIN_PERCENT:
-                mInnerMarginPercent = p;
-                break;
-            case Property.STACK_OFFSET_Y_PERCENT:
-                mStackOffsetYPercent = p;
-                break;
-        }
-    }
-
-    @Override
-    public void onPropertyAnimationFinished(@Property int prop) {}
-
-    /**
      * Called by the stacks whenever they start an animation.
      */
     public void onStackAnimationStarted() {
@@ -1548,30 +1564,30 @@
     }
 
     /**
-     * Creates an {@link org.chromium.chrome.browser.compositor.layouts.ChromeAnimation
-     * .AnimatableAnimation} and adds it to the animation.
+     * Creates an {@link CompositorAnimator} and adds it to the animation.
      * Automatically sets the start value at the beginning of the animation.
      */
-    protected void addToAnimation(
-            Animatable object, int prop, float start, float end, long duration, long startTime) {
-        ChromeAnimation.Animation<Animatable> component = createAnimation(object, prop, start, end,
-                duration, startTime, false, CompositorAnimator.DECELERATE_INTERPOLATOR);
-        if (mLayoutAnimations == null || mLayoutAnimations.finished()) {
-            mLayoutAnimations = new ChromeAnimation<Animatable>();
-            mLayoutAnimations.start();
-        }
-        component.start();
-        mLayoutAnimations.add(component);
+    protected void addToAnimation(FloatProperty<StackLayoutBase> property, float start, float end,
+            long duration, long startTime) {
+        CompositorAnimator compositorAnimator = CompositorAnimator.ofFloatProperty(
+                getAnimationHandler(), this, property, start, end, duration);
+        compositorAnimator.setStartDelay(startTime);
+        compositorAnimator.start();
+
+        mLayoutAnimations.add(
+                new Pair<CompositorAnimator, FloatProperty>(compositorAnimator, property));
+
         requestUpdate();
     }
 
     @Override
     protected void forceAnimationToFinish() {
         super.forceAnimationToFinish();
-        if (mLayoutAnimations != null) {
-            mLayoutAnimations.updateAndFinish();
-            mLayoutAnimations = null;
+
+        for (int i = 0; i < mLayoutAnimations.size(); i++) {
+            mLayoutAnimations.get(i).first.end();
         }
+        mLayoutAnimations.clear();
     }
 
     /**
@@ -1579,13 +1595,26 @@
      * @param object The object being animated.
      * @param prop   The property to search for.
      */
-    protected void cancelAnimation(Animatable object, int prop) {
-        if (mLayoutAnimations != null) mLayoutAnimations.cancel(object, prop);
+    protected void cancelAnimation(FloatProperty<StackLayoutBase> property) {
+        Pair<CompositorAnimator, FloatProperty> a;
+        Iterator<Pair<CompositorAnimator, FloatProperty>> animationIterator =
+                mLayoutAnimations.iterator();
+
+        while (animationIterator.hasNext()) {
+            a = animationIterator.next();
+            if (a.second == property) {
+                a.first.cancel();
+                animationIterator.remove();
+            }
+        }
     }
 
     @Override
     @VisibleForTesting
     public boolean isLayoutAnimating() {
-        return mLayoutAnimations != null && !mLayoutAnimations.finished();
+        for (int i = 0; i < mLayoutAnimations.size(); i++) {
+            if (mLayoutAnimations.get(i).first.isRunning()) return true;
+        }
+        return false;
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/database/SQLiteCursor.java b/chrome/android/java/src/org/chromium/chrome/browser/database/SQLiteCursor.java
index 81016d4..82fbfbbf 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/database/SQLiteCursor.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/database/SQLiteCursor.java
@@ -7,7 +7,7 @@
 import android.database.AbstractCursor;
 import android.database.CursorWindow;
 
-import org.chromium.base.GcStateAssert;
+import org.chromium.base.LifetimeAssert;
 import org.chromium.base.annotations.CalledByNative;
 
 import java.sql.Types;
@@ -33,7 +33,7 @@
     private final Object mMoveLock = new Object();
     private final Object mGetBlobLock = new Object();
 
-    private final GcStateAssert mGcStateAssert = GcStateAssert.create(this);
+    private final LifetimeAssert mLifetimeAssert = LifetimeAssert.create(this);
 
     private SQLiteCursor(long nativeSQLiteCursor) {
         mNativeSQLiteCursor = nativeSQLiteCursor;
@@ -99,7 +99,7 @@
             if (mNativeSQLiteCursor != 0) {
                 nativeDestroy(mNativeSQLiteCursor);
                 mNativeSQLiteCursor = 0;
-                GcStateAssert.setSafeToGc(mGcStateAssert, true);
+                LifetimeAssert.setSafeToGc(mLifetimeAssert, true);
             }
         }
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/CanMakePaymentQuery.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/CanMakePaymentQuery.java
index d52f941..ff0e9bd 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/CanMakePaymentQuery.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/CanMakePaymentQuery.java
@@ -21,12 +21,13 @@
      * @param topLevelOrigin The top level origin using the Payment Request API.
      * @param frameOrigin    The frame origin using the Payment Request API.
      * @param query          The payment method identifiers and payment method specific data.
+     * @param perMethodQuota Whether each payment method has its own query quota.
      *
      * @return True if the given query for canMakePayment() is allowed.
      */
     public static boolean canQuery(WebContents webContents, String topLevelOrigin,
-            String frameOrigin, Map<String, PaymentMethodData> query) {
-        return nativeCanQuery(webContents, topLevelOrigin, frameOrigin, query);
+            String frameOrigin, Map<String, PaymentMethodData> query, boolean perMethodQuota) {
+        return nativeCanQuery(webContents, topLevelOrigin, frameOrigin, query, perMethodQuota);
     }
 
     @CalledByNative
@@ -44,5 +45,5 @@
     private CanMakePaymentQuery() {} // Do not instantiate.
 
     private static native boolean nativeCanQuery(WebContents webContents, String topLevelOrigin,
-            String frameOrigin, Map<String, PaymentMethodData> query);
+            String frameOrigin, Map<String, PaymentMethodData> query, boolean perMethodQuota);
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestFactory.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestFactory.java
index 9581d7e..c831680f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestFactory.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestFactory.java
@@ -68,7 +68,7 @@
         }
 
         @Override
-        public void hasEnrolledInstrument() {
+        public void hasEnrolledInstrument(boolean perMethodQuota) {
             if (mClient != null) {
                 mClient.onHasEnrolledInstrument(
                         HasEnrolledInstrumentQueryResult.HAS_NO_ENROLLED_INSTRUMENT);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java
index ec4140ef..b2f3f05 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java
@@ -162,33 +162,31 @@
      * Rule 5: Frequently and recently used instruments before rarely and non-recently used
      *         instruments.
      */
-    private static final Comparator<PaymentInstrument> PAYMENT_INSTRUMENT_COMPARATOR =
-            (a, b) -> {
-                // Payment apps (not autofill) first.
-                int autofill =
-                        (a.isAutofillInstrument() ? 1 : 0) - (b.isAutofillInstrument() ? 1 : 0);
-                if (autofill != 0) return autofill;
+    private static final Comparator<PaymentInstrument> PAYMENT_INSTRUMENT_COMPARATOR = (a, b) -> {
+        // Payment apps (not autofill) first.
+        int autofill = (a.isAutofillInstrument() ? 1 : 0) - (b.isAutofillInstrument() ? 1 : 0);
+        if (autofill != 0) return autofill;
 
-                // Complete cards before cards with missing information.
-                int completeness = (b.isComplete() ? 1 : 0) - (a.isComplete() ? 1 : 0);
-                if (completeness != 0) return completeness;
+        // Complete cards before cards with missing information.
+        int completeness = (b.isComplete() ? 1 : 0) - (a.isComplete() ? 1 : 0);
+        if (completeness != 0) return completeness;
 
-                // Cards with matching type before unknown type cards.
-                int typeMatch = (b.isExactlyMatchingMerchantRequest() ? 1 : 0)
-                        - (a.isExactlyMatchingMerchantRequest() ? 1 : 0);
-                if (typeMatch != 0) return typeMatch;
+        // Cards with matching type before unknown type cards.
+        int typeMatch = (b.isExactlyMatchingMerchantRequest() ? 1 : 0)
+                - (a.isExactlyMatchingMerchantRequest() ? 1 : 0);
+        if (typeMatch != 0) return typeMatch;
 
-                // Preselectable instruments before non-preselectable instruments.
-                // Note that this only affects service worker payment apps' instruments for now
-                // since autofill payment instruments have already been sorted by preselect
-                // after sorting by completeness and typeMatch. And the other payment apps'
-                // instruments can always be preselected.
-                int canPreselect = (b.canPreselect() ? 1 : 0) - (a.canPreselect() ? 1 : 0);
-                if (canPreselect != 0) return canPreselect;
+        // Preselectable instruments before non-preselectable instruments.
+        // Note that this only affects service worker payment apps' instruments for now
+        // since autofill payment instruments have already been sorted by preselect
+        // after sorting by completeness and typeMatch. And the other payment apps'
+        // instruments can always be preselected.
+        int canPreselect = (b.canPreselect() ? 1 : 0) - (a.canPreselect() ? 1 : 0);
+        if (canPreselect != 0) return canPreselect;
 
-                // More frequently and recently used instruments first.
-                return compareInstrumentsByFrecency(b, a);
-            };
+        // More frequently and recently used instruments first.
+        return compareInstrumentsByFrecency(b, a);
+    };
 
     private static PaymentRequestServiceObserverForTest sObserverForTest;
     private static boolean sIsLocalCanMakePaymentQueryQuotaEnforcedForTest;
@@ -231,6 +229,7 @@
     private PaymentRequestClient mClient;
     private boolean mIsCanMakePaymentResponsePending;
     private boolean mIsHasEnrolledInstrumentResponsePending;
+    private boolean mHasEnrolledInstrumentUsesPerMethodQuota;
     private boolean mIsCurrentPaymentRequestShowing;
     private final Queue<Runnable> mRetryQueue = new LinkedList<>();
 
@@ -393,7 +392,7 @@
 
         if (!OriginSecurityChecker.isSchemeCryptographic(mWebContents.getLastCommittedUrl())
                 && !OriginSecurityChecker.isOriginLocalhostOrFile(
-                           mWebContents.getLastCommittedUrl())) {
+                        mWebContents.getLastCommittedUrl())) {
             Log.d(TAG, "Only localhost, file://, and cryptographic scheme origins allowed");
             // Don't show any UI. Resolve .canMakePayment() with "false". Reject .show() with
             // "NotSupportedError".
@@ -636,7 +635,7 @@
             // missing.
             if (mPaymentMethodsSection.getSize() > 1 || selectedInstrument == null
                     || (selectedInstrument.isUserGestureRequiredToSkipUi()
-                               && !mIsUserGestureShow)) {
+                            && !mIsUserGestureShow)) {
                 mUI.show();
             } else {
                 mUI.dimBackground();
@@ -1608,9 +1607,11 @@
      * Called by the merchant website to check if the user has complete payment instruments.
      */
     @Override
-    public void hasEnrolledInstrument() {
+    public void hasEnrolledInstrument(boolean perMethodQuota) {
         if (mClient == null) return;
 
+        mHasEnrolledInstrumentUsesPerMethodQuota = perMethodQuota;
+
         if (isFinishedQueryingPaymentApps()) {
             respondHasEnrolledInstrumentQuery(mHasEnrolledInstrument);
         } else {
@@ -1623,8 +1624,8 @@
 
         mIsHasEnrolledInstrumentResponsePending = false;
 
-        if (CanMakePaymentQuery.canQuery(
-                    mWebContents, mTopLevelOrigin, mPaymentRequestOrigin, mMethodData)) {
+        if (CanMakePaymentQuery.canQuery(mWebContents, mTopLevelOrigin, mPaymentRequestOrigin,
+                    mMethodData, mHasEnrolledInstrumentUsesPerMethodQuota)) {
             mClient.onHasEnrolledInstrument(response
                             ? HasEnrolledInstrumentQueryResult.HAS_ENROLLED_INSTRUMENT
                             : HasEnrolledInstrumentQueryResult.HAS_NO_ENROLLED_INSTRUMENT);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappActivity.java
index d4717f0..776594a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappActivity.java
@@ -767,7 +767,7 @@
         int statusBarColor = Color.BLACK;
         if (mBrandColor != null && mWebappInfo.displayMode() != WebDisplayMode.FULLSCREEN) {
             taskDescriptionColor = mBrandColor;
-            statusBarColor = mBrandColor;
+            statusBarColor = ColorUtils.getDarkenedColorForStatusBar(mBrandColor);
             if (getToolbarManager() != null) {
                 getToolbarManager().onThemeColorChanged(mBrandColor, false);
             }
@@ -775,21 +775,17 @@
 
         ApiCompatibilityUtils.setTaskDescription(this, title, icon,
                 ColorUtils.getOpaqueColor(taskDescriptionColor));
-        setStatusBarColor(statusBarColor, statusBarColor != Color.BLACK);
+        ApiCompatibilityUtils.setStatusBarColor(getWindow(), statusBarColor);
     }
 
     @Override
     protected void setStatusBarColor(Tab tab, int color) {
-        // Ignore any color that is not the brand color.
-        super.setStatusBarColor(
-                mBrandColor == null ? Color.BLACK : mBrandColor, mBrandColor == null);
+        // Intentionally do nothing as WebappActivity explicitly sets status bar color.
     }
 
     @Override
     protected void setStatusBarColor(int color, boolean isDefaultThemeColor) {
-        // Ignore any color that is not the brand color.
-        super.setStatusBarColor(
-                mBrandColor == null ? Color.BLACK : mBrandColor, mBrandColor == null);
+        // Intentionally do nothing as WebappActivity explicitly sets status bar color.
     }
 
     @Override
diff --git a/chrome/android/java/strings/android_chrome_strings.grd b/chrome/android/java/strings/android_chrome_strings.grd
index e632055..2aafd2ef 100644
--- a/chrome/android/java/strings/android_chrome_strings.grd
+++ b/chrome/android/java/strings/android_chrome_strings.grd
@@ -4040,8 +4040,8 @@
       <message name="IDS_PREFS_AUTOFILL_ASSISTANT_SWITCH" desc="Title for the switch toggling whether Autofill Assistant is enabled. [CHAR-LIMIT=32]">
         Assistant Triggered Checkout
       </message>
-      <message name="IDS_AUTOFILL_ASSISTANT_GOOGLE_TERMS_DESCRIPTION" desc="Message linking to the Google terms and conditions for Fast Checkout.">
-        By continuing, you agree that Chrome will send data from Chrome Autofill, the site’s URL and its content to Google to provide this service.\n\nVisit Chrome settings to turn off Google Assistant in Chrome and Chrome Autofill.  <ph name="BEGIN_LINK">&lt;link&gt;</ph>Learn more<ph name="END_LINK">&lt;/link&gt;</ph>
+      <message name="IDS_AUTOFILL_ASSISTANT_GOOGLE_TERMS_DESCRIPTION" desc="Message linking to the Google terms and conditions for Google Assistant in Chrome.">
+        By continuing, you agree that Chrome will send data from Chrome Autofill, the site’s URL and its content to Google to provide this service. \n\nYou also agree that Chrome will send personal data you selected to the website via Chrome Autofill. Visit Chrome settings to turn off Google Assistant in Chrome and Chrome Autofill.  <ph name="BEGIN_LINK">&lt;link&gt;</ph>Learn more<ph name="END_LINK">&lt;/link&gt;</ph>
       </message>
       <message name="IDS_AUTOFILL_ASSISTANT_GOOGLE_TERMS_URL" desc="URL for Google Autofill Assistant Terms of Service" translateable="false">
         http://support.google.com/assistant?p=fast_checkout
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestPaymentAppCanMakePaymentQueryTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestPaymentAppCanMakePaymentQueryTest.java
index 05e367d..e356f3891 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestPaymentAppCanMakePaymentQueryTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestPaymentAppCanMakePaymentQueryTest.java
@@ -18,6 +18,7 @@
 
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.Feature;
+import org.chromium.chrome.browser.ChromeFeatureList;
 import org.chromium.chrome.browser.ChromeSwitches;
 import org.chromium.chrome.browser.payments.PaymentRequestTestRule.MainActivityStartCallback;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
@@ -29,7 +30,8 @@
  * A payment integration test for checking whether user can make a payment using a payment app.
  */
 @RunWith(ChromeJUnit4ClassRunner.class)
-@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
+@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE,
+        "enable-features=" + ChromeFeatureList.PER_METHOD_CAN_MAKE_PAYMENT_QUOTA})
 public class PaymentRequestPaymentAppCanMakePaymentQueryTest implements MainActivityStartCallback {
     @Rule
     public PaymentRequestTestRule mPaymentRequestTestRule = new PaymentRequestTestRule(
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/provider/ProviderBookmarksUriTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/provider/ProviderBookmarksUriTest.java
index 6f9f7fc..33805a6 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/provider/ProviderBookmarksUriTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/provider/ProviderBookmarksUriTest.java
@@ -82,26 +82,30 @@
         values.put(BookmarkColumns.TITLE, title);
         Uri uri = mProviderTestRule.getContentResolver().insert(mBookmarksUri, values);
         Cursor cursor = mProviderTestRule.getContentResolver().query(uri, null, null, null, null);
-        Assert.assertEquals(1, cursor.getCount());
-        Assert.assertTrue(cursor.moveToNext());
-        int index = cursor.getColumnIndex(BookmarkColumns.BOOKMARK);
-        Assert.assertTrue(-1 != index);
-        Assert.assertEquals(0, cursor.getInt(index));
-        index = cursor.getColumnIndex(BookmarkColumns.CREATED);
-        Assert.assertTrue(-1 != index);
-        Assert.assertEquals(createdTime, cursor.getLong(index));
-        index = cursor.getColumnIndex(BookmarkColumns.DATE);
-        Assert.assertTrue(-1 != index);
-        Assert.assertEquals(lastUpdateTime, cursor.getLong(index));
-        index = cursor.getColumnIndex(BookmarkColumns.FAVICON);
-        Assert.assertTrue(-1 != index);
-        Assert.assertTrue(byteArraysEqual(FAVICON_DATA, cursor.getBlob(index)));
-        index = cursor.getColumnIndex(BookmarkColumns.URL);
-        Assert.assertTrue(-1 != index);
-        Assert.assertEquals(url, cursor.getString(index));
-        index = cursor.getColumnIndex(BookmarkColumns.VISITS);
-        Assert.assertTrue(-1 != index);
-        Assert.assertEquals(visits, cursor.getInt(index));
+        try {
+            Assert.assertEquals(1, cursor.getCount());
+            Assert.assertTrue(cursor.moveToNext());
+            int index = cursor.getColumnIndex(BookmarkColumns.BOOKMARK);
+            Assert.assertTrue(-1 != index);
+            Assert.assertEquals(0, cursor.getInt(index));
+            index = cursor.getColumnIndex(BookmarkColumns.CREATED);
+            Assert.assertTrue(-1 != index);
+            Assert.assertEquals(createdTime, cursor.getLong(index));
+            index = cursor.getColumnIndex(BookmarkColumns.DATE);
+            Assert.assertTrue(-1 != index);
+            Assert.assertEquals(lastUpdateTime, cursor.getLong(index));
+            index = cursor.getColumnIndex(BookmarkColumns.FAVICON);
+            Assert.assertTrue(-1 != index);
+            Assert.assertTrue(byteArraysEqual(FAVICON_DATA, cursor.getBlob(index)));
+            index = cursor.getColumnIndex(BookmarkColumns.URL);
+            Assert.assertTrue(-1 != index);
+            Assert.assertEquals(url, cursor.getString(index));
+            index = cursor.getColumnIndex(BookmarkColumns.VISITS);
+            Assert.assertTrue(-1 != index);
+            Assert.assertEquals(visits, cursor.getInt(index));
+        } finally {
+            cursor.close();
+        }
     }
 
     @Test
@@ -132,27 +136,31 @@
                         + BookmarkColumns.VISITS + " = ? AND " + BookmarkColumns.BOOKMARK
                         + " = ? AND " + BookmarkColumns.FAVICON + " IS NOT NULL",
                 selectionArgs, null);
-        Assert.assertNotNull(cursor);
-        Assert.assertEquals(1, cursor.getCount());
-        Assert.assertTrue(cursor.moveToNext());
-        int index = cursor.getColumnIndex(BookmarkColumns.BOOKMARK);
-        Assert.assertTrue(-1 != index);
-        Assert.assertEquals(isBookmark[0], cursor.getInt(index));
-        index = cursor.getColumnIndex(BookmarkColumns.CREATED);
-        Assert.assertTrue(-1 != index);
-        Assert.assertEquals(createdTime[0], cursor.getLong(index));
-        index = cursor.getColumnIndex(BookmarkColumns.DATE);
-        Assert.assertTrue(-1 != index);
-        Assert.assertEquals(lastUpdateTime[0], cursor.getLong(index));
-        index = cursor.getColumnIndex(BookmarkColumns.FAVICON);
-        Assert.assertTrue(-1 != index);
-        Assert.assertTrue(byteArraysEqual(icons[0], cursor.getBlob(index)));
-        index = cursor.getColumnIndex(BookmarkColumns.URL);
-        Assert.assertTrue(-1 != index);
-        Assert.assertEquals(url[0], cursor.getString(index));
-        index = cursor.getColumnIndex(BookmarkColumns.VISITS);
-        Assert.assertTrue(-1 != index);
-        Assert.assertEquals(visits[0], cursor.getInt(index));
+        try {
+            Assert.assertNotNull(cursor);
+            Assert.assertEquals(1, cursor.getCount());
+            Assert.assertTrue(cursor.moveToNext());
+            int index = cursor.getColumnIndex(BookmarkColumns.BOOKMARK);
+            Assert.assertTrue(-1 != index);
+            Assert.assertEquals(isBookmark[0], cursor.getInt(index));
+            index = cursor.getColumnIndex(BookmarkColumns.CREATED);
+            Assert.assertTrue(-1 != index);
+            Assert.assertEquals(createdTime[0], cursor.getLong(index));
+            index = cursor.getColumnIndex(BookmarkColumns.DATE);
+            Assert.assertTrue(-1 != index);
+            Assert.assertEquals(lastUpdateTime[0], cursor.getLong(index));
+            index = cursor.getColumnIndex(BookmarkColumns.FAVICON);
+            Assert.assertTrue(-1 != index);
+            Assert.assertTrue(byteArraysEqual(icons[0], cursor.getBlob(index)));
+            index = cursor.getColumnIndex(BookmarkColumns.URL);
+            Assert.assertTrue(-1 != index);
+            Assert.assertEquals(url[0], cursor.getString(index));
+            index = cursor.getColumnIndex(BookmarkColumns.VISITS);
+            Assert.assertTrue(-1 != index);
+            Assert.assertEquals(visits[0], cursor.getInt(index));
+        } finally {
+            cursor.close();
+        }
 
         // Query the 2nd row.
         String[] selectionArgs2 = { url[1], String.valueOf(lastUpdateTime[1]),
@@ -162,27 +170,30 @@
                         + BookmarkColumns.VISITS + " = ? AND " + BookmarkColumns.BOOKMARK
                         + " = ? AND " + BookmarkColumns.FAVICON + " IS NULL",
                 selectionArgs2, null);
-        Assert.assertNotNull(cursor);
-        Assert.assertEquals(1, cursor.getCount());
-        Assert.assertTrue(cursor.moveToNext());
-        index = cursor.getColumnIndex(BookmarkColumns.BOOKMARK);
-        Assert.assertTrue(-1 != index);
-        Assert.assertEquals(isBookmark[1], cursor.getInt(index));
-        index = cursor.getColumnIndex(BookmarkColumns.CREATED);
-        Assert.assertTrue(-1 != index);
-        Assert.assertEquals(createdTime[1], cursor.getLong(index));
-        index = cursor.getColumnIndex(BookmarkColumns.DATE);
-        Assert.assertTrue(-1 != index);
-        Assert.assertEquals(lastUpdateTime[1], cursor.getLong(index));
-        index = cursor.getColumnIndex(BookmarkColumns.FAVICON);
-        Assert.assertTrue(-1 != index);
-        Assert.assertTrue(byteArraysEqual(icons[1], cursor.getBlob(index)));
-        index = cursor.getColumnIndex(BookmarkColumns.URL);
-        Assert.assertTrue(-1 != index);
-        Assert.assertEquals(url[1], cursor.getString(index));
-        index = cursor.getColumnIndex(BookmarkColumns.VISITS);
-        Assert.assertTrue(-1 != index);
-        Assert.assertEquals(visits[1], cursor.getInt(index));
+        try {
+            Assert.assertEquals(1, cursor.getCount());
+            Assert.assertTrue(cursor.moveToNext());
+            int index = cursor.getColumnIndex(BookmarkColumns.BOOKMARK);
+            Assert.assertTrue(-1 != index);
+            Assert.assertEquals(isBookmark[1], cursor.getInt(index));
+            index = cursor.getColumnIndex(BookmarkColumns.CREATED);
+            Assert.assertTrue(-1 != index);
+            Assert.assertEquals(createdTime[1], cursor.getLong(index));
+            index = cursor.getColumnIndex(BookmarkColumns.DATE);
+            Assert.assertTrue(-1 != index);
+            Assert.assertEquals(lastUpdateTime[1], cursor.getLong(index));
+            index = cursor.getColumnIndex(BookmarkColumns.FAVICON);
+            Assert.assertTrue(-1 != index);
+            Assert.assertTrue(byteArraysEqual(icons[1], cursor.getBlob(index)));
+            index = cursor.getColumnIndex(BookmarkColumns.URL);
+            Assert.assertTrue(-1 != index);
+            Assert.assertEquals(url[1], cursor.getString(index));
+            index = cursor.getColumnIndex(BookmarkColumns.VISITS);
+            Assert.assertTrue(-1 != index);
+            Assert.assertEquals(visits[1], cursor.getInt(index));
+        } finally {
+            cursor.close();
+        }
     }
 
     @Test
@@ -217,26 +228,30 @@
                         + BookmarkColumns.BOOKMARK + " = ?",
                 selectionArgs);
         Cursor cursor = mProviderTestRule.getContentResolver().query(uri, null, null, null, null);
-        Assert.assertEquals(1, cursor.getCount());
-        Assert.assertTrue(cursor.moveToNext());
-        int index = cursor.getColumnIndex(BookmarkColumns.BOOKMARK);
-        Assert.assertTrue(-1 != index);
-        Assert.assertEquals(isBookmark[1], cursor.getInt(index));
-        index = cursor.getColumnIndex(BookmarkColumns.CREATED);
-        Assert.assertTrue(-1 != index);
-        Assert.assertEquals(createdTime[0], cursor.getLong(index));
-        index = cursor.getColumnIndex(BookmarkColumns.DATE);
-        Assert.assertTrue(-1 != index);
-        Assert.assertEquals(lastUpdateTime[1], cursor.getLong(index));
-        index = cursor.getColumnIndex(BookmarkColumns.FAVICON);
-        Assert.assertTrue(-1 != index);
-        Assert.assertTrue(byteArraysEqual(icons[1], cursor.getBlob(index)));
-        index = cursor.getColumnIndex(BookmarkColumns.URL);
-        Assert.assertTrue(-1 != index);
-        Assert.assertEquals(url[1], cursor.getString(index));
-        index = cursor.getColumnIndex(BookmarkColumns.VISITS);
-        Assert.assertTrue(-1 != index);
-        Assert.assertEquals(visits[1], cursor.getInt(index));
+        try {
+            Assert.assertEquals(1, cursor.getCount());
+            Assert.assertTrue(cursor.moveToNext());
+            int index = cursor.getColumnIndex(BookmarkColumns.BOOKMARK);
+            Assert.assertTrue(-1 != index);
+            Assert.assertEquals(isBookmark[1], cursor.getInt(index));
+            index = cursor.getColumnIndex(BookmarkColumns.CREATED);
+            Assert.assertTrue(-1 != index);
+            Assert.assertEquals(createdTime[0], cursor.getLong(index));
+            index = cursor.getColumnIndex(BookmarkColumns.DATE);
+            Assert.assertTrue(-1 != index);
+            Assert.assertEquals(lastUpdateTime[1], cursor.getLong(index));
+            index = cursor.getColumnIndex(BookmarkColumns.FAVICON);
+            Assert.assertTrue(-1 != index);
+            Assert.assertTrue(byteArraysEqual(icons[1], cursor.getBlob(index)));
+            index = cursor.getColumnIndex(BookmarkColumns.URL);
+            Assert.assertTrue(-1 != index);
+            Assert.assertEquals(url[1], cursor.getString(index));
+            index = cursor.getColumnIndex(BookmarkColumns.VISITS);
+            Assert.assertTrue(-1 != index);
+            Assert.assertEquals(visits[1], cursor.getInt(index));
+        } finally {
+            cursor.close();
+        }
     }
 
     @Test
@@ -267,11 +282,17 @@
                 selectionArgs);
         Cursor cursor =
                 mProviderTestRule.getContentResolver().query(uris[0], null, null, null, null);
-        Assert.assertNotNull(cursor);
-        Assert.assertEquals(0, cursor.getCount());
+        try {
+            Assert.assertEquals(0, cursor.getCount());
+        } finally {
+            cursor.close();
+        }
         cursor = mProviderTestRule.getContentResolver().query(uris[1], null, null, null, null);
-        Assert.assertNotNull(cursor);
-        Assert.assertEquals(1, cursor.getCount());
+        try {
+            Assert.assertEquals(1, cursor.getCount());
+        } finally {
+            cursor.close();
+        }
         String[] selectionArgs1 = { String.valueOf(lastUpdateTime[1]),
                 String.valueOf(isBookmark[1]) };
         mProviderTestRule.getContentResolver().delete(mBookmarksUri,
@@ -279,8 +300,11 @@
                         + BookmarkColumns.BOOKMARK + " = ?",
                 selectionArgs1);
         cursor = mProviderTestRule.getContentResolver().query(uris[1], null, null, null, null);
-        Assert.assertNotNull(cursor);
-        Assert.assertEquals(0, cursor.getCount());
+        try {
+            Assert.assertEquals(0, cursor.getCount());
+        } finally {
+            cursor.close();
+        }
     }
 
     /*
@@ -323,18 +347,21 @@
         Cursor cursor = mProviderTestRule.getContentResolver().query(mBookmarksUri,
                 bookmarksProjection, BookmarkColumns.TITLE + " = ?",
                 new String[] {insertBookmarkTitle}, BookmarkColumns.DATE);
-        Assert.assertTrue(cursor.moveToNext());
-        Assert.assertEquals(insertBookmarkTitle, cursor.getString(titleIndex));
-        Assert.assertEquals(insertBookmarkUrl, cursor.getString(urlIndex));
-        Assert.assertEquals(0, cursor.getInt(visitsIndex));
-        Assert.assertEquals(createDate, cursor.getLong(dataIndex));
-        Assert.assertEquals(createDate, cursor.getLong(createdIndex));
-        Assert.assertEquals(0, cursor.getInt(bookmarkIndex));
-        // TODO(michaelbai): according to the test this should be null instead of an empty byte[].
-        // BUG 6288508
-        // assertTrue(cursor.isNull(FAVICON_INDEX));
-        int Id = cursor.getInt(idIndex);
-        cursor.close();
+        int id = -1;
+        try {
+            Assert.assertTrue(cursor.moveToNext());
+            Assert.assertEquals(insertBookmarkTitle, cursor.getString(titleIndex));
+            Assert.assertEquals(insertBookmarkUrl, cursor.getString(urlIndex));
+            Assert.assertEquals(0, cursor.getInt(visitsIndex));
+            Assert.assertEquals(createDate, cursor.getLong(dataIndex));
+            Assert.assertEquals(createDate, cursor.getLong(createdIndex));
+            Assert.assertEquals(0, cursor.getInt(bookmarkIndex));
+            // TODO(michaelbai): according to the test this should be null instead of an empty
+            // byte[]. BUG 6288508 assertTrue(cursor.isNull(FAVICON_INDEX));
+            id = cursor.getInt(idIndex);
+        } finally {
+            cursor.close();
+        }
 
         // Test: update.
         value.clear();
@@ -347,24 +374,31 @@
         mProviderTestRule.getContentResolver().update(mBookmarksUri, value,
                 BookmarkColumns.TITLE + " = ?", new String[] {insertBookmarkTitle});
         cursor = mProviderTestRule.getContentResolver().query(
-                mBookmarksUri, bookmarksProjection, BookmarkColumns.ID + " = " + Id, null, null);
-        Assert.assertTrue(cursor.moveToNext());
-        Assert.assertEquals(updateBookmarkTitle, cursor.getString(titleIndex));
-        Assert.assertEquals(updateBookmarkUrl, cursor.getString(urlIndex));
-        Assert.assertEquals(1, cursor.getInt(visitsIndex));
-        Assert.assertEquals(updateDate, cursor.getLong(dataIndex));
-        Assert.assertEquals(createDate, cursor.getLong(createdIndex));
-        Assert.assertEquals(0, cursor.getInt(bookmarkIndex));
-        // TODO(michaelbai): according to the test this should be null instead of an empty byte[].
-        // BUG 6288508
-        // assertTrue(cursor.isNull(FAVICON_INDEX));
-        Assert.assertEquals(Id, cursor.getInt(idIndex));
+                mBookmarksUri, bookmarksProjection, BookmarkColumns.ID + " = " + id, null, null);
+        try {
+            Assert.assertTrue(cursor.moveToNext());
+            Assert.assertEquals(updateBookmarkTitle, cursor.getString(titleIndex));
+            Assert.assertEquals(updateBookmarkUrl, cursor.getString(urlIndex));
+            Assert.assertEquals(1, cursor.getInt(visitsIndex));
+            Assert.assertEquals(updateDate, cursor.getLong(dataIndex));
+            Assert.assertEquals(createDate, cursor.getLong(createdIndex));
+            Assert.assertEquals(0, cursor.getInt(bookmarkIndex));
+            // TODO(michaelbai): according to the test this should be null instead of an empty
+            // byte[]. BUG 6288508 assertTrue(cursor.isNull(FAVICON_INDEX));
+            Assert.assertEquals(id, cursor.getInt(idIndex));
+        } finally {
+            cursor.close();
+        }
 
         // Test: delete.
         mProviderTestRule.getContentResolver().delete(insertUri, null, null);
         cursor = mProviderTestRule.getContentResolver().query(
-                mBookmarksUri, bookmarksProjection, BookmarkColumns.ID + " = " + Id, null, null);
-        Assert.assertEquals(0, cursor.getCount());
+                mBookmarksUri, bookmarksProjection, BookmarkColumns.ID + " = " + id, null, null);
+        try {
+            Assert.assertEquals(0, cursor.getCount());
+        } finally {
+            cursor.close();
+        }
     }
 
     /**
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/provider/ProviderSearchesUriTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/provider/ProviderSearchesUriTest.java
index c08e3746..0d77df31 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/provider/ProviderSearchesUriTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/provider/ProviderSearchesUriTest.java
@@ -66,15 +66,18 @@
         Cursor cursor = mProviderTestRule.getContentResolver().query(uri, null,
                 SearchColumns.SEARCH + "=? AND " + SearchColumns.DATE + " = ? ", selectionArgs,
                 null);
-        Assert.assertNotNull(cursor);
-        Assert.assertEquals(1, cursor.getCount());
-        Assert.assertTrue(cursor.moveToNext());
-        int index = cursor.getColumnIndex(SearchColumns.SEARCH);
-        Assert.assertTrue(-1 != index);
-        Assert.assertEquals(searchTerm, cursor.getString(index));
-        index = cursor.getColumnIndex(SearchColumns.DATE);
-        Assert.assertTrue(-1 != index);
-        Assert.assertEquals(searchTime, cursor.getLong(index));
+        try {
+            Assert.assertEquals(1, cursor.getCount());
+            Assert.assertTrue(cursor.moveToNext());
+            int index = cursor.getColumnIndex(SearchColumns.SEARCH);
+            Assert.assertTrue(-1 != index);
+            Assert.assertEquals(searchTerm, cursor.getString(index));
+            index = cursor.getColumnIndex(SearchColumns.DATE);
+            Assert.assertTrue(-1 != index);
+            Assert.assertEquals(searchTime, cursor.getLong(index));
+        } finally {
+            cursor.close();
+        }
     }
 
     @Test
@@ -91,20 +94,26 @@
         String[] selectionArgs = { searchTerm[0] };
         Cursor cursor = mProviderTestRule.getContentResolver().query(
                 mSearchesUri, null, SearchColumns.SEARCH + "=?", selectionArgs, null);
-        Assert.assertNotNull(cursor);
-        Assert.assertEquals(0, cursor.getCount());
+        try {
+            Assert.assertEquals(0, cursor.getCount());
+        } finally {
+            cursor.close();
+        }
         String[] selectionArgs1 = { searchTerm[1] };
         cursor = mProviderTestRule.getContentResolver().query(
                 mSearchesUri, null, SearchColumns.SEARCH + "=?", selectionArgs1, null);
-        Assert.assertNotNull(cursor);
-        Assert.assertEquals(1, cursor.getCount());
-        Assert.assertTrue(cursor.moveToNext());
-        int index = cursor.getColumnIndex(SearchColumns.SEARCH);
-        Assert.assertTrue(-1 != index);
-        Assert.assertEquals(searchTerm[1], cursor.getString(index));
-        index = cursor.getColumnIndex(SearchColumns.DATE);
-        Assert.assertTrue(-1 != index);
-        Assert.assertEquals(searchTime[1], cursor.getLong(index));
+        try {
+            Assert.assertEquals(1, cursor.getCount());
+            Assert.assertTrue(cursor.moveToNext());
+            int index = cursor.getColumnIndex(SearchColumns.SEARCH);
+            Assert.assertTrue(-1 != index);
+            Assert.assertEquals(searchTerm[1], cursor.getString(index));
+            index = cursor.getColumnIndex(SearchColumns.DATE);
+            Assert.assertTrue(-1 != index);
+            Assert.assertEquals(searchTime[1], cursor.getLong(index));
+        } finally {
+            cursor.close();
+        }
     }
 
     @Test
@@ -122,24 +131,34 @@
         String[] selectionArgs = { searchTerm[0] };
         Cursor cursor = mProviderTestRule.getContentResolver().query(
                 mSearchesUri, null, SearchColumns.SEARCH + "=?", selectionArgs, null);
-        Assert.assertNotNull(cursor);
-        Assert.assertEquals(0, cursor.getCount());
-        String[] selectionArgs1 = { searchTerm[1] };
+        try {
+            Assert.assertEquals(0, cursor.getCount());
+        } finally {
+            cursor.close();
+        }
+        String[] selectionArgs1 = {searchTerm[1]};
         cursor = mProviderTestRule.getContentResolver().query(
                 mSearchesUri, null, SearchColumns.SEARCH + "=?", selectionArgs1, null);
-        Assert.assertNotNull(cursor);
-        Assert.assertEquals(1, cursor.getCount());
-        Assert.assertTrue(cursor.moveToNext());
-        int index = cursor.getColumnIndex(SearchColumns.SEARCH);
-        Assert.assertTrue(-1 != index);
-        Assert.assertEquals(searchTerm[1], cursor.getString(index));
-        index = cursor.getColumnIndex(SearchColumns.DATE);
-        Assert.assertTrue(-1 != index);
-        Assert.assertEquals(searchTime[1], cursor.getLong(index));
-        mProviderTestRule.getContentResolver().delete(uri[1], null, null);
+        try {
+            Assert.assertNotNull(cursor);
+            Assert.assertEquals(1, cursor.getCount());
+            Assert.assertTrue(cursor.moveToNext());
+            int index = cursor.getColumnIndex(SearchColumns.SEARCH);
+            Assert.assertTrue(-1 != index);
+            Assert.assertEquals(searchTerm[1], cursor.getString(index));
+            index = cursor.getColumnIndex(SearchColumns.DATE);
+            Assert.assertTrue(-1 != index);
+            Assert.assertEquals(searchTime[1], cursor.getLong(index));
+            mProviderTestRule.getContentResolver().delete(uri[1], null, null);
+        } finally {
+            cursor.close();
+        }
         cursor = mProviderTestRule.getContentResolver().query(uri[1], null, null, null, null);
-        Assert.assertNotNull(cursor);
-        Assert.assertEquals(0, cursor.getCount());
+        try {
+            Assert.assertEquals(0, cursor.getCount());
+        } finally {
+            cursor.close();
+        }
     }
 
     // Copied from CTS test with minor adaptations.
@@ -162,13 +181,17 @@
         Cursor cursor = mProviderTestRule.getContentResolver().query(mSearchesUri,
                 ChromeBrowserProvider.SEARCHES_PROJECTION, SearchColumns.SEARCH + " = ?",
                 new String[] {insertSearch}, null);
-        Assert.assertTrue(cursor.moveToNext());
-        Assert.assertEquals(insertSearch,
-                cursor.getString(ChromeBrowserProvider.SEARCHES_PROJECTION_SEARCH_INDEX));
-        Assert.assertEquals(
-                createDate, cursor.getLong(ChromeBrowserProvider.SEARCHES_PROJECTION_DATE_INDEX));
-        int id = cursor.getInt(idIndex);
-        cursor.close();
+        int id;
+        try {
+            Assert.assertTrue(cursor.moveToNext());
+            Assert.assertEquals(insertSearch,
+                    cursor.getString(ChromeBrowserProvider.SEARCHES_PROJECTION_SEARCH_INDEX));
+            Assert.assertEquals(createDate,
+                    cursor.getLong(ChromeBrowserProvider.SEARCHES_PROJECTION_DATE_INDEX));
+            id = cursor.getInt(idIndex);
+        } finally {
+            cursor.close();
+        }
 
         // Test: update
         value.clear();
@@ -181,18 +204,25 @@
         cursor = mProviderTestRule.getContentResolver().query(mSearchesUri,
                 ChromeBrowserProvider.SEARCHES_PROJECTION, SearchColumns.ID + " = " + id, null,
                 null);
-        Assert.assertTrue(cursor.moveToNext());
-        Assert.assertEquals(updateSearch,
-                cursor.getString(ChromeBrowserProvider.SEARCHES_PROJECTION_SEARCH_INDEX));
-        Assert.assertEquals(
-                updateDate, cursor.getLong(ChromeBrowserProvider.SEARCHES_PROJECTION_DATE_INDEX));
-        Assert.assertEquals(id, cursor.getInt(idIndex));
-
+        try {
+            Assert.assertTrue(cursor.moveToNext());
+            Assert.assertEquals(updateSearch,
+                    cursor.getString(ChromeBrowserProvider.SEARCHES_PROJECTION_SEARCH_INDEX));
+            Assert.assertEquals(updateDate,
+                    cursor.getLong(ChromeBrowserProvider.SEARCHES_PROJECTION_DATE_INDEX));
+            Assert.assertEquals(id, cursor.getInt(idIndex));
+        } finally {
+            cursor.close();
+        }
         // Test: delete
         mProviderTestRule.getContentResolver().delete(insertUri, null, null);
         cursor = mProviderTestRule.getContentResolver().query(mSearchesUri,
                 ChromeBrowserProvider.SEARCHES_PROJECTION, SearchColumns.ID + " = " + id, null,
                 null);
-        Assert.assertEquals(0, cursor.getCount());
+        try {
+            Assert.assertEquals(0, cursor.getCount());
+        } finally {
+            cursor.close();
+        }
     }
 }
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 6180851..29f2d2a 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -2737,6 +2737,10 @@
      flag_descriptions::kArcFilePickerExperimentName,
      flag_descriptions::kArcFilePickerExperimentDescription, kOsCrOS,
      FEATURE_VALUE_TYPE(arc::kFilePickerExperimentFeature)},
+    {"arc-graphics-buffer-visualization-tool",
+     flag_descriptions::kArcGraphicBuffersVisualizationToolName,
+     flag_descriptions::kArcGraphicBuffersVisualizationToolDescription, kOsCrOS,
+     FEATURE_VALUE_TYPE(arc::kGraphicBuffersVisualizationTool)},
     {"arc-native-bridge-experiment",
      flag_descriptions::kArcNativeBridgeExperimentName,
      flag_descriptions::kArcNativeBridgeExperimentDescription, kOsCrOS,
@@ -3574,6 +3578,11 @@
      flag_descriptions::kAutofillProfileClientValidationDescription, kOsAll,
      FEATURE_VALUE_TYPE(autofill::features::kAutofillProfileClientValidation)},
 
+    {"autofill-profile-server-validation",
+     flag_descriptions::kAutofillProfileServerValidationName,
+     flag_descriptions::kAutofillProfileServerValidationDescription, kOsAll,
+     FEATURE_VALUE_TYPE(autofill::features::kAutofillProfileServerValidation)},
+
     {"autofill-restrict-formless-form-extraction",
      flag_descriptions::kAutofillRestrictUnownedFieldsToFormlessCheckoutName,
      flag_descriptions::
diff --git a/chrome/browser/browser_resources.grd b/chrome/browser/browser_resources.grd
index cfad0e3..8213593 100644
--- a/chrome/browser/browser_resources.grd
+++ b/chrome/browser/browser_resources.grd
@@ -723,6 +723,12 @@
         <include name="IDR_SMART_DIM_EXAMPLE_PREPROCESSOR_CONFIG_PB" file="chromeos\power\ml\smart_dim\example_preprocessor_config.pb" type="BINDATA" />
         <include name="IDR_SMART_DIM_LITE_EXAMPLE_PREPROCESSOR_CONFIG_PB" file="chromeos\power\ml\smart_dim\lite_example_preprocessor_config.pb" type="BINDATA" />
       </if>
+      <if expr="chromeos">
+        <include name="IDR_ARC_GRAPHICS_TRACING_HTML" file="resources\chromeos\arc_graphics_tracing\arc_graphics_tracing.html" compress="gzip" type="BINDATA"/>
+        <include name="IDR_ARC_GRAPHICS_TRACING_JS" file="resources\chromeos\arc_graphics_tracing\arc_graphics_tracing.js" compress="gzip" type="BINDATA" />
+        <include name="IDR_ARC_GRAPHICS_TRACING_UI_JS" file="resources\chromeos\arc_graphics_tracing\arc_graphics_tracing_ui.js" compress="gzip" type="BINDATA" />
+        <include name="IDR_ARC_GRAPHICS_TRACING_CSS" file="resources\chromeos\arc_graphics_tracing\arc_graphics_tracing.css" compress="gzip" type="BINDATA" />
+      </if>
     </includes>
   </release>
 </grit>
diff --git a/chrome/browser/chromeos/accessibility/spoken_feedback_browsertest.cc b/chrome/browser/chromeos/accessibility/spoken_feedback_browsertest.cc
index 02fb519..4832929 100644
--- a/chrome/browser/chromeos/accessibility/spoken_feedback_browsertest.cc
+++ b/chrome/browser/chromeos/accessibility/spoken_feedback_browsertest.cc
@@ -247,8 +247,8 @@
   EXPECT_EQ("Not pressed", speech_monitor_.GetNextUtterance());
 }
 
-#if !defined(NDEBUG)
-// Flaky in debug: http://crbug.com/923090
+#if !defined(NDEBUG) || defined(ADDRESS_SANITIZER)
+// Flaky in debug & asan: http://crbug.com/923090
 #define MAYBE_KeyboardShortcutViewer DISABLED_KeyboardShortcutViewer
 #else
 #define MAYBE_KeyboardShortcutViewer KeyboardShortcutViewer
diff --git a/chrome/browser/chromeos/account_manager/account_manager_migrator.cc b/chrome/browser/chromeos/account_manager/account_manager_migrator.cc
index f37dae1..51b8d17a 100644
--- a/chrome/browser/chromeos/account_manager/account_manager_migrator.cc
+++ b/chrome/browser/chromeos/account_manager/account_manager_migrator.cc
@@ -28,7 +28,6 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/signin/account_reconcilor_factory.h"
 #include "chrome/browser/signin/account_tracker_service_factory.h"
-#include "chrome/browser/signin/gaia_cookie_manager_service_factory.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
 #include "chrome/browser/web_data_service_factory.h"
 #include "chrome/common/pref_names.h"
@@ -40,9 +39,9 @@
 #include "components/prefs/pref_service.h"
 #include "components/signin/core/browser/account_reconcilor.h"
 #include "components/signin/core/browser/account_tracker_service.h"
-#include "components/signin/core/browser/gaia_cookie_manager_service.h"
 #include "components/signin/core/browser/webdata/token_web_data.h"
 #include "components/webdata/common/web_data_service_consumer.h"
+#include "services/identity/public/cpp/accounts_in_cookie_jar_info.h"
 #include "services/identity/public/cpp/identity_manager.h"
 
 namespace chromeos {
@@ -273,39 +272,34 @@
 // to |AccountManager|. The objective is to migrate the account names only. We
 // cannot migrate any credentials (cookies).
 class ContentAreaAccountsMigration : public AccountMigrationBaseStep,
-                                     GaiaCookieManagerService::Observer {
+                                     identity::IdentityManager::Observer {
  public:
-  ContentAreaAccountsMigration(
-      AccountManager* account_manager,
-      identity::IdentityManager* identity_manager,
-      GaiaCookieManagerService* gaia_cookie_manager_service)
+  ContentAreaAccountsMigration(AccountManager* account_manager,
+                               identity::IdentityManager* identity_manager)
       : AccountMigrationBaseStep(kContentAreaAccountsMigration,
                                  account_manager,
                                  identity_manager),
-        gaia_cookie_manager_service_(gaia_cookie_manager_service) {}
+        identity_manager_(identity_manager) {}
   ~ContentAreaAccountsMigration() override {
-    gaia_cookie_manager_service_->RemoveObserver(this);
+    identity_manager_->RemoveObserver(this);
   }
 
  private:
   void StartMigration() override {
     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-    std::vector<gaia::ListedAccount> signed_in_content_area_accounts;
-    std::vector<gaia::ListedAccount> signed_out_content_area_accounts;
-    gaia_cookie_manager_service_->AddObserver(this);
-    if (gaia_cookie_manager_service_->ListAccounts(
-            &signed_in_content_area_accounts,
-            &signed_out_content_area_accounts)) {
-      OnGaiaAccountsInCookieUpdated(
-          signed_in_content_area_accounts, signed_out_content_area_accounts,
+    identity_manager_->AddObserver(this);
+    identity::AccountsInCookieJarInfo accounts_in_cookie_jar_info =
+        identity_manager_->GetAccountsInCookieJar();
+    if (accounts_in_cookie_jar_info.accounts_are_fresh) {
+      OnAccountsInCookieUpdated(
+          accounts_in_cookie_jar_info,
           GoogleServiceAuthError(GoogleServiceAuthError::NONE));
     }
   }
 
-  void OnGaiaAccountsInCookieUpdated(
-      const std::vector<gaia::ListedAccount>& signed_in_content_area_accounts,
-      const std::vector<gaia::ListedAccount>& signed_out_content_area_accounts,
+  void OnAccountsInCookieUpdated(
+      const identity::AccountsInCookieJarInfo& accounts_in_cookie_jar_info,
       const GoogleServiceAuthError& error) override {
     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
     // We should not have reached here without |OnGetAccounts| having been
@@ -313,10 +307,10 @@
     // Furthermore, Account Manager must have been populated with the Device
     // Account before this |Step| is run.
     DCHECK(!IsAccountManagerEmpty());
-    gaia_cookie_manager_service_->RemoveObserver(this);
+    identity_manager_->RemoveObserver(this);
 
-    MigrateAccounts(signed_in_content_area_accounts,
-                    signed_out_content_area_accounts);
+    MigrateAccounts(accounts_in_cookie_jar_info.signed_in_accounts,
+                    accounts_in_cookie_jar_info.signed_out_accounts);
 
     FinishWithSuccess();
   }
@@ -334,8 +328,8 @@
     }
   }
 
-  // A non-owning pointer to |GaiaCookieManagerService|.
-  GaiaCookieManagerService* const gaia_cookie_manager_service_;
+  // A non-owning pointer to |IdentityManager|.
+  identity::IdentityManager* const identity_manager_;
 
   SEQUENCE_CHECKER(sequence_checker_);
 
@@ -524,9 +518,7 @@
       WebDataServiceFactory::GetTokenWebDataForProfile(
           profile_, ServiceAccessType::EXPLICIT_ACCESS) /* token_web_data */));
   migration_runner_.AddStep(std::make_unique<ContentAreaAccountsMigration>(
-      account_manager, identity_manager,
-      GaiaCookieManagerServiceFactory::GetForProfile(
-          profile_) /* gaia_cookie_manager_service */));
+      account_manager, identity_manager));
 
   if (arc::IsArcProvisioned(profile_)) {
     // Add a migration step for ARC only if ARC has been provisioned. If ARC has
@@ -610,7 +602,7 @@
   // be re-enabled once migration is done.
   DependsOn(AccountReconcilorFactory::GetInstance());
   // For getting Chrome content area accounts.
-  DependsOn(GaiaCookieManagerServiceFactory::GetInstance());
+  DependsOn(IdentityManagerFactory::GetInstance());
 }
 
 AccountManagerMigratorFactory::~AccountManagerMigratorFactory() = default;
diff --git a/chrome/browser/chromeos/arc/arc_session_manager_unittest.cc b/chrome/browser/chromeos/arc/arc_session_manager_unittest.cc
index af5e774..795042a9 100644
--- a/chrome/browser/chromeos/arc/arc_session_manager_unittest.cc
+++ b/chrome/browser/chromeos/arc/arc_session_manager_unittest.cc
@@ -1075,7 +1075,7 @@
   arc_session_manager()->Shutdown();
 }
 
-INSTANTIATE_TEST_CASE_P(
+INSTANTIATE_TEST_SUITE_P(
     ,
     ArcSessionManagerPolicyTest,
     // testing::Values is incompatible with move-only types, hence ints are used
@@ -1269,9 +1269,9 @@
   DISALLOW_COPY_AND_ASSIGN(ArcSessionOobeOptInNegotiatorTest);
 };
 
-INSTANTIATE_TEST_CASE_P(,
-                        ArcSessionOobeOptInNegotiatorTest,
-                        ::testing::Values(true, false));
+INSTANTIATE_TEST_SUITE_P(,
+                         ArcSessionOobeOptInNegotiatorTest,
+                         ::testing::Values(true, false));
 
 TEST_P(ArcSessionOobeOptInNegotiatorTest, OobeTermsAccepted) {
   view()->Show();
@@ -1429,9 +1429,9 @@
   DISALLOW_COPY_AND_ASSIGN(ArcSessionRetryTest);
 };
 
-INSTANTIATE_TEST_CASE_P(,
-                        ArcSessionRetryTest,
-                        ::testing::ValuesIn(kRetryTestCases));
+INSTANTIATE_TEST_SUITE_P(,
+                         ArcSessionRetryTest,
+                         ::testing::ValuesIn(kRetryTestCases));
 
 // Verifies that Android container behaves as expected.* This checks:
 //   * Whether ARC++ container alive or not on error.
diff --git a/chrome/browser/chromeos/arc/arc_util_unittest.cc b/chrome/browser/chromeos/arc/arc_util_unittest.cc
index 426c5ad7..3be5cae 100644
--- a/chrome/browser/chromeos/arc/arc_util_unittest.cc
+++ b/chrome/browser/chromeos/arc/arc_util_unittest.cc
@@ -966,7 +966,7 @@
             IsArcMigrationAllowedByPolicyForProfile(profile()));
 }
 
-INSTANTIATE_TEST_CASE_P(
+INSTANTIATE_TEST_SUITE_P(
     ArcMigrationTest,
     ArcMigrationAskForEcryptfsArcUsersTest,
     ::testing::Values(
diff --git a/chrome/browser/chromeos/crostini/crostini_manager.cc b/chrome/browser/chromeos/crostini/crostini_manager.cc
index b7c4e0c..a3a2b580 100644
--- a/chrome/browser/chromeos/crostini/crostini_manager.cc
+++ b/chrome/browser/chromeos/crostini/crostini_manager.cc
@@ -1935,6 +1935,8 @@
       break;
     case vm_tools::cicerone::LxdContainerCreatedSignal::CREATED:
       result = CrostiniResult::SUCCESS;
+      AddNewLxdContainerToPrefs(profile_, signal.vm_name(),
+                                signal.container_name());
       break;
     case vm_tools::cicerone::LxdContainerCreatedSignal::DOWNLOAD_TIMED_OUT:
       result = CrostiniResult::CONTAINER_DOWNLOAD_TIMED_OUT;
diff --git a/chrome/browser/chromeos/crostini/crostini_pref_names.cc b/chrome/browser/chromeos/crostini/crostini_pref_names.cc
index e66ffee9..5289b69 100644
--- a/chrome/browser/chromeos/crostini/crostini_pref_names.cc
+++ b/chrome/browser/chromeos/crostini/crostini_pref_names.cc
@@ -4,6 +4,11 @@
 
 #include "chrome/browser/chromeos/crostini/crostini_pref_names.h"
 
+#include <memory>
+#include <utility>
+
+#include "base/values.h"
+#include "chrome/browser/chromeos/crostini/crostini_util.h"
 #include "components/prefs/pref_registry_simple.h"
 
 namespace crostini {
@@ -19,6 +24,7 @@
 // List of USB devices with their system guid, a name/description and their
 // enabled state for use with Crostini.
 const char kCrostiniSharedUsbDevices[] = "crostini.shared_usb_devices";
+const char kCrostiniContainers[] = "crostini.containers";
 
 // A boolean preference representing a user level enterprise policy to enable
 // Crostini use.
@@ -41,6 +47,22 @@
   registry->RegisterDictionaryPref(kCrostiniRegistry);
   registry->RegisterListPref(kCrostiniSharedPaths);
   registry->RegisterListPref(kCrostiniSharedUsbDevices);
+
+  // Set a default value for crostini.containers to ensure that we track the
+  // default container even if its creation predates this preference. This
+  // preference should not be accessed unless crostini is installed
+  // (i.e. kCrostiniEnabled is true).
+  base::Value default_container(base::Value::Type::DICTIONARY);
+  default_container.SetKey("vm_name", base::Value(kCrostiniDefaultVmName));
+  default_container.SetKey("container_name",
+                           base::Value(kCrostiniDefaultContainerName));
+
+  auto default_containers_list =
+      std::make_unique<base::Value>(base::Value::Type::LIST);
+  default_containers_list->GetList().push_back(std::move(default_container));
+  registry->RegisterListPref(kCrostiniContainers,
+                             std::move(default_containers_list));
+
   registry->RegisterBooleanPref(crostini::prefs::kReportCrostiniUsageEnabled,
                                 false);
   registry->RegisterStringPref(kCrostiniLastLaunchVersion, std::string());
diff --git a/chrome/browser/chromeos/crostini/crostini_pref_names.h b/chrome/browser/chromeos/crostini/crostini_pref_names.h
index 4443f72..eba65bbb 100644
--- a/chrome/browser/chromeos/crostini/crostini_pref_names.h
+++ b/chrome/browser/chromeos/crostini/crostini_pref_names.h
@@ -15,6 +15,7 @@
 extern const char kCrostiniRegistry[];
 extern const char kCrostiniSharedPaths[];
 extern const char kCrostiniSharedUsbDevices[];
+extern const char kCrostiniContainers[];
 
 extern const char kUserCrostiniAllowedByPolicy[];
 
diff --git a/chrome/browser/chromeos/crostini/crostini_remover.cc b/chrome/browser/chromeos/crostini/crostini_remover.cc
index d4e96ad1..8f41942 100644
--- a/chrome/browser/chromeos/crostini/crostini_remover.cc
+++ b/chrome/browser/chromeos/crostini/crostini_remover.cc
@@ -101,6 +101,8 @@
       CrostiniManager::GetForProfile(profile_)->UninstallTerminaComponent()) {
     profile_->GetPrefs()->SetBoolean(prefs::kCrostiniEnabled, false);
     profile_->GetPrefs()->ClearPref(prefs::kCrostiniLastDiskSize);
+    profile_->GetPrefs()->Set(prefs::kCrostiniContainers,
+                              base::Value(base::Value::Type::LIST));
   }
   std::move(callback_).Run(CrostiniResult::SUCCESS);
 }
diff --git a/chrome/browser/chromeos/crostini/crostini_util.cc b/chrome/browser/chromeos/crostini/crostini_util.cc
index ce6dbf6d..ba442527 100644
--- a/chrome/browser/chromeos/crostini/crostini_util.cc
+++ b/chrome/browser/chromeos/crostini/crostini_util.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/browser/chromeos/crostini/crostini_util.h"
 
+#include <utility>
+
 #include "base/bind.h"
 #include "base/callback.h"
 #include "base/feature_list.h"
@@ -408,4 +410,17 @@
   return true;
 }
 
+void AddNewLxdContainerToPrefs(Profile* profile,
+                               std::string vm_name,
+                               std::string container_name) {
+  auto* pref_service = profile->GetPrefs();
+
+  base::Value new_container(base::Value::Type::DICTIONARY);
+  new_container.SetKey("vm_name", base::Value(vm_name));
+  new_container.SetKey("container_name", base::Value(container_name));
+
+  ListPrefUpdate updater(pref_service, crostini::prefs::kCrostiniContainers);
+  updater->GetList().emplace_back(std::move(new_container));
+}
+
 }  // namespace crostini
diff --git a/chrome/browser/chromeos/crostini/crostini_util.h b/chrome/browser/chromeos/crostini/crostini_util.h
index d5e68599..0a00a10 100644
--- a/chrome/browser/chromeos/crostini/crostini_util.h
+++ b/chrome/browser/chromeos/crostini/crostini_util.h
@@ -139,6 +139,11 @@
 // policy.
 bool IsUnaffiliatedCrostiniAllowedByPolicy();
 
+// Add a newly created LXD container to the kCrostiniContainers pref
+void AddNewLxdContainerToPrefs(Profile* profile,
+                               std::string vm_name,
+                               std::string container_name);
+
 }  // namespace crostini
 
 #endif  // CHROME_BROWSER_CHROMEOS_CROSTINI_CROSTINI_UTIL_H_
diff --git a/chrome/browser/chromeos/login/users/chrome_user_manager_impl.cc b/chrome/browser/chromeos/login/users/chrome_user_manager_impl.cc
index 0ca250f0..e9fb591 100644
--- a/chrome/browser/chromeos/login/users/chrome_user_manager_impl.cc
+++ b/chrome/browser/chromeos/login/users/chrome_user_manager_impl.cc
@@ -9,7 +9,6 @@
 #include <cstddef>
 #include <set>
 #include <utility>
-#include <vector>
 
 #include "ash/public/cpp/ash_pref_names.h"
 #include "ash/public/interfaces/constants.mojom.h"
@@ -848,23 +847,9 @@
 }
 
 void ChromeUserManagerImpl::PerformPostUserListLoadingActions() {
-  std::vector<user_manager::User*> users_to_remove;
-
-  for (user_manager::User* user : users_) {
-    // TODO(http://crbug/866790): Remove supervised user accounts. After we have
-    // enough confidence that there are no more supervised users on devices in
-    // the wild, remove this.
-    if (base::FeatureList::IsEnabled(
-            features::kRemoveSupervisedUsersOnStartup) &&
-        user->IsSupervised()) {
-      users_to_remove.push_back(user);
-    } else {
-      GetUserImageManager(user->GetAccountId())->LoadUserImage();
-    }
-  }
-
-  for (user_manager::User* user : users_to_remove) {
-    RemoveUser(user->GetAccountId(), nullptr);
+  for (user_manager::UserList::iterator ui = users_.begin(), ue = users_.end();
+       ui != ue; ++ui) {
+    GetUserImageManager((*ui)->GetAccountId())->LoadUserImage();
   }
 }
 
diff --git a/chrome/browser/chromeos/login/users/remove_supervised_users_browsertest.cc b/chrome/browser/chromeos/login/users/remove_supervised_users_browsertest.cc
deleted file mode 100644
index b15f6f7..0000000
--- a/chrome/browser/chromeos/login/users/remove_supervised_users_browsertest.cc
+++ /dev/null
@@ -1,122 +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.
-
-#include <memory>
-#include <vector>
-
-#include "base/macros.h"
-#include "base/test/scoped_feature_list.h"
-#include "chrome/browser/chromeos/login/login_manager_test.h"
-#include "chrome/browser/chromeos/login/users/chrome_user_manager.h"
-#include "chrome/browser/chromeos/login/users/chrome_user_manager_impl.h"
-#include "chrome/browser/chromeos/settings/cros_settings.h"
-#include "chrome/browser/chromeos/settings/scoped_testing_cros_settings.h"
-#include "chrome/common/chrome_features.h"
-#include "components/account_id/account_id.h"
-#include "components/user_manager/scoped_user_manager.h"
-#include "components/user_manager/user_manager.h"
-#include "components/user_manager/user_manager_base.h"
-
-namespace chromeos {
-
-namespace {
-
-struct {
-  const char* email;
-  const char* gaia_id;
-} const kTestUsers[] = {
-    {"test-user1@gmail.com", "1111111111"},
-    {"test-user2@gmail.com", "2222222222"},
-    // Test Supervised User.
-    // The domain is defined in user_manager::kSupervisedUserDomain.
-    // That const isn't directly referenced here to keep this code readable by
-    // avoiding std::string concatenations.
-    {"test-superviseduser@locally-managed.localhost", "3333333333"},
-};
-
-}  // namespace
-
-class RemoveSupervisedUsersBrowserTest : public LoginManagerTest {
- public:
-  RemoveSupervisedUsersBrowserTest() : LoginManagerTest(false, false) {}
-
-  ~RemoveSupervisedUsersBrowserTest() override = default;
-
-  void SetUpOnMainThread() override {
-    LoginManagerTest::SetUpOnMainThread();
-    InitializeTestUsers();
-  }
-
-  void TearDownOnMainThread() override {
-    LoginManagerTest::TearDownOnMainThread();
-    scoped_user_manager_.reset();
-  }
-
- protected:
-  std::vector<AccountId> test_users_;
-
- private:
-  void InitializeTestUsers() {
-    for (size_t i = 0; i < base::size(kTestUsers); ++i) {
-      test_users_.emplace_back(AccountId::FromUserEmailGaiaId(
-          kTestUsers[i].email, kTestUsers[i].gaia_id));
-      RegisterUser(test_users_[i]);
-    }
-
-    // Setup a scoped real ChromeUserManager, since this is an end-to-end
-    // test.  This will read the users registered to the local state just
-    // before this in RegisterUser.
-    scoped_user_manager_ = std::make_unique<user_manager::ScopedUserManager>(
-        chromeos::ChromeUserManagerImpl::CreateChromeUserManager());
-  }
-
-  std::unique_ptr<user_manager::ScopedUserManager> scoped_user_manager_;
-  ScopedTestingCrosSettings scoped_testing_cros_settings_;
-
-  DISALLOW_COPY_AND_ASSIGN(RemoveSupervisedUsersBrowserTest);
-};
-
-class RemoveSupervisedUsersBrowserEnabledTest
-    : public RemoveSupervisedUsersBrowserTest {
- protected:
-  void SetUpInProcessBrowserTestFixture() override {
-    scoped_feature_list_.InitAndEnableFeature(
-        features::kRemoveSupervisedUsersOnStartup);
-  }
-
- private:
-  base::test::ScopedFeatureList scoped_feature_list_;
-};
-
-class RemoveSupervisedUsersBrowserDisabledTest
-    : public RemoveSupervisedUsersBrowserTest {
- protected:
-  void SetUpInProcessBrowserTestFixture() override {
-    scoped_feature_list_.InitAndDisableFeature(
-        features::kRemoveSupervisedUsersOnStartup);
-  }
-
- private:
-  base::test::ScopedFeatureList scoped_feature_list_;
-};
-
-IN_PROC_BROWSER_TEST_F(RemoveSupervisedUsersBrowserEnabledTest,
-                       SupervisedUserRemoved) {
-  // When Supervised users are removed, only 2 test users should be returned.
-  EXPECT_EQ(2U, user_manager::UserManager::Get()->GetUsers().size());
-
-  // None of the non-supervised users should have been removed.
-  for (user_manager::User* user :
-       user_manager::UserManager::Get()->GetUsers()) {
-    EXPECT_EQ(false, user->IsSupervised());
-  }
-}
-
-IN_PROC_BROWSER_TEST_F(RemoveSupervisedUsersBrowserDisabledTest,
-                       SupervisedUserNotRemoved) {
-  // When Supervised users are not removed, all 3 test users should be returned.
-  EXPECT_EQ(3U, user_manager::UserManager::Get()->GetUsers().size());
-}
-
-}  // namespace chromeos
diff --git a/chrome/browser/chromeos/oauth2_token_service_delegate.cc b/chrome/browser/chromeos/oauth2_token_service_delegate.cc
index 467ff7b..dc33522 100644
--- a/chrome/browser/chromeos/oauth2_token_service_delegate.cc
+++ b/chrome/browser/chromeos/oauth2_token_service_delegate.cc
@@ -146,7 +146,7 @@
   }
 
   auto it = errors_.find(account_id);
-  if ((it != errors_.end())) {
+  if (it != errors_.end()) {
     // Update the existing error.
     if (error.state() == GoogleServiceAuthError::NONE)
       errors_.erase(it);
@@ -268,13 +268,24 @@
   // invoked here, regardless. See the comment above |FireAuthErrorChanged| few
   // lines down.
   errors_.erase(account_id);
+  GoogleServiceAuthError error(GoogleServiceAuthError::AuthErrorNone());
+
+  // However, if we know that |account_key| has a dummy token, store a
+  // persistent error against it, so that we can pre-emptively reject access
+  // token requests for it.
+  if (account_manager_->HasDummyGaiaToken(account_key)) {
+    error = GoogleServiceAuthError::FromInvalidGaiaCredentialsReason(
+        GoogleServiceAuthError::InvalidGaiaCredentialsReason::
+            CREDENTIALS_REJECTED_BY_CLIENT);
+    errors_.emplace(account_id, AccountErrorStatus{error});
+  }
 
   ScopedBatchChange batch(this);
   FireRefreshTokenAvailable(account_id);
   // See |OAuth2TokenService::Observer::OnAuthErrorChanged|.
   // |OnAuthErrorChanged| must be always called after
   // |OnRefreshTokenAvailable|, when refresh token is updated.
-  FireAuthErrorChanged(account_id, GoogleServiceAuthError::AuthErrorNone());
+  FireAuthErrorChanged(account_id, error);
 }
 
 void ChromeOSOAuth2TokenServiceDelegate::OnAccountRemoved(
diff --git a/chrome/browser/chromeos/oauth2_token_service_delegate_unittest.cc b/chrome/browser/chromeos/oauth2_token_service_delegate_unittest.cc
index 2fd9d9d..9edb49b 100644
--- a/chrome/browser/chromeos/oauth2_token_service_delegate_unittest.cc
+++ b/chrome/browser/chromeos/oauth2_token_service_delegate_unittest.cc
@@ -84,9 +84,16 @@
 
   void OnRefreshTokenAvailable(const std::string& account_id) override {
     EXPECT_TRUE(is_inside_batch_);
-    // We should not be seeing any cached errors for a freshly updated account.
-    EXPECT_EQ(GoogleServiceAuthError::AuthErrorNone(),
-              delegate_->GetAuthError(account_id));
+    // We should not be seeing any cached errors for a freshly updated account,
+    // except when they have been generated by us (i.e.
+    // CREDENTIALS_REJECTED_BY_CLIENT).
+    const GoogleServiceAuthError error = delegate_->GetAuthError(account_id);
+    EXPECT_TRUE((error == GoogleServiceAuthError::AuthErrorNone()) ||
+                (error.state() ==
+                     GoogleServiceAuthError::State::INVALID_GAIA_CREDENTIALS &&
+                 error.GetInvalidGaiaCredentialsReason() ==
+                     GoogleServiceAuthError::InvalidGaiaCredentialsReason::
+                         CREDENTIALS_REJECTED_BY_CLIENT));
 
     account_ids_.insert(account_id);
 
@@ -273,6 +280,24 @@
   delegate_->UpdateCredentials(account_info_.account_id, "new-token");
 }
 
+TEST_F(CrOSOAuthDelegateTest, DummyTokensArePreEmptivelyRejected) {
+  TokenServiceObserver observer(delegate_.get());
+  delegate_->UpdateCredentials(account_info_.account_id,
+                               AccountManager::kInvalidToken);
+
+  const GoogleServiceAuthError error =
+      delegate_->GetAuthError(account_info_.account_id);
+  EXPECT_EQ(GoogleServiceAuthError::State::INVALID_GAIA_CREDENTIALS,
+            error.state());
+  EXPECT_EQ(GoogleServiceAuthError::InvalidGaiaCredentialsReason::
+                CREDENTIALS_REJECTED_BY_CLIENT,
+            error.GetInvalidGaiaCredentialsReason());
+
+  // Observer notification should also have notified about the same error.
+  EXPECT_EQ(error, observer.last_err_);
+  EXPECT_EQ(account_info_.account_id, observer.last_err_account_id_);
+}
+
 TEST_F(CrOSOAuthDelegateTest, ObserversAreNotifiedOnCredentialsUpdate) {
   TokenServiceObserver observer(delegate_.get());
   delegate_->UpdateCredentials(account_info_.account_id, kGaiaToken);
diff --git a/chrome/browser/extensions/api/settings_private/prefs_util.cc b/chrome/browser/extensions/api/settings_private/prefs_util.cc
index 8b881046..3ce3534b 100644
--- a/chrome/browser/extensions/api/settings_private/prefs_util.cc
+++ b/chrome/browser/extensions/api/settings_private/prefs_util.cc
@@ -372,6 +372,8 @@
       settings_api::PrefType::PREF_TYPE_LIST;
   (*s_whitelist)[crostini::prefs::kCrostiniSharedUsbDevices] =
       settings_api::PrefType::PREF_TYPE_LIST;
+  (*s_whitelist)[crostini::prefs::kCrostiniContainers] =
+      settings_api::PrefType::PREF_TYPE_LIST;
 
   // Android Apps.
   (*s_whitelist)[arc::prefs::kArcEnabled] =
diff --git a/chrome/browser/extensions/api/tabs/tabs_api_unittest.cc b/chrome/browser/extensions/api/tabs/tabs_api_unittest.cc
index 70d1069b..c3efc0b 100644
--- a/chrome/browser/extensions/api/tabs/tabs_api_unittest.cc
+++ b/chrome/browser/extensions/api/tabs/tabs_api_unittest.cc
@@ -19,6 +19,7 @@
 #include "chrome/test/base/test_browser_window.h"
 #include "content/public/browser/navigation_entry.h"
 #include "content/public/test/browser_side_navigation_test_utils.h"
+#include "content/public/test/navigation_simulator.h"
 #include "content/public/test/web_contents_tester.h"
 #include "extensions/browser/api_test_utils.h"
 #include "extensions/common/constants.h"
@@ -59,12 +60,8 @@
       content::WebContentsTester::CreateTestWebContents(profile, nullptr);
 
   for (const auto& url : urls) {
-    web_contents->GetController().LoadURL(
-        url, content::Referrer(),
-        ui::PageTransitionFromInt(ui::PAGE_TRANSITION_LINK), std::string());
-
-    content::RenderFrameHostTester::CommitPendingLoad(
-        &web_contents->GetController());
+    content::NavigationSimulator::NavigateAndCommitFromBrowser(
+        web_contents.get(), url);
     EXPECT_EQ(url, web_contents->GetLastCommittedURL());
     EXPECT_EQ(url, web_contents->GetVisibleURL());
   }
diff --git a/chrome/browser/extensions/external_install_error.cc b/chrome/browser/extensions/external_install_error.cc
index b183576f..c10c8696 100644
--- a/chrome/browser/extensions/external_install_error.cc
+++ b/chrome/browser/extensions/external_install_error.cc
@@ -382,13 +382,11 @@
       break;
     case ExtensionInstallPrompt::Result::USER_CANCELED:
       if (extension) {
-        bool uninstallation_result = ExtensionSystem::Get(browser_context_)
+        ExtensionSystem::Get(browser_context_)
             ->extension_service()
             ->UninstallExtension(extension_id_,
                                  extensions::UNINSTALL_REASON_INSTALL_CANCELED,
                                  nullptr);  // Ignore error.
-        UMA_HISTOGRAM_BOOLEAN("Extensions.ExternalWarningUninstallationResult",
-                              uninstallation_result);
       }
       break;
     case ExtensionInstallPrompt::Result::ABORTED:
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index dc664bc..adfcacdf 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -137,6 +137,11 @@
     "expiry_milestone": 80
   },
   {
+    "name": "arc-graphics-buffer-visualization-tool",
+    "owners": [ "khmel" ],
+    "expiry_milestone": 76
+  },
+  {
     "name": "arc-native-bridge-experiment",
     "owners": [ "levarum@google.com" ],
     "expiry_milestone": 76
@@ -271,6 +276,11 @@
     "expiry_milestone": 77
   },
   {
+    "name": "autofill-profile-server-validation",
+    "owners": [ "parastoog" ],
+    "expiry_milestone": 77
+  },
+  {
     "name": "autofill-restrict-formless-form-extraction",
     "owners": [ "rogerm" ],
     "expiry_milestone": 76
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 03349c6..8852bfee 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -128,6 +128,11 @@
 const char kAutofillProfileClientValidationDescription[] =
     "Allows autofill to validate profiles on the client side";
 
+const char kAutofillProfileServerValidationName[] =
+    "Autofill Uses Server Validation";
+const char kAutofillProfileServerValidationDescription[] =
+    "Allows autofill to use server side validation";
+
 const char kAutofillPreviewStyleExperimentName[] =
     "Autofill Preview Style Experiment";
 const char kAutofillPreviewStyleExperimentDescription[] =
@@ -3041,6 +3046,12 @@
 const char kArcFilePickerExperimentDescription[] =
     "Enables using Chrome OS file picker in ARC.";
 
+const char kArcGraphicBuffersVisualizationToolName[] =
+    "Enable ARC graphic buffers visualization tool";
+const char kArcGraphicBuffersVisualizationToolDescription[] =
+    "Enable ARC graphic buffers visualization tool "
+    "(chrome://arc-graphics-tracing).";
+
 const char kArcNativeBridgeExperimentName[] =
     "Enable native bridge experiment for ARC";
 const char kArcNativeBridgeExperimentDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 22703094..023393a 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -117,6 +117,9 @@
 extern const char kAutofillProfileClientValidationName[];
 extern const char kAutofillProfileClientValidationDescription[];
 
+extern const char kAutofillProfileServerValidationName[];
+extern const char kAutofillProfileServerValidationDescription[];
+
 extern const char kAutofillPreviewStyleExperimentName[];
 extern const char kAutofillPreviewStyleExperimentDescription[];
 
@@ -1817,6 +1820,9 @@
 extern const char kArcFilePickerExperimentName[];
 extern const char kArcFilePickerExperimentDescription[];
 
+extern const char kArcGraphicBuffersVisualizationToolName[];
+extern const char kArcGraphicBuffersVisualizationToolDescription[];
+
 extern const char kArcNativeBridgeExperimentName[];
 extern const char kArcNativeBridgeExperimentDescription[];
 
diff --git a/chrome/browser/payments/android/can_make_payment_query_android.cc b/chrome/browser/payments/android/can_make_payment_query_android.cc
index 9d33ac6..8c1ed0ee 100644
--- a/chrome/browser/payments/android/can_make_payment_query_android.cc
+++ b/chrome/browser/payments/android/can_make_payment_query_android.cc
@@ -24,7 +24,8 @@
     const base::android::JavaParamRef<jobject>& jweb_contents,
     const base::android::JavaParamRef<jstring>& jtop_level_origin,
     const base::android::JavaParamRef<jstring>& jframe_origin,
-    const base::android::JavaParamRef<jobject>& jquery_map) {
+    const base::android::JavaParamRef<jobject>& jquery_map,
+    jboolean per_method_quota) {
   auto* web_contents = content::WebContents::FromJavaWebContents(jweb_contents);
   if (!web_contents)
     return false;
@@ -50,7 +51,7 @@
       ->CanQuery(
           GURL(base::android::ConvertJavaStringToUTF8(env, jtop_level_origin)),
           GURL(base::android::ConvertJavaStringToUTF8(env, jframe_origin)),
-          query);
+          query, per_method_quota);
 }
 
 }  // namespace payments
diff --git a/chrome/browser/policy/cloud/user_policy_signin_service_unittest.cc b/chrome/browser/policy/cloud/user_policy_signin_service_unittest.cc
index 1d49a41..f8feb38 100644
--- a/chrome/browser/policy/cloud/user_policy_signin_service_unittest.cc
+++ b/chrome/browser/policy/cloud/user_policy_signin_service_unittest.cc
@@ -20,7 +20,6 @@
 #include "chrome/browser/prefs/browser_prefs.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/signin/account_fetcher_service_factory.h"
-#include "chrome/browser/signin/account_tracker_service_factory.h"
 #include "chrome/browser/signin/chrome_signin_client_factory.h"
 #include "chrome/browser/signin/fake_account_fetcher_service_builder.h"
 #include "chrome/browser/signin/identity_test_environment_profile_adaptor.h"
@@ -37,7 +36,7 @@
 #include "components/policy/core/common/cloud/user_cloud_policy_manager.h"
 #include "components/policy/core/common/schema_registry.h"
 #include "components/prefs/pref_service.h"
-#include "components/signin/core/browser/account_tracker_service.h"
+#include "components/signin/core/browser/account_fetcher_service.h"
 #include "components/signin/core/browser/fake_account_fetcher_service.h"
 #include "components/sync_preferences/testing_pref_service_syncable.h"
 #include "content/public/browser/browser_context.h"
@@ -58,6 +57,7 @@
 
 #if defined(OS_ANDROID)
 #include "chrome/browser/policy/cloud/user_policy_signin_service_mobile.h"
+#include "components/signin/core/browser/child_account_info_fetcher_android.h"
 #else
 #include "chrome/browser/policy/cloud/user_policy_signin_service.h"
 #endif
@@ -72,7 +72,6 @@
 
 namespace {
 
-constexpr char kTestGaiaId[] = "gaia-id-testuser@test.com";
 constexpr char kTestUser[] = "testuser@test.com";
 
 #if !defined(OS_ANDROID)
@@ -106,8 +105,9 @@
   UserPolicySigninServiceTest()
       : mock_store_(NULL),
         thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP),
-        test_account_id_(
-            AccountId::FromUserEmailGaiaId(kTestUser, kTestGaiaId)),
+        test_account_id_(AccountId::FromUserEmailGaiaId(
+            kTestUser,
+            identity::GetTestGaiaIdForEmail(kTestUser))),
         register_completed_(false),
         test_system_shared_loader_factory_(
             base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
@@ -130,10 +130,10 @@
         base::Bind(&UserPolicySigninServiceTest::OnRegisterCompleted,
                    base::Unretained(this));
 #if defined(OS_ANDROID)
-    identity_test_env()->SetRefreshTokenForAccount(
-        AccountTrackerServiceFactory::GetForProfile(profile_.get())
-            ->SeedAccountInfo(kTestGaiaId, kTestUser));
-    service->RegisterForPolicyWithAccountId(kTestUser, kTestGaiaId, callback);
+    AccountInfo account_info =
+        identity_test_env()->MakeAccountAvailable(kTestUser);
+    service->RegisterForPolicyWithAccountId(kTestUser, account_info.gaia,
+                                            callback);
     ASSERT_TRUE(IsRequestActive());
 #else
     service->RegisterForPolicyWithLoginToken(kTestUser, "mock_oauth_token",
@@ -430,16 +430,13 @@
   ASSERT_FALSE(IsRequestActive());
 
   // Make oauth token available.
-  std::string account_id =
-      AccountTrackerServiceFactory::GetForProfile(profile_.get())
-          ->SeedAccountInfo(kTestGaiaId, kTestUser);
-  identity_test_env()->SetRefreshTokenForAccount(account_id);
+  identity_test_env()->MakeAccountAvailable(kTestUser);
 
   // Not signed in yet, so client registration should be deferred.
   ASSERT_FALSE(IsRequestActive());
 
   // Sign in to Chrome.
-  identity_test_env()->MakePrimaryAccountAvailable(kTestUser);
+  identity_test_env()->SetPrimaryAccount(kTestUser);
 
   // Complete initialization of the store.
   mock_store_->NotifyStoreLoaded();
@@ -783,9 +780,21 @@
 }
 
 TEST_F(UserPolicySigninServiceTest, SignOutThenSignInAgain) {
+#if defined(OS_ANDROID)
+  ChildAccountInfoFetcherAndroid::InitializeForTests();
+
+  // Explicitly forcing this call is necessary for the clearing of the primary
+  // account to result in the account being fully removed in this testing
+  // context.
+  AccountFetcherService* account_fetcher_service =
+      AccountFetcherServiceFactory::GetForProfile(profile_.get());
+  account_fetcher_service->EnableNetworkFetchesForTest();
+#endif
+
   ASSERT_NO_FATAL_FAILURE(TestSuccessfulSignin());
 
   EXPECT_CALL(*mock_store_, Clear());
+
   identity_test_env()->ClearPrimaryAccount();
   ASSERT_FALSE(manager_->core()->service());
 
diff --git a/chrome/browser/previews/previews_infobar_delegate_unittest.cc b/chrome/browser/previews/previews_infobar_delegate_unittest.cc
index 550d431..78e80d9 100644
--- a/chrome/browser/previews/previews_infobar_delegate_unittest.cc
+++ b/chrome/browser/previews/previews_infobar_delegate_unittest.cc
@@ -432,8 +432,7 @@
       PreviewsInfoBarDelegate::INFOBAR_LOAD_ORIGINAL_CLICKED, 1);
 
   std::unique_ptr<content::NavigationSimulator> simulator =
-      content::NavigationSimulator::CreateFromPendingBrowserInitiated(
-          web_contents());
+      content::NavigationSimulator::CreateFromPending(web_contents());
   simulator->Commit();
 
   EXPECT_EQ(content::ReloadType::ORIGINAL_REQUEST_URL,
@@ -566,8 +565,7 @@
   EXPECT_EQ(0U, infobar_service()->infobar_count());
 
   std::unique_ptr<content::NavigationSimulator> simulator =
-      content::NavigationSimulator::CreateFromPendingBrowserInitiated(
-          web_contents());
+      content::NavigationSimulator::CreateFromPending(web_contents());
   simulator->Commit();
 
   EXPECT_EQ(content::ReloadType::ORIGINAL_REQUEST_URL,
diff --git a/chrome/browser/printing/print_preview_dialog_controller_unittest.cc b/chrome/browser/printing/print_preview_dialog_controller_unittest.cc
index 0071f890..dba60bb 100644
--- a/chrome/browser/printing/print_preview_dialog_controller_unittest.cc
+++ b/chrome/browser/printing/print_preview_dialog_controller_unittest.cc
@@ -21,6 +21,7 @@
 #include "content/public/browser/web_contents_observer.h"
 #include "content/public/common/url_constants.h"
 #include "content/public/test/mock_render_process_host.h"
+#include "content/public/test/navigation_simulator.h"
 #include "content/public/test/web_contents_tester.h"
 
 using content::WebContents;
@@ -224,13 +225,10 @@
   WebContents* web_contents =
       browser()->tab_strip_model()->GetActiveWebContents();
   ASSERT_TRUE(web_contents);
-  content::NavigationController& nav_controller = web_contents->GetController();
 
   // Navigate to first page
-  nav_controller.LoadURL(tiger, content::Referrer(),
-                         ui::PageTransitionFromInt(ui::PAGE_TRANSITION_LINK),
-                         std::string());
-  CommitPendingLoad(&nav_controller);
+  content::NavigationSimulator::NavigateAndCommitFromBrowser(web_contents,
+                                                             tiger);
   EXPECT_EQ(tiger, web_contents->GetLastCommittedURL());
 
   // Get the preview dialog
@@ -249,10 +247,8 @@
   PrintPreviewDialogDestroyedObserver tiger_destroyed(tiger_preview_dialog);
 
   // Navigate via link to a similar page.
-  nav_controller.LoadURL(tiger_barb, content::Referrer(),
-                         ui::PageTransitionFromInt(ui::PAGE_TRANSITION_LINK),
-                         std::string());
-  CommitPendingLoad(&nav_controller);
+  content::NavigationSimulator::NavigateAndCommitFromBrowser(web_contents,
+                                                             tiger_barb);
 
   // Check navigation was successful
   EXPECT_EQ(tiger_barb, web_contents->GetLastCommittedURL());
@@ -276,8 +272,7 @@
   EXPECT_FALSE(manager->PrintPreviewNow(web_contents->GetMainFrame(), false));
 
   // Navigate with back button or ALT+LEFT ARROW to a similar page.
-  nav_controller.GoBack();
-  CommitPendingLoad(&nav_controller);
+  content::NavigationSimulator::GoBack(web_contents);
   EXPECT_EQ(tiger, web_contents->GetLastCommittedURL());
   EXPECT_TRUE(manager->PrintPreviewNow(web_contents->GetMainFrame(), false));
 
@@ -296,6 +291,7 @@
   // Try to simulate Gmail navigation: Navigate to an existing page (via
   // Forward) but modify the navigation type while pending to look like an
   // address bar + typed transition (like Gmail auto navigation)
+  content::NavigationController& nav_controller = web_contents->GetController();
   nav_controller.GoForward();
   nav_controller.GetPendingEntry()->SetTransitionType(ui::PageTransitionFromInt(
       ui::PAGE_TRANSITION_TYPED | ui::PAGE_TRANSITION_FROM_ADDRESS_BAR));
diff --git a/chrome/browser/resources/chromeos/arc_graphics_tracing/arc_graphics_tracing.css b/chrome/browser/resources/chromeos/arc_graphics_tracing/arc_graphics_tracing.css
new file mode 100644
index 0000000..222ca75
--- /dev/null
+++ b/chrome/browser/resources/chromeos/arc_graphics_tracing/arc_graphics_tracing.css
@@ -0,0 +1,59 @@
+/* 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. */
+
+#arc-event-bands {
+  margin: 0 auto;
+  overflow: auto;
+  white-space: nowrap;
+  width: 100%;
+}
+
+#arc-event-band-tooltip {
+  background-color: rgb(255, 255, 192);
+  border: 1px solid #a0a0a0;
+  display: none;
+  pointer-events: none;
+  position: absolute;
+  text-align: start;
+  width: 240px;
+}
+
+#arc-event-band-tooltip.active {
+  display: block;
+}
+
+.arc-events-inner-band {
+  display: block;
+  margin: 0 0 4px 0;
+}
+
+.arc-events-top-band {
+  display: block;
+  margin: 0 0 8px 0;
+}
+
+.arc-events-band-title {
+  border: none;
+  display: block;
+  left: 0;
+  outline: none;
+  position: sticky;
+  text-align: start;
+}
+
+.arc-events-band-title::before {
+  content: '\2212';
+  float: left;
+  font-weight: bold;
+  margin-inline-end: 4px;
+}
+
+.hidden.arc-events-band-title::before {
+  content: '\002B';
+}
+
+.hidden.arc-events-inner-band,
+.hidden.arc-events-top-band {
+  display: none;
+}
diff --git a/chrome/browser/resources/chromeos/arc_graphics_tracing/arc_graphics_tracing.html b/chrome/browser/resources/chromeos/arc_graphics_tracing/arc_graphics_tracing.html
new file mode 100644
index 0000000..1a82518
--- /dev/null
+++ b/chrome/browser/resources/chromeos/arc_graphics_tracing/arc_graphics_tracing.html
@@ -0,0 +1,35 @@
+<!-- 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. -->
+
+<!doctype html>
+<html>
+  <head>
+    <meta charset=utf-8>
+    <link rel="stylesheet" href="arc_graphics_tracing.css">
+    <script src="chrome://resources/js/cr.js"></script>
+    <script src="chrome://resources/js/cr/ui.js"></script>
+    <script src="chrome://resources/js/util.js"></script>
+    <script src="arc_graphics_tracing_ui.js"></script>
+    <script src="arc_graphics_tracing.js"></script>
+  </head>
+  <title>Graphics buffer</title>
+<body>
+  <div id="arc-graphics-tracing-control">
+    <h4>ARC graphics tracing</h4>
+    <input type="button"
+           id="arc-graphics-tracing-start"
+           value="Start tracing in ">
+    <input type="text"
+           id="arc-graphics-tracing-start-delay"
+           value="3">
+    seconds for
+    <input type="text"
+           id="arc-graphics-tracing-duration"
+           value="2">
+    seconds.
+  </div>
+  <div id='arc-event-bands'></div>
+  <svg id='arc-event-band-tooltip'></svg>
+</body>
+</html>
diff --git a/chrome/browser/resources/chromeos/arc_graphics_tracing/arc_graphics_tracing.js b/chrome/browser/resources/chromeos/arc_graphics_tracing/arc_graphics_tracing.js
new file mode 100644
index 0000000..e23e7d4e
--- /dev/null
+++ b/chrome/browser/resources/chromeos/arc_graphics_tracing/arc_graphics_tracing.js
@@ -0,0 +1,36 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview ARC Graphics Tracing UI root element.
+ */
+
+cr.define('cr.ArcGraphicsTracing', function() {
+  return {
+    /**
+     * Initializes internal structures.
+     */
+    initialize: function() {
+      var startDelay = $('arc-graphics-tracing-start-delay');
+      var duration = $('arc-graphics-tracing-duration');
+      $('arc-graphics-tracing-start')
+          .addEventListener('click', function(event) {
+            chrome.send(
+                'startTracing',
+                [Number(startDelay.value), Number(duration.value)]);
+          }, false);
+    },
+
+    setModel: function(model) {
+      setGraphicBuffersModel(model);
+    }
+  };
+});
+
+/**
+ * Initializes UI.
+ */
+window.onload = function() {
+  cr.ArcGraphicsTracing.initialize();
+};
diff --git a/chrome/browser/resources/chromeos/arc_graphics_tracing/arc_graphics_tracing_ui.js b/chrome/browser/resources/chromeos/arc_graphics_tracing/arc_graphics_tracing_ui.js
new file mode 100644
index 0000000..691d955
--- /dev/null
+++ b/chrome/browser/resources/chromeos/arc_graphics_tracing/arc_graphics_tracing_ui.js
@@ -0,0 +1,433 @@
+// 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.
+
+/**
+ * @fileoverview ARC Graphics Tracing UI.
+ */
+
+// Namespace of SVG elements
+var svgNS = 'http://www.w3.org/2000/svg';
+
+// Background color for the band with events.
+var bandColor = '#d3d3d3';
+
+/**
+ * Keep in sync with ArcTracingGraphicsModel::BufferEventType
+ * See chrome/browser/chromeos/arc/tracing/arc_tracing_graphics_model.h.
+ * Describes how events should be rendered. |color| specifies color of the
+ * event, |idle| indicates if this is the last event in the sequence of
+ * events. In case |idle| is true this means that sequence of events is
+ * done and relevant component is idle. |name| is used in tooltips.
+ */
+var eventAttributes = {
+  // kBufferQueueDequeueStart
+  100: {color: '#99cc00', idle: false, name: 'app requests buffer'},
+  // kBufferQueueDequeueDone
+  101: {color: '#669999', idle: false, name: 'app fills buffer'},
+  // kBufferQueueQueueStart
+  102: {color: '#cccc00', idle: false, name: 'app queues buffer'},
+  // kBufferQueueQueueDone
+  103: {color: bandColor, idle: true, name: 'buffer is queued'},
+  // kBufferQueueAcquire
+  104: {color: '#66ffcc', idle: false, name: 'use buffer'},
+  // kBufferQueueReleased
+  105: {color: bandColor, idle: true, name: 'buffer released'},
+
+  // kExoSurfaceAttach.
+  200: {color: '#99ccff', idle: false, name: 'surface attach'},
+  // kExoProduceResource
+  201: {color: '#cc66ff', idle: false, name: 'produce resource'},
+  // kExoBound
+  202: {color: '#66ffff', idle: false, name: 'buffer bound'},
+  // kExoPendingQuery
+  203: {color: '#00ff99', idle: false, name: 'pending query'},
+  // kExoReleased
+  204: {color: bandColor, idle: true, name: 'released'},
+
+  // kChromeBarrierOrder.
+  300: {color: '#ff9933', idle: false, name: 'barrier order'},
+  // kChromeBarrierFlush
+  301: {color: bandColor, idle: true, name: 'barrier flush'},
+
+  // kVsync
+  400: {color: '#ff3300', idle: false, name: 'vsync'},
+  // kSurfaceFlingerInvalidationStart
+  401: {color: '#ff9933', idle: false, name: 'invalidation start'},
+  // kSurfaceFlingerInvalidationDone
+  402: {color: bandColor, idle: true, name: 'invalidation done'},
+  // kSurfaceFlingerCompositionStart
+  403: {color: '#3399ff', idle: false, name: 'composition start'},
+  // kSurfaceFlingerCompositionDone
+  404: {color: bandColor, idle: true, name: 'composition done'},
+
+  // kChromeOSDraw
+  500: {color: '#3399ff', idle: false, name: 'draw'},
+  // kChromeOSSwap
+  501: {color: '#cc9900', idle: false, name: 'swap'},
+  // kChromeOSWaitForAck
+  502: {color: '#ccffff', idle: false, name: 'wait for ack'},
+  // kChromeOSWaitForPresentation
+  503: {color: '#65f441', idle: false, name: 'wait for presentation'},
+  // kChromeOSDrawFinished
+  504: {color: bandColor, idle: true, name: 'done'},
+};
+
+/**
+ * Converts timestamp into pixel offset. 1 pixel corresponds 100 microseconds.
+ *
+ * @param {number} timestamp in microseconds.
+ */
+function timestampToOffset(timestamp) {
+  return timestamp / 100.0;
+}
+
+/**
+ * Opposite conversion of |timestampToOffset|
+ *
+ * @param {number} offset in pixels.
+ */
+function offsetToTime(offset) {
+  return offset * 100.0;
+}
+
+/**
+ * Returns text representation of timestamp in milliseconds with one number
+ * after the decimal point.
+ *
+ * @param {number} timestamp in microseconds.
+ */
+function timestempToMsText(timestamp) {
+  return (timestamp / 1000.0).toFixed(1);
+}
+
+/** Factory class for SVG elements. */
+class SVG {
+  // Creates rectangle element in the |svg| with provided attributes.
+  static addRect(svg, x, y, width, height, color) {
+    var rect = document.createElementNS(svgNS, 'rect');
+    rect.setAttributeNS(null, 'x', x);
+    rect.setAttributeNS(null, 'y', y);
+    rect.setAttributeNS(null, 'width', width);
+    rect.setAttributeNS(null, 'height', height);
+    rect.setAttributeNS(null, 'fill', color);
+    rect.setAttributeNS(null, 'stroke', 'none');
+    svg.appendChild(rect);
+  }
+
+  // Creates circle element in the |svg| with provided attributes.
+  static addCircle(svg, x, y, radius, strokeWidth, color, strokeColor) {
+    var circle = document.createElementNS(svgNS, 'circle');
+    circle.setAttributeNS(null, 'cx', x);
+    circle.setAttributeNS(null, 'cy', y);
+    circle.setAttributeNS(null, 'r', radius);
+    circle.setAttributeNS(null, 'fill', color);
+    circle.setAttributeNS(null, 'stroke', strokeColor);
+    circle.setAttributeNS(null, 'stroke-width', strokeWidth);
+    svg.appendChild(circle);
+  }
+
+  // Creates text element in the |svg| with provided attributes.
+  static addText(svg, x, y, fontSize, textContent) {
+    var text = document.createElementNS(svgNS, 'text');
+    text.setAttributeNS(null, 'x', x);
+    text.setAttributeNS(null, 'y', y);
+    text.setAttributeNS(null, 'fill', 'black');
+    text.setAttributeNS(null, 'font-size', fontSize);
+    text.appendChild(document.createTextNode(textContent));
+    svg.appendChild(text);
+  }
+}
+
+/**
+ * Represents title for events bands that can collapse/expand controlled
+ * content.
+ */
+class EventBandTitle {
+  constructor(title) {
+    this.div = document.createElement('div');
+    this.div.classList.add('arc-events-band-title');
+    this.div.appendChild(document.createTextNode(title));
+    this.controlledItems = [];
+    this.div.onclick = this.onClick_.bind(this);
+    var parent = $('arc-event-bands');
+    parent.appendChild(this.div);
+  }
+
+  /**
+   * Adds extra HTML element under the control. This element will be
+   * automatically expanded/collapsed together with this title.
+   *
+   * @param {HTMLElement} item svg element to control.
+   */
+  addContolledItems(item) {
+    this.controlledItems.push(item);
+  }
+
+  onClick_() {
+    this.div.classList.toggle('hidden');
+    for (var i = 0; i < this.controlledItems.length; ++i) {
+      this.controlledItems[i].classList.toggle('hidden');
+    }
+  }
+}
+
+/** Represents one band with events. */
+class EventBand {
+  /**
+   * Creates band with events.
+   *
+   * @param {EventBandTitle} title controls visibility of this band.
+   * @param {string} className class name of the svg element that represents
+   *     this band. 'arc-events-top-band' is used for top-level events and
+   *     'arc-events-inner-band' is used for per-buffer events.
+   * @param {number} duration of band in microseconds.
+   * @param {height} height of elements in this band.
+   */
+  constructor(title, className, duration, height) {
+    this.duration = duration;
+    this.width = timestampToOffset(duration);
+    this.height = height;
+    this.svg = document.createElementNS(svgNS, 'svg');
+    this.svg.setAttributeNS(
+        'http://www.w3.org/2000/xmlns/', 'xmlns:xlink',
+        'http://www.w3.org/1999/xlink');
+    this.svg.setAttribute('width', this.width + 'px');
+    this.svg.setAttribute('height', this.height + 'px');
+    this.svg.classList.add(className);
+
+    this.setTooltip_();
+    title.addContolledItems(this.svg);
+    var parent = $('arc-event-bands');
+    parent.appendChild(this.svg);
+  }
+
+  /**
+   * Assigns events for this band. Events with type between |eventTypeMin|
+   * and |eventTypeMax| are only displayed on the band.
+   *
+   * @param {Object[]} events non-filtered list of all events. Each has
+   *     array where first element is type and second is timestamp.
+   * @param {number} eventTypeMin minimum inclusive type of the event to be
+   *     displayed on this band.
+   * @param {number} eventTypeMax maximum inclusive type of the event to be
+   *     displayed on this band.
+   */
+  setEvents(events, eventTypeMin, eventTypeMax) {
+    this.events = events;
+    this.eventTypeMin = eventTypeMin;
+    this.eventTypeMax = eventTypeMax;
+    this.drawBand_();
+  }
+
+  /** Enumerates events and creates visual representation */
+  drawBand_() {
+    var currentColor = bandColor;
+    var x = 0;
+    for (var i = 0; i < this.events.length; ++i) {
+      var event = this.events[i];
+      if (event[0] < this.eventTypeMin || event[0] > this.eventTypeMax) {
+        continue;
+      }
+      var nextX = timestampToOffset(event[1]);
+      SVG.addRect(this.svg, x, 0, nextX - x, this.height, currentColor);
+      currentColor = eventAttributes[event[0]].color;
+      x = nextX;
+    }
+    SVG.addRect(this.svg, x, 0, this.width - x, this.height, currentColor);
+  }
+
+  /** Updates tooltip and shows it for this band. */
+  showToolTip_(event) {
+    this.updateToolTip_(event);
+    this.tooltip.classList.add('active');
+  }
+
+  /** Hides the tooltip. */
+  hideToolTip_() {
+    this.tooltip.classList.remove('active');
+  }
+
+  /**
+   * Helper that finds next or previous event. Events that pass filter are
+   * only processed.
+   *
+   * @param {number} index starting index for the search, not inclusive.
+   * @param {direction} direction to search, 1 means to find the next event
+   *     and -1 means the previous event.
+   * @returns {number} index of the next or previous event or -1 in case
+   *     not found.
+   */
+  getNextEvent_(index, direction) {
+    while (true) {
+      index += direction;
+      if (index < 0 || index >= this.events.length) {
+        return -1;
+      }
+      if (this.events[index][0] >= this.eventTypeMin &&
+          this.events[index][0] <= this.eventTypeMax) {
+        return index;
+      }
+    }
+  }
+
+  /**
+   * Helper that returns render attributes for the event.
+   *
+   * @param {number} index element index in |this.events|.
+   */
+  getEventAttributes_(index) {
+    return eventAttributes[this.events[index][0]];
+  }
+
+  /**
+   * Updates tool tip based on event under the current cursor.
+   *
+   * @param {Object} event mouse event.
+   */
+  updateToolTip_(event) {
+    // Clear previous content.
+    this.tooltip.textContent = '';
+
+    // Tooltip constants for rendering.
+    var horizontalGap = 10;
+    var eventIconOffset = 70;
+    var eventIconRadius = 4;
+    var eventNameOffset = 78;
+    var verticalGap = 5;
+    var lineHeight = 16;
+    var fontSize = 12;
+
+    var yOffset = verticalGap + lineHeight;
+    var eventTimestamp = offsetToTime(event.offsetX);
+    SVG.addText(
+        this.tooltip, horizontalGap, yOffset, fontSize,
+        timestempToMsText(eventTimestamp) + ' ms');
+    yOffset += lineHeight;
+
+    // Find the event under the cursor. |index| points to the current event
+    // and |nextIndex| points to the next event.
+    var nextIndex = this.getNextEvent_(-1 /* index */, 1 /* direction */);
+    while (nextIndex >= 0) {
+      if (this.events[nextIndex][1] > eventTimestamp) {
+        break;
+      }
+      nextIndex = this.getNextEvent_(nextIndex, 1 /* direction */);
+    }
+    var index = this.getNextEvent_(nextIndex, -1 /* direction */);
+
+    // In case cursor points to idle event, show its interval. Otherwise
+    // show the sequence of non-idle events.
+    if (index < 0 || this.getEventAttributes_(index).idle) {
+      var startIdle = index < 0 ? 0 : this.events[index][1];
+      var endIdle = index < 0 ? this.duration : this.events[nextIndex][1];
+      SVG.addText(
+          this.tooltip, horizontalGap, yOffset, 12,
+          'Idle ' + timestempToMsText(startIdle) + '...' +
+              timestempToMsText(endIdle) + ' ms.');
+      yOffset += lineHeight;
+    } else {
+      // Find the start of the non-idle sequence.
+      while (true) {
+        var prevIndex = this.getNextEvent_(index, -1 /* direction */);
+        if (prevIndex < 0 || this.getEventAttributes_(prevIndex).idle) {
+          break;
+        }
+        index = prevIndex;
+      }
+
+      var sequenceTimestamp = this.events[index][1];
+      var lastTimestamp = sequenceTimestamp;
+      var firstEvent = true;
+      // Scan for the entries to show.
+      var entriesToShow = [];
+      while (index >= 0) {
+        var attributes = this.getEventAttributes_(index);
+        var eventTimestamp = this.events[index][1];
+        var entryToShow = {};
+        if (firstEvent) {
+          // Show the global timestamp.
+          entryToShow.prefix = timestempToMsText(sequenceTimestamp) + ' ms';
+          firstEvent = false;
+        } else {
+          // Show the offset relative to the start of sequence of events.
+          entryToShow.prefix = '+' +
+              timestempToMsText(eventTimestamp - sequenceTimestamp) + ' ms';
+        }
+        entryToShow.color = attributes.color;
+        entryToShow.text = attributes.name;
+        if (entriesToShow.length > 0) {
+          entriesToShow[entriesToShow.length - 1].text +=
+              ' [' + timestempToMsText(eventTimestamp - lastTimestamp) + ' ms]';
+        }
+        entriesToShow.push(entryToShow);
+        if (attributes.idle) {
+          break;
+        }
+        lastTimestamp = eventTimestamp;
+        index = this.getNextEvent_(index, 1 /* direction */);
+      }
+      for (var i = 0; i < entriesToShow.length; ++i) {
+        var entryToShow = entriesToShow[i];
+        SVG.addText(
+            this.tooltip, horizontalGap, yOffset, fontSize, entryToShow.prefix);
+        SVG.addCircle(
+            this.tooltip, eventIconOffset, yOffset - eventIconRadius,
+            eventIconRadius, 1, entryToShow.color, 'black');
+        SVG.addText(
+            this.tooltip, eventNameOffset, yOffset, fontSize, entryToShow.text);
+        yOffset += lineHeight;
+      }
+    }
+    yOffset += verticalGap;
+
+    this.tooltip.style.left = event.clientX + 'px';
+    this.tooltip.style.top = event.clientY + 'px';
+    this.tooltip.style.height = yOffset + 'px';
+  }
+
+  /** Initializes tooltip support by observing mouse events */
+  setTooltip_() {
+    this.tooltip = getSVGElement('arc-event-band-tooltip');
+    this.svg.onmouseover = this.showToolTip_.bind(this);
+    this.svg.onmouseout = this.hideToolTip_.bind(this);
+    this.svg.onmousemove = this.updateToolTip_.bind(this);
+  }
+}
+
+/**
+ * Creates visual representation of graphic buffers event model.
+ *
+ * @param {Object} model object produced by |ArcTracingGraphicsModel|.
+ */
+function setGraphicBuffersModel(model) {
+  // Clear previous content.
+  $('arc-event-bands').textContent = '';
+
+  var chromeTopBandTitle = new EventBandTitle('Chrome');
+  var chromeTopBand = new EventBand(
+      chromeTopBandTitle, 'arc-events-top-band', model.duration, 20);
+  chromeTopBand.setEvents(model.chrome, 500, 504);
+
+  var androidTopBandTitle = new EventBandTitle('Android');
+  var androidTopBand = new EventBand(
+      androidTopBandTitle, 'arc-events-top-band', model.duration, 20);
+  androidTopBand.setEvents(model.android, 401, 504);
+
+  for (i = 0; i < model.views.length; i++) {
+    var view = model.views[i];
+    var activityTitleText = 'Task #' + view.task_id + ' - ' + view.activity;
+    var activityTitle = new EventBandTitle(activityTitleText);
+    for (j = 0; j < view.buffers.length; j++) {
+      var androidBand = new EventBand(
+          activityTitle, 'arc-events-inner-band', model.duration, 14);
+      androidBand.setEvents(view.buffers[j], 100, 105);
+      var exoBand = new EventBand(
+          activityTitle, 'arc-events-inner-band', model.duration, 14);
+      exoBand.setEvents(view.buffers[j], 200, 204);
+      var chromeBand = new EventBand(
+          activityTitle, 'arc-events-inner-band', model.duration, 14);
+      chromeBand.setEvents(view.buffers[j], 300, 301);
+    }
+  }
+}
diff --git a/chrome/browser/resources/pdf/ink/BUILD.gn b/chrome/browser/resources/pdf/ink/BUILD.gn
index ff59d37b..b98fd60 100644
--- a/chrome/browser/resources/pdf/ink/BUILD.gn
+++ b/chrome/browser/resources/pdf/ink/BUILD.gn
@@ -11,10 +11,7 @@
 }
 
 js_library("ink_api") {
-  externs_list = [
-    "externs.js",
-    "../../../../../third_party/ink/build/ink_lib_externs.js",
-  ]
+  externs_list = [ "//third_party/ink/build/ink_lib_externs.js" ]
 }
 
 js_type_check("ink") {
diff --git a/chrome/browser/resources/settings/about_page/management_page.html b/chrome/browser/resources/settings/about_page/management_page.html
index 00babc7..d4ada60 100644
--- a/chrome/browser/resources/settings/about_page/management_page.html
+++ b/chrome/browser/resources/settings/about_page/management_page.html
@@ -3,6 +3,7 @@
 <link rel="import" href="management_browser_proxy.html">
 <link rel="import" href="../settings_shared_css.html">
 <link rel="import" href="../icons.html">
+<link rel="import" href="chrome://resources/html/i18n_behavior.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
 
 <dom-module id="settings-management-page">
@@ -19,6 +20,7 @@
 
       ul {
         list-style-type : none;
+        margin: 0;
         padding: 0;
         text-align: start;
       }
@@ -26,7 +28,6 @@
       #reportingInfoList div {
         align-items: center;
         display: flex;
-        margin-bottom: 2em;
       }
 
       #reportingInfoList div + div {
@@ -38,6 +39,11 @@
         width: 24px;
       }
 
+      #reportingInfoList,
+      #extensionsTable {
+        margin-bottom: 2em;
+      }
+
       .table-head {
         font-weight: 500;
       }
@@ -70,30 +76,52 @@
         </span>
       </div>
 
-      <div class="settings-box block single-column" id="policies" hidden>
-        <h2>$i18n{managementDeviceReporting}</h2>
-        <div class="content-indented subtitle" id="deviceConfiguration">
-            $i18n{managementDeviceConfiguration}
-        </div>
-        <div id="reportingInfoList">
-          <div class="content-indented" id="reportingDevice" hidden>
-            <iron-icon icon="settings:computer"></iron-icon>
-            <span id="reportingDeviceText"> </span>
+      <template is="dom-if" if="[[!shouldHidePolicies_(reportingDevice_,
+          reportingSecurity_, reportingUserActivity_, reportingWeb_)]]">
+        <div class="settings-box block single-column" id="policies">
+          <h2>$i18n{managementDeviceReporting}</h2>
+          <div class="content-indented subtitle" id="deviceConfiguration">
+              $i18n{managementDeviceConfiguration}
           </div>
-          <div class="content-indented" id="reportingSecurity" hidden>
-            <iron-icon icon="settings:security"></iron-icon>
-            <span id="reportingSecurityText"> </span>
-          </div>
-          <div class="content-indented" id="reportingUserActivity" hidden>
-            <iron-icon icon="settings:person"></iron-icon>
-            <span id="reportingUserActivityText"> </span>
-          </div>
-          <div class="content-indented" id="reportingWeb" hidden>
-            <iron-icon icon="settings:public"></iron-icon>
-            <span id="reportingWebText"> </span>
+          <div id="reportingInfoList">
+            <div class="content-indented"
+              hidden$="[[!reportingDevice_.length]]">
+              <iron-icon icon="settings:computer"></iron-icon>
+              <ul>
+                <template is="dom-repeat" items="[[reportingDevice_]]">
+                  <li>[[i18n(item)]]</li>
+                </template>
+              </ul>
+            </div>
+            <div class="content-indented"
+              hidden$="[[!reportingSecurity_.length]]">
+              <iron-icon icon="settings:security"></iron-icon>
+              <ul>
+                <template is="dom-repeat" items="[[reportingSecurity_]]">
+                  <li>[[i18n(item)]]</li>
+                </template>
+              </ul>
+            </div>
+            <div class="content-indented"
+              hidden$="[[!reportingUserActivity_.length]]">
+              <iron-icon icon="settings:person"></iron-icon>
+              <ul>
+                <template is="dom-repeat" items="[[reportingUserActivity_]]">
+                  <li>[[i18n(item)]]</li>
+                </template>
+              </ul>
+            </div>
+            <div class="content-indented" hidden$="[[!reportingWeb_.length]]">
+              <iron-icon icon="settings:public"></iron-icon>
+              <ul>
+                <template is="dom-repeat" items="[[reportingWeb_]]">
+                  <li>[[i18n(item)]]</li>
+                </template>
+              </ul>
+            </div>
           </div>
         </div>
-      </div>
+      </template>
 
       <div class="settings-box two-line single-column" id="extensions" hidden>
         <h2>$i18n{managementExtensionReporting}</h2>
diff --git a/chrome/browser/resources/settings/about_page/management_page.js b/chrome/browser/resources/settings/about_page/management_page.js
index fe07871f..16bf68d 100644
--- a/chrome/browser/resources/settings/about_page/management_page.js
+++ b/chrome/browser/resources/settings/about_page/management_page.js
@@ -9,9 +9,34 @@
 Polymer({
   is: 'settings-management-page',
 
+  behaviors: [I18nBehavior],
+
+  properties: {
+    /** @private {!Array<string>} */
+    reportingDevice_: Array,
+
+    /** @private {!Array<string>} */
+    reportingSecurity_: Array,
+
+    /** @private {!Array<string>} */
+    reportingUserActivity_: Array,
+
+    /** @private {!Array<string>} */
+    reportingWeb_: Array,
+  },
+
   /** @private {?settings.ManagementBrowserProxy} */
   browserProxy_: null,
 
+  /**
+   * @return {boolean}
+   * @private
+   */
+  shouldHidePolicies_: function() {
+    return !this.reportingDevice_.length && !this.reportingSecurity_.length &&
+        !this.reportingUserActivity_.length && !this.reportingWeb_.length;
+  },
+
   /** @private */
   getDeviceManagementStatus_: function() {
     this.browserProxy_.getDeviceManagementStatus()
@@ -28,89 +53,6 @@
   },
 
   /** @private */
-  getReportingDevice_: function() {
-    this.browserProxy_.getReportingDevice().then(reportingSources => {
-      if (reportingSources.length == 0) {
-        return;
-      }
-      let reportingInfoAdded = false;
-
-      for (const id of reportingSources) {
-        reportingInfoAdded = true;
-        this.$.reportingDeviceText.textContent +=
-            loadTimeData.getString(id) + ' ';
-      }
-
-      if (reportingInfoAdded) {
-        this.$.policies.hidden = false;
-        this.$.reportingDevice.hidden = false;
-      }
-    });
-  },
-
-  /** @private */
-  getReportingSecurity_: function() {
-    this.browserProxy_.getReportingSecurity().then(reportingSources => {
-      if (reportingSources.length == 0) {
-        return;
-      }
-      let reportingInfoAdded = false;
-
-      for (const id of reportingSources) {
-        reportingInfoAdded = true;
-        this.$.reportingSecurityText.textContent +=
-            loadTimeData.getString(id) + ' ';
-      }
-
-      if (reportingInfoAdded) {
-        this.$.policies.hidden = false;
-        this.$.reportingSecurity.hidden = false;
-      }
-    });
-  },
-
-  /** @private */
-  getReportingUserActivity_: function() {
-    this.browserProxy_.getReportingUserActivity().then(reportingSources => {
-      if (reportingSources.length == 0) {
-        return;
-      }
-      let reportingInfoAdded = false;
-
-      for (const id of reportingSources) {
-        reportingInfoAdded = true;
-        this.$.reportingUserActivityText.textContent +=
-            loadTimeData.getString(id) + ' ';
-      }
-
-      if (reportingInfoAdded) {
-        this.$.policies.hidden = false;
-        this.$.reportingUserActivity.hidden = false;
-      }
-    });
-  },
-
-  /** @private */
-  getReportingWeb_: function() {
-    this.browserProxy_.getReportingWeb().then(reportingSources => {
-      if (reportingSources.length == 0) {
-        return;
-      }
-      let reportingInfoAdded = false;
-
-      for (const id of reportingSources) {
-        reportingInfoAdded = true;
-        this.$.reportingWebText.textContent += loadTimeData.getString(id) + ' ';
-      }
-
-      if (reportingInfoAdded) {
-        this.$.policies.hidden = false;
-        this.$.reportingWeb.hidden = false;
-      }
-    });
-  },
-
-  /** @private */
   getExtensions_: function() {
     // Show names and permissions of |extensions| in a table.
     this.browserProxy_.getExtensions().then(extensions => {
@@ -166,13 +108,21 @@
 
     this.getDeviceManagementStatus_();
 
-    this.getReportingDevice_();
+    this.browserProxy_.getReportingDevice().then(reportingSources => {
+      this.reportingDevice_ = reportingSources;
+    });
 
-    this.getReportingSecurity_();
+    this.browserProxy_.getReportingSecurity().then(reportingSources => {
+      this.reportingSecurity_ = reportingSources;
+    });
 
-    this.getReportingUserActivity_();
+    this.browserProxy_.getReportingUserActivity().then(reportingSources => {
+      this.reportingUserActivity_ = reportingSources;
+    });
 
-    this.getReportingWeb_();
+    this.browserProxy_.getReportingWeb().then(reportingSources => {
+      this.reportingWeb_ = reportingSources;
+    });
 
     this.getExtensions_();
 
diff --git a/chrome/browser/safe_browsing/advanced_protection_status_manager_unittest.cc b/chrome/browser/safe_browsing/advanced_protection_status_manager_unittest.cc
index f6d80608..dddbbffc 100644
--- a/chrome/browser/safe_browsing/advanced_protection_status_manager_unittest.cc
+++ b/chrome/browser/safe_browsing/advanced_protection_status_manager_unittest.cc
@@ -7,13 +7,14 @@
 #include "base/bind.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "chrome/browser/safe_browsing/advanced_protection_status_manager_factory.h"
-#include "chrome/browser/signin/account_tracker_service_factory.h"
+#include "chrome/browser/signin/account_fetcher_service_factory.h"
 #include "chrome/browser/signin/identity_test_environment_profile_adaptor.h"
 #include "chrome/test/base/testing_profile.h"
 #include "components/prefs/pref_service.h"
 #include "components/safe_browsing/common/safe_browsing_prefs.h"
-#include "components/signin/core/browser/account_tracker_service.h"
+#include "components/signin/core/browser/account_fetcher_service.h"
 #include "content/public/test/test_browser_thread_bundle.h"
+#include "services/identity/public/cpp/accounts_mutator.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -43,9 +44,6 @@
     identity_test_env_adaptor_ =
         std::make_unique<IdentityTestEnvironmentProfileAdaptor>(
             testing_profile_.get());
-
-    account_tracker_service_ =
-        AccountTrackerServiceFactory::GetForProfile(testing_profile_.get());
   }
 
   ~AdvancedProtectionStatusManagerTest() override {}
@@ -92,7 +90,6 @@
   std::unique_ptr<TestingProfile> testing_profile_;
   std::unique_ptr<IdentityTestEnvironmentProfileAdaptor>
       identity_test_env_adaptor_;
-  AccountTrackerService* account_tracker_service_;
 };
 
 }  // namespace
@@ -342,12 +339,23 @@
   EXPECT_FALSE(aps_manager.IsRefreshScheduled());
 
   // Simulates account update.
-  account_tracker_service_->SetIsAdvancedProtectionAccount(
-      account_id, /* is_under_advanced_protection= */ true);
+  identity_test_env()
+      ->identity_manager()
+      ->GetAccountsMutator()
+      ->UpdateAccountInfo(account_id,
+                          /*is_child_account=*/false,
+                          /*is_under_advanced_protection=*/true);
   EXPECT_TRUE(aps_manager.is_under_advanced_protection());
   EXPECT_TRUE(aps_manager.IsRefreshScheduled());
 
-  account_tracker_service_->RemoveAccount(account_id);
+  AccountFetcherService* account_fetcher_service =
+      AccountFetcherServiceFactory::GetForProfile(testing_profile_.get());
+  // This call is necessary to ensure that the account removal is fully
+  // processed in this testing context.
+  account_fetcher_service->EnableNetworkFetchesForTest();
+  identity_test_env()->identity_manager()->GetAccountsMutator()->RemoveAccount(
+      account_id,
+      signin_metrics::SourceForRefreshTokenOperation::kUserMenu_RemoveAccount);
   EXPECT_FALSE(aps_manager.is_under_advanced_protection());
   EXPECT_TRUE(testing_profile_->GetPrefs()->HasPrefPath(
       prefs::kAdvancedProtectionLastRefreshInUs));
diff --git a/chrome/browser/supervised_user/child_accounts/child_account_service.cc b/chrome/browser/supervised_user/child_accounts/child_account_service.cc
index 024910d..69d4a17 100644
--- a/chrome/browser/supervised_user/child_accounts/child_account_service.cc
+++ b/chrome/browser/supervised_user/child_accounts/child_account_service.cc
@@ -29,7 +29,6 @@
 #include "components/pref_registry/pref_registry_syncable.h"
 #include "components/prefs/pref_service.h"
 #include "components/signin/core/browser/gaia_cookie_manager_service.h"
-#include "components/signin/core/browser/signin_pref_names.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/storage_partition.h"
 #include "services/identity/public/cpp/identity_manager.h"
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 0d70af2..3a789c8 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -1516,6 +1516,10 @@
       "webui/chromeos/account_manager_welcome_dialog.h",
       "webui/chromeos/account_manager_welcome_ui.cc",
       "webui/chromeos/account_manager_welcome_ui.h",
+      "webui/chromeos/arc_graphics_tracing/arc_graphics_tracing_handler.cc",
+      "webui/chromeos/arc_graphics_tracing/arc_graphics_tracing_handler.h",
+      "webui/chromeos/arc_graphics_tracing/arc_graphics_tracing_ui.cc",
+      "webui/chromeos/arc_graphics_tracing/arc_graphics_tracing_ui.h",
       "webui/chromeos/assistant_optin/assistant_optin_ui.cc",
       "webui/chromeos/assistant_optin/assistant_optin_ui.h",
       "webui/chromeos/assistant_optin/assistant_optin_utils.cc",
diff --git a/chrome/browser/ui/managed_ui_browsertest.cc b/chrome/browser/ui/managed_ui_browsertest.cc
index 638293e..0e552a0 100644
--- a/chrome/browser/ui/managed_ui_browsertest.cc
+++ b/chrome/browser/ui/managed_ui_browsertest.cc
@@ -8,6 +8,7 @@
 #include "base/values.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
+#include "chrome/common/chrome_features.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "components/policy/core/browser/browser_policy_connector.h"
 #include "components/policy/core/common/mock_configuration_policy_provider.h"
@@ -35,6 +36,9 @@
 };
 
 IN_PROC_BROWSER_TEST_F(ManagedUiTest, ShouldDisplayManagedUiFlagDisabled) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitFromCommandLine("", "ShowManagedUi");
+
   PolicyMap policy_map;
   policy_map.Set("test-policy", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_MACHINE,
                  POLICY_SOURCE_PLATFORM,
diff --git a/chrome/browser/ui/views/crostini/crostini_installer_view.cc b/chrome/browser/ui/views/crostini/crostini_installer_view.cc
index d7c16a36e..61f7b4d 100644
--- a/chrome/browser/ui/views/crostini/crostini_installer_view.cc
+++ b/chrome/browser/ui/views/crostini/crostini_installer_view.cc
@@ -149,6 +149,13 @@
   UpdateState(State::INSTALL_START);
   profile_->GetPrefs()->SetBoolean(crostini::prefs::kCrostiniEnabled, true);
 
+  // The default value of kCrostiniContainers is set to migrate existing
+  // crostini users who don't have the pref set. If crostini is being installed,
+  // then we know the user must not actually have any containers yet, so we set
+  // this pref to the empty list.
+  profile_->GetPrefs()->Set(crostini::prefs::kCrostiniContainers,
+                            base::Value(base::Value::Type::LIST));
+
   progress_bar_->SetVisible(true);
 
   // |learn_more_link_| should only be present in State::PROMPT.
diff --git a/chrome/browser/ui/views/frame/immersive_mode_controller_ash.cc b/chrome/browser/ui/views/frame/immersive_mode_controller_ash.cc
index 368ee50..afc0730f 100644
--- a/chrome/browser/ui/views/frame/immersive_mode_controller_ash.cc
+++ b/chrome/browser/ui/views/frame/immersive_mode_controller_ash.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/ui/views/frame/immersive_mode_controller_ash.h"
 
 #include "ash/public/cpp/immersive/immersive_revealed_lock.h"
+#include "ash/public/cpp/window_pin_type.h"
 #include "ash/public/cpp/window_properties.h"
 #include "ash/public/cpp/window_state_type.h"
 #include "ash/public/interfaces/window_state_type.mojom.h"
@@ -195,6 +196,11 @@
   if (!TabletModeClient::Get()->tablet_mode_enabled())
     return;
 
+  // Don't use immersive mode as long as we are in the locked fullscreen mode
+  // since immersive shows browser controls which allow exiting the mode.
+  if (ash::IsWindowTrustedPinned(widget->GetNativeWindow()))
+    return;
+
   // Enable immersive mode if the widget is activated. Do not disable immersive
   // mode if the widget deactivates, but is not minimized.
   ash::ImmersiveFullscreenController::EnableForWidget(
diff --git a/chrome/browser/ui/views/toolbar/toolbar_button_views_unittest.cc b/chrome/browser/ui/views/toolbar/toolbar_button_unittest.cc
similarity index 100%
rename from chrome/browser/ui/views/toolbar/toolbar_button_views_unittest.cc
rename to chrome/browser/ui/views/toolbar/toolbar_button_unittest.cc
diff --git a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
index 9e0f8a5..04b957a 100644
--- a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
+++ b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
@@ -143,6 +143,7 @@
 #include "chrome/browser/chromeos/login/easy_unlock/easy_unlock_service.h"
 #include "chrome/browser/chromeos/login/easy_unlock/easy_unlock_service_factory.h"
 #include "chrome/browser/chromeos/secure_channel/secure_channel_client_provider.h"
+#include "chrome/browser/ui/webui/chromeos/arc_graphics_tracing/arc_graphics_tracing_ui.h"
 #include "chrome/browser/ui/webui/chromeos/assistant_optin/assistant_optin_ui.h"
 #include "chrome/browser/ui/webui/chromeos/bluetooth_pairing_dialog.h"
 #include "chrome/browser/ui/webui/chromeos/certificate_manager_dialog_ui.h"
@@ -166,6 +167,7 @@
 #include "chromeos/components/multidevice/debug_webui/proximity_auth_ui.h"
 #include "chromeos/components/multidevice/debug_webui/url_constants.h"
 #include "chromeos/constants/chromeos_features.h"
+#include "components/arc/arc_features.h"
 #endif
 
 #if defined(OS_CHROMEOS) && !defined(OFFICIAL_BUILD)
@@ -523,6 +525,12 @@
     return &NewWebUI<SysInternalsUI>;
   if (url.host_piece() == chrome::kChromeUIAssistantOptInHost)
     return &NewWebUI<chromeos::AssistantOptInUI>;
+
+  if (url.host_piece() == chrome::kChromeUIArcGraphicsTracingHost) {
+    if (!base::FeatureList::IsEnabled(arc::kGraphicBuffersVisualizationTool))
+      return nullptr;
+    return &NewWebUI<chromeos::ArcGraphicsTracingUI>;
+  }
 #if !defined(OFFICIAL_BUILD)
   if (!base::SysInfo::IsRunningOnChromeOS()) {
     if (url.host_piece() == chrome::kChromeUIDeviceEmulatorHost)
diff --git a/chrome/browser/ui/webui/chromeos/arc_graphics_tracing/arc_graphics_tracing_handler.cc b/chrome/browser/ui/webui/chromeos/arc_graphics_tracing/arc_graphics_tracing_handler.cc
new file mode 100644
index 0000000..196f4c1e
--- /dev/null
+++ b/chrome/browser/ui/webui/chromeos/arc_graphics_tracing/arc_graphics_tracing_handler.cc
@@ -0,0 +1,112 @@
+// 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 "chrome/browser/ui/webui/chromeos/arc_graphics_tracing/arc_graphics_tracing_handler.h"
+
+#include <string>
+
+#include "base/bind.h"
+#include "base/memory/ref_counted_memory.h"
+#include "base/task/post_task.h"
+#include "base/values.h"
+#include "chrome/browser/chromeos/arc/tracing/arc_tracing_graphics_model.h"
+#include "chrome/browser/chromeos/arc/tracing/arc_tracing_model.h"
+#include "content/public/browser/tracing_controller.h"
+#include "content/public/browser/web_ui.h"
+
+namespace chromeos {
+
+namespace {
+
+std::unique_ptr<base::Value> BuildGraphicsModel(const std::string& data) {
+  arc::ArcTracingModel common_model;
+  if (!common_model.Build(data)) {
+    LOG(ERROR) << "Failed to build common model";
+    return nullptr;
+  }
+
+  arc::ArcTracingGraphicsModel graphics_model;
+  if (!graphics_model.Build(common_model)) {
+    LOG(ERROR) << "Failed to build graphic buffers model";
+    return nullptr;
+  }
+
+  return graphics_model.Serialize();
+}
+
+}  // namespace
+
+ArcGraphicsTracingHandler::ArcGraphicsTracingHandler()
+    : weak_ptr_factory_(this) {}
+
+ArcGraphicsTracingHandler::~ArcGraphicsTracingHandler() = default;
+
+void ArcGraphicsTracingHandler::RegisterMessages() {
+  web_ui()->RegisterMessageCallback(
+      "startTracing",
+      base::BindRepeating(&ArcGraphicsTracingHandler::HandleStartTracing,
+                          base::Unretained(this)));
+}
+
+void ArcGraphicsTracingHandler::StartTracing(double duration) {
+  base::trace_event::TraceConfig config(
+      "-*,exo,viz,toplevel,gpu,cc,blink,disabled-by-default-android "
+      "gfx,disabled-by-default-android hal",
+      base::trace_event::RECORD_CONTINUOUSLY);
+  config.EnableSystrace();
+  content::TracingController::GetInstance()->StartTracing(
+      config, base::BindOnce(&ArcGraphicsTracingHandler::OnTracingStarted,
+                             weak_ptr_factory_.GetWeakPtr(), duration));
+}
+
+void ArcGraphicsTracingHandler::StopTracing() {
+  content::TracingController::GetInstance()->StopTracing(
+      content::TracingController::CreateStringEndpoint(
+          base::BindRepeating(&ArcGraphicsTracingHandler::OnTracingStopped,
+                              weak_ptr_factory_.GetWeakPtr())));
+}
+
+void ArcGraphicsTracingHandler::OnTracingStarted(double duration) {
+  tracing_timer_.Start(FROM_HERE, base::TimeDelta::FromSecondsD(duration),
+                       base::BindOnce(&ArcGraphicsTracingHandler::StopTracing,
+                                      base::Unretained(this)));
+}
+
+void ArcGraphicsTracingHandler::OnTracingStopped(
+    std::unique_ptr<const base::DictionaryValue> metadata,
+    base::RefCountedString* trace_data) {
+  std::string string_data;
+  string_data.swap(trace_data->data());
+
+  base::PostTaskWithTraitsAndReplyWithResult(
+      FROM_HERE, {base::MayBlock(), base::TaskPriority::BEST_EFFORT},
+      base::BindOnce(&BuildGraphicsModel, std::move(string_data)),
+      base::BindOnce(&ArcGraphicsTracingHandler::OnGraphicsModelReady,
+                     weak_ptr_factory_.GetWeakPtr()));
+}
+
+void ArcGraphicsTracingHandler::OnGraphicsModelReady(
+    std::unique_ptr<base::Value> model) {
+  if (!model)
+    return;
+  AllowJavascript();
+  CallJavascriptFunction("cr.ArcGraphicsTracing.setModel", *model);
+}
+
+void ArcGraphicsTracingHandler::HandleStartTracing(
+    const base::ListValue* args) {
+  DCHECK_EQ(2U, args->GetSize());
+  if ((!args->GetList()[0].is_double() && !args->GetList()[0].is_int()) ||
+      (!args->GetList()[1].is_double() && !args->GetList()[1].is_int())) {
+    LOG(ERROR) << "Invalid input";
+    return;
+  }
+  const double delay = args->GetList()[0].GetDouble();
+  const double duration = args->GetList()[1].GetDouble();
+  tracing_timer_.Start(FROM_HERE, base::TimeDelta::FromSecondsD(delay),
+                       base::BindOnce(&ArcGraphicsTracingHandler::StartTracing,
+                                      base::Unretained(this), duration));
+}
+
+}  // namespace chromeos
diff --git a/chrome/browser/ui/webui/chromeos/arc_graphics_tracing/arc_graphics_tracing_handler.h b/chrome/browser/ui/webui/chromeos/arc_graphics_tracing/arc_graphics_tracing_handler.h
new file mode 100644
index 0000000..661a2cce
--- /dev/null
+++ b/chrome/browser/ui/webui/chromeos/arc_graphics_tracing/arc_graphics_tracing_handler.h
@@ -0,0 +1,55 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_CHROMEOS_ARC_GRAPHICS_TRACING_ARC_GRAPHICS_TRACING_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_ARC_GRAPHICS_TRACING_ARC_GRAPHICS_TRACING_HANDLER_H_
+
+#include <memory>
+#include <utility>
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/timer/timer.h"
+#include "content/public/browser/web_ui_message_handler.h"
+
+namespace base {
+class ListValue;
+class RefCountedString;
+}  // namespace base
+
+namespace chromeos {
+
+class ArcGraphicsTracingHandler : public content::WebUIMessageHandler {
+ public:
+  ArcGraphicsTracingHandler();
+  ~ArcGraphicsTracingHandler() override;
+
+  // content::WebUIMessageHandler:
+  void RegisterMessages() override;
+
+ private:
+  void StartTracing(double duration);
+  void StopTracing();
+
+  void OnTracingStarted(double duration);
+  void OnTracingStopped(std::unique_ptr<const base::DictionaryValue> metadata,
+                        base::RefCountedString* trace_data);
+
+  // Called when graphics model is built and ready.
+  void OnGraphicsModelReady(std::unique_ptr<base::Value> model);
+
+  // Handlers for calls from JS.
+  void HandleStartTracing(const base::ListValue* args);
+
+  // To implement start/stop tracing.
+  base::OneShotTimer tracing_timer_;
+
+  base::WeakPtrFactory<ArcGraphicsTracingHandler> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(ArcGraphicsTracingHandler);
+};
+
+}  // namespace chromeos
+
+#endif  // CHROME_BROWSER_UI_WEBUI_CHROMEOS_ARC_GRAPHICS_TRACING_ARC_GRAPHICS_TRACING_HANDLER_H_
diff --git a/chrome/browser/ui/webui/chromeos/arc_graphics_tracing/arc_graphics_tracing_ui.cc b/chrome/browser/ui/webui/chromeos/arc_graphics_tracing/arc_graphics_tracing_ui.cc
new file mode 100644
index 0000000..880704f
--- /dev/null
+++ b/chrome/browser/ui/webui/chromeos/arc_graphics_tracing/arc_graphics_tracing_ui.cc
@@ -0,0 +1,58 @@
+// 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 "chrome/browser/ui/webui/chromeos/arc_graphics_tracing/arc_graphics_tracing_ui.h"
+
+#include <memory>
+#include <string>
+
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/webui/chromeos/arc_graphics_tracing/arc_graphics_tracing_handler.h"
+#include "chrome/common/webui_url_constants.h"
+#include "chrome/grit/browser_resources.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_data_source.h"
+#include "ui/base/webui/web_ui_util.h"
+
+namespace {
+
+constexpr char kArcGraphicsTracingJsPath[] = "arc_graphics_tracing.js";
+constexpr char kArcGraphicsTracingUiJsPath[] = "arc_graphics_tracing_ui.js";
+constexpr char kArcGraphicsTracingCssPath[] = "arc_graphics_tracing.css";
+
+content::WebUIDataSource* CreateDataSource() {
+  content::WebUIDataSource* source =
+      content::WebUIDataSource::Create(chrome::kChromeUIArcGraphicsTracingHost);
+  source->SetJsonPath("strings.js");
+  source->SetDefaultResource(IDR_ARC_GRAPHICS_TRACING_HTML);
+  source->AddResourcePath(kArcGraphicsTracingJsPath,
+                          IDR_ARC_GRAPHICS_TRACING_JS);
+  source->AddResourcePath(kArcGraphicsTracingUiJsPath,
+                          IDR_ARC_GRAPHICS_TRACING_UI_JS);
+  source->AddResourcePath(kArcGraphicsTracingCssPath,
+                          IDR_ARC_GRAPHICS_TRACING_CSS);
+  source->OverrideContentSecurityPolicyScriptSrc(
+      "script-src chrome://resources 'self';");
+
+  base::DictionaryValue localized_strings;
+  const std::string& app_locale = g_browser_process->GetApplicationLocale();
+  webui::SetLoadTimeDataDefaults(app_locale, &localized_strings);
+  source->AddLocalizedStrings(localized_strings);
+  source->UseGzip();
+
+  return source;
+}
+
+}  // anonymous namespace
+
+namespace chromeos {
+
+ArcGraphicsTracingUI::ArcGraphicsTracingUI(content::WebUI* web_ui)
+    : WebUIController(web_ui) {
+  web_ui->AddMessageHandler(std::make_unique<ArcGraphicsTracingHandler>());
+  content::WebUIDataSource::Add(Profile::FromWebUI(web_ui), CreateDataSource());
+}
+
+}  // namespace chromeos
diff --git a/chrome/browser/ui/webui/chromeos/arc_graphics_tracing/arc_graphics_tracing_ui.h b/chrome/browser/ui/webui/chromeos/arc_graphics_tracing/arc_graphics_tracing_ui.h
new file mode 100644
index 0000000..c6f408d
--- /dev/null
+++ b/chrome/browser/ui/webui/chromeos/arc_graphics_tracing/arc_graphics_tracing_ui.h
@@ -0,0 +1,28 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_CHROMEOS_ARC_GRAPHICS_TRACING_ARC_GRAPHICS_TRACING_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_ARC_GRAPHICS_TRACING_ARC_GRAPHICS_TRACING_UI_H_
+
+#include "base/macros.h"
+#include "content/public/browser/web_ui_controller.h"
+
+namespace content {
+class WebUI;
+}
+
+namespace chromeos {
+
+// WebUI controller for arc graphics tracing.
+class ArcGraphicsTracingUI : public content::WebUIController {
+ public:
+  explicit ArcGraphicsTracingUI(content::WebUI* web_ui);
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ArcGraphicsTracingUI);
+};
+
+}  // namespace chromeos
+
+#endif  // CHROME_BROWSER_UI_WEBUI_CHROMEOS_ARC_GRAPHICS_TRACING_ARC_GRAPHICS_TRACING_UI_H_
diff --git a/chrome/browser/ui/webui/settings/people_handler.cc b/chrome/browser/ui/webui/settings/people_handler.cc
index 26e85c9..4b906465 100644
--- a/chrome/browser/ui/webui/settings/people_handler.cc
+++ b/chrome/browser/ui/webui/settings/people_handler.cc
@@ -546,8 +546,10 @@
   // Don't allow "encrypt all" if the SyncService doesn't allow it.
   // The UI is hidden, but the user may have enabled it e.g. by fiddling with
   // the web inspector.
-  if (!service->GetUserSettings()->IsEncryptEverythingAllowed())
+  if (!service->GetUserSettings()->IsEncryptEverythingAllowed()) {
     configuration.encrypt_all = false;
+    configuration.set_new_passphrase = false;
+  }
 
   // Note: Data encryption will not occur until configuration is complete
   // (when the PSS receives its CONFIGURE_DONE notification from the sync
diff --git a/chrome/browser/ui/webui/settings/people_handler_unittest.cc b/chrome/browser/ui/webui/settings/people_handler_unittest.cc
index 7ef1443..a4fe434 100644
--- a/chrome/browser/ui/webui/settings/people_handler_unittest.cc
+++ b/chrome/browser/ui/webui/settings/people_handler_unittest.cc
@@ -731,6 +731,8 @@
   base::ListValue list_args;
   list_args.AppendString(kTestCallbackId);
   list_args.AppendString(args);
+  ON_CALL(*mock_pss_->GetUserSettingsMock(), IsEncryptEverythingAllowed())
+      .WillByDefault(Return(true));
   ON_CALL(*mock_pss_->GetUserSettingsMock(),
           IsPassphraseRequiredForDecryption())
       .WillByDefault(Return(false));
@@ -1067,11 +1069,6 @@
 }
 
 TEST_F(PeopleHandlerTest, TurnOnEncryptAllDisallowed) {
-  std::string args = GetConfiguration(
-      NULL, SYNC_ALL_DATA, GetAllTypes(), std::string(), ENCRYPT_ALL_DATA);
-  base::ListValue list_args;
-  list_args.AppendString(kTestCallbackId);
-  list_args.AppendString(args);
   ON_CALL(*mock_pss_->GetUserSettingsMock(),
           IsPassphraseRequiredForDecryption())
       .WillByDefault(Return(false));
@@ -1080,8 +1077,20 @@
   SetupInitializedProfileSyncService();
   ON_CALL(*mock_pss_->GetUserSettingsMock(), IsEncryptEverythingAllowed())
       .WillByDefault(Return(false));
+
+  base::DictionaryValue dict;
+  dict.SetBoolean("setNewPassphrase", true);
+  std::string args = GetConfiguration(&dict, SYNC_ALL_DATA, GetAllTypes(),
+                                      "password", ENCRYPT_ALL_DATA);
+  base::ListValue list_args;
+  list_args.AppendString(kTestCallbackId);
+  list_args.AppendString(args);
+
   EXPECT_CALL(*mock_pss_->GetUserSettingsMock(), EnableEncryptEverything())
       .Times(0);
+  EXPECT_CALL(*mock_pss_->GetUserSettingsMock(), SetEncryptionPassphrase(_))
+      .Times(0);
+
   handler_->HandleSetEncryption(&list_args);
 
   ExpectPageStatusResponse(PeopleHandler::kConfigurePageStatus);
diff --git a/chrome/browser/ui/webui/signin/inline_login_handler_impl.cc b/chrome/browser/ui/webui/signin/inline_login_handler_impl.cc
index 14cb1c7..0069276 100644
--- a/chrome/browser/ui/webui/signin/inline_login_handler_impl.cc
+++ b/chrome/browser/ui/webui/signin/inline_login_handler_impl.cc
@@ -304,16 +304,15 @@
       AboutSigninInternalsFactory::GetForProfile(profile_);
   about_signin_internals->OnRefreshTokenReceived("Successful");
 
-  // Prime the account tracker with this combination of gaia id/display email.
-  // TODO(crbug.com/922026): Migrate to AccountsMutator, figuring out how to
-  // integrate with the call to AddOrUpdateAccount() below.
-  AccountTrackerService* account_tracker_service =
-      AccountTrackerServiceFactory::GetForProfile(profile_);
-  std::string account_id =
-      account_tracker_service->SeedAccountInfo(gaia_id_, email_);
-
   identity::IdentityManager* identity_manager =
       IdentityManagerFactory::GetForProfile(profile_);
+
+  // Seed the account with this combination of gaia id/display email.
+  AccountInfo account_info;
+  account_info.gaia = gaia_id_;
+  account_info.email = email_;
+  identity_manager->LegacySeedAccountInfo(account_info);
+
   std::string primary_email = identity_manager->GetPrimaryAccountInfo().email;
   if (gaia::AreEmailsSame(email_, primary_email) &&
       (reason == signin_metrics::Reason::REASON_REAUTHENTICATION ||
diff --git a/chrome/common/chrome_features.cc b/chrome/common/chrome_features.cc
index 594214c..cc10e29 100644
--- a/chrome/common/chrome_features.cc
+++ b/chrome/common/chrome_features.cc
@@ -495,12 +495,6 @@
 const base::Feature kPushMessagingBackgroundMode{
     "PushMessagingBackgroundMode", base::FEATURE_DISABLED_BY_DEFAULT};
 
-#if defined(OS_CHROMEOS)
-// Enables permanent removal of Legacy Supervised Users on startup.
-const base::Feature kRemoveSupervisedUsersOnStartup{
-    "RemoveSupervisedUsersOnStartup", base::FEATURE_DISABLED_BY_DEFAULT};
-#endif
-
 const base::Feature kSafeSearchUrlReporting{"SafeSearchUrlReporting",
                                             base::FEATURE_DISABLED_BY_DEFAULT};
 
diff --git a/chrome/common/chrome_features.h b/chrome/common/chrome_features.h
index f617750..8919c3b 100644
--- a/chrome/common/chrome_features.h
+++ b/chrome/common/chrome_features.h
@@ -327,11 +327,6 @@
 COMPONENT_EXPORT(CHROME_FEATURES)
 extern const base::Feature kPushMessagingBackgroundMode;
 
-#if defined(OS_CHROMEOS)
-COMPONENT_EXPORT(CHROME_FEATURES)
-extern const base::Feature kRemoveSupervisedUsersOnStartup;
-#endif
-
 COMPONENT_EXPORT(CHROME_FEATURES)
 extern const base::Feature kSafeSearchUrlReporting;
 
diff --git a/chrome/common/webui_url_constants.cc b/chrome/common/webui_url_constants.cc
index e8615bd..e0f7d5e 100644
--- a/chrome/common/webui_url_constants.cc
+++ b/chrome/common/webui_url_constants.cc
@@ -235,6 +235,8 @@
 const char kChromeUIUserImageURL[] = "chrome://userimage/";
 const char kChromeUIAssistantOptInHost[] = "assistant-optin";
 const char kChromeUIAssistantOptInURL[] = "chrome://assistant-optin/";
+const char kChromeUIArcGraphicsTracingHost[] = "arc-graphics-tracing";
+const char kChromeUIArcGraphicsTracingURL[] = "chrome://arc-graphics-tracing/";
 #endif  // defined(OS_CHROMEOS)
 
 #if defined(OS_WIN)
diff --git a/chrome/common/webui_url_constants.h b/chrome/common/webui_url_constants.h
index 9cdf6ad9..287fbed 100644
--- a/chrome/common/webui_url_constants.h
+++ b/chrome/common/webui_url_constants.h
@@ -230,6 +230,8 @@
 extern const char kChromeUIUserImageURL[];
 extern const char kChromeUIAssistantOptInHost[];
 extern const char kChromeUIAssistantOptInURL[];
+extern const char kChromeUIArcGraphicsTracingHost[];
+extern const char kChromeUIArcGraphicsTracingURL[];
 #endif  // defined(OS_CHROMEOS)
 
 #if defined(OS_WIN)
diff --git a/chrome/installer/linux/common/rpm.include b/chrome/installer/linux/common/rpm.include
index d7b589cb..c815aff 100644
--- a/chrome/installer/linux/common/rpm.include
+++ b/chrome/installer/linux/common/rpm.include
@@ -192,7 +192,7 @@
   if [ -x "$LSB_RELEASE" ]; then
     RELEASE=$(lsb_release -i 2> /dev/null | sed 's/:\t/:/' | cut -d ':' -f 2-)
     case $RELEASE in
-    "Fedora")
+    "Fedora"|"Amazon")
       PACKAGEMANAGERS=(yum)
       ;;
     "Mageia")
@@ -231,6 +231,8 @@
 
   if [ -f "/etc/fedora-release" ] || [ -f "/etc/redhat-release" ]; then
     PACKAGEMANAGERS=(yum)
+  elif [ -f "/etc/system-release" ] && grep -Fq "Amazon Linux" "/etc/system-release"; then
+    PACKAGEMANAGERS=(yum)
   elif [ -f "/etc/SuSE-release" ]; then
     PACKAGEMANAGERS=(yast)
   elif [ -f "/etc/mandriva-release" ]; then
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 6e514d4..ed862d53 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -1787,7 +1787,6 @@
         "../browser/chromeos/login/users/avatar/user_image_manager_browsertest.cc",
         "../browser/chromeos/login/users/avatar/user_image_manager_test_util.cc",
         "../browser/chromeos/login/users/avatar/user_image_manager_test_util.h",
-        "../browser/chromeos/login/users/remove_supervised_users_browsertest.cc",
         "../browser/chromeos/login/users/user_manager_hide_supervised_users_browsertest.cc",
         "../browser/chromeos/login/users/wallpaper_policy_browsertest.cc",
         "../browser/chromeos/login/webview_login_browsertest.cc",
@@ -4428,12 +4427,7 @@
       "../browser/ui/views/toolbar/reload_button_unittest.cc",
       "../browser/ui/views/toolbar/toolbar_action_view_unittest.cc",
       "../browser/ui/views/toolbar/toolbar_actions_bar_bubble_views_unittest.cc",
-
-      # TODO(thestig): Rename toolbar_button_views_unittest.cc to
-      # toolbar_button_unittest.cc once toolbar_button_unittest.mm goes away.
-      # The 2 files cannot co-exist when they both try to generate
-      # toolbar_button_unittest.o.
-      "../browser/ui/views/toolbar/toolbar_button_views_unittest.cc",
+      "../browser/ui/views/toolbar/toolbar_button_unittest.cc",
       "../browser/ui/views/translate/translate_bubble_view_unittest.cc",
       "../browser/ui/views/webshare/webshare_target_picker_view_unittest.cc",
     ]
@@ -4555,6 +4549,11 @@
   if (enable_widevine && is_win) {
     sources += [ "../gpu/widevine_cdm_proxy_factory_unittest.cc" ]
   }
+
+  # Chrome desktop updater tests.
+  if (is_win || is_mac) {
+    deps += [ "//chrome/updater:updater_tests" ]
+  }
 }
 
 static_library("test_support_unit") {
diff --git a/chrome/test/base/browser_with_test_window_test.cc b/chrome/test/base/browser_with_test_window_test.cc
index 0f44f9c9..db76213 100644
--- a/chrome/test/base/browser_with_test_window_test.cc
+++ b/chrome/test/base/browser_with_test_window_test.cc
@@ -22,6 +22,7 @@
 #include "content/public/browser/navigation_entry.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/test/browser_side_navigation_test_utils.h"
+#include "content/public/test/navigation_simulator.h"
 #include "content/public/test/test_renderer_host.h"
 #include "ui/base/page_transition_types.h"
 #include "ui/views/test/test_views_delegate.h"
@@ -159,9 +160,8 @@
 void BrowserWithTestWindowTest::NavigateAndCommit(
     NavigationController* controller,
     const GURL& url) {
-  controller->LoadURL(
-      url, content::Referrer(), ui::PAGE_TRANSITION_LINK, std::string());
-  CommitPendingLoad(controller);
+  content::NavigationSimulator::NavigateAndCommitFromBrowser(
+      controller->GetWebContents(), url);
 }
 
 void BrowserWithTestWindowTest::NavigateAndCommitActiveTab(const GURL& url) {
diff --git a/chrome/updater/BUILD.gn b/chrome/updater/BUILD.gn
new file mode 100644
index 0000000..f7ecfb6
--- /dev/null
+++ b/chrome/updater/BUILD.gn
@@ -0,0 +1,34 @@
+# Copyright 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/chrome_build.gni")
+import("//build/config/sanitizers/sanitizers.gni")
+import("//testing/test.gni")
+
+group("updater") {
+  if (is_win) {
+    deps = [
+      "//chrome/updater/win",
+    ]
+  }
+}
+
+source_set("updater_tests") {
+  testonly = true
+  if (is_win) {
+    sources = [
+      "win/updater_unittest.cc",
+    ]
+
+    data_deps = [
+      "//chrome/updater/win:updater",
+    ]
+  }
+
+  deps = [
+    ":updater",
+    "//base/test:test_support",
+    "//testing/gtest",
+  ]
+}
diff --git a/chrome/updater/win/BUILD.gn b/chrome/updater/win/BUILD.gn
new file mode 100644
index 0000000..d89de9363
--- /dev/null
+++ b/chrome/updater/win/BUILD.gn
@@ -0,0 +1,32 @@
+# Copyright 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//chrome/process_version_rc_template.gni")
+
+group("win") {
+  deps = [
+    ":updater",
+  ]
+}
+
+executable("updater") {
+  sources = [
+    "main.cc",
+    "updater.rc",
+  ]
+
+  configs += [ "//build/config/win:windowed" ]
+
+  deps = [
+    ":version_resources",
+    "//build/win:default_exe_manifest",
+  ]
+}
+
+process_version_rc_template("version_resources") {
+  sources = [
+    "updater.ver",
+  ]
+  output = "$target_gen_dir/updater_exe.rc"
+}
diff --git a/chrome/updater/win/main.cc b/chrome/updater/win/main.cc
new file mode 100644
index 0000000..0937c5d
--- /dev/null
+++ b/chrome/updater/win/main.cc
@@ -0,0 +1,9 @@
+// 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 <windows.h>
+
+int APIENTRY wWinMain(HINSTANCE instance, HINSTANCE prev, wchar_t*, int) {
+  return 0;
+}
diff --git a/chrome/updater/win/updater.rc b/chrome/updater/win/updater.rc
new file mode 100644
index 0000000..b53c04b
--- /dev/null
+++ b/chrome/updater/win/updater.rc
@@ -0,0 +1,38 @@
+// Microsoft Visual C++ generated resource script.
+//
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "winres.h"
+#include "verrsrc.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (United States) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE
+BEGIN
+    "#include ""winres.h""\r\n"
+    "#include ""verrsrc.h""\r\n"
+    "\0"
+END
+
+#endif    // APSTUDIO_INVOKED
+
+#endif    // English (United States) resources
+
+/////////////////////////////////////////////////////////////////////////////
diff --git a/chrome/updater/win/updater.ver b/chrome/updater/win/updater.ver
new file mode 100644
index 0000000..bb2b1d63
--- /dev/null
+++ b/chrome/updater/win/updater.ver
@@ -0,0 +1,3 @@
+INTERNAL_NAME=updater_exe

+ORIGINAL_FILENAME=updater.exe

+PRODUCT_FULLNAME=updater

diff --git a/chrome/updater/win/updater_unittest.cc b/chrome/updater/win/updater_unittest.cc
new file mode 100644
index 0000000..e5a3093
--- /dev/null
+++ b/chrome/updater/win/updater_unittest.cc
@@ -0,0 +1,29 @@
+// 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 <windows.h>
+
+#include "base/command_line.h"
+#include "base/files/file_path.h"
+#include "base/process/launch.h"
+#include "base/process/process.h"
+#include "base/time/time.h"
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+// Tests the updater process returns 0 when run with no arguments.
+TEST(UpdaterTest, UpdaterExitCode) {
+  base::FilePath::CharType buffer[MAX_PATH] = {0};
+  ASSERT_NE(0u, GetModuleFileName(0, buffer, base::size(buffer)));
+  const base::FilePath updater =
+      base::FilePath(buffer).DirName().Append(FILE_PATH_LITERAL("updater.exe"));
+  base::LaunchOptions options;
+  options.start_hidden = true;
+  auto process = base::LaunchProcess(base::CommandLine(updater), options);
+  ASSERT_TRUE(process.IsValid());
+  int exit_code = -1;
+  EXPECT_TRUE(process.WaitForExitWithTimeout(base::TimeDelta::FromSeconds(60),
+                                             &exit_code));
+  EXPECT_EQ(0, exit_code);
+}
diff --git a/chromeos/account_manager/account_manager.cc b/chromeos/account_manager/account_manager.cc
index 92c0accf..8337f22 100644
--- a/chromeos/account_manager/account_manager.cc
+++ b/chromeos/account_manager/account_manager.cc
@@ -398,6 +398,14 @@
          it->second != kActiveDirectoryDummyToken;
 }
 
+bool AccountManager::HasDummyGaiaToken(const AccountKey& account_key) const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK_EQ(init_state_, InitializationState::kInitialized);
+
+  auto it = tokens_.find(account_key);
+  return it != tokens_.end() && it->second == kInvalidToken;
+}
+
 void AccountManager::MaybeRevokeTokenOnServer(const AccountKey& account_key) {
   auto it = tokens_.find(account_key);
   if (it == tokens_.end()) {
diff --git a/chromeos/account_manager/account_manager.h b/chromeos/account_manager/account_manager.h
index d2d1829..5e2065b 100644
--- a/chromeos/account_manager/account_manager.h
+++ b/chromeos/account_manager/account_manager.h
@@ -174,6 +174,12 @@
   // initialized yet.
   bool IsTokenAvailable(const AccountKey& account_key) const;
 
+  // Returns true if the token stored against |account_key| is a dummy Gaia
+  // token. This is meant to be used only by
+  // |ChromeOSOAuth2TokenServiceDelegate| to pre-emptively reject access token
+  // requests for |account_key|.
+  bool HasDummyGaiaToken(const AccountKey& account_key) const;
+
  private:
   enum InitializationState {
     kNotStarted,   // Initialize has not been called
diff --git a/components/arc/arc_features.cc b/components/arc/arc_features.cc
index cc26303..0e1b8c2 100644
--- a/components/arc/arc_features.cc
+++ b/components/arc/arc_features.cc
@@ -43,6 +43,10 @@
 const base::Feature kFilePickerExperimentFeature{
     "ArcFilePickerExperiment", base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Controls experimental ARC graphic buffers visualization tools.
+const base::Feature kGraphicBuffersVisualizationTool{
+    "ArcGraphicBuffersVisualizationTool", base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Controls experimental native bridge feature for ARC.
 const base::Feature kNativeBridgeExperimentFeature {
     "ArcNativeBridgeExperiment", base::FEATURE_ENABLED_BY_DEFAULT
diff --git a/components/arc/arc_features.h b/components/arc/arc_features.h
index ad3c7f0..0fea34b 100644
--- a/components/arc/arc_features.h
+++ b/components/arc/arc_features.h
@@ -20,6 +20,7 @@
 extern const base::Feature kEnableRegularToChildTransitionFeature;
 extern const base::Feature kEnableUnifiedAudioFocusFeature;
 extern const base::Feature kFilePickerExperimentFeature;
+extern const base::Feature kGraphicBuffersVisualizationTool;
 extern const base::Feature kNativeBridgeExperimentFeature;
 extern const base::Feature kSmartTextSelectionFeature;
 extern const base::Feature kUsbHostFeature;
diff --git a/components/autofill/core/browser/personal_data_manager.cc b/components/autofill/core/browser/personal_data_manager.cc
index 119f18e..5b723bb 100644
--- a/components/autofill/core/browser/personal_data_manager.cc
+++ b/components/autofill/core/browser/personal_data_manager.cc
@@ -1004,6 +1004,11 @@
   bool update_validation =
       pref_service_->GetInteger(prefs::kAutofillLastVersionValidated) <
       CHROME_VERSION_MAJOR;
+
+  DVLOG(1) << "Autofill profile client validation "
+           << (update_validation ? "needs to be" : "has already been")
+           << " performed for this version";
+
   for (const auto* profile : profiles) {
     if (!profile->is_client_validity_states_updated() || update_validation) {
       profile->set_is_client_validity_states_updated(false);
@@ -1108,39 +1113,6 @@
   return profiles;
 }
 
-// static
-void PersonalDataManager::MaybeRemoveInvalidSuggestions(
-    const AutofillType& type,
-    std::vector<AutofillProfile*>* profiles) {
-  const bool suggest_invalid = base::FeatureList::IsEnabled(
-      features::kAutofillSuggestInvalidProfileData);
-
-  for (size_t i = 0; i < profiles->size(); ++i) {
-    bool is_client_invalid =
-        (*profiles)[i]->GetValidityState(type.GetStorableType(),
-                                         AutofillProfile::CLIENT) ==
-        AutofillProfile::INVALID;
-
-    bool is_server_invalid =
-        (*profiles)[i]->GetValidityState(type.GetStorableType(),
-                                         AutofillProfile::SERVER) ==
-        AutofillProfile::INVALID;
-
-    if ((is_server_invalid || is_client_invalid) && !suggest_invalid)
-      (*profiles)[i] = nullptr;
-    if (is_server_invalid || is_client_invalid)
-      UMA_HISTOGRAM_BOOLEAN("Autofill.InvalidProfileData.UsedForSuggestion",
-                            suggest_invalid);
-  }
-
-  if (!suggest_invalid) {
-    profiles->erase(
-        std::stable_partition(profiles->begin(), profiles->end(),
-                              [](AutofillProfile* p) { return p != nullptr; }),
-        profiles->end());
-  }
-}
-
 std::vector<Suggestion> PersonalDataManager::GetProfileSuggestions(
     const AutofillType& type,
     const base::string16& field_contents,
@@ -1166,9 +1138,6 @@
       suggestion_selection::RemoveProfilesNotUsedSinceTimestamp(
           min_last_used, &sorted_profiles);
     }
-    // We need the updated information on the validity states of the profiles.
-    UpdateProfilesServerValidityMapsIfNeeded(sorted_profiles);
-    MaybeRemoveInvalidSuggestions(type, &sorted_profiles);
   }
 
   std::vector<AutofillProfile*> matched_profiles;
@@ -2576,6 +2545,9 @@
 }
 
 void PersonalDataManager::ApplyAddressFixesAndCleanups() {
+  // Validate profiles once per major.
+  UpdateClientValidityStates(GetProfiles());
+
   // One-time fix, otherwise NOP.
   RemoveOrphanAutofillTableRows();
 
diff --git a/components/autofill/core/browser/personal_data_manager_unittest.cc b/components/autofill/core/browser/personal_data_manager_unittest.cc
index f2230dc..2ec682e 100644
--- a/components/autofill/core/browser/personal_data_manager_unittest.cc
+++ b/components/autofill/core/browser/personal_data_manager_unittest.cc
@@ -2274,121 +2274,6 @@
   }
 }
 
-// Tests that suggestions based on invalid data are handled correctly.
-TEST_F(PersonalDataManagerTest,
-       GetProfileSuggestions_InvalidDataBasedOnClient) {
-  // The prefs are empty, therefore, there is no server validation.
-  // Set up 2 different profiles: one valid and one invalid.
-  AutofillProfile invalid_profile(base::GenerateGUID(), test::kEmptyOrigin);
-  test::SetProfileInfo(&invalid_profile, "Marion1", "Mitchell", "Morrison",
-                       "johnwayne@me.xyz", "Fox",
-                       "123 Zoo St.\nSecond Line\nThird line", "unit 5",
-                       "Hollywood", "CA", "91601", "US", "Invalid Phone");
-  invalid_profile.set_use_date(AutofillClock::Now() -
-                               base::TimeDelta::FromDays(20));
-  AddProfileToPersonalDataManager(invalid_profile);
-
-  AutofillProfile valid_profile(test::GetFullValidProfileForCanada());
-  AddProfileToPersonalDataManager(valid_profile);
-
-  ResetPersonalDataManager(USER_MODE_NORMAL);
-  {
-    base::HistogramTester histogram_tester;
-    base::test::ScopedFeatureList scoped_features;
-    scoped_features.InitAndDisableFeature(
-        features::kAutofillSuggestInvalidProfileData);
-    std::vector<Suggestion> suggestions = personal_data_->GetProfileSuggestions(
-        AutofillType(PHONE_HOME_WHOLE_NUMBER), base::string16(), false,
-        std::vector<ServerFieldType>());
-    ASSERT_EQ(1U, suggestions.size());
-    EXPECT_EQ(base::ASCIIToUTF16("+15141112233"), suggestions[0].value);
-    histogram_tester.ExpectUniqueSample(
-        "Autofill.InvalidProfileData.UsedForSuggestion", false, 1);
-  }
-
-  {
-    base::HistogramTester histogram_tester;
-    base::test::ScopedFeatureList scoped_features;
-    scoped_features.InitAndEnableFeature(
-        features::kAutofillSuggestInvalidProfileData);
-    std::vector<Suggestion> suggestions = personal_data_->GetProfileSuggestions(
-        AutofillType(PHONE_HOME_WHOLE_NUMBER), base::string16(), false,
-        std::vector<ServerFieldType>());
-    ASSERT_EQ(2U, suggestions.size());
-    EXPECT_EQ(base::ASCIIToUTF16("+15141112233"), suggestions[0].value);
-    EXPECT_EQ(base::ASCIIToUTF16("Invalid Phone"), suggestions[1].value);
-    histogram_tester.ExpectUniqueSample(
-        "Autofill.InvalidProfileData.UsedForSuggestion", true, 1);
-  }
-}
-
-// Tests that suggestions based on invalid data are handled correctly.
-TEST_F(PersonalDataManagerTest,
-       GetProfileSuggestions_InvalidDataBasedOnServer) {
-  // Set up 2 different profiles.
-  AutofillProfile profile1(base::GenerateGUID(), test::kEmptyOrigin);
-  test::SetProfileInfo(&profile1, "Marion1", "Mitchell", "Morrison",
-                       "johnwayne@me.xyz", "Fox",
-                       "123 Zoo St.\nSecond Line\nThird line", "unit 5",
-                       "Hollywood", "CA", "91601", "US", "9876543210");
-  // Set the validity state of ADDRESS_HOME_STATE to INVALID on the prefs.
-  {
-    ProfileValidityMap profile_validity_map;
-    UserProfileValidityMap user_profile_validity_map;
-    std::string autofill_profile_validity;
-    personal_data_->pref_service_->SetString(prefs::kAutofillProfileValidity,
-                                             autofill_profile_validity);
-    (*profile_validity_map.mutable_field_validity_states())[static_cast<int>(
-        ADDRESS_HOME_STATE)] = static_cast<int>(AutofillProfile::INVALID);
-    (*user_profile_validity_map.mutable_profile_validity())[profile1.guid()] =
-        profile_validity_map;
-    ASSERT_TRUE(user_profile_validity_map.SerializeToString(
-        &autofill_profile_validity));
-    base::Base64Encode(autofill_profile_validity, &autofill_profile_validity);
-    personal_data_->pref_service_->SetString(prefs::kAutofillProfileValidity,
-                                             autofill_profile_validity);
-  }
-  profile1.set_use_date(AutofillClock::Now() - base::TimeDelta::FromDays(20));
-  AddProfileToPersonalDataManager(profile1);
-
-  AutofillProfile profile2(base::GenerateGUID(), test::kEmptyOrigin);
-  test::SetProfileInfo(&profile2, "Marion2", "Mitchell", "Morrison",
-                       "johnwayne@me.xyz", "Fox",
-                       "456 Zoo St.\nSecond Line\nThird line", "unit 5",
-                       "Hollywood", "NY", "91601", "US", "1234567890");
-  AddProfileToPersonalDataManager(profile2);
-
-  ResetPersonalDataManager(USER_MODE_NORMAL);
-  {
-    base::HistogramTester histogram_tester;
-    base::test::ScopedFeatureList scoped_features;
-    scoped_features.InitAndDisableFeature(
-        features::kAutofillSuggestInvalidProfileData);
-    std::vector<Suggestion> suggestions = personal_data_->GetProfileSuggestions(
-        AutofillType(ADDRESS_HOME_STATE), base::string16(), false,
-        std::vector<ServerFieldType>());
-    ASSERT_EQ(1U, suggestions.size());
-    EXPECT_EQ(base::ASCIIToUTF16("NY"), suggestions[0].value);
-    histogram_tester.ExpectUniqueSample(
-        "Autofill.InvalidProfileData.UsedForSuggestion", false, 1);
-  }
-
-  {
-    base::HistogramTester histogram_tester;
-    base::test::ScopedFeatureList scoped_features;
-    scoped_features.InitAndEnableFeature(
-        features::kAutofillSuggestInvalidProfileData);
-    std::vector<Suggestion> suggestions = personal_data_->GetProfileSuggestions(
-        AutofillType(ADDRESS_HOME_STATE), base::string16(), false,
-        std::vector<ServerFieldType>());
-    ASSERT_EQ(2U, suggestions.size());
-    EXPECT_EQ(base::ASCIIToUTF16("CA"), suggestions[1].value);
-    EXPECT_EQ(base::ASCIIToUTF16("NY"), suggestions[0].value);
-    histogram_tester.ExpectUniqueSample(
-        "Autofill.InvalidProfileData.UsedForSuggestion", true, 1);
-  }
-}
-
 // Test that local and server profiles are not shown if
 // |kAutofillProfileEnabled| is set to |false|.
 TEST_F(PersonalDataManagerTest, GetProfileSuggestions_ProfileAutofillDisabled) {
@@ -7144,24 +7029,48 @@
   AddProfileToPersonalDataManager(profile1);
 
   auto profiles = personal_data_->GetProfiles();
+  ASSERT_EQ(2U, profiles.size());
 
-  // Pretend that the validity states are updated.
-  profiles[0]->set_is_client_validity_states_updated(true);
-  profiles[1]->set_is_client_validity_states_updated(true);
+  EXPECT_TRUE(profiles[0]->is_client_validity_states_updated());
+  EXPECT_TRUE(profiles[1]->is_client_validity_states_updated());
+  EXPECT_EQ(CHROME_VERSION_MAJOR, GetLastVersionValidatedUpdate());
 
-  // Should validate regardless of the validity update flag, because of the
-  // major version update.
-  ResetAutofillLastVersionValidated();
+  // No validation as both validity update flags are true, and the validation
+  // version is set to this version.
+  base::RunLoop run_loop;
+  EXPECT_CALL(*personal_data_, OnValidated(testing::_)).Times(0);
+  EXPECT_CALL(personal_data_observer_, OnPersonalDataChanged()).Times(0);
+  personal_data_->UpdateClientValidityStates(profiles);
 
   profiles = personal_data_->GetProfiles();
   ASSERT_EQ(2U, profiles.size());
+  ResetAutofillLastVersionValidated();
 
-  UpdateClientValidityStatesOnPersonalDataManager(profiles);
+  EXPECT_EQ(0, GetLastVersionValidatedUpdate());
+  EXPECT_TRUE(profiles[0]->is_client_validity_states_updated());
+  EXPECT_TRUE(profiles[1]->is_client_validity_states_updated());
+
+  // Should validate regardless of the validity update flag, because of the
+  // major version update.
+  EXPECT_CALL(*personal_data_, OnValidated(testing::_)).Times(2);
+  ON_CALL(*personal_data_, OnValidated(testing::_))
+      .WillByDefault(testing::Invoke(personal_data_.get(),
+                                     &PersonalDataManagerMock::OnValidatedPDM));
+
+  EXPECT_CALL(personal_data_observer_, OnPersonalDataFinishedProfileTasks())
+      .WillRepeatedly(QuitMessageLoop(&run_loop));
+  EXPECT_CALL(personal_data_observer_, OnPersonalDataChanged()).Times(2);
+  // Validate the profiles through the client validation API.
+  personal_data_->UpdateClientValidityStates(profiles);
+  run_loop.Run();
 
   profiles = personal_data_->GetProfiles();
   ASSERT_EQ(2U, profiles.size());
   ASSERT_EQ(profiles[0]->guid(), profile1.guid());
 
+  // Verify that the version of the last update is set to this version.
+  EXPECT_EQ(CHROME_VERSION_MAJOR, GetLastVersionValidatedUpdate());
+
   EXPECT_EQ(AutofillProfile::VALID,
             profiles[0]->GetValidityState(ADDRESS_HOME_COUNTRY,
                                           AutofillProfile::CLIENT));
@@ -7175,9 +7084,6 @@
   EXPECT_EQ(AutofillProfile::INVALID,
             profiles[1]->GetValidityState(ADDRESS_HOME_STATE,
                                           AutofillProfile::CLIENT));
-
-  // Verify that the version of the last update is set to this version.
-  EXPECT_EQ(CHROME_VERSION_MAJOR, GetLastVersionValidatedUpdate());
 }
 
 // Verifies that the profiles are validated when added, updated.
diff --git a/components/autofill/core/common/autofill_features.cc b/components/autofill/core/common/autofill_features.cc
index aa2e7caf..a062e8b 100644
--- a/components/autofill/core/common/autofill_features.cc
+++ b/components/autofill/core/common/autofill_features.cc
@@ -253,8 +253,8 @@
 const base::Feature kAutofillSkipComparingInferredLabels{
     "AutofillSkipComparingInferredLabels", base::FEATURE_DISABLED_BY_DEFAULT};
 
-const base::Feature kAutofillSuggestInvalidProfileData{
-    "AutofillSuggestInvalidProfileData", base::FEATURE_ENABLED_BY_DEFAULT};
+const base::Feature kAutofillProfileServerValidation{
+    "AutofillProfileServerValidation", base::FEATURE_DISABLED_BY_DEFAULT};
 
 const base::Feature kAutofillSuppressDisusedAddresses{
     "AutofillSuppressDisusedAddresses", base::FEATURE_ENABLED_BY_DEFAULT};
diff --git a/components/autofill/core/common/autofill_features.h b/components/autofill/core/common/autofill_features.h
index 977b4bc..e767b0fd 100644
--- a/components/autofill/core/common/autofill_features.h
+++ b/components/autofill/core/common/autofill_features.h
@@ -68,7 +68,7 @@
 extern const base::Feature kAutofillShowAutocompleteConsoleWarnings;
 extern const base::Feature kAutofillShowTypePredictions;
 extern const base::Feature kAutofillSkipComparingInferredLabels;
-extern const base::Feature kAutofillSuggestInvalidProfileData;
+extern const base::Feature kAutofillProfileServerValidation;
 extern const base::Feature kAutofillSuppressDisusedAddresses;
 extern const base::Feature kAutofillSuppressDisusedCreditCards;
 extern const base::Feature kAutofillUploadThrottling;
diff --git a/components/autofill_assistant/browser/controller.cc b/components/autofill_assistant/browser/controller.cc
index 7e1dcf2..721b514 100644
--- a/components/autofill_assistant/browser/controller.cc
+++ b/components/autofill_assistant/browser/controller.cc
@@ -319,6 +319,7 @@
       DLOG(ERROR) << "Unexpected value for at_end: " << result.at_end;
       break;
   }
+  EnterState(AutofillAssistantState::PROMPT);
   GetOrCheckScripts(web_contents()->GetLastCommittedURL());
 }
 
diff --git a/components/autofill_assistant/browser/controller_unittest.cc b/components/autofill_assistant/browser/controller_unittest.cc
index aea610c1..689e163 100644
--- a/components/autofill_assistant/browser/controller_unittest.cc
+++ b/components/autofill_assistant/browser/controller_unittest.cc
@@ -513,6 +513,7 @@
 
   EXPECT_EQ(AutofillAssistantState::STOPPED, GetUiDelegate()->GetState());
   EXPECT_THAT(states_, ElementsAre(AutofillAssistantState::RUNNING,
+                                   AutofillAssistantState::PROMPT,
                                    AutofillAssistantState::STOPPED));
 }
 }  // namespace autofill_assistant
diff --git a/components/browser_sync/profile_sync_service.cc b/components/browser_sync/profile_sync_service.cc
index 0c159f1..40b223b 100644
--- a/components/browser_sync/profile_sync_service.cc
+++ b/components/browser_sync/profile_sync_service.cc
@@ -966,8 +966,8 @@
 
   data_type_manager_ =
       sync_client_->GetSyncApiComponentFactory()->CreateDataTypeManager(
-          initial_types, debug_info_listener, &data_type_controllers_, this,
-          engine_.get(), this);
+          initial_types, debug_info_listener, &data_type_controllers_,
+          user_settings_.get(), engine_.get(), this);
 
   crypto_.SetSyncEngine(engine_.get());
 
diff --git a/components/browser_sync/profile_sync_service.h b/components/browser_sync/profile_sync_service.h
index 74818b5..1d824c9 100644
--- a/components/browser_sync/profile_sync_service.h
+++ b/components/browser_sync/profile_sync_service.h
@@ -269,9 +269,10 @@
       const syncer::DataTypeManager::ConfigureResult& result) override;
   void OnConfigureStart() override;
 
-  // DataTypeEncryptionHandler implementation.
-  bool IsPassphraseRequired() const override;
-  syncer::ModelTypeSet GetEncryptedDataTypes() const override;
+  // TODO(crbug.com/884159): Remove these; they should be queried via
+  // SyncUserSettings instead.
+  bool IsPassphraseRequired() const;
+  syncer::ModelTypeSet GetEncryptedDataTypes() const;
 
   // IdentityManager::Observer implementation.
   void OnAccountsInCookieUpdated(
diff --git a/components/browser_sync/profile_sync_service_mock.cc b/components/browser_sync/profile_sync_service_mock.cc
index d465720..8c0004d 100644
--- a/components/browser_sync/profile_sync_service_mock.cc
+++ b/components/browser_sync/profile_sync_service_mock.cc
@@ -45,10 +45,6 @@
   return user_settings_.IsUsingSecondaryPassphrase();
 }
 
-bool ProfileSyncServiceMock::IsPassphraseRequired() const {
-  return user_settings_.IsPassphraseRequired();
-}
-
 std::unique_ptr<syncer::SyncSetupInProgressHandle>
 ProfileSyncServiceMock::GetSetupInProgressHandleConcrete() {
   return browser_sync::ProfileSyncService::GetSetupInProgressHandle();
diff --git a/components/browser_sync/profile_sync_service_mock.h b/components/browser_sync/profile_sync_service_mock.h
index 1f292be..be370f65 100644
--- a/components/browser_sync/profile_sync_service_mock.h
+++ b/components/browser_sync/profile_sync_service_mock.h
@@ -80,9 +80,6 @@
                void(const syncer::DataTypeManager::ConfigureResult&));
   MOCK_METHOD0(OnConfigureStart, void());
 
-  // DataTypeEncryptionHandler overrides.
-  bool IsPassphraseRequired() const override;
-
   // syncer::UnrecoverableErrorHandler overrides.
   MOCK_METHOD2(OnUnrecoverableError,
                void(const base::Location& location,
diff --git a/components/browser_sync/sync_user_settings_impl.h b/components/browser_sync/sync_user_settings_impl.h
index 5ef1ea4e..f92028bb 100644
--- a/components/browser_sync/sync_user_settings_impl.h
+++ b/components/browser_sync/sync_user_settings_impl.h
@@ -7,10 +7,9 @@
 
 #include <string>
 
-#include "components/sync/driver/sync_user_settings.h"
-
 #include "base/callback.h"
 #include "components/sync/base/model_type.h"
+#include "components/sync/driver/sync_user_settings.h"
 
 namespace syncer {
 class SyncPrefs;
@@ -48,6 +47,7 @@
   bool IsEncryptEverythingEnabled() const override;
   void EnableEncryptEverything() override;
 
+  syncer::ModelTypeSet GetEncryptedDataTypes() const override;
   bool IsPassphraseRequired() const override;
   bool IsPassphraseRequiredForDecryption() const override;
   bool IsUsingSecondaryPassphrase() const override;
@@ -58,7 +58,6 @@
   bool SetDecryptionPassphrase(const std::string& passphrase) override;
 
   syncer::ModelTypeSet GetPreferredDataTypes() const;
-  syncer::ModelTypeSet GetEncryptedDataTypes() const;
   bool IsEncryptedDatatypeEnabled() const;
   bool IsEncryptionPending() const;
 
diff --git a/components/browser_sync/sync_user_settings_mock.h b/components/browser_sync/sync_user_settings_mock.h
index 2d5b6351..8d98ff1 100644
--- a/components/browser_sync/sync_user_settings_mock.h
+++ b/components/browser_sync/sync_user_settings_mock.h
@@ -35,6 +35,7 @@
   MOCK_CONST_METHOD0(IsEncryptEverythingEnabled, bool());
   MOCK_METHOD0(EnableEncryptEverything, void());
 
+  MOCK_CONST_METHOD0(GetEncryptedDataTypes, syncer::ModelTypeSet());
   MOCK_CONST_METHOD0(IsPassphraseRequired, bool());
   MOCK_CONST_METHOD0(IsPassphraseRequiredForDecryption, bool());
   MOCK_CONST_METHOD0(IsUsingSecondaryPassphrase, bool());
diff --git a/components/payments/content/payment_request.cc b/components/payments/content/payment_request.cc
index 081d4394..b410cc7 100644
--- a/components/payments/content/payment_request.cc
+++ b/components/payments/content/payment_request.cc
@@ -379,7 +379,7 @@
   }
 }
 
-void PaymentRequest::HasEnrolledInstrument() {
+void PaymentRequest::HasEnrolledInstrument(bool per_method_quota) {
   if (!IsInitialized()) {
     log_.Error("Attempted hasEnrolledInstrument without initialization");
     OnConnectionTerminated();
@@ -393,11 +393,12 @@
 
   if (!delegate_->GetPrefService()->GetBoolean(kCanMakePaymentEnabled) ||
       !state_) {
-    HasEnrolledInstrumentCallback(/*has_enrolled_instrument=*/false);
+    HasEnrolledInstrumentCallback(per_method_quota,
+                                  /*has_enrolled_instrument=*/false);
   } else {
     state_->HasEnrolledInstrument(
         base::BindOnce(&PaymentRequest::HasEnrolledInstrumentCallback,
-                       weak_ptr_factory_.GetWeakPtr()));
+                       weak_ptr_factory_.GetWeakPtr(), per_method_quota));
   }
 }
 
@@ -568,11 +569,13 @@
 }
 
 void PaymentRequest::HasEnrolledInstrumentCallback(
+    bool per_method_quota,
     bool has_enrolled_instrument) {
-  if (!spec_ || CanMakePaymentQueryFactory::GetInstance()
-                    ->GetForContext(web_contents_->GetBrowserContext())
-                    ->CanQuery(top_level_origin_, frame_origin_,
-                               spec_->stringified_method_data())) {
+  if (!spec_ ||
+      CanMakePaymentQueryFactory::GetInstance()
+          ->GetForContext(web_contents_->GetBrowserContext())
+          ->CanQuery(top_level_origin_, frame_origin_,
+                     spec_->stringified_method_data(), per_method_quota)) {
     RespondToHasEnrolledInstrumentQuery(has_enrolled_instrument,
                                         /*warn_localhost_or_file=*/false);
   } else if (OriginSecurityChecker::IsOriginLocalhostOrFile(frame_origin_)) {
diff --git a/components/payments/content/payment_request.h b/components/payments/content/payment_request.h
index 1752553..78b35a0c 100644
--- a/components/payments/content/payment_request.h
+++ b/components/payments/content/payment_request.h
@@ -75,7 +75,7 @@
   void Abort() override;
   void Complete(mojom::PaymentComplete result) override;
   void CanMakePayment() override;
-  void HasEnrolledInstrument() override;
+  void HasEnrolledInstrument(bool per_method_quota) override;
 
   // PaymentRequestSpec::Observer:
   void OnSpecUpdated() override {}
@@ -148,7 +148,8 @@
 
   // The callback for PaymentRequestState::HasEnrolledInstrument. Checks for
   // query quota and may send QUERY_QUOTA_EXCEEDED.
-  void HasEnrolledInstrumentCallback(bool has_enrolled_instrument);
+  void HasEnrolledInstrumentCallback(bool per_method_quota,
+                                     bool has_enrolled_instrument);
 
   // The callback for PaymentRequestState::AreRequestedMethodsSupported.
   void AreRequestedMethodsSupportedCallback(bool methods_supported);
diff --git a/components/payments/core/can_make_payment_query.cc b/components/payments/core/can_make_payment_query.cc
index 181bb66..b7a41bb6 100644
--- a/components/payments/core/can_make_payment_query.cc
+++ b/components/payments/core/can_make_payment_query.cc
@@ -15,8 +15,6 @@
 #include "components/payments/core/features.h"
 #include "url/gurl.h"
 
-#include "base/logging.h"
-
 namespace payments {
 namespace {
 
@@ -48,52 +46,75 @@
 bool CanMakePaymentQuery::CanQuery(
     const GURL& top_level_origin,
     const GURL& frame_origin,
-    const std::map<std::string, std::set<std::string>>& query) {
-  if (base::FeatureList::IsEnabled(
+    const std::map<std::string, std::set<std::string>>& query,
+    bool per_method_quota) {
+  // Check both with and without per-method quota, so that both queries are
+  // recorded in case if two different tabs of the same website run with and
+  // without the origin trial.
+  bool can_query_with_per_method_quota =
+      CanQueryWithPerMethodQuota(top_level_origin, frame_origin, query);
+
+  bool can_query_without_per_method_quota =
+      CanQueryWithoutPerMethodQuota(top_level_origin, frame_origin, query);
+
+  if (per_method_quota ||
+      base::FeatureList::IsEnabled(
           features::kWebPaymentsPerMethodCanMakePaymentQuota)) {
-    bool can_query = true;
-    for (const auto& method_and_params : query) {
-      std::string method = method_and_params.first;
-      std::set<std::string> params = method_and_params.second;
-      SimpleNormalize(&method, &params);
-
-      const std::string id =
-          frame_origin.spec() + ":" + top_level_origin.spec() + ":" + method;
-
-      auto it = per_method_queries_.find(id);
-      if (it == per_method_queries_.end()) {
-        auto timer = std::make_unique<base::OneShotTimer>();
-        timer->Start(
-            FROM_HERE, base::TimeDelta::FromMinutes(30),
-            base::BindOnce(
-                &CanMakePaymentQuery::ExpireQuotaForFrameOriginAndMethod,
-                base::Unretained(this), id));
-        timers_.insert(std::make_pair(id, std::move(timer)));
-        per_method_queries_.insert(std::make_pair(id, params));
-        continue;
-      }
-
-      can_query &= it->second == params;
-    }
-
-    return can_query;
-  } else {
-    const std::string id = frame_origin.spec() + ":" + top_level_origin.spec();
-
-    const auto& it = queries_.find(id);
-    if (it == queries_.end()) {
-      auto timer = std::make_unique<base::OneShotTimer>();
-      timer->Start(
-          FROM_HERE, base::TimeDelta::FromMinutes(30),
-          base::BindOnce(&CanMakePaymentQuery::ExpireQuotaForFrameOrigin,
-                         base::Unretained(this), id));
-      timers_.insert(std::make_pair(id, std::move(timer)));
-      queries_.insert(std::make_pair(id, query));
-      return true;
-    }
-
-    return it->second == query;
+    return can_query_with_per_method_quota;
   }
+
+  return can_query_without_per_method_quota;
+}
+
+bool CanMakePaymentQuery::CanQueryWithPerMethodQuota(
+    const GURL& top_level_origin,
+    const GURL& frame_origin,
+    const std::map<std::string, std::set<std::string>>& query) {
+  bool can_query = true;
+  for (const auto& method_and_params : query) {
+    std::string method = method_and_params.first;
+    std::set<std::string> params = method_and_params.second;
+    SimpleNormalize(&method, &params);
+
+    const std::string id =
+        frame_origin.spec() + ":" + top_level_origin.spec() + ":" + method;
+
+    auto it = per_method_queries_.find(id);
+    if (it == per_method_queries_.end()) {
+      auto timer = std::make_unique<base::OneShotTimer>();
+      timer->Start(FROM_HERE, base::TimeDelta::FromMinutes(30),
+                   base::BindOnce(
+                       &CanMakePaymentQuery::ExpireQuotaForFrameOriginAndMethod,
+                       base::Unretained(this), id));
+      timers_.insert(std::make_pair(id, std::move(timer)));
+      per_method_queries_.insert(std::make_pair(id, params));
+      continue;
+    }
+
+    can_query &= it->second == params;
+  }
+
+  return can_query;
+}
+
+bool CanMakePaymentQuery::CanQueryWithoutPerMethodQuota(
+    const GURL& top_level_origin,
+    const GURL& frame_origin,
+    const std::map<std::string, std::set<std::string>>& query) {
+  const std::string id = frame_origin.spec() + ":" + top_level_origin.spec();
+
+  const auto& it = queries_.find(id);
+  if (it == queries_.end()) {
+    auto timer = std::make_unique<base::OneShotTimer>();
+    timer->Start(FROM_HERE, base::TimeDelta::FromMinutes(30),
+                 base::BindOnce(&CanMakePaymentQuery::ExpireQuotaForFrameOrigin,
+                                base::Unretained(this), id));
+    timers_.insert(std::make_pair(id, std::move(timer)));
+    queries_.insert(std::make_pair(id, query));
+    return true;
+  }
+
+  return it->second == query;
 }
 
 void CanMakePaymentQuery::ExpireQuotaForFrameOrigin(const std::string& id) {
diff --git a/components/payments/core/can_make_payment_query.h b/components/payments/core/can_make_payment_query.h
index 6c9e6002..8c7be07 100644
--- a/components/payments/core/can_make_payment_query.h
+++ b/components/payments/core/can_make_payment_query.h
@@ -42,15 +42,23 @@
   // also allowed.
   bool CanQuery(const GURL& top_level_origin,
                 const GURL& frame_origin,
-                const std::map<std::string, std::set<std::string>>& query);
+                const std::map<std::string, std::set<std::string>>& query,
+                bool per_method_quota);
 
  private:
+  bool CanQueryWithPerMethodQuota(
+      const GURL& top_level_origin,
+      const GURL& frame_origin,
+      const std::map<std::string, std::set<std::string>>& query);
+  bool CanQueryWithoutPerMethodQuota(
+      const GURL& top_level_origin,
+      const GURL& frame_origin,
+      const std::map<std::string, std::set<std::string>>& query);
   void ExpireQuotaForFrameOrigin(const std::string& id);
   void ExpireQuotaForFrameOriginAndMethod(const std::string& id);
 
   // A mapping of an identififer to the timer that, when fired, allows the frame
-  // to invoke canMakePayment() with different payment methods specific
-  // parameters.
+  // to invoke canMakePayment() with the same identifier again.
   std::map<std::string, std::unique_ptr<base::OneShotTimer>> timers_;
 
   // A mapping of frame origin and top level origin to its last query. Each
diff --git a/components/payments/core/can_make_payment_query_unittest.cc b/components/payments/core/can_make_payment_query_unittest.cc
index ca4be0d..90d7a1b3 100644
--- a/components/payments/core/can_make_payment_query_unittest.cc
+++ b/components/payments/core/can_make_payment_query_unittest.cc
@@ -4,9 +4,7 @@
 
 #include "components/payments/core/can_make_payment_query.h"
 
-#include "base/test/scoped_feature_list.h"
 #include "base/test/scoped_task_environment.h"
-#include "components/payments/core/features.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
 
@@ -26,9 +24,11 @@
 TEST_F(CanMakePaymentQueryTest,
        SameHttpsOriginCannotQueryTwoDifferentCardNetworks) {
   EXPECT_TRUE(guard_.CanQuery(GURL("https://example.com"),
-                              GURL("https://example.com"), {{"amex", {}}}));
+                              GURL("https://example.com"), {{"amex", {}}},
+                              /*per_method_quota=*/true));
   EXPECT_FALSE(guard_.CanQuery(GURL("https://example.com"),
-                               GURL("https://example.com"), {{"visa", {}}}));
+                               GURL("https://example.com"), {{"visa", {}}},
+                               /*per_method_quota=*/true));
 }
 
 // A localhost website is not allowed to query all of the networks of the cards
@@ -36,9 +36,11 @@
 TEST_F(CanMakePaymentQueryTest,
        SameLocalhostOriginCannotQueryTwoDifferentCardNetworks) {
   EXPECT_TRUE(guard_.CanQuery(GURL("http://localhost:8080"),
-                              GURL("http://localhost:8080"), {{"amex", {}}}));
+                              GURL("http://localhost:8080"), {{"amex", {}}},
+                              /*per_method_quota=*/true));
   EXPECT_FALSE(guard_.CanQuery(GURL("http://localhost:8080"),
-                               GURL("http://localhost:8080"), {{"visa", {}}}));
+                               GURL("http://localhost:8080"), {{"visa", {}}},
+                               /*per_method_quota=*/true));
 }
 
 // A file website is not allowed to query all of the networks of the cards in
@@ -46,9 +48,11 @@
 TEST_F(CanMakePaymentQueryTest,
        SameFileOriginCannotQueryTwoDifferentCardNetworks) {
   EXPECT_TRUE(guard_.CanQuery(GURL("file:///tmp/test.html"),
-                              GURL("file:///tmp/test.html"), {{"amex", {}}}));
+                              GURL("file:///tmp/test.html"), {{"amex", {}}},
+                              /*per_method_quota=*/true));
   EXPECT_FALSE(guard_.CanQuery(GURL("file:///tmp/test.html"),
-                               GURL("file:///tmp/test.html"), {{"visa", {}}}));
+                               GURL("file:///tmp/test.html"), {{"visa", {}}},
+                               /*per_method_quota=*/true));
 }
 
 // Different HTTPS websites are allowed to query different card networks in
@@ -56,9 +60,11 @@
 TEST_F(CanMakePaymentQueryTest,
        DifferentHttpsOriginsCanQueryTwoDifferentCardNetworks) {
   EXPECT_TRUE(guard_.CanQuery(GURL("https://example.com"),
-                              GURL("https://example.com"), {{"amex", {}}}));
+                              GURL("https://example.com"), {{"amex", {}}},
+                              /*per_method_quota=*/true));
   EXPECT_TRUE(guard_.CanQuery(GURL("https://not-example.com"),
-                              GURL("https://not-example.com"), {{"visa", {}}}));
+                              GURL("https://not-example.com"), {{"visa", {}}},
+                              /*per_method_quota=*/true));
 }
 
 // Different localhost websites are allowed to query different card networks in
@@ -66,9 +72,11 @@
 TEST_F(CanMakePaymentQueryTest,
        DifferentLocalhostOriginsCanQueryTwoDifferentCardNetworks) {
   EXPECT_TRUE(guard_.CanQuery(GURL("http://localhost:8080"),
-                              GURL("http://localhost:8080"), {{"amex", {}}}));
+                              GURL("http://localhost:8080"), {{"amex", {}}},
+                              /*per_method_quota=*/true));
   EXPECT_TRUE(guard_.CanQuery(GURL("http://localhost:9090"),
-                              GURL("http://localhost:9090"), {{"visa", {}}}));
+                              GURL("http://localhost:9090"), {{"visa", {}}},
+                              /*per_method_quota=*/true));
 }
 
 // Different file websites are allowed to query different card networks in
@@ -76,31 +84,32 @@
 TEST_F(CanMakePaymentQueryTest,
        DifferentFileOriginsCanQueryTwoDifferentCardNetworks) {
   EXPECT_TRUE(guard_.CanQuery(GURL("file:///tmp/test.html"),
-                              GURL("file:///tmp/test.html"), {{"amex", {}}}));
+                              GURL("file:///tmp/test.html"), {{"amex", {}}},
+                              /*per_method_quota=*/true));
   EXPECT_TRUE(guard_.CanQuery(GURL("file:///tmp/not-test.html"),
-                              GURL("file:///tmp/not-test.html"),
-                              {{"visa", {}}}));
+                              GURL("file:///tmp/not-test.html"), {{"visa", {}}},
+                              /*per_method_quota=*/true));
 }
 
 // The same website is not allowed to query the same payment method with
 // different parameters.
 TEST_F(CanMakePaymentQueryTest,
        SameOriginCannotQueryBasicCardWithTwoDifferentCardNetworks) {
-  base::test::ScopedFeatureList features;
-  features.InitAndEnableFeature(
-      features::kWebPaymentsPerMethodCanMakePaymentQuota);
   EXPECT_TRUE(
       guard_.CanQuery(GURL("https://example.com"), GURL("https://example.com"),
                       {{"basic-card", {"{supportedNetworks: ['visa']}"}},
-                       {"https://alicepay.com", {"{alicePayParameter: 1}"}}}));
+                       {"https://alicepay.com", {"{alicePayParameter: 1}"}}},
+                      /*per_method_quota=*/true));
   EXPECT_TRUE(
       guard_.CanQuery(GURL("https://example.com"), GURL("https://example.com"),
                       {{"basic-card", {"{supportedNetworks: ['visa']}"}},
-                       {"https://bobpay.com", {"{bobPayParameter: 2}"}}}));
+                       {"https://bobpay.com", {"{bobPayParameter: 2}"}}},
+                      /*per_method_quota=*/true));
   EXPECT_FALSE(
       guard_.CanQuery(GURL("https://example.com"), GURL("https://example.com"),
                       {{"basic-card", {"{supportedNetworks: ['amex']}"}},
-                       {"https://bobpay.com", {"{bobPayParameter: 2}"}}}));
+                       {"https://bobpay.com", {"{bobPayParameter: 2}"}}},
+                      /*per_method_quota=*/true));
 }
 
 // Two different websites are allowed to query the same payment method with
@@ -109,64 +118,145 @@
        DifferentOriginsCanQueryBasicCardWithTwoDifferentCardNetworks) {
   EXPECT_TRUE(
       guard_.CanQuery(GURL("https://example.com"), GURL("https://example.com"),
-                      {{"basic-card", {"{supportedNetworks: ['visa']}"}}}));
+                      {{"basic-card", {"{supportedNetworks: ['visa']}"}}},
+                      /*per_method_quota=*/true));
   EXPECT_TRUE(guard_.CanQuery(
       GURL("https://not-example.com"), GURL("https://not-example.com"),
-      {{"basic-card", {"{supportedNetworks: ['amex']}"}}}));
+      {{"basic-card", {"{supportedNetworks: ['amex']}"}}},
+      /*per_method_quota=*/true));
 }
 
 // A website can query several different payment methods, as long as each
 // payment method is queried with the same payment-method-specific data.
 TEST_F(CanMakePaymentQueryTest,
        SameOriginCanQuerySeveralDifferentPaymentMethodIdentifiers) {
-  base::test::ScopedFeatureList features;
-  features.InitAndEnableFeature(
-      features::kWebPaymentsPerMethodCanMakePaymentQuota);
   EXPECT_TRUE(
       guard_.CanQuery(GURL("https://example.com"), GURL("https://example.com"),
                       {{"basic-card", {"{supportedNetworks: ['visa']}"}},
-                       {"https://alicepay.com", {"{alicePayParameter: 1}"}}}));
+                       {"https://alicepay.com", {"{alicePayParameter: 1}"}}},
+                      /*per_method_quota=*/true));
   EXPECT_TRUE(
       guard_.CanQuery(GURL("https://example.com"), GURL("https://example.com"),
                       {{"https://alicepay.com", {"{alicePayParameter: 1}"}},
-                       {"https://bobpay.com", {"{bobPayParameter: 2}"}}}));
+                       {"https://bobpay.com", {"{bobPayParameter: 2}"}}},
+                      /*per_method_quota=*/true));
   EXPECT_TRUE(
       guard_.CanQuery(GURL("https://example.com"), GURL("https://example.com"),
                       {{"https://bobpay.com", {"{bobPayParameter: 2}"}},
-                       {"basic-card", {"{supportedNetworks: ['visa']}"}}}));
+                       {"basic-card", {"{supportedNetworks: ['visa']}"}}},
+                      /*per_method_quota=*/true));
 }
 
 // A website cannot query several different payment methods without the
-// per-method query feature, even if method-specific data remains unchanged.
+// per-method quota, even if method-specific data remains unchanged.
 TEST_F(CanMakePaymentQueryTest,
        SameOriginCannotQueryDifferentMethodsWithoutPerMethodQuota) {
-  base::test::ScopedFeatureList features;
-  features.InitAndDisableFeature(
-      features::kWebPaymentsPerMethodCanMakePaymentQuota);
   EXPECT_TRUE(
       guard_.CanQuery(GURL("https://example.com"), GURL("https://example.com"),
                       {{"basic-card", {"{supportedNetworks: ['visa']}"}},
-                       {"https://alicepay.com", {"{alicePayParameter: 1}"}}}));
+                       {"https://alicepay.com", {"{alicePayParameter: 1}"}}},
+                      /*per_method_quota=*/false));
   EXPECT_FALSE(
       guard_.CanQuery(GURL("https://example.com"), GURL("https://example.com"),
                       {{"https://alicepay.com", {"{alicePayParameter: 1}"}},
-                       {"https://bobpay.com", {"{bobPayParameter: 2}"}}}));
+                       {"https://bobpay.com", {"{bobPayParameter: 2}"}}},
+                      /*per_method_quota=*/false));
   EXPECT_FALSE(
       guard_.CanQuery(GURL("https://example.com"), GURL("https://example.com"),
                       {{"https://bobpay.com", {"{bobPayParameter: 2}"}},
-                       {"basic-card", {"{supportedNetworks: ['visa']}"}}}));
+                       {"basic-card", {"{supportedNetworks: ['visa']}"}}},
+                      /*per_method_quota=*/false));
+}
+
+// An instance of a website with per-method quota enabled (e.g., through an
+// origin trial) can query different payment methods, as long as each payment
+// method is queried with the same method-specific data. Another instance of the
+// same website (e.g., in a different tab) without the per-method quota feature
+// cannot query different payment methods.
+TEST_F(CanMakePaymentQueryTest, SameWebsiteDifferentQuotaPolicy) {
+  // First instance of https://example.com has per-method quota feature enabled
+  // and so can query different payment methods, as long as the method-specific
+  // data stays the same.
+  EXPECT_TRUE(
+      guard_.CanQuery(GURL("https://example.com"), GURL("https://example.com"),
+                      {{"basic-card", {"{supportedNetworks: ['visa']}"}},
+                       {"https://alicepay.com", {"{alicePayParameter: 1}"}}},
+                      /*per_method_quota=*/true));
+  EXPECT_TRUE(
+      guard_.CanQuery(GURL("https://example.com"), GURL("https://example.com"),
+                      {{"https://alicepay.com", {"{alicePayParameter: 1}"}},
+                       {"https://bobpay.com", {"{bobPayParameter: 2}"}}},
+                      /*per_method_quota=*/true));
+  EXPECT_TRUE(
+      guard_.CanQuery(GURL("https://example.com"), GURL("https://example.com"),
+                      {{"https://bobpay.com", {"{bobPayParameter: 2}"}},
+                       {"basic-card", {"{supportedNetworks: ['visa']}"}}},
+                      /*per_method_quota=*/true));
+
+  // Second instance of https://example.com has per-method quota feature
+  // disabled and so can only repeat the first query.
+  EXPECT_FALSE(
+      guard_.CanQuery(GURL("https://example.com"), GURL("https://example.com"),
+                      {{"https://alicepay.com", {"{alicePayParameter: 1}"}},
+                       {"https://bobpay.com", {"{bobPayParameter: 2}"}}},
+                      /*per_method_quota=*/false));
+  EXPECT_TRUE(
+      guard_.CanQuery(GURL("https://example.com"), GURL("https://example.com"),
+                      {{"basic-card", {"{supportedNetworks: ['visa']}"}},
+                       {"https://alicepay.com", {"{alicePayParameter: 1}"}}},
+                      /*per_method_quota=*/false));
+  EXPECT_FALSE(
+      guard_.CanQuery(GURL("https://example.com"), GURL("https://example.com"),
+                      {{"https://bobpay.com", {"{bobPayParameter: 2}"}},
+                       {"basic-card", {"{supportedNetworks: ['visa']}"}}},
+                      /*per_method_quota=*/false));
+
+  // The two website queries can be interleaved any number of times in any order
+  // with the same results.
+  EXPECT_TRUE(
+      guard_.CanQuery(GURL("https://example.com"), GURL("https://example.com"),
+                      {{"basic-card", {"{supportedNetworks: ['visa']}"}},
+                       {"https://alicepay.com", {"{alicePayParameter: 1}"}}},
+                      /*per_method_quota=*/true));
+  EXPECT_TRUE(
+      guard_.CanQuery(GURL("https://example.com"), GURL("https://example.com"),
+                      {{"https://bobpay.com", {"{bobPayParameter: 2}"}},
+                       {"basic-card", {"{supportedNetworks: ['visa']}"}}},
+                      /*per_method_quota=*/true));
+  EXPECT_FALSE(
+      guard_.CanQuery(GURL("https://example.com"), GURL("https://example.com"),
+                      {{"https://alicepay.com", {"{alicePayParameter: 1}"}},
+                       {"https://bobpay.com", {"{bobPayParameter: 2}"}}},
+                      /*per_method_quota=*/false));
+  EXPECT_TRUE(
+      guard_.CanQuery(GURL("https://example.com"), GURL("https://example.com"),
+                      {{"basic-card", {"{supportedNetworks: ['visa']}"}},
+                       {"https://alicepay.com", {"{alicePayParameter: 1}"}}},
+                      /*per_method_quota=*/false));
+  EXPECT_TRUE(
+      guard_.CanQuery(GURL("https://example.com"), GURL("https://example.com"),
+                      {{"https://alicepay.com", {"{alicePayParameter: 1}"}},
+                       {"https://bobpay.com", {"{bobPayParameter: 2}"}}},
+                      /*per_method_quota=*/true));
+  EXPECT_FALSE(
+      guard_.CanQuery(GURL("https://example.com"), GURL("https://example.com"),
+                      {{"https://bobpay.com", {"{bobPayParameter: 2}"}},
+                       {"basic-card", {"{supportedNetworks: ['visa']}"}}},
+                      /*per_method_quota=*/false));
 }
 
 // A website is not allowed to query all of the networks of the cards in user's
-// autofill database, even if they vary the format of the query.
+// autofill database, even if the website varies the format of the query.
 TEST_F(
     CanMakePaymentQueryTest,
     SameOriginCannotQueryDifferentCardNetworksAndTypesUsingDifferentFormats) {
   EXPECT_TRUE(guard_.CanQuery(GURL("https://example.com"),
-                              GURL("https://example.com"), {{"amex", {}}}));
+                              GURL("https://example.com"), {{"amex", {}}},
+                              /*per_method_quota=*/true));
   EXPECT_FALSE(
       guard_.CanQuery(GURL("https://example.com"), GURL("https://example.com"),
-                      {{"basic-card", {"{supportedNetworks: ['visa']}"}}}));
+                      {{"basic-card", {"{supportedNetworks: ['visa']}"}}},
+                      /*per_method_quota=*/true));
 }
 
 }  // namespace
diff --git a/components/policy/resources/policy_templates.json b/components/policy/resources/policy_templates.json
index bb76880a..af24d6e 100644
--- a/components/policy/resources/policy_templates.json
+++ b/components/policy/resources/policy_templates.json
@@ -11334,7 +11334,7 @@
           'hash': { 'type': 'string' }
         },
       },
-      'supported_on': ['chrome_os:66-'],
+      'supported_on': ['chrome_os:73-'],
       'features': {
         'dynamic_refresh': True,
         'per_profile': False,
@@ -11367,7 +11367,7 @@
       'name': 'DeviceNativePrintersAccessMode',
       'id': 387,
       'device_only': True,
-      'supported_on': ['chrome_os:66-'],
+      'supported_on': ['chrome_os:73-'],
       'type': 'int-enum',
       'schema': {
         'type': 'integer',
@@ -11406,7 +11406,7 @@
     },
     {
       'name': 'DeviceNativePrintersBlacklist',
-      'supported_on': ['chrome_os:66-'],
+      'supported_on': ['chrome_os:73-'],
       'device_only': True,
       'id': 388,
       'type': 'list',
@@ -11429,7 +11429,7 @@
     },
     {
       'name': 'DeviceNativePrintersWhitelist',
-      'supported_on': ['chrome_os:66-'],
+      'supported_on': ['chrome_os:73-'],
       'device_only': True,
       'id': 389,
       'type': 'list',
diff --git a/components/signin/core/browser/account_reconcilor_unittest.cc b/components/signin/core/browser/account_reconcilor_unittest.cc
index 0ac578b3..4ea81bf2 100644
--- a/components/signin/core/browser/account_reconcilor_unittest.cc
+++ b/components/signin/core/browser/account_reconcilor_unittest.cc
@@ -251,7 +251,7 @@
 }  // namespace
 
 class AccountReconcilorTest : public ::testing::Test {
- public:
+ protected:
   AccountReconcilorTest();
   ~AccountReconcilorTest() override;
 
@@ -260,7 +260,7 @@
   }
   DiceTestSigninClient* test_signin_client() { return &test_signin_client_; }
   AccountTrackerService* account_tracker() { return &account_tracker_; }
-  FakeGaiaCookieManagerService* cookie_manager_service() {
+  GaiaCookieManagerService* cookie_manager_service() {
     return &cookie_manager_service_;
   }
   base::HistogramTester* histogram_tester() { return &histogram_tester_; }
@@ -295,6 +295,8 @@
 
   void DeleteReconcilor() { mock_reconcilor_.reset(); }
 
+  network::TestURLLoaderFactory test_url_loader_factory_;
+
  private:
   base::test::ScopedTaskEnvironment task_environment_;
   signin::AccountConsistencyMethod account_consistency_;
@@ -302,7 +304,6 @@
   DiceTestSigninClient test_signin_client_;
   FakeProfileOAuth2TokenService token_service_;
   AccountTrackerService account_tracker_;
-  network::TestURLLoaderFactory test_url_loader_factory_;
   FakeGaiaCookieManagerService cookie_manager_service_;
   FakeSigninManagerForTesting signin_manager_;
   identity::IdentityTestEnvironment identity_test_env_;
@@ -376,7 +377,7 @@
       prefs::kTokenServiceDiceCompatible, false);
 
   account_tracker_.Initialize(&pref_service_, base::FilePath());
-  cookie_manager_service_.SetListAccountsResponseHttpNotFound();
+  signin::SetListAccountsResponseHttpNotFound(&test_url_loader_factory_);
   signin_manager_.Initialize(nullptr);
 
   // The reconcilor should not be built before the test can set the account
@@ -650,7 +651,8 @@
                                cookie.is_valid, false /* signed_out */,
                                true /* verified */});
     }
-    cookie_manager_service()->SetListAccountsResponseWithParams(cookie_params);
+    signin::SetListAccountsResponseWithParams(cookie_params,
+                                              &test_url_loader_factory_);
     cookie_manager_service()->set_list_accounts_stale_for_testing(true);
   }
 
@@ -1169,7 +1171,7 @@
   // Add a token in Chrome but do not sign in.
   const std::string account_id =
       identity_test_env()->MakeAccountAvailable("user@gmail.com").account_id;
-  cookie_manager_service()->SetListAccountsResponseNoAccounts();
+  signin::SetListAccountsResponseNoAccounts(&test_url_loader_factory_);
 
   if (!IsMultiloginEnabled()) {
     EXPECT_CALL(*GetMockReconcilor(), PerformMergeAction(account_id));
@@ -1201,7 +1203,7 @@
 // cookie.
 TEST_P(AccountReconcilorDiceEndpointParamTest, DiceReconcileNoop) {
   // No Chrome account and no cookie.
-  cookie_manager_service()->SetListAccountsResponseNoAccounts();
+  signin::SetListAccountsResponseNoAccounts(&test_url_loader_factory_);
   EXPECT_CALL(*GetMockReconcilor(), PerformMergeAction(testing::_)).Times(0);
   EXPECT_CALL(*GetMockReconcilor(), PerformLogoutAllAccountsAction()).Times(0);
   EXPECT_CALL(*GetMockReconcilor(), PerformSetCookiesAction(testing::_))
@@ -1235,8 +1237,9 @@
 
   // Add accounts 2 and 3 to the Gaia cookie.
   const std::string account_id_3 = SeedAccountInfo("9999", "foo@gmail.com");
-  cookie_manager_service()->SetListAccountsResponseTwoAccounts(
-      account_info_2.email, account_info_2.gaia, "foo@gmail.com", "9999");
+  signin::SetListAccountsResponseTwoAccounts(
+      account_info_2.email, account_info_2.gaia, "foo@gmail.com", "9999",
+      &test_url_loader_factory_);
 
   if (!IsMultiloginEnabled()) {
     testing::InSequence mock_sequence;
@@ -1281,9 +1284,9 @@
   AccountInfo account_info_2 =
       identity_test_env()->MakeAccountAvailable("other@gmail.com");
   const std::string account_id_2 = account_info_2.account_id;
-  cookie_manager_service()->SetListAccountsResponseTwoAccounts(
+  signin::SetListAccountsResponseTwoAccounts(
       account_info_2.email, account_info_2.gaia, account_info_1.email,
-      account_info_1.gaia);
+      account_info_1.gaia, &test_url_loader_factory_);
 
   auto* identity_manager = identity_test_env()->identity_manager();
   std::vector<AccountInfo> accounts =
@@ -1310,7 +1313,7 @@
   }
 
   // Delete the cookies.
-  cookie_manager_service()->SetListAccountsResponseNoAccounts();
+  signin::SetListAccountsResponseNoAccounts(&test_url_loader_factory_);
   cookie_manager_service()->set_list_accounts_stale_for_testing(true);
 
   if (!IsMultiloginEnabled()) {
@@ -1353,9 +1356,10 @@
 // Checks that the reconcilor does not log out unverified accounts.
 TEST_P(AccountReconcilorDiceEndpointParamTest, UnverifiedAccountNoop) {
   // Add a unverified account to the Gaia cookie.
-  cookie_manager_service()->SetListAccountsResponseOneAccountWithParams(
+  signin::SetListAccountsResponseOneAccountWithParams(
       {"user@gmail.com", "12345", true /* valid */, false /* signed_out */,
-       false /* verified */});
+       false /* verified */},
+      &test_url_loader_factory_);
 
   // Check that nothing happens.
   EXPECT_CALL(*GetMockReconcilor(), PerformMergeAction(testing::_)).Times(0);
@@ -1375,9 +1379,10 @@
 // a new account to the Gaia cookie.
 TEST_P(AccountReconcilorDiceEndpointParamTest, UnverifiedAccountMerge) {
   // Add a unverified account to the Gaia cookie.
-  cookie_manager_service()->SetListAccountsResponseOneAccountWithParams(
+  signin::SetListAccountsResponseOneAccountWithParams(
       {"user@gmail.com", "12345", true /* valid */, false /* signed_out */,
-       false /* verified */});
+       false /* verified */},
+      &test_url_loader_factory_);
 
   // Add a token to Chrome.
   const std::string chrome_account_id =
@@ -1424,8 +1429,8 @@
   // Chrome account is consistent with the cookie.
   AccountInfo account_info =
       identity_test_env()->MakeAccountAvailable("user@gmail.com");
-  cookie_manager_service()->SetListAccountsResponseOneAccount(
-      account_info.email, account_info.gaia);
+  signin::SetListAccountsResponseOneAccount(
+      account_info.email, account_info.gaia, &test_url_loader_factory_);
   AccountReconcilor* reconcilor = GetMockReconcilor();
   // Dice is not enabled by default.
   EXPECT_FALSE(reconcilor->delegate_->IsAccountConsistencyEnforced());
@@ -1454,8 +1459,8 @@
   // Chrome account is consistent with the cookie.
   AccountInfo account_info =
       identity_test_env()->MakeAccountAvailable("user@gmail.com");
-  cookie_manager_service()->SetListAccountsResponseOneAccount(
-      account_info.email, account_info.gaia);
+  signin::SetListAccountsResponseOneAccount(
+      account_info.email, account_info.gaia, &test_url_loader_factory_);
   AccountReconcilor* reconcilor = GetMockReconcilor();
   // Dice is not enabled by default.
   EXPECT_FALSE(reconcilor->delegate_->IsAccountConsistencyEnforced());
@@ -1484,7 +1489,7 @@
   // Add a token in Chrome.
   const std::string account_id =
       ConnectProfileToAccount("user@gmail.com").account_id;
-  cookie_manager_service()->SetListAccountsResponseNoAccounts();
+  signin::SetListAccountsResponseNoAccounts(&test_url_loader_factory_);
   AccountReconcilor* reconcilor = GetMockReconcilor();
 
   // Dice is not enabled by default.
@@ -1526,7 +1531,7 @@
       ConnectProfileToAccount("user@gmail.com").account_id;
   const std::string account_id_2 =
       identity_test_env()->MakeAccountAvailable("other@gmail.com").account_id;
-  cookie_manager_service()->SetListAccountsResponseNoAccounts();
+  signin::SetListAccountsResponseNoAccounts(&test_url_loader_factory_);
 
   auto* identity_manager = identity_test_env()->identity_manager();
   ASSERT_TRUE(identity_manager->HasAccountWithRefreshToken(account_id_1));
@@ -1576,7 +1581,7 @@
       identity_test_env()->MakeAccountAvailable("user@gmail.com").account_id;
   const std::string account_id_2 =
       identity_test_env()->MakeAccountAvailable("other@gmail.com").account_id;
-  cookie_manager_service()->SetListAccountsResponseNoAccounts();
+  signin::SetListAccountsResponseNoAccounts(&test_url_loader_factory_);
 
   auto* identity_manager = identity_test_env()->identity_manager();
   ASSERT_TRUE(identity_manager->HasAccountWithRefreshToken(account_id_1));
@@ -1833,7 +1838,7 @@
 TEST_P(AccountReconcilorMirrorEndpointParamTest, TokensNotLoaded) {
   const std::string account_id =
       ConnectProfileToAccount("user@gmail.com").account_id;
-  cookie_manager_service()->SetListAccountsResponseNoAccounts();
+  signin::SetListAccountsResponseNoAccounts(&test_url_loader_factory_);
   identity_test_env()->ResetToAccountsNotYetLoadedFromDiskState();
 
   AccountReconcilor* reconcilor = GetMockReconcilor();
@@ -1872,9 +1877,10 @@
 TEST_P(AccountReconcilorMirrorEndpointParamTest, GetAccountsFromCookieSuccess) {
   AccountInfo account_info = ConnectProfileToAccount("user@gmail.com");
   const std::string account_id = account_info.account_id;
-  cookie_manager_service()->SetListAccountsResponseOneAccountWithParams(
+  signin::SetListAccountsResponseOneAccountWithParams(
       {account_info.email, account_info.gaia, false /* valid */,
-       false /* signed_out */, true /* verified */});
+       false /* signed_out */, true /* verified */},
+      &test_url_loader_factory_);
 
   if (!IsMultiloginEnabled()) {
     EXPECT_CALL(*GetMockReconcilor(), PerformMergeAction(account_id));
@@ -1906,7 +1912,7 @@
 
 TEST_P(AccountReconcilorMirrorEndpointParamTest, GetAccountsFromCookieFailure) {
   ConnectProfileToAccount("user@gmail.com");
-  cookie_manager_service()->SetListAccountsResponseWebLoginRequired();
+  signin::SetListAccountsResponseWebLoginRequired(&test_url_loader_factory_);
 
   AccountReconcilor* reconcilor = GetMockReconcilor();
   ASSERT_TRUE(reconcilor);
@@ -1936,8 +1942,8 @@
       account_info.email, account_info.gaia, false /* valid */,
       false /* signed_out */, true /* verified */};
 
-  cookie_manager_service()->SetListAccountsResponseOneAccountWithParams(
-      cookie_params);
+  signin::SetListAccountsResponseOneAccountWithParams(
+      cookie_params, &test_url_loader_factory_);
 
   if (!IsMultiloginEnabled()) {
     EXPECT_CALL(*GetMockReconcilor(), PerformMergeAction(account_id));
@@ -1981,8 +1987,8 @@
   AccountReconcilor* reconcilor = GetMockReconcilor();
   ASSERT_TRUE(reconcilor);
 
-  cookie_manager_service()->SetListAccountsResponseOneAccount(
-      account_info.email, account_info.gaia);
+  signin::SetListAccountsResponseOneAccount(
+      account_info.email, account_info.gaia, &test_url_loader_factory_);
 
   reconcilor->StartReconcile();
   ASSERT_TRUE(reconcilor->is_reconcile_started_);
@@ -2101,8 +2107,8 @@
   }
 
   AccountInfo account_info = ConnectProfileToAccount("Dot.S@gmail.com");
-  cookie_manager_service()->SetListAccountsResponseOneAccount(
-      account_info.email, account_info.gaia);
+  signin::SetListAccountsResponseOneAccount(
+      account_info.email, account_info.gaia, &test_url_loader_factory_);
   AccountReconcilor* reconcilor = GetMockReconcilor();
   ASSERT_TRUE(reconcilor);
 
@@ -2121,9 +2127,9 @@
   AccountInfo account_info = ConnectProfileToAccount("user@gmail.com");
   AccountInfo account_info_2 =
       identity_test_env()->MakeAccountAvailable("other@gmail.com");
-  cookie_manager_service()->SetListAccountsResponseTwoAccounts(
+  signin::SetListAccountsResponseTwoAccounts(
       account_info.email, account_info.gaia, account_info_2.email,
-      account_info_2.gaia);
+      account_info_2.gaia, &test_url_loader_factory_);
 
   AccountReconcilor* reconcilor = GetMockReconcilor();
   ASSERT_TRUE(reconcilor);
@@ -2145,8 +2151,8 @@
   AccountInfo account_info = ConnectProfileToAccount("user@gmail.com");
   const std::string account_id = account_info.account_id;
   identity_test_env()->SetRefreshTokenForAccount(account_id);
-  cookie_manager_service()->SetListAccountsResponseOneAccount(
-      account_info.email, account_info.gaia);
+  signin::SetListAccountsResponseOneAccount(
+      account_info.email, account_info.gaia, &test_url_loader_factory_);
 
   const std::string account_id2 =
       identity_test_env()->MakeAccountAvailable("other@gmail.com").account_id;
@@ -2224,8 +2230,8 @@
   AccountReconcilor* reconcilor = GetMockReconcilor();
   base::RunLoop().RunUntilIdle();
   ASSERT_FALSE(reconcilor->is_reconcile_started_);
-  cookie_manager_service()->SetListAccountsResponseOneAccount(
-      account_info.email, account_info.gaia);
+  signin::SetListAccountsResponseOneAccount(
+      account_info.email, account_info.gaia, &test_url_loader_factory_);
   if (account_consistency == signin::AccountConsistencyMethod::kDice) {
     EXPECT_CALL(*GetMockReconcilor(), PerformLogoutAllAccountsAction())
         .Times(1);
@@ -2256,8 +2262,8 @@
   AccountInfo account_info = ConnectProfileToAccount("user@gmail.com");
   const std::string account_id = account_info.account_id;
   identity_test_env()->SetRefreshTokenForAccount(account_id);
-  cookie_manager_service()->SetListAccountsResponseOneAccount(
-      account_info.email, account_info.gaia);
+  signin::SetListAccountsResponseOneAccount(
+      account_info.email, account_info.gaia, &test_url_loader_factory_);
 
   const std::string account_id2 =
       identity_test_env()->MakeAccountAvailable("other@gmail.com").account_id;
@@ -2305,8 +2311,9 @@
   AccountInfo account_info = ConnectProfileToAccount("user@gmail.com");
   const std::string account_id = account_info.account_id;
   identity_test_env()->SetRefreshTokenForAccount(account_id);
-  cookie_manager_service()->SetListAccountsResponseTwoAccounts(
-      account_info.email, account_info.gaia, "other@gmail.com", "12345");
+  signin::SetListAccountsResponseTwoAccounts(
+      account_info.email, account_info.gaia, "other@gmail.com", "12345",
+      &test_url_loader_factory_);
 
   if (!IsMultiloginEnabled()) {
     EXPECT_CALL(*GetMockReconcilor(), PerformLogoutAllAccountsAction());
@@ -2352,8 +2359,9 @@
       identity_test_env()->identity_manager(), account_info.account_id,
       GoogleServiceAuthError(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS));
 
-  cookie_manager_service()->SetListAccountsResponseTwoAccounts(
-      account_info.email, account_info.gaia, "other@gmail.com", "67890");
+  signin::SetListAccountsResponseTwoAccounts(
+      account_info.email, account_info.gaia, "other@gmail.com", "67890",
+      &test_url_loader_factory_);
 
   AccountReconcilor* reconcilor = GetMockReconcilor();
   reconcilor->StartReconcile();
@@ -2371,8 +2379,8 @@
   const std::string account_id2 = account_info2.account_id;
   const std::string account_id3 = SeedAccountInfo("34567", "third@gmail.com");
 
-  cookie_manager_service()->SetListAccountsResponseOneAccount(
-      account_info.email, account_info.gaia);
+  signin::SetListAccountsResponseOneAccount(
+      account_info.email, account_info.gaia, &test_url_loader_factory_);
 
   if (!IsMultiloginEnabled()) {
     EXPECT_CALL(*GetMockReconcilor(), PerformMergeAction(account_id2));
@@ -2410,9 +2418,9 @@
   }
 
   // Do another pass after I've added a third account to the token service
-  cookie_manager_service()->SetListAccountsResponseTwoAccounts(
+  signin::SetListAccountsResponseTwoAccounts(
       account_info.email, account_info.gaia, account_info2.email,
-      account_info2.gaia);
+      account_info2.gaia, &test_url_loader_factory_);
   cookie_manager_service()->set_list_accounts_stale_for_testing(true);
 
   // This will cause the reconcilor to fire.
@@ -2462,9 +2470,9 @@
   AccountInfo account_info2 =
       identity_test_env()->MakeAccountAvailable("other@gmail.com");
   const std::string account_id2 = account_info2.account_id;
-  cookie_manager_service()->SetListAccountsResponseTwoAccounts(
+  signin::SetListAccountsResponseTwoAccounts(
       account_info2.email, account_info2.gaia, account_info.email,
-      account_info.gaia);
+      account_info.gaia, &test_url_loader_factory_);
 
   if (!IsMultiloginEnabled()) {
     EXPECT_CALL(*GetMockReconcilor(), PerformLogoutAllAccountsAction());
@@ -2508,8 +2516,8 @@
 
 TEST_P(AccountReconcilorMirrorEndpointParamTest, StartReconcileOnlyOnce) {
   AccountInfo account_info = ConnectProfileToAccount("user@gmail.com");
-  cookie_manager_service()->SetListAccountsResponseOneAccount(
-      account_info.email, account_info.gaia);
+  signin::SetListAccountsResponseOneAccount(
+      account_info.email, account_info.gaia, &test_url_loader_factory_);
 
   AccountReconcilor* reconcilor = GetMockReconcilor();
   ASSERT_TRUE(reconcilor);
@@ -2524,8 +2532,8 @@
 
 TEST_P(AccountReconcilorMirrorEndpointParamTest, Lock) {
   AccountInfo account_info = ConnectProfileToAccount("user@gmail.com");
-  cookie_manager_service()->SetListAccountsResponseOneAccount(
-      account_info.email, account_info.gaia);
+  signin::SetListAccountsResponseOneAccount(
+      account_info.email, account_info.gaia, &test_url_loader_factory_);
 
   AccountReconcilor* reconcilor = GetMockReconcilor();
   ASSERT_TRUE(reconcilor);
@@ -2602,11 +2610,12 @@
   AccountInfo account_info2 =
       identity_test_env()->MakeAccountAvailable("other@gmail.com");
   const std::string account_id2 = account_info2.account_id;
-  cookie_manager_service()->SetListAccountsResponseWithParams(
+  signin::SetListAccountsResponseWithParams(
       {{account_info.email, account_info.gaia, false /* valid */,
         false /* signed_out */, true /* verified */},
        {account_info2.email, account_info2.gaia, true /* valid */,
-        false /* signed_out */, true /* verified */}});
+        false /* signed_out */, true /* verified */}},
+      &test_url_loader_factory_);
 
   EXPECT_CALL(*GetMockReconcilor(), PerformMergeAction(account_id));
 
@@ -2627,9 +2636,10 @@
        AddAccountToCookieCompletedWithBogusAccount) {
   AccountInfo account_info = ConnectProfileToAccount("user@gmail.com");
   const std::string account_id = account_info.account_id;
-  cookie_manager_service()->SetListAccountsResponseOneAccountWithParams(
+  signin::SetListAccountsResponseOneAccountWithParams(
       {account_info.email, account_info.gaia, false /* valid */,
-       false /* signed_out */, true /* verified */});
+       false /* signed_out */, true /* verified */},
+      &test_url_loader_factory_);
 
   if (!IsMultiloginEnabled()) {
     EXPECT_CALL(*GetMockReconcilor(), PerformMergeAction(account_id));
@@ -2682,9 +2692,10 @@
     EXPECT_CALL(*GetMockReconcilor(), PerformSetCookiesAction(params));
   }
   // The primary account is in auth error, so it is not in the cookie.
-  cookie_manager_service()->SetListAccountsResponseOneAccountWithParams(
+  signin::SetListAccountsResponseOneAccountWithParams(
       {account_info2.email, account_info2.gaia, false /* valid */,
-       false /* signed_out */, true /* verified */});
+       false /* signed_out */, true /* verified */},
+      &test_url_loader_factory_);
 
   AccountReconcilor* reconcilor = GetMockReconcilor();
   ASSERT_TRUE(reconcilor);
@@ -2735,7 +2746,7 @@
       GoogleServiceAuthError(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS));
 
   // The cookie starts empty.
-  cookie_manager_service()->SetListAccountsResponseNoAccounts();
+  signin::SetListAccountsResponseNoAccounts(&test_url_loader_factory_);
 
   // Since the cookie jar starts empty, the reconcilor should attempt to merge
   // accounts into it.  However, it should only try accounts not in auth
@@ -2797,8 +2808,8 @@
 // valid timeout.
 TEST_P(AccountReconcilorMirrorEndpointParamTest, DelegateTimeoutIsNotCalled) {
   AccountInfo account_info = ConnectProfileToAccount("user@gmail.com");
-  cookie_manager_service()->SetListAccountsResponseOneAccount(
-      account_info.email, account_info.gaia);
+  signin::SetListAccountsResponseOneAccount(
+      account_info.email, account_info.gaia, &test_url_loader_factory_);
   AccountReconcilor* reconcilor = GetMockReconcilor();
   ASSERT_TRUE(reconcilor);
   auto timer0 = std::make_unique<base::MockOneShotTimer>();
@@ -2816,8 +2827,8 @@
 
 TEST_F(AccountReconcilorTest, DelegateTimeoutIsNotCalledIfTimeoutIsNotReached) {
   AccountInfo account_info = ConnectProfileToAccount("user@gmail.com");
-  cookie_manager_service()->SetListAccountsResponseOneAccount(
-      account_info.email, account_info.gaia);
+  signin::SetListAccountsResponseOneAccount(
+      account_info.email, account_info.gaia, &test_url_loader_factory_);
   auto spy_delegate0 = std::make_unique<SpyReconcilorDelegate>();
   SpyReconcilorDelegate* spy_delegate = spy_delegate0.get();
   AccountReconcilor* reconcilor = GetMockReconcilor(std::move(spy_delegate0));
diff --git a/components/sync/driver/about_sync_util.cc b/components/sync/driver/about_sync_util.cc
index de3aefc..a771388 100644
--- a/components/sync/driver/about_sync_util.cc
+++ b/components/sync/driver/about_sync_util.cc
@@ -504,7 +504,8 @@
   // Encryption.
   if (service->IsSyncFeatureActive()) {
     is_using_explicit_passphrase->Set(service->IsUsingSecondaryPassphrase());
-    is_passphrase_required->Set(service->IsPassphraseRequired());
+    is_passphrase_required->Set(
+        service->GetUserSettings()->IsPassphraseRequired());
     passphrase_time->Set(
         GetTimeStr(service->GetUserSettings()->GetExplicitPassphraseTime(),
                    "No Passphrase Time"));
diff --git a/components/sync/driver/fake_sync_service.cc b/components/sync/driver/fake_sync_service.cc
index f3bfe499..18d2a51 100644
--- a/components/sync/driver/fake_sync_service.cc
+++ b/components/sync/driver/fake_sync_service.cc
@@ -159,14 +159,6 @@
 
 void FakeSyncService::SetInvalidationsForSessionsEnabled(bool enabled) {}
 
-bool FakeSyncService::IsPassphraseRequired() const {
-  return false;
-}
-
-ModelTypeSet FakeSyncService::GetEncryptedDataTypes() const {
-  return ModelTypeSet();
-}
-
 void FakeSyncService::Shutdown() {}
 
 }  // namespace syncer
diff --git a/components/sync/driver/fake_sync_service.h b/components/sync/driver/fake_sync_service.h
index 7fe8c82..f83bdb2 100644
--- a/components/sync/driver/fake_sync_service.h
+++ b/components/sync/driver/fake_sync_service.h
@@ -67,10 +67,6 @@
                        callback) override;
   void SetInvalidationsForSessionsEnabled(bool enabled) override;
 
-  // DataTypeEncryptionHandler implementation.
-  bool IsPassphraseRequired() const override;
-  ModelTypeSet GetEncryptedDataTypes() const override;
-
   // KeyedService implementation.
   void Shutdown() override;
 
diff --git a/components/sync/driver/sync_service.h b/components/sync/driver/sync_service.h
index a502082..b8121198 100644
--- a/components/sync/driver/sync_service.h
+++ b/components/sync/driver/sync_service.h
@@ -15,7 +15,6 @@
 #include "base/time/time.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "components/sync/base/model_type.h"
-#include "components/sync/driver/data_type_encryption_handler.h"
 #include "components/sync/driver/sync_service_observer.h"
 
 struct AccountInfo;
@@ -48,7 +47,7 @@
   base::Closure on_destroy_;
 };
 
-class SyncService : public DataTypeEncryptionHandler, public KeyedService {
+class SyncService : public KeyedService {
  public:
   // The set of reasons due to which Sync can be disabled. Meant to be used as a
   // bitmask.
diff --git a/components/sync/driver/sync_service_utils.cc b/components/sync/driver/sync_service_utils.cc
index 71947b5..7cd72c3 100644
--- a/components/sync/driver/sync_service_utils.cc
+++ b/components/sync/driver/sync_service_utils.cc
@@ -7,6 +7,7 @@
 #include "base/metrics/histogram_macros.h"
 #include "components/sync/base/sync_prefs.h"
 #include "components/sync/driver/sync_service.h"
+#include "components/sync/driver/sync_user_settings.h"
 #include "components/sync/engine/cycle/sync_cycle_snapshot.h"
 #include "google_apis/gaia/google_service_auth_error.h"
 
@@ -29,7 +30,7 @@
   // encrypted, but not necessarily with a custom passphrase. On the other hand,
   // some data types are never encrypted (e.g. DEVICE_INFO), even if the
   // "encrypt everything" setting is enabled.
-  if (sync_service->GetEncryptedDataTypes().Has(type) &&
+  if (sync_service->GetUserSettings()->GetEncryptedDataTypes().Has(type) &&
       sync_service->IsUsingSecondaryPassphrase()) {
     return UploadState::NOT_ACTIVE;
   }
diff --git a/components/sync/driver/sync_user_settings.h b/components/sync/driver/sync_user_settings.h
index 6a99e85..1703cc4 100644
--- a/components/sync/driver/sync_user_settings.h
+++ b/components/sync/driver/sync_user_settings.h
@@ -11,13 +11,14 @@
 #include "base/time/time.h"
 #include "components/sync/base/model_type.h"
 #include "components/sync/base/passphrase_enums.h"
+#include "components/sync/driver/data_type_encryption_handler.h"
 
 namespace syncer {
 
 // This class encapsulates all the user-configurable bits of Sync.
-class SyncUserSettings {
+class SyncUserSettings : public syncer::DataTypeEncryptionHandler {
  public:
-  virtual ~SyncUserSettings() = default;
+  ~SyncUserSettings() override = default;
 
   // Whether the user wants Sync to run, a.k.a. the Sync feature toggle in
   // settings. This maps to DISABLE_REASON_USER_CHOICE.
@@ -63,10 +64,12 @@
   // after calling this to force the encryption to occur.
   virtual void EnableEncryptEverything() = 0;
 
+  // The current set of encrypted data types.
+  syncer::ModelTypeSet GetEncryptedDataTypes() const override = 0;
   // Whether a passphrase is required for encryption or decryption to proceed.
   // Note that Sync might still be working fine if the user has disabled all
   // encrypted data types.
-  virtual bool IsPassphraseRequired() const = 0;
+  bool IsPassphraseRequired() const override = 0;
   // Whether a passphrase is required to decrypt the data for any currently
   // enabled data type.
   virtual bool IsPassphraseRequiredForDecryption() const = 0;
diff --git a/components/sync/driver/test_sync_service.cc b/components/sync/driver/test_sync_service.cc
index 715ebfc..4ee9b849 100644
--- a/components/sync/driver/test_sync_service.cc
+++ b/components/sync/driver/test_sync_service.cc
@@ -106,7 +106,7 @@
 }
 
 void TestSyncService::SetPassphraseRequired(bool required) {
-  passphrase_required_ = required;
+  user_settings_.SetPassphraseRequired(required);
 }
 
 void TestSyncService::SetPassphraseRequiredForDecryption(bool required) {
@@ -262,20 +262,6 @@
 
 void TestSyncService::SetInvalidationsForSessionsEnabled(bool enabled) {}
 
-bool TestSyncService::IsPassphraseRequired() const {
-  return passphrase_required_;
-}
-
-ModelTypeSet TestSyncService::GetEncryptedDataTypes() const {
-  if (!using_secondary_passphrase_) {
-    // PASSWORDS are always encrypted.
-    return ModelTypeSet(PASSWORDS);
-  }
-  // Some types can never be encrypted, e.g. DEVICE_INFO and
-  // AUTOFILL_WALLET_DATA, so make sure we don't report them as encrypted.
-  return Intersection(GetPreferredDataTypes(), EncryptableUserTypes());
-}
-
 void TestSyncService::Shutdown() {}
 
 }  // namespace syncer
diff --git a/components/sync/driver/test_sync_service.h b/components/sync/driver/test_sync_service.h
index d39830f..db392d0 100644
--- a/components/sync/driver/test_sync_service.h
+++ b/components/sync/driver/test_sync_service.h
@@ -97,10 +97,6 @@
                        callback) override;
   void SetInvalidationsForSessionsEnabled(bool enabled) override;
 
-  // DataTypeEncryptionHandler implementation.
-  bool IsPassphraseRequired() const override;
-  ModelTypeSet GetEncryptedDataTypes() const override;
-
   // KeyedService implementation.
   void Shutdown() override;
 
@@ -119,7 +115,6 @@
   ModelTypeSet active_data_types_;
 
   bool using_secondary_passphrase_ = false;
-  bool passphrase_required_ = false;
   bool passphrase_required_for_decryption_ = false;
 
   bool detailed_sync_status_engine_available_ = false;
diff --git a/components/sync/driver/test_sync_user_settings.cc b/components/sync/driver/test_sync_user_settings.cc
index ea11007..21f61c8 100644
--- a/components/sync/driver/test_sync_user_settings.cc
+++ b/components/sync/driver/test_sync_user_settings.cc
@@ -87,8 +87,19 @@
 
 void TestSyncUserSettings::EnableEncryptEverything() {}
 
+ModelTypeSet TestSyncUserSettings::GetEncryptedDataTypes() const {
+  if (!service_->IsUsingSecondaryPassphrase()) {
+    // PASSWORDS are always encrypted.
+    return ModelTypeSet(PASSWORDS);
+  }
+  // Some types can never be encrypted, e.g. DEVICE_INFO and
+  // AUTOFILL_WALLET_DATA, so make sure we don't report them as encrypted.
+  return Intersection(service_->GetPreferredDataTypes(),
+                      EncryptableUserTypes());
+}
+
 bool TestSyncUserSettings::IsPassphraseRequired() const {
-  return service_->IsPassphraseRequired();
+  return passphrase_required_;
 }
 
 bool TestSyncUserSettings::IsPassphraseRequiredForDecryption() const {
@@ -120,4 +131,8 @@
   first_setup_complete_ = first_setup_complete;
 }
 
+void TestSyncUserSettings::SetPassphraseRequired(bool required) {
+  passphrase_required_ = required;
+}
+
 }  // namespace syncer
diff --git a/components/sync/driver/test_sync_user_settings.h b/components/sync/driver/test_sync_user_settings.h
index c77c484..f2a3252f 100644
--- a/components/sync/driver/test_sync_user_settings.h
+++ b/components/sync/driver/test_sync_user_settings.h
@@ -38,6 +38,7 @@
   bool IsEncryptEverythingEnabled() const override;
   void EnableEncryptEverything() override;
 
+  syncer::ModelTypeSet GetEncryptedDataTypes() const override;
   bool IsPassphraseRequired() const override;
   bool IsPassphraseRequiredForDecryption() const override;
   bool IsUsingSecondaryPassphrase() const override;
@@ -48,12 +49,15 @@
   bool SetDecryptionPassphrase(const std::string& passphrase) override;
 
   void SetFirstSetupComplete(bool first_setup_complete);
+  void SetPassphraseRequired(bool required);
 
  private:
   TestSyncService* service_;
 
   bool first_setup_complete_ = true;
   bool sync_everything_enabled_ = true;
+
+  bool passphrase_required_ = false;
 };
 
 }  // namespace syncer
diff --git a/components/sync/model_impl/syncable_service_based_bridge.cc b/components/sync/model_impl/syncable_service_based_bridge.cc
index 0b4788f..11c531c 100644
--- a/components/sync/model_impl/syncable_service_based_bridge.cc
+++ b/components/sync/model_impl/syncable_service_based_bridge.cc
@@ -31,7 +31,6 @@
     const std::string& client_tag_hash,
     sync_pb::PersistedEntityData data) {
   DCHECK(!client_tag_hash.empty());
-  DCHECK(!data.non_unique_name().empty());
   auto entity_data = std::make_unique<EntityData>();
 
   entity_data->non_unique_name = std::move(*data.mutable_non_unique_name());
@@ -47,8 +46,6 @@
 
 sync_pb::PersistedEntityData CreatePersistedFromEntityData(
     const EntityData& entity_data) {
-  DCHECK(!entity_data.non_unique_name.empty());
-
   sync_pb::PersistedEntityData persisted;
   persisted.set_non_unique_name(entity_data.non_unique_name);
   *persisted.mutable_specifics() = entity_data.specifics;
diff --git a/components/viz/test/test_context_provider.cc b/components/viz/test/test_context_provider.cc
index 66e828c..604ca8d3 100644
--- a/components/viz/test/test_context_provider.cc
+++ b/components/viz/test/test_context_provider.cc
@@ -23,6 +23,7 @@
 #include "gpu/skia_bindings/grcontext_for_gles2_interface.h"
 #include "third_party/skia/include/gpu/GrContext.h"
 #include "third_party/skia/include/gpu/gl/GrGLInterface.h"
+#include "ui/gfx/gpu_memory_buffer.h"
 
 namespace viz {
 
@@ -123,6 +124,7 @@
     uint32_t usage) {
   auto mailbox = gpu::Mailbox::GenerateForSharedImage();
   shared_images_.insert(mailbox);
+  most_recent_size_ = size;
   return mailbox;
 }
 
@@ -144,6 +146,7 @@
     uint32_t usage) {
   auto mailbox = gpu::Mailbox::GenerateForSharedImage();
   shared_images_.insert(mailbox);
+  most_recent_size_ = gpu_memory_buffer->GetSize();
   return mailbox;
 }
 
@@ -164,6 +167,11 @@
                         gpu::CommandBufferId(), ++release_id_);
 }
 
+bool TestSharedImageInterface::CheckSharedImageExists(
+    const gpu::Mailbox& mailbox) const {
+  return shared_images_.contains(mailbox);
+}
+
 // static
 scoped_refptr<TestContextProvider> TestContextProvider::Create(
     std::string additional_extensions) {
diff --git a/components/viz/test/test_context_provider.h b/components/viz/test/test_context_provider.h
index ab0af20f..8e36387 100644
--- a/components/viz/test/test_context_provider.h
+++ b/components/viz/test/test_context_provider.h
@@ -23,6 +23,7 @@
 #include "gpu/command_buffer/client/gles2_interface_stub.h"
 #include "gpu/command_buffer/client/shared_image_interface.h"
 #include "gpu/config/gpu_feature_info.h"
+#include "testing/gmock/include/gmock/gmock.h"
 #include "third_party/skia/include/core/SkRefCnt.h"
 
 namespace skia_bindings {
@@ -63,9 +64,12 @@
   gpu::SyncToken GenUnverifiedSyncToken() override;
 
   size_t shared_image_count() const { return shared_images_.size(); }
+  const gfx::Size& MostRecentSize() const { return most_recent_size_; }
+  bool CheckSharedImageExists(const gpu::Mailbox& mailbox) const;
 
  private:
   uint64_t release_id_ = 0;
+  gfx::Size most_recent_size_;
   base::flat_set<gpu::Mailbox> shared_images_;
 };
 
diff --git a/components/viz/test/test_gpu_memory_buffer_manager.cc b/components/viz/test/test_gpu_memory_buffer_manager.cc
index 8b2a789..b8baf92 100644
--- a/components/viz/test/test_gpu_memory_buffer_manager.cc
+++ b/components/viz/test/test_gpu_memory_buffer_manager.cc
@@ -178,6 +178,8 @@
     gfx::BufferFormat format,
     gfx::BufferUsage usage,
     gpu::SurfaceHandle surface_handle) {
+  if (fail_on_create_)
+    return nullptr;
   const size_t buffer_size = gfx::BufferSizeForBufferFormat(size, format);
   base::UnsafeSharedMemoryRegion shared_memory_region =
       base::UnsafeSharedMemoryRegion::Create(buffer_size);
diff --git a/components/viz/test/test_gpu_memory_buffer_manager.h b/components/viz/test/test_gpu_memory_buffer_manager.h
index 2fad0d7..180574de 100644
--- a/components/viz/test/test_gpu_memory_buffer_manager.h
+++ b/components/viz/test/test_gpu_memory_buffer_manager.h
@@ -24,6 +24,10 @@
 
   void OnGpuMemoryBufferDestroyed(gfx::GpuMemoryBufferId gpu_memory_buffer_id);
 
+  void SetFailOnCreate(bool fail_on_create) {
+    fail_on_create_ = fail_on_create;
+  }
+
   // Overridden from gpu::GpuMemoryBufferManager:
   std::unique_ptr<gfx::GpuMemoryBuffer> CreateGpuMemoryBuffer(
       const gfx::Size& size,
@@ -48,6 +52,8 @@
   int last_client_id_ = 5000;
   std::map<int, TestGpuMemoryBufferManager*> clients_;
 
+  bool fail_on_create_ = false;
+
   DISALLOW_COPY_AND_ASSIGN(TestGpuMemoryBufferManager);
 };
 
diff --git a/content/browser/child_process_security_policy_impl.cc b/content/browser/child_process_security_policy_impl.cc
index a98ab71..d2351328 100644
--- a/content/browser/child_process_security_policy_impl.cc
+++ b/content/browser/child_process_security_policy_impl.cc
@@ -23,10 +23,12 @@
 #include "content/browser/renderer_host/render_process_host_impl.h"
 #include "content/browser/site_instance_impl.h"
 #include "content/public/browser/browser_context.h"
+#include "content/public/browser/browser_or_resource_context.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/child_process_data.h"
 #include "content/public/browser/content_browser_client.h"
 #include "content/public/browser/render_process_host.h"
+#include "content/public/browser/resource_context.h"
 #include "content/public/browser/site_isolation_policy.h"
 #include "content/public/browser/storage_partition.h"
 #include "content/public/common/bindings_policy.h"
@@ -125,10 +127,12 @@
 // information.
 class ChildProcessSecurityPolicyImpl::SecurityState {
  public:
-  SecurityState()
-    : enabled_bindings_(0),
-      can_read_raw_cookies_(false),
-      can_send_midi_sysex_(false) { }
+  explicit SecurityState(BrowserContext* browser_context)
+      : enabled_bindings_(0),
+        can_read_raw_cookies_(false),
+        can_send_midi_sysex_(false),
+        browser_context_(browser_context),
+        resource_context_(browser_context->GetResourceContext()) {}
 
   ~SecurityState() {
     storage::IsolatedContext* isolated_context =
@@ -355,6 +359,18 @@
     return can_send_midi_sysex_;
   }
 
+  BrowserOrResourceContext GetBrowserOrResourceContext() const {
+    if (BrowserThread::CurrentlyOn(BrowserThread::UI) && browser_context_)
+      return BrowserOrResourceContext(browser_context_);
+
+    if (BrowserThread::CurrentlyOn(BrowserThread::IO) && resource_context_)
+      return BrowserOrResourceContext(resource_context_);
+
+    return BrowserOrResourceContext();
+  }
+
+  void ClearBrowserContext() { browser_context_ = nullptr; }
+
  private:
   enum class CommitRequestPolicy {
     kRequestOnly,
@@ -421,6 +437,9 @@
   // The set of isolated filesystems the child process is permitted to access.
   FileSystemMap filesystem_permissions_;
 
+  BrowserContext* browser_context_;
+  ResourceContext* resource_context_;
+
   DISALLOW_COPY_AND_ASSIGN(SecurityState);
 };
 
@@ -478,9 +497,12 @@
   return base::Singleton<ChildProcessSecurityPolicyImpl>::get();
 }
 
-void ChildProcessSecurityPolicyImpl::Add(int child_id) {
+void ChildProcessSecurityPolicyImpl::Add(int child_id,
+                                         BrowserContext* browser_context) {
+  DCHECK(browser_context);
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   base::AutoLock lock(lock_);
-  AddChild(child_id);
+  AddChild(child_id, browser_context);
 }
 
 void ChildProcessSecurityPolicyImpl::Remove(int child_id) {
@@ -491,6 +513,8 @@
   if (state == security_state_.end())
     return;
 
+  state->second->ClearBrowserContext();
+
   // Moving the existing SecurityState object into a pending map so
   // that we can preserve permission state and avoid mutations to this
   // state after Remove() has been called.
@@ -1191,13 +1215,15 @@
   return state->second->can_read_raw_cookies();
 }
 
-void ChildProcessSecurityPolicyImpl::AddChild(int child_id) {
+void ChildProcessSecurityPolicyImpl::AddChild(int child_id,
+                                              BrowserContext* browser_context) {
+  DCHECK(browser_context);
   if (security_state_.count(child_id) != 0) {
     NOTREACHED() << "Add child process at most once.";
     return;
   }
 
-  security_state_[child_id] = std::make_unique<SecurityState>();
+  security_state_[child_id] = std::make_unique<SecurityState>(browser_context);
 }
 
 bool ChildProcessSecurityPolicyImpl::ChildProcessHasPermissionsForFile(
@@ -1217,26 +1243,37 @@
 
   // Determine the BrowsingInstance ID for calculating the expected process
   // lock URL.
-  BrowsingInstanceId browsing_instance_id;
+  GURL expected_process_lock;
+  BrowserOrResourceContext context;
+  if (security_state) {
+    context = security_state->GetBrowserOrResourceContext();
+    if (context) {
+      BrowsingInstanceId browsing_instance_id =
+          security_state->lowest_browsing_instance_id();
+      expected_process_lock = SiteInstanceImpl::DetermineProcessLockURL(
+          context, IsolationContext(browsing_instance_id), url);
+    }
+  }
 
-  if (security_state)
-    browsing_instance_id = security_state->lowest_browsing_instance_id();
-
-  GURL expected_process_lock = SiteInstanceImpl::DetermineProcessLockURL(
-      nullptr, IsolationContext(browsing_instance_id), url);
-
-  bool can_access = security_state && security_state->CanAccessDataForOrigin(
-                                          expected_process_lock);
+  bool can_access =
+      context && security_state &&
+      security_state->CanAccessDataForOrigin(expected_process_lock);
   if (!can_access) {
     // Returning false here will result in a renderer kill.  Set some crash
     // keys that will help understand the circumstances of that kill.
     base::debug::SetCrashKeyString(bad_message::GetRequestedSiteURLKey(),
                                    expected_process_lock.spec());
 
+    std::string killed_process_origin_lock;
+    if (!security_state) {
+      killed_process_origin_lock = "(child id not found)";
+    } else if (!context) {
+      killed_process_origin_lock = "(context is null)";
+    } else {
+      killed_process_origin_lock = security_state->origin_lock().spec();
+    }
     base::debug::SetCrashKeyString(bad_message::GetKilledProcessOriginLockKey(),
-                                   security_state
-                                       ? security_state->origin_lock().spec()
-                                       : "(child id not found)");
+                                   killed_process_origin_lock);
 
     static auto* requested_origin_key = base::debug::AllocateCrashKeyString(
         "requested_origin", base::debug::CrashKeySize::Size64);
@@ -1258,9 +1295,10 @@
   // Sanity-check that the |gurl| argument can be used as a lock.
   RenderProcessHost* rph = RenderProcessHostImpl::FromID(child_id);
   if (rph) {  // |rph| can be null in unittests.
-    DCHECK_EQ(SiteInstanceImpl::DetermineProcessLockURL(
-                  rph->GetBrowserContext(), context, gurl),
-              gurl);
+    DCHECK_EQ(
+        SiteInstanceImpl::DetermineProcessLockURL(
+            BrowserOrResourceContext(rph->GetBrowserContext()), context, gurl),
+        gurl);
   }
 #endif
 
diff --git a/content/browser/child_process_security_policy_impl.h b/content/browser/child_process_security_policy_impl.h
index 93af81e..994aebf 100644
--- a/content/browser/child_process_security_policy_impl.h
+++ b/content/browser/child_process_security_policy_impl.h
@@ -43,6 +43,7 @@
 
 namespace content {
 
+class BrowserContext;
 class IsolationContext;
 class SiteInstance;
 
@@ -165,11 +166,11 @@
   bool IsPseudoScheme(const std::string& scheme);
 
   // Upon creation, child processes should register themselves by calling this
-  // this method exactly once.
-  void Add(int child_id);
+  // this method exactly once. This call must be made on the UI thread.
+  void Add(int child_id, BrowserContext* browser_context);
 
   // Upon destruction, child processes should unregister themselves by calling
-  // this method exactly once.
+  // this method exactly once. This call must be made on the UI thread.
   //
   // Note: Pre-Remove() permissions remain in effect on the IO thread until
   // the task posted to the IO thread by this call runs and removes the entry
@@ -397,7 +398,8 @@
   friend struct base::DefaultSingletonTraits<ChildProcessSecurityPolicyImpl>;
 
   // Adds child process during registration.
-  void AddChild(int child_id) EXCLUSIVE_LOCKS_REQUIRED(lock_);
+  void AddChild(int child_id, BrowserContext* browser_context)
+      EXCLUSIVE_LOCKS_REQUIRED(lock_);
 
   // Determines if certain permissions were granted for a file to given child
   // process. |permissions| is an internally defined bit-set.
diff --git a/content/browser/child_process_security_policy_unittest.cc b/content/browser/child_process_security_policy_unittest.cc
index fbba808b..9d2a65b 100644
--- a/content/browser/child_process_security_policy_unittest.cc
+++ b/content/browser/child_process_security_policy_unittest.cc
@@ -170,8 +170,11 @@
     EXPECT_FALSE(p->CanDeleteFileSystemFile(kRendererID, url));
   }
 
+  BrowserContext* browser_context() { return &browser_context_; }
+
  private:
   TestBrowserThreadBundle thread_bundle_;
+  TestBrowserContext browser_context_;
   ChildProcessSecurityPolicyTestBrowserClient test_browser_client_;
   ContentBrowserClient* old_browser_client_;
 };
@@ -215,7 +218,7 @@
   ChildProcessSecurityPolicyImpl* p =
       ChildProcessSecurityPolicyImpl::GetInstance();
 
-  p->Add(kRendererID);
+  p->Add(kRendererID, browser_context());
 
   // Safe to request, redirect or commit.
   EXPECT_TRUE(p->CanRequestURL(kRendererID, GURL("http://www.google.com/")));
@@ -277,7 +280,7 @@
   ChildProcessSecurityPolicyImpl* p =
       ChildProcessSecurityPolicyImpl::GetInstance();
 
-  p->Add(kRendererID);
+  p->Add(kRendererID, browser_context());
 
   EXPECT_TRUE(
       p->CanRequestURL(kRendererID, GURL("blob:http://localhost/some-guid")));
@@ -342,7 +345,7 @@
   ChildProcessSecurityPolicyImpl* p =
       ChildProcessSecurityPolicyImpl::GetInstance();
 
-  p->Add(kRendererID);
+  p->Add(kRendererID, browser_context());
 
   EXPECT_TRUE(p->CanRequestURL(kRendererID, GURL("about:blank")));
   EXPECT_FALSE(p->CanRequestURL(kRendererID, GURL("about:BlAnK")));
@@ -415,7 +418,7 @@
   ChildProcessSecurityPolicyImpl* p =
       ChildProcessSecurityPolicyImpl::GetInstance();
 
-  p->Add(kRendererID);
+  p->Add(kRendererID, browser_context());
 
   EXPECT_FALSE(p->CanRequestURL(kRendererID, GURL("javascript:alert('xss')")));
   EXPECT_FALSE(p->CanRedirectToURL(GURL("javascript:alert('xss')")));
@@ -436,7 +439,7 @@
   ChildProcessSecurityPolicyImpl* p =
       ChildProcessSecurityPolicyImpl::GetInstance();
 
-  p->Add(kRendererID);
+  p->Add(kRendererID, browser_context());
 
   // Currently, "asdf" is destined for ShellExecute, so it is allowed to be
   // requested but not committed.
@@ -467,7 +470,7 @@
   ChildProcessSecurityPolicyImpl* p =
       ChildProcessSecurityPolicyImpl::GetInstance();
 
-  p->Add(kRendererID);
+  p->Add(kRendererID, browser_context());
 
   EXPECT_FALSE(p->CanRequestURL(kRendererID, GURL("file:///etc/passwd")));
   EXPECT_TRUE(p->CanRedirectToURL(GURL("file:///etc/passwd")));
@@ -482,7 +485,7 @@
 
   // We should forget our state if we repeat a renderer id.
   p->Remove(kRendererID);
-  p->Add(kRendererID);
+  p->Add(kRendererID, browser_context());
   EXPECT_FALSE(p->CanRequestURL(kRendererID, GURL("file:///etc/passwd")));
   EXPECT_TRUE(p->CanRedirectToURL(GURL("file:///etc/passwd")));
   EXPECT_FALSE(p->CanCommitURL(kRendererID, GURL("file:///etc/passwd")));
@@ -495,7 +498,7 @@
   ChildProcessSecurityPolicyImpl* p =
       ChildProcessSecurityPolicyImpl::GetInstance();
 
-  p->Add(kRendererID);
+  p->Add(kRendererID, browser_context());
 
   // Child processes cannot request view source URLs.
   EXPECT_FALSE(p->CanRequestURL(kRendererID,
@@ -560,7 +563,7 @@
   ASSERT_TRUE(url::Origin::Create(url2).opaque());
   RegisterTestScheme("httpxml");
 
-  p->Add(kRendererID);
+  p->Add(kRendererID, browser_context());
 
   EXPECT_FALSE(p->CanRequestURL(kRendererID, url));
   EXPECT_FALSE(p->CanRequestURL(kRendererID, url2));
@@ -591,7 +594,7 @@
   ChildProcessSecurityPolicyImpl* p =
       ChildProcessSecurityPolicyImpl::GetInstance();
 
-  p->Add(kRendererID);
+  p->Add(kRendererID, browser_context());
 
   GURL icon_url("file:///tmp/foo.png");
   GURL sensitive_url("file:///etc/passwd");
@@ -631,7 +634,7 @@
   ChildProcessSecurityPolicyImpl* p =
       ChildProcessSecurityPolicyImpl::GetInstance();
 
-  p->Add(kRendererID);
+  p->Add(kRendererID, browser_context());
   std::string read_id =
       storage::IsolatedContext::GetInstance()->RegisterFileSystemForVirtualPath(
           storage::kFileSystemTypeTest, "read_filesystem", base::FilePath());
@@ -691,7 +694,7 @@
   CheckHasNoFileSystemPermission(p, delete_from_id);
 
   // Test having no permissions upon re-adding same renderer ID.
-  p->Add(kRendererID);
+  p->Add(kRendererID, browser_context());
   CheckHasNoFileSystemPermission(p, read_id);
   CheckHasNoFileSystemPermission(p, read_write_id);
   CheckHasNoFileSystemPermission(p, copy_into_id);
@@ -713,7 +716,7 @@
       storage::kFileSystemTypeTest,
       storage::FILE_PERMISSION_USE_FILE_PERMISSION);
 
-  p->Add(kRendererID);
+  p->Add(kRendererID, browser_context());
   base::FilePath file(TEST_PATH("/dir/testfile"));
   file = file.NormalizePathSeparators();
   storage::FileSystemURL url = storage::FileSystemURL::CreateForTest(
@@ -761,7 +764,7 @@
   CheckHasNoFileSystemFilePermission(p, file, url);
 
   // Test having no permissions upon re-adding same renderer ID.
-  p->Add(kRendererID);
+  p->Add(kRendererID, browser_context());
   CheckHasNoFileSystemFilePermission(p, file, url);
 
   // Cleanup.
@@ -790,7 +793,7 @@
       ChildProcessSecurityPolicyImpl::GetInstance();
 
   // Grant permissions for a file.
-  p->Add(kRendererID);
+  p->Add(kRendererID, browser_context());
   EXPECT_FALSE(p->HasPermissionsForFile(kRendererID, granted_file,
                                         base::File::FLAG_OPEN));
 
@@ -843,7 +846,7 @@
   p->Remove(kRendererID);
 
   // Grant permissions for the directory the file is in.
-  p->Add(kRendererID);
+  p->Add(kRendererID, browser_context());
   EXPECT_FALSE(p->HasPermissionsForFile(kRendererID, granted_file,
                                         base::File::FLAG_OPEN));
   GrantPermissionsForFile(p, kRendererID, parent_file,
@@ -857,7 +860,7 @@
   p->Remove(kRendererID);
 
   // Grant permissions for the directory the file is in (with trailing '/').
-  p->Add(kRendererID);
+  p->Add(kRendererID, browser_context());
   EXPECT_FALSE(p->HasPermissionsForFile(kRendererID, granted_file,
                                         base::File::FLAG_OPEN));
   GrantPermissionsForFile(p, kRendererID, parent_slash_file,
@@ -888,8 +891,7 @@
                                         base::File::FLAG_TEMPORARY));
   p->Remove(kRendererID);
 
-
-  p->Add(kRendererID);
+  p->Add(kRendererID, browser_context());
   GrantPermissionsForFile(p, kRendererID, relative_file,
                              base::File::FLAG_OPEN);
   EXPECT_FALSE(p->HasPermissionsForFile(kRendererID, relative_file,
@@ -905,7 +907,7 @@
   const GURL other_url("chrome://not-thumb/");
   const url::Origin origin = url::Origin::Create(url);
   {
-    p->Add(kRendererID);
+    p->Add(kRendererID, browser_context());
 
     EXPECT_FALSE(p->HasWebUIBindings(kRendererID));
 
@@ -942,7 +944,7 @@
     p->Remove(kRendererID);
   }
   {
-    p->Add(kRendererID);
+    p->Add(kRendererID, browser_context());
 
     EXPECT_FALSE(p->HasWebUIBindings(kRendererID));
 
@@ -979,7 +981,7 @@
     p->Remove(kRendererID);
   }
   {
-    p->Add(kRendererID);
+    p->Add(kRendererID, browser_context());
 
     EXPECT_FALSE(p->HasWebUIBindings(kRendererID));
 
@@ -1025,7 +1027,7 @@
   GURL url("file:///etc/passwd");
   base::FilePath file(TEST_PATH("/etc/passwd"));
 
-  p->Add(kRendererID);
+  p->Add(kRendererID, browser_context());
 
   p->GrantCommitURL(kRendererID, url);
   p->GrantReadFile(kRendererID, file);
@@ -1065,7 +1067,7 @@
 
   GURL url("file:///etc/passwd");
 
-  p->Add(kRendererID);
+  p->Add(kRendererID, browser_context());
 
   base::WaitableEvent ready_for_remove_event;
   base::WaitableEvent remove_called_event;
@@ -1179,7 +1181,7 @@
   EXPECT_FALSE(p->CanAccessDataForOrigin(kRendererID, http2_url));
 
   TestBrowserContext browser_context;
-  p->Add(kRendererID);
+  p->Add(kRendererID, &browser_context);
 
   // Verify unlocked origin permissions.
   EXPECT_TRUE(p->CanAccessDataForOrigin(kRendererID, file_url));
@@ -1218,7 +1220,7 @@
   ChildProcessSecurityPolicyImpl* p =
       ChildProcessSecurityPolicyImpl::GetInstance();
 
-  p->Add(kRendererID);
+  p->Add(kRendererID, browser_context());
 
   GURL url_foo1("chrome://foo/resource1");
   GURL url_foo2("chrome://foo/resource2");
diff --git a/content/browser/dom_storage/session_storage_context_mojo_unittest.cc b/content/browser/dom_storage/session_storage_context_mojo_unittest.cc
index fa416ff..f5a6325 100644
--- a/content/browser/dom_storage/session_storage_context_mojo_unittest.cc
+++ b/content/browser/dom_storage/session_storage_context_mojo_unittest.cc
@@ -27,6 +27,7 @@
 #include "content/browser/dom_storage/test/storage_area_test_util.h"
 #include "content/common/dom_storage/dom_storage_types.h"
 #include "content/public/browser/session_storage_usage_info.h"
+#include "content/public/test/test_browser_context.h"
 #include "content/public/test/test_utils.h"
 #include "mojo/core/embedder/embedder.h"
 #include "mojo/public/cpp/bindings/strong_associated_binding.h"
@@ -66,7 +67,8 @@
     mojo::core::SetDefaultProcessErrorCallback(base::BindRepeating(
         &SessionStorageContextMojoTest::OnBadMessage, base::Unretained(this)));
 
-    ChildProcessSecurityPolicyImpl::GetInstance()->Add(kTestProcessId);
+    ChildProcessSecurityPolicyImpl::GetInstance()->Add(kTestProcessId,
+                                                       &browser_context_);
   }
 
   void TearDown() override {
@@ -160,6 +162,7 @@
   bool bad_message_called_ = false;
 
  private:
+  TestBrowserContext browser_context_;
   SessionStorageContextMojo::BackingMode backing_mode_ =
       SessionStorageContextMojo::BackingMode::kRestoreDiskState;
   base::test::ScopedFeatureList features_;
diff --git a/content/browser/dom_storage/session_storage_namespace_impl_mojo_unittest.cc b/content/browser/dom_storage/session_storage_namespace_impl_mojo_unittest.cc
index c04d727..a29283d4 100644
--- a/content/browser/dom_storage/session_storage_namespace_impl_mojo_unittest.cc
+++ b/content/browser/dom_storage/session_storage_namespace_impl_mojo_unittest.cc
@@ -15,6 +15,7 @@
 #include "content/browser/dom_storage/session_storage_metadata.h"
 #include "content/browser/dom_storage/test/storage_area_test_util.h"
 #include "content/browser/site_instance_impl.h"
+#include "content/public/test/test_browser_context.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "content/test/fake_leveldb_database.h"
 #include "content/test/gmock_util.h"
@@ -72,9 +73,9 @@
         StdStringToUint8Vector("data1");
 
     auto* security_policy = ChildProcessSecurityPolicyImpl::GetInstance();
-    security_policy->Add(kTestProcessIdOrigin1);
-    security_policy->Add(kTestProcessIdAllOrigins);
-    security_policy->Add(kTestProcessIdOrigin3);
+    security_policy->Add(kTestProcessIdOrigin1, &browser_context_);
+    security_policy->Add(kTestProcessIdAllOrigins, &browser_context_);
+    security_policy->Add(kTestProcessIdOrigin3, &browser_context_);
     security_policy->AddIsolatedOrigins(
         {test_origin1_, test_origin2_, test_origin3_});
     security_policy->LockToOrigin(IsolationContext(), kTestProcessIdOrigin1,
@@ -159,6 +160,7 @@
 
  protected:
   TestBrowserThreadBundle test_browser_thread_bundle_;
+  TestBrowserContext browser_context_;
   const std::string test_namespace_id1_;
   const std::string test_namespace_id2_;
   const url::Origin test_origin1_;
diff --git a/content/browser/fileapi/browser_file_system_helper_unittest.cc b/content/browser/fileapi/browser_file_system_helper_unittest.cc
index 67e528b5..9c12c5a 100644
--- a/content/browser/fileapi/browser_file_system_helper_unittest.cc
+++ b/content/browser/fileapi/browser_file_system_helper_unittest.cc
@@ -13,6 +13,7 @@
 #include "content/browser/child_process_security_policy_impl.h"
 #include "content/browser/fileapi/browser_file_system_helper.h"
 #include "content/public/common/drop_data.h"
+#include "content/public/test/test_browser_context.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "net/base/filename_util.h"
 #include "storage/browser/fileapi/external_mount_points.h"
@@ -35,9 +36,10 @@
   ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
 
   TestBrowserThreadBundle thread_bundle;
+  TestBrowserContext browser_context;
   ChildProcessSecurityPolicyImpl* p =
       ChildProcessSecurityPolicyImpl::GetInstance();
-  p->Add(kRendererID);
+  p->Add(kRendererID, &browser_context);
 
   // Prepare |original_file| FileSystemURL that comes from a |sensitive_origin|.
   // This attempts to simulate for unit testing the drive URL from
@@ -139,9 +141,10 @@
   ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
 
   TestBrowserThreadBundle thread_bundle;
+  TestBrowserContext browser_context;
   ChildProcessSecurityPolicyImpl* p =
       ChildProcessSecurityPolicyImpl::GetInstance();
-  p->Add(kRendererID);
+  p->Add(kRendererID, &browser_context);
 
   // Prepare content::DropData containing some local files.
   const base::FilePath kDraggedFile =
diff --git a/content/browser/frame_host/navigation_controller_impl_unittest.cc b/content/browser/frame_host/navigation_controller_impl_unittest.cc
index 309ecef..d380682 100644
--- a/content/browser/frame_host/navigation_controller_impl_unittest.cc
+++ b/content/browser/frame_host/navigation_controller_impl_unittest.cc
@@ -4856,8 +4856,7 @@
   NavigateAndCommit(url1);
   NavigateAndCommit(url2);
   NavigateAndCommit(url3);
-  controller.GoBack();
-  contents()->CommitPendingNavigation();
+  NavigationSimulator::GoBack(contents());
   EXPECT_EQ(3, controller.GetEntryCount());
   EXPECT_EQ(1, controller.GetCurrentEntryIndex());
 
@@ -4878,10 +4877,8 @@
 
   // Assume that the RenderFrame correctly cleared its history and commit the
   // navigation.
-  contents()->GetMainFrame()->SendBeforeUnloadACK(true);
-  contents()->GetPendingMainFrame()->
-      set_simulate_history_list_was_cleared(true);
-  contents()->CommitPendingNavigation();
+  navigation->set_history_list_was_cleared(true);
+  navigation->Commit();
 
   // Verify that the NavigationController's session history was correctly
   // cleared.
diff --git a/content/browser/frame_host/navigator_impl_unittest.cc b/content/browser/frame_host/navigator_impl_unittest.cc
index 70641c3..80de540b 100644
--- a/content/browser/frame_host/navigator_impl_unittest.cc
+++ b/content/browser/frame_host/navigator_impl_unittest.cc
@@ -85,8 +85,8 @@
   }
 };
 
-// PlzNavigate: Test a complete browser-initiated navigation starting with a
-// non-live renderer.
+// Tests a complete browser-initiated navigation starting with a non-live
+// renderer.
 TEST_F(NavigatorTestWithBrowserSideNavigation,
        SimpleBrowserInitiatedNavigationFromNonLiveRenderer) {
   const GURL kUrl("http://chromium.org/");
@@ -94,9 +94,11 @@
   EXPECT_FALSE(main_test_rfh()->IsRenderFrameLive());
 
   // Start a browser-initiated navigation.
+  auto navigation =
+      NavigationSimulator::CreateBrowserInitiated(kUrl, contents());
   int32_t site_instance_id = main_test_rfh()->GetSiteInstance()->GetId();
   FrameTreeNode* node = main_test_rfh()->frame_tree_node();
-  int entry_id = RequestNavigation(node, kUrl);
+  navigation->Start();
   NavigationRequest* request = node->navigation_request();
   ASSERT_TRUE(request);
   EXPECT_EQ(kUrl, request->common_params().url);
@@ -107,21 +109,13 @@
   EXPECT_EQ(NavigationRequest::STARTED, request->state());
   ASSERT_TRUE(GetLoaderForNavigationRequest(request));
   EXPECT_FALSE(GetSpeculativeRenderFrameHost(node));
-  int64_t navigation_id = request->navigation_handle()->GetNavigationId();
 
-  // Have the current RenderFrameHost commit the navigation.
-  scoped_refptr<network::ResourceResponse> response(
-      new network::ResourceResponse);
-  GetLoaderForNavigationRequest(request)->CallOnResponseStarted(response,
-                                                                nullptr);
+  navigation->ReadyToCommit();
   EXPECT_TRUE(main_test_rfh()->is_loading());
   EXPECT_FALSE(node->navigation_request());
 
   // Commit the navigation.
-  main_test_rfh()->SimulateCommitProcessed(navigation_id,
-                                           true /* was_successful */);
-  EXPECT_TRUE(main_test_rfh()->navigation_request());
-  main_test_rfh()->SendNavigate(entry_id, true, kUrl);
+  navigation->Commit();
   EXPECT_TRUE(main_test_rfh()->is_active());
   EXPECT_EQ(SiteInstance::GetSiteForURL(browser_context(), kUrl),
             main_test_rfh()->GetSiteInstance()->GetSiteURL());
@@ -426,31 +420,22 @@
   FrameTreeNode* node = main_test_rfh()->frame_tree_node();
 
   // Navigate to a different site.
-  EXPECT_FALSE(main_test_rfh()->navigation_request());
-  int entry_id = RequestNavigation(node, kUrl2);
+  EXPECT_EQ(main_test_rfh()->navigation_requests().size(), 0u);
+  auto navigation =
+      NavigationSimulator::CreateBrowserInitiated(kUrl2, contents());
+  navigation->Start();
   NavigationRequest* main_request = node->navigation_request();
   ASSERT_TRUE(main_request);
   TestRenderFrameHost* speculative_rfh = GetSpeculativeRenderFrameHost(node);
   ASSERT_TRUE(speculative_rfh);
-
-  // Receive the beforeUnload ACK.
-  main_test_rfh()->SendBeforeUnloadACK(true);
-  int64_t navigation_id = main_request->navigation_handle()->GetNavigationId();
   EXPECT_EQ(speculative_rfh, GetSpeculativeRenderFrameHost(node));
 
-  scoped_refptr<network::ResourceResponse> response(
-      new network::ResourceResponse);
-  GetLoaderForNavigationRequest(main_request)
-      ->CallOnResponseStarted(response, nullptr);
+  navigation->ReadyToCommit();
   EXPECT_EQ(speculative_rfh, GetSpeculativeRenderFrameHost(node));
-  EXPECT_FALSE(main_test_rfh()->navigation_request());
+  EXPECT_EQ(speculative_rfh->navigation_requests().size(), 1u);
+  EXPECT_EQ(main_test_rfh()->navigation_requests().size(), 0u);
 
-  speculative_rfh->SimulateCommitProcessed(navigation_id,
-                                           true /* was_successful */);
-  EXPECT_TRUE(speculative_rfh->navigation_request());
-
-  speculative_rfh->SendNavigate(entry_id, true, kUrl2);
-
+  navigation->Commit();
   RenderFrameHostImpl* final_rfh = main_test_rfh();
   EXPECT_EQ(speculative_rfh, final_rfh);
   EXPECT_NE(initial_rfh, final_rfh);
@@ -470,36 +455,28 @@
   FrameTreeNode* node = main_test_rfh()->frame_tree_node();
 
   // Navigate to a URL on the same site.
-  EXPECT_FALSE(main_test_rfh()->navigation_request());
-  int entry_id = RequestNavigation(node, kUrl1);
-  main_test_rfh()->SendBeforeUnloadACK(true);
+  EXPECT_EQ(main_test_rfh()->navigation_requests().size(), 0u);
+  auto navigation =
+      NavigationSimulator::CreateBrowserInitiated(kUrl1, contents());
+  navigation->Start();
   NavigationRequest* main_request = node->navigation_request();
   ASSERT_TRUE(main_request);
   EXPECT_FALSE(GetSpeculativeRenderFrameHost(node));
 
   // It then redirects to another site.
-  GetLoaderForNavigationRequest(main_request)->SimulateServerRedirect(kUrl2);
+  navigation->Redirect(kUrl2);
 
   // The redirect should have been followed.
   EXPECT_EQ(1, GetLoaderForNavigationRequest(main_request)->redirect_count());
   EXPECT_FALSE(GetSpeculativeRenderFrameHost(node));
 
-  int64_t navigation_id = main_request->navigation_handle()->GetNavigationId();
-
-  // Have the RenderFrameHost commit the navigation.
-  scoped_refptr<network::ResourceResponse> response(
-      new network::ResourceResponse);
-  GetLoaderForNavigationRequest(main_request)
-      ->CallOnResponseStarted(response, nullptr);
+  navigation->ReadyToCommit();
   TestRenderFrameHost* final_speculative_rfh =
       GetSpeculativeRenderFrameHost(node);
   EXPECT_TRUE(final_speculative_rfh);
+  EXPECT_EQ(final_speculative_rfh->navigation_requests().size(), 1u);
 
-  // Commit the navigation.
-  final_speculative_rfh->SimulateCommitProcessed(navigation_id,
-                                                 true /* was_successful */);
-  EXPECT_TRUE(final_speculative_rfh->navigation_request());
-  final_speculative_rfh->SendNavigate(entry_id, true, kUrl2);
+  navigation->Commit();
   RenderFrameHostImpl* final_rfh = main_test_rfh();
   ASSERT_TRUE(final_rfh);
   EXPECT_NE(rfh, final_rfh);
@@ -526,8 +503,9 @@
 
   // Request navigation to the 1st URL.
   EXPECT_FALSE(main_test_rfh()->navigation_request());
-  RequestNavigation(node, kUrl1);
-  main_test_rfh()->SendBeforeUnloadACK(true);
+  auto navigation1 =
+      NavigationSimulator::CreateBrowserInitiated(kUrl1, contents());
+  navigation1->Start();
   NavigationRequest* request1 = node->navigation_request();
   ASSERT_TRUE(request1);
   EXPECT_EQ(kUrl1, request1->common_params().url);
@@ -544,10 +522,10 @@
 
   // Request navigation to the 2nd URL; the NavigationRequest must have been
   // replaced by a new one with a different URL.
-  int entry_id = RequestNavigation(node, kUrl2);
-  main_test_rfh()->SendBeforeUnloadACK(true);
+  auto navigation2 =
+      NavigationSimulator::CreateBrowserInitiated(kUrl2, contents());
+  navigation2->Start();
   NavigationRequest* request2 = node->navigation_request();
-  int64_t navigation_id = request2->navigation_handle()->GetNavigationId();
   ASSERT_TRUE(request2);
   EXPECT_EQ(kUrl2, request2->common_params().url);
   EXPECT_TRUE(request2->browser_initiated());
@@ -561,17 +539,12 @@
   int32_t site_instance_id_2 = speculative_rfh->GetSiteInstance()->GetId();
   EXPECT_NE(site_instance_id_1, site_instance_id_2);
 
+  navigation2->ReadyToCommit();
+  EXPECT_EQ(speculative_rfh->navigation_requests().size(), 1u);
+  EXPECT_EQ(main_test_rfh()->navigation_requests().size(), 0u);
+
   // Have the RenderFrameHost commit the navigation.
-  scoped_refptr<network::ResourceResponse> response(
-      new network::ResourceResponse);
-  GetLoaderForNavigationRequest(request2)->CallOnResponseStarted(response,
-                                                                 nullptr);
-  // Commit the navigation.
-  speculative_rfh->SimulateCommitProcessed(navigation_id,
-                                           true /* was_successful */);
-  EXPECT_TRUE(speculative_rfh->navigation_request());
-  EXPECT_FALSE(main_test_rfh()->navigation_request());
-  speculative_rfh->SendNavigate(entry_id, true, kUrl2);
+  navigation2->Commit();
 
   // Confirm that the commit corresponds to the new request.
   ASSERT_TRUE(main_test_rfh());
@@ -712,12 +685,15 @@
 
   // Start a browser-initiated navigation to the 1st URL.
   EXPECT_FALSE(main_test_rfh()->navigation_request());
-  int entry_id = RequestNavigation(node, kUrl1);
+  auto navigation =
+      NavigationSimulator::CreateBrowserInitiated(kUrl1, contents());
+  navigation->Start();
   NavigationRequest* request1 = node->navigation_request();
   ASSERT_TRUE(request1);
   EXPECT_EQ(kUrl1, request1->common_params().url);
   EXPECT_TRUE(request1->browser_initiated());
-  EXPECT_TRUE(GetSpeculativeRenderFrameHost(node));
+  TestRenderFrameHost* speculative_rfh = GetSpeculativeRenderFrameHost(node);
+  ASSERT_TRUE(speculative_rfh);
 
   // Now receive a renderer-initiated non-user-initiated request. Nothing should
   // change.
@@ -728,26 +704,13 @@
   EXPECT_EQ(request1, request2);
   EXPECT_EQ(kUrl1, request2->common_params().url);
   EXPECT_TRUE(request2->browser_initiated());
-  EXPECT_TRUE(GetSpeculativeRenderFrameHost(node));
+  EXPECT_TRUE(speculative_rfh);
 
-  // Now receive the beforeUnload ACK from the still ongoing navigation.
-  main_test_rfh()->SendBeforeUnloadACK(true);
-  int64_t navigation_id = request1->navigation_handle()->GetNavigationId();
-  TestRenderFrameHost* speculative_rfh = GetSpeculativeRenderFrameHost(node);
-  ASSERT_TRUE(speculative_rfh);
+  navigation->ReadyToCommit();
+  EXPECT_EQ(speculative_rfh->navigation_requests().size(), 1u);
+  EXPECT_EQ(main_test_rfh()->navigation_requests().size(), 0u);
 
-  // Have the RenderFrameHost commit the navigation.
-  scoped_refptr<network::ResourceResponse> response(
-      new network::ResourceResponse);
-  GetLoaderForNavigationRequest(request2)->CallOnResponseStarted(response,
-                                                                 nullptr);
-  speculative_rfh->SimulateCommitProcessed(navigation_id,
-                                           true /* was_successful */);
-  EXPECT_TRUE(speculative_rfh->navigation_request());
-  EXPECT_FALSE(main_test_rfh()->navigation_request());
-
-  // Commit the navigation.
-  speculative_rfh->SendNavigate(entry_id, true, kUrl1);
+  navigation->Commit();
   EXPECT_EQ(kUrl1, contents()->GetLastCommittedURL());
 }
 
@@ -862,34 +825,21 @@
   // Begin navigating to another site.
   const GURL kUrl("http://google.com/");
   EXPECT_FALSE(main_test_rfh()->navigation_request());
-  int entry_id = RequestNavigation(node, kUrl);
+  auto navigation =
+      NavigationSimulator::CreateBrowserInitiated(kUrl, contents());
+  navigation->Start();
   TestRenderFrameHost* speculative_rfh = GetSpeculativeRenderFrameHost(node);
+  int32_t site_instance_id = speculative_rfh->GetSiteInstance()->GetId();
   ASSERT_TRUE(speculative_rfh);
   EXPECT_NE(speculative_rfh, main_test_rfh());
-
-  // Receive the beforeUnload ACK.
-  main_test_rfh()->SendBeforeUnloadACK(true);
-  EXPECT_EQ(speculative_rfh, GetSpeculativeRenderFrameHost(node));
   EXPECT_EQ(SiteInstance::GetSiteForURL(browser_context(), kUrl),
             speculative_rfh->GetSiteInstance()->GetSiteURL());
-  int32_t site_instance_id = speculative_rfh->GetSiteInstance()->GetId();
-  int64_t navigation_id =
-      node->navigation_request()->navigation_handle()->GetNavigationId();
 
-  // Ask Navigator to commit the navigation by simulating a call to
-  // OnResponseStarted.
-  scoped_refptr<network::ResourceResponse> response(
-      new network::ResourceResponse);
-  GetLoaderForNavigationRequest(node->navigation_request())
-      ->CallOnResponseStarted(response, nullptr);
-  EXPECT_EQ(speculative_rfh, GetSpeculativeRenderFrameHost(node));
-  EXPECT_EQ(site_instance_id, speculative_rfh->GetSiteInstance()->GetId());
+  navigation->ReadyToCommit();
+  EXPECT_EQ(speculative_rfh->navigation_requests().size(), 1u);
 
-  // Invoke DidCommitProvisionalLoad.
-  speculative_rfh->SimulateCommitProcessed(navigation_id,
-                                           true /* was_successful */);
-  EXPECT_TRUE(speculative_rfh->navigation_request());
-  speculative_rfh->SendNavigate(entry_id, true, kUrl);
+  // Ask the navigation to commit.
+  navigation->Commit();
   EXPECT_EQ(site_instance_id, main_test_rfh()->GetSiteInstance()->GetId());
   EXPECT_FALSE(GetSpeculativeRenderFrameHost(node));
 }
@@ -907,7 +857,10 @@
   // Begin navigating to another site.
   const GURL kUrl("http://google.com/");
   EXPECT_FALSE(main_test_rfh()->navigation_request());
-  int entry_id = RequestNavigation(node, kUrl);
+  auto navigation =
+      NavigationSimulator::CreateBrowserInitiated(kUrl, contents());
+  navigation->Start();
+
   TestRenderFrameHost* speculative_rfh = GetSpeculativeRenderFrameHost(node);
   ASSERT_TRUE(speculative_rfh);
   int32_t site_instance_id = speculative_rfh->GetSiteInstance()->GetId();
@@ -918,17 +871,11 @@
   EXPECT_EQ(SiteInstance::GetSiteForURL(browser_context(), kUrl),
             speculative_rfh->GetSiteInstance()->GetSiteURL());
 
-  // Receive the beforeUnload ACK.
-  main_test_rfh()->SendBeforeUnloadACK(true);
-  EXPECT_EQ(speculative_rfh, GetSpeculativeRenderFrameHost(node));
-
   // It then redirects to yet another site.
   NavigationRequest* main_request = node->navigation_request();
-  int64_t navigation_id = main_request->navigation_handle()->GetNavigationId();
   ASSERT_TRUE(main_request);
   const GURL kUrlRedirect("https://www.google.com/");
-  GetLoaderForNavigationRequest(main_request)
-      ->SimulateServerRedirect(kUrlRedirect);
+  navigation->Redirect(kUrlRedirect);
   EXPECT_EQ(init_site_instance_id, main_test_rfh()->GetSiteInstance()->GetId());
 
   // For now, ensure that the speculative RenderFrameHost does not change after
@@ -939,14 +886,11 @@
   EXPECT_EQ(site_instance_id, speculative_rfh->GetSiteInstance()->GetId());
   EXPECT_FALSE(rfh_deleted_observer.deleted());
 
-  // Commit the navigation with Navigator by simulating the call to
-  // OnResponseStarted.
-  scoped_refptr<network::ResourceResponse> response(
-      new network::ResourceResponse);
-  GetLoaderForNavigationRequest(main_request)
-      ->CallOnResponseStarted(response, nullptr);
+  // Send the commit to the renderer.
+  navigation->ReadyToCommit();
   speculative_rfh = GetSpeculativeRenderFrameHost(node);
   ASSERT_TRUE(speculative_rfh);
+  EXPECT_EQ(speculative_rfh->navigation_requests().size(), 1u);
   EXPECT_EQ(init_site_instance_id, main_test_rfh()->GetSiteInstance()->GetId());
   EXPECT_TRUE(rfh_deleted_observer.deleted());
 
@@ -960,10 +904,7 @@
   EXPECT_NE(site_instance_id, redirect_site_instance_id);
 
   // Invoke DidCommitProvisionalLoad.
-  speculative_rfh->SimulateCommitProcessed(navigation_id,
-                                           true /* was_successful */);
-  EXPECT_TRUE(speculative_rfh->navigation_request());
-  speculative_rfh->SendNavigate(entry_id, true, kUrlRedirect);
+  navigation->Commit();
 
   // Check that the speculative RenderFrameHost was swapped in.
   EXPECT_EQ(redirect_site_instance_id,
diff --git a/content/browser/frame_host/render_frame_host_manager_unittest.cc b/content/browser/frame_host/render_frame_host_manager_unittest.cc
index 279dd0c..291e6c4 100644
--- a/content/browser/frame_host/render_frame_host_manager_unittest.cc
+++ b/content/browser/frame_host/render_frame_host_manager_unittest.cc
@@ -48,9 +48,9 @@
 #include "content/public/common/url_utils.h"
 #include "content/public/test/browser_side_navigation_test_utils.h"
 #include "content/public/test/mock_render_process_host.h"
-#include "content/public/test/navigation_simulator.h"
 #include "content/public/test/test_utils.h"
 #include "content/test/mock_widget_input_handler.h"
+#include "content/test/navigation_simulator_impl.h"
 #include "content/test/test_content_browser_client.h"
 #include "content/test/test_content_client.h"
 #include "content/test/test_render_frame_host.h"
@@ -843,24 +843,17 @@
   // into a view-source mode and then navigating to the inner URL, so that's why
   // the bare URL is what's committed and returned by the last committed entry's
   // GetURL() call.
-  controller().LoadURL(
-      kViewSourceUrl, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
-  int entry_id = controller().GetPendingEntry()->GetUniqueID();
-
+  auto navigation = NavigationSimulatorImpl::CreateBrowserInitiated(
+      kViewSourceUrl, contents());
+  navigation->Start();
   NavigationRequest* request =
       main_test_rfh()->frame_tree_node()->navigation_request();
   CHECK(request);
-
-  // Simulate response from RenderFrame for DispatchBeforeUnload.
-  contents()->GetMainFrame()->PrepareForCommit();
   ASSERT_TRUE(contents()->GetPendingMainFrame())
       << "Expected new pending RenderFrameHost to be created.";
   RenderFrameHost* last_rfh = contents()->GetPendingMainFrame();
-  contents()->GetPendingMainFrame()->SimulateCommitProcessed(
-      request->navigation_handle()->GetNavigationId(),
-      true /* was_successful */);
-  contents()->GetPendingMainFrame()->SendNavigate(entry_id, true, kUrl);
 
+  navigation->Commit();
   EXPECT_EQ(1, controller().GetLastCommittedEntryIndex());
   NavigationEntry* last_committed = controller().GetLastCommittedEntry();
   ASSERT_NE(nullptr, last_committed);
@@ -875,23 +868,19 @@
   process()->sink().ClearMessages();
 
   // Navigate, again.
-  controller().LoadURL(
-      kViewSourceUrl, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
+  navigation = NavigationSimulatorImpl::CreateBrowserInitiated(kViewSourceUrl,
+                                                               contents());
+  navigation->set_did_create_new_entry(false);
+  navigation->Start();
   request = main_test_rfh()->frame_tree_node()->navigation_request();
   CHECK(request);
-  entry_id = controller().GetPendingEntry()->GetUniqueID();
-  contents()->GetMainFrame()->PrepareForCommit();
 
   // The same RenderViewHost should be reused.
+  navigation->ReadyToCommit();
   EXPECT_FALSE(contents()->GetPendingMainFrame());
   EXPECT_EQ(last_rfh, contents()->GetMainFrame());
 
-  // The renderer sends a commit.
-  contents()->GetMainFrame()->SimulateCommitProcessed(
-      request->navigation_handle()->GetNavigationId(),
-      true /* was_successful */);
-  contents()->GetMainFrame()->SendNavigateWithTransition(
-      entry_id, false, kUrl, ui::PAGE_TRANSITION_TYPED);
+  navigation->Commit();
   EXPECT_EQ(1, controller().GetLastCommittedEntryIndex());
   EXPECT_FALSE(controller().GetPendingEntry());
 
diff --git a/content/browser/idle/idle_manager_unittest.cc b/content/browser/idle/idle_manager_unittest.cc
index c41857f..2c255ee 100644
--- a/content/browser/idle/idle_manager_unittest.cc
+++ b/content/browser/idle/idle_manager_unittest.cc
@@ -161,4 +161,47 @@
   loop.Run();
 }
 
+TEST_F(IdleManagerTest, RemoveMonitorStopsPolling) {
+  // Simulates the renderer disconnecting (e.g. on page reload) and verifies
+  // that the polling stops for the idle detection.
+
+  auto impl = std::make_unique<IdleManager>();
+  blink::mojom::IdleManagerPtr service_ptr;
+  GURL url("http://google.com");
+  impl->CreateService(mojo::MakeRequest(&service_ptr),
+                      url::Origin::Create(url));
+
+  blink::mojom::IdleMonitorPtr monitor_ptr;
+  blink::mojom::IdleMonitorRequest monitor_request =
+      mojo::MakeRequest(&monitor_ptr);
+  MockIdleMonitor monitor_impl;
+  mojo::Binding<blink::mojom::IdleMonitor> monitor_binding(
+      &monitor_impl, std::move(monitor_request));
+
+  {
+    base::RunLoop loop;
+
+    service_ptr->AddMonitor(
+        kTresholdInSecs, std::move(monitor_ptr),
+        base::BindLambdaForTesting(
+            [&](blink::mojom::IdleState state) { loop.Quit(); }));
+
+    loop.Run();
+  }
+
+  EXPECT_TRUE(impl->IsPollingForTest());
+
+  {
+    base::RunLoop loop;
+
+    // Simulates the renderer disconnecting.
+    monitor_binding.Close();
+
+    // Wait for the IdleManager to observe the pipe close.
+    loop.RunUntilIdle();
+  }
+
+  EXPECT_FALSE(impl->IsPollingForTest());
+}
+
 }  // namespace content
diff --git a/content/browser/isolated_origin_browsertest.cc b/content/browser/isolated_origin_browsertest.cc
index c4fdd51..0f2f828 100644
--- a/content/browser/isolated_origin_browsertest.cc
+++ b/content/browser/isolated_origin_browsertest.cc
@@ -15,6 +15,7 @@
 #include "content/browser/renderer_host/render_process_host_impl.h"
 #include "content/browser/storage_partition_impl.h"
 #include "content/browser/web_contents/web_contents_impl.h"
+#include "content/public/browser/browser_or_resource_context.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/site_isolation_policy.h"
@@ -593,7 +594,7 @@
                               RenderProcessHost* process, const GURL& url) {
     GURL site_url(SiteInstance::GetSiteForURL(browser_context, url));
     GURL lock_url(SiteInstanceImpl::DetermineProcessLockURL(
-        browser_context, isolation_context, url));
+        BrowserOrResourceContext(browser_context), isolation_context, url));
     return RenderProcessHostImpl::IsSuitableHost(
         process, browser_context, isolation_context, site_url, lock_url);
   };
diff --git a/content/browser/loader/resource_dispatcher_host_unittest.cc b/content/browser/loader/resource_dispatcher_host_unittest.cc
index cb6179d..617e603 100644
--- a/content/browser/loader/resource_dispatcher_host_unittest.cc
+++ b/content/browser/loader/resource_dispatcher_host_unittest.cc
@@ -171,7 +171,8 @@
       : TestFilterSpecifyingChild(
             browser_context,
             ChildProcessHostImpl::GenerateChildProcessUniqueId()) {
-    ChildProcessSecurityPolicyImpl::GetInstance()->Add(child_id());
+    ChildProcessSecurityPolicyImpl::GetInstance()->Add(child_id(),
+                                                       browser_context);
   }
 
  protected:
@@ -696,7 +697,8 @@
 
   // testing::Test
   void SetUp() override {
-    ChildProcessSecurityPolicyImpl::GetInstance()->Add(0);
+    ChildProcessSecurityPolicyImpl::GetInstance()->Add(0,
+                                                       browser_context_.get());
     HandleScheme("test");
     scoped_refptr<SiteInstance> site_instance =
         SiteInstance::Create(browser_context_.get());
diff --git a/content/browser/loader/url_loader_factory_impl_unittest.cc b/content/browser/loader/url_loader_factory_impl_unittest.cc
index 9886900f..4ad0a600 100644
--- a/content/browser/loader/url_loader_factory_impl_unittest.cc
+++ b/content/browser/loader/url_loader_factory_impl_unittest.cc
@@ -84,7 +84,8 @@
                 {BrowserThread::IO}))) {
     // Some tests specify request.report_raw_headers, but the RDH checks the
     // CanReadRawCookies permission before enabling it.
-    ChildProcessSecurityPolicyImpl::GetInstance()->Add(kChildId);
+    ChildProcessSecurityPolicyImpl::GetInstance()->Add(kChildId,
+                                                       browser_context_.get());
     ChildProcessSecurityPolicyImpl::GetInstance()->GrantReadRawCookies(
         kChildId);
 
diff --git a/content/browser/network_service_client_unittest.cc b/content/browser/network_service_client_unittest.cc
index a0259c64..9e6284ae 100644
--- a/content/browser/network_service_client_unittest.cc
+++ b/content/browser/network_service_client_unittest.cc
@@ -14,6 +14,7 @@
 #include "base/test/test_file_util.h"
 #include "build/build_config.h"
 #include "content/browser/child_process_security_policy_impl.h"
+#include "content/public/test/test_browser_context.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -70,7 +71,8 @@
 
   void SetUp() override {
     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
-    ChildProcessSecurityPolicyImpl::GetInstance()->Add(kRendererProcessId);
+    ChildProcessSecurityPolicyImpl::GetInstance()->Add(kRendererProcessId,
+                                                       &browser_context_);
   }
 
   void TearDown() override {
@@ -79,6 +81,7 @@
 
  protected:
   TestBrowserThreadBundle scoped_task_environment_;
+  TestBrowserContext browser_context_;
   network::mojom::NetworkServiceClientPtr client_ptr_;
   NetworkServiceClient client_;
   base::ScopedTempDir temp_dir_;
diff --git a/content/browser/renderer_host/input/synthetic_gesture_target_aura.cc b/content/browser/renderer_host/input/synthetic_gesture_target_aura.cc
index 156c1aa..15cc5de 100644
--- a/content/browser/renderer_host/input/synthetic_gesture_target_aura.cc
+++ b/content/browser/renderer_host/input/synthetic_gesture_target_aura.cc
@@ -24,6 +24,24 @@
 
 namespace content {
 
+namespace {
+
+int WebEventButtonToUIEventButtonFlags(blink::WebMouseEvent::Button button) {
+  if (button == blink::WebMouseEvent::Button::kLeft)
+    return ui::EF_LEFT_MOUSE_BUTTON;
+  if (button == blink::WebMouseEvent::Button::kMiddle)
+    return ui::EF_MIDDLE_MOUSE_BUTTON;
+  if (button == blink::WebMouseEvent::Button::kRight)
+    return ui::EF_RIGHT_MOUSE_BUTTON;
+  if (button == blink::WebMouseEvent::Button::kBack)
+    return ui::EF_BACK_MOUSE_BUTTON;
+  if (button == blink::WebMouseEvent::Button::kForward)
+    return ui::EF_FORWARD_MOUSE_BUTTON;
+  return 0;
+}
+
+}  // namespace
+
 SyntheticGestureTargetAura::SyntheticGestureTargetAura(
     RenderWidgetHostImpl* host)
     : SyntheticGestureTargetBase(host) {
@@ -153,8 +171,14 @@
   int flags = ui::WebEventModifiersToEventFlags(web_mouse_event.GetModifiers());
   ui::PointerDetails pointer_details(
       ui::WebPointerTypeToEventPointerType(web_mouse_event.pointer_type));
+  int changed_button_flags = 0;
+  if (event_type == ui::ET_MOUSE_PRESSED ||
+      event_type == ui::ET_MOUSE_RELEASED) {
+    changed_button_flags =
+        WebEventButtonToUIEventButtonFlags(web_mouse_event.button);
+  }
   ui::MouseEvent mouse_event(event_type, gfx::Point(), gfx::Point(),
-                             ui::EventTimeForNow(), flags, flags,
+                             ui::EventTimeForNow(), flags, changed_button_flags,
                              pointer_details);
   gfx::PointF location(
       web_mouse_event.PositionInWidget().x * device_scale_factor_,
diff --git a/content/browser/renderer_host/input/synthetic_mouse_driver.cc b/content/browser/renderer_host/input/synthetic_mouse_driver.cc
index 3ae84c10..05fb8170 100644
--- a/content/browser/renderer_host/input/synthetic_mouse_driver.cc
+++ b/content/browser/renderer_host/input/synthetic_mouse_driver.cc
@@ -26,23 +26,27 @@
 void SyntheticMouseDriver::Press(float x,
                                  float y,
                                  int index,
-                                 SyntheticPointerActionParams::Button button) {
+                                 SyntheticPointerActionParams::Button button,
+                                 int key_modifiers) {
   DCHECK_EQ(index, 0);
   int modifiers =
       SyntheticPointerActionParams::GetWebMouseEventModifier(button);
   mouse_event_ = SyntheticWebMouseEventBuilder::Build(
-      blink::WebInputEvent::kMouseDown, x, y, modifiers | last_modifiers_,
-      mouse_event_.pointer_type);
+      blink::WebInputEvent::kMouseDown, x, y,
+      modifiers | key_modifiers | last_modifiers_, mouse_event_.pointer_type);
   mouse_event_.click_count = 1;
   mouse_event_.button =
       SyntheticPointerActionParams::GetWebMouseEventButton(button);
   last_modifiers_ = modifiers | last_modifiers_;
 }
 
-void SyntheticMouseDriver::Move(float x, float y, int index) {
+void SyntheticMouseDriver::Move(float x,
+                                float y,
+                                int index,
+                                int key_modifiers) {
   DCHECK_EQ(index, 0);
   mouse_event_ = SyntheticWebMouseEventBuilder::Build(
-      blink::WebInputEvent::kMouseMove, x, y, last_modifiers_,
+      blink::WebInputEvent::kMouseMove, x, y, key_modifiers | last_modifiers_,
       mouse_event_.pointer_type);
   mouse_event_.button =
       SyntheticPointerActionParams::GetWebMouseEventButtonFromModifier(
@@ -50,13 +54,13 @@
   mouse_event_.click_count = 0;
 }
 
-void SyntheticMouseDriver::Release(
-    int index,
-    SyntheticPointerActionParams::Button button) {
+void SyntheticMouseDriver::Release(int index,
+                                   SyntheticPointerActionParams::Button button,
+                                   int key_modifiers) {
   DCHECK_EQ(index, 0);
   mouse_event_ = SyntheticWebMouseEventBuilder::Build(
       blink::WebInputEvent::kMouseUp, mouse_event_.PositionInWidget().x,
-      mouse_event_.PositionInWidget().y, last_modifiers_,
+      mouse_event_.PositionInWidget().y, key_modifiers | last_modifiers_,
       mouse_event_.pointer_type);
   mouse_event_.click_count = 1;
   mouse_event_.button =
diff --git a/content/browser/renderer_host/input/synthetic_mouse_driver.h b/content/browser/renderer_host/input/synthetic_mouse_driver.h
index 341d1671..aeb408d 100644
--- a/content/browser/renderer_host/input/synthetic_mouse_driver.h
+++ b/content/browser/renderer_host/input/synthetic_mouse_driver.h
@@ -24,11 +24,13 @@
              float y,
              int index = 0,
              SyntheticPointerActionParams::Button button =
-                 SyntheticPointerActionParams::Button::LEFT) override;
-  void Move(float x, float y, int index = 0) override;
+                 SyntheticPointerActionParams::Button::LEFT,
+             int key_modifiers = 0) override;
+  void Move(float x, float y, int index = 0, int key_modifiers = 0) override;
   void Release(int index = 0,
                SyntheticPointerActionParams::Button button =
-                   SyntheticPointerActionParams::Button::LEFT) override;
+                   SyntheticPointerActionParams::Button::LEFT,
+               int key_modifiers = 0) override;
   void Leave(int index = 0) override;
 
   bool UserInputCheck(
diff --git a/content/browser/renderer_host/input/synthetic_pointer_action.cc b/content/browser/renderer_host/input/synthetic_pointer_action.cc
index de37391..00b5580 100644
--- a/content/browser/renderer_host/input/synthetic_pointer_action.cc
+++ b/content/browser/renderer_host/input/synthetic_pointer_action.cc
@@ -73,16 +73,18 @@
 
     switch (param.pointer_action_type()) {
       case SyntheticPointerActionParams::PointerActionType::PRESS:
-        synthetic_pointer_driver_->Press(param.position().x(),
-                                         param.position().y(),
-                                         param.pointer_id(), param.button());
+        synthetic_pointer_driver_->Press(
+            param.position().x(), param.position().y(), param.pointer_id(),
+            param.button(), param.key_modifiers());
         break;
       case SyntheticPointerActionParams::PointerActionType::MOVE:
         synthetic_pointer_driver_->Move(
-            param.position().x(), param.position().y(), param.pointer_id());
+            param.position().x(), param.position().y(), param.pointer_id(),
+            param.key_modifiers());
         break;
       case SyntheticPointerActionParams::PointerActionType::RELEASE:
-        synthetic_pointer_driver_->Release(param.pointer_id(), param.button());
+        synthetic_pointer_driver_->Release(param.pointer_id(), param.button(),
+                                           param.key_modifiers());
         break;
       case SyntheticPointerActionParams::PointerActionType::LEAVE:
         synthetic_pointer_driver_->Leave(param.pointer_id());
diff --git a/content/browser/renderer_host/input/synthetic_pointer_action_unittest.cc b/content/browser/renderer_host/input/synthetic_pointer_action_unittest.cc
index 0d554c5..c62b53b6 100644
--- a/content/browser/renderer_host/input/synthetic_pointer_action_unittest.cc
+++ b/content/browser/renderer_host/input/synthetic_pointer_action_unittest.cc
@@ -275,9 +275,11 @@
     }
 
     int modifiers = 0;
-    for (size_t index = 0; index < buttons.size(); ++index)
+    for (size_t index = 0; index < buttons.size(); ++index) {
       modifiers |= SyntheticPointerActionParams::GetWebMouseEventModifier(
           buttons[index]);
+    }
+    modifiers |= param.key_modifiers();
     if (modifiers_ != modifiers) {
       return testing::AssertionFailure() << "Pointer modifiers was "
                                          << modifiers_ << ", expected "
@@ -698,6 +700,54 @@
   buttons.pop_back();
 }
 
+TEST_F(SyntheticPointerActionTest, PointerMouseActionWithKey) {
+  CreateSyntheticPointerActionTarget<MockSyntheticPointerMouseActionTarget>();
+
+  // Send a mouse move.
+  SyntheticPointerActionParams param1 = SyntheticPointerActionParams(
+      SyntheticPointerActionParams::PointerActionType::MOVE);
+  param1.set_position(gfx::PointF(189, 62));
+  params_.PushPointerActionParams(param1);
+
+  // Move the mouse while alt and control keys are pressed.
+  SyntheticPointerActionParams param2 = SyntheticPointerActionParams(
+      SyntheticPointerActionParams::PointerActionType::MOVE);
+  param2.set_position(gfx::PointF(139, 98));
+  param2.set_key_modifiers(6);
+  params_.PushPointerActionParams(param2);
+
+  // Send a mouse down with left button while alt and control keys are pressed.
+  SyntheticPointerActionParams param3 = SyntheticPointerActionParams(
+      SyntheticPointerActionParams::PointerActionType::PRESS);
+  param3.set_position(gfx::PointF(139, 98));
+  param3.set_button(SyntheticPointerActionParams::Button::LEFT);
+  param3.set_key_modifiers(6);
+  params_.PushPointerActionParams(param3);
+  pointer_action_.reset(new SyntheticPointerAction(params_));
+
+  ForwardSyntheticPointerAction();
+  MockSyntheticPointerMouseActionTarget* pointer_mouse_target =
+      static_cast<MockSyntheticPointerMouseActionTarget*>(target_.get());
+  EXPECT_EQ(1, num_success_);
+  EXPECT_EQ(0, num_failure_);
+  std::vector<SyntheticPointerActionParams::Button> buttons;
+  EXPECT_TRUE(pointer_mouse_target->SyntheticMouseActionDispatchedCorrectly(
+      param1, 0, buttons));
+
+  ForwardSyntheticPointerAction();
+  EXPECT_EQ(2, num_success_);
+  EXPECT_EQ(0, num_failure_);
+  EXPECT_TRUE(pointer_mouse_target->SyntheticMouseActionDispatchedCorrectly(
+      param2, 0, buttons));
+
+  ForwardSyntheticPointerAction();
+  EXPECT_EQ(3, num_success_);
+  EXPECT_EQ(0, num_failure_);
+  buttons.push_back(SyntheticPointerActionParams::Button::LEFT);
+  EXPECT_TRUE(pointer_mouse_target->SyntheticMouseActionDispatchedCorrectly(
+      param3, 1, buttons, SyntheticPointerActionParams::Button::LEFT));
+}
+
 TEST_F(SyntheticPointerActionTest, PointerMouseActionTypeInvalid) {
   CreateSyntheticPointerActionTarget<MockSyntheticPointerMouseActionTarget>();
 
diff --git a/content/browser/renderer_host/input/synthetic_pointer_driver.h b/content/browser/renderer_host/input/synthetic_pointer_driver.h
index 4325654..abaf51cd 100644
--- a/content/browser/renderer_host/input/synthetic_pointer_driver.h
+++ b/content/browser/renderer_host/input/synthetic_pointer_driver.h
@@ -32,11 +32,13 @@
                      float y,
                      int index = 0,
                      SyntheticPointerActionParams::Button button =
-                         SyntheticPointerActionParams::Button::LEFT) = 0;
-  virtual void Move(float x, float y, int index = 0) = 0;
+                         SyntheticPointerActionParams::Button::LEFT,
+                     int key_modifiers = 0) = 0;
+  virtual void Move(float x, float y, int index = 0, int key_modifiers = 0) = 0;
   virtual void Release(int index = 0,
                        SyntheticPointerActionParams::Button button =
-                           SyntheticPointerActionParams::Button::LEFT) = 0;
+                           SyntheticPointerActionParams::Button::LEFT,
+                       int key_modifiers = 0) = 0;
   virtual void Leave(int index = 0) = 0;
 
   // Check if the user inputs in the SyntheticPointerActionParams can generate
diff --git a/content/browser/renderer_host/input/synthetic_touch_driver.cc b/content/browser/renderer_host/input/synthetic_touch_driver.cc
index e4b16de..40733b5 100644
--- a/content/browser/renderer_host/input/synthetic_touch_driver.cc
+++ b/content/browser/renderer_host/input/synthetic_touch_driver.cc
@@ -27,7 +27,8 @@
 void SyntheticTouchDriver::Press(float x,
                                  float y,
                                  int index,
-                                 SyntheticPointerActionParams::Button button) {
+                                 SyntheticPointerActionParams::Button button,
+                                 int key_modifiers) {
   DCHECK_GE(index, 0);
   DCHECK(pointer_id_map_.find(index) == pointer_id_map_.end());
   int touch_index = touch_event_.PressPoint(x, y);
@@ -35,15 +36,18 @@
   pointer_id_map_[index] = touch_index;
 }
 
-void SyntheticTouchDriver::Move(float x, float y, int index) {
+void SyntheticTouchDriver::Move(float x,
+                                float y,
+                                int index,
+                                int key_modifiers) {
   DCHECK_GE(index, 0);
   DCHECK(pointer_id_map_.find(index) != pointer_id_map_.end());
   touch_event_.MovePoint(pointer_id_map_[index], x, y);
 }
 
-void SyntheticTouchDriver::Release(
-    int index,
-    SyntheticPointerActionParams::Button button) {
+void SyntheticTouchDriver::Release(int index,
+                                   SyntheticPointerActionParams::Button button,
+                                   int key_modifiers) {
   DCHECK_GE(index, 0);
   DCHECK(pointer_id_map_.find(index) != pointer_id_map_.end());
   touch_event_.ReleasePoint(pointer_id_map_[index]);
diff --git a/content/browser/renderer_host/input/synthetic_touch_driver.h b/content/browser/renderer_host/input/synthetic_touch_driver.h
index bd2e8ec..2aa88c6e 100644
--- a/content/browser/renderer_host/input/synthetic_touch_driver.h
+++ b/content/browser/renderer_host/input/synthetic_touch_driver.h
@@ -26,11 +26,13 @@
              float y,
              int index,
              SyntheticPointerActionParams::Button button =
-                 SyntheticPointerActionParams::Button::LEFT) override;
-  void Move(float x, float y, int index) override;
+                 SyntheticPointerActionParams::Button::LEFT,
+             int key_modifiers = 0) override;
+  void Move(float x, float y, int index, int key_modifiers = 0) override;
   void Release(int index,
                SyntheticPointerActionParams::Button button =
-                   SyntheticPointerActionParams::Button::LEFT) override;
+                   SyntheticPointerActionParams::Button::LEFT,
+               int key_modifiers = 0) override;
   void Leave(int index) override;
 
   bool UserInputCheck(
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index 55fb237..15871fc3 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -155,6 +155,7 @@
 #include "content/common/view_messages.h"
 #include "content/common/widget_messages.h"
 #include "content/public/browser/browser_context.h"
+#include "content/public/browser/browser_or_resource_context.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/content_browser_client.h"
 #include "content/public/browser/network_service_instance.h"
@@ -1612,7 +1613,7 @@
 
   widget_helper_ = new RenderWidgetHelper();
 
-  ChildProcessSecurityPolicyImpl::GetInstance()->Add(GetID());
+  ChildProcessSecurityPolicyImpl::GetInstance()->Add(GetID(), browser_context);
 
   CHECK(!BrowserMainRunner::ExitedMainMessageLoop());
   RegisterHost(GetID(), this);
@@ -3981,7 +3982,7 @@
       SiteInstanceImpl::GetSiteForURL(browser_context, isolation_context, url,
                                       true /* should_use_effective_urls */);
   GURL lock_url = SiteInstanceImpl::DetermineProcessLockURL(
-      browser_context, isolation_context, url);
+      BrowserOrResourceContext(browser_context), isolation_context, url);
   return GetSoleProcessHostForSite(browser_context, isolation_context, site_url,
                                    lock_url);
 }
diff --git a/content/browser/site_instance_impl.cc b/content/browser/site_instance_impl.cc
index e1a7560..b8b2749a 100644
--- a/content/browser/site_instance_impl.cc
+++ b/content/browser/site_instance_impl.cc
@@ -16,6 +16,7 @@
 #include "content/browser/isolation_context.h"
 #include "content/browser/renderer_host/render_process_host_impl.h"
 #include "content/browser/storage_partition_impl.h"
+#include "content/public/browser/browser_or_resource_context.h"
 #include "content/public/browser/content_browser_client.h"
 #include "content/public/browser/render_process_host_factory.h"
 #include "content/public/browser/site_isolation_policy.h"
@@ -194,11 +195,12 @@
   // URL is invalid.
   has_site_ = true;
   BrowserContext* browser_context = browsing_instance_->browser_context();
-  site_ = GetSiteForURL(browser_context, GetIsolationContext(), url,
+  site_ = GetSiteForURL(BrowserOrResourceContext(browser_context),
+                        GetIsolationContext(), url,
                         true /* should_use_effective_urls */);
   original_url_ = url;
-  lock_url_ =
-      DetermineProcessLockURL(browser_context, GetIsolationContext(), url);
+  lock_url_ = DetermineProcessLockURL(BrowserOrResourceContext(browser_context),
+                                      GetIsolationContext(), url);
 
   // Now that we have a site, register it with the BrowsingInstance.  This
   // ensures that we won't create another SiteInstance for this site within
@@ -280,7 +282,8 @@
   GURL site_url = SiteInstanceImpl::GetSiteForURL(
       browsing_instance_->browser_context(), GetIsolationContext(), url);
   GURL origin_lock = DetermineProcessLockURL(
-      browsing_instance_->browser_context(), GetIsolationContext(), url);
+      BrowserOrResourceContext(browsing_instance_->browser_context()),
+      GetIsolationContext(), url);
   return !RenderProcessHostImpl::IsSuitableHost(
       GetProcess(), browsing_instance_->browser_context(),
       GetIsolationContext(), site_url, origin_lock);
@@ -434,6 +437,9 @@
 // static
 GURL SiteInstance::GetSiteForURL(BrowserContext* browser_context,
                                  const GURL& url) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK(browser_context);
+
   // By default, GetSiteForURL will resolve |real_url| to an effective URL
   // before computing its site, so set |should_use_effective_urls| to true.
   //
@@ -443,24 +449,33 @@
   // where needed.  Eventually, GetSiteForURL should always require an
   // IsolationContext to be passed in, and this implementation should just
   // become SiteInstanceImpl::GetSiteForURL.
-  return SiteInstanceImpl::GetSiteForURL(browser_context, IsolationContext(),
-                                         url,
-                                         true /* should_use_effective_urls */);
+  return SiteInstanceImpl::GetSiteForURL(
+      BrowserOrResourceContext(browser_context), IsolationContext(), url,
+      true /* should_use_effective_urls */);
 }
 
 // static
 GURL SiteInstanceImpl::DetermineProcessLockURL(
-    BrowserContext* browser_context,
+    const BrowserOrResourceContext& context,
     const IsolationContext& isolation_context,
     const GURL& url) {
   // For the process lock URL, convert |url| to a site without resolving |url|
   // to an effective URL.
-  return SiteInstanceImpl::GetSiteForURL(browser_context, isolation_context,
-                                         url,
+  return SiteInstanceImpl::GetSiteForURL(context, isolation_context, url,
                                          false /* should_use_effective_urls */);
 }
 
-GURL SiteInstanceImpl::GetSiteForURL(BrowserContext* browser_context,
+// static
+GURL SiteInstanceImpl::GetSiteForURL(BrowserContext* context,
+                                     const IsolationContext& isolation_context,
+                                     const GURL& url,
+                                     bool should_use_effective_urls) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  return GetSiteForURL(BrowserOrResourceContext(context), isolation_context,
+                       url, should_use_effective_urls);
+}
+
+GURL SiteInstanceImpl::GetSiteForURL(const BrowserOrResourceContext& context,
                                      const IsolationContext& isolation_context,
                                      const GURL& real_url,
                                      bool should_use_effective_urls) {
@@ -468,8 +483,12 @@
   if (real_url.SchemeIs(kGuestScheme))
     return real_url;
 
+  if (should_use_effective_urls)
+    DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
   GURL url = should_use_effective_urls
-                 ? SiteInstanceImpl::GetEffectiveURL(browser_context, real_url)
+                 ? SiteInstanceImpl::GetEffectiveURL(context.ToBrowserContext(),
+                                                     real_url)
                  : real_url;
   url::Origin origin = url::Origin::Create(url);
 
@@ -499,7 +518,7 @@
     // a proper security principal.
     if (should_use_effective_urls && url != real_url) {
       std::string non_translated_site_url(
-          GetSiteForURL(browser_context, isolation_context, real_url,
+          GetSiteForURL(context, isolation_context, real_url,
                         false /* should_use_effective_urls */)
               .spec());
       GURL::Replacements replacements;
@@ -573,6 +592,7 @@
 // static
 GURL SiteInstanceImpl::GetEffectiveURL(BrowserContext* browser_context,
                                        const GURL& url) {
+  DCHECK(browser_context);
   return GetContentClient()->browser()->GetEffectiveURL(browser_context, url);
 }
 
diff --git a/content/browser/site_instance_impl.h b/content/browser/site_instance_impl.h
index da2696d..0ab3f78 100644
--- a/content/browser/site_instance_impl.h
+++ b/content/browser/site_instance_impl.h
@@ -20,6 +20,7 @@
 
 namespace content {
 class BrowsingInstance;
+class BrowserOrResourceContext;
 class RenderProcessHostFactory;
 
 class CONTENT_EXPORT SiteInstanceImpl final : public SiteInstance,
@@ -136,6 +137,13 @@
   // |should_use_effective_urls| defaults to true and specifies whether to
   // resolve |url| to an effective URL (via
   // ContentBrowserClient::GetEffectiveURL()) before determining the site.
+  static GURL GetSiteForURL(const BrowserOrResourceContext& context,
+                            const IsolationContext& isolation_context,
+                            const GURL& url,
+                            bool should_use_effective_urls = true);
+
+  // TODO(acolwell): Remove after all call sites have been updated to use
+  // BrowserOrResourceContext.
   static GURL GetSiteForURL(BrowserContext* context,
                             const IsolationContext& isolation_context,
                             const GURL& url,
@@ -151,7 +159,7 @@
   // Returns the URL to which a process should be locked for the given URL.
   // This is computed similarly to the site URL (see GetSiteForURL), but
   // without resolving effective URLs.
-  static GURL DetermineProcessLockURL(BrowserContext* context,
+  static GURL DetermineProcessLockURL(const BrowserOrResourceContext& context,
                                       const IsolationContext& isolation_context,
                                       const GURL& url);
 
diff --git a/content/browser/site_instance_impl_unittest.cc b/content/browser/site_instance_impl_unittest.cc
index 90a2d2a..a70a674 100644
--- a/content/browser/site_instance_impl_unittest.cc
+++ b/content/browser/site_instance_impl_unittest.cc
@@ -26,6 +26,7 @@
 #include "content/browser/web_contents/web_contents_impl.h"
 #include "content/browser/webui/content_web_ui_controller_factory.h"
 #include "content/browser/webui/web_ui_controller_factory_registry.h"
+#include "content/public/browser/browser_or_resource_context.h"
 #include "content/public/common/bindings_policy.h"
 #include "content/public/common/content_client.h"
 #include "content/public/common/content_constants.h"
@@ -425,7 +426,6 @@
 
 // Test that process lock URLs are computed without using effective URLs.
 TEST_F(SiteInstanceTest, ProcessLockDoesNotUseEffectiveURL) {
-  TestBrowserContext context;
   GURL test_url("https://some.app.foo.com/");
   GURL nonapp_site_url("https://foo.com/");
   GURL app_url("https://app.com/");
@@ -440,12 +440,13 @@
   // URL's site (app.com) and the original URL's site (foo.com).
   GURL expected_app_site_url(app_url.spec() + "#" + nonapp_site_url.spec());
   {
+    BrowserOrResourceContext context(browser_context.get());
     GURL site_url = SiteInstanceImpl::GetSiteForURL(
-        &context, isolation_context, test_url, false /* use_effective_urls */);
+        context, isolation_context, test_url, false /* use_effective_urls */);
     EXPECT_EQ(nonapp_site_url, site_url);
 
     site_url = SiteInstanceImpl::GetSiteForURL(
-        &context, isolation_context, test_url, true /* use_effective_urls */);
+        context, isolation_context, test_url, true /* use_effective_urls */);
     EXPECT_EQ(expected_app_site_url, site_url);
   }
 
diff --git a/content/browser/site_per_process_browsertest.cc b/content/browser/site_per_process_browsertest.cc
index 3b17827..c9dc674 100644
--- a/content/browser/site_per_process_browsertest.cc
+++ b/content/browser/site_per_process_browsertest.cc
@@ -2007,15 +2007,9 @@
 // nested <iframe>'s after the inner-most frame scrolls into view. The
 // measurements are for two identical pages where one page does not have any
 // OOPIFs while the other has some nested OOPIFs.
-#if defined(OS_LINUX)
-// crbug.com/827431
-#define MAYBE_ScrollElementIntoView DISABLED_ScrollElementIntoView
-#else
-#define MAYBE_ScrollElementIntoView ScrollElementIntoView
-#endif
-
+// TODO(crbug.com/827431): This test is flaking on all platforms.
 IN_PROC_BROWSER_TEST_F(SitePerProcessProgrammaticScrollTest,
-                       MAYBE_ScrollElementIntoView) {
+                       DISABLED_ScrollElementIntoView) {
   const GURL url_a(
       embedded_test_server()->GetURL("a.com", kIframeOutOfViewHTML));
   const GURL url_b(
diff --git a/content/common/input/actions_parser.cc b/content/common/input/actions_parser.cc
index 1057083..39ffd83 100644
--- a/content/common/input/actions_parser.cc
+++ b/content/common/input/actions_parser.cc
@@ -55,6 +55,24 @@
   return SyntheticPointerActionParams::Button();
 }
 
+int ToKeyModifiers(std::string key) {
+  if (key == "Alt")
+    return blink::WebInputEvent::kAltKey;
+  if (key == "Control")
+    return blink::WebInputEvent::kControlKey;
+  if (key == "Meta")
+    return blink::WebInputEvent::kMetaKey;
+  if (key == "Shift")
+    return blink::WebInputEvent::kShiftKey;
+  if (key == "CapsLock")
+    return blink::WebInputEvent::kCapsLockOn;
+  if (key == "NumLock")
+    return blink::WebInputEvent::kNumLockOn;
+  if (key == "AltGraph")
+    return blink::WebInputEvent::kAltGrKey;
+  return 0;
+}
+
 }  // namespace
 
 ActionsParser::ActionsParser(base::Value* pointer_actions_value)
@@ -212,8 +230,7 @@
     if (pointer_type != "touch" && action_index_ > 0) {
       error_message_ = std::string(
           "for input source type of mouse and pen, we only support one device "
-          "in "
-          "one sequence");
+          "in one sequence");
       return false;
     }
 
@@ -339,6 +356,25 @@
   SyntheticPointerActionParams::Button button =
       ToSyntheticMouseButton(button_id);
 
+  int key_modifiers = 0;
+  std::string keys;
+  if (action.HasKey("keys") && !action.GetString("keys", &keys)) {
+    error_message_ = base::StringPrintf(
+        "actions[%d].actions.key is not a string", action_index_);
+    return false;
+  }
+  std::vector<std::string> key_list =
+      base::SplitString(keys, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
+  for (std::string& key : key_list) {
+    int key_modifier = ToKeyModifiers(key);
+    if (key_modifier == 0) {
+      error_message_ = base::StringPrintf(
+          "actions[%d].actions.key is not a valid key", action_index_);
+      return false;
+    }
+    key_modifiers |= key_modifier;
+  }
+
   double duration = 0;
   int num_idle = 0;
   if (pointer_action_type ==
@@ -366,12 +402,15 @@
     case SyntheticPointerActionParams::PointerActionType::PRESS:
       action_param.set_position(gfx::PointF(position_x, position_y));
       action_param.set_button(button);
+      action_param.set_key_modifiers(key_modifiers);
       break;
     case SyntheticPointerActionParams::PointerActionType::MOVE:
       action_param.set_position(gfx::PointF(position_x, position_y));
+      action_param.set_key_modifiers(key_modifiers);
       break;
     case SyntheticPointerActionParams::PointerActionType::RELEASE:
       action_param.set_button(button);
+      action_param.set_key_modifiers(key_modifiers);
       break;
     case SyntheticPointerActionParams::PointerActionType::LEAVE:
     case SyntheticPointerActionParams::PointerActionType::IDLE:
diff --git a/content/common/input/actions_parser_unittest.cc b/content/common/input/actions_parser_unittest.cc
index 05c79937..03407e2 100644
--- a/content/common/input/actions_parser_unittest.cc
+++ b/content/common/input/actions_parser_unittest.cc
@@ -463,4 +463,16 @@
       actions_parser.error_message());
 }
 
+TEST(ActionsParserTest, OldParsePointerActionSequenceInvalidKey) {
+  std::unique_ptr<base::Value> value = base::JSONReader::Read(
+      R"( [{"source": "mouse", "id": 0,
+            "actions": [{"name": "pointerDown", "x": 3, "y": 5,
+                         "keys": "Ctrl"} ]}] )");
+
+  ActionsParser actions_parser(value.get());
+  EXPECT_FALSE(actions_parser.ParsePointerActionSequence());
+  EXPECT_EQ("actions[0].actions.key is not a valid key",
+            actions_parser.error_message());
+}
+
 }  // namespace content
diff --git a/content/common/input/synthetic_pointer_action_params.cc b/content/common/input/synthetic_pointer_action_params.cc
index 0f93e358..1987447 100644
--- a/content/common/input/synthetic_pointer_action_params.cc
+++ b/content/common/input/synthetic_pointer_action_params.cc
@@ -9,13 +9,15 @@
 SyntheticPointerActionParams::SyntheticPointerActionParams()
     : pointer_action_type_(PointerActionType::NOT_INITIALIZED),
       pointer_id_(0),
-      button_(Button::LEFT) {}
+      button_(Button::LEFT),
+      key_modifiers_(0) {}
 
 SyntheticPointerActionParams::SyntheticPointerActionParams(
     PointerActionType action_type)
     : pointer_action_type_(action_type),
       pointer_id_(0),
-      button_(Button::LEFT) {}
+      button_(Button::LEFT),
+      key_modifiers_(0) {}
 
 SyntheticPointerActionParams::~SyntheticPointerActionParams() {}
 
diff --git a/content/common/input/synthetic_pointer_action_params.h b/content/common/input/synthetic_pointer_action_params.h
index 7a8ec22..cea60c9 100644
--- a/content/common/input/synthetic_pointer_action_params.h
+++ b/content/common/input/synthetic_pointer_action_params.h
@@ -66,6 +66,11 @@
     button_ = button;
   }
 
+  void set_key_modifiers(int key_modifiers) {
+    DCHECK_NE(PointerActionType::IDLE, pointer_action_type_);
+    key_modifiers_ = key_modifiers;
+  }
+
   PointerActionType pointer_action_type() const { return pointer_action_type_; }
 
   uint32_t pointer_id() const { return pointer_id_; }
@@ -82,6 +87,11 @@
     return button_;
   }
 
+  int key_modifiers() const {
+    DCHECK_NE(PointerActionType::IDLE, pointer_action_type_);
+    return key_modifiers_;
+  }
+
   static unsigned GetWebMouseEventModifier(
       SyntheticPointerActionParams::Button button);
   static blink::WebMouseEvent::Button GetWebMouseEventButton(
@@ -99,6 +109,10 @@
   // The id of the pointer given by the users.
   uint32_t pointer_id_;
   Button button_;
+  // “Alt“, ”Control“, ”Meta“, ”Shift“, ”CapsLock“, ”NumLock“, ”AltGraph”
+  // buttons are supported right now. It stores a matching modifiers defined
+  // in WebInputEvent class.
+  int key_modifiers_;
 };
 
 }  // namespace content
diff --git a/content/common/input_messages.h b/content/common/input_messages.h
index 299e214..e870323f 100644
--- a/content/common/input_messages.h
+++ b/content/common/input_messages.h
@@ -144,6 +144,7 @@
   IPC_STRUCT_TRAITS_MEMBER(pointer_id_)
   IPC_STRUCT_TRAITS_MEMBER(position_)
   IPC_STRUCT_TRAITS_MEMBER(button_)
+  IPC_STRUCT_TRAITS_MEMBER(key_modifiers_)
 IPC_STRUCT_TRAITS_END()
 
 IPC_STRUCT_TRAITS_BEGIN(content::SyntheticPointerActionListParams)
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/scheduler/NativePostTaskTest.java b/content/public/android/javatests/src/org/chromium/content/browser/scheduler/NativePostTaskTest.java
index dc3f537..fcfc254 100644
--- a/content/public/android/javatests/src/org/chromium/content/browser/scheduler/NativePostTaskTest.java
+++ b/content/public/android/javatests/src/org/chromium/content/browser/scheduler/NativePostTaskTest.java
@@ -7,8 +7,6 @@
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.collection.IsIterableContainingInOrder.contains;
 
-import android.annotation.TargetApi;
-import android.os.Build;
 import android.support.test.filters.MediumTest;
 
 import org.junit.After;
@@ -23,7 +21,6 @@
 import org.chromium.base.test.BaseJUnit4ClassRunner;
 import org.chromium.base.test.task.SchedulerTestHelpers;
 import org.chromium.base.test.task.TaskSchedulerTestHelpers;
-import org.chromium.base.test.util.MinAndroidSdkLevel;
 import org.chromium.content.app.ContentMain;
 import org.chromium.content_public.browser.test.NativeLibraryTestRule;
 
@@ -38,8 +35,6 @@
  * instead.
  */
 @RunWith(BaseJUnit4ClassRunner.class)
-@MinAndroidSdkLevel(23)
-@TargetApi(Build.VERSION_CODES.M)
 public class NativePostTaskTest {
     @Rule
     public NativeLibraryTestRule mNativeLibraryTestRule = new NativeLibraryTestRule();
@@ -123,18 +118,22 @@
     }
 
     private void testRunningTasksInSequence(TaskRunner taskQueue) {
-        List<Integer> orderListImmediate = new ArrayList<>();
-        List<Integer> orderListDelayed = new ArrayList<>();
+        try {
+            List<Integer> orderListImmediate = new ArrayList<>();
+            List<Integer> orderListDelayed = new ArrayList<>();
 
-        SchedulerTestHelpers.postThreeTasksInOrder(taskQueue, orderListImmediate);
-        SchedulerTestHelpers.postTaskAndBlockUntilRun(taskQueue);
+            SchedulerTestHelpers.postThreeTasksInOrder(taskQueue, orderListImmediate);
+            SchedulerTestHelpers.postTaskAndBlockUntilRun(taskQueue);
 
-        assertThat(orderListImmediate, contains(1, 2, 3));
+            assertThat(orderListImmediate, contains(1, 2, 3));
 
-        SchedulerTestHelpers.postThreeDelayedTasksInOrder(taskQueue, orderListDelayed);
-        SchedulerTestHelpers.postDelayedTaskAndBlockUntilRun(taskQueue, 1);
+            SchedulerTestHelpers.postThreeDelayedTasksInOrder(taskQueue, orderListDelayed);
+            SchedulerTestHelpers.postDelayedTaskAndBlockUntilRun(taskQueue, 1);
 
-        assertThat(orderListDelayed, contains(1, 2, 3));
+            assertThat(orderListDelayed, contains(1, 2, 3));
+        } finally {
+            taskQueue.destroy();
+        }
     }
 
     @Test
@@ -184,6 +183,7 @@
                 }
             }
         }, 1);
+        taskQueue.destroy();
 
         // We verify that the task didn't get scheduled before the native scheduler is initialised
         Assert.assertFalse(taskExecuted.get());
@@ -208,7 +208,11 @@
         List<Integer> orderListImmediate = new ArrayList<>();
         List<Integer> orderListDelayed = new ArrayList<>();
         TaskRunner taskQueue = PostTask.createSequencedTaskRunner(new TaskTraits());
-        performSequencedTestSchedulerMigration(taskQueue, orderListImmediate, orderListDelayed);
+        try {
+            performSequencedTestSchedulerMigration(taskQueue, orderListImmediate, orderListDelayed);
+        } finally {
+            taskQueue.destroy();
+        }
 
         assertThat(orderListImmediate, contains(1, 2, 3, 4));
         assertThat(orderListDelayed, contains(1, 2, 3));
@@ -220,7 +224,11 @@
         List<Integer> orderListImmediate = new ArrayList<>();
         List<Integer> orderListDelayed = new ArrayList<>();
         TaskRunner taskQueue = PostTask.createSingleThreadTaskRunner(new TaskTraits());
-        performSequencedTestSchedulerMigration(taskQueue, orderListImmediate, orderListDelayed);
+        try {
+            performSequencedTestSchedulerMigration(taskQueue, orderListImmediate, orderListDelayed);
+        } finally {
+            taskQueue.destroy();
+        }
 
         assertThat(orderListImmediate, contains(1, 2, 3, 4));
         assertThat(orderListDelayed, contains(1, 2, 3));
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/scheduler/UiThreadSchedulerTest.java b/content/public/android/javatests/src/org/chromium/content/browser/scheduler/UiThreadSchedulerTest.java
index 30ff6d5..26042cc0 100644
--- a/content/public/android/javatests/src/org/chromium/content/browser/scheduler/UiThreadSchedulerTest.java
+++ b/content/public/android/javatests/src/org/chromium/content/browser/scheduler/UiThreadSchedulerTest.java
@@ -7,8 +7,6 @@
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.collection.IsIterableContainingInOrder.contains;
 
-import android.annotation.TargetApi;
-import android.os.Build;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.support.test.filters.MediumTest;
@@ -26,7 +24,6 @@
 import org.chromium.base.task.TaskTraits;
 import org.chromium.base.test.BaseJUnit4ClassRunner;
 import org.chromium.base.test.task.SchedulerTestHelpers;
-import org.chromium.base.test.util.MinAndroidSdkLevel;
 import org.chromium.content.app.ContentMain;
 import org.chromium.content_public.browser.BrowserTaskExecutor;
 import org.chromium.content_public.browser.UiThreadTaskTraits;
@@ -40,8 +37,6 @@
  * Test class for scheduling on the UI Thread.
  */
 @RunWith(BaseJUnit4ClassRunner.class)
-@MinAndroidSdkLevel(23)
-@TargetApi(Build.VERSION_CODES.M)
 public class UiThreadSchedulerTest {
     @Rule
     public NativeLibraryTestRule mNativeLibraryTestRule = new NativeLibraryTestRule();
@@ -69,13 +64,17 @@
     public void testSimpleUiThreadPostingBeforeNativeLoaded() throws Exception {
         TaskRunner uiThreadTaskRunner =
                 PostTask.createSingleThreadTaskRunner(new UiThreadTaskTraits());
-        List<Integer> orderList = new ArrayList<>();
-        SchedulerTestHelpers.postRecordOrderTask(uiThreadTaskRunner, orderList, 1);
-        SchedulerTestHelpers.postRecordOrderTask(uiThreadTaskRunner, orderList, 2);
-        SchedulerTestHelpers.postRecordOrderTask(uiThreadTaskRunner, orderList, 3);
-        SchedulerTestHelpers.postTaskAndBlockUntilRun(uiThreadTaskRunner);
+        try {
+            List<Integer> orderList = new ArrayList<>();
+            SchedulerTestHelpers.postRecordOrderTask(uiThreadTaskRunner, orderList, 1);
+            SchedulerTestHelpers.postRecordOrderTask(uiThreadTaskRunner, orderList, 2);
+            SchedulerTestHelpers.postRecordOrderTask(uiThreadTaskRunner, orderList, 3);
+            SchedulerTestHelpers.postTaskAndBlockUntilRun(uiThreadTaskRunner);
 
-        assertThat(orderList, contains(1, 2, 3));
+            assertThat(orderList, contains(1, 2, 3));
+        } finally {
+            uiThreadTaskRunner.destroy();
+        }
     }
 
     @Test
@@ -83,18 +82,21 @@
     public void testUiThreadTaskRunnerMigrationToNative() throws Exception {
         TaskRunner uiThreadTaskRunner =
                 PostTask.createSingleThreadTaskRunner(new UiThreadTaskTraits());
-        List<Integer> orderList = new ArrayList<>();
-        SchedulerTestHelpers.postRecordOrderTask(uiThreadTaskRunner, orderList, 1);
+        try {
+            List<Integer> orderList = new ArrayList<>();
+            SchedulerTestHelpers.postRecordOrderTask(uiThreadTaskRunner, orderList, 1);
 
-        postRepeatingTaskAndStartNativeSchedulerThenWaitForTaskToRun(
-                uiThreadTaskRunner, new Runnable() {
-                    @Override
-                    public void run() {
-                        orderList.add(2);
-                    }
-                });
-
-        assertThat(orderList, contains(1, 2));
+            postRepeatingTaskAndStartNativeSchedulerThenWaitForTaskToRun(
+                    uiThreadTaskRunner, new Runnable() {
+                        @Override
+                        public void run() {
+                            orderList.add(2);
+                        }
+                    });
+            assertThat(orderList, contains(1, 2));
+        } finally {
+            uiThreadTaskRunner.destroy();
+        }
     }
 
     @Test
@@ -103,33 +105,40 @@
         TaskRunner uiThreadTaskRunner =
                 PostTask.createSingleThreadTaskRunner(new UiThreadTaskTraits());
 
-        startContentMainOnUiThread();
+        try {
+            startContentMainOnUiThread();
 
-        uiThreadTaskRunner.postTask(new Runnable() {
-            @Override
-            public void run() {
-                Assert.assertTrue(ThreadUtils.runningOnUiThread());
-            }
-        });
+            uiThreadTaskRunner.postTask(new Runnable() {
+                @Override
+                public void run() {
+                    Assert.assertTrue(ThreadUtils.runningOnUiThread());
+                }
+            });
 
-        SchedulerTestHelpers.postTaskAndBlockUntilRun(uiThreadTaskRunner);
+            SchedulerTestHelpers.postTaskAndBlockUntilRun(uiThreadTaskRunner);
+        } finally {
+            uiThreadTaskRunner.destroy();
+        }
     }
 
     @Test
     @MediumTest
     public void testTaskNotRunOnUiThreadWithoutUiThreadTaskTraits() throws Exception {
         TaskRunner uiThreadTaskRunner = PostTask.createSingleThreadTaskRunner(new TaskTraits());
+        try {
+            startContentMainOnUiThread();
 
-        startContentMainOnUiThread();
+            uiThreadTaskRunner.postTask(new Runnable() {
+                @Override
+                public void run() {
+                    Assert.assertFalse(ThreadUtils.runningOnUiThread());
+                }
+            });
 
-        uiThreadTaskRunner.postTask(new Runnable() {
-            @Override
-            public void run() {
-                Assert.assertFalse(ThreadUtils.runningOnUiThread());
-            }
-        });
-
-        SchedulerTestHelpers.postTaskAndBlockUntilRun(uiThreadTaskRunner);
+            SchedulerTestHelpers.postTaskAndBlockUntilRun(uiThreadTaskRunner);
+        } finally {
+            uiThreadTaskRunner.destroy();
+        }
     }
 
     private void startContentMainOnUiThread() {
diff --git a/content/public/browser/BUILD.gn b/content/public/browser/BUILD.gn
index 0d9061f..517045a 100644
--- a/content/public/browser/BUILD.gn
+++ b/content/public/browser/BUILD.gn
@@ -76,6 +76,7 @@
     "browser_main_runner.h",
     "browser_message_filter.cc",
     "browser_message_filter.h",
+    "browser_or_resource_context.h",
     "browser_plugin_guest_delegate.cc",
     "browser_plugin_guest_delegate.h",
     "browser_plugin_guest_manager.cc",
diff --git a/content/public/browser/browser_or_resource_context.h b/content/public/browser/browser_or_resource_context.h
new file mode 100644
index 0000000..cacd119
--- /dev/null
+++ b/content/public/browser/browser_or_resource_context.h
@@ -0,0 +1,98 @@
+// 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.
+
+#ifndef CONTENT_PUBLIC_BROWSER_BROWSER_OR_RESOURCE_CONTEXT_H_
+#define CONTENT_PUBLIC_BROWSER_BROWSER_OR_RESOURCE_CONTEXT_H_
+
+#include <type_traits>
+
+#include "base/logging.h"
+#include "content/public/browser/browser_thread.h"
+
+namespace content {
+
+class BrowserContext;
+class ResourceContext;
+
+// A class holding either a BrowserContext* or a ResourceContext*.
+// This class should hold a BrowserContext* when constructed on the UI thread
+// and a ResourceContext* when constructed on the IO thread. This object must
+// only be accessed on the thread it was constructed and does not allow
+// converting between the two pointer types.
+class BrowserOrResourceContext final {
+ public:
+  BrowserOrResourceContext() {
+    union_.browser_context_ = nullptr;
+    flavour_ = kNullFlavour;
+  }
+
+  // BrowserOrResourceContext is implicitly constructible from either
+  // BrowserContext* or ResourceContext*.  Neither of the constructor arguments
+  // can be null (enforced by DCHECKs and in some cases at compile time).
+  explicit BrowserOrResourceContext(BrowserContext* browser_context) {
+    DCHECK(browser_context);
+    DCHECK_CURRENTLY_ON(BrowserThread::UI);
+    union_.browser_context_ = browser_context;
+    flavour_ = kBrowserContextFlavour;
+  }
+
+  explicit BrowserOrResourceContext(ResourceContext* resource_context) {
+    DCHECK(resource_context);
+    DCHECK_CURRENTLY_ON(BrowserThread::IO);
+    union_.resource_context_ = resource_context;
+    flavour_ = kResourceContextFlavour;
+  }
+  BrowserOrResourceContext(nullptr_t) = delete;
+
+  // BrowserOrResourceContext has a trivial, default destructor.
+  ~BrowserOrResourceContext() = default;
+
+  // BrowserOrResourceContext is trivially copyable.
+  BrowserOrResourceContext(const BrowserOrResourceContext& other) = default;
+  BrowserOrResourceContext& operator=(const BrowserOrResourceContext& other) =
+      default;
+
+  explicit operator bool() const {
+    return (union_.resource_context_ != nullptr &&
+            union_.browser_context_ != nullptr);
+  }
+
+  // To be called only on the UI thread.  In DCHECK-enabled builds will verify
+  // that this object has kBrowserContextFlavour (implying that the returned
+  // BrowserContext* is valid and non-null.
+  BrowserContext* ToBrowserContext() const {
+    DCHECK_CURRENTLY_ON(BrowserThread::UI);
+    CHECK_EQ(kBrowserContextFlavour, flavour_);
+    return union_.browser_context_;
+  }
+
+  // To be called only on the IO thread.  In DCHECK-enabled builds will verify
+  // that this object has kResourceContextFlavour (implying that the returned
+  // ResourceContext* is valid and non-null.
+  ResourceContext* ToResourceContext() const {
+    DCHECK_CURRENTLY_ON(BrowserThread::IO);
+    CHECK_EQ(kResourceContextFlavour, flavour_);
+    return union_.resource_context_;
+  }
+
+ private:
+  union Union {
+    BrowserContext* browser_context_;
+    ResourceContext* resource_context_;
+  } union_;
+
+  enum Flavour {
+    kNullFlavour,
+    kBrowserContextFlavour,
+    kResourceContextFlavour,
+  } flavour_;
+};
+
+static_assert(
+    std::is_trivially_copyable<BrowserOrResourceContext>::value,
+    "BrowserOrResourceContext should be trivially copyable in release builds.");
+
+}  // namespace content
+
+#endif  // CONTENT_PUBLIC_BROWSER_BROWSER_OR_RESOURCE_CONTEXT_H_
diff --git a/content/public/test/mock_render_process_host.cc b/content/public/test/mock_render_process_host.cc
index 4be6589..d1429ce9 100644
--- a/content/public/test/mock_render_process_host.cc
+++ b/content/public/test/mock_render_process_host.cc
@@ -66,7 +66,7 @@
       weak_ptr_factory_(this) {
   // Child process security operations can't be unit tested unless we add
   // ourselves as an existing child process.
-  ChildProcessSecurityPolicyImpl::GetInstance()->Add(GetID());
+  ChildProcessSecurityPolicyImpl::GetInstance()->Add(GetID(), browser_context);
 
   RenderProcessHostImpl::RegisterHost(GetID(), this);
 }
diff --git a/content/public/test/navigation_simulator.h b/content/public/test/navigation_simulator.h
index 6e93880..06899619 100644
--- a/content/public/test/navigation_simulator.h
+++ b/content/public/test/navigation_simulator.h
@@ -127,10 +127,10 @@
       const GURL& original_url,
       RenderFrameHost* render_frame_host);
 
-  // Creates a NavigationSimulator for an already-started browser initiated
-  // navigation via LoadURL / Reload / GoToOffset. Can be used to drive the
-  // navigation to completion.
-  static std::unique_ptr<NavigationSimulator> CreateFromPendingBrowserInitiated(
+  // Creates a NavigationSimulator for an already-started navigation via
+  // LoadURL / Reload / GoToOffset / history.GoBack() scripts, etc. Can be used
+  // to drive the navigation to completion.
+  static std::unique_ptr<NavigationSimulator> CreateFromPending(
       WebContents* contents);
 
   virtual ~NavigationSimulator() {}
diff --git a/content/public/test/test_renderer_host.cc b/content/public/test/test_renderer_host.cc
index 531d71ab..d1b38ba 100644
--- a/content/public/test/test_renderer_host.cc
+++ b/content/public/test/test_renderer_host.cc
@@ -25,6 +25,7 @@
 #include "content/public/common/navigation_policy.h"
 #include "content/public/test/browser_side_navigation_test_utils.h"
 #include "content/public/test/mock_render_process_host.h"
+#include "content/public/test/navigation_simulator.h"
 #include "content/public/test/test_browser_context.h"
 #include "content/test/content_browser_sanity_checker.h"
 #include "content/test/test_render_frame_host.h"
@@ -77,42 +78,12 @@
     NavigationController* controller) {
   // This function is currently used by BrowserWithTestWindowTest. It would be
   // ideal to instead make the users of that class create TestWebContents
-  // (rather than WebContentsImpl directly). This would allow the implementation
-  // of PrepareForCommitIfNecessary() to live directly in
-  // TestWebContents::CommitPendingNavigation() which could then be the only
-  // place to handle this simulation. Unfortunately, it is not trivial to make
-  // that change, so for now we have this extra simulation for
+  // (rather than WebContentsImpl directly). It is not trivial to make
+  // that change, so for now we have this extra function for
   // non-TestWebContents.
-  TestRenderFrameHost* old_rfh = static_cast<TestRenderFrameHost*>(
-      controller->GetWebContents()->GetMainFrame());
-  NavigationRequest* request = old_rfh->frame_tree_node()->navigation_request();
-  old_rfh->PrepareForCommitIfNecessary();
-
-  WebContentsImpl* web_contents =
-      static_cast<WebContentsImpl*>(controller->GetWebContents());
-  TestRenderFrameHost* pending_rfh = static_cast<TestRenderFrameHost*>(
-      web_contents->GetRenderManagerForTesting()
-          ->speculative_render_frame_host_.get());
-
-  // Commit on the pending_rfh, if one exists.
-  TestRenderFrameHost* test_rfh = pending_rfh ? pending_rfh : old_rfh;
-  if (request && !request->navigation_handle()->IsSameDocument()) {
-    test_rfh->SimulateCommitProcessed(
-        request->navigation_handle()->GetNavigationId(),
-        true /* was successful */);
-  }
-
-  if (controller->GetPendingEntryIndex() >= 0) {
-    test_rfh->SendNavigateWithTransition(
-        controller->GetPendingEntry()->GetUniqueID(), false,
-        controller->GetPendingEntry()->GetURL(),
-        controller->GetPendingEntry()->GetTransitionType());
-  } else {
-    test_rfh->SendNavigateWithTransition(
-        controller->GetPendingEntry()->GetUniqueID(), true,
-        controller->GetPendingEntry()->GetURL(),
-        controller->GetPendingEntry()->GetTransitionType());
-  }
+  auto navigation =
+      NavigationSimulator::CreateFromPending(controller->GetWebContents());
+  navigation->Commit();
 }
 
 // RenderViewHostTester -------------------------------------------------------
diff --git a/content/public/test/test_renderer_host.h b/content/public/test/test_renderer_host.h
index 478c713..17e189fe 100644
--- a/content/public/test/test_renderer_host.h
+++ b/content/public/test/test_renderer_host.h
@@ -71,6 +71,8 @@
   static bool TestOnMessageReceived(RenderFrameHost* rfh,
                                     const IPC::Message& msg);
 
+  // Commit the load pending in the given |controller| if any.
+  // TODO(ahemery): This should take a WebContents directly.
   static void CommitPendingLoad(NavigationController* controller);
 
   virtual ~RenderFrameHostTester() {}
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index 3adc000..87d6fc2 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -3300,9 +3300,9 @@
     DecodeDataURL(common_params, commit_params, &mime_type, &charset, &data,
                   &base_url);
     navigation_params->url = base_url;
-    navigation_params->data = WebData(data.c_str(), data.length());
-    navigation_params->mime_type = WebString::FromUTF8(mime_type);
-    navigation_params->text_encoding = WebString::FromUTF8(charset);
+    WebNavigationParams::FillStaticResponse(navigation_params.get(),
+                                            WebString::FromUTF8(mime_type),
+                                            WebString::FromUTF8(charset), data);
     // Needed so that history-url-only changes don't become reloads.
     navigation_params->unreachable_url = common_params.history_url_for_data_url;
   } else {
@@ -3456,10 +3456,8 @@
       BuildServiceWorkerNetworkProviderForNavigation(&commit_params, nullptr);
   FillMiscNavigationParams(common_params, commit_params,
                            navigation_params.get());
-
-  navigation_params->data = WebData(error_html.data(), error_html.length());
-  navigation_params->mime_type = "text/html";
-  navigation_params->text_encoding = "UTF-8";
+  WebNavigationParams::FillStaticResponse(navigation_params.get(), "text/html",
+                                          "UTF-8", error_html);
   navigation_params->unreachable_url = error.url();
 
   std::unique_ptr<DocumentState> document_state = BuildDocumentStateFromParams(
@@ -7031,9 +7029,9 @@
                                      bool replace_current_item) {
   auto navigation_params = std::make_unique<WebNavigationParams>();
   navigation_params->url = base_url;
-  navigation_params->data = WebData(html.data(), html.length());
-  navigation_params->mime_type = "text/html";
-  navigation_params->text_encoding = WebString::FromUTF8(text_encoding);
+  WebNavigationParams::FillStaticResponse(navigation_params.get(), "text/html",
+                                          WebString::FromUTF8(text_encoding),
+                                          html);
   navigation_params->unreachable_url = unreachable_url;
   navigation_params->frame_load_type =
       replace_current_item ? blink::WebFrameLoadType::kReplaceCurrentItem
diff --git a/content/renderer/webgraphicscontext3d_provider_impl.cc b/content/renderer/webgraphicscontext3d_provider_impl.cc
index a26f1a5..b828238 100644
--- a/content/renderer/webgraphicscontext3d_provider_impl.cc
+++ b/content/renderer/webgraphicscontext3d_provider_impl.cc
@@ -104,4 +104,9 @@
   return cache_iterator->second.get();
 }
 
+gpu::SharedImageInterface*
+WebGraphicsContext3DProviderImpl::SharedImageInterface() {
+  return provider_->SharedImageInterface();
+}
+
 }  // namespace content
diff --git a/content/renderer/webgraphicscontext3d_provider_impl.h b/content/renderer/webgraphicscontext3d_provider_impl.h
index 88b0f7ba..354b166 100644
--- a/content/renderer/webgraphicscontext3d_provider_impl.h
+++ b/content/renderer/webgraphicscontext3d_provider_impl.h
@@ -54,6 +54,7 @@
   cc::ImageDecodeCache* ImageDecodeCache(
       SkColorType color_type,
       sk_sp<SkColorSpace> color_space) override;
+  gpu::SharedImageInterface* SharedImageInterface() override;
 
   ws::ContextProviderCommandBuffer* context_provider() const {
     return provider_.get();
diff --git a/content/test/gpu/gpu_tests/webgl_conformance_expectations.py b/content/test/gpu/gpu_tests/webgl_conformance_expectations.py
index f81bb68..e663228 100644
--- a/content/test/gpu/gpu_tests/webgl_conformance_expectations.py
+++ b/content/test/gpu/gpu_tests/webgl_conformance_expectations.py
@@ -198,6 +198,9 @@
         ['win', 'nvidia', 'no_passthrough'], bug=626524)
     self.Flaky('conformance/textures/misc/texture-upload-size.html',
         ['win', 'nvidia'], bug=630860)
+    self.Skip('conformance/rendering/out-of-bounds-index-buffers.html',
+              ['win', 'nvidia', 'passthrough', 'vulkan'], bug=3018) # ANGLE bug
+
     # self.Fail('conformance/extensions/ext-sRGB.html',
     #     ['win', 'nvidia', 'no_passthrough'], bug=679696)
 
diff --git a/content/test/navigation_simulator_impl.cc b/content/test/navigation_simulator_impl.cc
index b4bf4a235..3a70f62c 100644
--- a/content/test/navigation_simulator_impl.cc
+++ b/content/test/navigation_simulator_impl.cc
@@ -5,12 +5,12 @@
 #include "content/test/navigation_simulator_impl.h"
 
 #include <utility>
-
 #include "base/bind.h"
 #include "base/memory/ptr_util.h"
 #include "base/run_loop.h"
 #include "content/browser/frame_host/debug_urls.h"
 #include "content/browser/frame_host/frame_tree_node.h"
+#include "content/browser/frame_host/navigation_entry_impl.h"
 #include "content/browser/frame_host/navigation_handle_impl.h"
 #include "content/browser/frame_host/navigation_request.h"
 #include "content/browser/frame_host/render_frame_host_impl.h"
@@ -76,6 +76,16 @@
 
 int64_t g_unique_identifier = 0;
 
+FrameTreeNode* GetFrameTreeNodeForPendingEntry(WebContentsImpl* contents) {
+  NavigationEntryImpl* pending_entry =
+      contents->GetController().GetPendingEntry();
+  int frame_tree_node_id = pending_entry->frame_tree_node_id();
+  FrameTree* frame_tree = contents->GetFrameTree();
+  if (frame_tree_node_id == -1)
+    return frame_tree->root();
+  return frame_tree->FindByID(frame_tree_node_id);
+}
+
 }  // namespace
 
 // static
@@ -249,29 +259,44 @@
 }
 
 // static
-std::unique_ptr<NavigationSimulator>
-NavigationSimulator::CreateFromPendingBrowserInitiated(WebContents* contents) {
-  return NavigationSimulatorImpl::CreateFromPendingBrowserInitiated(contents);
+std::unique_ptr<NavigationSimulator> NavigationSimulator::CreateFromPending(
+    WebContents* contents) {
+  return NavigationSimulatorImpl::CreateFromPending(contents);
 }
 
 // static
 std::unique_ptr<NavigationSimulatorImpl>
-NavigationSimulatorImpl::CreateFromPendingBrowserInitiated(
-    WebContents* contents) {
+NavigationSimulatorImpl::CreateFromPending(WebContents* contents) {
+  WebContentsImpl* contents_impl = static_cast<WebContentsImpl*>(contents);
+
+  FrameTreeNode* frame_tree_node =
+      GetFrameTreeNodeForPendingEntry(contents_impl);
+  CHECK(frame_tree_node);
   TestRenderFrameHost* test_frame_host =
-      static_cast<TestRenderFrameHost*>(contents->GetMainFrame());
+      static_cast<TestRenderFrameHost*>(frame_tree_node->current_frame_host());
+  CHECK(test_frame_host);
+  NavigationRequest* request = frame_tree_node->navigation_request();
+  // It is possible to not have a NavigationRequest in the frame tree node if
+  // it did not go to the network (such as about:blank). In that case it is
+  // already in the RenderFrameHost.
+  if (!request)
+    request = test_frame_host->navigation_requests().begin()->second.get();
+  CHECK(request);
 
   // Simulate the BeforeUnload ACK if needed.
-  NavigationRequest* request =
-      test_frame_host->frame_tree_node()->navigation_request();
-  CHECK(request);
   if (request->state() == NavigationRequest::WAITING_FOR_RENDERER_RESPONSE)
     test_frame_host->SendBeforeUnloadACK(true /*proceed */);
 
   auto simulator = base::WrapUnique(new NavigationSimulatorImpl(
-      GURL(), true /* browser_initiated */,
+      GURL(),
+      !contents_impl->GetController()
+           .GetPendingEntry()
+           ->is_renderer_initiated(),
       static_cast<WebContentsImpl*>(contents), test_frame_host));
+  simulator->frame_tree_node_ = frame_tree_node;
   simulator->InitializeFromStartedRequest(request);
+  simulator->set_did_create_new_entry(
+      contents->GetController().GetPendingEntryIndex() == -1);
   return simulator;
 }
 
@@ -325,8 +350,8 @@
 void NavigationSimulatorImpl::InitializeFromStartedRequest(
     NavigationRequest* request) {
   NavigationHandle* handle = request->navigation_handle();
-  CHECK(handle);
-  CHECK_EQ(NavigationRequest::STARTED, request->state());
+  DCHECK(handle);
+  CHECK_LE(NavigationRequest::STARTED, request->state());
   state_ = STARTED;
   CHECK_EQ(web_contents_, handle->GetWebContents());
   CHECK(render_frame_host_);
@@ -423,8 +448,10 @@
       num_did_redirect_navigation_called_;
 
   PrepareCompleteCallbackOnHandle();
-  NavigationRequest* request =
-      render_frame_host_->frame_tree_node()->navigation_request();
+  NavigationRequest* request = frame_tree_node_->navigation_request();
+  CHECK(request) << "Trying to redirect a navigation that does not go to the "
+                    "network stack.";
+
   TestNavigationURLLoader* url_loader =
       static_cast<TestNavigationURLLoader*>(request->loader_for_testing());
   CHECK(url_loader);
@@ -551,7 +578,7 @@
   auto params = BuildDidCommitProvisionalLoadParams(
       false /* same_document */, false /* failed_navigation */);
   render_frame_host_->SimulateCommitProcessed(
-      handle_->GetNavigationId(), true /* was_successful */, std::move(params),
+      handle_->GetNavigationId(), std::move(params),
       std::move(interface_provider_request_),
       std::move(document_interface_broker_content_request_),
       std::move(document_interface_broker_blink_request_), same_document_);
@@ -583,8 +610,7 @@
   CHECK(render_frame_host_)
       << "NavigationSimulatorImpl::AbortCommit can only be "
          "called for navigations that commit.";
-  render_frame_host_->SimulateCommitProcessed(handle_->GetNavigationId(),
-                                              false /* was_successful */);
+  render_frame_host_->AbortCommit(handle_->GetNavigationId());
 
   state_ = FINISHED;
   CHECK_EQ(1, num_did_finish_navigation_called_);
@@ -670,7 +696,7 @@
   auto params = BuildDidCommitProvisionalLoadParams(
       false /* same_document */, true /* failed_navigation */);
   render_frame_host_->SimulateCommitProcessed(
-      handle_->GetNavigationId(), true /* was_successful */, std::move(params),
+      handle_->GetNavigationId(), std::move(params),
       std::move(interface_provider_request_),
       std::move(document_interface_broker_content_request_),
       std::move(document_interface_broker_blink_request_),
@@ -706,8 +732,8 @@
   document_interface_broker_blink_request_ = nullptr;
 
   render_frame_host_->SimulateCommitProcessed(
-      handle_ ? handle_->GetNavigationId() : -1, true /* was_successful */,
-      std::move(params), nullptr /* interface_provider_request_ */,
+      handle_ ? handle_->GetNavigationId() : -1, std::move(params),
+      nullptr /* interface_provider_request_ */,
       nullptr /* document_interface_broker_content_handle */,
       nullptr /* document_interface_broker_blink_handle */,
       true /* same_document */);
@@ -904,6 +930,11 @@
     }
   }
 
+  frame_tree_node_ = GetFrameTreeNodeForPendingEntry(web_contents_);
+  CHECK(frame_tree_node_);
+  render_frame_host_ =
+      static_cast<TestRenderFrameHost*>(frame_tree_node_->current_frame_host());
+
   // The navigation url might have been rewritten by the NavigationController.
   // Update it.
   navigation_url_ = web_contents_->GetController().GetPendingEntry()->GetURL();
@@ -912,8 +943,7 @@
   NavigationRequest* request = frame_tree_node_->navigation_request();
   if (request &&
       request->state() == NavigationRequest::WAITING_FOR_RENDERER_RESPONSE) {
-    static_cast<TestRenderFrameHost*>(web_contents_->GetMainFrame())
-        ->SendBeforeUnloadACK(true /*proceed */);
+    render_frame_host_->SendBeforeUnloadACK(true /*proceed */);
   }
 
   // Note: WillStartRequest checks can destroy the request synchronously, or
@@ -1093,6 +1123,8 @@
 }
 
 bool NavigationSimulatorImpl::DidCreateNewEntry() {
+  if (did_create_new_entry_.has_value())
+    return did_create_new_entry_.value();
   if (ui::PageTransitionCoreTypeIs(transition_,
                                    ui::PAGE_TRANSITION_AUTO_SUBFRAME))
     return false;
@@ -1112,6 +1144,16 @@
       ui::PageTransitionFromInt(transition_ | ui::PAGE_TRANSITION_FORWARD_BACK);
 }
 
+void NavigationSimulatorImpl::set_did_create_new_entry(
+    bool did_create_new_entry) {
+  did_create_new_entry_ = did_create_new_entry;
+}
+
+void NavigationSimulatorImpl::set_history_list_was_cleared(
+    bool history_cleared) {
+  history_list_was_cleared_ = history_cleared;
+}
+
 std::unique_ptr<FrameHostMsg_DidCommitProvisionalLoad_Params>
 NavigationSimulatorImpl::BuildDidCommitProvisionalLoadParams(
     bool same_document,
@@ -1125,6 +1167,7 @@
   params->transition = transition_;
   params->gesture =
       has_user_gesture_ ? NavigationGestureUser : NavigationGestureAuto;
+  params->history_list_was_cleared = history_list_was_cleared_;
 
   if (failed_navigation) {
     // Note: Error pages must commit in a unique origin. So it is left unset.
@@ -1137,7 +1180,6 @@
       params->method = "POST";
     params->http_status_code = 200;
     params->should_update_history = true;
-    params->history_list_was_cleared = false;
   }
 
   if (same_document) {
diff --git a/content/test/navigation_simulator_impl.h b/content/test/navigation_simulator_impl.h
index bd782dc..7d2ebfd 100644
--- a/content/test/navigation_simulator_impl.h
+++ b/content/test/navigation_simulator_impl.h
@@ -53,8 +53,8 @@
       const GURL& original_url,
       RenderFrameHost* render_frame_host);
 
-  static std::unique_ptr<NavigationSimulatorImpl>
-  CreateFromPendingBrowserInitiated(WebContents* contents);
+  static std::unique_ptr<NavigationSimulatorImpl> CreateFromPending(
+      WebContents* contents);
 
   // NavigationSimulator implementation.
   void Start() override;
@@ -91,11 +91,21 @@
   content::GlobalRequestID GetGlobalRequestID() const override;
 
   // Additional utilites usable only inside content/.
+
+  // Set LoadURLParams and make browser initiated navigations use
+  // LoadURLWithParams instead of LoadURL.
   void SetLoadURLParams(NavigationController::LoadURLParams* load_url_params);
   void set_should_check_main_world_csp(CSPDisposition disposition) {
     should_check_main_world_csp_ = disposition;
   }
 
+  // Set DidCommit*Params history_list_was_cleared flag to |history_cleared|.
+  void set_history_list_was_cleared(bool history_cleared);
+
+  // Manually force the value of did_create_new__entry flag in DidCommit*Params
+  // to |did_create_new_entry|.
+  void set_did_create_new_entry(bool did_create_new_entry);
+
  private:
   NavigationSimulatorImpl(const GURL& original_url,
                           bool browser_initiated,
@@ -179,6 +189,9 @@
   State state_ = INITIALIZATION;
 
   // The WebContents in which the navigation is taking place.
+  // IMPORTANT: Because NavigationSimulator is used outside content/ where we
+  // sometimes use WebContentsImpl and not TestWebContents, this cannot be
+  // assumed to cast properly to TestWebContents.
   WebContentsImpl* web_contents_;
 
   // The renderer associated with this navigation.
@@ -212,11 +225,14 @@
   std::string contents_mime_type_;
   CSPDisposition should_check_main_world_csp_ = CSPDisposition::CHECK;
 
+  bool auto_advance_ = true;
+
   // Generic params structure used for fully customized browser initiated
   // navigation requests. Only valid if explicitely provided.
   NavigationController::LoadURLParams* load_url_params_;
 
-  bool auto_advance_ = true;
+  bool history_list_was_cleared_ = false;
+  base::Optional<bool> did_create_new_entry_;
 
   // These are used to sanity check the content/public/ API calls emitted as
   // part of the navigation.
diff --git a/content/test/test_render_frame.cc b/content/test/test_render_frame.cc
index 72791325..0d148c8a 100644
--- a/content/test/test_render_frame.cc
+++ b/content/test/test_render_frame.cc
@@ -273,17 +273,15 @@
     navigation_params->url = url;
     if (!url.IsAboutBlank() && url != content::kAboutSrcDocURL) {
       std::string mime_type, charset, data;
-      bool success = net::DataURL::Parse(url, &mime_type, &charset, &data);
-      navigation_params->data = blink::WebData(data.c_str(), data.length());
-      if (success) {
-        navigation_params->mime_type = blink::WebString::FromUTF8(mime_type);
-        navigation_params->text_encoding = blink::WebString::FromUTF8(charset);
-      } else {
+      if (!net::DataURL::Parse(url, &mime_type, &charset, &data)) {
         // This case is only here to allow cluster fuzz pass any url,
         // to unblock further fuzzing.
-        navigation_params->mime_type = blink::WebString::FromUTF8("text/html");
-        navigation_params->text_encoding = blink::WebString::FromUTF8("UTF-8");
+        mime_type = "text/html";
+        charset = "UTF-8";
       }
+      blink::WebNavigationParams::FillStaticResponse(
+          navigation_params.get(), blink::WebString::FromUTF8(mime_type),
+          blink::WebString::FromUTF8(charset), data);
     }
     frame_->CommitNavigation(std::move(navigation_params),
                              nullptr /* extra_data */);
diff --git a/content/test/test_render_frame_host.cc b/content/test/test_render_frame_host.cc
index 100b96d7..7fb1713 100644
--- a/content/test/test_render_frame_host.cc
+++ b/content/test/test_render_frame_host.cc
@@ -437,15 +437,8 @@
     PrepareForCommit();
 }
 
-void TestRenderFrameHost::SimulateCommitProcessed(int64_t navigation_id,
-                                                  bool was_successful) {
-  SimulateCommitProcessed(navigation_id, was_successful, nullptr, nullptr,
-                          nullptr, nullptr, false);
-}
-
 void TestRenderFrameHost::SimulateCommitProcessed(
     int64_t navigation_id,
-    bool was_successful,
     std::unique_ptr<FrameHostMsg_DidCommitProvisionalLoad_Params> params,
     service_manager::mojom::InterfaceProviderRequest interface_provider_request,
     blink::mojom::DocumentInterfaceBrokerRequest
@@ -453,9 +446,9 @@
     blink::mojom::DocumentInterfaceBrokerRequest
         document_interface_broker_blink_request,
     bool same_document) {
-  blink::mojom::CommitResult result = was_successful
-                                          ? blink::mojom::CommitResult::Ok
-                                          : blink::mojom::CommitResult::Aborted;
+  CHECK(params);
+  blink::mojom::CommitResult result = blink::mojom::CommitResult::Ok;
+
   if (!same_document) {
     // Note: Although the code does not prohibit the running of multiple
     // callbacks, no more than 1 callback will ever run, because navigation_id
@@ -483,15 +476,13 @@
     }
   }
 
-  if (params) {
-    SendNavigateWithParamsAndInterfaceParams(
-        params.release(),
-        mojom::DidCommitProvisionalLoadInterfaceParams::New(
-            std::move(interface_provider_request),
-            std::move(document_interface_broker_content_request),
-            std::move(document_interface_broker_blink_request)),
-        same_document);
-  }
+  SendNavigateWithParamsAndInterfaceParams(
+      params.release(),
+      mojom::DidCommitProvisionalLoadInterfaceParams::New(
+          std::move(interface_provider_request),
+          std::move(document_interface_broker_content_request),
+          std::move(document_interface_broker_blink_request)),
+      same_document);
 }
 
 WebBluetoothServiceImpl*
@@ -646,6 +637,11 @@
   return interface_params;
 }
 
+void TestRenderFrameHost::AbortCommit(int64_t navigation_id) {
+  OnCrossDocumentCommitProcessed(navigation_id,
+                                 blink::mojom::CommitResult::Aborted);
+}
+
 // static
 service_manager::mojom::InterfaceProviderRequest
 TestRenderFrameHost::CreateStubInterfaceProviderRequest() {
diff --git a/content/test/test_render_frame_host.h b/content/test/test_render_frame_host.h
index 4fa49126..cde7c0f 100644
--- a/content/test/test_render_frame_host.h
+++ b/content/test/test_render_frame_host.h
@@ -165,10 +165,8 @@
   // Used to simulate the commit of a navigation having been processed in the
   // renderer. If parameters required to commit are not provided, they will be
   // set to default null values.
-  void SimulateCommitProcessed(int64_t navigation_id, bool was_successful);
   void SimulateCommitProcessed(
       int64_t navigation_id,
-      bool was_successful,
       std::unique_ptr<FrameHostMsg_DidCommitProvisionalLoad_Params> params,
       service_manager::mojom::InterfaceProviderRequest
           interface_provider_request,
@@ -202,6 +200,16 @@
   static blink::mojom::DocumentInterfaceBrokerRequest
   CreateStubDocumentInterfaceBrokerRequest();
 
+  // This simulates aborting a cross document navigation.
+  // Will abort the navigation with the given |navigation_id|.
+  void AbortCommit(int64_t navigation_id);
+
+  // Returns the navigations that are trying to commit.
+  const std::map<int64_t, std::unique_ptr<NavigationRequest>>&
+  navigation_requests() {
+    return navigation_requests_;
+  }
+
  protected:
   void SendCommitNavigation(
       mojom::NavigationClient* navigation_client,
diff --git a/content/test/test_web_contents.cc b/content/test/test_web_contents.cc
index fce46f23..8abd2624 100644
--- a/content/test/test_web_contents.cc
+++ b/content/test/test_web_contents.cc
@@ -322,41 +322,8 @@
   NavigationEntry* entry = GetController().GetPendingEntry();
   DCHECK(entry);
 
-  TestRenderFrameHost* old_rfh = GetMainFrame();
-  NavigationRequest* request = old_rfh->frame_tree_node()->navigation_request();
-
-  // PlzNavigate: the pending RenderFrameHost is not created before the
-  // navigation commit, so it is necessary to simulate the IO thread response
-  // here to commit in the proper renderer. It is necessary to call
-  // PrepareForCommit before getting the main and the pending frame because when
-  // we are trying to navigate to a webui from a new tab, a RenderFrameHost is
-  // created to display it that is committed immediately (since it is a new
-  // tab). Therefore the main frame is replaced without a pending frame being
-  // created, and we don't get the right values for the RenderFrameHost to
-  // navigate: we try to use the old one that has been deleted in the meantime.
-  // Note that for some synchronous navigations (about:blank, javascript urls,
-  // etc.), no simulation of the network stack is required.
-  old_rfh->PrepareForCommitIfNecessary();
-
-  TestRenderFrameHost* rfh = GetPendingMainFrame();
-  if (!rfh)
-    rfh = old_rfh;
-  CHECK(rfh->is_loading() || IsRendererDebugURL(entry->GetURL()));
-  CHECK(!rfh->frame_tree_node()->navigation_request());
-
-  if (request && !request->navigation_handle()->IsSameDocument()) {
-    rfh->SimulateCommitProcessed(
-        request->navigation_handle()->GetNavigationId(),
-        true /* was successful */);
-  }
-
-  rfh->SendNavigateWithTransition(entry->GetUniqueID(),
-                                  GetController().GetPendingEntryIndex() == -1,
-                                  entry->GetURL(), entry->GetTransitionType());
-  // Simulate the SwapOut_ACK. This is needed when cross-site navigation
-  // happens.
-  if (old_rfh != rfh)
-    old_rfh->OnSwappedOut();
+  auto navigation = NavigationSimulator::CreateFromPending(this);
+  navigation->Commit();
 }
 
 RenderViewHostDelegateView* TestWebContents::GetDelegateView() {
diff --git a/extensions/browser/api/socket/socket_api.cc b/extensions/browser/api/socket/socket_api.cc
index 93e9dfa..4b85f45 100644
--- a/extensions/browser/api/socket/socket_api.cc
+++ b/extensions/browser/api/socket/socket_api.cc
@@ -14,6 +14,7 @@
 #include "build/build_config.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_task_traits.h"
+#include "content/public/browser/network_service_instance.h"
 #include "content/public/browser/storage_partition.h"
 #include "extensions/browser/api/socket/socket.h"
 #include "extensions/browser/api/socket/tcp_socket.h"
@@ -852,44 +853,23 @@
 }
 
 ExtensionFunction::ResponseAction SocketGetNetworkListFunction::Run() {
-  base::PostTaskWithTraits(
-      FROM_HERE,
-      {base::MayBlock(), base::TaskPriority::USER_VISIBLE,
-       base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
-      base::Bind(&SocketGetNetworkListFunction::GetNetworkListOnFileThread,
-                 this));
+  content::GetNetworkService()->GetNetworkList(
+      net::INCLUDE_HOST_SCOPE_VIRTUAL_INTERFACES,
+      base::BindOnce(&SocketGetNetworkListFunction::GotNetworkList, this));
   return RespondLater();
 }
 
-void SocketGetNetworkListFunction::GetNetworkListOnFileThread() {
-  net::NetworkInterfaceList interface_list;
-  if (GetNetworkList(&interface_list,
-                     net::INCLUDE_HOST_SCOPE_VIRTUAL_INTERFACES)) {
-    base::PostTaskWithTraits(
-        FROM_HERE, {BrowserThread::UI},
-        base::Bind(&SocketGetNetworkListFunction::SendResponseOnUIThread, this,
-                   interface_list));
+void SocketGetNetworkListFunction::GotNetworkList(
+    const base::Optional<net::NetworkInterfaceList>& interface_list) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  if (!interface_list.has_value()) {
+    Respond(Error(kNetworkListError));
     return;
   }
 
-  base::PostTaskWithTraits(
-      FROM_HERE, {BrowserThread::UI},
-      base::Bind(&SocketGetNetworkListFunction::HandleGetNetworkListError,
-                 this));
-}
-
-void SocketGetNetworkListFunction::HandleGetNetworkListError() {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  Respond(Error(kNetworkListError));
-}
-
-void SocketGetNetworkListFunction::SendResponseOnUIThread(
-    const net::NetworkInterfaceList& interface_list) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-
   std::vector<api::socket::NetworkInterface> create_arg;
-  create_arg.reserve(interface_list.size());
-  for (const net::NetworkInterface& interface : interface_list) {
+  create_arg.reserve(interface_list->size());
+  for (const net::NetworkInterface& interface : interface_list.value()) {
     api::socket::NetworkInterface info;
     info.name = interface.name;
     info.address = interface.address.ToString();
diff --git a/extensions/browser/api/socket/socket_api.h b/extensions/browser/api/socket/socket_api.h
index 00007c80..d8a1023 100644
--- a/extensions/browser/api/socket/socket_api.h
+++ b/extensions/browser/api/socket/socket_api.h
@@ -472,9 +472,8 @@
   ResponseAction Run() override;
 
  private:
-  void GetNetworkListOnFileThread();
-  void HandleGetNetworkListError();
-  void SendResponseOnUIThread(const net::NetworkInterfaceList& interface_list);
+  void GotNetworkList(
+      const base::Optional<net::NetworkInterfaceList>& interface_list);
 };
 
 class SocketJoinGroupFunction : public SocketAsyncApiFunction {
diff --git a/extensions/browser/guest_view/web_view/web_view_content_script_manager.cc b/extensions/browser/guest_view/web_view/web_view_content_script_manager.cc
index 355c920..403f9157 100644
--- a/extensions/browser/guest_view/web_view/web_view_content_script_manager.cc
+++ b/extensions/browser/guest_view/web_view/web_view_content_script_manager.cc
@@ -58,7 +58,7 @@
           ->GetDeclarativeUserScriptMasterByID(host_id);
   DCHECK(master);
 
-  // We need to update WebViewRenderState in the IO thread if the guest exists.
+  // We need to update WebViewRenderState.
   std::set<int> ids_to_add;
 
   GuestMapKey key = std::pair<int, int>(embedder_process_id, view_instance_id);
@@ -109,16 +109,10 @@
   if (host_it == webview_host_id_map_.end())
     webview_host_id_map_.insert(std::make_pair(key, host_id));
 
-  // Step 6: updates WebViewRenderState in the IO thread.
-  // It is safe to use base::Unretained(WebViewRendererState::GetInstance())
-  // since WebViewRendererState::GetInstance() always returns a Singleton of
-  // WebViewRendererState.
+  // Step 6: updates WebViewRenderState.
   if (!ids_to_add.empty()) {
-    base::PostTaskWithTraits(
-        FROM_HERE, {content::BrowserThread::IO},
-        base::Bind(&WebViewRendererState::AddContentScriptIDs,
-                   base::Unretained(WebViewRendererState::GetInstance()),
-                   embedder_process_id, view_instance_id, ids_to_add));
+    WebViewRendererState::GetInstance()->AddContentScriptIDs(
+        embedder_process_id, view_instance_id, ids_to_add);
   }
 }
 
@@ -157,7 +151,7 @@
           ->GetDeclarativeUserScriptMasterByID(host_id);
   CHECK(master);
 
-  // We need to update WebViewRenderState in the IO thread if the guest exists.
+  // We need to update WebViewRenderState.
   std::set<int> ids_to_delete;
   std::set<UserScriptIDPair> scripts_to_delete;
 
@@ -196,13 +190,10 @@
   // Step 3: removes content scripts from master.
   master->RemoveScripts(scripts_to_delete);
 
-  // Step 4: updates WebViewRenderState in the IO thread.
+  // Step 4: updates WebViewRenderState.
   if (!ids_to_delete.empty()) {
-    base::PostTaskWithTraits(
-        FROM_HERE, {content::BrowserThread::IO},
-        base::Bind(&WebViewRendererState::RemoveContentScriptIDs,
-                   base::Unretained(WebViewRendererState::GetInstance()),
-                   embedder_process_id, view_instance_id, ids_to_delete));
+    WebViewRendererState::GetInstance()->RemoveContentScriptIDs(
+        embedder_process_id, view_instance_id, ids_to_delete);
   }
 }
 
diff --git a/extensions/browser/guest_view/web_view/web_view_guest.cc b/extensions/browser/guest_view/web_view/web_view_guest.cc
index 0394d5a..884534b 100644
--- a/extensions/browser/guest_view/web_view/web_view_guest.cc
+++ b/extensions/browser/guest_view/web_view/web_view_guest.cc
@@ -509,7 +509,9 @@
 }
 
 void WebViewGuest::GuestDestroyed() {
-  RemoveWebViewStateFromIOThread(web_contents());
+  WebViewRendererState::GetInstance()->RemoveGuest(
+      web_contents()->GetRenderViewHost()->GetProcess()->GetID(),
+      web_contents()->GetRenderViewHost()->GetRoutingID());
 }
 
 void WebViewGuest::GuestReady() {
@@ -1027,24 +1029,9 @@
   web_view_info.content_script_ids = manager->GetContentScriptIDSet(
       web_view_info.embedder_process_id, web_view_info.instance_id);
 
-  base::PostTaskWithTraits(
-      FROM_HERE, {content::BrowserThread::IO},
-      base::Bind(&WebViewRendererState::AddGuest,
-                 base::Unretained(WebViewRendererState::GetInstance()),
-                 web_contents()->GetRenderViewHost()->GetProcess()->GetID(),
-                 web_contents()->GetRenderViewHost()->GetRoutingID(),
-                 web_view_info));
-}
-
-// static
-void WebViewGuest::RemoveWebViewStateFromIOThread(
-    WebContents* web_contents) {
-  base::PostTaskWithTraits(
-      FROM_HERE, {content::BrowserThread::IO},
-      base::Bind(&WebViewRendererState::RemoveGuest,
-                 base::Unretained(WebViewRendererState::GetInstance()),
-                 web_contents->GetRenderViewHost()->GetProcess()->GetID(),
-                 web_contents->GetRenderViewHost()->GetRoutingID()));
+  WebViewRendererState::GetInstance()->AddGuest(
+      web_contents()->GetRenderViewHost()->GetProcess()->GetID(),
+      web_contents()->GetRenderViewHost()->GetRoutingID(), web_view_info);
 }
 
 void WebViewGuest::RequestMediaAccessPermission(
diff --git a/extensions/browser/guest_view/web_view/web_view_guest.h b/extensions/browser/guest_view/web_view/web_view_guest.h
index 3fc40de6..691ed1f 100644
--- a/extensions/browser/guest_view/web_view/web_view_guest.h
+++ b/extensions/browser/guest_view/web_view/web_view_guest.h
@@ -284,8 +284,6 @@
   void ReportFrameNameChange(const std::string& name);
 
   void PushWebViewStateToIOThread();
-  static void RemoveWebViewStateFromIOThread(
-      content::WebContents* web_contents);
 
   // Loads the |url| provided. |force_navigation| indicates whether to reload
   // the content if the provided |url| matches the current page of the guest.
diff --git a/gpu/command_buffer/common/shared_image_usage.h b/gpu/command_buffer/common/shared_image_usage.h
index 45c6cba..34311eb 100644
--- a/gpu/command_buffer/common/shared_image_usage.h
+++ b/gpu/command_buffer/common/shared_image_usage.h
@@ -24,6 +24,8 @@
   // TODO(backer): Fold back into SHARED_IMAGE_USAGE_RASTER once RasterInterface
   // can CPU raster (CopySubImage?) to SkImage.
   SHARED_IMAGE_USAGE_OOP_RASTERIZATION = 1 << 5,
+  // Image will be used for RGB emulation in WebGL on Mac.
+  SHARED_IMAGE_USAGE_RGB_EMULATION = 1 << 6,
 };
 
 }  // namespace gpu
diff --git a/gpu/command_buffer/service/BUILD.gn b/gpu/command_buffer/service/BUILD.gn
index c0fd9ed..cd1605f 100644
--- a/gpu/command_buffer/service/BUILD.gn
+++ b/gpu/command_buffer/service/BUILD.gn
@@ -276,6 +276,7 @@
     "//gpu/command_buffer/common:gles2_sources",
     "//gpu/command_buffer/common:raster_sources",
     "//gpu/command_buffer/common:webgpu_sources",
+    "//skia",
   ]
   deps = [
     ":disk_cache_proto",
diff --git a/gpu/command_buffer/service/decoder_context.h b/gpu/command_buffer/service/decoder_context.h
index 61b0954..482960f 100644
--- a/gpu/command_buffer/service/decoder_context.h
+++ b/gpu/command_buffer/service/decoder_context.h
@@ -219,6 +219,13 @@
 
   // Restores texture states for a given service id.
   virtual void RestoreTextureState(unsigned service_id) = 0;
+
+  //
+  // Methods required by ImageDecodeAcceleratorStub
+  //
+  // Returns the ID of a RasterDecoder. This is not applicable to other
+  // implementations and it returns a negative number in that case.
+  virtual int GetRasterDecoderId() const = 0;
 };
 
 }  // namespace gpu
diff --git a/gpu/command_buffer/service/gl_utils.cc b/gpu/command_buffer/service/gl_utils.cc
index bc1e858..39428c16 100644
--- a/gpu/command_buffer/service/gl_utils.cc
+++ b/gpu/command_buffer/service/gl_utils.cc
@@ -903,13 +903,6 @@
       break;
   }
 
-  // Sometimes glCopyTexImage2D() fails if source is GL_RGB10_A2 and dest isn't.
-  if (feature_info->workarounds().disable_copy_tex_image_2d_rgb10_a2 &&
-      source_internal_format == GL_RGB10_A2 &&
-      dest_internal_format != GL_RGB10_A2) {
-    return CopyTextureMethod::DRAW_AND_COPY;
-  }
-
   // CopyTexImage* should not allow internalformat of GL_BGRA_EXT and
   // GL_BGRA8_EXT. https://crbug.com/663086.
   bool copy_tex_image_format_valid =
@@ -1025,8 +1018,7 @@
       source_internal_format == GL_BGRA8_EXT ||
       source_internal_format == GL_RGB_YCBCR_420V_CHROMIUM ||
       source_internal_format == GL_RGB_YCBCR_422_CHROMIUM ||
-      source_internal_format == GL_R16_EXT ||
-      source_internal_format == GL_RGB10_A2;
+      source_internal_format == GL_R16_EXT;
   if (!valid_source_format) {
     *output_error_msg = "invalid source internal format " +
                         GLES2Util::GetStringEnum(source_internal_format);
diff --git a/gpu/command_buffer/service/gles2_cmd_copy_texture_chromium.cc b/gpu/command_buffer/service/gles2_cmd_copy_texture_chromium.cc
index a58de95..939bf436 100644
--- a/gpu/command_buffer/service/gles2_cmd_copy_texture_chromium.cc
+++ b/gpu/command_buffer/service/gles2_cmd_copy_texture_chromium.cc
@@ -43,7 +43,6 @@
   S_FORMAT_RGB_YCBCR_420V_CHROMIUM,
   S_FORMAT_RGB_YCBCR_422_CHROMIUM,
   S_FORMAT_COMPRESSED,
-  S_FORMAT_RGB10_A2,
   NUM_S_FORMAT
 };
 
@@ -186,12 +185,8 @@
     case GL_ETC1_RGB8_OES:
       sourceFormatIndex = S_FORMAT_COMPRESSED;
       break;
-    case GL_RGB10_A2:
-      sourceFormatIndex = S_FORMAT_RGB10_A2;
-      break;
     default:
-      NOTREACHED() << "Invalid source format "
-                   << gl::GLEnums::GetStringEnum(source_format);
+      NOTREACHED();
       break;
   }
 
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc
index d40f364..2ba78ea 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc
@@ -605,6 +605,11 @@
   return outputter_;
 }
 
+int GLES2Decoder::GetRasterDecoderId() const {
+  NOTREACHED();
+  return -1;
+}
+
 // This class implements GLES2Decoder so we don't have to expose all the GLES2
 // cmd stuff to outside this class.
 class GLES2DecoderImpl : public GLES2Decoder, public ErrorStateClient {
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.h b/gpu/command_buffer/service/gles2_cmd_decoder.h
index 37f590e1..dee3fa1 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder.h
+++ b/gpu/command_buffer/service/gles2_cmd_decoder.h
@@ -136,6 +136,8 @@
 
   Outputter* outputter() const override;
 
+  int GetRasterDecoderId() const override;
+
   // Set the surface associated with the default FBO.
   virtual void SetSurface(const scoped_refptr<gl::GLSurface>& surface) = 0;
   // Releases the surface associated with the GL context.
diff --git a/gpu/command_buffer/service/raster_decoder.cc b/gpu/command_buffer/service/raster_decoder.cc
index 3ae1bb8..f518362 100644
--- a/gpu/command_buffer/service/raster_decoder.cc
+++ b/gpu/command_buffer/service/raster_decoder.cc
@@ -321,6 +321,7 @@
     NOTIMPLEMENTED();
     return false;
   }
+  int GetRasterDecoderId() const override;
   int DecoderIdForTest() override;
   ServiceTransferCache* GetTransferCacheForTest() override;
   void SetUpForRasterCHROMIUMForTest() override;
@@ -1374,6 +1375,10 @@
   return false;
 }
 
+int RasterDecoderImpl::GetRasterDecoderId() const {
+  return raster_decoder_id_;
+}
+
 int RasterDecoderImpl::DecoderIdForTest() {
   return raster_decoder_id_;
 }
diff --git a/gpu/command_buffer/service/raster_decoder_mock.h b/gpu/command_buffer/service/raster_decoder_mock.h
index 6c5424c..e79c0528 100644
--- a/gpu/command_buffer/service/raster_decoder_mock.h
+++ b/gpu/command_buffer/service/raster_decoder_mock.h
@@ -97,6 +97,7 @@
   MOCK_METHOD1(SetIgnoreCachedStateForTest, void(bool ignore));
   MOCK_METHOD0(GetImageManagerForTest, gles2::ImageManager*());
   MOCK_METHOD0(GetTransferCacheForTest, ServiceTransferCache*());
+  MOCK_CONST_METHOD0(GetRasterDecoderId, int());
   MOCK_METHOD0(DecoderIdForTest, int());
   MOCK_METHOD0(SetUpForRasterCHROMIUMForTest, void());
   MOCK_METHOD0(SetOOMErrorForTest, void());
diff --git a/gpu/command_buffer/service/service_transfer_cache.cc b/gpu/command_buffer/service/service_transfer_cache.cc
index 3373500..56f7b63 100644
--- a/gpu/command_buffer/service/service_transfer_cache.cc
+++ b/gpu/command_buffer/service/service_transfer_cache.cc
@@ -6,6 +6,8 @@
 
 #include <inttypes.h>
 
+#include <utility>
+
 #include "base/bind.h"
 #include "base/strings/stringprintf.h"
 #include "base/system/sys_info.h"
@@ -13,6 +15,7 @@
 #include "base/trace_event/memory_dump_manager.h"
 #include "cc/paint/image_transfer_cache_entry.h"
 #include "gpu/command_buffer/service/service_discardable_manager.h"
+#include "third_party/skia/include/core/SkImage.h"
 #include "third_party/skia/include/gpu/GrBackendSurface.h"
 #include "ui/gl/trace_util.h"
 
@@ -204,6 +207,37 @@
   }
 }
 
+bool ServiceTransferCache::CreateLockedImageEntry(
+    int decoder_id,
+    uint32_t entry_id,
+    ServiceDiscardableHandle handle,
+    GrContext* context,
+    base::span<const uint8_t> decoded_image,
+    size_t row_bytes,
+    const SkImageInfo& image_info,
+    bool needs_mips,
+    sk_sp<SkColorSpace> target_color_space) {
+  EntryKey key(decoder_id, cc::TransferCacheEntryType::kImage, entry_id);
+  auto found = entries_.Peek(key);
+  if (found != entries_.end())
+    return false;
+
+  // Create the service-side image transfer cache entry. Note that this involves
+  // uploading the image if it fits in GPU memory.
+  auto entry = std::make_unique<cc::ServiceImageTransferCacheEntry>();
+  if (!entry->BuildFromDecodedData(context, decoded_image, row_bytes,
+                                   image_info, needs_mips,
+                                   target_color_space)) {
+    return false;
+  }
+
+  // Insert it in the transfer cache.
+  total_size_ += entry->CachedSize();
+  entries_.Put(key, CacheEntryInternal(handle, std::move(entry)));
+  EnforceLimits();
+  return true;
+}
+
 bool ServiceTransferCache::OnMemoryDump(
     const base::trace_event::MemoryDumpArgs& args,
     base::trace_event::ProcessMemoryDump* pmd) {
diff --git a/gpu/command_buffer/service/service_transfer_cache.h b/gpu/command_buffer/service/service_transfer_cache.h
index 39229664..8b2f16a 100644
--- a/gpu/command_buffer/service/service_transfer_cache.h
+++ b/gpu/command_buffer/service/service_transfer_cache.h
@@ -5,7 +5,10 @@
 #ifndef GPU_COMMAND_BUFFER_SERVICE_SERVICE_TRANSFER_CACHE_H_
 #define GPU_COMMAND_BUFFER_SERVICE_SERVICE_TRANSFER_CACHE_H_
 
-#include <vector>
+#include <stddef.h>
+#include <stdint.h>
+
+#include <memory>
 
 #include "base/containers/mru_cache.h"
 #include "base/containers/span.h"
@@ -14,12 +17,18 @@
 #include "gpu/command_buffer/common/discardable_handle.h"
 #include "gpu/command_buffer/service/context_group.h"
 #include "gpu/gpu_gles2_export.h"
+#include "third_party/skia/include/core/SkRefCnt.h"
+
+class GrContext;
+class SkColorSpace;
+struct SkImageInfo;
 
 namespace gpu {
 
-// ServiceTransferCache is a GPU process interface for retreiving cached entries
+// ServiceTransferCache is a GPU process interface for retrieving cached entries
 // from the transfer cache. These entries are populated by client calls to the
-// ClientTransferCache.
+// ClientTransferCache or by an image decode accelerator task in the GPU
+// process.
 //
 // In addition to access, the ServiceTransferCache is also responsible for
 // unlocking and deleting entries when no longer needed, as well as enforcing
@@ -51,6 +60,23 @@
   cc::ServiceTransferCacheEntry* GetEntry(const EntryKey& key);
   void DeleteAllEntriesForDecoder(int decoder_id);
 
+  // Creates an image transfer cache entry using the decoded data in
+  // |decoded_image|. The |context| will be used to upload the image (if it's
+  // determined to fit in the GPU). |row_bytes| is the stride, and |image_info|
+  // describes the decoded data. |decoder_id| and |entry_id| are used for
+  // creating the ServiceTransferCache::EntryKey (assuming
+  // cc::TransferCacheEntryType:kImage for the type). Returns true if the entry
+  // could be created and inserted; false otherwise.
+  bool CreateLockedImageEntry(int decoder_id,
+                              uint32_t entry_id,
+                              ServiceDiscardableHandle handle,
+                              GrContext* context,
+                              base::span<const uint8_t> decoded_image,
+                              size_t row_bytes,
+                              const SkImageInfo& image_info,
+                              bool needs_mips,
+                              sk_sp<SkColorSpace> target_color_space);
+
   void PurgeMemory(
       base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level);
 
diff --git a/gpu/command_buffer/service/shared_image_backing_factory_gl_texture.cc b/gpu/command_buffer/service/shared_image_backing_factory_gl_texture.cc
index 56090006..321836d 100644
--- a/gpu/command_buffer/service/shared_image_backing_factory_gl_texture.cc
+++ b/gpu/command_buffer/service/shared_image_backing_factory_gl_texture.cc
@@ -524,7 +524,11 @@
       image_factory_(image_factory) {
   gl::GLApi* api = gl::g_current_gl_context;
   api->glGetIntegervFn(GL_MAX_TEXTURE_SIZE, &max_texture_size_);
-  if (workarounds.max_texture_size) {
+  // When the passthrough command decoder is used, the max_texture_size
+  // workaround is implemented by ANGLE. Trying to adjust the max size here
+  // would cause discrepency between what we think the max size is and what
+  // ANGLE tells the clients.
+  if (!use_passthrough_ && workarounds.max_texture_size) {
     max_texture_size_ =
         std::min(max_texture_size_, workarounds.max_texture_size);
   }
@@ -825,22 +829,29 @@
                 SHARED_IMAGE_USAGE_GLES2_FRAMEBUFFER_HINT)) != 0;
   GLuint service_id = MakeTextureAndSetParameters(
       api, target, for_framebuffer_attachment && texture_usage_angle_);
+  bool is_rgb_emulation = usage & SHARED_IMAGE_USAGE_RGB_EMULATION;
 
-  // TODO(piman): RGB emulation
   gles2::Texture::ImageState image_state = gles2::Texture::UNBOUND;
   if (image->ShouldBindOrCopy() == gl::GLImage::BIND) {
-    if (!image->BindTexImage(target)) {
+    bool is_bound = false;
+    if (is_rgb_emulation)
+      is_bound = image->BindTexImageWithInternalformat(target, GL_RGB);
+    else
+      is_bound = image->BindTexImage(target);
+    if (is_bound) {
+      image_state = gles2::Texture::BOUND;
+    } else {
       LOG(ERROR) << "Failed to bind image to target.";
       api->glDeleteTexturesFn(1, &service_id);
       return nullptr;
     }
-    image_state = gles2::Texture::BOUND;
   } else if (use_passthrough_) {
     image->CopyTexImage(target);
     image_state = gles2::Texture::COPIED;
   }
 
-  GLuint internal_format = image->GetInternalFormat();
+  GLuint internal_format =
+      is_rgb_emulation ? GL_RGB : image->GetInternalFormat();
   GLenum gl_format =
       gles2::TextureManager::ExtractFormatFromStorageFormat(internal_format);
   GLenum gl_type =
diff --git a/gpu/command_buffer/service/webgpu_decoder.cc b/gpu/command_buffer/service/webgpu_decoder.cc
index 6e1cb4e..aa12b087 100644
--- a/gpu/command_buffer/service/webgpu_decoder.cc
+++ b/gpu/command_buffer/service/webgpu_decoder.cc
@@ -4,6 +4,7 @@
 
 #include "gpu/command_buffer/service/webgpu_decoder.h"
 
+#include "base/logging.h"
 #include "base/macros.h"
 #include "gpu/command_buffer/common/webgpu_cmd_format.h"
 #include "gpu/command_buffer/common/webgpu_cmd_ids.h"
@@ -200,6 +201,10 @@
     NOTIMPLEMENTED();
     return nullptr;
   }
+  int GetRasterDecoderId() const override {
+    NOTREACHED();
+    return -1;
+  }
 
  private:
   typedef error::Error (WebGPUDecoderImpl::*CmdHandler)(
diff --git a/gpu/command_buffer/tests/gl_copy_texture_CHROMIUM_unittest.cc b/gpu/command_buffer/tests/gl_copy_texture_CHROMIUM_unittest.cc
index 5542e25..78255a9 100644
--- a/gpu/command_buffer/tests/gl_copy_texture_CHROMIUM_unittest.cc
+++ b/gpu/command_buffer/tests/gl_copy_texture_CHROMIUM_unittest.cc
@@ -14,12 +14,10 @@
 #include <stdint.h>
 
 #include "base/stl_util.h"
-#include "build/build_config.h"
 #include "gpu/command_buffer/tests/gl_manager.h"
 #include "gpu/command_buffer/tests/gl_test_utils.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "ui/gl/gl_enums.h"
 #include "ui/gl/gl_version_info.h"
 
 namespace gpu {
@@ -155,11 +153,11 @@
   color[3] = a;
 }
 
-void getExpectedColorAndMask(GLenum src_internal_format,
-                             GLenum dest_internal_format,
-                             const uint8_t* color,
-                             uint8_t* expected_color,
-                             uint8_t* expected_mask) {
+void getExpectedColor(GLenum src_internal_format,
+                      GLenum dest_internal_format,
+                      uint8_t* color,
+                      uint8_t* expected_color,
+                      uint8_t* mask) {
   uint8_t adjusted_color[4];
   switch (src_internal_format) {
     case GL_ALPHA:
@@ -189,34 +187,37 @@
     case GL_BGRA8_EXT:
       setColor(color[2], color[1], color[0], color[3], adjusted_color);
       break;
-    case GL_RGB10_A2: {
-      // Map the source 2-bit Alpha to 8-bits.
-      const uint8_t alpha_value = (color[3] & 0x3) * 255 / 3;
-      setColor(color[0] >> 2, color[1] >> 2, color[2] >> 2, alpha_value,
-               adjusted_color);
-      break;
-    }
     default:
-      NOTREACHED() << gl::GLEnums::GetStringEnum(src_internal_format);
+      NOTREACHED();
       break;
   }
 
   switch (dest_internal_format) {
-    // TODO(crbug.com/577144): Enable GL_ALPHA, GL_LUMINANCE and
-    // GL_LUMINANCE_ALPHA.
+    case GL_ALPHA:
+      setColor(0, 0, 0, adjusted_color[3], expected_color);
+      setColor(0, 0, 0, 1, mask);
+      break;
     case GL_R8:
     case GL_R16F:
     case GL_R32F:
     case GL_R8UI:
       setColor(adjusted_color[0], 0, 0, 0, expected_color);
-      setColor(1, 0, 0, 0, expected_mask);
+      setColor(1, 0, 0, 0, mask);
+      break;
+    case GL_LUMINANCE:
+      setColor(adjusted_color[0], 0, 0, 0, expected_color);
+      setColor(1, 0, 0, 0, mask);
+      break;
+    case GL_LUMINANCE_ALPHA:
+      setColor(adjusted_color[0], 0, 0, adjusted_color[3], expected_color);
+      setColor(1, 0, 0, 1, mask);
       break;
     case GL_RG8:
     case GL_RG16F:
     case GL_RG32F:
     case GL_RG8UI:
       setColor(adjusted_color[0], adjusted_color[1], 0, 0, expected_color);
-      setColor(1, 1, 0, 0, expected_mask);
+      setColor(1, 1, 0, 0, mask);
       break;
     case GL_RGB:
     case GL_RGB8:
@@ -230,7 +231,7 @@
     case GL_RGB8UI:
       setColor(adjusted_color[0], adjusted_color[1], adjusted_color[2], 0,
                expected_color);
-      setColor(1, 1, 1, 0, expected_mask);
+      setColor(1, 1, 1, 0, mask);
       break;
     case GL_RGBA:
     case GL_RGBA8:
@@ -244,25 +245,8 @@
     case GL_RGBA8UI:
       setColor(adjusted_color[0], adjusted_color[1], adjusted_color[2],
                adjusted_color[3], expected_color);
-      setColor(1, 1, 1, 1, expected_mask);
+      setColor(1, 1, 1, 1, mask);
       break;
-    case GL_RGB10_A2: {
-      //  Map the 2-bit Alpha values back to full bytes.
-      constexpr uint8_t step = 0x55;
-      const uint8_t alpha_value = (adjusted_color[3] + step / 2) / step * step;
-
-      setColor(adjusted_color[0], adjusted_color[1], adjusted_color[2],
-               alpha_value, expected_color);
-#if defined(OS_MACOSX)
-      // The alpha channel values for LUMINANCE_ALPHA source don't work as
-      // expected on Mac, so skip comparison of those.
-      setColor(1, 1, 1, src_internal_format != GL_LUMINANCE_ALPHA,
-               expected_mask);
-#else
-      setColor(1, 1, 1, 1, expected_mask);
-#endif
-      break;
-    }
     case GL_RGB5_A1:
       setColor(adjusted_color[0], adjusted_color[1], adjusted_color[2],
                (adjusted_color[3] >> 7) ? 0xFF : 0x0, expected_color);
@@ -270,10 +254,10 @@
       // channel of expected color is the source alpha value other than 255.
       // This should be wrong. Skip the alpha channel check and revisit this in
       // future.
-      setColor(1, 1, 1, 0, expected_mask);
+      setColor(1, 1, 1, 0, mask);
       break;
     default:
-      NOTREACHED() << gl::GLEnums::GetStringEnum(dest_internal_format);
+      NOTREACHED();
       break;
   }
 }
@@ -287,51 +271,38 @@
     uint8_t* expected_mask) {
   const uint32_t src_channel_count = gles2::GLES2Util::ElementsPerGroup(
       src_format_type.format, src_format_type.type);
-  constexpr uint8_t color[4] = {1u, 63u, 127u, 255u};
-  getExpectedColorAndMask(src_format_type.internal_format,
-                          dest_format_type.internal_format, color,
-                          expected_color, expected_mask);
-  const size_t num_pixels = width * height;
-  // TODO(mcasas): use std::make_unique<uint8_t[]> in this function.
-
+  uint8_t color[4] = {1u, 63u, 127u, 255u};
+  getExpectedColor(src_format_type.internal_format,
+                   dest_format_type.internal_format, color, expected_color,
+                   expected_mask);
   if (src_format_type.type == GL_UNSIGNED_BYTE) {
     std::unique_ptr<uint8_t[]> pixels(
-        new uint8_t[num_pixels * src_channel_count]);
-    for (uint32_t i = 0; i < num_pixels * src_channel_count;
+        new uint8_t[width * height * src_channel_count]);
+    for (uint32_t i = 0; i < width * height * src_channel_count;
          i += src_channel_count) {
       for (uint32_t j = 0; j < src_channel_count; ++j)
         pixels[i + j] = color[j];
     }
     return pixels;
   } else if (src_format_type.type == GL_UNSIGNED_SHORT) {
-    constexpr uint16_t color_16bit[4] = {color[0] << 8, color[1] << 8,
-                                         color[2] << 8, color[3] << 8};
+    uint16_t color_16bit[4] = {1u << 8, 63u << 8, 127u << 8, 255u << 8};
     std::unique_ptr<uint8_t[]> data(
-        new uint8_t[num_pixels * src_channel_count * sizeof(uint16_t)]);
+        new uint8_t[width * height * src_channel_count * sizeof(uint16_t)]);
     uint16_t* pixels = reinterpret_cast<uint16_t*>(data.get());
     int16_t flip_sign = -1;
-    for (uint32_t i = 0; i < num_pixels * src_channel_count;
+    for (uint32_t i = 0; i < width * height * src_channel_count;
          i += src_channel_count) {
       for (uint32_t j = 0; j < src_channel_count; ++j) {
         // Introduce an offset to the value to check. Expected value should be
         // the same as without the offset.
         flip_sign *= -1;
         pixels[i + j] =
-            color_16bit[j] + flip_sign * (0x7F * (i + j)) / num_pixels;
+            color_16bit[j] + flip_sign * (0x7F * (i + j)) / (width * height);
       }
     }
     return data;
-  } else if (src_format_type.type == GL_UNSIGNED_INT_2_10_10_10_REV) {
-    DCHECK_EQ(src_channel_count, 1u);
-    constexpr uint32_t color_rgb10_a2 = ((color[3] & 0x3) << 30) +
-                                        (color[2] << 20) + (color[1] << 10) +
-                                        color[0];
-    std::unique_ptr<uint8_t[]> data(new uint8_t[num_pixels * sizeof(uint32_t)]);
-    uint32_t* pixels = reinterpret_cast<uint32_t*>(data.get());
-    std::fill(pixels, pixels + num_pixels, color_rgb10_a2);
-    return data;
   }
-  NOTREACHED() << gl::GLEnums::GetStringEnum(src_format_type.type);
+  NOTREACHED();
   return nullptr;
 }
 
@@ -506,9 +477,7 @@
                                textures_[1], dest_level, 0, 0, 0, 0, width_,
                                height_, false, false, false);
     }
-    const GLenum last_error = glGetError();
-    EXPECT_TRUE(last_error == GL_NO_ERROR)
-        << gl::GLEnums::GetStringError(last_error);
+    EXPECT_TRUE(glGetError() == GL_NO_ERROR);
 
     // Draw destination texture to a fbo with a TEXTURE_2D texture attachment
     // in RGBA format.
@@ -602,19 +571,6 @@
 #endif
     return !gl_.decoder()->GetFeatureInfo()->feature_flags().ext_texture_norm16;
   }
-
-  bool ShouldSkipRGB10A2() const {
-    DCHECK(!ShouldSkipTest());
-    const gl::GLVersionInfo& gl_version_info =
-        gl_.decoder()->GetFeatureInfo()->gl_version_info();
-    // XB30 support was introduced in GLES 3.0/ OpenGL 3.3, before that it was
-    // signalled via a specific extension.
-    const bool supports_rgb10_a2 =
-        gl_version_info.IsAtLeastGL(3, 3) ||
-        gl_version_info.IsAtLeastGLES(3, 0) ||
-        GLTestHelper::HasExtension("GL_EXT_texture_type_2_10_10_10_REV");
-    return !supports_rgb10_a2;
-  }
 };
 
 INSTANTIATE_TEST_CASE_P(CopyType,
@@ -671,7 +627,7 @@
         << "Passthrough command decoder expected failure. Skipping test...";
     return;
   }
-  const CopyType copy_type = GetParam();
+  CopyType copy_type = GetParam();
 
   FormatType src_format_types[] = {
       {GL_LUMINANCE, GL_LUMINANCE, GL_UNSIGNED_BYTE},
@@ -683,7 +639,6 @@
       {GL_BGRA_EXT, GL_BGRA_EXT, GL_UNSIGNED_BYTE},
       {GL_BGRA8_EXT, GL_BGRA_EXT, GL_UNSIGNED_BYTE},
       {GL_R16_EXT, GL_RED, GL_UNSIGNED_SHORT},
-      {GL_RGB10_A2, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV},
   };
 
   FormatType dest_format_types[] = {
@@ -728,7 +683,6 @@
       {GL_RGBA16F, GL_RGBA, GL_FLOAT},
       {GL_RGBA32F, GL_RGBA, GL_FLOAT},
       {GL_RGBA8UI, GL_RGBA_INTEGER, GL_UNSIGNED_BYTE},
-      {GL_RGB10_A2, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV},
   };
 
   for (auto src_format_type : src_format_types) {
@@ -741,18 +695,14 @@
         continue;
       }
       if (gles2::GLES2Util::IsFloatFormat(dest_format_type.internal_format) &&
-          ShouldSkipFloatFormat()) {
+          ShouldSkipFloatFormat())
         continue;
-      }
       if ((dest_format_type.internal_format == GL_SRGB_EXT ||
            dest_format_type.internal_format == GL_SRGB_ALPHA_EXT) &&
-          ShouldSkipSRGBEXT()) {
+          ShouldSkipSRGBEXT())
         continue;
-      }
       if (src_format_type.internal_format == GL_R16_EXT && ShouldSkipNorm16())
         continue;
-      if (src_format_type.internal_format == GL_RGB10_A2 && ShouldSkipRGB10A2())
-        continue;
 
       RunCopyTexture(GL_TEXTURE_2D, copy_type, src_format_type, 0,
                      dest_format_type, 0, true);
diff --git a/gpu/config/gpu_driver_bug_list.json b/gpu/config/gpu_driver_bug_list.json
index 480a5e4..2ac3cf7 100644
--- a/gpu/config/gpu_driver_bug_list.json
+++ b/gpu/config/gpu_driver_bug_list.json
@@ -3067,23 +3067,6 @@
       "features": [
         "disable_direct_composition"
       ]
-    },
-    {
-      "id": 287,
-      "description": "glCopyTexImage2D on Adreno 4xx fails if source is GL_RGB10_A2 and destination is not.",
-      "cr_bugs": [925986],
-      "os": {
-        "type": "android",
-        "version": {
-          "op": ">=",
-          "value": "5.0.0"
-        }
-      },
-      "gl_vendor": "Qualcomm.*",
-      "gl_renderer": ".*4\\d\\d",
-      "features": [
-        "disable_copy_tex_image_2d_rgb10_a2"
-      ]
     }
   ]
 }
diff --git a/gpu/config/gpu_workaround_list.txt b/gpu/config/gpu_workaround_list.txt
index dda9a14..826c74a 100644
--- a/gpu/config/gpu_workaround_list.txt
+++ b/gpu/config/gpu_workaround_list.txt
@@ -106,4 +106,3 @@
 validate_multisample_buffer_allocation
 wake_up_gpu_before_drawing
 use_copyteximage2d_instead_of_readpixels_on_multisampled_textures
-disable_copy_tex_image_2d_rgb10_a2
diff --git a/gpu/ipc/service/image_decode_accelerator_stub.cc b/gpu/ipc/service/image_decode_accelerator_stub.cc
index 917d56e..c46fd8f4 100644
--- a/gpu/ipc/service/image_decode_accelerator_stub.cc
+++ b/gpu/ipc/service/image_decode_accelerator_stub.cc
@@ -4,24 +4,38 @@
 
 #include "gpu/ipc/service/image_decode_accelerator_stub.h"
 
+#include <utility>
+
 #include "base/bind.h"
+#include "base/containers/span.h"
 #include "base/feature_list.h"
 #include "base/location.h"
 #include "base/logging.h"
 #include "base/single_thread_task_runner.h"
 #include "gpu/command_buffer/common/constants.h"
+#include "gpu/command_buffer/common/context_result.h"
+#include "gpu/command_buffer/common/discardable_handle.h"
 #include "gpu/command_buffer/common/scheduling_priority.h"
 #include "gpu/command_buffer/common/sync_token.h"
+#include "gpu/command_buffer/service/context_group.h"
+#include "gpu/command_buffer/service/decoder_context.h"
 #include "gpu/command_buffer/service/scheduler.h"
+#include "gpu/command_buffer/service/service_transfer_cache.h"
+#include "gpu/command_buffer/service/shared_context_state.h"
 #include "gpu/command_buffer/service/sync_point_manager.h"
+#include "gpu/command_buffer/service/transfer_buffer_manager.h"
 #include "gpu/config/gpu_finch_features.h"
 #include "gpu/ipc/common/command_buffer_id.h"
+#include "gpu/ipc/service/command_buffer_stub.h"
 #include "gpu/ipc/service/gpu_channel.h"
+#include "gpu/ipc/service/gpu_channel_manager.h"
 #include "gpu/ipc/service/image_decode_accelerator_worker.h"
 #include "ipc/ipc_message.h"
 #include "ipc/ipc_message_macros.h"
+#include "ui/gfx/color_space.h"
 
 namespace gpu {
+class Buffer;
 
 ImageDecodeAcceleratorStub::ImageDecodeAcceleratorStub(
     ImageDecodeAcceleratorWorker* worker,
@@ -77,8 +91,8 @@
     uint64_t release_count) {
   DCHECK(io_task_runner_->BelongsToCurrentThread());
   base::AutoLock lock(lock_);
-  if (!channel_) {
-    // The channel is no longer available, so don't schedule a decode.
+  if (!channel_ || destroying_channel_) {
+    // The channel is no longer available, so don't do anything.
     return;
   }
 
@@ -99,10 +113,10 @@
   }
 
   // Start the actual decode.
-  worker_->Decode(std::move(decode_params.encoded_data),
-                  decode_params.output_size,
-                  base::BindOnce(&ImageDecodeAcceleratorStub::OnDecodeCompleted,
-                                 base::WrapRefCounted(this)));
+  worker_->Decode(
+      std::move(decode_params.encoded_data), decode_params.output_size,
+      base::BindOnce(&ImageDecodeAcceleratorStub::OnDecodeCompleted,
+                     base::WrapRefCounted(this), decode_params.output_size));
 
   // Schedule a task to eventually release the decode sync token. Note that this
   // task won't run until the sequence is re-enabled when a decode completes.
@@ -119,14 +133,86 @@
     uint64_t decode_release_count) {
   DCHECK(main_task_runner_->BelongsToCurrentThread());
   base::AutoLock lock(lock_);
-  if (!channel_) {
+  if (!channel_ || destroying_channel_) {
     // The channel is no longer available, so don't do anything.
     return;
   }
 
-  // TODO(andrescj): create the transfer cache entry. Doing so will also upload
-  // the decoded image to a GPU texture.
+  DCHECK(!pending_completed_decodes_.empty());
+  std::unique_ptr<CompletedDecode> completed_decode =
+      std::move(pending_completed_decodes_.front());
 
+  // Gain access to the transfer cache through the GpuChannelManager's
+  // SharedContextState. We will also use that to get a GrContext that will be
+  // used for uploading the image.
+  ContextResult context_result;
+  scoped_refptr<SharedContextState> shared_context_state =
+      channel_->gpu_channel_manager()->GetSharedContextState(&context_result);
+  if (context_result != ContextResult::kSuccess) {
+    DLOG(ERROR) << "Unable to obtain the SharedContextState";
+    OnError();
+    return;
+  }
+  DCHECK(shared_context_state);
+  if (!shared_context_state->gr_context()) {
+    DLOG(ERROR) << "Could not get the GrContext";
+    OnError();
+    return;
+  }
+  if (!shared_context_state->MakeCurrent(nullptr /* surface */)) {
+    DLOG(ERROR) << "Could not MakeCurrent the shared context";
+    OnError();
+    return;
+  }
+
+  // Insert the cache entry in the transfer cache. Note that this section
+  // validates several of the IPC parameters: |params.raster_decoder_route_id|,
+  // |params.transfer_cache_entry_id|, |params.discardable_handle_shm_id|, and
+  // |params.discardable_handle_shm_offset|.
+  CommandBufferStub* command_buffer =
+      channel_->LookupCommandBuffer(params.raster_decoder_route_id);
+  if (!command_buffer) {
+    DLOG(ERROR) << "Could not find the command buffer";
+    OnError();
+    return;
+  }
+  DCHECK(command_buffer->context_group());
+  DCHECK(command_buffer->context_group()->transfer_buffer_manager());
+  scoped_refptr<Buffer> handle_buffer =
+      command_buffer->context_group()
+          ->transfer_buffer_manager()
+          ->GetTransferBuffer(params.discardable_handle_shm_id);
+  if (!DiscardableHandleBase::ValidateParameters(
+          handle_buffer.get(), params.discardable_handle_shm_offset)) {
+    DLOG(ERROR) << "Could not validate the discardable handle parameters";
+    OnError();
+    return;
+  }
+  DCHECK(command_buffer->decoder_context());
+  if (command_buffer->decoder_context()->GetRasterDecoderId() < 0) {
+    DLOG(ERROR) << "Could not get the raster decoder ID";
+    OnError();
+    return;
+  }
+  DCHECK(shared_context_state->transfer_cache());
+  if (!shared_context_state->transfer_cache()->CreateLockedImageEntry(
+          command_buffer->decoder_context()->GetRasterDecoderId(),
+          params.transfer_cache_entry_id,
+          ServiceDiscardableHandle(std::move(handle_buffer),
+                                   params.discardable_handle_shm_offset,
+                                   params.discardable_handle_shm_id),
+          shared_context_state->gr_context(),
+          base::make_span(completed_decode->output),
+          completed_decode->row_bytes, completed_decode->image_info,
+          params.needs_mips, params.target_color_space.ToSkColorSpace())) {
+    DLOG(ERROR) << "Could not create and insert the transfer cache entry";
+    OnError();
+    return;
+  }
+  shared_context_state->set_need_context_state_reset(true);
+
+  // All done! The decoded image can now be used for rasterization, so we can
+  // release the decode sync token.
   sync_point_client_state_->ReleaseFenceSync(decode_release_count);
 
   // If there are no more completed decodes to be processed, we can disable the
@@ -137,27 +223,42 @@
     channel_->scheduler()->DisableSequence(sequence_);
 }
 
+ImageDecodeAcceleratorStub::CompletedDecode::CompletedDecode(
+    std::vector<uint8_t> output,
+    size_t row_bytes,
+    SkImageInfo image_info)
+    : output(std::move(output)), row_bytes(row_bytes), image_info(image_info) {}
+
+ImageDecodeAcceleratorStub::CompletedDecode::~CompletedDecode() = default;
+
 void ImageDecodeAcceleratorStub::OnDecodeCompleted(
-    std::vector<uint8_t> rgba_output) {
+    gfx::Size expected_output_size,
+    std::vector<uint8_t> output,
+    size_t row_bytes,
+    SkImageInfo image_info) {
   base::AutoLock lock(lock_);
-  if (!channel_) {
+  if (!channel_ || destroying_channel_) {
     // The channel is no longer available, so don't do anything.
     return;
   }
 
-  if (!accepting_completed_decodes_) {
-    // We're still waiting for the channel to be destroyed because of an earlier
-    // failure, so don't do anything.
-    return;
-  }
-
-  if (rgba_output.empty()) {
+  if (output.empty()) {
     DLOG(ERROR) << "The decode failed";
     OnError();
     return;
   }
 
-  pending_completed_decodes_.push(std::move(rgba_output));
+  // Some sanity checks on the output of the decoder.
+  DCHECK_EQ(expected_output_size.width(), image_info.width());
+  DCHECK_EQ(expected_output_size.height(), image_info.height());
+  DCHECK_NE(0u, image_info.minRowBytes());
+  DCHECK_GE(row_bytes, image_info.minRowBytes());
+  DCHECK_EQ(output.size(), image_info.computeByteSize(row_bytes));
+
+  // The decode is ready to be processed: add it to |pending_completed_decodes_|
+  // so that ProcessCompletedDecode() can pick it up.
+  pending_completed_decodes_.push(std::make_unique<CompletedDecode>(
+      std::move(output), row_bytes, image_info));
 
   // We only need to enable the sequence when the number of pending completed
   // decodes is 1. If there are more, the sequence should already be enabled.
@@ -166,6 +267,7 @@
 }
 
 void ImageDecodeAcceleratorStub::OnError() {
+  lock_.AssertAcquired();
   DCHECK(channel_);
 
   // Trigger the destruction of the channel and stop processing further
@@ -173,7 +275,7 @@
   // GpuChannel::OnChannelError() directly because that will end up calling
   // ImageDecodeAcceleratorStub::Shutdown() while |lock_| is still acquired. So,
   // we post a task to the main thread instead.
-  accepting_completed_decodes_ = false;
+  destroying_channel_ = true;
   channel_->task_runner()->PostTask(
       FROM_HERE,
       base::BindOnce(&GpuChannel::OnChannelError, channel_->AsWeakPtr()));
diff --git a/gpu/ipc/service/image_decode_accelerator_stub.h b/gpu/ipc/service/image_decode_accelerator_stub.h
index a8b207a..bc274251 100644
--- a/gpu/ipc/service/image_decode_accelerator_stub.h
+++ b/gpu/ipc/service/image_decode_accelerator_stub.h
@@ -5,6 +5,10 @@
 #ifndef GPU_IPC_SERVICE_IMAGE_DECODE_ACCELERATOR_STUB_H_
 #define GPU_IPC_SERVICE_IMAGE_DECODE_ACCELERATOR_STUB_H_
 
+#include <stddef.h>
+#include <stdint.h>
+
+#include <memory>
 #include <vector>
 
 #include "base/containers/queue.h"
@@ -15,6 +19,8 @@
 #include "base/thread_annotations.h"
 #include "gpu/command_buffer/service/sequence_id.h"
 #include "gpu/ipc/common/gpu_messages.h"
+#include "third_party/skia/include/core/SkImageInfo.h"
+#include "ui/gfx/geometry/size.h"
 
 namespace base {
 class SingleThreadTaskRunner;
@@ -74,10 +80,13 @@
                               uint64_t decode_release_count);
 
   // The |worker_| calls this when a decode is completed. If the decode is
-  // successful (i.e., |rgba_output| is not empty), |sequence_| will be enabled
-  // so that ProcessCompletedDecode() is called. If the decode is not
-  // successful, we destroy the channel (see OnError()).
-  void OnDecodeCompleted(std::vector<uint8_t> rgba_output);
+  // successful (i.e., |output| is not empty), |sequence_| will be enabled so
+  // that ProcessCompletedDecode() is called. If the decode is not successful,
+  // we destroy the channel (see OnError()).
+  void OnDecodeCompleted(gfx::Size expected_output_size,
+                         std::vector<uint8_t> output,
+                         size_t row_bytes,
+                         SkImageInfo image_info);
 
   // Triggers the destruction of the channel asynchronously and makes it so that
   // we stop accepting completed decodes. On entry, |channel_| must not be
@@ -87,14 +96,27 @@
   // The object to which the actual decoding can be delegated.
   ImageDecodeAcceleratorWorker* worker_ = nullptr;
 
+  struct CompletedDecode {
+    CompletedDecode(std::vector<uint8_t> output,
+                    size_t row_bytes,
+                    SkImageInfo image_info);
+    ~CompletedDecode();
+
+    std::vector<uint8_t> output;
+    size_t row_bytes;
+    SkImageInfo image_info;
+
+    DISALLOW_COPY_AND_ASSIGN(CompletedDecode);
+  };
+
   base::Lock lock_;
   GpuChannel* channel_ GUARDED_BY(lock_) = nullptr;
   SequenceId sequence_ GUARDED_BY(lock_);
   scoped_refptr<SyncPointClientState> sync_point_client_state_
       GUARDED_BY(lock_);
-  base::queue<std::vector<uint8_t>> pending_completed_decodes_
+  base::queue<std::unique_ptr<CompletedDecode>> pending_completed_decodes_
       GUARDED_BY(lock_);
-  bool accepting_completed_decodes_ GUARDED_BY(lock_) = true;
+  bool destroying_channel_ GUARDED_BY(lock_) = false;
   uint64_t last_release_count_ GUARDED_BY(lock_) = 0;
 
   scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_;
diff --git a/gpu/ipc/service/image_decode_accelerator_stub_unittest.cc b/gpu/ipc/service/image_decode_accelerator_stub_unittest.cc
index 8b738c6f..ac9f6a2 100644
--- a/gpu/ipc/service/image_decode_accelerator_stub_unittest.cc
+++ b/gpu/ipc/service/image_decode_accelerator_stub_unittest.cc
@@ -2,6 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <stddef.h>
+#include <stdint.h>
+
+#include <utility>
+#include <vector>
+
 #include "base/containers/queue.h"
 #include "base/macros.h"
 #include "base/numerics/checked_math.h"
@@ -17,6 +23,7 @@
 #include "gpu/ipc/service/gpu_channel_test_common.h"
 #include "gpu/ipc/service/image_decode_accelerator_worker.h"
 #include "testing/gmock/include/gmock/gmock.h"
+#include "third_party/skia/include/core/SkImageInfo.h"
 #include "ui/gfx/color_space.h"
 #include "ui/gfx/geometry/size.h"
 
@@ -34,7 +41,7 @@
 
   void Decode(std::vector<uint8_t> encoded_data,
               const gfx::Size& output_size,
-              base::OnceCallback<void(std::vector<uint8_t>)> decode_cb) {
+              CompletedDecodeCB decode_cb) {
     pending_decodes_.push(PendingDecode{output_size, std::move(decode_cb)});
     DoDecode(output_size);
   }
@@ -45,13 +52,19 @@
     PendingDecode next_decode = std::move(pending_decodes_.front());
     pending_decodes_.pop();
     if (success) {
-      base::CheckedNumeric<size_t> rgba_bytes = 4u;
-      rgba_bytes *= next_decode.output_size.width();
+      base::CheckedNumeric<size_t> row_bytes = 4u;
+      row_bytes *= next_decode.output_size.width();
+      base::CheckedNumeric<size_t> rgba_bytes = row_bytes;
       rgba_bytes *= next_decode.output_size.height();
       std::vector<uint8_t> rgba_output(rgba_bytes.ValueOrDie(), 0u);
-      std::move(next_decode.decode_cb).Run(std::move(rgba_output));
+      std::move(next_decode.decode_cb)
+          .Run(std::move(rgba_output), row_bytes.ValueOrDie(),
+               SkImageInfo::Make(next_decode.output_size.width(),
+                                 next_decode.output_size.height(),
+                                 kRGBA_8888_SkColorType, kOpaque_SkAlphaType));
     } else {
-      std::move(next_decode.decode_cb).Run(std::vector<uint8_t>());
+      std::move(next_decode.decode_cb)
+          .Run(std::vector<uint8_t>(), 0u, SkImageInfo());
     }
   }
 
@@ -60,7 +73,7 @@
  private:
   struct PendingDecode {
     gfx::Size output_size;
-    base::OnceCallback<void(std::vector<uint8_t>)> decode_cb;
+    CompletedDecodeCB decode_cb;
   };
 
   base::queue<PendingDecode> pending_decodes_;
@@ -157,8 +170,11 @@
 // completed. This should cause one sync token to be released and the scheduler
 // sequence to be disabled. Then, the second decode is completed. This should
 // cause the other sync token to be released.
+//
+// Disabled until ImageDecodeAcceleratorStubTest supports transfer cache
+// infrastructure. See https://crbug.com/868400.
 TEST_F(ImageDecodeAcceleratorStubTest,
-       MultipleDecodesCompletedAfterSequenceIsDisabled) {
+       DISABLED_MultipleDecodesCompletedAfterSequenceIsDisabled) {
   {
     InSequence call_sequence;
     EXPECT_CALL(image_decode_accelerator_worker_, DoDecode(gfx::Size(100, 100)))
@@ -198,8 +214,11 @@
 // completes which should cause the scheduler sequence to be enabled. Right
 // after that (while the sequence is still enabled), the other two decodes
 // complete. At the end, all the sync tokens should be released.
+//
+// Disabled until ImageDecodeAcceleratorStubTest supports transfer cache
+// infrastructure. See https://crbug.com/868400.
 TEST_F(ImageDecodeAcceleratorStubTest,
-       MultipleDecodesCompletedWhileSequenceIsEnabled) {
+       DISABLED_MultipleDecodesCompletedWhileSequenceIsEnabled) {
   {
     InSequence call_sequence;
     EXPECT_CALL(image_decode_accelerator_worker_, DoDecode(gfx::Size(100, 100)))
@@ -239,7 +258,10 @@
 // fails which should trigger the destruction of the channel. The second
 // succeeds and the third one fails. Regardless, the channel should still be
 // destroyed and all sync tokens should be released.
-TEST_F(ImageDecodeAcceleratorStubTest, FailedDecodes) {
+//
+// Disabled until ImageDecodeAcceleratorStubTest supports transfer cache
+// infrastructure. See https://crbug.com/868400.
+TEST_F(ImageDecodeAcceleratorStubTest, DISABLED_FailedDecodes) {
   {
     InSequence call_sequence;
     EXPECT_CALL(image_decode_accelerator_worker_, DoDecode(gfx::Size(100, 100)))
@@ -274,7 +296,9 @@
   EXPECT_TRUE(sync_point_manager()->IsSyncTokenReleased(decode3_sync_token));
 }
 
-TEST_F(ImageDecodeAcceleratorStubTest, OutOfOrderSyncTokens) {
+// Disabled until ImageDecodeAcceleratorStubTest supports transfer cache
+// infrastructure. See https://crbug.com/868400.
+TEST_F(ImageDecodeAcceleratorStubTest, DISABLED_OutOfOrderSyncTokens) {
   EXPECT_CALL(image_decode_accelerator_worker_, DoDecode(gfx::Size(100, 100)))
       .Times(1);
   const SyncToken decode1_sync_token = SendDecodeRequest(
@@ -290,7 +314,9 @@
   EXPECT_TRUE(sync_point_manager()->IsSyncTokenReleased(decode2_sync_token));
 }
 
-TEST_F(ImageDecodeAcceleratorStubTest, ZeroReleaseCountSyncToken) {
+// Disabled until ImageDecodeAcceleratorStubTest supports transfer cache
+// infrastructure. See https://crbug.com/868400.
+TEST_F(ImageDecodeAcceleratorStubTest, DISABLED_ZeroReleaseCountSyncToken) {
   const SyncToken decode_sync_token = SendDecodeRequest(
       gfx::Size(100, 100) /* output_size */, 0u /* release_count */);
 
@@ -301,7 +327,9 @@
   EXPECT_TRUE(sync_point_manager()->IsSyncTokenReleased(decode_sync_token));
 }
 
-TEST_F(ImageDecodeAcceleratorStubTest, ZeroWidthOutputSize) {
+// Disabled until ImageDecodeAcceleratorStubTest supports transfer cache
+// infrastructure. See https://crbug.com/868400.
+TEST_F(ImageDecodeAcceleratorStubTest, DISABLED_ZeroWidthOutputSize) {
   const SyncToken decode_sync_token = SendDecodeRequest(
       gfx::Size(0, 100) /* output_size */, 1u /* release_count */);
 
@@ -312,7 +340,9 @@
   EXPECT_TRUE(sync_point_manager()->IsSyncTokenReleased(decode_sync_token));
 }
 
-TEST_F(ImageDecodeAcceleratorStubTest, ZeroHeightOutputSize) {
+// Disabled until ImageDecodeAcceleratorStubTest supports transfer cache
+// infrastructure. See https://crbug.com/868400.
+TEST_F(ImageDecodeAcceleratorStubTest, DISABLED_ZeroHeightOutputSize) {
   const SyncToken decode_sync_token = SendDecodeRequest(
       gfx::Size(100, 0) /* output_size */, 1u /* release_count */);
 
diff --git a/gpu/ipc/service/image_decode_accelerator_worker.h b/gpu/ipc/service/image_decode_accelerator_worker.h
index 66efb30..a494783 100644
--- a/gpu/ipc/service/image_decode_accelerator_worker.h
+++ b/gpu/ipc/service/image_decode_accelerator_worker.h
@@ -5,9 +5,13 @@
 #ifndef GPU_IPC_SERVICE_IMAGE_DECODE_ACCELERATOR_WORKER_H_
 #define GPU_IPC_SERVICE_IMAGE_DECODE_ACCELERATOR_WORKER_H_
 
+#include <stddef.h>
+#include <stdint.h>
+
 #include <vector>
 
 #include "base/callback.h"
+#include "third_party/skia/include/core/SkImageInfo.h"
 
 namespace gfx {
 class Size;
@@ -21,16 +25,27 @@
  public:
   virtual ~ImageDecodeAcceleratorWorker() {}
 
+  using CompletedDecodeCB =
+      base::OnceCallback<void(std::vector<uint8_t> /* output */,
+                              size_t /* row_bytes */,
+                              SkImageInfo /* image_info */)>;
+
   // Enqueue a decode of |encoded_data|. The |decode_cb| is called
-  // asynchronously when the decode completes passing as a parameter a vector
-  // containing the decoded image in RGBA format (the stride of the output is
-  // |output_size|.width() * 4). If the decode fails, |decode_cb| is called
-  // asynchronously with an empty vector. Callbacks should be called in the
-  // order that this method is called.
-  virtual void Decode(
-      std::vector<uint8_t> encoded_data,
-      const gfx::Size& output_size,
-      base::OnceCallback<void(std::vector<uint8_t>)> decode_cb) = 0;
+  // asynchronously when the decode completes passing as parameters a vector
+  // containing the decoded image (|output|), the stride (|row_bytes|), and a
+  // SkImageInfo (|image_info|) with information about the decoded output.
+  // For a successful decode, implementations must guarantee that:
+  //
+  // 1) |image_info|.width() == |output_size|.width().
+  // 2) |image_info|.height() == |output_size|.height().
+  // 3) |row_bytes| >= |image_info|.minRowBytes().
+  // 4) |output|.size() == |image_info|.computeByteSize(|row_bytes|).
+  //
+  // If the decode fails, |decode_cb| is called asynchronously with an empty
+  // vector. Callbacks should be called in the order that this method is called.
+  virtual void Decode(std::vector<uint8_t> encoded_data,
+                      const gfx::Size& output_size,
+                      CompletedDecodeCB decode_cb) = 0;
 };
 
 }  // namespace gpu
diff --git a/gpu/perftests/texture_upload_perftest.cc b/gpu/perftests/texture_upload_perftest.cc
index ff91cff..e7be34d 100644
--- a/gpu/perftests/texture_upload_perftest.cc
+++ b/gpu/perftests/texture_upload_perftest.cc
@@ -64,7 +64,9 @@
 // clang-format on
 
 void CheckNoGlError(const std::string& msg) {
-  CHECK_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError()) << " " << msg;
+  const GLenum error = glGetError();
+  CHECK_EQ(static_cast<GLenum>(GL_NO_ERROR), error)
+      << msg << " " << gl::GLEnums::GetStringError(error);
 }
 
 // Utility function to compile a shader from a string.
diff --git a/gpu/vulkan/android/vulkan_android_unittests.cc b/gpu/vulkan/android/vulkan_android_unittests.cc
index a27c8e08..cee9d41e 100644
--- a/gpu/vulkan/android/vulkan_android_unittests.cc
+++ b/gpu/vulkan/android/vulkan_android_unittests.cc
@@ -82,15 +82,8 @@
   // signal operation pending execution before the export.
   // Semaphores can be signaled by including them in a batch as part of a queue
   // submission command, defining a queue operation to signal that semaphore.
-  unsigned int submit_count = 1;
-  VkFence fence = VK_NULL_HANDLE;
-  VkSubmitInfo submit_info = {VK_STRUCTURE_TYPE_SUBMIT_INFO};
-  submit_info.signalSemaphoreCount = 1;
-  submit_info.pSignalSemaphores = &semaphore1;
-  result =
-      vkQueueSubmit(vk_context_provider_->GetDeviceQueue()->GetVulkanQueue(),
-                    submit_count, &submit_info, fence);
-  EXPECT_EQ(result, VK_SUCCESS);
+  EXPECT_TRUE(vk_implementation_->SubmitSignalSemaphore(
+      vk_context_provider_->GetDeviceQueue()->GetVulkanQueue(), semaphore1));
 
   // Export a sync fd from the semaphore.
   base::ScopedFD sync_fd;
diff --git a/gpu/vulkan/vulkan_implementation.cc b/gpu/vulkan/vulkan_implementation.cc
index b4655d1..b8973ff6 100644
--- a/gpu/vulkan/vulkan_implementation.cc
+++ b/gpu/vulkan/vulkan_implementation.cc
@@ -6,6 +6,7 @@
 
 #include "base/bind.h"
 #include "gpu/vulkan/vulkan_device_queue.h"
+#include "gpu/vulkan/vulkan_function_pointers.h"
 #include "gpu/vulkan/vulkan_instance.h"
 
 namespace gpu {
@@ -33,4 +34,34 @@
   return device_queue;
 }
 
+bool VulkanImplementation::SubmitSignalSemaphore(VkQueue vk_queue,
+                                                 VkSemaphore vk_semaphore,
+                                                 VkFence vk_fence) {
+  // Structure specifying a queue submit operation.
+  VkSubmitInfo submit_info = {VK_STRUCTURE_TYPE_SUBMIT_INFO};
+  submit_info.signalSemaphoreCount = 1;
+  submit_info.pSignalSemaphores = &vk_semaphore;
+  const unsigned int submit_count = 1;
+  if (vkQueueSubmit(vk_queue, submit_count, &submit_info, vk_fence) !=
+      VK_SUCCESS) {
+    return false;
+  }
+  return true;
+}
+
+bool VulkanImplementation::SubmitWaitSemaphore(VkQueue vk_queue,
+                                               VkSemaphore vk_semaphore,
+                                               VkFence vk_fence) {
+  // Structure specifying a queue submit operation.
+  VkSubmitInfo submit_info = {VK_STRUCTURE_TYPE_SUBMIT_INFO};
+  submit_info.waitSemaphoreCount = 1;
+  submit_info.pWaitSemaphores = &vk_semaphore;
+  const unsigned int submit_count = 1;
+  if (vkQueueSubmit(vk_queue, submit_count, &submit_info, vk_fence) !=
+      VK_SUCCESS) {
+    return false;
+  }
+  return true;
+}
+
 }  // namespace gpu
diff --git a/gpu/vulkan/vulkan_implementation.h b/gpu/vulkan/vulkan_implementation.h
index 721db21..0b9e7e95 100644
--- a/gpu/vulkan/vulkan_implementation.h
+++ b/gpu/vulkan/vulkan_implementation.h
@@ -8,7 +8,6 @@
 #include <vulkan/vulkan.h>
 #include <memory>
 
-#include "base/files/scoped_file.h"
 #include "base/macros.h"
 #include "build/build_config.h"
 #include "gpu/vulkan/vulkan_export.h"
@@ -30,8 +29,9 @@
 class VulkanSurface;
 class VulkanInstance;
 
-// This object provides factory functions for creating vulkan objects that use
-// platform-specific extensions (e.g. for creation of VkSurfaceKHR objects).
+// Base class which provides functions for creating vulkan objects for different
+// platforms that use platform-specific extensions (e.g. for creation of
+// VkSurfaceKHR objects). It also provides helper/utility functions.
 class VULKAN_EXPORT VulkanImplementation {
  public:
   VulkanImplementation();
@@ -62,6 +62,20 @@
       VkDevice vk_device,
       VkFence vk_fence) = 0;
 
+  // Submits a semaphore to be signalled to the vulkan queue. Semaphore is
+  // signalled once this submission is executed. vk_fence is an optional handle
+  // to fence to be signaled once this submission completes execution.
+  bool SubmitSignalSemaphore(VkQueue vk_queue,
+                             VkSemaphore vk_semaphore,
+                             VkFence vk_fence = VK_NULL_HANDLE);
+
+  // Submits a semaphore to be waited upon to the vulkan queue. Semaphore is
+  // waited on before this submission is executed. vk_fence is an optional
+  // handle to fence to be signaled once this submission completes execution.
+  bool SubmitWaitSemaphore(VkQueue vk_queue,
+                           VkSemaphore vk_semaphore,
+                           VkFence vk_fence = VK_NULL_HANDLE);
+
 #if defined(OS_ANDROID)
   // Import a VkSemaphore from a POSIX sync file descriptor. Importing a
   // semaphore payload from a file descriptor transfers ownership of the file
diff --git a/infra/config/global/cr-buildbucket.cfg b/infra/config/global/cr-buildbucket.cfg
index ad47f18..b1923e2d 100644
--- a/infra/config/global/cr-buildbucket.cfg
+++ b/infra/config/global/cr-buildbucket.cfg
@@ -326,6 +326,12 @@
 }
 
 builder_mixins {
+  name: "experimental"
+  experimental: YES
+  luci_migration_host: "-"
+}
+
+builder_mixins {
   name: "gpu-fyi-ci"
   service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
   recipe {
@@ -2550,51 +2556,61 @@
       name: "Chromium Linux Goma RBE Staging (dbg)"
       dimensions: "os:Ubuntu-14.04"
       mixins: "goma-ci"
+      mixins: "experimental"
     }
     builders {
       name: "CrWinGomaStaging"
       dimensions: "os:Windows-10"
       mixins: "goma-ci"
+      mixins: "experimental"
     }
     builders {
       name: "Chromium Linux Goma RBE ToT"
       dimensions: "os:Ubuntu-14.04"
       mixins: "goma-ci"
+      mixins: "experimental"
     }
     builders {
       name: "Chromium Linux Goma RBE ToT (ATS)"
       dimensions: "os:Ubuntu-14.04"
       mixins: "goma-ci"
+      mixins: "experimental"
     }
     builders {
       name: "Chromium Linux Goma RBE Staging"
       dimensions: "os:Ubuntu-14.04"
       mixins: "goma-ci"
+      mixins: "experimental"
     }
     builders {
       name: "Chromium Linux Goma RBE Staging (clobber)"
       dimensions: "os:Ubuntu-14.04"
       mixins: "goma-ci"
+      mixins: "experimental"
     }
     builders {
       name: "Chromium Linux Goma RBE Prod"
       dimensions: "os:Ubuntu-14.04"
       mixins: "goma-ci"
+      mixins: "experimental"
     }
     builders {
       name: "Chromium Linux Goma RBE Prod (clobber)"
       dimensions: "os:Ubuntu-14.04"
       mixins: "goma-ci"
+      mixins: "experimental"
     }
     builders {
       name: "Chromium Linux Goma RBE Prod (dbg)"
       dimensions: "os:Ubuntu-14.04"
       mixins: "goma-ci"
+      mixins: "experimental"
     }
     builders {
       name: "Chromium Linux Goma RBE Prod (dbg) (clobber)"
       dimensions: "os:Ubuntu-14.04"
       mixins: "goma-ci"
+      mixins: "experimental"
     }
     builders {
       name: "Chromium Mac Goma RBE Staging (clobber)"
@@ -2602,6 +2618,7 @@
       dimensions: "cores:4"
       mixins: "goma-ci"
       mixins: "goma-j80"
+      mixins: "experimental"
     }
     builders {
       name: "Chromium Mac Goma RBE Staging"
@@ -2609,6 +2626,7 @@
       dimensions: "cores:4"
       mixins: "goma-ci"
       mixins: "goma-j80"
+      mixins: "experimental"
     }
     builders {
       name: "Chromium Mac Goma RBE Staging (dbg)"
@@ -2616,46 +2634,55 @@
       dimensions: "cores:4"
       mixins: "goma-ci"
       mixins: "goma-j80"
+      mixins: "experimental"
     }
     builders {
       name: "Chromium Win Goma RBE ToT"
       dimensions: "os:Windows-10"
       mixins: "goma-ci"
+      mixins: "experimental"
     }
     builders {
       name: "Chromium Android ARM 32-bit Goma RBE ToT"
       dimensions: "os:Ubuntu-14.04"
       mixins: "goma-ci"
+      mixins: "experimental"
     }
     builders {
       name: "Chromium Android ARM 32-bit Goma RBE Staging"
       dimensions: "os:Ubuntu-14.04"
       mixins: "goma-ci"
+      mixins: "experimental"
     }
     builders {
       name: "Chromium Android ARM 32-bit Goma RBE Prod"
       dimensions: "os:Ubuntu-14.04"
       mixins: "goma-ci"
+      mixins: "experimental"
     }
     builders {
       name: "Chromium Android ARM 32-bit Goma RBE Prod (clobber)"
       dimensions: "os:Ubuntu-14.04"
       mixins: "goma-ci"
+      mixins: "experimental"
     }
     builders {
       name: "Chromium Android ARM 32-bit Goma RBE Prod (dbg)"
       dimensions: "os:Ubuntu-14.04"
       mixins: "goma-ci"
+      mixins: "experimental"
     }
     builders {
       name: "Chromium Android ARM 32-bit Goma RBE Prod (dbg) (clobber)"
       dimensions: "os:Ubuntu-14.04"
       mixins: "goma-ci"
+      mixins: "experimental"
     }
     builders {
       name: "Chromium Linux Goma RBE Staging (dbg) (clobber)"
       dimensions: "os:Ubuntu-14.04"
       mixins: "goma-ci"
+      mixins: "experimental"
     }
     builders {
       name: "Chromium Mac Goma Staging"
@@ -2663,11 +2690,13 @@
       dimensions: "cores:4"
       mixins: "goma-ci"
       mixins: "goma-j80"
+      mixins: "experimental"
     }
     builders {
       name: "Chromium Linux Goma Staging"
       dimensions: "os:Ubuntu-14.04"
       mixins: "goma-ci"
+      mixins: "experimental"
     }
     # Goma Canary
     builders {
diff --git a/ios/chrome/browser/BUILD.gn b/ios/chrome/browser/BUILD.gn
index 2d36f2e..51d3a08 100644
--- a/ios/chrome/browser/BUILD.gn
+++ b/ios/chrome/browser/BUILD.gn
@@ -138,7 +138,6 @@
     "//ios/chrome/browser/ui/fullscreen:feature_flags",
     "//ios/chrome/browser/ui/infobars:feature_flags",
     "//ios/chrome/browser/ui/omnibox",
-    "//ios/chrome/browser/ui/sad_tab:features",
     "//ios/chrome/browser/ui/toolbar/public:feature_flags",
     "//ios/chrome/browser/ui/toolbar_container:feature_flags",
     "//ios/chrome/browser/web:feature_flags",
diff --git a/ios/chrome/browser/about_flags.mm b/ios/chrome/browser/about_flags.mm
index 1568dab..a52a8a9 100644
--- a/ios/chrome/browser/about_flags.mm
+++ b/ios/chrome/browser/about_flags.mm
@@ -58,7 +58,6 @@
 #import "ios/chrome/browser/ui/dialogs/dialog_features.h"
 #import "ios/chrome/browser/ui/fullscreen/fullscreen_features.h"
 #import "ios/chrome/browser/ui/infobars/infobar_feature.h"
-#include "ios/chrome/browser/ui/sad_tab/features.h"
 #import "ios/chrome/browser/ui/toolbar/public/features.h"
 #import "ios/chrome/browser/ui/toolbar_container/toolbar_container_features.h"
 #include "ios/chrome/browser/ui/ui_feature_flags.h"
@@ -414,10 +413,6 @@
     {"toolbar-container", flag_descriptions::kToolbarContainerName,
      flag_descriptions::kToolbarContainerDescription, flags_ui::kOsIos,
      FEATURE_VALUE_TYPE(toolbar_container::kToolbarContainerEnabled)},
-    {"present-sad-tab-in-view-controller",
-     flag_descriptions::kPresentSadTabInViewControllerName,
-     flag_descriptions::kPresentSadTabInViewControllerDescription,
-     flags_ui::kOsIos, FEATURE_VALUE_TYPE(kPresentSadTabInViewController)},
     {"omnibox-popup-shortcuts",
      flag_descriptions::kOmniboxPopupShortcutIconsInZeroStateName,
      flag_descriptions::kOmniboxPopupShortcutIconsInZeroStateDescription,
diff --git a/ios/chrome/browser/ios_chrome_flag_descriptions.cc b/ios/chrome/browser/ios_chrome_flag_descriptions.cc
index 94db1825..f3b6e07 100644
--- a/ios/chrome/browser/ios_chrome_flag_descriptions.cc
+++ b/ios/chrome/browser/ios_chrome_flag_descriptions.cc
@@ -387,12 +387,6 @@
     "When enabled, the toolbars and their fullscreen animations will be "
     "managed by the toolbar container coordinator rather than BVC.";
 
-const char kPresentSadTabInViewControllerName[] =
-    "Present SadTab in UIViewController";
-const char kPresentSadTabInViewControllerDescription[] =
-    "When enabled, SadTab UI will be presented with UIViewController instead "
-    "of using WebState::ShowTransientView";
-
 const char kSnapshotDrawViewName[] = "Use DrawViewHierarchy for Snapshots";
 const char kSnapshotDrawViewDescription[] =
     "When enabled, snapshots will be taken using |-drawViewHierarchy:|.";
diff --git a/ios/chrome/browser/ios_chrome_flag_descriptions.h b/ios/chrome/browser/ios_chrome_flag_descriptions.h
index 61012e6..0b478f6 100644
--- a/ios/chrome/browser/ios_chrome_flag_descriptions.h
+++ b/ios/chrome/browser/ios_chrome_flag_descriptions.h
@@ -316,11 +316,6 @@
 extern const char kToolbarContainerName[];
 extern const char kToolbarContainerDescription[];
 
-// Title and description for the flag to use UIViewController for Sad Tab UI
-// presentation.
-extern const char kPresentSadTabInViewControllerName[];
-extern const char kPresentSadTabInViewControllerDescription[];
-
 // Title and description for the flag to use |-drawViewHierarchy:| for taking
 // snapshots.
 extern const char kSnapshotDrawViewName[];
diff --git a/ios/chrome/browser/tabs/tab_model_unittest.mm b/ios/chrome/browser/tabs/tab_model_unittest.mm
index caa740ae..f70cc2f 100644
--- a/ios/chrome/browser/tabs/tab_model_unittest.mm
+++ b/ios/chrome/browser/tabs/tab_model_unittest.mm
@@ -701,7 +701,7 @@
   EXPECT_NSNE(nil, LegacyTabHelper::GetTabForWebState(web_state_ptr));
 }
 
-TEST_P(TabModelTest, PersistSelectionChange) {
+TEST_P(TabModelTest, DISABLED_PersistSelectionChange) {
   NSString* stashPath =
       base::SysUTF8ToNSString(chrome_browser_state_->GetStatePath().value());
 
diff --git a/ios/chrome/browser/ui/BUILD.gn b/ios/chrome/browser/ui/BUILD.gn
index 0336e5fa..096f975 100644
--- a/ios/chrome/browser/ui/BUILD.gn
+++ b/ios/chrome/browser/ui/BUILD.gn
@@ -345,7 +345,6 @@
     "//ios/chrome/browser/ui/reading_list",
     "//ios/chrome/browser/ui/sad_tab",
     "//ios/chrome/browser/ui/sad_tab:coordinator",
-    "//ios/chrome/browser/ui/sad_tab:features",
     "//ios/chrome/browser/ui/settings/sync_utils",
     "//ios/chrome/browser/ui/snackbar",
     "//ios/chrome/browser/ui/static_content",
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/BUILD.gn b/ios/chrome/browser/ui/autofill/manual_fill/BUILD.gn
index 39b03e7b..0ca6cdd 100644
--- a/ios/chrome/browser/ui/autofill/manual_fill/BUILD.gn
+++ b/ios/chrome/browser/ui/autofill/manual_fill/BUILD.gn
@@ -205,6 +205,8 @@
     "//ios/chrome/browser/autofill",
     "//ios/chrome/browser/passwords",
     "//ios/chrome/browser/ui/settings",
+    "//ios/chrome/browser/ui/settings/autofill",
+    "//ios/chrome/browser/ui/settings/password",
     "//ios/chrome/browser/ui/util",
     "//ios/chrome/test/app:test_support",
     "//ios/chrome/test/earl_grey:test_support",
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/card_view_controller_egtest.mm b/ios/chrome/browser/ui/autofill/manual_fill/card_view_controller_egtest.mm
index 7f2f7044..4c7732c 100644
--- a/ios/chrome/browser/ui/autofill/manual_fill/card_view_controller_egtest.mm
+++ b/ios/chrome/browser/ui/autofill/manual_fill/card_view_controller_egtest.mm
@@ -19,7 +19,7 @@
 #import "ios/chrome/browser/ui/autofill/manual_fill/card_mediator.h"
 #import "ios/chrome/browser/ui/autofill/manual_fill/card_view_controller.h"
 #import "ios/chrome/browser/ui/autofill/manual_fill/manual_fill_accessory_view_controller.h"
-#import "ios/chrome/browser/ui/settings/autofill_credit_card_table_view_controller.h"
+#import "ios/chrome/browser/ui/settings/autofill/autofill_credit_card_table_view_controller.h"
 #import "ios/chrome/browser/ui/util/ui_util.h"
 #import "ios/chrome/test/app/chrome_test_util.h"
 #import "ios/chrome/test/earl_grey/chrome_actions.h"
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/password_view_controller_egtest.mm b/ios/chrome/browser/ui/autofill/manual_fill/password_view_controller_egtest.mm
index 400c3f4..94177ab 100644
--- a/ios/chrome/browser/ui/autofill/manual_fill/password_view_controller_egtest.mm
+++ b/ios/chrome/browser/ui/autofill/manual_fill/password_view_controller_egtest.mm
@@ -20,7 +20,7 @@
 #import "ios/chrome/browser/ui/autofill/manual_fill/password_coordinator.h"
 #import "ios/chrome/browser/ui/autofill/manual_fill/password_mediator.h"
 #import "ios/chrome/browser/ui/autofill/manual_fill/password_view_controller.h"
-#import "ios/chrome/browser/ui/settings/passwords_table_view_controller.h"
+#import "ios/chrome/browser/ui/settings/password/passwords_table_view_controller.h"
 #import "ios/chrome/browser/ui/util/ui_util.h"
 #import "ios/chrome/test/app/chrome_test_util.h"
 #import "ios/chrome/test/earl_grey/chrome_actions.h"
diff --git a/ios/chrome/browser/ui/browser_view_controller.mm b/ios/chrome/browser/ui/browser_view_controller.mm
index 213b8db..6b9978d 100644
--- a/ios/chrome/browser/ui/browser_view_controller.mm
+++ b/ios/chrome/browser/ui/browser_view_controller.mm
@@ -124,9 +124,7 @@
 #import "ios/chrome/browser/ui/popup_menu/popup_menu_coordinator.h"
 #import "ios/chrome/browser/ui/presenters/vertical_animation_container.h"
 #import "ios/chrome/browser/ui/reading_list/offline_page_native_content.h"
-#include "ios/chrome/browser/ui/sad_tab/features.h"
 #import "ios/chrome/browser/ui/sad_tab/sad_tab_coordinator.h"
-#import "ios/chrome/browser/ui/sad_tab/sad_tab_legacy_coordinator.h"
 #import "ios/chrome/browser/ui/side_swipe/side_swipe_controller.h"
 #import "ios/chrome/browser/ui/side_swipe/swipe_view.h"
 #import "ios/chrome/browser/ui/static_content/static_html_native_content.h"
@@ -456,7 +454,7 @@
   AlertCoordinator* _alertCoordinator;
 
   // Coordinator for displaying Sad Tab.
-  id<SadTabTabHelperDelegate> _sadTabCoordinator;
+  SadTabCoordinator* _sadTabCoordinator;
 
   ToolbarCoordinatorAdaptor* _toolbarCoordinatorAdaptor;
 
@@ -2176,20 +2174,11 @@
       self.popupMenuCoordinator;
   self.tabStripCoordinator.longPressDelegate = self.popupMenuCoordinator;
 
-  if (base::FeatureList::IsEnabled(kPresentSadTabInViewController)) {
-    SadTabCoordinator* sadTabCoordinator = [[SadTabCoordinator alloc]
-        initWithBaseViewController:self.browserContainerViewController
-                      browserState:_browserState];
-    sadTabCoordinator.dispatcher = self.dispatcher;
-    sadTabCoordinator.overscrollDelegate = self;
-    _sadTabCoordinator = sadTabCoordinator;
-  } else {
-    SadTabLegacyCoordinator* sadTabCoordinator =
-        [[SadTabLegacyCoordinator alloc] init];
-    sadTabCoordinator.baseViewController = self;
-    sadTabCoordinator.dispatcher = self.dispatcher;
-    _sadTabCoordinator = sadTabCoordinator;
-  }
+  _sadTabCoordinator = [[SadTabCoordinator alloc]
+      initWithBaseViewController:self.browserContainerViewController
+                    browserState:_browserState];
+  _sadTabCoordinator.dispatcher = self.dispatcher;
+  _sadTabCoordinator.overscrollDelegate = self;
 
   // If there are any existing SadTabHelpers in |self.tabModel|, update the
   // helpers delegate with the new |_sadTabCoordinator|.
@@ -2932,17 +2921,12 @@
     [overlays addObject:downloadManagerOverlay];
   }
 
-  if (base::FeatureList::IsEnabled(kPresentSadTabInViewController)) {
-    UIViewController* viewController =
-        [base::mac::ObjCCastStrict<SadTabCoordinator>(_sadTabCoordinator)
-            viewController];
-    UIView* sadTabView = viewController.view;
-    if (sadTabView) {
-      SnapshotOverlay* sadTabOverlay =
-          [[SnapshotOverlay alloc] initWithView:sadTabView
-                                        yOffset:self.headerHeight];
-      [overlays addObject:sadTabOverlay];
-    }
+  UIView* sadTabView = _sadTabCoordinator.viewController.view;
+  if (sadTabView) {
+    SnapshotOverlay* sadTabOverlay =
+        [[SnapshotOverlay alloc] initWithView:sadTabView
+                                      yOffset:self.headerHeight];
+    [overlays addObject:sadTabOverlay];
   }
 
   return overlays;
diff --git a/ios/chrome/browser/ui/first_run/BUILD.gn b/ios/chrome/browser/ui/first_run/BUILD.gn
index f2ad62e3..3671ff1 100644
--- a/ios/chrome/browser/ui/first_run/BUILD.gn
+++ b/ios/chrome/browser/ui/first_run/BUILD.gn
@@ -41,6 +41,7 @@
     "//ios/chrome/browser/ui/promos",
     "//ios/chrome/browser/ui/settings",
     "//ios/chrome/browser/ui/settings/sync_utils",
+    "//ios/chrome/browser/ui/settings/utils",
     "//ios/chrome/browser/ui/util",
     "//ios/chrome/common",
     "//ios/public/provider/chrome/browser",
diff --git a/ios/chrome/browser/ui/first_run/first_run_util.mm b/ios/chrome/browser/ui/first_run/first_run_util.mm
index 236dabc..b35b5bca6 100644
--- a/ios/chrome/browser/ui/first_run/first_run_util.mm
+++ b/ios/chrome/browser/ui/first_run/first_run_util.mm
@@ -19,8 +19,8 @@
 #include "ios/chrome/browser/signin/identity_manager_factory.h"
 #include "ios/chrome/browser/tabs/tab.h"
 #include "ios/chrome/browser/ui/first_run/first_run_histograms.h"
-#import "ios/chrome/browser/ui/settings/settings_utils.h"
 #import "ios/chrome/browser/ui/settings/sync_utils/sync_util.h"
+#import "ios/chrome/browser/ui/settings/utils/settings_utils.h"
 #include "ios/chrome/browser/ui/util/ui_util.h"
 #include "ios/web/public/web_thread.h"
 #include "services/identity/public/cpp/identity_manager.h"
diff --git a/ios/chrome/browser/ui/payments/BUILD.gn b/ios/chrome/browser/ui/payments/BUILD.gn
index 4e901cc..4d39046 100644
--- a/ios/chrome/browser/ui/payments/BUILD.gn
+++ b/ios/chrome/browser/ui/payments/BUILD.gn
@@ -294,6 +294,7 @@
     "//ios/chrome/browser/ui/payments/cells",
     "//ios/chrome/browser/ui/popup_menu:constants",
     "//ios/chrome/browser/ui/settings",
+    "//ios/chrome/browser/ui/settings/autofill",
     "//ios/chrome/test/app:test_support",
     "//ios/chrome/test/earl_grey:test_support",
     "//ios/testing/earl_grey:earl_grey_support",
diff --git a/ios/chrome/browser/ui/payments/payment_request_cancel_pay_abort_egtest.mm b/ios/chrome/browser/ui/payments/payment_request_cancel_pay_abort_egtest.mm
index 8075860..c0c6ad37 100644
--- a/ios/chrome/browser/ui/payments/payment_request_cancel_pay_abort_egtest.mm
+++ b/ios/chrome/browser/ui/payments/payment_request_cancel_pay_abort_egtest.mm
@@ -11,7 +11,7 @@
 #include "components/strings/grit/components_strings.h"
 #include "ios/chrome/browser/ui/autofill/card_unmask_prompt_view_bridge.h"
 #import "ios/chrome/browser/ui/payments/payment_request_egtest_base.h"
-#import "ios/chrome/browser/ui/settings/autofill_profile_table_view_controller.h"
+#import "ios/chrome/browser/ui/settings/autofill/autofill_profile_table_view_controller.h"
 #import "ios/chrome/test/app/chrome_test_util.h"
 #import "ios/chrome/test/earl_grey/chrome_earl_grey.h"
 #import "ios/chrome/test/earl_grey/chrome_matchers.h"
diff --git a/ios/chrome/browser/ui/payments/payment_request_manager.mm b/ios/chrome/browser/ui/payments/payment_request_manager.mm
index 94e4553..b24dfeac 100644
--- a/ios/chrome/browser/ui/payments/payment_request_manager.mm
+++ b/ios/chrome/browser/ui/payments/payment_request_manager.mm
@@ -761,13 +761,14 @@
       IOSCanMakePaymentQueryFactory::GetForBrowserState(
           _browserState->GetOriginalChromeBrowserState());
   DCHECK(canMakePaymentQuery);
-  // iOS PaymentRequest does not support iframes.
+  // iOS PaymentRequest does not support iframes or origin trials.
   if (canMakePaymentQuery->CanQuery(
           GURL(url_formatter::FormatUrlForSecurityDisplay(
               _activeWebState->GetLastCommittedURL())),
           GURL(url_formatter::FormatUrlForSecurityDisplay(
               _activeWebState->GetLastCommittedURL())),
-          paymentRequest->stringified_method_data())) {
+          paymentRequest->stringified_method_data(),
+          /*per_method_quota=*/false)) {
     // canMakePayment should return false if user has not allowed canMakePayment
     // to return a truthful value.
     canMakePayment &=
diff --git a/ios/chrome/browser/ui/sad_tab/BUILD.gn b/ios/chrome/browser/ui/sad_tab/BUILD.gn
index 0290357..6193e4d 100644
--- a/ios/chrome/browser/ui/sad_tab/BUILD.gn
+++ b/ios/chrome/browser/ui/sad_tab/BUILD.gn
@@ -11,7 +11,6 @@
     "sad_tab_view_controller.mm",
   ]
   deps = [
-    ":features",
     "//base",
     "//components/resources",
     "//components/strings",
@@ -32,23 +31,11 @@
   libs = [ "UIKit.framework" ]
 }
 
-source_set("features") {
-  sources = [
-    "features.cc",
-    "features.h",
-  ]
-  deps = [
-    "//base",
-  ]
-}
-
 source_set("coordinator") {
   configs += [ "//build/config/compiler:enable_arc" ]
   sources = [
     "sad_tab_coordinator.h",
     "sad_tab_coordinator.mm",
-    "sad_tab_legacy_coordinator.h",
-    "sad_tab_legacy_coordinator.mm",
   ]
   deps = [
     ":sad_tab",
diff --git a/ios/chrome/browser/ui/sad_tab/features.cc b/ios/chrome/browser/ui/sad_tab/features.cc
deleted file mode 100644
index ac5cc11c..0000000
--- a/ios/chrome/browser/ui/sad_tab/features.cc
+++ /dev/null
@@ -1,8 +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.
-
-#include "ios/chrome/browser/ui/sad_tab/features.h"
-
-const base::Feature kPresentSadTabInViewController{
-    "PresentSadTabInViewController", base::FEATURE_ENABLED_BY_DEFAULT};
diff --git a/ios/chrome/browser/ui/sad_tab/features.h b/ios/chrome/browser/ui/sad_tab/features.h
deleted file mode 100644
index d3365dd..0000000
--- a/ios/chrome/browser/ui/sad_tab/features.h
+++ /dev/null
@@ -1,13 +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.
-
-#ifndef IOS_CHROME_BROWSER_UI_SAD_TAB_FEATURES_H_
-#define IOS_CHROME_BROWSER_UI_SAD_TAB_FEATURES_H_
-
-#include "base/feature_list.h"
-
-// Feature flag to enable SadTab UI presentation with UIViewController.
-extern const base::Feature kPresentSadTabInViewController;
-
-#endif  // IOS_CHROME_BROWSER_UI_SAD_TAB_FEATURES_H_
diff --git a/ios/chrome/browser/ui/sad_tab/sad_tab_legacy_coordinator.h b/ios/chrome/browser/ui/sad_tab/sad_tab_legacy_coordinator.h
deleted file mode 100644
index 033b33e2..0000000
--- a/ios/chrome/browser/ui/sad_tab/sad_tab_legacy_coordinator.h
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_UI_SAD_TAB_SAD_TAB_LEGACY_COORDINATOR_H_
-#define IOS_CHROME_BROWSER_UI_SAD_TAB_SAD_TAB_LEGACY_COORDINATOR_H_
-
-#import <UIKit/UIKit.h>
-
-#import "ios/chrome/browser/web/sad_tab_tab_helper_delegate.h"
-
-@protocol ApplicationCommands;
-@protocol BrowserCommands;
-
-// Coordinator that displays a SadTab view.
-@interface SadTabLegacyCoordinator : NSObject<SadTabTabHelperDelegate>
-
-// The view controller from which to present VCs.
-@property(nonatomic, weak) UIViewController* baseViewController;
-
-// The dispatcher for this Coordinator.
-@property(nonatomic, weak) id<ApplicationCommands, BrowserCommands> dispatcher;
-
-@end
-
-#endif  // IOS_CHROME_BROWSER_UI_SAD_TAB_SAD_TAB_LEGACY_COORDINATOR_H_
diff --git a/ios/chrome/browser/ui/sad_tab/sad_tab_legacy_coordinator.mm b/ios/chrome/browser/ui/sad_tab/sad_tab_legacy_coordinator.mm
deleted file mode 100644
index c2f6e284..0000000
--- a/ios/chrome/browser/ui/sad_tab/sad_tab_legacy_coordinator.mm
+++ /dev/null
@@ -1,69 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/chrome/browser/ui/sad_tab/sad_tab_legacy_coordinator.h"
-
-#import "ios/chrome/browser/ui/commands/application_commands.h"
-#import "ios/chrome/browser/ui/commands/browser_commands.h"
-#import "ios/chrome/browser/ui/commands/open_new_tab_command.h"
-#import "ios/chrome/browser/ui/sad_tab/sad_tab_view.h"
-#import "ios/chrome/browser/web/sad_tab_tab_helper.h"
-#include "ios/web/public/browser_state.h"
-#import "ios/web/public/navigation_manager.h"
-#import "ios/web/public/web_state/ui/crw_generic_content_view.h"
-#import "ios/web/public/web_state/web_state.h"
-
-@interface SadTabLegacyCoordinator ()<SadTabViewDelegate>
-@end
-
-@implementation SadTabLegacyCoordinator
-@synthesize baseViewController = _baseViewController;
-@synthesize dispatcher = _dispatcher;
-
-#pragma mark - SadTabViewDelegate
-
-- (void)sadTabViewShowReportAnIssue:(SadTabView*)sadTabView {
-  [self.dispatcher showReportAnIssueFromViewController:self.baseViewController];
-}
-
-- (void)sadTabView:(SadTabView*)sadTabView
-    showSuggestionsPageWithURL:(const GURL&)URL {
-  OpenNewTabCommand* command = [OpenNewTabCommand commandWithURLFromChrome:URL];
-  [self.dispatcher openURLInNewTab:command];
-}
-
-- (void)sadTabViewReload:(SadTabView*)sadTabView {
-  [self.dispatcher reload];
-}
-
-#pragma mark - SadTabTabHelperDelegate
-
-- (void)sadTabTabHelper:(SadTabTabHelper*)tabHelper
-    presentSadTabForWebState:(web::WebState*)webState
-             repeatedFailure:(BOOL)repeatedFailure {
-  SadTabViewMode mode =
-      repeatedFailure ? SadTabViewMode::FEEDBACK : SadTabViewMode::RELOAD;
-  SadTabView* sadTabView = [[SadTabView alloc]
-      initWithMode:mode
-      offTheRecord:webState->GetBrowserState()->IsOffTheRecord()];
-  sadTabView.delegate = self;
-  CRWContentView* contentView =
-      [[CRWGenericContentView alloc] initWithView:sadTabView];
-  webState->ShowTransientContentView(contentView);
-}
-
-- (void)sadTabTabHelperDismissSadTab:(SadTabTabHelper*)tabHelper {
-  // Transient Content View is dismissed automatically.
-}
-
-- (void)sadTabTabHelper:(SadTabTabHelper*)tabHelper
-    didShowForRepeatedFailure:(BOOL)repeatedFailure {
-  // No-op. Transient content view was not removed when Tab was hidden.
-}
-
-- (void)sadTabTabHelperDidHide:(SadTabTabHelper*)tabHelper {
-  // No-op. Transient content view should not be removed when Tab is hidden.
-}
-
-@end
diff --git a/ios/chrome/browser/ui/settings/BUILD.gn b/ios/chrome/browser/ui/settings/BUILD.gn
index 60df24e..9df8e1c2 100644
--- a/ios/chrome/browser/ui/settings/BUILD.gn
+++ b/ios/chrome/browser/ui/settings/BUILD.gn
@@ -43,17 +43,6 @@
     "about_chrome_table_view_controller.mm",
     "accounts_table_view_controller.h",
     "accounts_table_view_controller.mm",
-    "autofill_credit_card_edit_table_view_controller.h",
-    "autofill_credit_card_edit_table_view_controller.mm",
-    "autofill_credit_card_table_view_controller.h",
-    "autofill_credit_card_table_view_controller.mm",
-    "autofill_edit_table_view_controller+protected.h",
-    "autofill_edit_table_view_controller.h",
-    "autofill_edit_table_view_controller.mm",
-    "autofill_profile_edit_table_view_controller.h",
-    "autofill_profile_edit_table_view_controller.mm",
-    "autofill_profile_table_view_controller.h",
-    "autofill_profile_table_view_controller.mm",
     "bandwidth_management_table_view_controller.h",
     "bandwidth_management_table_view_controller.mm",
     "block_popups_table_view_controller.h",
@@ -88,25 +77,13 @@
     "manage_sync_settings_view_controller_model_delegate.h",
     "material_cell_catalog_view_controller.h",
     "material_cell_catalog_view_controller.mm",
-    "password_details_table_view_controller.h",
-    "password_details_table_view_controller.mm",
-    "password_details_table_view_controller_delegate.h",
-    "password_exporter.h",
-    "password_exporter.mm",
-    "passwords_table_view_controller.h",
-    "passwords_table_view_controller.mm",
     "privacy_table_view_controller.h",
     "privacy_table_view_controller.mm",
-    "reauthentication_module.h",
-    "reauthentication_module.mm",
-    "reauthentication_protocol.h",
     "search_engine_table_view_controller.h",
     "search_engine_table_view_controller.mm",
     "settings_navigation_controller.mm",
     "settings_table_view_controller.h",
     "settings_table_view_controller.mm",
-    "settings_utils.h",
-    "settings_utils.mm",
     "sync_create_passphrase_table_view_controller.h",
     "sync_create_passphrase_table_view_controller.mm",
     "sync_encryption_passphrase_table_view_controller.h",
@@ -145,7 +122,6 @@
     "//base:i18n",
     "//components/autofill/core/browser",
     "//components/autofill/core/common",
-    "//components/autofill/ios/browser",
     "//components/browser_sync",
     "//components/browsing_data/core",
     "//components/content_settings/core/browser",
@@ -173,8 +149,6 @@
     "//components/version_info",
     "//ios/chrome/app/strings",
     "//ios/chrome/browser",
-    "//ios/chrome/browser/autofill",
-    "//ios/chrome/browser/autofill:autofill_internal",
     "//ios/chrome/browser/browser_state",
     "//ios/chrome/browser/browser_state:browser_state_impl",
     "//ios/chrome/browser/browsing_data",
@@ -192,8 +166,6 @@
     "//ios/chrome/browser/ui/alert_coordinator",
     "//ios/chrome/browser/ui/authentication",
     "//ios/chrome/browser/ui/authentication/cells",
-    "//ios/chrome/browser/ui/autofill",
-    "//ios/chrome/browser/ui/autofill:autofill_ui",
     "//ios/chrome/browser/ui/autofill/cells",
     "//ios/chrome/browser/ui/collection_view",
     "//ios/chrome/browser/ui/colors",
@@ -207,8 +179,10 @@
     "//ios/chrome/browser/ui/list_model",
     "//ios/chrome/browser/ui/material_components",
     "//ios/chrome/browser/ui/payments/cells",
+    "//ios/chrome/browser/ui/settings/autofill",
     "//ios/chrome/browser/ui/settings/cells",
     "//ios/chrome/browser/ui/settings/clear_browsing_data",
+    "//ios/chrome/browser/ui/settings/password",
     "//ios/chrome/browser/ui/settings/sync_utils",
     "//ios/chrome/browser/ui/settings/utils",
     "//ios/chrome/browser/ui/signin_interaction",
@@ -252,11 +226,8 @@
   sources = [
     "passphrase_table_view_controller_test.h",
     "passphrase_table_view_controller_test.mm",
-    "password_details_table_view_controller+testing.h",
-    "password_exporter_for_testing.h",
     "personal_data_manager_finished_profile_tasks_waiter.cc",
     "personal_data_manager_finished_profile_tasks_waiter.h",
-    "reauthentication_module_for_testing.h",
   ]
   deps = [
     ":settings",
@@ -278,6 +249,7 @@
     "//ios/chrome/browser/signin:test_support",
     "//ios/chrome/browser/sync",
     "//ios/chrome/browser/sync:test_support",
+    "//ios/chrome/browser/ui/settings/password",
     "//ios/chrome/browser/ui/table_view:test_support",
     "//ios/public/provider/chrome/browser/signin:test_support",
     "//ios/web/public/test",
@@ -290,19 +262,12 @@
   testonly = true
   sources = [
     "about_chrome_table_view_controller_unittest.mm",
-    "autofill_credit_card_table_view_controller_unittest.mm",
-    "autofill_profile_edit_table_view_controller_unittest.mm",
-    "autofill_profile_table_view_controller_unittest.mm",
     "bandwidth_management_table_view_controller_unittest.mm",
     "block_popups_table_view_controller_unittest.mm",
     "content_settings_table_view_controller_unittest.mm",
     "dataplan_usage_table_view_controller_unittest.mm",
     "import_data_table_view_controller_unittest.mm",
-    "password_details_table_view_controller_unittest.mm",
-    "password_exporter_unittest.mm",
-    "passwords_table_view_controller_unittest.mm",
     "privacy_table_view_controller_unittest.mm",
-    "reauthentication_module_unittest.mm",
     "search_engine_table_view_controller_unittest.mm",
     "settings_navigation_controller_unittest.mm",
     "settings_root_collection_view_controller_unittest.mm",
@@ -320,7 +285,6 @@
     ":test_support",
     "//base",
     "//base/test:test_support",
-    "//components/autofill/core/browser",
     "//components/autofill/core/common",
     "//components/browser_sync:test_support",
     "//components/content_settings/core/browser",
@@ -340,8 +304,6 @@
     "//components/unified_consent",
     "//ios/chrome/app/strings",
     "//ios/chrome/browser",
-    "//ios/chrome/browser/autofill",
-    "//ios/chrome/browser/autofill:autofill_internal",
     "//ios/chrome/browser/browser_state",
     "//ios/chrome/browser/browser_state:test_support",
     "//ios/chrome/browser/browsing_data",
@@ -391,10 +353,7 @@
   testonly = true
   sources = [
     "accounts_table_egtest.mm",
-    "autofill_credit_card_settings_egtest.mm",
-    "autofill_profile_settings_egtest.mm",
     "block_popups_egtest.mm",
-    "passwords_settings_egtest.mm",
     "settings_egtest.mm",
     "signin_settings_egtest.mm",
     "translate_ui_egtest.mm",
@@ -404,8 +363,6 @@
     ":settings",
     "//base",
     "//base/test:test_support",
-    "//components/autofill/core/browser",
-    "//components/autofill/core/browser:test_support",
     "//components/autofill/core/common",
     "//components/browser_sync",
     "//components/browsing_data/core",
@@ -421,7 +378,6 @@
     "//ios/chrome/app/strings",
     "//ios/chrome/app/theme",
     "//ios/chrome/browser",
-    "//ios/chrome/browser/autofill",
     "//ios/chrome/browser/browser_state",
     "//ios/chrome/browser/browsing_data:feature_flags",
     "//ios/chrome/browser/content_settings",
diff --git a/ios/chrome/browser/ui/settings/about_chrome_table_view_controller.mm b/ios/chrome/browser/ui/settings/about_chrome_table_view_controller.mm
index 023ff7c..7a092825 100644
--- a/ios/chrome/browser/ui/settings/about_chrome_table_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/about_chrome_table_view_controller.mm
@@ -12,7 +12,7 @@
 #include "components/version_info/version_info.h"
 #include "ios/chrome/browser/chrome_url_constants.h"
 #import "ios/chrome/browser/ui/settings/cells/version_item.h"
-#import "ios/chrome/browser/ui/settings/settings_utils.h"
+#import "ios/chrome/browser/ui/settings/utils/settings_utils.h"
 #import "ios/chrome/browser/ui/table_view/cells/table_view_detail_text_item.h"
 #import "ios/chrome/browser/ui/table_view/chrome_table_view_styler.h"
 #include "ios/chrome/browser/ui/ui_feature_flags.h"
diff --git a/ios/chrome/browser/ui/settings/autofill/BUILD.gn b/ios/chrome/browser/ui/settings/autofill/BUILD.gn
new file mode 100644
index 0000000..d838673
--- /dev/null
+++ b/ios/chrome/browser/ui/settings/autofill/BUILD.gn
@@ -0,0 +1,88 @@
+# 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.
+
+source_set("autofill") {
+  configs += [ "//build/config/compiler:enable_arc" ]
+  sources = [
+    "autofill_credit_card_edit_table_view_controller.h",
+    "autofill_credit_card_edit_table_view_controller.mm",
+    "autofill_credit_card_table_view_controller.h",
+    "autofill_credit_card_table_view_controller.mm",
+    "autofill_edit_table_view_controller+protected.h",
+    "autofill_edit_table_view_controller.h",
+    "autofill_edit_table_view_controller.mm",
+    "autofill_profile_edit_table_view_controller.h",
+    "autofill_profile_edit_table_view_controller.mm",
+    "autofill_profile_table_view_controller.h",
+    "autofill_profile_table_view_controller.mm",
+  ]
+  deps = [
+    "//components/autofill/core/browser",
+    "//components/autofill/core/common",
+    "//components/autofill/ios/browser",
+    "//components/prefs",
+    "//components/strings",
+    "//ios/chrome/app/strings",
+    "//ios/chrome/browser",
+    "//ios/chrome/browser/autofill",
+    "//ios/chrome/browser/browser_state",
+    "//ios/chrome/browser/ui:feature_flags",
+    "//ios/chrome/browser/ui/autofill",
+    "//ios/chrome/browser/ui/autofill:autofill_ui",
+    "//ios/chrome/browser/ui/autofill/cells",
+    "//ios/chrome/browser/ui/commands",
+    "//ios/chrome/browser/ui/settings:settings_root",
+    "//ios/chrome/browser/ui/settings/cells",
+    "//ios/chrome/browser/ui/table_view",
+    "//ios/chrome/browser/ui/table_view/cells",
+    "//ios/chrome/browser/ui/util",
+    "//ui/base",
+  ]
+}
+
+source_set("unit_tests") {
+  configs += [ "//build/config/compiler:enable_arc" ]
+  testonly = true
+  sources = [
+    "autofill_credit_card_table_view_controller_unittest.mm",
+    "autofill_profile_edit_table_view_controller_unittest.mm",
+    "autofill_profile_table_view_controller_unittest.mm",
+  ]
+  deps = [
+    ":autofill",
+    "//base/test:test_support",
+    "//components/autofill/core/browser",
+    "//components/strings",
+    "//ios/chrome/browser",
+    "//ios/chrome/browser/autofill",
+    "//ios/chrome/browser/browser_state",
+    "//ios/chrome/browser/browser_state:test_support",
+    "//ios/chrome/browser/ui/settings:settings_root",
+    "//ios/chrome/browser/ui/settings:test_support",
+    "//ios/chrome/browser/ui/table_view",
+    "//ios/chrome/browser/ui/table_view:test_support",
+    "//ios/web/public/test",
+    "//testing/gtest",
+  ]
+}
+
+source_set("eg_tests") {
+  configs += [ "//build/config/compiler:enable_arc" ]
+  testonly = true
+  sources = [
+    "autofill_credit_card_settings_egtest.mm",
+    "autofill_profile_settings_egtest.mm",
+  ]
+  deps = [
+    ":autofill",
+    "//components/autofill/core/browser",
+    "//components/autofill/core/browser:test_support",
+    "//components/strings",
+    "//ios/chrome/app/strings",
+    "//ios/chrome/browser/autofill",
+    "//ios/chrome/test/app:test_support",
+    "//ios/chrome/test/earl_grey:test_support",
+    "//ui/base",
+  ]
+}
diff --git a/ios/chrome/browser/ui/settings/autofill_credit_card_edit_table_view_controller.h b/ios/chrome/browser/ui/settings/autofill/autofill_credit_card_edit_table_view_controller.h
similarity index 70%
rename from ios/chrome/browser/ui/settings/autofill_credit_card_edit_table_view_controller.h
rename to ios/chrome/browser/ui/settings/autofill/autofill_credit_card_edit_table_view_controller.h
index 7a89132..92d63f58 100644
--- a/ios/chrome/browser/ui/settings/autofill_credit_card_edit_table_view_controller.h
+++ b/ios/chrome/browser/ui/settings/autofill/autofill_credit_card_edit_table_view_controller.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 IOS_CHROME_BROWSER_UI_SETTINGS_AUTOFILL_CREDIT_CARD_EDIT_TABLE_VIEW_CONTROLLER_H_
-#define IOS_CHROME_BROWSER_UI_SETTINGS_AUTOFILL_CREDIT_CARD_EDIT_TABLE_VIEW_CONTROLLER_H_
+#ifndef IOS_CHROME_BROWSER_UI_SETTINGS_AUTOFILL_AUTOFILL_CREDIT_CARD_EDIT_TABLE_VIEW_CONTROLLER_H_
+#define IOS_CHROME_BROWSER_UI_SETTINGS_AUTOFILL_AUTOFILL_CREDIT_CARD_EDIT_TABLE_VIEW_CONTROLLER_H_
 
-#import "ios/chrome/browser/ui/settings/autofill_edit_table_view_controller.h"
+#import "ios/chrome/browser/ui/settings/autofill/autofill_edit_table_view_controller.h"
 
 namespace autofill {
 class CreditCard;
@@ -29,4 +29,4 @@
 
 @end
 
-#endif  // IOS_CHROME_BROWSER_UI_SETTINGS_AUTOFILL_CREDIT_CARD_EDIT_TABLE_VIEW_CONTROLLER_H_
+#endif  // IOS_CHROME_BROWSER_UI_SETTINGS_AUTOFILL_AUTOFILL_CREDIT_CARD_EDIT_TABLE_VIEW_CONTROLLER_H_
diff --git a/ios/chrome/browser/ui/settings/autofill_credit_card_edit_table_view_controller.mm b/ios/chrome/browser/ui/settings/autofill/autofill_credit_card_edit_table_view_controller.mm
similarity index 98%
rename from ios/chrome/browser/ui/settings/autofill_credit_card_edit_table_view_controller.mm
rename to ios/chrome/browser/ui/settings/autofill/autofill_credit_card_edit_table_view_controller.mm
index 56fe2897..ae91ca71 100644
--- a/ios/chrome/browser/ui/settings/autofill_credit_card_edit_table_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/autofill/autofill_credit_card_edit_table_view_controller.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/chrome/browser/ui/settings/autofill_credit_card_edit_table_view_controller.h"
+#import "ios/chrome/browser/ui/settings/autofill/autofill_credit_card_edit_table_view_controller.h"
 
 #include "base/format_macros.h"
 #import "base/ios/block_types.h"
@@ -22,7 +22,7 @@
 #import "ios/chrome/browser/ui/autofill/cells/autofill_edit_item.h"
 #import "ios/chrome/browser/ui/commands/application_commands.h"
 #import "ios/chrome/browser/ui/commands/open_new_tab_command.h"
-#import "ios/chrome/browser/ui/settings/autofill_edit_table_view_controller+protected.h"
+#import "ios/chrome/browser/ui/settings/autofill/autofill_edit_table_view_controller+protected.h"
 #import "ios/chrome/browser/ui/settings/cells/copied_to_chrome_item.h"
 #include "ios/chrome/browser/ui/ui_feature_flags.h"
 #import "ios/chrome/browser/ui/util/uikit_ui_util.h"
diff --git a/ios/chrome/browser/ui/settings/autofill_credit_card_settings_egtest.mm b/ios/chrome/browser/ui/settings/autofill/autofill_credit_card_settings_egtest.mm
similarity index 98%
rename from ios/chrome/browser/ui/settings/autofill_credit_card_settings_egtest.mm
rename to ios/chrome/browser/ui/settings/autofill/autofill_credit_card_settings_egtest.mm
index e78c20d..b8ea908 100644
--- a/ios/chrome/browser/ui/settings/autofill_credit_card_settings_egtest.mm
+++ b/ios/chrome/browser/ui/settings/autofill/autofill_credit_card_settings_egtest.mm
@@ -11,7 +11,7 @@
 #include "components/autofill/core/browser/personal_data_manager.h"
 #include "components/strings/grit/components_strings.h"
 #include "ios/chrome/browser/autofill/personal_data_manager_factory.h"
-#import "ios/chrome/browser/ui/settings/autofill_credit_card_table_view_controller.h"
+#import "ios/chrome/browser/ui/settings/autofill/autofill_credit_card_table_view_controller.h"
 #include "ios/chrome/grit/ios_strings.h"
 #import "ios/chrome/test/app/chrome_test_util.h"
 #import "ios/chrome/test/app/web_view_interaction_test_util.h"
diff --git a/ios/chrome/browser/ui/settings/autofill_credit_card_table_view_controller.h b/ios/chrome/browser/ui/settings/autofill/autofill_credit_card_table_view_controller.h
similarity index 76%
rename from ios/chrome/browser/ui/settings/autofill_credit_card_table_view_controller.h
rename to ios/chrome/browser/ui/settings/autofill/autofill_credit_card_table_view_controller.h
index e3fdf57..49dd8876 100644
--- a/ios/chrome/browser/ui/settings/autofill_credit_card_table_view_controller.h
+++ b/ios/chrome/browser/ui/settings/autofill/autofill_credit_card_table_view_controller.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 IOS_CHROME_BROWSER_UI_SETTINGS_AUTOFILL_CREDIT_CARD_TABLE_VIEW_CONTROLLER_H_
-#define IOS_CHROME_BROWSER_UI_SETTINGS_AUTOFILL_CREDIT_CARD_TABLE_VIEW_CONTROLLER_H_
+#ifndef IOS_CHROME_BROWSER_UI_SETTINGS_AUTOFILL_AUTOFILL_CREDIT_CARD_TABLE_VIEW_CONTROLLER_H_
+#define IOS_CHROME_BROWSER_UI_SETTINGS_AUTOFILL_AUTOFILL_CREDIT_CARD_TABLE_VIEW_CONTROLLER_H_
 
 #import "ios/chrome/browser/ui/settings/settings_root_table_view_controller.h"
 
@@ -28,4 +28,4 @@
 
 @end
 
-#endif  // IOS_CHROME_BROWSER_UI_SETTINGS_AUTOFILL_CREDIT_CARD_TABLE_VIEW_CONTROLLER_H_
+#endif  // IOS_CHROME_BROWSER_UI_SETTINGS_AUTOFILL_AUTOFILL_CREDIT_CARD_TABLE_VIEW_CONTROLLER_H_
diff --git a/ios/chrome/browser/ui/settings/autofill_credit_card_table_view_controller.mm b/ios/chrome/browser/ui/settings/autofill/autofill_credit_card_table_view_controller.mm
similarity index 98%
rename from ios/chrome/browser/ui/settings/autofill_credit_card_table_view_controller.mm
rename to ios/chrome/browser/ui/settings/autofill/autofill_credit_card_table_view_controller.mm
index 6144b3cd..6e7ba311 100644
--- a/ios/chrome/browser/ui/settings/autofill_credit_card_table_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/autofill/autofill_credit_card_table_view_controller.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/chrome/browser/ui/settings/autofill_credit_card_table_view_controller.h"
+#import "ios/chrome/browser/ui/settings/autofill/autofill_credit_card_table_view_controller.h"
 
 #include "base/logging.h"
 #include "base/mac/foundation_util.h"
@@ -17,7 +17,7 @@
 #include "ios/chrome/browser/application_context.h"
 #include "ios/chrome/browser/autofill/personal_data_manager_factory.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
-#import "ios/chrome/browser/ui/settings/autofill_credit_card_edit_table_view_controller.h"
+#import "ios/chrome/browser/ui/settings/autofill/autofill_credit_card_edit_table_view_controller.h"
 #import "ios/chrome/browser/ui/settings/cells/autofill_data_item.h"
 #import "ios/chrome/browser/ui/settings/cells/settings_switch_cell.h"
 #import "ios/chrome/browser/ui/settings/cells/settings_switch_item.h"
diff --git a/ios/chrome/browser/ui/settings/autofill_credit_card_table_view_controller_unittest.mm b/ios/chrome/browser/ui/settings/autofill/autofill_credit_card_table_view_controller_unittest.mm
similarity index 97%
rename from ios/chrome/browser/ui/settings/autofill_credit_card_table_view_controller_unittest.mm
rename to ios/chrome/browser/ui/settings/autofill/autofill_credit_card_table_view_controller_unittest.mm
index 9271822..a2ef5694 100644
--- a/ios/chrome/browser/ui/settings/autofill_credit_card_table_view_controller_unittest.mm
+++ b/ios/chrome/browser/ui/settings/autofill/autofill_credit_card_table_view_controller_unittest.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/chrome/browser/ui/settings/autofill_credit_card_table_view_controller.h"
+#import "ios/chrome/browser/ui/settings/autofill/autofill_credit_card_table_view_controller.h"
 
 #include "base/guid.h"
 #include "base/mac/foundation_util.h"
diff --git a/ios/chrome/browser/ui/settings/autofill/autofill_edit_table_view_controller+protected.h b/ios/chrome/browser/ui/settings/autofill/autofill_edit_table_view_controller+protected.h
new file mode 100644
index 0000000..e0188e4
--- /dev/null
+++ b/ios/chrome/browser/ui/settings/autofill/autofill_edit_table_view_controller+protected.h
@@ -0,0 +1,18 @@
+// 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.
+
+#ifndef IOS_CHROME_BROWSER_UI_SETTINGS_AUTOFILL_AUTOFILL_EDIT_TABLE_VIEW_CONTROLLER_PROTECTED_H_
+#define IOS_CHROME_BROWSER_UI_SETTINGS_AUTOFILL_AUTOFILL_EDIT_TABLE_VIEW_CONTROLLER_PROTECTED_H_
+
+#import "ios/chrome/browser/ui/settings/autofill/autofill_edit_table_view_controller.h"
+
+// The table view for an Autofill edit entry menu.
+@interface AutofillEditTableViewController (Protected)
+
+// Returns the indexPath for the currently focused text field when in edit mode.
+- (NSIndexPath*)indexPathForCurrentTextField;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_SETTINGS_AUTOFILL_AUTOFILL_EDIT_TABLE_VIEW_CONTROLLER_PROTECTED_H_
diff --git a/ios/chrome/browser/ui/settings/autofill_edit_table_view_controller.h b/ios/chrome/browser/ui/settings/autofill/autofill_edit_table_view_controller.h
similarity index 60%
rename from ios/chrome/browser/ui/settings/autofill_edit_table_view_controller.h
rename to ios/chrome/browser/ui/settings/autofill/autofill_edit_table_view_controller.h
index d607981..7c25317 100644
--- a/ios/chrome/browser/ui/settings/autofill_edit_table_view_controller.h
+++ b/ios/chrome/browser/ui/settings/autofill/autofill_edit_table_view_controller.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 IOS_CHROME_BROWSER_UI_SETTINGS_AUTOFILL_EDIT_TABLE_VIEW_CONTROLLER_H_
-#define IOS_CHROME_BROWSER_UI_SETTINGS_AUTOFILL_EDIT_TABLE_VIEW_CONTROLLER_H_
+#ifndef IOS_CHROME_BROWSER_UI_SETTINGS_AUTOFILL_AUTOFILL_EDIT_TABLE_VIEW_CONTROLLER_H_
+#define IOS_CHROME_BROWSER_UI_SETTINGS_AUTOFILL_AUTOFILL_EDIT_TABLE_VIEW_CONTROLLER_H_
 
 #import "ios/chrome/browser/ui/settings/settings_root_table_view_controller.h"
 
@@ -12,4 +12,4 @@
     : SettingsRootTableViewController <UITextFieldDelegate>
 @end
 
-#endif  // IOS_CHROME_BROWSER_UI_SETTINGS_AUTOFILL_EDIT_TABLE_VIEW_CONTROLLER_H_
+#endif  // IOS_CHROME_BROWSER_UI_SETTINGS_AUTOFILL_AUTOFILL_EDIT_TABLE_VIEW_CONTROLLER_H_
diff --git a/ios/chrome/browser/ui/settings/autofill_edit_table_view_controller.mm b/ios/chrome/browser/ui/settings/autofill/autofill_edit_table_view_controller.mm
similarity index 96%
rename from ios/chrome/browser/ui/settings/autofill_edit_table_view_controller.mm
rename to ios/chrome/browser/ui/settings/autofill/autofill_edit_table_view_controller.mm
index 227d20a..3a0ee7b 100644
--- a/ios/chrome/browser/ui/settings/autofill_edit_table_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/autofill/autofill_edit_table_view_controller.mm
@@ -2,13 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/chrome/browser/ui/settings/autofill_edit_table_view_controller.h"
+#import "ios/chrome/browser/ui/settings/autofill/autofill_edit_table_view_controller.h"
 
 #include "base/logging.h"
 #import "base/mac/foundation_util.h"
 #import "ios/chrome/browser/ui/autofill/autofill_edit_accessory_view.h"
 #import "ios/chrome/browser/ui/autofill/cells/autofill_edit_item.h"
-#import "ios/chrome/browser/ui/settings/autofill_edit_table_view_controller+protected.h"
+#import "ios/chrome/browser/ui/settings/autofill/autofill_edit_table_view_controller+protected.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
diff --git a/ios/chrome/browser/ui/settings/autofill_profile_edit_table_view_controller.h b/ios/chrome/browser/ui/settings/autofill/autofill_profile_edit_table_view_controller.h
similarity index 71%
rename from ios/chrome/browser/ui/settings/autofill_profile_edit_table_view_controller.h
rename to ios/chrome/browser/ui/settings/autofill/autofill_profile_edit_table_view_controller.h
index 79da7c5e..6a63d0f 100644
--- a/ios/chrome/browser/ui/settings/autofill_profile_edit_table_view_controller.h
+++ b/ios/chrome/browser/ui/settings/autofill/autofill_profile_edit_table_view_controller.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 IOS_CHROME_BROWSER_UI_SETTINGS_AUTOFILL_PROFILE_EDIT_TABLE_VIEW_CONTROLLER_H_
-#define IOS_CHROME_BROWSER_UI_SETTINGS_AUTOFILL_PROFILE_EDIT_TABLE_VIEW_CONTROLLER_H_
+#ifndef IOS_CHROME_BROWSER_UI_SETTINGS_AUTOFILL_AUTOFILL_PROFILE_EDIT_TABLE_VIEW_CONTROLLER_H_
+#define IOS_CHROME_BROWSER_UI_SETTINGS_AUTOFILL_AUTOFILL_PROFILE_EDIT_TABLE_VIEW_CONTROLLER_H_
 
-#import "ios/chrome/browser/ui/settings/autofill_edit_table_view_controller.h"
+#import "ios/chrome/browser/ui/settings/autofill/autofill_edit_table_view_controller.h"
 
 namespace autofill {
 class AutofillProfile;
@@ -30,4 +30,4 @@
 
 @end
 
-#endif  // IOS_CHROME_BROWSER_UI_SETTINGS_AUTOFILL_PROFILE_EDIT_TABLE_VIEW_CONTROLLER_H_
+#endif  // IOS_CHROME_BROWSER_UI_SETTINGS_AUTOFILL_AUTOFILL_PROFILE_EDIT_TABLE_VIEW_CONTROLLER_H_
diff --git a/ios/chrome/browser/ui/settings/autofill_profile_edit_table_view_controller.mm b/ios/chrome/browser/ui/settings/autofill/autofill_profile_edit_table_view_controller.mm
similarity index 98%
rename from ios/chrome/browser/ui/settings/autofill_profile_edit_table_view_controller.mm
rename to ios/chrome/browser/ui/settings/autofill/autofill_profile_edit_table_view_controller.mm
index 258e690..8bd19c2 100644
--- a/ios/chrome/browser/ui/settings/autofill_profile_edit_table_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/autofill/autofill_profile_edit_table_view_controller.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/chrome/browser/ui/settings/autofill_profile_edit_table_view_controller.h"
+#import "ios/chrome/browser/ui/settings/autofill/autofill_profile_edit_table_view_controller.h"
 
 #include "base/mac/foundation_util.h"
 #include "base/stl_util.h"
diff --git a/ios/chrome/browser/ui/settings/autofill_profile_edit_table_view_controller_unittest.mm b/ios/chrome/browser/ui/settings/autofill/autofill_profile_edit_table_view_controller_unittest.mm
similarity index 97%
rename from ios/chrome/browser/ui/settings/autofill_profile_edit_table_view_controller_unittest.mm
rename to ios/chrome/browser/ui/settings/autofill/autofill_profile_edit_table_view_controller_unittest.mm
index 90e0c5a..45951422 100644
--- a/ios/chrome/browser/ui/settings/autofill_profile_edit_table_view_controller_unittest.mm
+++ b/ios/chrome/browser/ui/settings/autofill/autofill_profile_edit_table_view_controller_unittest.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/chrome/browser/ui/settings/autofill_profile_edit_table_view_controller.h"
+#import "ios/chrome/browser/ui/settings/autofill/autofill_profile_edit_table_view_controller.h"
 
 #include <memory>
 
diff --git a/ios/chrome/browser/ui/settings/autofill_profile_settings_egtest.mm b/ios/chrome/browser/ui/settings/autofill/autofill_profile_settings_egtest.mm
similarity index 98%
rename from ios/chrome/browser/ui/settings/autofill_profile_settings_egtest.mm
rename to ios/chrome/browser/ui/settings/autofill/autofill_profile_settings_egtest.mm
index b016bf3..26687e51 100644
--- a/ios/chrome/browser/ui/settings/autofill_profile_settings_egtest.mm
+++ b/ios/chrome/browser/ui/settings/autofill/autofill_profile_settings_egtest.mm
@@ -10,7 +10,7 @@
 #include "components/autofill/core/browser/personal_data_manager.h"
 #include "components/strings/grit/components_strings.h"
 #include "ios/chrome/browser/autofill/personal_data_manager_factory.h"
-#import "ios/chrome/browser/ui/settings/autofill_profile_edit_table_view_controller.h"
+#import "ios/chrome/browser/ui/settings/autofill/autofill_profile_edit_table_view_controller.h"
 #include "ios/chrome/grit/ios_strings.h"
 #import "ios/chrome/test/app/chrome_test_util.h"
 #import "ios/chrome/test/app/web_view_interaction_test_util.h"
diff --git a/ios/chrome/browser/ui/settings/autofill_profile_table_view_controller.h b/ios/chrome/browser/ui/settings/autofill/autofill_profile_table_view_controller.h
similarity index 75%
rename from ios/chrome/browser/ui/settings/autofill_profile_table_view_controller.h
rename to ios/chrome/browser/ui/settings/autofill/autofill_profile_table_view_controller.h
index 51a174e..fe73dd0 100644
--- a/ios/chrome/browser/ui/settings/autofill_profile_table_view_controller.h
+++ b/ios/chrome/browser/ui/settings/autofill/autofill_profile_table_view_controller.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 IOS_CHROME_BROWSER_UI_SETTINGS_AUTOFILL_PROFILE_TABLE_VIEW_CONTROLLER_H_
-#define IOS_CHROME_BROWSER_UI_SETTINGS_AUTOFILL_PROFILE_TABLE_VIEW_CONTROLLER_H_
+#ifndef IOS_CHROME_BROWSER_UI_SETTINGS_AUTOFILL_AUTOFILL_PROFILE_TABLE_VIEW_CONTROLLER_H_
+#define IOS_CHROME_BROWSER_UI_SETTINGS_AUTOFILL_AUTOFILL_PROFILE_TABLE_VIEW_CONTROLLER_H_
 
 #import "ios/chrome/browser/ui/settings/settings_root_table_view_controller.h"
 
@@ -26,4 +26,4 @@
 
 @end
 
-#endif  // IOS_CHROME_BROWSER_UI_SETTINGS_AUTOFILL_PROFILE_TABLE_VIEW_CONTROLLER_H_
+#endif  // IOS_CHROME_BROWSER_UI_SETTINGS_AUTOFILL_AUTOFILL_PROFILE_TABLE_VIEW_CONTROLLER_H_
diff --git a/ios/chrome/browser/ui/settings/autofill_profile_table_view_controller.mm b/ios/chrome/browser/ui/settings/autofill/autofill_profile_table_view_controller.mm
similarity index 98%
rename from ios/chrome/browser/ui/settings/autofill_profile_table_view_controller.mm
rename to ios/chrome/browser/ui/settings/autofill/autofill_profile_table_view_controller.mm
index d4a9746..bcd1b433 100644
--- a/ios/chrome/browser/ui/settings/autofill_profile_table_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/autofill/autofill_profile_table_view_controller.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/chrome/browser/ui/settings/autofill_profile_table_view_controller.h"
+#import "ios/chrome/browser/ui/settings/autofill/autofill_profile_table_view_controller.h"
 
 #include "base/logging.h"
 #include "base/mac/foundation_util.h"
@@ -15,7 +15,7 @@
 #include "ios/chrome/browser/application_context.h"
 #include "ios/chrome/browser/autofill/personal_data_manager_factory.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
-#import "ios/chrome/browser/ui/settings/autofill_profile_edit_table_view_controller.h"
+#import "ios/chrome/browser/ui/settings/autofill/autofill_profile_edit_table_view_controller.h"
 #import "ios/chrome/browser/ui/settings/cells/autofill_data_item.h"
 #import "ios/chrome/browser/ui/settings/cells/settings_switch_cell.h"
 #import "ios/chrome/browser/ui/settings/cells/settings_switch_item.h"
diff --git a/ios/chrome/browser/ui/settings/autofill_profile_table_view_controller_unittest.mm b/ios/chrome/browser/ui/settings/autofill/autofill_profile_table_view_controller_unittest.mm
similarity index 97%
rename from ios/chrome/browser/ui/settings/autofill_profile_table_view_controller_unittest.mm
rename to ios/chrome/browser/ui/settings/autofill/autofill_profile_table_view_controller_unittest.mm
index bd0aa7c..44c2d63 100644
--- a/ios/chrome/browser/ui/settings/autofill_profile_table_view_controller_unittest.mm
+++ b/ios/chrome/browser/ui/settings/autofill/autofill_profile_table_view_controller_unittest.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/chrome/browser/ui/settings/autofill_profile_table_view_controller.h"
+#import "ios/chrome/browser/ui/settings/autofill/autofill_profile_table_view_controller.h"
 
 #include "base/guid.h"
 #include "base/mac/foundation_util.h"
diff --git a/ios/chrome/browser/ui/settings/autofill_edit_table_view_controller+protected.h b/ios/chrome/browser/ui/settings/autofill_edit_table_view_controller+protected.h
deleted file mode 100644
index 591ebb0..0000000
--- a/ios/chrome/browser/ui/settings/autofill_edit_table_view_controller+protected.h
+++ /dev/null
@@ -1,18 +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.
-
-#ifndef IOS_CHROME_BROWSER_UI_SETTINGS_AUTOFILL_EDIT_TABLE_VIEW_CONTROLLER_PROTECTED_H_
-#define IOS_CHROME_BROWSER_UI_SETTINGS_AUTOFILL_EDIT_TABLE_VIEW_CONTROLLER_PROTECTED_H_
-
-#import "ios/chrome/browser/ui/settings/autofill_edit_table_view_controller.h"
-
-// The table view for an Autofill edit entry menu.
-@interface AutofillEditTableViewController (Protected)
-
-// Returns the indexPath for the currently focused text field when in edit mode.
-- (NSIndexPath*)indexPathForCurrentTextField;
-
-@end
-
-#endif  // IOS_CHROME_BROWSER_UI_SETTINGS_AUTOFILL_EDIT_TABLE_VIEW_CONTROLLER_PROTECTED_H_
diff --git a/ios/chrome/browser/ui/settings/bandwidth_management_table_view_controller.mm b/ios/chrome/browser/ui/settings/bandwidth_management_table_view_controller.mm
index 1345aa7..12627d4 100644
--- a/ios/chrome/browser/ui/settings/bandwidth_management_table_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/bandwidth_management_table_view_controller.mm
@@ -12,7 +12,7 @@
 #include "ios/chrome/browser/pref_names.h"
 #import "ios/chrome/browser/ui/settings/cells/settings_detail_item.h"
 #import "ios/chrome/browser/ui/settings/dataplan_usage_table_view_controller.h"
-#import "ios/chrome/browser/ui/settings/settings_utils.h"
+#import "ios/chrome/browser/ui/settings/utils/settings_utils.h"
 #import "ios/chrome/browser/ui/table_view/cells/table_view_link_header_footer_item.h"
 #import "ios/chrome/browser/ui/table_view/cells/table_view_text_header_footer_item.h"
 #import "ios/chrome/browser/ui/table_view/cells/table_view_text_item.h"
diff --git a/ios/chrome/browser/ui/settings/password/BUILD.gn b/ios/chrome/browser/ui/settings/password/BUILD.gn
new file mode 100644
index 0000000..c71b6f7
--- /dev/null
+++ b/ios/chrome/browser/ui/settings/password/BUILD.gn
@@ -0,0 +1,131 @@
+# 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.
+
+source_set("password") {
+  configs += [ "//build/config/compiler:enable_arc" ]
+  sources = [
+    "password_details_table_view_controller.h",
+    "password_details_table_view_controller.mm",
+    "password_details_table_view_controller_delegate.h",
+    "password_exporter.h",
+    "password_exporter.mm",
+    "passwords_table_view_controller.h",
+    "passwords_table_view_controller.mm",
+    "reauthentication_module.h",
+    "reauthentication_module.mm",
+    "reauthentication_protocol.h",
+  ]
+  deps = [
+    "//base",
+    "//components/autofill/core/common",
+    "//components/google/core/common",
+    "//components/keyed_service/core",
+    "//components/password_manager/core/browser",
+    "//components/password_manager/core/common",
+    "//components/prefs",
+    "//components/strings",
+    "//components/url_formatter",
+    "//ios/chrome/app/strings",
+    "//ios/chrome/browser",
+    "//ios/chrome/browser",
+    "//ios/chrome/browser/browser_state",
+    "//ios/chrome/browser/passwords",
+    "//ios/chrome/browser/ui:feature_flags",
+    "//ios/chrome/browser/ui/settings:settings_root",
+    "//ios/chrome/browser/ui/settings/cells",
+    "//ios/chrome/browser/ui/settings/utils",
+    "//ios/chrome/browser/ui/table_view",
+    "//ios/chrome/browser/ui/table_view/cells",
+    "//ios/chrome/browser/ui/util",
+    "//ios/chrome/common/ui_util",
+    "//ios/third_party/material_components_ios",
+    "//ui/base",
+    "//url",
+  ]
+}
+
+source_set("test_support") {
+  configs += [ "//build/config/compiler:enable_arc" ]
+  testonly = true
+  sources = [
+    "password_details_table_view_controller+testing.h",
+    "password_exporter_for_testing.h",
+    "reauthentication_module_for_testing.h",
+  ]
+  deps = [
+    ":password",
+  ]
+}
+
+source_set("unit_tests") {
+  configs += [ "//build/config/compiler:enable_arc" ]
+  testonly = true
+  sources = [
+    "password_details_table_view_controller_unittest.mm",
+    "password_exporter_unittest.mm",
+    "passwords_table_view_controller_unittest.mm",
+    "reauthentication_module_unittest.mm",
+  ]
+  deps = [
+    ":password",
+    ":test_support",
+    "//base",
+    "//base/test:test_support",
+    "//components/autofill/core/common",
+    "//components/keyed_service/core",
+    "//components/password_manager/core/browser:test_support",
+    "//components/password_manager/core/common",
+    "//components/strings",
+    "//ios/chrome/app/strings",
+    "//ios/chrome/app/strings",
+    "//ios/chrome/browser/browser_state:test_support",
+    "//ios/chrome/browser/passwords",
+    "//ios/chrome/browser/ui/table_view:test_support",
+    "//ios/chrome/browser/ui/table_view/cells",
+    "//ios/chrome/browser/ui/util",
+    "//ios/chrome/browser/web:test_support",
+    "//ios/chrome/test/app:test_support",
+    "//ios/web/public/test",
+    "//ios/web/public/test",
+    "//testing/gmock",
+    "//testing/gtest",
+    "//testing/gtest",
+    "//third_party/ocmock",
+    "//ui/base",
+    "//url",
+  ]
+}
+
+source_set("eg_tests") {
+  configs += [ "//build/config/compiler:enable_arc" ]
+  testonly = true
+  sources = [
+    "passwords_settings_egtest.mm",
+  ]
+  deps = [
+    ":password",
+    "//base",
+    "//base/test:test_support",
+    "//components/autofill/core/common",
+    "//components/keyed_service/core",
+    "//components/password_manager/core/browser",
+    "//components/password_manager/core/common",
+    "//components/prefs",
+    "//components/strings",
+    "//ios/chrome/app/strings",
+    "//ios/chrome/browser/browser_state",
+    "//ios/chrome/browser/passwords",
+    "//ios/chrome/browser/ui/settings",
+    "//ios/chrome/browser/ui/table_view/cells",
+    "//ios/chrome/browser/ui/util",
+    "//ios/chrome/test/app:test_support",
+    "//ios/chrome/test/earl_grey:test_support",
+    "//ios/third_party/material_components_ios",
+    "//ios/web:earl_grey_test_support",
+    "//ios/web/public/test:util",
+    "//ios/web/public/test/http_server",
+    "//ui/base",
+    "//url",
+  ]
+}
diff --git a/ios/chrome/browser/ui/settings/password_details_table_view_controller+testing.h b/ios/chrome/browser/ui/settings/password/password_details_table_view_controller+testing.h
similarity index 61%
rename from ios/chrome/browser/ui/settings/password_details_table_view_controller+testing.h
rename to ios/chrome/browser/ui/settings/password/password_details_table_view_controller+testing.h
index 36ac1c5..7950cb47 100644
--- a/ios/chrome/browser/ui/settings/password_details_table_view_controller+testing.h
+++ b/ios/chrome/browser/ui/settings/password/password_details_table_view_controller+testing.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 IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_DETAILS_TABLE_VIEW_CONTROLLER_TESTING_H_
-#define IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_DETAILS_TABLE_VIEW_CONTROLLER_TESTING_H_
+#ifndef IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_PASSWORD_DETAILS_TABLE_VIEW_CONTROLLER_TESTING_H_
+#define IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_PASSWORD_DETAILS_TABLE_VIEW_CONTROLLER_TESTING_H_
 
-#import "ios/chrome/browser/ui/settings/password_details_table_view_controller.h"
+#import "ios/chrome/browser/ui/settings/password/password_details_table_view_controller.h"
 
 // TODO(crbug.com/894791): Refactor the PasswordTableViewController and
 // PasswordsSettingsTestCase to remove this Category file.
@@ -19,4 +19,4 @@
 
 @end
 
-#endif  // IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_DETAILS_TABLE_VIEW_CONTROLLER_TESTING_H_
+#endif  // IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_PASSWORD_DETAILS_TABLE_VIEW_CONTROLLER_TESTING_H_
diff --git a/ios/chrome/browser/ui/settings/password_details_table_view_controller.h b/ios/chrome/browser/ui/settings/password/password_details_table_view_controller.h
similarity index 80%
rename from ios/chrome/browser/ui/settings/password_details_table_view_controller.h
rename to ios/chrome/browser/ui/settings/password/password_details_table_view_controller.h
index 2a35703..df436432 100644
--- a/ios/chrome/browser/ui/settings/password_details_table_view_controller.h
+++ b/ios/chrome/browser/ui/settings/password/password_details_table_view_controller.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 IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_DETAILS_TABLE_VIEW_CONTROLLER_H_
-#define IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_DETAILS_TABLE_VIEW_CONTROLLER_H_
+#ifndef IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_PASSWORD_DETAILS_TABLE_VIEW_CONTROLLER_H_
+#define IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_PASSWORD_DETAILS_TABLE_VIEW_CONTROLLER_H_
 
-#import "ios/chrome/browser/ui/settings/password_details_table_view_controller_delegate.h"
+#import "ios/chrome/browser/ui/settings/password/password_details_table_view_controller_delegate.h"
 #import "ios/chrome/browser/ui/settings/settings_root_table_view_controller.h"
 
 namespace autofill {
@@ -40,4 +40,4 @@
 
 @end
 
-#endif  // IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_DETAILS_TABLE_VIEW_CONTROLLER_H_
+#endif  // IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_PASSWORD_DETAILS_TABLE_VIEW_CONTROLLER_H_
diff --git a/ios/chrome/browser/ui/settings/password_details_table_view_controller.mm b/ios/chrome/browser/ui/settings/password/password_details_table_view_controller.mm
similarity index 98%
rename from ios/chrome/browser/ui/settings/password_details_table_view_controller.mm
rename to ios/chrome/browser/ui/settings/password/password_details_table_view_controller.mm
index 5ff9009..b84b7f2 100644
--- a/ios/chrome/browser/ui/settings/password_details_table_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/password/password_details_table_view_controller.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/chrome/browser/ui/settings/password_details_table_view_controller.h"
+#import "ios/chrome/browser/ui/settings/password/password_details_table_view_controller.h"
 
 #import <UIKit/UIKit.h>
 
@@ -15,9 +15,9 @@
 #include "components/password_manager/core/browser/password_ui_utils.h"
 #include "components/strings/grit/components_strings.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
-#import "ios/chrome/browser/ui/settings/passwords_table_view_controller.h"
-#import "ios/chrome/browser/ui/settings/reauthentication_module.h"
-#import "ios/chrome/browser/ui/settings/settings_utils.h"
+#import "ios/chrome/browser/ui/settings/password/passwords_table_view_controller.h"
+#import "ios/chrome/browser/ui/settings/password/reauthentication_module.h"
+#import "ios/chrome/browser/ui/settings/utils/settings_utils.h"
 #import "ios/chrome/browser/ui/table_view/cells/table_view_cells_constants.h"
 #import "ios/chrome/browser/ui/table_view/cells/table_view_detail_text_item.h"
 #import "ios/chrome/browser/ui/table_view/cells/table_view_text_header_footer_item.h"
diff --git a/ios/chrome/browser/ui/settings/password_details_table_view_controller_delegate.h b/ios/chrome/browser/ui/settings/password/password_details_table_view_controller_delegate.h
similarity index 70%
rename from ios/chrome/browser/ui/settings/password_details_table_view_controller_delegate.h
rename to ios/chrome/browser/ui/settings/password/password_details_table_view_controller_delegate.h
index e650ca3..4373925 100644
--- a/ios/chrome/browser/ui/settings/password_details_table_view_controller_delegate.h
+++ b/ios/chrome/browser/ui/settings/password/password_details_table_view_controller_delegate.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 IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_DETAILS_TABLE_VIEW_CONTROLLER_DELEGATE_H_
-#define IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_DETAILS_TABLE_VIEW_CONTROLLER_DELEGATE_H_
+#ifndef IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_PASSWORD_DETAILS_TABLE_VIEW_CONTROLLER_DELEGATE_H_
+#define IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_PASSWORD_DETAILS_TABLE_VIEW_CONTROLLER_DELEGATE_H_
 
 #import <Foundation/Foundation.h>
 
@@ -24,4 +24,4 @@
 
 @end
 
-#endif  // IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_DETAILS_TABLE_VIEW_CONTROLLER_DELEGATE_H_
+#endif  // IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_PASSWORD_DETAILS_TABLE_VIEW_CONTROLLER_DELEGATE_H_
diff --git a/ios/chrome/browser/ui/settings/password_details_table_view_controller_unittest.mm b/ios/chrome/browser/ui/settings/password/password_details_table_view_controller_unittest.mm
similarity index 98%
rename from ios/chrome/browser/ui/settings/password_details_table_view_controller_unittest.mm
rename to ios/chrome/browser/ui/settings/password/password_details_table_view_controller_unittest.mm
index f964145f..a00eb039 100644
--- a/ios/chrome/browser/ui/settings/password_details_table_view_controller_unittest.mm
+++ b/ios/chrome/browser/ui/settings/password/password_details_table_view_controller_unittest.mm
@@ -2,12 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/chrome/browser/ui/settings/password_details_table_view_controller.h"
+#import "ios/chrome/browser/ui/settings/password/password_details_table_view_controller.h"
 
 #include "base/mac/foundation_util.h"
 #include "base/strings/sys_string_conversions.h"
 #include "components/autofill/core/common/password_form.h"
-#import "ios/chrome/browser/ui/settings/reauthentication_module.h"
+#import "ios/chrome/browser/ui/settings/password/reauthentication_module.h"
 #import "ios/chrome/browser/ui/table_view/cells/table_view_text_item.h"
 #import "ios/chrome/browser/ui/table_view/chrome_table_view_controller_test.h"
 #import "ios/chrome/browser/web/chrome_web_test.h"
diff --git a/ios/chrome/browser/ui/settings/password_exporter.h b/ios/chrome/browser/ui/settings/password/password_exporter.h
similarity index 91%
rename from ios/chrome/browser/ui/settings/password_exporter.h
rename to ios/chrome/browser/ui/settings/password/password_exporter.h
index f75a2815..c0b73ba7 100644
--- a/ios/chrome/browser/ui/settings/password_exporter.h
+++ b/ios/chrome/browser/ui/settings/password/password_exporter.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 IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_EXPORTER_H_
-#define IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_EXPORTER_H_
+#ifndef IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_PASSWORD_EXPORTER_H_
+#define IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_PASSWORD_EXPORTER_H_
 
 #import <Foundation/Foundation.h>
 
@@ -28,7 +28,7 @@
 
 @protocol ReauthenticationProtocol;
 
-@protocol FileWriterProtocol<NSObject>
+@protocol FileWriterProtocol <NSObject>
 
 // Posts a task to write the data in |data| to the file at |fileURL| and
 // executes |handler| when the writing is finished.
@@ -38,7 +38,7 @@
 
 @end
 
-@protocol PasswordSerializerBridge<NSObject>
+@protocol PasswordSerializerBridge <NSObject>
 
 // Posts task to serialize passwords and calls |serializedPasswordsHandler|
 // when serialization is finished.
@@ -48,7 +48,7 @@
 
 @end
 
-@protocol PasswordExporterDelegate<NSObject>
+@protocol PasswordExporterDelegate <NSObject>
 
 // Displays a dialog informing the user that they must set up a passcode
 // in order to export passwords.
@@ -119,4 +119,4 @@
 
 @end
 
-#endif  // IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_EXPORTER_H_
+#endif  // IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_PASSWORD_EXPORTER_H_
diff --git a/ios/chrome/browser/ui/settings/password_exporter.mm b/ios/chrome/browser/ui/settings/password/password_exporter.mm
similarity index 94%
rename from ios/chrome/browser/ui/settings/password_exporter.mm
rename to ios/chrome/browser/ui/settings/password/password_exporter.mm
index 03be0072..4befaa6 100644
--- a/ios/chrome/browser/ui/settings/password_exporter.mm
+++ b/ios/chrome/browser/ui/settings/password/password_exporter.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/chrome/browser/ui/settings/password_exporter.h"
+#import "ios/chrome/browser/ui/settings/password/password_exporter.h"
 
 #include "base/bind.h"
 #include "base/files/file_path.h"
@@ -17,7 +17,7 @@
 #include "components/password_manager/core/browser/password_manager_metrics_util.h"
 #include "components/password_manager/core/common/passwords_directory_util_ios.h"
 #include "components/strings/grit/components_strings.h"
-#import "ios/chrome/browser/ui/settings/reauthentication_module.h"
+#import "ios/chrome/browser/ui/settings/password/reauthentication_module.h"
 #include "ios/chrome/grit/ios_strings.h"
 #include "ui/base/l10n/l10n_util_mac.h"
 
@@ -35,7 +35,7 @@
 
 }  // namespace
 
-@interface PasswordSerializerBridge : NSObject<PasswordSerializerBridge>
+@interface PasswordSerializerBridge : NSObject <PasswordSerializerBridge>
 @end
 
 @implementation PasswordSerializerBridge
@@ -52,7 +52,7 @@
 
 @end
 
-@interface PasswordFileWriter : NSObject<FileWriterProtocol>
+@interface PasswordFileWriter : NSObject <FileWriterProtocol>
 @end
 
 @implementation PasswordFileWriter
@@ -192,19 +192,18 @@
 
   base::Time exportPreparationStart = base::Time::Now();
   __weak PasswordExporter* weakSelf = self;
-  void (^onPasswordsSerialized)(std::string) =
-      ^(std::string serializedPasswords) {
-        PasswordExporter* strongSelf = weakSelf;
-        if (!strongSelf)
-          return;
-        strongSelf.serializedPasswords =
-            base::SysUTF8ToNSString(serializedPasswords);
-        strongSelf.serializingFinished = YES;
-        UMA_HISTOGRAM_MEDIUM_TIMES(
-            "PasswordManager.TimeReadingExportedPasswords",
-            base::Time::Now() - exportPreparationStart);
-        [strongSelf tryExporting];
-      };
+  void (^onPasswordsSerialized)(std::string) = ^(
+      std::string serializedPasswords) {
+    PasswordExporter* strongSelf = weakSelf;
+    if (!strongSelf)
+      return;
+    strongSelf.serializedPasswords =
+        base::SysUTF8ToNSString(serializedPasswords);
+    strongSelf.serializingFinished = YES;
+    UMA_HISTOGRAM_MEDIUM_TIMES("PasswordManager.TimeReadingExportedPasswords",
+                               base::Time::Now() - exportPreparationStart);
+    [strongSelf tryExporting];
+  };
 
   [_passwordSerializerBridge serializePasswords:std::move(passwords)
                                         handler:onPasswordsSerialized];
diff --git a/ios/chrome/browser/ui/settings/password/password_exporter_for_testing.h b/ios/chrome/browser/ui/settings/password/password_exporter_for_testing.h
new file mode 100644
index 0000000..e9d4250e
--- /dev/null
+++ b/ios/chrome/browser/ui/settings/password/password_exporter_for_testing.h
@@ -0,0 +1,19 @@
+// 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.
+
+#ifndef IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_PASSWORD_EXPORTER_FOR_TESTING_H_
+#define IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_PASSWORD_EXPORTER_FOR_TESTING_H_
+
+#import "ios/chrome/browser/ui/settings/password/password_exporter.h"
+
+@interface PasswordExporter (ForTesting)
+
+- (void)setPasswordSerializerBridge:
+    (id<PasswordSerializerBridge>)passwordSerialzerBridge;
+
+- (void)setPasswordFileWriter:(id<FileWriterProtocol>)passwordFileWriter;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_PASSWORD_EXPORTER_FOR_TESTING_H_
diff --git a/ios/chrome/browser/ui/settings/password_exporter_unittest.mm b/ios/chrome/browser/ui/settings/password/password_exporter_unittest.mm
similarity index 98%
rename from ios/chrome/browser/ui/settings/password_exporter_unittest.mm
rename to ios/chrome/browser/ui/settings/password/password_exporter_unittest.mm
index 33506470..7ba969f 100644
--- a/ios/chrome/browser/ui/settings/password_exporter_unittest.mm
+++ b/ios/chrome/browser/ui/settings/password/password_exporter_unittest.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/chrome/browser/ui/settings/password_exporter_for_testing.h"
+#import "ios/chrome/browser/ui/settings/password/password_exporter_for_testing.h"
 
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/metrics/histogram_tester.h"
@@ -21,7 +21,7 @@
 #error "This file requires ARC support."
 #endif
 
-@interface FakePasswordSerialzerBridge : NSObject<PasswordSerializerBridge>
+@interface FakePasswordSerialzerBridge : NSObject <PasswordSerializerBridge>
 
 // Allows for on demand execution of the block that handles the serialized
 // passwords.
@@ -46,7 +46,7 @@
 
 @end
 
-@interface FakePasswordFileWriter : NSObject<FileWriterProtocol>
+@interface FakePasswordFileWriter : NSObject <FileWriterProtocol>
 
 // Allows for on demand execution of the block that should be executed after
 // the file has finished writing.
diff --git a/ios/chrome/browser/ui/settings/passwords_settings_egtest.mm b/ios/chrome/browser/ui/settings/password/passwords_settings_egtest.mm
similarity index 99%
rename from ios/chrome/browser/ui/settings/passwords_settings_egtest.mm
rename to ios/chrome/browser/ui/settings/password/passwords_settings_egtest.mm
index 665ce51..a3169a4 100644
--- a/ios/chrome/browser/ui/settings/passwords_settings_egtest.mm
+++ b/ios/chrome/browser/ui/settings/password/passwords_settings_egtest.mm
@@ -22,9 +22,9 @@
 #include "components/strings/grit/components_strings.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
 #include "ios/chrome/browser/passwords/ios_chrome_password_store_factory.h"
-#import "ios/chrome/browser/ui/settings/password_details_table_view_controller.h"
-#import "ios/chrome/browser/ui/settings/passwords_table_view_controller.h"
-#import "ios/chrome/browser/ui/settings/reauthentication_module.h"
+#import "ios/chrome/browser/ui/settings/password/password_details_table_view_controller.h"
+#import "ios/chrome/browser/ui/settings/password/passwords_table_view_controller.h"
+#import "ios/chrome/browser/ui/settings/password/reauthentication_module.h"
 #import "ios/chrome/browser/ui/table_view/cells/table_view_cells_constants.h"
 #include "ios/chrome/browser/ui/util/ui_util.h"
 #include "ios/chrome/grit/ios_strings.h"
diff --git a/ios/chrome/browser/ui/settings/passwords_table_view_controller.h b/ios/chrome/browser/ui/settings/password/passwords_table_view_controller.h
similarity index 80%
rename from ios/chrome/browser/ui/settings/passwords_table_view_controller.h
rename to ios/chrome/browser/ui/settings/password/passwords_table_view_controller.h
index d2ec3f7..6489a44 100644
--- a/ios/chrome/browser/ui/settings/passwords_table_view_controller.h
+++ b/ios/chrome/browser/ui/settings/password/passwords_table_view_controller.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 IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORDS_TABLE_VIEW_CONTROLLER_H_
-#define IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORDS_TABLE_VIEW_CONTROLLER_H_
+#ifndef IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_PASSWORDS_TABLE_VIEW_CONTROLLER_H_
+#define IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_PASSWORDS_TABLE_VIEW_CONTROLLER_H_
 
-#import "ios/chrome/browser/ui/settings/password_details_table_view_controller_delegate.h"
+#import "ios/chrome/browser/ui/settings/password/password_details_table_view_controller_delegate.h"
 #import "ios/chrome/browser/ui/settings/settings_root_table_view_controller.h"
 
 namespace ios {
@@ -46,4 +46,4 @@
 
 @end
 
-#endif  // IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORDS_TABLE_VIEW_CONTROLLER_H_
+#endif  // IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_PASSWORDS_TABLE_VIEW_CONTROLLER_H_
diff --git a/ios/chrome/browser/ui/settings/passwords_table_view_controller.mm b/ios/chrome/browser/ui/settings/password/passwords_table_view_controller.mm
similarity index 98%
rename from ios/chrome/browser/ui/settings/passwords_table_view_controller.mm
rename to ios/chrome/browser/ui/settings/password/passwords_table_view_controller.mm
index 2e58062..2356a19e 100644
--- a/ios/chrome/browser/ui/settings/passwords_table_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/password/passwords_table_view_controller.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/chrome/browser/ui/settings/passwords_table_view_controller.h"
+#import "ios/chrome/browser/ui/settings/password/passwords_table_view_controller.h"
 
 #import <UIKit/UIKit.h>
 
@@ -33,12 +33,12 @@
 #import "ios/chrome/browser/ui/settings/cells/settings_cells_constants.h"
 #import "ios/chrome/browser/ui/settings/cells/settings_switch_cell.h"
 #import "ios/chrome/browser/ui/settings/cells/settings_switch_item.h"
-#import "ios/chrome/browser/ui/settings/password_details_table_view_controller.h"
-#import "ios/chrome/browser/ui/settings/password_details_table_view_controller_delegate.h"
-#import "ios/chrome/browser/ui/settings/password_exporter.h"
-#import "ios/chrome/browser/ui/settings/reauthentication_module.h"
-#import "ios/chrome/browser/ui/settings/settings_utils.h"
+#import "ios/chrome/browser/ui/settings/password/password_details_table_view_controller.h"
+#import "ios/chrome/browser/ui/settings/password/password_details_table_view_controller_delegate.h"
+#import "ios/chrome/browser/ui/settings/password/password_exporter.h"
+#import "ios/chrome/browser/ui/settings/password/reauthentication_module.h"
 #import "ios/chrome/browser/ui/settings/utils/pref_backed_boolean.h"
+#import "ios/chrome/browser/ui/settings/utils/settings_utils.h"
 #import "ios/chrome/browser/ui/table_view/cells/table_view_cells_constants.h"
 #import "ios/chrome/browser/ui/table_view/cells/table_view_detail_text_item.h"
 #import "ios/chrome/browser/ui/table_view/cells/table_view_link_header_footer_item.h"
diff --git a/ios/chrome/browser/ui/settings/passwords_table_view_controller_unittest.mm b/ios/chrome/browser/ui/settings/password/passwords_table_view_controller_unittest.mm
similarity index 98%
rename from ios/chrome/browser/ui/settings/passwords_table_view_controller_unittest.mm
rename to ios/chrome/browser/ui/settings/password/passwords_table_view_controller_unittest.mm
index 02a4fbe..45754c29 100644
--- a/ios/chrome/browser/ui/settings/passwords_table_view_controller_unittest.mm
+++ b/ios/chrome/browser/ui/settings/password/passwords_table_view_controller_unittest.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/chrome/browser/ui/settings/passwords_table_view_controller.h"
+#import "ios/chrome/browser/ui/settings/password/passwords_table_view_controller.h"
 
 #include "base/bind.h"
 #include "base/compiler_specific.h"
@@ -16,7 +16,7 @@
 #include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
 #include "ios/chrome/browser/passwords/ios_chrome_password_store_factory.h"
 #include "ios/chrome/browser/passwords/save_passwords_consumer.h"
-#import "ios/chrome/browser/ui/settings/password_details_table_view_controller.h"
+#import "ios/chrome/browser/ui/settings/password/password_details_table_view_controller.h"
 #import "ios/chrome/browser/ui/table_view/cells/table_view_cells_constants.h"
 #import "ios/chrome/browser/ui/table_view/cells/table_view_detail_text_item.h"
 #include "ios/chrome/browser/ui/table_view/chrome_table_view_controller_test.h"
diff --git a/ios/chrome/browser/ui/settings/reauthentication_module.h b/ios/chrome/browser/ui/settings/password/reauthentication_module.h
similarity index 71%
rename from ios/chrome/browser/ui/settings/reauthentication_module.h
rename to ios/chrome/browser/ui/settings/password/reauthentication_module.h
index a72a23c9..deffd55 100644
--- a/ios/chrome/browser/ui/settings/reauthentication_module.h
+++ b/ios/chrome/browser/ui/settings/password/reauthentication_module.h
@@ -2,17 +2,17 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef IOS_CHROME_BROWSER_UI_SETTINGS_REAUTHENTICATION_MODULE_H_
-#define IOS_CHROME_BROWSER_UI_SETTINGS_REAUTHENTICATION_MODULE_H_
+#ifndef IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_REAUTHENTICATION_MODULE_H_
+#define IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_REAUTHENTICATION_MODULE_H_
 
 #import <Foundation/Foundation.h>
 
-#import "ios/chrome/browser/ui/settings/reauthentication_protocol.h"
+#import "ios/chrome/browser/ui/settings/password/reauthentication_protocol.h"
 
 // A help article on how to set up a passcode.
 extern const char kPasscodeArticleURL[];
 
-@protocol SuccessfulReauthTimeAccessor<NSObject>
+@protocol SuccessfulReauthTimeAccessor <NSObject>
 
 // Method meant to be called by the |ReauthenticationModule| to update
 // the time of the last successful re-authentication.
@@ -28,7 +28,7 @@
  * |PasswordExporter|to re-authenticate the user before displaying the password
  * in plain text, allowing it to be copied, or exporting passwords.
  */
-@interface ReauthenticationModule : NSObject<ReauthenticationProtocol>
+@interface ReauthenticationModule : NSObject <ReauthenticationProtocol>
 
 // The designated initializer. |successfulReauthTimeAccessor| must not be nil.
 - (instancetype)initWithSuccessfulReauthTimeAccessor:
@@ -39,4 +39,4 @@
 
 @end
 
-#endif  // IOS_CHROME_BROWSER_UI_SETTINGS_REAUTHENTICATION_MODULE_H_
+#endif  // IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_REAUTHENTICATION_MODULE_H_
diff --git a/ios/chrome/browser/ui/settings/reauthentication_module.mm b/ios/chrome/browser/ui/settings/password/reauthentication_module.mm
similarity index 94%
rename from ios/chrome/browser/ui/settings/reauthentication_module.mm
rename to ios/chrome/browser/ui/settings/password/reauthentication_module.mm
index a342f83..73934c7 100644
--- a/ios/chrome/browser/ui/settings/reauthentication_module.mm
+++ b/ios/chrome/browser/ui/settings/password/reauthentication_module.mm
@@ -1,7 +1,7 @@
 // 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 "ios/chrome/browser/ui/settings/reauthentication_module.h"
+#import "ios/chrome/browser/ui/settings/password/reauthentication_module.h"
 
 #import <LocalAuthentication/LocalAuthentication.h>
 
@@ -42,8 +42,8 @@
 - (BOOL)canAttemptReauth {
   LAContext* context = _createLAContext();
   // The authentication method is Touch ID, Face ID or passcode.
-  return
-      [context canEvaluatePolicy:LAPolicyDeviceOwnerAuthentication error:nil];
+  return [context canEvaluatePolicy:LAPolicyDeviceOwnerAuthentication
+                              error:nil];
 }
 
 - (void)attemptReauthWithLocalizedReason:(NSString*)localizedReason
diff --git a/ios/chrome/browser/ui/settings/password/reauthentication_module_for_testing.h b/ios/chrome/browser/ui/settings/password/reauthentication_module_for_testing.h
new file mode 100644
index 0000000..8ba4170c
--- /dev/null
+++ b/ios/chrome/browser/ui/settings/password/reauthentication_module_for_testing.h
@@ -0,0 +1,20 @@
+// 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.
+
+#ifndef IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_REAUTHENTICATION_MODULE_FOR_TESTING_H_
+#define IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_REAUTHENTICATION_MODULE_FOR_TESTING_H_
+
+#import "ios/chrome/browser/ui/settings/password/reauthentication_module.h"
+
+#import <LocalAuthentication/LocalAuthentication.h>
+
+@interface ReauthenticationModule (ForTesting)
+
+// Allows the replacement of the |LAContext| objects used by
+// |ReauthenticationModule| with a mock to facilitate testing.
+- (void)setCreateLAContext:(LAContext* (^)(void))createLAContext;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_REAUTHENTICATION_MODULE_FOR_TESTING_H
diff --git a/ios/chrome/browser/ui/settings/reauthentication_module_unittest.mm b/ios/chrome/browser/ui/settings/password/reauthentication_module_unittest.mm
similarity index 97%
rename from ios/chrome/browser/ui/settings/reauthentication_module_unittest.mm
rename to ios/chrome/browser/ui/settings/password/reauthentication_module_unittest.mm
index e287561..94cb1f7d 100644
--- a/ios/chrome/browser/ui/settings/reauthentication_module_unittest.mm
+++ b/ios/chrome/browser/ui/settings/password/reauthentication_module_unittest.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/chrome/browser/ui/settings/reauthentication_module_for_testing.h"
+#import "ios/chrome/browser/ui/settings/password/reauthentication_module_for_testing.h"
 
 #import <LocalAuthentication/LocalAuthentication.h>
 
@@ -17,7 +17,7 @@
 #endif
 
 @interface TestingSuccessfulReauthTimeAccessor
-    : NSObject<SuccessfulReauthTimeAccessor> {
+    : NSObject <SuccessfulReauthTimeAccessor> {
   // Object storing the time of a fake previous successful re-authentication
   // to be used by the |ReauthenticationModule|.
   NSDate* successfulReauthTime_;
diff --git a/ios/chrome/browser/ui/settings/reauthentication_protocol.h b/ios/chrome/browser/ui/settings/password/reauthentication_protocol.h
similarity index 75%
rename from ios/chrome/browser/ui/settings/reauthentication_protocol.h
rename to ios/chrome/browser/ui/settings/password/reauthentication_protocol.h
index 57b051ef..230819d6 100644
--- a/ios/chrome/browser/ui/settings/reauthentication_protocol.h
+++ b/ios/chrome/browser/ui/settings/password/reauthentication_protocol.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 IOS_CHROME_BROWSER_UI_SETTINGS_REAUTHENTICATION_PROTOCOL_H_
-#define IOS_CHROME_BROWSER_UI_SETTINGS_REAUTHENTICATION_PROTOCOL_H_
+#ifndef IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_REAUTHENTICATION_PROTOCOL_H_
+#define IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_REAUTHENTICATION_PROTOCOL_H_
 
 #import <Foundation/Foundation.h>
 
-@protocol ReauthenticationProtocol<NSObject>
+@protocol ReauthenticationProtocol <NSObject>
 
 // Checks whether Touch ID and/or passcode is enabled for the device.
 - (BOOL)canAttemptReauth;
@@ -23,4 +23,4 @@
 
 @end
 
-#endif  // IOS_CHROME_BROWSER_UI_SETTINGS_REAUTHENTICATION_PROTOCOL_H_
+#endif  // IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_REAUTHENTICATION_PROTOCOL_H_
diff --git a/ios/chrome/browser/ui/settings/password_exporter_for_testing.h b/ios/chrome/browser/ui/settings/password_exporter_for_testing.h
deleted file mode 100644
index 03c749e..0000000
--- a/ios/chrome/browser/ui/settings/password_exporter_for_testing.h
+++ /dev/null
@@ -1,19 +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.
-
-#ifndef IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_EXPORTER_FOR_TESTING_H_
-#define IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_EXPORTER_FOR_TESTING_H_
-
-#import "ios/chrome/browser/ui/settings/password_exporter.h"
-
-@interface PasswordExporter (ForTesting)
-
-- (void)setPasswordSerializerBridge:
-    (id<PasswordSerializerBridge>)passwordSerialzerBridge;
-
-- (void)setPasswordFileWriter:(id<FileWriterProtocol>)passwordFileWriter;
-
-@end
-
-#endif  // IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_EXPORTER_FOR_TESTING_H_
diff --git a/ios/chrome/browser/ui/settings/privacy_table_view_controller.mm b/ios/chrome/browser/ui/settings/privacy_table_view_controller.mm
index 52bcbfa..80a6e14c 100644
--- a/ios/chrome/browser/ui/settings/privacy_table_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/privacy_table_view_controller.mm
@@ -31,8 +31,8 @@
 #import "ios/chrome/browser/ui/settings/dataplan_usage_table_view_controller.h"
 #import "ios/chrome/browser/ui/settings/handoff_table_view_controller.h"
 #import "ios/chrome/browser/ui/settings/settings_navigation_controller.h"
-#import "ios/chrome/browser/ui/settings/settings_utils.h"
 #import "ios/chrome/browser/ui/settings/utils/pref_backed_boolean.h"
+#import "ios/chrome/browser/ui/settings/utils/settings_utils.h"
 #import "ios/chrome/browser/ui/table_view/cells/table_view_detail_text_item.h"
 #import "ios/chrome/browser/ui/table_view/cells/table_view_link_header_footer_item.h"
 #import "ios/chrome/browser/ui/table_view/cells/table_view_text_header_footer_item.h"
diff --git a/ios/chrome/browser/ui/settings/reauthentication_module_for_testing.h b/ios/chrome/browser/ui/settings/reauthentication_module_for_testing.h
deleted file mode 100644
index 4939204..0000000
--- a/ios/chrome/browser/ui/settings/reauthentication_module_for_testing.h
+++ /dev/null
@@ -1,20 +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.
-
-#ifndef IOS_CHROME_BROWSER_UI_SETTINGS_REAUTHENTICATION_MODULE_FOR_TESTING_H_
-#define IOS_CHROME_BROWSER_UI_SETTINGS_REAUTHENTICATION_MODULE_FOR_TESTING_H_
-
-#import "ios/chrome/browser/ui/settings/reauthentication_module.h"
-
-#import <LocalAuthentication/LocalAuthentication.h>
-
-@interface ReauthenticationModule (ForTesting)
-
-// Allows the replacement of the |LAContext| objects used by
-// |ReauthenticationModule| with a mock to facilitate testing.
-- (void)setCreateLAContext:(LAContext* (^)(void))createLAContext;
-
-@end
-
-#endif  // IOS_CHROME_BROWSER_UI_SETTINGS_REAUTHENTICATION_MODULE_FOR_TESTING_H
diff --git a/ios/chrome/browser/ui/settings/settings_navigation_controller.mm b/ios/chrome/browser/ui/settings/settings_navigation_controller.mm
index 2aa1fff..b0c8ab7 100644
--- a/ios/chrome/browser/ui/settings/settings_navigation_controller.mm
+++ b/ios/chrome/browser/ui/settings/settings_navigation_controller.mm
@@ -13,17 +13,17 @@
 #import "ios/chrome/browser/ui/keyboard/UIKeyCommand+Chrome.h"
 #import "ios/chrome/browser/ui/material_components/utils.h"
 #import "ios/chrome/browser/ui/settings/accounts_table_view_controller.h"
-#import "ios/chrome/browser/ui/settings/autofill_credit_card_table_view_controller.h"
-#import "ios/chrome/browser/ui/settings/autofill_profile_table_view_controller.h"
+#import "ios/chrome/browser/ui/settings/autofill/autofill_credit_card_table_view_controller.h"
+#import "ios/chrome/browser/ui/settings/autofill/autofill_profile_table_view_controller.h"
 #import "ios/chrome/browser/ui/settings/google_services_settings_coordinator.h"
 #import "ios/chrome/browser/ui/settings/google_services_settings_view_controller.h"
 #import "ios/chrome/browser/ui/settings/import_data_table_view_controller.h"
-#import "ios/chrome/browser/ui/settings/passwords_table_view_controller.h"
+#import "ios/chrome/browser/ui/settings/password/passwords_table_view_controller.h"
 #import "ios/chrome/browser/ui/settings/settings_root_collection_view_controller.h"
 #import "ios/chrome/browser/ui/settings/settings_table_view_controller.h"
-#import "ios/chrome/browser/ui/settings/settings_utils.h"
 #import "ios/chrome/browser/ui/settings/sync_encryption_passphrase_table_view_controller.h"
 #import "ios/chrome/browser/ui/settings/sync_settings_collection_view_controller.h"
+#import "ios/chrome/browser/ui/settings/utils/settings_utils.h"
 #include "ios/chrome/browser/ui/ui_feature_flags.h"
 #include "ios/chrome/browser/ui/util/ui_util.h"
 #import "ios/chrome/browser/ui/util/uikit_ui_util.h"
diff --git a/ios/chrome/browser/ui/settings/settings_table_view_controller.mm b/ios/chrome/browser/ui/settings/settings_table_view_controller.mm
index 79d97c1..c1be3b0 100644
--- a/ios/chrome/browser/ui/settings/settings_table_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/settings_table_view_controller.mm
@@ -41,8 +41,8 @@
 #import "ios/chrome/browser/ui/commands/settings_main_page_commands.h"
 #import "ios/chrome/browser/ui/settings/about_chrome_table_view_controller.h"
 #import "ios/chrome/browser/ui/settings/accounts_table_view_controller.h"
-#import "ios/chrome/browser/ui/settings/autofill_credit_card_table_view_controller.h"
-#import "ios/chrome/browser/ui/settings/autofill_profile_table_view_controller.h"
+#import "ios/chrome/browser/ui/settings/autofill/autofill_credit_card_table_view_controller.h"
+#import "ios/chrome/browser/ui/settings/autofill/autofill_profile_table_view_controller.h"
 #import "ios/chrome/browser/ui/settings/bandwidth_management_table_view_controller.h"
 #import "ios/chrome/browser/ui/settings/cells/account_sign_in_item.h"
 #import "ios/chrome/browser/ui/settings/cells/settings_detail_item.h"
@@ -52,7 +52,7 @@
 #import "ios/chrome/browser/ui/settings/content_settings_table_view_controller.h"
 #import "ios/chrome/browser/ui/settings/google_services_settings_coordinator.h"
 #import "ios/chrome/browser/ui/settings/material_cell_catalog_view_controller.h"
-#import "ios/chrome/browser/ui/settings/passwords_table_view_controller.h"
+#import "ios/chrome/browser/ui/settings/password/passwords_table_view_controller.h"
 #import "ios/chrome/browser/ui/settings/privacy_table_view_controller.h"
 #import "ios/chrome/browser/ui/settings/search_engine_table_view_controller.h"
 #import "ios/chrome/browser/ui/settings/sync_utils/sync_util.h"
diff --git a/ios/chrome/browser/ui/settings/sync_encryption_passphrase_table_view_controller.mm b/ios/chrome/browser/ui/settings/sync_encryption_passphrase_table_view_controller.mm
index 98d16423..2e2c3ce3 100644
--- a/ios/chrome/browser/ui/settings/sync_encryption_passphrase_table_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/sync_encryption_passphrase_table_view_controller.mm
@@ -26,8 +26,8 @@
 #import "ios/chrome/browser/ui/settings/cells/card_multiline_item.h"
 #import "ios/chrome/browser/ui/settings/cells/passphrase_error_item.h"
 #import "ios/chrome/browser/ui/settings/settings_navigation_controller.h"
-#import "ios/chrome/browser/ui/settings/settings_utils.h"
 #import "ios/chrome/browser/ui/settings/sync_utils/sync_util.h"
+#import "ios/chrome/browser/ui/settings/utils/settings_utils.h"
 #import "ios/chrome/browser/ui/table_view/cells/table_view_link_header_footer_item.h"
 #include "ios/chrome/browser/ui/ui_feature_flags.h"
 #import "ios/chrome/browser/ui/util/uikit_ui_util.h"
@@ -463,8 +463,9 @@
   }
 
   // Checking if the operation succeeded.
-  if (!service->IsPassphraseRequired() &&
-      (service->IsUsingSecondaryPassphrase() || [self forDecryption])) {
+  if (!service->GetUserSettings()->IsPassphraseRequired() &&
+      (service->GetUserSettings()->IsUsingSecondaryPassphrase() ||
+       [self forDecryption])) {
     syncObserver_.reset();
     [base::mac::ObjCCastStrict<SettingsNavigationController>(
         self.navigationController)
@@ -473,7 +474,7 @@
   }
 
   // Handling passphrase error case.
-  if (service->IsPassphraseRequired()) {
+  if (service->GetUserSettings()->IsPassphraseRequired()) {
     self.syncErrorMessage =
         l10n_util::GetNSString(IDS_IOS_SYNC_INCORRECT_PASSPHRASE);
   }
diff --git a/ios/chrome/browser/ui/settings/sync_encryption_table_view_controller.mm b/ios/chrome/browser/ui/settings/sync_encryption_table_view_controller.mm
index ff8f860b..975ac81 100644
--- a/ios/chrome/browser/ui/settings/sync_encryption_table_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/sync_encryption_table_view_controller.mm
@@ -19,9 +19,9 @@
 #include "ios/chrome/browser/sync/profile_sync_service_factory.h"
 #import "ios/chrome/browser/sync/sync_observer_bridge.h"
 #import "ios/chrome/browser/ui/settings/cells/encryption_item.h"
-#import "ios/chrome/browser/ui/settings/settings_utils.h"
 #import "ios/chrome/browser/ui/settings/sync_create_passphrase_table_view_controller.h"
 #import "ios/chrome/browser/ui/settings/sync_encryption_passphrase_table_view_controller.h"
+#import "ios/chrome/browser/ui/settings/utils/settings_utils.h"
 #import "ios/chrome/browser/ui/table_view/cells/table_view_cells_constants.h"
 #import "ios/chrome/browser/ui/table_view/cells/table_view_item.h"
 #import "ios/chrome/browser/ui/table_view/cells/table_view_link_header_footer_item.h"
diff --git a/ios/chrome/browser/ui/settings/sync_settings_collection_view_controller.mm b/ios/chrome/browser/ui/settings/sync_settings_collection_view_controller.mm
index 24c92445..6763bf6 100644
--- a/ios/chrome/browser/ui/settings/sync_settings_collection_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/sync_settings_collection_view_controller.mm
@@ -14,6 +14,7 @@
 #include "components/strings/grit/components_strings.h"
 #include "components/sync/base/model_type.h"
 #include "components/sync/driver/sync_service.h"
+#include "components/sync/driver/sync_user_settings.h"
 #include "ios/chrome/browser/application_context.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
 #include "ios/chrome/browser/chrome_url_constants.h"
@@ -763,7 +764,7 @@
   UIViewController<SettingsRootViewControlling>* controllerToPush;
   // If there was a sync error, prompt the user to enter the passphrase.
   // Otherwise, show the full encryption options.
-  if (syncService->IsPassphraseRequired()) {
+  if (syncService->GetUserSettings()->IsPassphraseRequired()) {
     controllerToPush = [[SyncEncryptionPassphraseTableViewController alloc]
         initWithBrowserState:_browserState];
   } else {
diff --git a/ios/chrome/browser/ui/settings/translate_table_view_controller.mm b/ios/chrome/browser/ui/settings/translate_table_view_controller.mm
index c636337..a4a1bb2 100644
--- a/ios/chrome/browser/ui/settings/translate_table_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/translate_table_view_controller.mm
@@ -18,8 +18,8 @@
 #import "ios/chrome/browser/ui/settings/cells/settings_cells_constants.h"
 #import "ios/chrome/browser/ui/settings/cells/settings_switch_cell.h"
 #import "ios/chrome/browser/ui/settings/cells/settings_switch_item.h"
-#import "ios/chrome/browser/ui/settings/settings_utils.h"
 #import "ios/chrome/browser/ui/settings/utils/pref_backed_boolean.h"
+#import "ios/chrome/browser/ui/settings/utils/settings_utils.h"
 #import "ios/chrome/browser/ui/table_view/cells/table_view_detail_text_item.h"
 #import "ios/chrome/browser/ui/table_view/cells/table_view_link_header_footer_item.h"
 #import "ios/chrome/browser/ui/table_view/table_view_model.h"
diff --git a/ios/chrome/browser/ui/settings/utils/BUILD.gn b/ios/chrome/browser/ui/settings/utils/BUILD.gn
index 2e4473c..e295cac 100644
--- a/ios/chrome/browser/ui/settings/utils/BUILD.gn
+++ b/ios/chrome/browser/ui/settings/utils/BUILD.gn
@@ -10,12 +10,16 @@
     "observable_boolean.h",
     "pref_backed_boolean.h",
     "pref_backed_boolean.mm",
+    "settings_utils.h",
+    "settings_utils.mm",
   ]
   deps = [
     "//base",
     "//components/content_settings/core/browser",
     "//components/content_settings/core/common",
     "//components/prefs",
+    "//ios/chrome/browser",
+    "//ios/chrome/browser/ui/commands",
   ]
 }
 
diff --git a/ios/chrome/browser/ui/settings/settings_utils.h b/ios/chrome/browser/ui/settings/utils/settings_utils.h
similarity index 73%
rename from ios/chrome/browser/ui/settings/settings_utils.h
rename to ios/chrome/browser/ui/settings/utils/settings_utils.h
index a795c3b..f8a8890 100644
--- a/ios/chrome/browser/ui/settings/settings_utils.h
+++ b/ios/chrome/browser/ui/settings/utils/settings_utils.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 IOS_CHROME_BROWSER_UI_SETTINGS_SETTINGS_UTILS_H_
-#define IOS_CHROME_BROWSER_UI_SETTINGS_SETTINGS_UTILS_H_
+#ifndef IOS_CHROME_BROWSER_UI_SETTINGS_UTILS_SETTINGS_UTILS_H_
+#define IOS_CHROME_BROWSER_UI_SETTINGS_UTILS_SETTINGS_UTILS_H_
 
 #import <UIKit/UIKit.h>
 
@@ -16,4 +16,4 @@
 ProceduralBlockWithURL BlockToOpenURL(UIResponder* responder,
                                       id<ApplicationCommands> dispatcher);
 
-#endif  // IOS_CHROME_BROWSER_UI_SETTINGS_SETTINGS_UTILS_H_
+#endif  // IOS_CHROME_BROWSER_UI_SETTINGS_UTILS_SETTINGS_UTILS_H_
diff --git a/ios/chrome/browser/ui/settings/settings_utils.mm b/ios/chrome/browser/ui/settings/utils/settings_utils.mm
similarity index 93%
rename from ios/chrome/browser/ui/settings/settings_utils.mm
rename to ios/chrome/browser/ui/settings/utils/settings_utils.mm
index ddbc84e1..d969bb2 100644
--- a/ios/chrome/browser/ui/settings/settings_utils.mm
+++ b/ios/chrome/browser/ui/settings/utils/settings_utils.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/chrome/browser/ui/settings/settings_utils.h"
+#import "ios/chrome/browser/ui/settings/utils/settings_utils.h"
 
 #import "ios/chrome/browser/ui/commands/application_commands.h"
 #import "ios/chrome/browser/ui/commands/open_new_tab_command.h"
diff --git a/ios/chrome/test/BUILD.gn b/ios/chrome/test/BUILD.gn
index f13c7ba..ab92131 100644
--- a/ios/chrome/test/BUILD.gn
+++ b/ios/chrome/test/BUILD.gn
@@ -225,9 +225,11 @@
     "//ios/chrome/browser/ui/sad_tab:unit_tests",
     "//ios/chrome/browser/ui/safe_mode:unit_tests",
     "//ios/chrome/browser/ui/settings:unit_tests",
+    "//ios/chrome/browser/ui/settings/autofill:unit_tests",
     "//ios/chrome/browser/ui/settings/cells:unit_tests",
     "//ios/chrome/browser/ui/settings/cells/legacy:unit_tests",
     "//ios/chrome/browser/ui/settings/clear_browsing_data:unit_tests",
+    "//ios/chrome/browser/ui/settings/password:unit_tests",
     "//ios/chrome/browser/ui/side_swipe:unit_tests",
     "//ios/chrome/browser/ui/snackbar:unit_tests",
     "//ios/chrome/browser/ui/static_content:unit_tests",
diff --git a/ios/chrome/test/app/BUILD.gn b/ios/chrome/test/app/BUILD.gn
index 6f34eac..9ffc32b 100644
--- a/ios/chrome/test/app/BUILD.gn
+++ b/ios/chrome/test/app/BUILD.gn
@@ -73,6 +73,8 @@
     "//ios/chrome/browser/ui/settings",
     "//ios/chrome/browser/ui/settings:settings_root",
     "//ios/chrome/browser/ui/settings:test_support",
+    "//ios/chrome/browser/ui/settings/password",
+    "//ios/chrome/browser/ui/settings/password:test_support",
     "//ios/chrome/browser/ui/static_content",
     "//ios/chrome/browser/ui/tabs",
     "//ios/chrome/browser/ui/util",
diff --git a/ios/chrome/test/app/password_test_util.h b/ios/chrome/test/app/password_test_util.h
index cbaf0167..ab65c268 100644
--- a/ios/chrome/test/app/password_test_util.h
+++ b/ios/chrome/test/app/password_test_util.h
@@ -5,7 +5,7 @@
 #ifndef IOS_CHROME_TEST_APP_PASSWORD_TEST_UTIL_H_
 #define IOS_CHROME_TEST_APP_PASSWORD_TEST_UTIL_H_
 
-#import "ios/chrome/browser/ui/settings/reauthentication_module.h"
+#import "ios/chrome/browser/ui/settings/password/reauthentication_module.h"
 
 @interface MockReauthenticationModule : NSObject<ReauthenticationProtocol>
 
diff --git a/ios/chrome/test/app/password_test_util.mm b/ios/chrome/test/app/password_test_util.mm
index 58b5a31..740e094 100644
--- a/ios/chrome/test/app/password_test_util.mm
+++ b/ios/chrome/test/app/password_test_util.mm
@@ -5,8 +5,8 @@
 #include "ios/chrome/test/app/password_test_util.h"
 
 #include "base/mac/foundation_util.h"
-#import "ios/chrome/browser/ui/settings/password_details_table_view_controller+testing.h"
-#import "ios/chrome/browser/ui/settings/passwords_table_view_controller.h"
+#import "ios/chrome/browser/ui/settings/password/password_details_table_view_controller+testing.h"
+#import "ios/chrome/browser/ui/settings/password/passwords_table_view_controller.h"
 #import "ios/chrome/browser/ui/settings/settings_navigation_controller.h"
 #import "ios/chrome/browser/ui/util/top_view_controller.h"
 
diff --git a/ios/chrome/test/earl_grey/BUILD.gn b/ios/chrome/test/earl_grey/BUILD.gn
index b8cb504..e9a20c3a 100644
--- a/ios/chrome/test/earl_grey/BUILD.gn
+++ b/ios/chrome/test/earl_grey/BUILD.gn
@@ -70,7 +70,9 @@
 chrome_ios_eg_test("ios_chrome_settings_egtests") {
   deps = [
     "//ios/chrome/browser/ui/settings:eg_tests",
+    "//ios/chrome/browser/ui/settings/autofill:eg_tests",
     "//ios/chrome/browser/ui/settings/clear_browsing_data:eg_tests",
+    "//ios/chrome/browser/ui/settings/password:eg_tests",
   ]
 }
 
diff --git a/media/filters/chunk_demuxer.cc b/media/filters/chunk_demuxer.cc
index c29a7a95..39cc4a12 100644
--- a/media/filters/chunk_demuxer.cc
+++ b/media/filters/chunk_demuxer.cc
@@ -154,6 +154,9 @@
 }
 
 bool ChunkDemuxerStream::Append(const StreamParser::BufferQueue& buffers) {
+  if (append_observer_cb_)
+    append_observer_cb_.Run(&buffers);
+
   if (buffers.empty())
     return false;
 
@@ -258,6 +261,10 @@
   DVLOG(2) << "ChunkDemuxerStream::OnStartOfCodedFrameGroup(dts "
            << start_dts.InSecondsF() << ", pts " << start_pts.InSecondsF()
            << ")";
+
+  if (group_start_observer_cb_)
+    group_start_observer_cb_.Run(start_dts, start_pts);
+
   base::AutoLock auto_lock(lock_);
   SBSTREAM_OP(OnStartOfCodedFrameGroup(start_dts, start_pts));
 }
diff --git a/media/filters/chunk_demuxer.h b/media/filters/chunk_demuxer.h
index 5d085b1..5cd48c0 100644
--- a/media/filters/chunk_demuxer.h
+++ b/media/filters/chunk_demuxer.h
@@ -153,6 +153,20 @@
 
   MediaTrack::Id media_track_id() const { return media_track_id_; }
 
+  // Allows tests to verify invocations of Append().
+  using AppendObserverCB = base::RepeatingCallback<void(const BufferQueue*)>;
+  void set_append_observer_for_testing(AppendObserverCB append_observer_cb) {
+    append_observer_cb_ = std::move(append_observer_cb);
+  }
+
+  // Allows tests to verify invocations of OnStartOfCodedFrameGroup().
+  using GroupStartObserverCB =
+      base::RepeatingCallback<void(DecodeTimestamp, base::TimeDelta)>;
+  void set_group_start_observer_for_testing(
+      GroupStartObserverCB group_start_observer_cb) {
+    group_start_observer_cb_ = std::move(group_start_observer_cb);
+  }
+
  private:
   enum State {
     UNINITIALIZED,
@@ -181,6 +195,9 @@
 
   const MediaTrack::Id media_track_id_;
 
+  AppendObserverCB append_observer_cb_;
+  GroupStartObserverCB group_start_observer_cb_;
+
   mutable base::Lock lock_;
   State state_ GUARDED_BY(lock_);
   ReadCB read_cb_ GUARDED_BY(lock_);
diff --git a/media/filters/frame_processor.cc b/media/filters/frame_processor.cc
index 7549370..cacb304 100644
--- a/media/filters/frame_processor.cc
+++ b/media/filters/frame_processor.cc
@@ -949,13 +949,14 @@
 
       // When buffering by PTS intervals and an otherwise continuous coded frame
       // group (by DTS, and with non-decreasing keyframe PTS) contains a
-      // keyframe with PTS in the future, signal a new coded frame group with
+      // keyframe with PTS in the future significantly far enough that it may be
+      // outside of buffering fudge room, signal a new coded frame group with
       // start time set to the previous highest frame end time in the coded
       // frame group for this track. This lets the stream coalesce a potential
       // gap, and also pass internal buffer adjacency checks.
       signal_new_cfg |=
           track_buffer->highest_presentation_timestamp() != kNoTimestamp &&
-          track_buffer->highest_presentation_timestamp() <
+          track_buffer->highest_presentation_timestamp() + frame->duration() <
               presentation_timestamp;
     }
 
diff --git a/media/filters/frame_processor_unittest.cc b/media/filters/frame_processor_unittest.cc
index 20969c2..a616c780 100644
--- a/media/filters/frame_processor_unittest.cc
+++ b/media/filters/frame_processor_unittest.cc
@@ -28,6 +28,7 @@
 #include "media/filters/frame_processor.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+using ::testing::_;
 using ::testing::InSequence;
 using ::testing::StrictMock;
 using ::testing::Values;
@@ -79,6 +80,14 @@
     ASSERT_NE(kInfiniteDuration, new_duration);
   }
 
+  MOCK_METHOD2(OnAppend,
+               void(const DemuxerStream::Type type,
+                    const BufferQueue* buffers));
+  MOCK_METHOD3(OnGroupStart,
+               void(const DemuxerStream::Type type,
+                    DecodeTimestamp start_dts,
+                    base::TimeDelta start_pts));
+
  private:
   DISALLOW_COPY_AND_ASSIGN(FrameProcessorTestCallbackHelper);
 };
@@ -107,7 +116,8 @@
 
   enum StreamFlags {
     HAS_AUDIO = 1 << 0,
-    HAS_VIDEO = 1 << 1
+    HAS_VIDEO = 1 << 1,
+    OBSERVE_APPENDS_AND_GROUP_STARTS = 1 << 2
   };
 
   void AddTestTracks(int stream_flags) {
@@ -115,14 +125,17 @@
     const bool has_video = (stream_flags & HAS_VIDEO) != 0;
     ASSERT_TRUE(has_audio || has_video);
 
+    const bool setup_observers =
+        (stream_flags & OBSERVE_APPENDS_AND_GROUP_STARTS) != 0;
+
     if (has_audio) {
-      CreateAndConfigureStream(DemuxerStream::AUDIO);
+      CreateAndConfigureStream(DemuxerStream::AUDIO, setup_observers);
       ASSERT_TRUE(audio_);
       EXPECT_TRUE(frame_processor_->AddTrack(audio_id_, audio_.get()));
       SeekStream(audio_.get(), Milliseconds(0));
     }
     if (has_video) {
-      CreateAndConfigureStream(DemuxerStream::VIDEO);
+      CreateAndConfigureStream(DemuxerStream::VIDEO, setup_observers);
       ASSERT_TRUE(video_);
       EXPECT_TRUE(frame_processor_->AddTrack(video_id_, video_.get()));
       SeekStream(video_.get(), Milliseconds(0));
@@ -341,8 +354,11 @@
     last_read_buffer_ = buffer;
   }
 
-  void CreateAndConfigureStream(DemuxerStream::Type type) {
+  void CreateAndConfigureStream(DemuxerStream::Type type,
+                                bool setup_observers) {
     // TODO(wolenetz/dalecurtis): Also test with splicing disabled?
+
+    ChunkDemuxerStream* stream;
     switch (type) {
       case DemuxerStream::AUDIO: {
         ASSERT_FALSE(audio_);
@@ -354,6 +370,8 @@
         frame_processor_->OnPossibleAudioConfigUpdate(decoder_config);
         ASSERT_TRUE(
             audio_->UpdateAudioConfig(decoder_config, false, &media_log_));
+
+        stream = audio_.get();
         break;
       }
       case DemuxerStream::VIDEO: {
@@ -362,6 +380,7 @@
             new ChunkDemuxerStream(DemuxerStream::VIDEO, "2", range_api_));
         ASSERT_TRUE(video_->UpdateVideoConfig(TestVideoConfig::Normal(), false,
                                               &media_log_));
+        stream = video_.get();
         break;
       }
       // TODO(wolenetz): Test text coded frame processing.
@@ -370,6 +389,15 @@
         ASSERT_FALSE(true);
       }
     }
+
+    if (setup_observers) {
+      stream->set_append_observer_for_testing(
+          base::BindRepeating(&FrameProcessorTestCallbackHelper::OnAppend,
+                              base::Unretained(&callbacks_), type));
+      stream->set_group_start_observer_for_testing(
+          base::BindRepeating(&FrameProcessorTestCallbackHelper::OnGroupStart,
+                              base::Unretained(&callbacks_), type));
+    }
   }
 
   DISALLOW_COPY_AND_ASSIGN(FrameProcessorTest);
@@ -1331,7 +1359,7 @@
   }
 
   InSequence s;
-  AddTestTracks(HAS_VIDEO);
+  AddTestTracks(HAS_VIDEO | OBSERVE_APPENDS_AND_GROUP_STARTS);
   frame_processor_->SetSequenceMode(use_sequence_mode_);
 
   // Make the sequence mode buffering appear just like segments mode to simplify
@@ -1339,32 +1367,40 @@
   if (use_sequence_mode_)
     SetTimestampOffset(Milliseconds(1060));
 
+  // Note that the PTS of GOP non-keyframes earlier than the keyframe doesn't
+  // modify the GOP start of the buffered range here. This may change if we
+  // decide to improve spec for SAP Type 2 GOPs that begin a coded frame group.
+  EXPECT_CALL(callbacks_, OnGroupStart(DemuxerStream::VIDEO, DecodeTimestamp(),
+                                       Milliseconds(1060)));
   EXPECT_CALL(callbacks_,
               OnParseWarning(
                   SourceBufferParseWarning::kKeyframeTimeGreaterThanDependant));
   EXPECT_MEDIA_LOG(KeyframeTimeGreaterThanDependant("1.06", "1"));
+  EXPECT_CALL(callbacks_, OnAppend(DemuxerStream::VIDEO, _));
   EXPECT_CALL(callbacks_, PossibleDurationIncrease(Milliseconds(1070)));
   EXPECT_TRUE(ProcessFrames(
       "", "1060|0K 1000|10 1050|20 1010|30 1040|40 1020|50 1030|60"));
   EXPECT_EQ(Milliseconds(0), timestamp_offset_);
-
-  // Note that the PTS of GOP non-keyframes earlier than the keyframe doesn't
-  // modify the GOP start of the buffered range here. This may change if we
-  // decide to improve spec for SAP Type 2 GOPs that begin a coded frame group.
   CheckExpectedRangesByTimestamp(video_.get(), "{ [1060,1070) }");
 
   // Process just the keyframe of the next SAP Type 2 GOP in decode continuity
   // with the previous one.
+  // Note that this second GOP is buffered continuous with the first because
+  // there is no decode discontinuity detected. This results in inclusion of
+  // the significant PTS jump forward in the same continuous range.
+  EXPECT_CALL(
+      callbacks_,
+      OnGroupStart(DemuxerStream::VIDEO,
+                   DecodeTimestamp::FromPresentationTime(Milliseconds(60)),
+                   Milliseconds(1070)));
+  EXPECT_CALL(callbacks_, OnAppend(DemuxerStream::VIDEO, _));
   EXPECT_CALL(callbacks_, PossibleDurationIncrease(Milliseconds(1140)));
   EXPECT_TRUE(ProcessFrames("", "1130|70K"));
   EXPECT_EQ(Milliseconds(0), timestamp_offset_);
-
-  // Note that the second GOP is buffered continuous with the first because
-  // there was no decode discontinuity detected. This results in inclusion of
-  // the significant PTS jump forward in the same continuous range.
   CheckExpectedRangesByTimestamp(video_.get(), "{ [1060,1140) }");
 
   // Process the remainder of the second GOP.
+  EXPECT_CALL(callbacks_, OnAppend(DemuxerStream::VIDEO, _));
   EXPECT_CALL(callbacks_, PossibleDurationIncrease(Milliseconds(1140)));
   EXPECT_TRUE(
       ProcessFrames("", "1070|80 1120|90 1080|100 1110|110 1090|120 1100|130"));
@@ -1393,7 +1429,7 @@
   }
 
   InSequence s;
-  AddTestTracks(HAS_VIDEO);
+  AddTestTracks(HAS_VIDEO | OBSERVE_APPENDS_AND_GROUP_STARTS);
   frame_processor_->SetSequenceMode(use_sequence_mode_);
 
   // Make the sequence mode buffering appear just like segments mode to simplify
@@ -1401,10 +1437,19 @@
   if (use_sequence_mode_)
     SetTimestampOffset(Milliseconds(100));
 
+  EXPECT_CALL(callbacks_, OnGroupStart(DemuxerStream::VIDEO, DecodeTimestamp(),
+                                       Milliseconds(100)));
+  EXPECT_CALL(callbacks_, OnAppend(DemuxerStream::VIDEO, _));
   EXPECT_CALL(callbacks_, PossibleDurationIncrease(Milliseconds(140)));
   EXPECT_TRUE(ProcessFrames("", "100|0K 110|10 120|20 130|30"));
   EXPECT_EQ(Milliseconds(0), timestamp_offset_);
 
+  EXPECT_CALL(
+      callbacks_,
+      OnGroupStart(DemuxerStream::VIDEO,
+                   DecodeTimestamp::FromPresentationTime(Milliseconds(30)),
+                   Milliseconds(125)));
+  EXPECT_CALL(callbacks_, OnAppend(DemuxerStream::VIDEO, _));
   EXPECT_CALL(callbacks_, PossibleDurationIncrease(Milliseconds(165)));
   EXPECT_TRUE(ProcessFrames("", "125|40K 135|50 145|60 155|70"));
   EXPECT_EQ(Milliseconds(0), timestamp_offset_);
@@ -1425,7 +1470,7 @@
   }
 
   InSequence s;
-  AddTestTracks(HAS_VIDEO);
+  AddTestTracks(HAS_VIDEO | OBSERVE_APPENDS_AND_GROUP_STARTS);
   frame_processor_->SetSequenceMode(use_sequence_mode_);
 
   // Make the sequence mode buffering appear just like segments mode to simplify
@@ -1433,10 +1478,19 @@
   if (use_sequence_mode_)
     SetTimestampOffset(Milliseconds(100));
 
+  EXPECT_CALL(callbacks_, OnGroupStart(DemuxerStream::VIDEO, DecodeTimestamp(),
+                                       Milliseconds(100)));
+  EXPECT_CALL(callbacks_, OnAppend(DemuxerStream::VIDEO, _));
   EXPECT_CALL(callbacks_, PossibleDurationIncrease(Milliseconds(140)));
   EXPECT_TRUE(ProcessFrames("", "100|0K 110|10 120|20K 130|30"));
   EXPECT_EQ(Milliseconds(0), timestamp_offset_);
 
+  EXPECT_CALL(
+      callbacks_,
+      OnGroupStart(DemuxerStream::VIDEO,
+                   DecodeTimestamp::FromPresentationTime(Milliseconds(30)),
+                   Milliseconds(115)));
+  EXPECT_CALL(callbacks_, OnAppend(DemuxerStream::VIDEO, _));
   // TODO(wolenetz): Duration shouldn't be allowed to possibly increase to 140ms
   // here. See https://crbug.com/763620.
   EXPECT_CALL(callbacks_, PossibleDurationIncrease(Milliseconds(140)));
@@ -1459,7 +1513,7 @@
   }
 
   InSequence s;
-  AddTestTracks(HAS_VIDEO);
+  AddTestTracks(HAS_VIDEO | OBSERVE_APPENDS_AND_GROUP_STARTS);
   frame_processor_->SetSequenceMode(use_sequence_mode_);
 
   // Make the sequence mode buffering appear just like segments mode to simplify
@@ -1467,14 +1521,21 @@
   if (use_sequence_mode_)
     SetTimestampOffset(Milliseconds(120));
 
+  EXPECT_CALL(callbacks_, OnGroupStart(DemuxerStream::VIDEO, DecodeTimestamp(),
+                                       Milliseconds(120)));
   EXPECT_CALL(callbacks_,
               OnParseWarning(
                   SourceBufferParseWarning::kKeyframeTimeGreaterThanDependant));
   EXPECT_MEDIA_LOG(KeyframeTimeGreaterThanDependant("0.12", "0.1"));
+  EXPECT_CALL(callbacks_, OnAppend(DemuxerStream::VIDEO, _));
   EXPECT_CALL(callbacks_, PossibleDurationIncrease(Milliseconds(140)));
   EXPECT_TRUE(ProcessFrames("", "120|0K 100|10 130|20 110|30"));
   EXPECT_EQ(Milliseconds(0), timestamp_offset_);
 
+  // Note, we *don't* expect another OnGroupStart during the next ProcessFrames,
+  // since the next GOP's keyframe PTS is after the first GOP and close enough
+  // to be assured adjacent.
+  EXPECT_CALL(callbacks_, OnAppend(DemuxerStream::VIDEO, _));
   EXPECT_CALL(callbacks_, PossibleDurationIncrease(Milliseconds(165)));
   EXPECT_TRUE(ProcessFrames("", "145|40K 125|50 155|60 135|70"));
   EXPECT_EQ(Milliseconds(0), timestamp_offset_);
@@ -1499,7 +1560,7 @@
   }
 
   InSequence s;
-  AddTestTracks(HAS_VIDEO);
+  AddTestTracks(HAS_VIDEO | OBSERVE_APPENDS_AND_GROUP_STARTS);
   frame_processor_->SetSequenceMode(use_sequence_mode_);
 
   // Make the sequence mode buffering appear just like segments mode to simplify
@@ -1507,15 +1568,33 @@
   if (use_sequence_mode_)
     SetTimestampOffset(Milliseconds(120));
 
+  EXPECT_CALL(callbacks_, OnGroupStart(DemuxerStream::VIDEO, DecodeTimestamp(),
+                                       Milliseconds(120)));
   EXPECT_CALL(callbacks_,
               OnParseWarning(
                   SourceBufferParseWarning::kKeyframeTimeGreaterThanDependant));
   EXPECT_MEDIA_LOG(KeyframeTimeGreaterThanDependant("0.12", "0.1"));
+  EXPECT_CALL(callbacks_, OnAppend(DemuxerStream::VIDEO, _));
+  // There is a second GOP that is SAP-Type-2 within this first ProcessFrames,
+  // with PTS jumping forward far enough to trigger group start signalling and a
+  // flush.
+  EXPECT_CALL(
+      callbacks_,
+      OnGroupStart(DemuxerStream::VIDEO,
+                   DecodeTimestamp::FromPresentationTime(Milliseconds(30)),
+                   Milliseconds(140)));
+  EXPECT_CALL(callbacks_, OnAppend(DemuxerStream::VIDEO, _));
   EXPECT_CALL(callbacks_, PossibleDurationIncrease(Milliseconds(180)));
   EXPECT_TRUE(ProcessFrames(
       "", "120|0K 100|10 130|20 110|30 160|40K 140|50 170|60 150|70"));
   EXPECT_EQ(Milliseconds(0), timestamp_offset_);
 
+  EXPECT_CALL(
+      callbacks_,
+      OnGroupStart(DemuxerStream::VIDEO,
+                   DecodeTimestamp::FromPresentationTime(Milliseconds(70)),
+                   Milliseconds(155)));
+  EXPECT_CALL(callbacks_, OnAppend(DemuxerStream::VIDEO, _));
   // TODO(wolenetz): Duration shouldn't be allowed to possibly increase to 180ms
   // here. See https://crbug.com/763620.
   EXPECT_CALL(callbacks_, PossibleDurationIncrease(Milliseconds(180)));
@@ -1549,7 +1628,7 @@
   }
 
   InSequence s;
-  AddTestTracks(HAS_VIDEO);
+  AddTestTracks(HAS_VIDEO | OBSERVE_APPENDS_AND_GROUP_STARTS);
   frame_processor_->SetSequenceMode(use_sequence_mode_);
 
   // Make the sequence mode buffering appear just like segments mode to simplify
@@ -1557,11 +1636,20 @@
   if (use_sequence_mode_)
     SetTimestampOffset(Milliseconds(200));
 
+  EXPECT_CALL(callbacks_, OnGroupStart(DemuxerStream::VIDEO, DecodeTimestamp(),
+                                       Milliseconds(200)));
+  EXPECT_CALL(callbacks_, OnAppend(DemuxerStream::VIDEO, _));
   EXPECT_CALL(callbacks_, PossibleDurationIncrease(Milliseconds(240)));
   EXPECT_TRUE(ProcessFrames("", "200|0K 210|10 220|20 230|30"));
   EXPECT_EQ(Milliseconds(0), timestamp_offset_);
   CheckExpectedRangesByTimestamp(video_.get(), "{ [200,240) }");
 
+  EXPECT_CALL(
+      callbacks_,
+      OnGroupStart(DemuxerStream::VIDEO,
+                   DecodeTimestamp::FromPresentationTime(Milliseconds(30)),
+                   Milliseconds(100)));
+  EXPECT_CALL(callbacks_, OnAppend(DemuxerStream::VIDEO, _));
   // TODO(wolenetz): Duration shouldn't be allowed to possibly increase to 240ms
   // here. See https://crbug.com/763620.
   EXPECT_CALL(callbacks_, PossibleDurationIncrease(Milliseconds(240)));
@@ -1569,6 +1657,12 @@
   EXPECT_EQ(Milliseconds(0), timestamp_offset_);
   CheckExpectedRangesByTimestamp(video_.get(), "{ [100,140) [200,240) }");
 
+  EXPECT_CALL(
+      callbacks_,
+      OnGroupStart(DemuxerStream::VIDEO,
+                   DecodeTimestamp::FromPresentationTime(Milliseconds(70)),
+                   Milliseconds(140)));
+  EXPECT_CALL(callbacks_, OnAppend(DemuxerStream::VIDEO, _));
   EXPECT_CALL(callbacks_, PossibleDurationIncrease(Milliseconds(260)));
   EXPECT_TRUE(ProcessFrames("", "240|80K 250|90"));
   EXPECT_EQ(Milliseconds(0), timestamp_offset_);
@@ -1588,7 +1682,7 @@
   // the append sequence is required to have monotonically increasing DTS (even
   // across GOPs).
   InSequence s;
-  AddTestTracks(HAS_VIDEO);
+  AddTestTracks(HAS_VIDEO | OBSERVE_APPENDS_AND_GROUP_STARTS);
   frame_processor_->SetSequenceMode(use_sequence_mode_);
 
   // Make the sequence mode buffering appear just like segments mode to simplify
@@ -1596,14 +1690,25 @@
   if (use_sequence_mode_)
     SetTimestampOffset(Milliseconds(200));
 
+  EXPECT_CALL(
+      callbacks_,
+      OnGroupStart(DemuxerStream::VIDEO,
+                   DecodeTimestamp::FromPresentationTime(Milliseconds(200)),
+                   Milliseconds(200)));
+  EXPECT_CALL(callbacks_, OnAppend(DemuxerStream::VIDEO, _));
   EXPECT_CALL(callbacks_, PossibleDurationIncrease(Milliseconds(240)));
   EXPECT_TRUE(ProcessFrames("", "200K 210 220 230"));
   EXPECT_EQ(Milliseconds(0), timestamp_offset_);
   CheckExpectedRangesByTimestamp(video_.get(), "{ [200,240) }");
 
+  EXPECT_CALL(
+      callbacks_,
+      OnGroupStart(DemuxerStream::VIDEO,
+                   DecodeTimestamp::FromPresentationTime(Milliseconds(225)),
+                   Milliseconds(240)));
+  EXPECT_CALL(callbacks_, OnAppend(DemuxerStream::VIDEO, _));
   // Note that duration is reported based on PTS regardless of buffering model.
   EXPECT_CALL(callbacks_, PossibleDurationIncrease(Milliseconds(280)));
-
   // Append a second GOP whose first DTS is below the last DTS of the first GOP,
   // but whose PTS interval is continuous with the end of the first GOP.
   EXPECT_TRUE(ProcessFrames("", "240|225K 250|235 260|245 270|255"));
@@ -1621,6 +1726,108 @@
   }
 }
 
+TEST_P(FrameProcessorTest, OnlyKeyframes_ContinuousDts_ContinousPts_1) {
+  // Verifies that precisely one group start and one stream append occurs for a
+  // single continuous set of frames.
+  InSequence s;
+  AddTestTracks(HAS_AUDIO | OBSERVE_APPENDS_AND_GROUP_STARTS);
+  if (use_sequence_mode_)
+    frame_processor_->SetSequenceMode(true);
+
+  // Default test frame duration is 10 milliseconds.
+
+  EXPECT_CALL(callbacks_, OnGroupStart(DemuxerStream::AUDIO, DecodeTimestamp(),
+                                       base::TimeDelta()));
+  EXPECT_CALL(callbacks_, OnAppend(DemuxerStream::AUDIO, _));
+  EXPECT_CALL(callbacks_, PossibleDurationIncrease(Milliseconds(40)));
+  EXPECT_TRUE(ProcessFrames("0K 10K 20K 30K", ""));
+  EXPECT_EQ(Milliseconds(0), timestamp_offset_);
+
+  CheckExpectedRangesByTimestamp(audio_.get(), "{ [0,40) }");
+  CheckReadsThenReadStalls(audio_.get(), "0 10 20 30");
+}
+
+TEST_P(FrameProcessorTest, OnlyKeyframes_ContinuousDts_ContinuousPts_2) {
+  // Verifies that precisely one group start and one stream append occurs while
+  // processing a single continuous set of frames that uses fudge room to just
+  // barely remain adjacent.
+  InSequence s;
+  AddTestTracks(HAS_AUDIO | OBSERVE_APPENDS_AND_GROUP_STARTS);
+  if (use_sequence_mode_)
+    frame_processor_->SetSequenceMode(true);
+
+  frame_duration_ = Milliseconds(5);
+
+  EXPECT_CALL(callbacks_, OnGroupStart(DemuxerStream::AUDIO, DecodeTimestamp(),
+                                       base::TimeDelta()));
+  EXPECT_CALL(callbacks_, OnAppend(DemuxerStream::AUDIO, _));
+  EXPECT_CALL(callbacks_, PossibleDurationIncrease(Milliseconds(35)));
+  EXPECT_TRUE(ProcessFrames("0K 10K 20K 30K", ""));
+  EXPECT_EQ(Milliseconds(0), timestamp_offset_);
+
+  CheckExpectedRangesByTimestamp(audio_.get(), "{ [0,35) }");
+  CheckReadsThenReadStalls(audio_.get(), "0 10 20 30");
+}
+
+TEST_P(FrameProcessorTest,
+       OnlyKeyframes_ContinuousDts_DiscontinuousPtsJustBeyondFudgeRoom) {
+  // Verifies that, in ByPts, multiple group starts and distinct appends occur
+  // when processing a single DTS-continuous set of frames with PTS deltas that
+  // just barely exceed the adjacency assumption in FrameProcessor.
+  // Verifies that, in ByDts, precisely one group start and one stream append
+  // occur.
+  InSequence s;
+  AddTestTracks(HAS_AUDIO | OBSERVE_APPENDS_AND_GROUP_STARTS);
+  if (use_sequence_mode_)
+    frame_processor_->SetSequenceMode(true);
+
+  frame_duration_ = base::TimeDelta::FromMicroseconds(4999);
+
+  EXPECT_CALL(callbacks_, OnGroupStart(DemuxerStream::AUDIO, DecodeTimestamp(),
+                                       base::TimeDelta()));
+  EXPECT_CALL(callbacks_, OnAppend(DemuxerStream::AUDIO, _));
+  if (range_api_ == ChunkDemuxerStream::RangeApi::kNewByPts) {
+    // Frame "10|5K" following "0K" triggers start of new group and eventual
+    // append.
+    EXPECT_CALL(callbacks_, OnGroupStart(DemuxerStream::AUDIO,
+                                         DecodeTimestamp(), frame_duration_));
+    EXPECT_CALL(callbacks_, OnAppend(DemuxerStream::AUDIO, _));
+
+    // Frame "20|10K" following "10|5K" triggers start of new group and eventual
+    // append.
+    EXPECT_CALL(
+        callbacks_,
+        OnGroupStart(DemuxerStream::AUDIO,
+                     DecodeTimestamp::FromPresentationTime(Milliseconds(5)),
+                     Milliseconds(10) + frame_duration_));
+    EXPECT_CALL(callbacks_, OnAppend(DemuxerStream::AUDIO, _));
+
+    // Frame "30|15K" following "20|10K" triggers start of new group and
+    // eventual append.
+    EXPECT_CALL(
+        callbacks_,
+        OnGroupStart(DemuxerStream::AUDIO,
+                     DecodeTimestamp::FromPresentationTime(Milliseconds(10)),
+                     Milliseconds(20) + frame_duration_));
+    EXPECT_CALL(callbacks_, OnAppend(DemuxerStream::AUDIO, _));
+  }
+  EXPECT_CALL(callbacks_, PossibleDurationIncrease(
+                              base::TimeDelta::FromMicroseconds(34999)));
+  EXPECT_TRUE(ProcessFrames("0K 10|5K 20|10K 30|15K", ""));
+  EXPECT_EQ(Milliseconds(0), timestamp_offset_);
+
+  if (range_api_ == ChunkDemuxerStream::RangeApi::kNewByPts) {
+    // Note that the ByPts result is still buffered continuous since DTS was
+    // continuous and PTS was monotonically increasing (such that each group
+    // start was signalled by FrameProcessor to be continuous with the end of
+    // the previous group, if any.)
+    CheckExpectedRangesByTimestamp(audio_.get(), "{ [0,34) }");
+  } else {
+    CheckExpectedRangesByTimestamp(audio_.get(), "{ [0,19) }");
+  }
+  CheckReadsThenReadStalls(audio_.get(), "0 10 20 30");
+}
+
 INSTANTIATE_TEST_CASE_P(SequenceModeLegacyByDts,
                         FrameProcessorTest,
                         Values(FrameProcessorTestParams(
diff --git a/media/gpu/android/codec_image.cc b/media/gpu/android/codec_image.cc
index 9ce0109..05eaef9 100644
--- a/media/gpu/android/codec_image.cc
+++ b/media/gpu/android/codec_image.cc
@@ -20,14 +20,23 @@
 // Makes |texture_owner|'s context current if it isn't already.
 std::unique_ptr<ui::ScopedMakeCurrent> MakeCurrentIfNeeded(
     TextureOwner* texture_owner) {
+  gl::GLContext* context = texture_owner->GetContext();
   // Note: this works for virtual contexts too, because IsCurrent() returns true
   // if their shared platform context is current, regardless of which virtual
   // context is current.
-  return std::unique_ptr<ui::ScopedMakeCurrent>(
-      texture_owner->GetContext()->IsCurrent(nullptr)
-          ? nullptr
-          : new ui::ScopedMakeCurrent(texture_owner->GetContext(),
-                                      texture_owner->GetSurface()));
+  if (context->IsCurrent(nullptr))
+    return nullptr;
+
+  auto scoped_current = std::make_unique<ui::ScopedMakeCurrent>(
+      context, texture_owner->GetSurface());
+  // Log an error if ScopedMakeCurrent failed for debugging
+  // https://crbug.com/878042.
+  // TODO(ericrk): Remove this once debugging is completed.
+  if (!context->IsCurrent(nullptr)) {
+    LOG(ERROR) << "Failed to make context current in CodecImage. Subsequent "
+                  "UpdateTexImage may fail.";
+  }
+  return scoped_current;
 }
 
 }  // namespace
diff --git a/media/gpu/android/image_reader_gl_owner.cc b/media/gpu/android/image_reader_gl_owner.cc
index 690d5de..056864f 100644
--- a/media/gpu/android/image_reader_gl_owner.cc
+++ b/media/gpu/android/image_reader_gl_owner.cc
@@ -389,25 +389,29 @@
   const base::TimeDelta elapsed = call_time - release_time_;
   const base::TimeDelta remaining = max_wait - elapsed;
   release_time_ = base::TimeTicks();
+  bool timed_out = false;
 
   if (remaining <= base::TimeDelta()) {
     if (!frame_available_event_->event.IsSignaled()) {
       DVLOG(1) << "Deferred WaitForFrameAvailable() timed out, elapsed: "
                << elapsed.InMillisecondsF() << "ms";
+      timed_out = true;
     }
-    return;
+  } else {
+    DCHECK_LE(remaining, max_wait);
+    SCOPED_UMA_HISTOGRAM_TIMER(
+        "Media.CodecImage.ImageReaderGLOwner.WaitTimeForFrame");
+    if (!frame_available_event_->event.TimedWait(remaining)) {
+      DVLOG(1) << "WaitForFrameAvailable() timed out, elapsed: "
+               << elapsed.InMillisecondsF()
+               << "ms, additionally waited: " << remaining.InMillisecondsF()
+               << "ms, total: " << (elapsed + remaining).InMillisecondsF()
+               << "ms";
+      timed_out = true;
+    }
   }
-
-  DCHECK_LE(remaining, max_wait);
-  SCOPED_UMA_HISTOGRAM_TIMER(
-      "Media.CodecImage.ImageReaderGLOwner.WaitTimeForFrame");
-  if (!frame_available_event_->event.TimedWait(remaining)) {
-    DVLOG(1) << "WaitForFrameAvailable() timed out, elapsed: "
-             << elapsed.InMillisecondsF()
-             << "ms, additionally waited: " << remaining.InMillisecondsF()
-             << "ms, total: " << (elapsed + remaining).InMillisecondsF()
-             << "ms";
-  }
+  UMA_HISTOGRAM_BOOLEAN("Media.CodecImage.ImageReaderGLOwner.FrameTimedOut",
+                        timed_out);
 }
 
 ImageReaderGLOwner::ImageRef::ImageRef() = default;
diff --git a/media/gpu/android/surface_texture_gl_owner.cc b/media/gpu/android/surface_texture_gl_owner.cc
index 4258dc7..a692b2b4 100644
--- a/media/gpu/android/surface_texture_gl_owner.cc
+++ b/media/gpu/android/surface_texture_gl_owner.cc
@@ -126,25 +126,29 @@
   const base::TimeDelta elapsed = call_time - release_time_;
   const base::TimeDelta remaining = max_wait - elapsed;
   release_time_ = base::TimeTicks();
+  bool timed_out = false;
 
   if (remaining <= base::TimeDelta()) {
     if (!frame_available_event_->event.IsSignaled()) {
       DVLOG(1) << "Deferred WaitForFrameAvailable() timed out, elapsed: "
                << elapsed.InMillisecondsF() << "ms";
+      timed_out = true;
     }
-    return;
+  } else {
+    DCHECK_LE(remaining, max_wait);
+    SCOPED_UMA_HISTOGRAM_TIMER(
+        "Media.CodecImage.SurfaceTextureGLOwner.WaitTimeForFrame");
+    if (!frame_available_event_->event.TimedWait(remaining)) {
+      DVLOG(1) << "WaitForFrameAvailable() timed out, elapsed: "
+               << elapsed.InMillisecondsF()
+               << "ms, additionally waited: " << remaining.InMillisecondsF()
+               << "ms, total: " << (elapsed + remaining).InMillisecondsF()
+               << "ms";
+      timed_out = true;
+    }
   }
-
-  DCHECK_LE(remaining, max_wait);
-  SCOPED_UMA_HISTOGRAM_TIMER(
-      "Media.CodecImage.SurfaceTextureGLOwner.WaitTimeForFrame");
-  if (!frame_available_event_->event.TimedWait(remaining)) {
-    DVLOG(1) << "WaitForFrameAvailable() timed out, elapsed: "
-             << elapsed.InMillisecondsF()
-             << "ms, additionally waited: " << remaining.InMillisecondsF()
-             << "ms, total: " << (elapsed + remaining).InMillisecondsF()
-             << "ms";
-  }
+  UMA_HISTOGRAM_BOOLEAN("Media.CodecImage.SurfaceTextureGLOwner.FrameTimedOut",
+                        timed_out);
 }
 
 std::unique_ptr<base::android::ScopedHardwareBufferFenceSync>
diff --git a/net/third_party/quic/core/qpack/fuzzer/qpack_encoder_stream_receiver_fuzzer.cc b/net/third_party/quic/core/qpack/fuzzer/qpack_encoder_stream_receiver_fuzzer.cc
index 5869e6e..d7a0db4 100644
--- a/net/third_party/quic/core/qpack/fuzzer/qpack_encoder_stream_receiver_fuzzer.cc
+++ b/net/third_party/quic/core/qpack/fuzzer/qpack_encoder_stream_receiver_fuzzer.cc
@@ -28,7 +28,7 @@
   void OnInsertWithoutNameReference(QuicStringPiece name,
                                     QuicStringPiece value) override {}
   void OnDuplicate(uint64_t index) override {}
-  void OnDynamicTableSizeUpdate(uint64_t max_size) override {}
+  void OnSetDynamicTableCapacity(uint64_t capacity) override {}
   void OnErrorDetected(QuicStringPiece error_message) override {
     error_detected_ = true;
   }
diff --git a/net/third_party/quic/core/qpack/fuzzer/qpack_encoder_stream_sender_fuzzer.cc b/net/third_party/quic/core/qpack/fuzzer/qpack_encoder_stream_sender_fuzzer.cc
index 24f339bf..905815a5 100644
--- a/net/third_party/quic/core/qpack/fuzzer/qpack_encoder_stream_sender_fuzzer.cc
+++ b/net/third_party/quic/core/qpack/fuzzer/qpack_encoder_stream_sender_fuzzer.cc
@@ -65,8 +65,8 @@
         break;
       }
       case 3: {
-        uint64_t max_size = provider.ConsumeIntegral<uint64_t>();
-        sender.SendDynamicTableSizeUpdate(max_size);
+        uint64_t capacity = provider.ConsumeIntegral<uint64_t>();
+        sender.SendSetDynamicTableCapacity(capacity);
         break;
       }
     }
diff --git a/net/third_party/quic/core/qpack/qpack_constants.cc b/net/third_party/quic/core/qpack/qpack_constants.cc
index 2f97727..ad7ef51 100644
--- a/net/third_party/quic/core/qpack/qpack_constants.cc
+++ b/net/third_party/quic/core/qpack/qpack_constants.cc
@@ -69,7 +69,7 @@
   return instruction;
 }
 
-const QpackInstruction* DynamicTableSizeUpdateInstruction() {
+const QpackInstruction* SetDynamicTableCapacityInstruction() {
   static const QpackInstructionOpcode* const opcode =
       new QpackInstructionOpcode{0b00100000, 0b11100000};
   static const QpackInstruction* const instruction =
@@ -81,12 +81,12 @@
   static const QpackLanguage* const language = new QpackLanguage{
       InsertWithNameReferenceInstruction(),
       InsertWithoutNameReferenceInstruction(), DuplicateInstruction(),
-      DynamicTableSizeUpdateInstruction()};
+      SetDynamicTableCapacityInstruction()};
   ValidateLangague(language);
   return language;
 }
 
-const QpackInstruction* TableStateSynchronizeInstruction() {
+const QpackInstruction* InsertCountIncrementInstruction() {
   static const QpackInstructionOpcode* const opcode =
       new QpackInstructionOpcode{0b00000000, 0b11000000};
   static const QpackInstruction* const instruction =
@@ -112,7 +112,7 @@
 
 const QpackLanguage* QpackDecoderStreamLanguage() {
   static const QpackLanguage* const language = new QpackLanguage{
-      TableStateSynchronizeInstruction(), HeaderAcknowledgementInstruction(),
+      InsertCountIncrementInstruction(), HeaderAcknowledgementInstruction(),
       StreamCancellationInstruction()};
   ValidateLangague(language);
   return language;
diff --git a/net/third_party/quic/core/qpack/qpack_constants.h b/net/third_party/quic/core/qpack/qpack_constants.h
index 6e7cf89..5e6a31d8 100644
--- a/net/third_party/quic/core/qpack/qpack_constants.h
+++ b/net/third_party/quic/core/qpack/qpack_constants.h
@@ -30,14 +30,14 @@
 // literal consumes all bytes containing the field value.
 enum class QpackInstructionFieldType {
   // A single bit indicating whether the index refers to the static table, or
-  // indicating the sign of Delta Base Index.  Called "S" bit because both
-  // "static" and "sign" start with the letter "S".
+  // indicating the sign of Delta Base.  Called "S" bit because both "static"
+  // and "sign" start with the letter "S".
   kSbit,
   // An integer encoded with variable length encoding.  This could be an index,
-  // stream ID, maximum size, or Largest Reference.
+  // stream ID, maximum size, or Encoded Required Insert Count.
   kVarint,
   // A second integer encoded with variable length encoding.  This could be
-  // Delta Base Index.
+  // Delta Base.
   kVarint2,
   // A header name or header value encoded as:
   //   a bit indicating whether it is Huffman encoded;
@@ -97,15 +97,15 @@
 const QpackInstruction* DuplicateInstruction();
 
 // 5.2.4 Dynamic Table Size Update
-const QpackInstruction* DynamicTableSizeUpdateInstruction();
+const QpackInstruction* SetDynamicTableCapacityInstruction();
 
 // Encoder stream language.
 const QpackLanguage* QpackEncoderStreamLanguage();
 
 // 5.3 Decoder stream instructions
 
-// 5.3.1 Table State Synchronize
-const QpackInstruction* TableStateSynchronizeInstruction();
+// 5.3.1 Insert Count Increment
+const QpackInstruction* InsertCountIncrementInstruction();
 
 // 5.3.2 Header Acknowledgement
 const QpackInstruction* HeaderAcknowledgementInstruction();
diff --git a/net/third_party/quic/core/qpack/qpack_decoder.cc b/net/third_party/quic/core/qpack/qpack_decoder.cc
index ae0ba5f..264b7fc 100644
--- a/net/third_party/quic/core/qpack/qpack_decoder.cc
+++ b/net/third_party/quic/core/qpack/qpack_decoder.cc
@@ -55,14 +55,14 @@
     return;
   }
 
-  uint64_t real_index;
-  if (!EncoderStreamRelativeIndexToRealIndex(name_index, &real_index)) {
+  uint64_t absolute_index;
+  if (!EncoderStreamRelativeIndexToAbsoluteIndex(name_index, &absolute_index)) {
     encoder_stream_error_delegate_->OnError("Invalid relative index.");
     return;
   }
 
   const QpackEntry* entry =
-      header_table_.LookupEntry(/* is_static = */ false, real_index);
+      header_table_.LookupEntry(/* is_static = */ false, absolute_index);
   if (!entry) {
     encoder_stream_error_delegate_->OnError("Dynamic table entry not found.");
     return;
@@ -83,14 +83,14 @@
 }
 
 void QpackDecoder::OnDuplicate(uint64_t index) {
-  uint64_t real_index;
-  if (!EncoderStreamRelativeIndexToRealIndex(index, &real_index)) {
+  uint64_t absolute_index;
+  if (!EncoderStreamRelativeIndexToAbsoluteIndex(index, &absolute_index)) {
     encoder_stream_error_delegate_->OnError("Invalid relative index.");
     return;
   }
 
   const QpackEntry* entry =
-      header_table_.LookupEntry(/* is_static = */ false, real_index);
+      header_table_.LookupEntry(/* is_static = */ false, absolute_index);
   if (!entry) {
     encoder_stream_error_delegate_->OnError("Dynamic table entry not found.");
     return;
@@ -101,10 +101,10 @@
   }
 }
 
-void QpackDecoder::OnDynamicTableSizeUpdate(uint64_t max_size) {
-  if (!header_table_.UpdateTableSize(max_size)) {
+void QpackDecoder::OnSetDynamicTableCapacity(uint64_t capacity) {
+  if (!header_table_.SetDynamicTableCapacity(capacity)) {
     encoder_stream_error_delegate_->OnError(
-        "Error updating dynamic table size.");
+        "Error updating dynamic table capacity.");
   }
 }
 
@@ -112,16 +112,16 @@
   encoder_stream_error_delegate_->OnError(error_message);
 }
 
-bool QpackDecoder::EncoderStreamRelativeIndexToRealIndex(
+bool QpackDecoder::EncoderStreamRelativeIndexToAbsoluteIndex(
     uint64_t relative_index,
-    uint64_t* real_index) const {
+    uint64_t* absolute_index) const {
   if (relative_index == std::numeric_limits<uint64_t>::max() ||
       relative_index + 1 > std::numeric_limits<uint64_t>::max() -
                                header_table_.inserted_entry_count()) {
     return false;
   }
 
-  *real_index = header_table_.inserted_entry_count() - relative_index - 1;
+  *absolute_index = header_table_.inserted_entry_count() - relative_index - 1;
   return true;
 }
 
diff --git a/net/third_party/quic/core/qpack/qpack_decoder.h b/net/third_party/quic/core/qpack/qpack_decoder.h
index 38617582..1816fe0 100644
--- a/net/third_party/quic/core/qpack/qpack_decoder.h
+++ b/net/third_party/quic/core/qpack/qpack_decoder.h
@@ -79,18 +79,17 @@
   void OnInsertWithoutNameReference(QuicStringPiece name,
                                     QuicStringPiece value) override;
   void OnDuplicate(uint64_t index) override;
-  void OnDynamicTableSizeUpdate(uint64_t max_size) override;
+  void OnSetDynamicTableCapacity(uint64_t capacity) override;
   void OnErrorDetected(QuicStringPiece error_message) override;
 
  private:
   // The encoder stream uses relative index (but different from the kind of
-  // relative index used on a request stream).
-  // The spec describes how to convert these into absolute index (one based).
-  // QpackHeaderTable uses real index (zero based, one less than the absolute
-  // index).  This method converts relative index to real index.  It returns
-  // true on success, or false if conversion fails due to overflow/underflow.
-  bool EncoderStreamRelativeIndexToRealIndex(uint64_t relative_index,
-                                             uint64_t* real_index) const;
+  // relative index used on a request stream).  This method converts relative
+  // index to absolute index (zero based).  It returns true on success, or false
+  // if conversion fails due to overflow/underflow.
+  bool EncoderStreamRelativeIndexToAbsoluteIndex(
+      uint64_t relative_index,
+      uint64_t* absolute_index) const;
 
   EncoderStreamErrorDelegate* const encoder_stream_error_delegate_;
   QpackEncoderStreamReceiver encoder_stream_receiver_;
diff --git a/net/third_party/quic/core/qpack/qpack_decoder_stream_receiver.cc b/net/third_party/quic/core/qpack/qpack_decoder_stream_receiver.cc
index 7efd5ec..f187d35 100644
--- a/net/third_party/quic/core/qpack/qpack_decoder_stream_receiver.cc
+++ b/net/third_party/quic/core/qpack/qpack_decoder_stream_receiver.cc
@@ -27,8 +27,8 @@
 
 bool QpackDecoderStreamReceiver::OnInstructionDecoded(
     const QpackInstruction* instruction) {
-  if (instruction == TableStateSynchronizeInstruction()) {
-    delegate_->OnTableStateSynchronize(instruction_decoder_.varint());
+  if (instruction == InsertCountIncrementInstruction()) {
+    delegate_->OnInsertCountIncrement(instruction_decoder_.varint());
     return true;
   }
 
diff --git a/net/third_party/quic/core/qpack/qpack_decoder_stream_receiver.h b/net/third_party/quic/core/qpack/qpack_decoder_stream_receiver.h
index b9fd17e..e20722a 100644
--- a/net/third_party/quic/core/qpack/qpack_decoder_stream_receiver.h
+++ b/net/third_party/quic/core/qpack/qpack_decoder_stream_receiver.h
@@ -25,8 +25,8 @@
    public:
     virtual ~Delegate() = default;
 
-    // 5.3.1 Table State Synchronize
-    virtual void OnTableStateSynchronize(uint64_t insert_count) = 0;
+    // 5.3.1 Insert Count Increment
+    virtual void OnInsertCountIncrement(uint64_t increment) = 0;
     // 5.3.2 Header Acknowledgement
     virtual void OnHeaderAcknowledgement(QuicStreamId stream_id) = 0;
     // 5.3.3 Stream Cancellation
diff --git a/net/third_party/quic/core/qpack/qpack_decoder_stream_receiver_test.cc b/net/third_party/quic/core/qpack/qpack_decoder_stream_receiver_test.cc
index 4d27ce2..3bc14ee4f 100644
--- a/net/third_party/quic/core/qpack/qpack_decoder_stream_receiver_test.cc
+++ b/net/third_party/quic/core/qpack/qpack_decoder_stream_receiver_test.cc
@@ -20,7 +20,7 @@
  public:
   ~MockDelegate() override = default;
 
-  MOCK_METHOD1(OnTableStateSynchronize, void(uint64_t insert_count));
+  MOCK_METHOD1(OnInsertCountIncrement, void(uint64_t increment));
   MOCK_METHOD1(OnHeaderAcknowledgement, void(QuicStreamId stream_id));
   MOCK_METHOD1(OnStreamCancellation, void(QuicStreamId stream_id));
   MOCK_METHOD1(OnErrorDetected, void(QuicStringPiece error_message));
@@ -35,17 +35,17 @@
   StrictMock<MockDelegate> delegate_;
 };
 
-TEST_F(QpackDecoderStreamReceiverTest, TableStateSynchronize) {
-  EXPECT_CALL(delegate_, OnTableStateSynchronize(0));
+TEST_F(QpackDecoderStreamReceiverTest, InsertCountIncrement) {
+  EXPECT_CALL(delegate_, OnInsertCountIncrement(0));
   stream_.Decode(QuicTextUtils::HexDecode("00"));
 
-  EXPECT_CALL(delegate_, OnTableStateSynchronize(10));
+  EXPECT_CALL(delegate_, OnInsertCountIncrement(10));
   stream_.Decode(QuicTextUtils::HexDecode("0a"));
 
-  EXPECT_CALL(delegate_, OnTableStateSynchronize(63));
+  EXPECT_CALL(delegate_, OnInsertCountIncrement(63));
   stream_.Decode(QuicTextUtils::HexDecode("3f00"));
 
-  EXPECT_CALL(delegate_, OnTableStateSynchronize(200));
+  EXPECT_CALL(delegate_, OnInsertCountIncrement(200));
   stream_.Decode(QuicTextUtils::HexDecode("3f8901"));
 
   EXPECT_CALL(delegate_, OnErrorDetected(Eq("Encoded integer too large.")));
diff --git a/net/third_party/quic/core/qpack/qpack_decoder_stream_sender.cc b/net/third_party/quic/core/qpack/qpack_decoder_stream_sender.cc
index 9ff09d9..9083f0a8 100644
--- a/net/third_party/quic/core/qpack/qpack_decoder_stream_sender.cc
+++ b/net/third_party/quic/core/qpack/qpack_decoder_stream_sender.cc
@@ -18,11 +18,10 @@
   DCHECK(delegate_);
 }
 
-void QpackDecoderStreamSender::SendTableStateSynchronize(
-    uint64_t insert_count) {
-  instruction_encoder_.set_varint(insert_count);
+void QpackDecoderStreamSender::SendInsertCountIncrement(uint64_t increment) {
+  instruction_encoder_.set_varint(increment);
 
-  instruction_encoder_.Encode(TableStateSynchronizeInstruction());
+  instruction_encoder_.Encode(InsertCountIncrementInstruction());
 
   QuicString output;
 
diff --git a/net/third_party/quic/core/qpack/qpack_decoder_stream_sender.h b/net/third_party/quic/core/qpack/qpack_decoder_stream_sender.h
index bc47649..d4941cd 100644
--- a/net/third_party/quic/core/qpack/qpack_decoder_stream_sender.h
+++ b/net/third_party/quic/core/qpack/qpack_decoder_stream_sender.h
@@ -37,8 +37,8 @@
   // Methods for sending instructions, see
   // https://quicwg.org/base-drafts/draft-ietf-quic-qpack.html#rfc.section.5.3
 
-  // 5.3.1 Table State Synchronize
-  void SendTableStateSynchronize(uint64_t insert_count);
+  // 5.3.1 Insert Count Increment
+  void SendInsertCountIncrement(uint64_t increment);
   // 5.3.2 Header Acknowledgement
   void SendHeaderAcknowledgement(QuicStreamId stream_id);
   // 5.3.3 Stream Cancellation
diff --git a/net/third_party/quic/core/qpack/qpack_decoder_stream_sender_test.cc b/net/third_party/quic/core/qpack/qpack_decoder_stream_sender_test.cc
index 1b1ed1d..68d9f80 100644
--- a/net/third_party/quic/core/qpack/qpack_decoder_stream_sender_test.cc
+++ b/net/third_party/quic/core/qpack/qpack_decoder_stream_sender_test.cc
@@ -32,18 +32,18 @@
   QpackDecoderStreamSender stream_;
 };
 
-TEST_F(QpackDecoderStreamSenderTest, TableStateSynchronize) {
+TEST_F(QpackDecoderStreamSenderTest, InsertCountIncrement) {
   EXPECT_CALL(delegate_, Write(Eq(QuicTextUtils::HexDecode("00"))));
-  stream_.SendTableStateSynchronize(0);
+  stream_.SendInsertCountIncrement(0);
 
   EXPECT_CALL(delegate_, Write(Eq(QuicTextUtils::HexDecode("0a"))));
-  stream_.SendTableStateSynchronize(10);
+  stream_.SendInsertCountIncrement(10);
 
   EXPECT_CALL(delegate_, Write(Eq(QuicTextUtils::HexDecode("3f00"))));
-  stream_.SendTableStateSynchronize(63);
+  stream_.SendInsertCountIncrement(63);
 
   EXPECT_CALL(delegate_, Write(Eq(QuicTextUtils::HexDecode("3f8901"))));
-  stream_.SendTableStateSynchronize(200);
+  stream_.SendInsertCountIncrement(200);
 }
 
 TEST_F(QpackDecoderStreamSenderTest, HeaderAcknowledgement) {
diff --git a/net/third_party/quic/core/qpack/qpack_decoder_test.cc b/net/third_party/quic/core/qpack/qpack_decoder_test.cc
index dacf370..093ee31c 100644
--- a/net/third_party/quic/core/qpack/qpack_decoder_test.cc
+++ b/net/third_party/quic/core/qpack/qpack_decoder_test.cc
@@ -296,11 +296,10 @@
       "01"));           // Duplicate entry with relative index 1.
 
   // Now there are four entries in the dynamic table.
-  // Note that absolute indices start with 1.
-  // Entry 1: "foo", "bar"
-  // Entry 2: "foo", "ZZZ"
-  // Entry 3: ":method", "foo"
-  // Entry 4: "foo", "ZZZ"
+  // Entry 0: "foo", "bar"
+  // Entry 1: "foo", "ZZZ"
+  // Entry 2: ":method", "foo"
+  // Entry 3: "foo", "ZZZ"
 
   // Use a Sequence to test that mock methods are called in order.
   Sequence s;
@@ -317,12 +316,12 @@
   EXPECT_CALL(handler_, OnDecodingCompleted()).InSequence(s);
 
   DecodeHeaderBlock(QuicTextUtils::HexDecode(
-      "0500"  // Largest Reference 4 and Delta Base Index 0.
-              // Base Index is 4 + 0 = 4.
-      "83"    // Dynamic table entry with relative index 3, absolute index 1.
-      "82"    // Dynamic table entry with relative index 2, absolute index 2.
-      "81"    // Dynamic table entry with relative index 1, absolute index 3.
-      "80"    // Dynamic table entry with relative index 0, absolute index 4.
+      "0500"  // Required Insert Count 4 and Delta Base 0.
+              // Base is 4 + 0 = 4.
+      "83"    // Dynamic table entry with relative index 3, absolute index 0.
+      "82"    // Dynamic table entry with relative index 2, absolute index 1.
+      "81"    // Dynamic table entry with relative index 1, absolute index 2.
+      "80"    // Dynamic table entry with relative index 0, absolute index 3.
       "41025a5a"));  // Name of entry 1 (relative index) from dynamic table,
                      // with value "ZZ".
 
@@ -338,12 +337,12 @@
   EXPECT_CALL(handler_, OnDecodingCompleted()).InSequence(s);
 
   DecodeHeaderBlock(QuicTextUtils::HexDecode(
-      "0502"  // Largest Reference 4 and Delta Base Index 2.
-              // Base Index is 4 + 2 = 6.
-      "85"    // Dynamic table entry with relative index 5, absolute index 1.
-      "84"    // Dynamic table entry with relative index 4, absolute index 2.
-      "83"    // Dynamic table entry with relative index 3, absolute index 3.
-      "82"    // Dynamic table entry with relative index 2, absolute index 4.
+      "0502"  // Required Insert Count 4 and Delta Base 2.
+              // Base is 4 + 2 = 6.
+      "85"    // Dynamic table entry with relative index 5, absolute index 0.
+      "84"    // Dynamic table entry with relative index 4, absolute index 1.
+      "83"    // Dynamic table entry with relative index 3, absolute index 2.
+      "82"    // Dynamic table entry with relative index 2, absolute index 3.
       "43025a5a"));  // Name of entry 3 (relative index) from dynamic table,
                      // with value "ZZ".
 
@@ -359,12 +358,12 @@
   EXPECT_CALL(handler_, OnDecodingCompleted()).InSequence(s);
 
   DecodeHeaderBlock(QuicTextUtils::HexDecode(
-      "0582"  // Largest Reference 4 and Delta Base Index 2 with sign bit set.
-              // Base Index is 4 - 2 - 1 = 1.
-      "80"    // Dynamic table entry with relative index 0, absolute index 1.
-      "10"    // Dynamic table entry with post-base index 0, absolute index 2.
-      "11"    // Dynamic table entry with post-base index 1, absolute index 3.
-      "12"    // Dynamic table entry with post-base index 2, absolute index 4.
+      "0582"  // Required Insert Count 4 and Delta Base 2 with sign bit set.
+              // Base is 4 - 2 - 1 = 1.
+      "80"    // Dynamic table entry with relative index 0, absolute index 0.
+      "10"    // Dynamic table entry with post-base index 0, absolute index 1.
+      "11"    // Dynamic table entry with post-base index 1, absolute index 2.
+      "12"    // Dynamic table entry with post-base index 2, absolute index 3.
       "01025a5a"));  // Name of entry 1 (post-base index) from dynamic table,
                      // with value "ZZ".
 }
@@ -379,9 +378,9 @@
               Write(Eq(kHeaderAcknowledgement)));
 
   DecodeHeaderBlock(QuicTextUtils::HexDecode(
-      "0200"   // Largest Reference 1 and Delta Base Index 0.
-               // Base Index is 1 + 0 = 1.
-      "80"));  // Dynamic table entry with relative index 0, absolute index 1.
+      "0200"   // Required Insert Count 1 and Delta Base 0.
+               // Base is 1 + 0 = 1.
+      "80"));  // Dynamic table entry with relative index 0, absolute index 0.
 
   // Change dynamic table capacity to 32 bytes, smaller than the entry.
   // This must cause the entry to be evicted.
@@ -391,9 +390,9 @@
               OnDecodingErrorDetected(Eq("Dynamic table entry not found.")));
 
   DecodeHeaderBlock(QuicTextUtils::HexDecode(
-      "0200"   // Largest Reference 1 and Delta Base Index 0.
-               // Base Index is 1 + 0 = 1.
-      "80"));  // Dynamic table entry with relative index 0, absolute index 1.
+      "0200"   // Required Insert Count 1 and Delta Base 0.
+               // Base is 1 + 0 = 1.
+      "80"));  // Dynamic table entry with relative index 0, absolute index 0.
 }
 
 TEST_P(QpackDecoderTest, EncoderStreamErrorEntryTooLarge) {
@@ -443,22 +442,20 @@
   DecodeEncoderStreamData(QuicTextUtils::HexDecode("3fffffffffffffffffffff"));
 }
 
-TEST_P(QpackDecoderTest, InvalidDynamicEntryWhenBaseIndexIsZero) {
+TEST_P(QpackDecoderTest, InvalidDynamicEntryWhenBaseIsZero) {
   EXPECT_CALL(handler_, OnDecodingErrorDetected(Eq("Invalid relative index.")));
 
   DecodeHeaderBlock(QuicTextUtils::HexDecode(
-      "0280"  // Largest Reference is 1.  Base Index 1 - 1 - 0 = 0 is explicitly
-              // permitted by the spec.
+      "0280"   // Required Insert Count is 1.  Base 1 - 1 - 0 = 0 is explicitly
+               // permitted by the spec.
       "80"));  // However, addressing entry with relative index 0 would point to
-               // absolute index 0, which is invalid (absolute index is one
-               // based).
+               // absolute index -1, which is invalid.
 }
 
-TEST_P(QpackDecoderTest, InvalidNegativeBaseIndex) {
-  EXPECT_CALL(handler_,
-              OnDecodingErrorDetected(Eq("Error calculating Base Index.")));
+TEST_P(QpackDecoderTest, InvalidNegativeBase) {
+  EXPECT_CALL(handler_, OnDecodingErrorDetected(Eq("Error calculating Base.")));
 
-  // Largest reference 1, Delta Base Index 1 with sign bit set, Base Index would
+  // Required Insert Count 1, Delta Base 1 with sign bit set, Base would
   // be 1 - 1 - 1 = -1, but it is not allowed to be negative.
   DecodeHeaderBlock(QuicTextUtils::HexDecode("0281"));
 }
@@ -471,38 +468,37 @@
               OnDecodingErrorDetected(Eq("Dynamic table entry not found.")));
 
   DecodeHeaderBlock(QuicTextUtils::HexDecode(
-      "0500"   // Largest Reference 4 and Delta Base Index 0.
-               // Base Index is 4 + 0 = 4.
+      "0500"   // Required Insert Count 4 and Delta Base 0.
+               // Base is 4 + 0 = 4.
       "82"));  // Indexed Header Field instruction addressing relative index 2.
-               // This is absolute index 2. Such entry does not exist.
+               // This is absolute index 1. Such entry does not exist.
 
   EXPECT_CALL(handler_, OnDecodingErrorDetected(Eq("Invalid relative index.")));
 
   DecodeHeaderBlock(QuicTextUtils::HexDecode(
-      "0500"   // Largest Reference 4 and Delta Base Index 0.
-               // Base Index is 4 + 0 = 4.
+      "0500"   // Required Insert Count 4 and Delta Base 0.
+               // Base is 4 + 0 = 4.
       "84"));  // Indexed Header Field instruction addressing relative index 4.
-               // This is absolute index 0, which is invalid, because absolute
-               // indexing starts from 1.
+               // This is absolute index -1, which is invalid.
 
   EXPECT_CALL(handler_,
               OnDecodingErrorDetected(Eq("Dynamic table entry not found.")));
 
   DecodeHeaderBlock(QuicTextUtils::HexDecode(
-      "0500"     // Largest Reference 4 and Delta Base Index 0.
-                 // Base Index is 4 + 0 = 4.
+      "0500"     // Required Insert Count 4 and Delta Base 0.
+                 // Base is 4 + 0 = 4.
       "4200"));  // Literal Header Field with Name Reference instruction
-                 // addressing relative index 2.  This is absolute index 2. Such
+                 // addressing relative index 2.  This is absolute index 1. Such
                  // entry does not exist.
 
   EXPECT_CALL(handler_, OnDecodingErrorDetected(Eq("Invalid relative index.")));
 
   DecodeHeaderBlock(QuicTextUtils::HexDecode(
-      "0500"     // Largest Reference 4 and Delta Base Index 0.
-                 // Base Index is 4 + 0 = 4.
+      "0500"     // Required Insert Count 4 and Delta Base 0.
+                 // Base is 4 + 0 = 4.
       "4400"));  // Literal Header Field with Name Reference instruction
-                 // addressing relative index 4.  This is absolute index 0,
-                 // which is invalid, because absolute indexing starts from 1.
+                 // addressing relative index 4.  This is absolute index -1,
+                 // which is invalid.
 }
 
 TEST_P(QpackDecoderTest, InvalidDynamicEntryByPostBaseIndex) {
@@ -513,26 +509,26 @@
               OnDecodingErrorDetected(Eq("Dynamic table entry not found.")));
 
   DecodeHeaderBlock(QuicTextUtils::HexDecode(
-      "0380"   // Largest Reference 2 and Delta Base Index 0 with sign bit set.
-               // Base Index is 2 - 0 - 1 = 1
+      "0380"   // Required Insert Count 2 and Delta Base 0 with sign bit set.
+               // Base is 2 - 0 - 1 = 1
       "10"));  // Indexed Header Field instruction addressing dynamic table
-               // entry with post-base index 0, absolute index 2.  Such entry
+               // entry with post-base index 0, absolute index 1.  Such entry
                // does not exist.
 
   EXPECT_CALL(handler_,
               OnDecodingErrorDetected(Eq("Dynamic table entry not found.")));
 
   DecodeHeaderBlock(QuicTextUtils::HexDecode(
-      "0380"  // Largest Reference 2 and Delta Base Index 0 with sign bit set.
-              // Base Index is 2 - 0 - 1 = 1
+      "0380"     // Required Insert Count 2 and Delta Base 0 with sign bit set.
+                 // Base is 2 - 0 - 1 = 1
       "0000"));  // Literal Header Field With Name Reference instruction
                  // addressing dynamic table entry with post-base index 0,
-                 // absolute index 2.  Such entry does not exist.
+                 // absolute index 1.  Such entry does not exist.
 }
 
 TEST_P(QpackDecoderTest, TableCapacityMustNotExceedMaximum) {
   EXPECT_CALL(encoder_stream_error_delegate_,
-              OnError(Eq("Error updating dynamic table size.")));
+              OnError(Eq("Error updating dynamic table capacity.")));
 
   // Try to update dynamic table capacity to 2048, which exceeds the maximum.
   DecodeEncoderStreamData(QuicTextUtils::HexDecode("3fe10f"));
@@ -543,17 +539,17 @@
   DecodeEncoderStreamData(QuicTextUtils::HexDecode("3f61"));
 }
 
-TEST_P(QpackDecoderTest, LargestReferenceOutOfRange) {
+TEST_P(QpackDecoderTest, InvalidEncodedRequiredInsertCount) {
   // Maximum dynamic table capacity is 1024.
   // MaxEntries is 1024 / 32 = 32.
-  // Largest Reference is decoded modulo 2 * MaxEntries, that is, modulo 64.
+  // Required Insert Count is decoded modulo 2 * MaxEntries, that is, modulo 64.
   // A value of 1 cannot be encoded as 65 even though it has the same remainder.
-  EXPECT_CALL(handler_,
-              OnDecodingErrorDetected(Eq("Error decoding Largest Reference.")));
+  EXPECT_CALL(handler_, OnDecodingErrorDetected(
+                            Eq("Error decoding Required Insert Count.")));
   DecodeHeaderBlock(QuicTextUtils::HexDecode("4100"));
 }
 
-TEST_P(QpackDecoderTest, WrappedLargestReference) {
+TEST_P(QpackDecoderTest, WrappedRequiredInsertCount) {
   // Maximum dynamic table capacity is 1024.
   // MaxEntries is 1024 / 32 = 32.
 
@@ -568,74 +564,84 @@
   // Duplicate most recent entry 200 times.
   DecodeEncoderStreamData(QuicString(200, '\x00'));
 
-  // Now there is only one entry in the dynamic table, with absolute index 201.
+  // Now there is only one entry in the dynamic table, with absolute index 200.
 
   EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq(header_value)));
   EXPECT_CALL(handler_, OnDecodingCompleted());
   EXPECT_CALL(decoder_stream_sender_delegate_,
               Write(Eq(kHeaderAcknowledgement)));
 
-  // Send header block with Largest Reference = 201.
+  // Send header block with Required Insert Count = 201.
   DecodeHeaderBlock(QuicTextUtils::HexDecode(
-      "0a00"   // Wire Largest Reference 10, Largest Reference 201,
-               // Delta Base Index 0, Base Index 201.
+      "0a00"   // Encoded Required Insert Count 10, Required Insert Count 201,
+               // Delta Base 0, Base 201.
       "80"));  // Emit dynamic table entry with relative index 0.
 }
 
-TEST_P(QpackDecoderTest, NonZeroLargestReferenceButNoDynamicEntries) {
+TEST_P(QpackDecoderTest, NonZeroRequiredInsertCountButNoDynamicEntries) {
   EXPECT_CALL(handler_, OnHeaderDecoded(Eq(":method"), Eq("GET")));
   EXPECT_CALL(handler_,
-              OnDecodingErrorDetected(Eq("Largest Reference too large.")));
+              OnDecodingErrorDetected(Eq("Required Insert Count too large.")));
 
   DecodeHeaderBlock(QuicTextUtils::HexDecode(
-      "0200"   // Largest Reference is 1.
+      "0200"   // Required Insert Count is 1.
       "d1"));  // But the only instruction references the static table.
 }
 
-TEST_P(QpackDecoderTest, AddressEntryBeyondLargestReference) {
-  EXPECT_CALL(handler_, OnDecodingErrorDetected(
-                            Eq("Index larger than Largest Reference.")));
+TEST_P(QpackDecoderTest, AddressEntryNotAllowedByRequiredInsertCount) {
+  EXPECT_CALL(
+      handler_,
+      OnDecodingErrorDetected(
+          Eq("Absolute Index must be smaller than Required Insert Count.")));
 
   DecodeHeaderBlock(QuicTextUtils::HexDecode(
-      "0201"   // Largest Reference 1 and Delta Base Index 1.
-               // Base Index is 1 + 1 = 2.
+      "0201"   // Required Insert Count 1 and Delta Base 1.
+               // Base is 1 + 1 = 2.
       "80"));  // Indexed Header Field instruction addressing dynamic table
-               // entry with relative index 0, absolute index 2.  This is beyond
-               // Largest Reference.
+               // entry with relative index 0, absolute index 1.  This is not
+               // allowed by Required Insert Count.
 
-  EXPECT_CALL(handler_, OnDecodingErrorDetected(
-                            Eq("Index larger than Largest Reference.")));
+  EXPECT_CALL(
+      handler_,
+      OnDecodingErrorDetected(
+          Eq("Absolute Index must be smaller than Required Insert Count.")));
 
   DecodeHeaderBlock(QuicTextUtils::HexDecode(
-      "0201"     // Largest Reference 1 and Delta Base Index 1.
-                 // Base Index is 1 + 1 = 2.
+      "0201"     // Required Insert Count 1 and Delta Base 1.
+                 // Base is 1 + 1 = 2.
       "4000"));  // Literal Header Field with Name Reference instruction
                  // addressing dynamic table entry with relative index 0,
-                 // absolute index 2.  This is beyond Largest Reference.
+                 // absolute index 1.  This is not allowed by Required Index
+                 // Count.
 
-  EXPECT_CALL(handler_, OnDecodingErrorDetected(
-                            Eq("Index larger than Largest Reference.")));
+  EXPECT_CALL(
+      handler_,
+      OnDecodingErrorDetected(
+          Eq("Absolute Index must be smaller than Required Insert Count.")));
 
   DecodeHeaderBlock(QuicTextUtils::HexDecode(
-      "0200"   // Largest Reference 1 and Delta Base Index 0.
-               // Base Index is 1 + 0 = 1.
+      "0200"   // Required Insert Count 1 and Delta Base 0.
+               // Base is 1 + 0 = 1.
       "10"));  // Indexed Header Field with Post-Base Index instruction
                // addressing dynamic table entry with post-base index 0,
-               // absolute index 2.  This is beyond Largest Reference.
+               // absolute index 1.  This is not allowed by Required Insert
+               // Count.
 
-  EXPECT_CALL(handler_, OnDecodingErrorDetected(
-                            Eq("Index larger than Largest Reference.")));
+  EXPECT_CALL(
+      handler_,
+      OnDecodingErrorDetected(
+          Eq("Absolute Index must be smaller than Required Insert Count.")));
 
   DecodeHeaderBlock(QuicTextUtils::HexDecode(
-      "0200"     // Largest Reference 1 and Delta Base Index 0.
-                 // Base Index is 1 + 0 = 1.
+      "0200"     // Required Insert Count 1 and Delta Base 0.
+                 // Base is 1 + 0 = 1.
       "0000"));  // Literal Header Field with Post-Base Name Reference
                  // instruction addressing dynamic table entry with post-base
-                 // index 0, absolute index 2.  This is beyond Largest
-                 // Reference.
+                 // index 0, absolute index 1.  This is not allowed by Required
+                 // Index Count.
 }
 
-TEST_P(QpackDecoderTest, PromisedLargestReferenceLargerThanLargestActualIndex) {
+TEST_P(QpackDecoderTest, PromisedRequiredInsertCountLargerThanActual) {
   // Add literal entry with name "foo" and value "bar".
   DecodeEncoderStreamData(QuicTextUtils::HexDecode("6294e703626172"));
   // Duplicate entry.
@@ -643,51 +649,51 @@
 
   EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("bar")));
   EXPECT_CALL(handler_,
-              OnDecodingErrorDetected(Eq("Largest Reference too large.")));
+              OnDecodingErrorDetected(Eq("Required Insert Count too large.")));
 
   DecodeHeaderBlock(QuicTextUtils::HexDecode(
-      "0300"   // Largest Reference 2 and Delta Base Index 0.
-               // Base Index is 2 + 0 = 2.
+      "0300"   // Required Insert Count 2 and Delta Base 0.
+               // Base is 2 + 0 = 2.
       "81"));  // Indexed Header Field instruction addressing dynamic table
-               // entry with relative index 1, absolute index 1.  This is the
-               // largest reference in this header block, even though Largest
-               // Reference is 2.
+               // entry with relative index 1, absolute index 0.  Header block
+               // requires insert count of 1, even though Required Insert Count
+               // is 2.
 
   EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("")));
   EXPECT_CALL(handler_,
-              OnDecodingErrorDetected(Eq("Largest Reference too large.")));
+              OnDecodingErrorDetected(Eq("Required Insert Count too large.")));
 
   DecodeHeaderBlock(QuicTextUtils::HexDecode(
-      "0300"     // Largest Reference 2 and Delta Base Index 0.
-                 // Base Index is 2 + 0 = 2.
+      "0300"     // Required Insert Count 2 and Delta Base 0.
+                 // Base is 2 + 0 = 2.
       "4100"));  // Literal Header Field with Name Reference instruction
                  // addressing dynamic table entry with relative index 1,
-                 // absolute index 1.  This is the largest reference in this
-                 // header block, even though Largest Reference is 2.
+                 // absolute index 0.  Header block requires insert count of 1,
+                 // even though Required Insert Count is 2.
 
   EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("bar")));
   EXPECT_CALL(handler_,
-              OnDecodingErrorDetected(Eq("Largest Reference too large.")));
+              OnDecodingErrorDetected(Eq("Required Insert Count too large.")));
 
   DecodeHeaderBlock(QuicTextUtils::HexDecode(
-      "0481"   // Largest Reference 3 and Delta Base Index 1 with sign bit set.
-               // Base Index is 3 - 1 - 1 = 1.
+      "0481"   // Required Insert Count 3 and Delta Base 1 with sign bit set.
+               // Base is 3 - 1 - 1 = 1.
       "10"));  // Indexed Header Field with Post-Base Index instruction
                // addressing dynamic table entry with post-base index 0,
-               // absolute index 2.  This is the largest reference in this
-               // header block, even though Largest Reference is 3.
+               // absolute index 1.  Header block requires insert count of 2,
+               // even though Required Insert Count is 3.
 
   EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("")));
   EXPECT_CALL(handler_,
-              OnDecodingErrorDetected(Eq("Largest Reference too large.")));
+              OnDecodingErrorDetected(Eq("Required Insert Count too large.")));
 
   DecodeHeaderBlock(QuicTextUtils::HexDecode(
-      "0481"  // Largest Reference 3 and Delta Base Index 1 with sign bit set.
-              // Base Index is 3 - 1 - 1 = 1.
+      "0481"     // Required Insert Count 3 and Delta Base 1 with sign bit set.
+                 // Base is 3 - 1 - 1 = 1.
       "0000"));  // Literal Header Field with Post-Base Name Reference
                  // instruction addressing dynamic table entry with post-base
-                 // index 0, absolute index 2.  This is the largest reference in
-                 // this header block, even though Largest Reference is 3.
+                 // index 0, absolute index 1.  Header block requires insert
+                 // count of 2, even though Required Insert Count is 3.
 }
 
 }  // namespace
diff --git a/net/third_party/quic/core/qpack/qpack_encoder.cc b/net/third_party/quic/core/qpack/qpack_encoder.cc
index 490c4ebf..ce07d1f 100644
--- a/net/third_party/quic/core/qpack/qpack_encoder.cc
+++ b/net/third_party/quic/core/qpack/qpack_encoder.cc
@@ -35,7 +35,7 @@
   decoder_stream_receiver_.Decode(data);
 }
 
-void QpackEncoder::OnTableStateSynchronize(uint64_t insert_count) {
+void QpackEncoder::OnInsertCountIncrement(uint64_t increment) {
   // TODO(bnc): Implement dynamic table management for encoding.
 }
 
diff --git a/net/third_party/quic/core/qpack/qpack_encoder.h b/net/third_party/quic/core/qpack/qpack_encoder.h
index 39132e45..024f558 100644
--- a/net/third_party/quic/core/qpack/qpack_encoder.h
+++ b/net/third_party/quic/core/qpack/qpack_encoder.h
@@ -55,7 +55,7 @@
   void DecodeDecoderStreamData(QuicStringPiece data);
 
   // QpackDecoderStreamReceiver::Delegate implementation
-  void OnTableStateSynchronize(uint64_t insert_count) override;
+  void OnInsertCountIncrement(uint64_t increment) override;
   void OnHeaderAcknowledgement(QuicStreamId stream_id) override;
   void OnStreamCancellation(QuicStreamId stream_id) override;
   void OnErrorDetected(QuicStringPiece error_message) override;
diff --git a/net/third_party/quic/core/qpack/qpack_encoder_stream_receiver.cc b/net/third_party/quic/core/qpack/qpack_encoder_stream_receiver.cc
index 6eba52df..2a1ec15 100644
--- a/net/third_party/quic/core/qpack/qpack_encoder_stream_receiver.cc
+++ b/net/third_party/quic/core/qpack/qpack_encoder_stream_receiver.cc
@@ -45,8 +45,8 @@
     return true;
   }
 
-  DCHECK_EQ(instruction, DynamicTableSizeUpdateInstruction());
-  delegate_->OnDynamicTableSizeUpdate(instruction_decoder_.varint());
+  DCHECK_EQ(instruction, SetDynamicTableCapacityInstruction());
+  delegate_->OnSetDynamicTableCapacity(instruction_decoder_.varint());
   return true;
 }
 
diff --git a/net/third_party/quic/core/qpack/qpack_encoder_stream_receiver.h b/net/third_party/quic/core/qpack/qpack_encoder_stream_receiver.h
index 0f212d5..ecc9cb34 100644
--- a/net/third_party/quic/core/qpack/qpack_encoder_stream_receiver.h
+++ b/net/third_party/quic/core/qpack/qpack_encoder_stream_receiver.h
@@ -33,8 +33,8 @@
                                               QuicStringPiece value) = 0;
     // 5.2.3. Duplicate
     virtual void OnDuplicate(uint64_t index) = 0;
-    // 5.2.4. Dynamic Table Size Update
-    virtual void OnDynamicTableSizeUpdate(uint64_t max_size) = 0;
+    // 5.2.4. Set Dynamic Table Capacity
+    virtual void OnSetDynamicTableCapacity(uint64_t capacity) = 0;
     // Decoding error
     virtual void OnErrorDetected(QuicStringPiece error_message) = 0;
   };
diff --git a/net/third_party/quic/core/qpack/qpack_encoder_stream_receiver_test.cc b/net/third_party/quic/core/qpack/qpack_encoder_stream_receiver_test.cc
index 7b2ec4d..8b2035b 100644
--- a/net/third_party/quic/core/qpack/qpack_encoder_stream_receiver_test.cc
+++ b/net/third_party/quic/core/qpack/qpack_encoder_stream_receiver_test.cc
@@ -27,7 +27,7 @@
   MOCK_METHOD2(OnInsertWithoutNameReference,
                void(QuicStringPiece name, QuicStringPiece value));
   MOCK_METHOD1(OnDuplicate, void(uint64_t index));
-  MOCK_METHOD1(OnDynamicTableSizeUpdate, void(uint64_t max_size));
+  MOCK_METHOD1(OnSetDynamicTableCapacity, void(uint64_t capacity));
   MOCK_METHOD1(OnErrorDetected, void(QuicStringPiece error_message));
 };
 
@@ -149,16 +149,16 @@
   Decode(QuicTextUtils::HexDecode("1fffffffffffffffffffff"));
 }
 
-TEST_F(QpackEncoderStreamReceiverTest, DynamicTableSizeUpdate) {
-  // Small max size fits in prefix.
-  EXPECT_CALL(*delegate(), OnDynamicTableSizeUpdate(17));
-  // Large max size requires two extension bytes.
-  EXPECT_CALL(*delegate(), OnDynamicTableSizeUpdate(500));
+TEST_F(QpackEncoderStreamReceiverTest, SetDynamicTableCapacity) {
+  // Small capacity fits in prefix.
+  EXPECT_CALL(*delegate(), OnSetDynamicTableCapacity(17));
+  // Large capacity requires two extension bytes.
+  EXPECT_CALL(*delegate(), OnSetDynamicTableCapacity(500));
 
   Decode(QuicTextUtils::HexDecode("313fd503"));
 }
 
-TEST_F(QpackEncoderStreamReceiverTest, DynamicTableSizeUpdateMaxSizeTooLarge) {
+TEST_F(QpackEncoderStreamReceiverTest, SetDynamicTableCapacityTooLarge) {
   EXPECT_CALL(*delegate(), OnErrorDetected(Eq("Encoded integer too large.")));
 
   Decode(QuicTextUtils::HexDecode("3fffffffffffffffffffff"));
diff --git a/net/third_party/quic/core/qpack/qpack_encoder_stream_sender.cc b/net/third_party/quic/core/qpack/qpack_encoder_stream_sender.cc
index 0243cc1..e08538b 100644
--- a/net/third_party/quic/core/qpack/qpack_encoder_stream_sender.cc
+++ b/net/third_party/quic/core/qpack/qpack_encoder_stream_sender.cc
@@ -65,10 +65,10 @@
   delegate_->Write(output);
 }
 
-void QpackEncoderStreamSender::SendDynamicTableSizeUpdate(uint64_t max_size) {
-  instruction_encoder_.set_varint(max_size);
+void QpackEncoderStreamSender::SendSetDynamicTableCapacity(uint64_t capacity) {
+  instruction_encoder_.set_varint(capacity);
 
-  instruction_encoder_.Encode(DynamicTableSizeUpdateInstruction());
+  instruction_encoder_.Encode(SetDynamicTableCapacityInstruction());
 
   QuicString output;
 
diff --git a/net/third_party/quic/core/qpack/qpack_encoder_stream_sender.h b/net/third_party/quic/core/qpack/qpack_encoder_stream_sender.h
index 351676b..e4afad6d 100644
--- a/net/third_party/quic/core/qpack/qpack_encoder_stream_sender.h
+++ b/net/third_party/quic/core/qpack/qpack_encoder_stream_sender.h
@@ -44,8 +44,8 @@
                                       QuicStringPiece value);
   // 5.2.3. Duplicate
   void SendDuplicate(uint64_t index);
-  // 5.2.4. Dynamic Table Size Update
-  void SendDynamicTableSizeUpdate(uint64_t max_size);
+  // 5.2.4. Set Dynamic Table Capacity
+  void SendSetDynamicTableCapacity(uint64_t capacity);
 
  private:
   Delegate* const delegate_;
diff --git a/net/third_party/quic/core/qpack/qpack_encoder_stream_sender_test.cc b/net/third_party/quic/core/qpack/qpack_encoder_stream_sender_test.cc
index 6a1e4c6..316116e4 100644
--- a/net/third_party/quic/core/qpack/qpack_encoder_stream_sender_test.cc
+++ b/net/third_party/quic/core/qpack/qpack_encoder_stream_sender_test.cc
@@ -95,14 +95,14 @@
   stream_.SendDuplicate(500);
 }
 
-TEST_F(QpackEncoderStreamSenderTest, DynamicTableSizeUpdate) {
-  // Small max size fits in prefix.
+TEST_F(QpackEncoderStreamSenderTest, SetDynamicTableCapacity) {
+  // Small capacity fits in prefix.
   EXPECT_CALL(delegate_, Write(Eq(QuicTextUtils::HexDecode("31"))));
-  stream_.SendDynamicTableSizeUpdate(17);
+  stream_.SendSetDynamicTableCapacity(17);
 
-  // Large max size requires two extension bytes.
+  // Large capacity requires two extension bytes.
   EXPECT_CALL(delegate_, Write(Eq(QuicTextUtils::HexDecode("3fd503"))));
-  stream_.SendDynamicTableSizeUpdate(500);
+  stream_.SendSetDynamicTableCapacity(500);
 }
 
 }  // namespace
diff --git a/net/third_party/quic/core/qpack/qpack_header_table.cc b/net/third_party/quic/core/qpack/qpack_header_table.cc
index db5308c..e3b7e25 100644
--- a/net/third_party/quic/core/qpack/qpack_header_table.cc
+++ b/net/third_party/quic/core/qpack/qpack_header_table.cc
@@ -143,12 +143,12 @@
   return new_entry;
 }
 
-bool QpackHeaderTable::UpdateTableSize(uint64_t max_size) {
-  if (max_size > maximum_dynamic_table_capacity_) {
+bool QpackHeaderTable::SetDynamicTableCapacity(uint64_t capacity) {
+  if (capacity > maximum_dynamic_table_capacity_) {
     return false;
   }
 
-  dynamic_table_capacity_ = max_size;
+  dynamic_table_capacity_ = capacity;
   EvictDownToCurrentCapacity();
 
   DCHECK_LE(dynamic_table_size_, dynamic_table_capacity_);
diff --git a/net/third_party/quic/core/qpack/qpack_header_table.h b/net/third_party/quic/core/qpack/qpack_header_table.h
index 319386d6..ced595d 100644
--- a/net/third_party/quic/core/qpack/qpack_header_table.h
+++ b/net/third_party/quic/core/qpack/qpack_header_table.h
@@ -56,9 +56,9 @@
   // is larger than the capacity of the dynamic table.
   const QpackEntry* InsertEntry(QuicStringPiece name, QuicStringPiece value);
 
-  // Change dynamic table capacity to |max_size|.  Returns true on success.
-  // Returns false is |max_size| exceeds maximum dynamic table capacity.
-  bool UpdateTableSize(uint64_t max_size);
+  // Change dynamic table capacity to |capacity|.  Returns true on success.
+  // Returns false is |capacity| exceeds maximum dynamic table capacity.
+  bool SetDynamicTableCapacity(uint64_t capacity);
 
   // Set |maximum_dynamic_table_capacity_|.  The initial value is zero.  The
   // final value is determined by the decoder and is sent to the encoder as
@@ -68,7 +68,7 @@
   // This method must only be called at most once.
   void SetMaximumDynamicTableCapacity(uint64_t maximum_dynamic_table_capacity);
 
-  // Used by request streams to decode Largest Reference.
+  // Used on request streams to encode and decode Required Insert Count.
   uint64_t max_entries() const { return max_entries_; }
 
   // The number of entries inserted to the dynamic table (including ones that
diff --git a/net/third_party/quic/core/qpack/qpack_header_table_test.cc b/net/third_party/quic/core/qpack/qpack_header_table_test.cc
index 6f4496f..b058844 100644
--- a/net/third_party/quic/core/qpack/qpack_header_table_test.cc
+++ b/net/third_party/quic/core/qpack/qpack_header_table_test.cc
@@ -76,8 +76,8 @@
     EXPECT_FALSE(table_.InsertEntry(name, value));
   }
 
-  bool UpdateTableSize(uint64_t max_size) {
-    return table_.UpdateTableSize(max_size);
+  bool SetDynamicTableCapacity(uint64_t capacity) {
+    return table_.SetDynamicTableCapacity(capacity);
   }
 
   uint64_t max_entries() const { return table_.max_entries(); }
@@ -237,20 +237,21 @@
   EXPECT_EQ(15u, table2.max_entries());
 }
 
-TEST_F(QpackHeaderTableTest, UpdateTableSize) {
+TEST_F(QpackHeaderTableTest, SetDynamicTableCapacity) {
   // Dynamic table capacity does not affect MaxEntries.
-  EXPECT_TRUE(UpdateTableSize(1024));
+  EXPECT_TRUE(SetDynamicTableCapacity(1024));
   EXPECT_EQ(32u * 1024, max_entries());
 
-  EXPECT_TRUE(UpdateTableSize(500));
+  EXPECT_TRUE(SetDynamicTableCapacity(500));
   EXPECT_EQ(32u * 1024, max_entries());
 
   // Dynamic table capacity cannot exceed maximum dynamic table capacity.
-  EXPECT_FALSE(UpdateTableSize(2 * kMaximumDynamicTableCapacityForTesting));
+  EXPECT_FALSE(
+      SetDynamicTableCapacity(2 * kMaximumDynamicTableCapacityForTesting));
 }
 
 TEST_F(QpackHeaderTableTest, EvictByInsertion) {
-  EXPECT_TRUE(UpdateTableSize(40));
+  EXPECT_TRUE(SetDynamicTableCapacity(40));
 
   // Entry size is 3 + 3 + 32 = 38.
   InsertEntry("foo", "bar");
@@ -285,7 +286,7 @@
   ExpectMatch("baz", "qux", QpackHeaderTable::MatchType::kNameAndValue,
               /* expected_is_static = */ false, 1u);
 
-  EXPECT_TRUE(UpdateTableSize(40));
+  EXPECT_TRUE(SetDynamicTableCapacity(40));
   EXPECT_EQ(2u, inserted_entry_count());
   EXPECT_EQ(1u, dropped_entry_count());
 
@@ -293,7 +294,7 @@
   ExpectMatch("baz", "qux", QpackHeaderTable::MatchType::kNameAndValue,
               /* expected_is_static = */ false, 1u);
 
-  EXPECT_TRUE(UpdateTableSize(20));
+  EXPECT_TRUE(SetDynamicTableCapacity(20));
   EXPECT_EQ(2u, inserted_entry_count());
   EXPECT_EQ(2u, dropped_entry_count());
 
@@ -302,7 +303,7 @@
 }
 
 TEST_F(QpackHeaderTableTest, EvictOldestOfIdentical) {
-  EXPECT_TRUE(UpdateTableSize(80));
+  EXPECT_TRUE(SetDynamicTableCapacity(80));
 
   // Entry size is 3 + 3 + 32 = 38.
   // Insert same entry twice.
@@ -327,7 +328,7 @@
 }
 
 TEST_F(QpackHeaderTableTest, EvictOldestOfSameName) {
-  EXPECT_TRUE(UpdateTableSize(80));
+  EXPECT_TRUE(SetDynamicTableCapacity(80));
 
   // Entry size is 3 + 3 + 32 = 38.
   // Insert two entries with same name but different values.
diff --git a/net/third_party/quic/core/qpack/qpack_progressive_decoder.cc b/net/third_party/quic/core/qpack/qpack_progressive_decoder.cc
index 989166a..a23cd22 100644
--- a/net/third_party/quic/core/qpack/qpack_progressive_decoder.cc
+++ b/net/third_party/quic/core/qpack/qpack_progressive_decoder.cc
@@ -23,21 +23,21 @@
       header_table_(header_table),
       decoder_stream_sender_(decoder_stream_sender),
       handler_(handler),
-      largest_reference_(0),
-      base_index_(0),
-      largest_reference_seen_(0),
+      required_insert_count_(0),
+      base_(0),
+      required_insert_count_so_far_(0),
       prefix_decoded_(false),
       decoding_(true),
       error_detected_(false) {}
 
 // static
-bool QpackProgressiveDecoder::DecodeLargestReference(
-    uint64_t wire_largest_reference,
+bool QpackProgressiveDecoder::DecodeRequiredInsertCount(
+    uint64_t encoded_required_insert_count,
     uint64_t max_entries,
     uint64_t total_number_of_inserts,
-    uint64_t* largest_reference) {
-  if (wire_largest_reference == 0) {
-    *largest_reference = 0;
+    uint64_t* required_insert_count) {
+  if (encoded_required_insert_count == 0) {
+    *required_insert_count = 0;
     return true;
   }
 
@@ -45,37 +45,37 @@
   // precluding all calculations in this method from overflowing.
   DCHECK_LE(max_entries, std::numeric_limits<uint64_t>::max() / 32);
 
-  if (wire_largest_reference > 2 * max_entries) {
+  if (encoded_required_insert_count > 2 * max_entries) {
     return false;
   }
 
-  *largest_reference = wire_largest_reference - 1;
-  DCHECK_LT(*largest_reference, std::numeric_limits<uint64_t>::max() / 16);
+  *required_insert_count = encoded_required_insert_count - 1;
+  DCHECK_LT(*required_insert_count, std::numeric_limits<uint64_t>::max() / 16);
 
   uint64_t current_wrapped = total_number_of_inserts % (2 * max_entries);
   DCHECK_LT(current_wrapped, std::numeric_limits<uint64_t>::max() / 16);
 
-  if (current_wrapped >= *largest_reference + max_entries) {
-    // Largest Reference wrapped around 1 extra time.
-    *largest_reference += 2 * max_entries;
-  } else if (current_wrapped + max_entries < *largest_reference) {
+  if (current_wrapped >= *required_insert_count + max_entries) {
+    // Required Insert Count wrapped around 1 extra time.
+    *required_insert_count += 2 * max_entries;
+  } else if (current_wrapped + max_entries < *required_insert_count) {
     // Decoder wrapped around 1 extra time.
     current_wrapped += 2 * max_entries;
   }
 
-  if (*largest_reference >
+  if (*required_insert_count >
       std::numeric_limits<uint64_t>::max() - total_number_of_inserts) {
     return false;
   }
 
-  *largest_reference += total_number_of_inserts;
+  *required_insert_count += total_number_of_inserts;
 
-  // Prevent underflow, but also disallow invalid value 0 for Largest Reference.
-  if (current_wrapped >= *largest_reference) {
+  // Prevent underflow, also disallow invalid value 0 for Required Insert Count.
+  if (current_wrapped >= *required_insert_count) {
     return false;
   }
 
-  *largest_reference -= current_wrapped;
+  *required_insert_count -= current_wrapped;
 
   return true;
 }
@@ -118,8 +118,8 @@
     return;
   }
 
-  if (largest_reference_ != largest_reference_seen_) {
-    OnError("Largest Reference too large.");
+  if (required_insert_count_ != required_insert_count_so_far_) {
+    OnError("Required Insert Count too large.");
     return;
   }
 
@@ -164,17 +164,17 @@
       return false;
     }
 
-    if (absolute_index > largest_reference_) {
-      OnError("Index larger than Largest Reference.");
+    if (absolute_index >= required_insert_count_) {
+      OnError("Absolute Index must be smaller than Required Insert Count.");
       return false;
     }
 
-    largest_reference_seen_ = std::max(largest_reference_seen_, absolute_index);
+    DCHECK_LT(absolute_index, std::numeric_limits<uint64_t>::max());
+    required_insert_count_so_far_ =
+        std::max(required_insert_count_so_far_, absolute_index + 1);
 
-    DCHECK_NE(0u, absolute_index);
-    const uint64_t real_index = absolute_index - 1;
     auto entry =
-        header_table_->LookupEntry(/* is_static = */ false, real_index);
+        header_table_->LookupEntry(/* is_static = */ false, absolute_index);
     if (!entry) {
       OnError("Dynamic table entry not found.");
       return false;
@@ -203,16 +203,17 @@
     return false;
   }
 
-  if (absolute_index > largest_reference_) {
-    OnError("Index larger than Largest Reference.");
+  if (absolute_index >= required_insert_count_) {
+    OnError("Absolute Index must be smaller than Required Insert Count.");
     return false;
   }
 
-  largest_reference_seen_ = std::max(largest_reference_seen_, absolute_index);
+  DCHECK_LT(absolute_index, std::numeric_limits<uint64_t>::max());
+  required_insert_count_so_far_ =
+      std::max(required_insert_count_so_far_, absolute_index + 1);
 
-  DCHECK_NE(0u, absolute_index);
-  const uint64_t real_index = absolute_index - 1;
-  auto entry = header_table_->LookupEntry(/* is_static = */ false, real_index);
+  auto entry =
+      header_table_->LookupEntry(/* is_static = */ false, absolute_index);
   if (!entry) {
     OnError("Dynamic table entry not found.");
     return false;
@@ -231,17 +232,17 @@
       return false;
     }
 
-    if (absolute_index > largest_reference_) {
-      OnError("Index larger than Largest Reference.");
+    if (absolute_index >= required_insert_count_) {
+      OnError("Absolute Index must be smaller than Required Insert Count.");
       return false;
     }
 
-    largest_reference_seen_ = std::max(largest_reference_seen_, absolute_index);
+    DCHECK_LT(absolute_index, std::numeric_limits<uint64_t>::max());
+    required_insert_count_so_far_ =
+        std::max(required_insert_count_so_far_, absolute_index + 1);
 
-    DCHECK_NE(0u, absolute_index);
-    const uint64_t real_index = absolute_index - 1;
     auto entry =
-        header_table_->LookupEntry(/* is_static = */ false, real_index);
+        header_table_->LookupEntry(/* is_static = */ false, absolute_index);
     if (!entry) {
       OnError("Dynamic table entry not found.");
       return false;
@@ -270,16 +271,17 @@
     return false;
   }
 
-  if (absolute_index > largest_reference_) {
-    OnError("Index larger than Largest Reference.");
+  if (absolute_index >= required_insert_count_) {
+    OnError("Absolute Index must be smaller than Required Insert Count.");
     return false;
   }
 
-  largest_reference_seen_ = std::max(largest_reference_seen_, absolute_index);
+  DCHECK_LT(absolute_index, std::numeric_limits<uint64_t>::max());
+  required_insert_count_so_far_ =
+      std::max(required_insert_count_so_far_, absolute_index + 1);
 
-  DCHECK_NE(0u, absolute_index);
-  const uint64_t real_index = absolute_index - 1;
-  auto entry = header_table_->LookupEntry(/* is_static = */ false, real_index);
+  auto entry =
+      header_table_->LookupEntry(/* is_static = */ false, absolute_index);
   if (!entry) {
     OnError("Dynamic table entry not found.");
     return false;
@@ -299,17 +301,17 @@
 bool QpackProgressiveDecoder::DoPrefixInstruction() {
   DCHECK(!prefix_decoded_);
 
-  if (!DecodeLargestReference(
+  if (!DecodeRequiredInsertCount(
           prefix_decoder_->varint(), header_table_->max_entries(),
-          header_table_->inserted_entry_count(), &largest_reference_)) {
-    OnError("Error decoding Largest Reference.");
+          header_table_->inserted_entry_count(), &required_insert_count_)) {
+    OnError("Error decoding Required Insert Count.");
     return false;
   }
 
   const bool sign = prefix_decoder_->s_bit();
-  const uint64_t delta_base_index = prefix_decoder_->varint2();
-  if (!DeltaBaseIndexToBaseIndex(sign, delta_base_index, &base_index_)) {
-    OnError("Error calculating Base Index.");
+  const uint64_t delta_base = prefix_decoder_->varint2();
+  if (!DeltaBaseToBase(sign, delta_base, &base_)) {
+    OnError("Error calculating Base.");
     return false;
   }
 
@@ -318,24 +320,23 @@
   return true;
 }
 
-bool QpackProgressiveDecoder::DeltaBaseIndexToBaseIndex(
-    bool sign,
-    uint64_t delta_base_index,
-    uint64_t* base_index) {
+bool QpackProgressiveDecoder::DeltaBaseToBase(bool sign,
+                                              uint64_t delta_base,
+                                              uint64_t* base) {
   if (sign) {
-    if (delta_base_index == std::numeric_limits<uint64_t>::max() ||
-        largest_reference_ < delta_base_index + 1) {
+    if (delta_base == std::numeric_limits<uint64_t>::max() ||
+        required_insert_count_ < delta_base + 1) {
       return false;
     }
-    *base_index = largest_reference_ - delta_base_index - 1;
+    *base = required_insert_count_ - delta_base - 1;
     return true;
   }
 
-  if (delta_base_index >
-      std::numeric_limits<uint64_t>::max() - largest_reference_) {
+  if (delta_base >
+      std::numeric_limits<uint64_t>::max() - required_insert_count_) {
     return false;
   }
-  *base_index = largest_reference_ + delta_base_index;
+  *base = required_insert_count_ + delta_base;
   return true;
 }
 
@@ -343,22 +344,22 @@
     uint64_t relative_index,
     uint64_t* absolute_index) const {
   if (relative_index == std::numeric_limits<uint64_t>::max() ||
-      relative_index + 1 > base_index_) {
+      relative_index + 1 > base_) {
     return false;
   }
 
-  *absolute_index = base_index_ - relative_index;
+  *absolute_index = base_ - 1 - relative_index;
   return true;
 }
 
 bool QpackProgressiveDecoder::PostBaseIndexToAbsoluteIndex(
     uint64_t post_base_index,
     uint64_t* absolute_index) const {
-  if (post_base_index >= std::numeric_limits<uint64_t>::max() - base_index_) {
+  if (post_base_index >= std::numeric_limits<uint64_t>::max() - base_) {
     return false;
   }
 
-  *absolute_index = base_index_ + post_base_index + 1;
+  *absolute_index = base_ + post_base_index;
   return true;
 }
 
diff --git a/net/third_party/quic/core/qpack/qpack_progressive_decoder.h b/net/third_party/quic/core/qpack/qpack_progressive_decoder.h
index 5b9bc89..8fa1909 100644
--- a/net/third_party/quic/core/qpack/qpack_progressive_decoder.h
+++ b/net/third_party/quic/core/qpack/qpack_progressive_decoder.h
@@ -56,14 +56,14 @@
   QpackProgressiveDecoder& operator=(const QpackProgressiveDecoder&) = delete;
   ~QpackProgressiveDecoder() override = default;
 
-  // Calculate actual Largest Reference from largest reference value sent on
-  // wire, MaxEntries, and total number of dynamic table insertions according to
-  // https://quicwg.org/base-drafts/draft-ietf-quic-qpack.html#largest-reference
+  // Calculate Required Insert Count from Encoded Required Insert Count,
+  // MaxEntries, and total number of dynamic table insertions according to
+  // https://quicwg.org/base-drafts/draft-ietf-quic-qpack.html#ric.
   // Returns true on success, false on invalid input or overflow/underflow.
-  static bool DecodeLargestReference(uint64_t wire_largest_reference,
-                                     uint64_t max_entries,
-                                     uint64_t total_number_of_inserts,
-                                     uint64_t* largest_reference);
+  static bool DecodeRequiredInsertCount(uint64_t encoded_required_insert_count,
+                                        uint64_t max_entries,
+                                        uint64_t total_number_of_inserts,
+                                        uint64_t* required_insert_count);
 
   // Provide a data fragment to decode.
   void Decode(QuicStringPiece data);
@@ -84,19 +84,18 @@
   bool DoLiteralHeaderFieldInstruction();
   bool DoPrefixInstruction();
 
-  // Calculates Base Index from |largest_reference_|, which must be set before
-  // calling this method, and sign bit and Delta Base Index in the Header Data
-  // Prefix, which are passed in as arguments.  Returns true on success, false
-  // on failure due to overflow/underflow.
-  bool DeltaBaseIndexToBaseIndex(bool sign,
-                                 uint64_t delta_base_index,
-                                 uint64_t* base_index);
+  // Calculates Base from |required_insert_count_|, which must be set before
+  // calling this method, and sign bit and Delta Base in the Header Data Prefix,
+  // which are passed in as arguments.  Returns true on success, false on
+  // failure due to overflow/underflow.
+  bool DeltaBaseToBase(bool sign, uint64_t delta_base, uint64_t* base);
 
   // The request stream can use relative index (but different from the kind of
   // relative index used on the encoder stream), and post-base index.
   // These methods convert relative index and post-base index to absolute index
   // (one based).  They return true on success, or false if conversion fails due
-  // to overflow/underflow.
+  // to overflow/underflow.  On success, |*absolute_index| is guaranteed to be
+  // strictly less than std::numeric_limits<uint64_t>::max().
   bool RequestStreamRelativeIndexToAbsoluteIndex(
       uint64_t relative_index,
       uint64_t* absolute_index) const;
@@ -115,14 +114,16 @@
   QpackDecoderStreamSender* const decoder_stream_sender_;
   HeadersHandlerInterface* const handler_;
 
-  // Largest Reference and Base Index are parsed from the Header Data Prefix.
-  // They are both absolute indices, that is, one based.
-  uint64_t largest_reference_;
-  uint64_t base_index_;
+  // Required Insert Count and Base are decoded from the Header Data Prefix.
+  uint64_t required_insert_count_;
+  uint64_t base_;
 
-  // Keep track of largest reference seen in this header block.
-  // After decoding is completed, this can be compared to |largest_reference_|.
-  uint64_t largest_reference_seen_;
+  // Required Insert Count is one larger than the largest absolute index of all
+  // referenced dynamic table entries, or zero if no dynamic table entries are
+  // referenced.  |required_insert_count_so_far_| starts out as zero and keeps
+  // track of the Required Insert Count based on entries decoded so far.
+  // After decoding is completed, it is compared to |required_insert_count_|.
+  uint64_t required_insert_count_so_far_;
 
   // False until prefix is fully read and decoded.
   bool prefix_decoded_;
diff --git a/net/third_party/quic/core/qpack/qpack_progressive_decoder_test.cc b/net/third_party/quic/core/qpack/qpack_progressive_decoder_test.cc
index c88c50b4..5812be3 100644
--- a/net/third_party/quic/core/qpack/qpack_progressive_decoder_test.cc
+++ b/net/third_party/quic/core/qpack/qpack_progressive_decoder_test.cc
@@ -11,12 +11,12 @@
 namespace test {
 namespace {
 
-// For testing valid decodings, the encoded (wire) largest reference value is
-// calculated for actual Largest Reference values, so that there is an expected
-// value to comparte the decoded value against, and so that intricate
-// inequalities can be documented.
+// For testing valid decodings, the Encoded Required Insert Count is calculated
+// from Required Insert Count, so that there is an expected value to compare
+// the decoded value against, and so that intricate inequalities can be
+// documented.
 struct {
-  uint64_t largest_reference;
+  uint64_t required_insert_count;
   uint64_t max_entries;
   uint64_t total_number_of_inserts;
 } kTestData[] = {
@@ -25,70 +25,72 @@
     // No dynamic entries in header.
     {0, 100, 0},
     {0, 100, 500},
-    // Largest Reference has not wrapped around yet, no entries evicted.
+    // Required Insert Count has not wrapped around yet, no entries evicted.
     {15, 100, 25},
     {20, 100, 10},
-    // Largest Reference has not wrapped around yet, some entries evicted.
+    // Required Insert Count has not wrapped around yet, some entries evicted.
     {90, 100, 110},
-    // Largest Reference has wrapped around.
+    // Required Insert Count has wrapped around.
     {234, 100, 180},
-    // Largest Reference has wrapped around many times.
+    // Required Insert Count has wrapped around many times.
     {5678, 100, 5701},
-    // Lowest and highest possible Largest Reference values
+    // Lowest and highest possible Required Insert Count values
     // for given MaxEntries and total number of insertions.
     {401, 100, 500},
     {600, 100, 500}};
 
-uint64_t EncodeLargestReference(uint64_t largest_reference,
-                                uint64_t max_entries) {
-  if (largest_reference == 0) {
+uint64_t EncodeRequiredInsertCount(uint64_t required_insert_count,
+                                   uint64_t max_entries) {
+  if (required_insert_count == 0) {
     return 0;
   }
 
-  return largest_reference % (2 * max_entries) + 1;
+  return required_insert_count % (2 * max_entries) + 1;
 }
 
-TEST(QpackProgressiveDecoderTest, DecodeLargestReference) {
+TEST(QpackProgressiveDecoderTest, DecodeRequiredInsertCount) {
   for (size_t i = 0; i < QUIC_ARRAYSIZE(kTestData); ++i) {
-    const uint64_t largest_reference = kTestData[i].largest_reference;
+    const uint64_t required_insert_count = kTestData[i].required_insert_count;
     const uint64_t max_entries = kTestData[i].max_entries;
     const uint64_t total_number_of_inserts =
         kTestData[i].total_number_of_inserts;
 
-    if (largest_reference != 0) {
+    if (required_insert_count != 0) {
       // Dynamic entries cannot be referenced if dynamic table capacity is zero.
       ASSERT_LT(0u, max_entries) << i;
-      // Entry |total_number_of_inserts - max_entries| and earlier entries are
-      // evicted.  Entry |largest_reference| is referenced.  No evicted entry
-      // can be referenced.
-      ASSERT_LT(total_number_of_inserts, largest_reference + max_entries) << i;
-      // Entry |largest_reference - max_entries| and earlier entries are
-      // evicted, entry |total_number_of_inserts| is the last acknowledged
+      // Entry |total_number_of_inserts - 1 - max_entries| and earlier entries
+      // are evicted.  Entry |required_insert_count - 1| is referenced.  No
+      // evicted entry can be referenced.
+      ASSERT_LT(total_number_of_inserts, required_insert_count + max_entries)
+          << i;
+      // Entry |required_insert_count - 1 - max_entries| and earlier entries are
+      // evicted, entry |total_number_of_inserts - 1| is the last acknowledged
       // entry.  Every evicted entry must be acknowledged.
-      ASSERT_LE(largest_reference, total_number_of_inserts + max_entries) << i;
+      ASSERT_LE(required_insert_count, total_number_of_inserts + max_entries)
+          << i;
     }
 
-    uint64_t wire_largest_reference =
-        EncodeLargestReference(largest_reference, max_entries);
+    uint64_t encoded_required_insert_count =
+        EncodeRequiredInsertCount(required_insert_count, max_entries);
 
     // Initialize to a value different from the expected output to confirm that
-    // DecodeLargestReference() modifies the value of
-    // |decoded_largest_reference|.
-    uint64_t decoded_largest_reference = largest_reference + 1;
-    EXPECT_TRUE(QpackProgressiveDecoder::DecodeLargestReference(
-        wire_largest_reference, max_entries, total_number_of_inserts,
-        &decoded_largest_reference))
+    // DecodeRequiredInsertCount() modifies the value of
+    // |decoded_required_insert_count|.
+    uint64_t decoded_required_insert_count = required_insert_count + 1;
+    EXPECT_TRUE(QpackProgressiveDecoder::DecodeRequiredInsertCount(
+        encoded_required_insert_count, max_entries, total_number_of_inserts,
+        &decoded_required_insert_count))
         << i;
 
-    EXPECT_EQ(decoded_largest_reference, largest_reference) << i;
+    EXPECT_EQ(decoded_required_insert_count, required_insert_count) << i;
   }
 }
 
-// Failures are tested with hardcoded values for the on-the-wire largest
-// reference field, to provide test coverage for values that would never be
-// produced by a well behaved encoding function.
+// Failures are tested with hardcoded values for encoded required insert count,
+// to provide test coverage for values that would never be produced by a well
+// behaved encoding function.
 struct {
-  uint64_t wire_largest_reference;
+  uint64_t encoded_required_insert_count;
   uint64_t max_entries;
   uint64_t total_number_of_inserts;
 } kInvalidTestData[] = {
@@ -100,19 +102,19 @@
     // https://github.com/quicwg/base-drafts/issues/2112#issue-389626872.
     {1, 10, 2},
     {18, 10, 2},
-    // Largest Reference value too small or too large
+    // Encoded Required Insert Count value too small or too large
     // for given MaxEntries and total number of insertions.
     {400, 100, 500},
     {601, 100, 500}};
 
-TEST(QpackProgressiveDecoderTest, DecodeLargestReferenceError) {
+TEST(QpackProgressiveDecoderTest, DecodeRequiredInsertCountError) {
   for (size_t i = 0; i < QUIC_ARRAYSIZE(kInvalidTestData); ++i) {
-    uint64_t decoded_largest_reference = 0;
-    EXPECT_FALSE(QpackProgressiveDecoder::DecodeLargestReference(
-        kInvalidTestData[i].wire_largest_reference,
+    uint64_t decoded_required_insert_count = 0;
+    EXPECT_FALSE(QpackProgressiveDecoder::DecodeRequiredInsertCount(
+        kInvalidTestData[i].encoded_required_insert_count,
         kInvalidTestData[i].max_entries,
         kInvalidTestData[i].total_number_of_inserts,
-        &decoded_largest_reference))
+        &decoded_required_insert_count))
         << i;
   }
 }
diff --git a/net/third_party/quic/core/qpack/qpack_progressive_encoder.cc b/net/third_party/quic/core/qpack/qpack_progressive_encoder.cc
index ebfc038e..7c7d7dd 100644
--- a/net/third_party/quic/core/qpack/qpack_progressive_encoder.cc
+++ b/net/third_party/quic/core/qpack/qpack_progressive_encoder.cc
@@ -48,8 +48,8 @@
   DCHECK_LT(output->size(), max_length);
 
   if (!prefix_encoded_ && !instruction_encoder_.HasNext()) {
-    // TODO(bnc): Implement dynamic entries and set Largest Reference and
-    // Delta Base Index accordingly.
+    // TODO(bnc): Implement dynamic entries and set Required Insert Count and
+    // Delta Base accordingly.
     instruction_encoder_.set_varint(0);
     instruction_encoder_.set_varint2(0);
     instruction_encoder_.set_s_bit(false);
diff --git a/skia/BUILD.gn b/skia/BUILD.gn
index 0f96063..590eb21 100644
--- a/skia/BUILD.gn
+++ b/skia/BUILD.gn
@@ -33,6 +33,7 @@
     "config",
     "ext",
     "//third_party/skia/include/c",
+    "//third_party/skia/include/codec",
     "//third_party/skia/include/config",
     "//third_party/skia/include/core",
     "//third_party/skia/include/docs",
@@ -57,7 +58,6 @@
   ]
 
   if (!is_ios) {
-    include_dirs += [ "//third_party/skia/include/codec" ]
     defines += [ "SK_HAS_JPEG_LIBRARY" ]
   }
   if (enable_vulkan) {
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 1b75e321..72f3a01 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -4365,6 +4365,24 @@
             ]
         }
     ],
+    "ShowManagedUi": [
+        {
+            "platforms": [
+                "chromeos",
+                "linux",
+                "mac",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "ShowManagedUi"
+                    ]
+                }
+            ]
+        }
+    ],
     "SignInPasswordPromo": [
         {
             "platforms": [
diff --git a/third_party/blink/common/origin_trials/OWNERS b/third_party/blink/common/origin_trials/OWNERS
index 7627713..cd5f7a43 100644
--- a/third_party/blink/common/origin_trials/OWNERS
+++ b/third_party/blink/common/origin_trials/OWNERS
@@ -1,6 +1,7 @@
 # This file also covers ownership of the following directories:
 #   //chrome/common/origin_trials/
 #   //third_party/blink/public/common/origin_trials/
+#   //third_party/blink/renderer/core/origin_trials/
 #   //tools/origin_trials/
 
 chasej@chromium.org
diff --git a/third_party/blink/public/common/media/OWNERS b/third_party/blink/public/common/media/OWNERS
new file mode 100644
index 0000000..4c71fe7
--- /dev/null
+++ b/third_party/blink/public/common/media/OWNERS
@@ -0,0 +1,3 @@
+file://media/OWNERS
+
+# COMPONENT: Internals>Media
diff --git a/third_party/blink/public/mojom/payments/payment_request.mojom b/third_party/blink/public/mojom/payments/payment_request.mojom
index fc315100..9135e62 100644
--- a/third_party/blink/public/mojom/payments/payment_request.mojom
+++ b/third_party/blink/public/mojom/payments/payment_request.mojom
@@ -234,5 +234,5 @@
   // Queries whether support for the merchant-specified payment method is
   // available and the user has an enrolled instrument for that payment method
   // that is ready to pay.
-  HasEnrolledInstrument();
+  HasEnrolledInstrument(bool per_method_quota);
 };
diff --git a/third_party/blink/public/platform/web_feature.mojom b/third_party/blink/public/platform/web_feature.mojom
index 99530dd..1d8af2f 100644
--- a/third_party/blink/public/platform/web_feature.mojom
+++ b/third_party/blink/public/platform/web_feature.mojom
@@ -2205,6 +2205,7 @@
   kNoSysexWebMIDIWithoutPermission = 2770,
   kNoSysexWebMIDIOnInsecureOrigin = 2771,
   kApplicationCacheInstalledButNoManifest = 2772,
+  kPerMethodCanMakePaymentQuota = 2773,
 
   // Add new features immediately above this line. Don't change assigned
   // numbers of any item, and don't reuse removed slots.
diff --git a/third_party/blink/public/platform/web_graphics_context_3d_provider.h b/third_party/blink/public/platform/web_graphics_context_3d_provider.h
index e60c111..14b20a3 100644
--- a/third_party/blink/public/platform/web_graphics_context_3d_provider.h
+++ b/third_party/blink/public/platform/web_graphics_context_3d_provider.h
@@ -44,6 +44,7 @@
 namespace gpu {
 struct Capabilities;
 struct GpuFeatureInfo;
+class SharedImageInterface;
 
 namespace gles2 {
 class GLES2Interface;
@@ -82,6 +83,7 @@
   virtual cc::ImageDecodeCache* ImageDecodeCache(
       SkColorType color_type,
       sk_sp<SkColorSpace> color_space) = 0;
+  virtual gpu::SharedImageInterface* SharedImageInterface() = 0;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/public/web/web_navigation_params.h b/third_party/blink/public/web/web_navigation_params.h
index 94a9f07c..45cc9374 100644
--- a/third_party/blink/public/web/web_navigation_params.h
+++ b/third_party/blink/public/web/web_navigation_params.h
@@ -161,6 +161,16 @@
       const KURL& base_url);
 #endif
 
+  // Fills |body_loader| based on the provided static data.
+  static void FillBodyLoader(WebNavigationParams*, base::span<const char> data);
+
+  // Fills |response| and |body_loader| based on the provided static data.
+  // |url| must be set already.
+  static void FillStaticResponse(WebNavigationParams*,
+                                 WebString mime_type,
+                                 WebString text_encoding,
+                                 base::span<const char> data);
+
   // This block defines the request used to load the main resource
   // for this navigation.
 
@@ -191,17 +201,17 @@
 
   // This block defines the document content. The alternatives in the order
   // of precedence are:
-  // 1. If |data| is supplied, it is used as document content.
-  // 2. If url loads as an empty document (according to
-  //    WebDocumentLoader::WillLoadUrlAsEmpty), the document will be empty.
-  // 3. If loading an iframe of mhtml archive, the document will be
-  //    retrieved from the archive.
-  // 4. Otherwise, provided redirects and response are used to construct
+  // 1. If |is_static_data| is false:
+  //   1a. If url loads as an empty document (according to
+  //       WebDocumentLoader::WillLoadUrlAsEmpty), the document will be empty.
+  //   1b. If loading an iframe of mhtml archive, the document will be
+  //       retrieved from the archive.
+  // 2. Otherwise, provided redirects and response are used to construct
   //    the final response.
-  //   4a. If body loader is present, it will be used to fetch the content.
-  //   4b. If body loader is missing, but url is a data url, it will be
+  //   2a. If body loader is present, it will be used to fetch the content.
+  //   2b. If body loader is missing, but url is a data url, it will be
   //       decoded and used as response and document content.
-  //   4c. If decoding data url fails, or url is not a data url, the
+  //   2c. If decoding data url fails, or url is not a data url, the
   //       navigation will fail.
 
   struct RedirectInfo {
@@ -226,15 +236,9 @@
   WebURLResponse response;
   // The body loader which allows to retrieve the response body when available.
   std::unique_ptr<WebNavigationBodyLoader> body_loader;
-
-  // If the data is non null, it will be used as a main resource content
-  // instead of redirects, response and body loader.
-  WebData data;
-  // Specifies the mime type of the raw data. Must be set together with the
-  // data.
-  WebString mime_type;
-  // The encoding of the raw data. Must be set together with the data.
-  WebString text_encoding;
+  // Whether |response| and |body_loader| represent static data. In this case
+  // we skip some security checks and insist on loading this exact content.
+  bool is_static_data = false;
 
   // This block defines the type of the navigation.
 
diff --git a/third_party/blink/renderer/bindings/BUILD.gn b/third_party/blink/renderer/bindings/BUILD.gn
deleted file mode 100644
index 3c35b997..0000000
--- a/third_party/blink/renderer/bindings/BUILD.gn
+++ /dev/null
@@ -1,8 +0,0 @@
-# Copyright 2014 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("//third_party/blink/renderer/bindings/bindings.gni")
-import("//third_party/blink/renderer/build/scripts/scripts.gni")
-import("//third_party/blink/renderer/core/core.gni")
-import("//third_party/blink/renderer/modules/modules.gni")
diff --git a/third_party/blink/renderer/bindings/core/v8/v8_wasm_response_extensions.cc b/third_party/blink/renderer/bindings/core/v8/v8_wasm_response_extensions.cc
index 7cbc75cf..c50c77d 100644
--- a/third_party/blink/renderer/bindings/core/v8/v8_wasm_response_extensions.cc
+++ b/third_party/blink/renderer/bindings/core/v8/v8_wasm_response_extensions.cc
@@ -8,31 +8,19 @@
 #include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_response.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/fetch/body_stream_buffer.h"
 #include "third_party/blink/renderer/core/fetch/fetch_data_loader.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/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/bindings/script_state.h"
 #include "third_party/blink/renderer/platform/bindings/trace_wrapper_member.h"
 #include "third_party/blink/renderer/platform/bindings/v8_per_isolate_data.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
-#include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
-#include "third_party/blink/renderer/platform/loader/fetch/cached_metadata.h"
-#include "third_party/blink/renderer/platform/loader/fetch/raw_resource.h"
-#include "third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h"
-#include "third_party/blink/renderer/platform/loader/fetch/script_cached_metadata_handler.h"
-#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
 
 namespace blink {
 
 namespace {
 
-// Wasm only has a single metadata type, but we need to tag it.
-static const int kWasmModuleTag = 1;
-
 // The |FetchDataLoader| for streaming compilation of WebAssembly code. The
 // received bytes get forwarded to the V8 API class |WasmStreaming|.
 class FetchDataLoaderForWasmStreaming final : public FetchDataLoader,
@@ -40,12 +28,10 @@
   USING_GARBAGE_COLLECTED_MIXIN(FetchDataLoaderForWasmStreaming);
 
  public:
-  FetchDataLoaderForWasmStreaming(std::shared_ptr<v8::WasmStreaming> streaming,
-                                  ScriptState* script_state)
+  FetchDataLoaderForWasmStreaming(ScriptState* script_state,
+                                  std::shared_ptr<v8::WasmStreaming> streaming)
       : streaming_(std::move(streaming)), script_state_(script_state) {}
 
-  v8::WasmStreaming* streaming() const { return streaming_.get(); }
-
   void Start(BytesConsumer* consumer,
              FetchDataLoader::Client* client) override {
     DCHECK(!consumer_);
@@ -126,8 +112,7 @@
       streaming_->Abort(v8::Local<v8::Value>());
     }
   }
-
-  Member<BytesConsumer> consumer_;
+  TraceWrapperMember<BytesConsumer> consumer_;
   Member<FetchDataLoader::Client> client_;
   std::shared_ptr<v8::WasmStreaming> streaming_;
   const Member<ScriptState> script_state_;
@@ -182,87 +167,8 @@
   ExceptionState& exception_state_;
 };
 
-SingleCachedMetadataHandler* GetCachedMetadataHandler(ScriptState* script_state,
-                                                      const KURL& url) {
-  if (!RuntimeEnabledFeatures::WasmCodeCacheEnabled())
-    return nullptr;
-  ExecutionContext* execution_context = ExecutionContext::From(script_state);
-  if (!execution_context)
-    return nullptr;
-  ResourceFetcher* fetcher = execution_context->Fetcher();
-  if (!fetcher)
-    return nullptr;
-  if (!url.IsValid())
-    return nullptr;
-  Resource* resource = fetcher->CachedResource(url);
-  if (!resource)
-    return nullptr;
-
-  // Wasm modules should be fetched as raw resources.
-  DCHECK_EQ(ResourceType::kRaw, resource->GetType());
-  RawResource* raw_resource = ToRawResource(resource);
-  return raw_resource->ScriptCacheHandler();
-}
-
-class WasmStreamingClient : public v8::WasmStreaming::Client {
- public:
-  WasmStreamingClient(const KURL& url,
-                      v8::Isolate* isolate,
-                      v8::Local<v8::Context> context)
-      : url_(url), isolate_(isolate), context_(isolate, context) {
-    context_.SetWeak();
-  }
-
-  void OnModuleCompiled(v8::CompiledWasmModule compiled_module) override {
-    TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"),
-                         "v8.wasm.compiledModule", TRACE_EVENT_SCOPE_THREAD,
-                         "url", url_.GetString().Utf8());
-
-    // Don't cache if Context has been destroyed.
-    if (context_.IsEmpty())
-      return;
-
-    v8::HandleScope handle_scope(isolate_);
-    auto context = context_.Get(isolate_);
-    ScriptState* script_state = ScriptState::From(context);
-    SingleCachedMetadataHandler* cache_handler =
-        GetCachedMetadataHandler(script_state, url_);
-    if (!cache_handler)
-      return;
-
-    v8::MemorySpan<const uint8_t> wire_bytes =
-        compiled_module.GetWireBytesRef();
-    // Our heuristic for whether it's worthwhile to cache is that the module
-    // was fully compiled and it is "large". Wire bytes size is likely to be
-    // highly correlated with compiled module size so we use it to avoid the
-    // cost of serializing when not caching.
-    const size_t kWireBytesSizeThresholdBytes = 1UL << 17;  // 128 KB.
-    if (wire_bytes.size() < kWireBytesSizeThresholdBytes)
-      return;
-
-    v8::OwnedBuffer serialized_module = compiled_module.Serialize();
-    TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"),
-                         "v8.wasm.cachedModule", TRACE_EVENT_SCOPE_THREAD,
-                         "producedCacheSize", serialized_module.size);
-    cache_handler->SetCachedMetadata(
-        kWasmModuleTag,
-        reinterpret_cast<const uint8_t*>(serialized_module.buffer.get()),
-        serialized_module.size);
-  }
-
- private:
-  KURL url_;
-  v8::Isolate* isolate_;
-  v8::Global<v8::Context> context_;
-
-  DISALLOW_COPY_AND_ASSIGN(WasmStreamingClient);
-};
-
 void StreamFromResponseCallback(
     const v8::FunctionCallbackInfo<v8::Value>& args) {
-  TRACE_EVENT_INSTANT0(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"),
-                       "v8.wasm.streamFromResponseCallback",
-                       TRACE_EVENT_SCOPE_THREAD);
   ExceptionState exception_state(args.GetIsolate(),
                                  ExceptionState::kExecutionContext,
                                  "WebAssembly", "compile");
@@ -318,35 +224,9 @@
     return;
   }
 
-  KURL url(response->url());
-  SingleCachedMetadataHandler* cache_handler =
-      GetCachedMetadataHandler(script_state, url);
-  if (cache_handler) {
-    streaming->SetClient(std::make_shared<WasmStreamingClient>(
-        url, args.GetIsolate(), script_state->GetContext()));
-    scoped_refptr<CachedMetadata> cached_module =
-        cache_handler->GetCachedMetadata(kWasmModuleTag);
-    if (cached_module) {
-      TRACE_EVENT_INSTANT2(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"),
-                           "v8.wasm.moduleCacheHit", TRACE_EVENT_SCOPE_THREAD,
-                           "url", url.GetString().Utf8(), "consumedCacheSize",
-                           cached_module->size());
-      bool is_valid = streaming->SetCompiledModuleBytes(
-          reinterpret_cast<const uint8_t*>(cached_module->Data()),
-          cached_module->size());
-      if (!is_valid) {
-        TRACE_EVENT_INSTANT0(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"),
-                             "v8.wasm.moduleCacheInvalid",
-                             TRACE_EVENT_SCOPE_THREAD);
-        cache_handler->ClearCachedMetadata(
-            CachedMetadataHandler::kSendToPlatform);
-      }
-    }
-  }
-
   FetchDataLoaderForWasmStreaming* loader =
-      MakeGarbageCollected<FetchDataLoaderForWasmStreaming>(streaming,
-                                                            script_state);
+      MakeGarbageCollected<FetchDataLoaderForWasmStreaming>(script_state,
+                                                            streaming);
   response->BodyBuffer()->StartLoading(
       loader, MakeGarbageCollected<WasmDataLoaderClient>(), exception_state);
 }
diff --git a/third_party/blink/renderer/core/exported/web_navigation_params.cc b/third_party/blink/renderer/core/exported/web_navigation_params.cc
index 44fde77..a0036615 100644
--- a/third_party/blink/renderer/core/exported/web_navigation_params.cc
+++ b/third_party/blink/renderer/core/exported/web_navigation_params.cc
@@ -5,6 +5,7 @@
 #include "third_party/blink/public/web/web_navigation_params.h"
 
 #include "third_party/blink/renderer/core/exported/web_document_loader_impl.h"
+#include "third_party/blink/renderer/platform/loader/static_data_navigation_body_loader.h"
 #include "third_party/blink/renderer/platform/network/encoded_form_data.h"
 #include "third_party/blink/renderer/platform/network/http_names.h"
 #include "third_party/blink/renderer/platform/shared_buffer.h"
@@ -47,9 +48,7 @@
     const WebURL& base_url) {
   auto result = std::make_unique<WebNavigationParams>();
   result->url = base_url;
-  result->data = WebData(html.data(), html.size());
-  result->mime_type = "text/html";
-  result->text_encoding = "UTF-8";
+  FillStaticResponse(result.get(), "text/html", "UTF-8", html);
   return result;
 }
 
@@ -73,11 +72,32 @@
     const KURL& base_url) {
   auto result = std::make_unique<WebNavigationParams>();
   result->url = base_url;
-  result->data = WebData(std::move(buffer));
-  result->mime_type = "text/html";
-  result->text_encoding = "UTF-8";
+  FillStaticResponse(result.get(), "text/html", "UTF-8",
+                     base::make_span(buffer->Data(), buffer->size()));
   return result;
 }
 #endif
 
+// static
+void WebNavigationParams::FillBodyLoader(WebNavigationParams* params,
+                                         base::span<const char> data) {
+  params->response.SetExpectedContentLength(data.size());
+  auto body_loader = std::make_unique<StaticDataNavigationBodyLoader>();
+  body_loader->Write(data.data(), data.size());
+  body_loader->Finish();
+  params->body_loader = std::move(body_loader);
+  params->is_static_data = true;
+}
+
+// static
+void WebNavigationParams::FillStaticResponse(WebNavigationParams* params,
+                                             WebString mime_type,
+                                             WebString text_encoding,
+                                             base::span<const char> data) {
+  params->response = WebURLResponse(params->url);
+  params->response.SetMIMEType(mime_type);
+  params->response.SetTextEncodingName(text_encoding);
+  FillBodyLoader(params, data);
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/html/forms/slider_thumb_element.cc b/third_party/blink/renderer/core/html/forms/slider_thumb_element.cc
index eb64c4c..60b69f2 100644
--- a/third_party/blink/renderer/core/html/forms/slider_thumb_element.cc
+++ b/third_party/blink/renderer/core/html/forms/slider_thumb_element.cc
@@ -280,8 +280,10 @@
 
 void SliderThumbElement::DetachLayoutTree(const AttachContext& context) {
   if (in_drag_mode_) {
-    if (LocalFrame* frame = GetDocument().GetFrame())
-      frame->GetEventHandler().SetCapturingMouseEventsElement(nullptr);
+    if (LocalFrame* frame = GetDocument().GetFrame()) {
+      frame->GetEventHandler().ReleasePointerCapture(
+          PointerEventFactory::kMouseId, this);
+    }
   }
   HTMLDivElement::DetachLayoutTree(context);
 }
diff --git a/third_party/blink/renderer/core/html/forms/spin_button_element.cc b/third_party/blink/renderer/core/html/forms/spin_button_element.cc
index 38ae215..0dcfb0f 100644
--- a/third_party/blink/renderer/core/html/forms/spin_button_element.cc
+++ b/third_party/blink/renderer/core/html/forms/spin_button_element.cc
@@ -110,6 +110,17 @@
           DoStepAction(up_down_state_ == kUp ? 1 : -1);
         }
       }
+      // Check |GetLayoutObject| again to make sure element is not removed by
+      // |DoStepAction|
+      if (GetLayoutObject() && !capturing_) {
+        if (LocalFrame* frame = GetDocument().GetFrame()) {
+          frame->GetEventHandler().SetPointerCapture(
+              PointerEventFactory::kMouseId, this);
+          capturing_ = true;
+          if (Page* page = GetDocument().GetPage())
+            page->GetChromeClient().RegisterPopupOpeningObserver(this);
+        }
+      }
       event.SetDefaultHandled();
     }
   } else if (mouse_event.type() == event_type_names::kMouseup &&
@@ -118,14 +129,6 @@
     ReleaseCapture();
   } else if (event.type() == event_type_names::kMousemove) {
     if (box->PixelSnappedBorderBoxRect().Contains(local)) {
-      if (!capturing_) {
-        if (LocalFrame* frame = GetDocument().GetFrame()) {
-          frame->GetEventHandler().SetCapturingMouseEventsElement(this);
-          capturing_ = true;
-          if (Page* page = GetDocument().GetPage())
-            page->GetChromeClient().RegisterPopupOpeningObserver(this);
-        }
-      }
       UpDownState old_up_down_state = up_down_state_;
       up_down_state_ = (local.Y() < box->Size().Height() / 2) ? kUp : kDown;
       if (up_down_state_ != old_up_down_state)
@@ -191,7 +194,8 @@
   if (!capturing_)
     return;
   if (LocalFrame* frame = GetDocument().GetFrame()) {
-    frame->GetEventHandler().SetCapturingMouseEventsElement(nullptr);
+    frame->GetEventHandler().ReleasePointerCapture(
+        PointerEventFactory::kMouseId, this);
     capturing_ = false;
     if (Page* page = GetDocument().GetPage())
       page->GetChromeClient().UnregisterPopupOpeningObserver(this);
diff --git a/third_party/blink/renderer/core/html/html_image_element.cc b/third_party/blink/renderer/core/html/html_image_element.cc
index c7e0781..439f311 100644
--- a/third_party/blink/renderer/core/html/html_image_element.cc
+++ b/third_party/blink/renderer/core/html/html_image_element.cc
@@ -104,7 +104,6 @@
       form_was_set_by_parser_(false),
       element_created_by_parser_(created_by_parser),
       is_fallback_image_(false),
-      should_invert_color_(false),
       sizes_set_width_(false),
       referrer_policy_(network::mojom::ReferrerPolicy::kDefault) {
   SetHasCustomStyleCallbacks();
diff --git a/third_party/blink/renderer/core/html/html_image_element.h b/third_party/blink/renderer/core/html/html_image_element.h
index d6f338ba..f724691f 100644
--- a/third_party/blink/renderer/core/html/html_image_element.h
+++ b/third_party/blink/renderer/core/html/html_image_element.h
@@ -240,7 +240,6 @@
   unsigned form_was_set_by_parser_ : 1;
   unsigned element_created_by_parser_ : 1;
   unsigned is_fallback_image_ : 1;
-  bool should_invert_color_;
   bool sizes_set_width_;
   bool is_default_overridden_intrinsic_size_;
 
diff --git a/third_party/blink/renderer/core/input/event_handler.cc b/third_party/blink/renderer/core/input/event_handler.cc
index 4702bdb..4ed6e360 100644
--- a/third_party/blink/renderer/core/input/event_handler.cc
+++ b/third_party/blink/renderer/core/input/event_handler.cc
@@ -149,7 +149,6 @@
           frame.GetTaskRunner(TaskType::kInternalUserInteraction),
           this,
           &EventHandler::CursorUpdateTimerFired),
-      event_handler_will_reset_capturing_mouse_events_node_(0),
       should_only_fire_drag_over_event_(false),
       event_handler_registry_(
           frame_->IsLocalRoot()
@@ -211,7 +210,6 @@
   mouse_wheel_event_manager_->Clear();
   last_show_press_timestamp_.reset();
   last_deferred_tap_element_ = nullptr;
-  event_handler_will_reset_capturing_mouse_events_node_ = false;
   should_use_touch_event_adjusted_point_ = false;
   touch_adjustment_result_.unique_event_id = 0;
 }
@@ -609,8 +607,7 @@
   if (mouse_event.button == WebPointerProperties::Button::kNoButton)
     return WebInputEventResult::kHandledSuppressed;
 
-  if (event_handler_will_reset_capturing_mouse_events_node_)
-    capturing_mouse_events_element_ = nullptr;
+  capturing_mouse_events_element_ = nullptr;
   mouse_event_manager_->HandleMousePressEventUpdateStates(mouse_event);
   if (!frame_->View())
     return WebInputEventResult::kNotHandled;
@@ -643,10 +640,9 @@
     mouse_event_manager_->SetCapturesDragging(
         subframe->GetEventHandler().mouse_event_manager_->CapturesDragging());
     if (mouse_event_manager_->MousePressed() &&
-        mouse_event_manager_->CapturesDragging()) {
+        mouse_event_manager_->CapturesDragging())
       capturing_mouse_events_element_ = mev.InnerElement();
-      event_handler_will_reset_capturing_mouse_events_node_ = true;
-    }
+
     mouse_event_manager_->InvalidateClick();
     return result;
   }
@@ -837,8 +833,7 @@
       !(mouse_event.GetModifiers() &
         WebInputEvent::Modifiers::kRelativeMotionEvent)) {
     mouse_event_manager_->ClearDragHeuristicState();
-    if (event_handler_will_reset_capturing_mouse_events_node_)
-      capturing_mouse_events_element_ = nullptr;
+    capturing_mouse_events_element_ = nullptr;
     CaptureMouseEventsToWidget(false);
   }
 
@@ -1036,8 +1031,7 @@
                                                     mouse_event);
   LocalFrame* subframe = event_handling_util::GetTargetSubframe(
       mev, capturing_mouse_events_element_.Get());
-  if (event_handler_will_reset_capturing_mouse_events_node_)
-    capturing_mouse_events_element_ = nullptr;
+  capturing_mouse_events_element_ = nullptr;
   if (subframe)
     return PassMouseReleaseEventToSubframe(mev, subframe);
 
@@ -1234,11 +1228,6 @@
   mouse_event_manager_->RecomputeMouseHoverState();
 }
 
-void EventHandler::SetCapturingMouseEventsElement(Element* n) {
-  CaptureMouseEventsToWidget(n);
-  capturing_mouse_events_element_ = n;
-}
-
 Element* EventHandler::EffectiveMouseEventTargetElement(
     Element* target_element) {
   Element* new_element_under_mouse = target_element;
diff --git a/third_party/blink/renderer/core/input/event_handler.h b/third_party/blink/renderer/core/input/event_handler.h
index a102d0ab..ea525f4 100644
--- a/third_party/blink/renderer/core/input/event_handler.h
+++ b/third_party/blink/renderer/core/input/event_handler.h
@@ -112,8 +112,6 @@
     return mouse_event_manager_->IsMousePositionUnknown();
   }
   void ClearMouseEventManager() const { mouse_event_manager_->Clear(); }
-  void SetCapturingMouseEventsElement(
-      Element*);  // A caller is responsible for resetting capturing node to 0.
 
   WebInputEventResult UpdateDragAndDrop(const WebMouseEvent&, DataTransfer*);
   void CancelDragAndDrop(const WebMouseEvent&, DataTransfer*);
@@ -414,7 +412,6 @@
   TaskRunnerTimer<EventHandler> cursor_update_timer_;
 
   Member<Element> capturing_mouse_events_element_;
-  bool event_handler_will_reset_capturing_mouse_events_node_;
 
   // Indicates whether the current widget is capturing mouse input.
   // Only used for local frame root EventHandlers.
diff --git a/third_party/blink/renderer/core/loader/document_loader.cc b/third_party/blink/renderer/core/loader/document_loader.cc
index 665324f..73df111 100644
--- a/third_party/blink/renderer/core/loader/document_loader.cc
+++ b/third_party/blink/renderer/core/loader/document_loader.cc
@@ -161,29 +161,28 @@
   unreachable_url_ = params_->unreachable_url;
   previews_state_ = params_->previews_state;
 
-  if (params_->data.IsNull() && url_.IsAboutSrcdocURL()) {
+  if (!params_->is_static_data && url_.IsAboutSrcdocURL()) {
     loading_srcdoc_ = true;
-    params_->mime_type = "text/html";
-    params_->text_encoding = "UTF-8";
     // TODO(dgozman): instead of reaching to the owner here, we could instead:
     // - grab the "srcdoc" value when starting a navigation right in the owner;
     // - pass it around through BeginNavigation to CommitNavigation as |data|;
     // - use it here instead of re-reading from the owner.
     // This way we will get rid of extra dependency between starting and
     // committing navigation.
+    CString encoded_srcdoc;
     HTMLFrameOwnerElement* owner_element = frame_->DeprecatedLocalOwner();
     if (!IsHTMLIFrameElement(owner_element) ||
         !owner_element->FastHasAttribute(html_names::kSrcdocAttr)) {
       // Cannot retrieve srcdoc content anymore (perhaps, the attribute was
       // cleared) - load empty instead.
-      params_->data = SharedBuffer::Create();
     } else {
       String srcdoc = owner_element->FastGetAttribute(html_names::kSrcdocAttr);
       DCHECK(!srcdoc.IsNull());
-      CString encoded_srcdoc = srcdoc.Utf8();
-      params_->data =
-          SharedBuffer::Create(encoded_srcdoc.data(), encoded_srcdoc.length());
+      encoded_srcdoc = srcdoc.Utf8();
     }
+    WebNavigationParams::FillStaticResponse(
+        params_.get(), "text/html", "UTF-8",
+        base::make_span(encoded_srcdoc.data(), encoded_srcdoc.length()));
   }
 
   WebNavigationTimings& timings = params_->navigation_timings;
@@ -1027,14 +1026,6 @@
   FinishedLoading(CurrentTimeTicks());
 }
 
-void DocumentLoader::CreateBodyLoaderFromStaticData(const SharedBuffer& data) {
-  DCHECK(!body_loader_);
-  auto body_loader = std::make_unique<StaticDataNavigationBodyLoader>();
-  body_loader->Write(data);
-  body_loader->Finish();
-  body_loader_ = std::move(body_loader);
-}
-
 void DocumentLoader::StartLoading() {
   StartLoadingInternal();
   params_ = nullptr;
@@ -1046,21 +1037,13 @@
   DCHECK(params_);
   state_ = kProvisional;
 
-  if (params_->data.IsNull() && WillLoadUrlAsEmpty(url_)) {
+  if (!params_->is_static_data && WillLoadUrlAsEmpty(url_)) {
     LoadEmpty();
     return;
   }
 
   // See WebNavigationParams for special case explanations.
-  ResourceResponse final_response;
-  if (!params_->data.IsNull()) {
-    // Use provided data instead of loading resource body.
-    params_->redirects.Clear();
-    final_response.SetCurrentRequestUrl(url_);
-    final_response.SetMimeType(params_->mime_type);
-    final_response.SetExpectedContentLength(params_->data.size());
-    final_response.SetTextEncodingName(params_->text_encoding);
-    CreateBodyLoaderFromStaticData(params_->data);
+  if (params_->is_static_data) {
     has_substitute_data_ = true;
   } else if (fetcher_->Archive()) {
     // If we have an archive loaded in some ancestor frame, we should
@@ -1071,14 +1054,16 @@
     ArchiveResource* archive_resource =
         fetcher_->Archive()->SubresourceForURL(url_);
     if (archive_resource) {
-      params_->redirects.Clear();
-      final_response.SetCurrentRequestUrl(url_);
-      final_response.SetMimeType(archive_resource->MimeType());
-      final_response.SetExpectedContentLength(archive_resource->Data()->size());
-      final_response.SetTextEncodingName(archive_resource->TextEncoding());
-      CreateBodyLoaderFromStaticData(*archive_resource->Data());
+      SharedBuffer* archive_data = archive_resource->Data();
+      WebNavigationParams::FillStaticResponse(
+          params_.get(), archive_resource->MimeType(),
+          archive_resource->TextEncoding(),
+          base::make_span(archive_data->Data(), archive_data->size()));
     }
-  } else if (!params_->body_loader) {
+  }
+
+  ResourceResponse final_response;
+  if (!params_->body_loader) {
     // We can handle data urls in place.
     // TODO(dgozman): This is currently only used in tests. Perhaps we should
     // either handle all data urls locally, or rework tests.
@@ -1087,8 +1072,12 @@
       data =
           network_utils::ParseDataURLAndPopulateResponse(url_, final_response);
     }
-    if (data)
-      CreateBodyLoaderFromStaticData(*data);
+    if (data) {
+      auto body_loader = std::make_unique<StaticDataNavigationBodyLoader>();
+      body_loader->Write(*data);
+      body_loader->Finish();
+      body_loader_ = std::move(body_loader);
+    }
   } else {
     // The common case - both final response and body loader should be
     // provided.
diff --git a/third_party/blink/renderer/core/loader/document_loader.h b/third_party/blink/renderer/core/loader/document_loader.h
index f2a9e82..44646d62 100644
--- a/third_party/blink/renderer/core/loader/document_loader.h
+++ b/third_party/blink/renderer/core/loader/document_loader.h
@@ -325,7 +325,6 @@
   void HandleData(const char* data, size_t length);
 
   void LoadEmpty();
-  void CreateBodyLoaderFromStaticData(const SharedBuffer&);
 
   bool IsRedirectAfterPost(const ResourceRequest&, const ResourceResponse&);
 
diff --git a/third_party/blink/renderer/core/origin_trials/OWNERS b/third_party/blink/renderer/core/origin_trials/OWNERS
new file mode 100644
index 0000000..73686a7
--- /dev/null
+++ b/third_party/blink/renderer/core/origin_trials/OWNERS
@@ -0,0 +1 @@
+file://third_party/blink/common/origin_trials/OWNERS
diff --git a/third_party/blink/renderer/devtools/front_end/timeline/TimelineUIUtils.js b/third_party/blink/renderer/devtools/front_end/timeline/TimelineUIUtils.js
index 3993199..5334c9e 100644
--- a/third_party/blink/renderer/devtools/front_end/timeline/TimelineUIUtils.js
+++ b/third_party/blink/renderer/devtools/front_end/timeline/TimelineUIUtils.js
@@ -109,16 +109,6 @@
         new Timeline.TimelineRecordStyle(Common.UIString('Evaluate Module'), categories['scripting']);
     eventStyles[recordTypes.ParseScriptOnBackground] =
         new Timeline.TimelineRecordStyle(Common.UIString('Parse Script'), categories['scripting']);
-    eventStyles[recordTypes.WasmStreamFromResponseCallback] =
-        new Timeline.TimelineRecordStyle(Common.UIString(ls`Streaming Wasm Response`), categories['scripting']);
-    eventStyles[recordTypes.WasmCompiledModule] =
-        new Timeline.TimelineRecordStyle(Common.UIString(ls`Compiled Wasm Module`), categories['scripting']);
-    eventStyles[recordTypes.WasmCachedModule] =
-        new Timeline.TimelineRecordStyle(Common.UIString(ls`Cached Wasm Module`), categories['scripting']);
-    eventStyles[recordTypes.WasmModuleCacheHit] =
-        new Timeline.TimelineRecordStyle(Common.UIString(ls`Wasm Module Cache Hit`), categories['scripting']);
-    eventStyles[recordTypes.WasmModuleCacheInvalid] =
-        new Timeline.TimelineRecordStyle(Common.UIString(ls`Wasm Module Cache Invalid`), categories['scripting']);
     eventStyles[recordTypes.FrameStartedLoading] =
         new Timeline.TimelineRecordStyle(ls`Frame Started Loading`, categories['loading'], true);
     eventStyles[recordTypes.MarkLoad] =
@@ -570,14 +560,6 @@
           detailsText = Bindings.displayNameForURL(url) + ':' + (eventData['lineNumber'] + 1);
         break;
       }
-      case recordType.WasmCompiledModule:
-      case recordType.WasmModuleCacheHit: {
-        const url = event.args['url'];
-        if (url)
-          detailsText = Bindings.displayNameForURL(url);
-        break;
-      }
-
       case recordType.ParseScriptOnBackground:
       case recordType.XHRReadyStateChange:
       case recordType.XHRLoad: {
@@ -684,11 +666,6 @@
       case recordType.Animation:
       case recordType.EmbedderCallback:
       case recordType.ParseHTML:
-      case recordType.WasmStreamFromResponseCallback:
-      case recordType.WasmCompiledModule:
-      case recordType.WasmModuleCacheHit:
-      case recordType.WasmCachedModule:
-      case recordType.WasmModuleCacheInvalid:
       case recordType.WebSocketCreate:
       case recordType.WebSocketSendHandshakeRequest:
       case recordType.WebSocketReceiveHandshakeResponse:
@@ -916,23 +893,6 @@
         if (url)
           contentHelper.appendLocationRow(ls`Script`, url, eventData['lineNumber'], eventData['columnNumber']);
         break;
-      case recordTypes.WasmStreamFromResponseCallback:
-      case recordTypes.WasmCompiledModule:
-      case recordTypes.WasmCachedModule:
-      case recordTypes.WasmModuleCacheHit:
-      case recordTypes.WasmModuleCacheInvalid:
-        if (eventData) {
-          url = event.args['url'];
-          if (url)
-            contentHelper.appendTextRow(ls`Url`, url);
-          const producedCachedSize = event.args['producedCachedSize'];
-          if (producedCachedSize)
-            contentHelper.appendTextRow(ls`Produced Cache Size`, producedCachedSize);
-          const consumedCachedSize = event.args['consumedCachedSize'];
-          if (consumedCachedSize)
-            contentHelper.appendTextRow(ls`Consumed Cache Size`, consumedCachedSize);
-        }
-        break;
       case recordTypes.Paint:
         const clip = eventData['clip'];
         contentHelper.appendTextRow(ls`Location`, ls`(${clip[0]}, ${clip[1]})`);
diff --git a/third_party/blink/renderer/devtools/front_end/timeline_model/TimelineModel.js b/third_party/blink/renderer/devtools/front_end/timeline_model/TimelineModel.js
index 65809eba..1b0efac5 100644
--- a/third_party/blink/renderer/devtools/front_end/timeline_model/TimelineModel.js
+++ b/third_party/blink/renderer/devtools/front_end/timeline_model/TimelineModel.js
@@ -1211,11 +1211,6 @@
   EvaluateScript: 'EvaluateScript',
   CompileModule: 'v8.compileModule',
   EvaluateModule: 'v8.evaluateModule',
-  WasmStreamFromResponseCallback: 'v8.wasm.streamFromResponseCallback',
-  WasmCompiledModule: 'v8.wasm.compiledModule',
-  WasmCachedModule: 'v8.wasm.cachedModule',
-  WasmModuleCacheHit: 'v8.wasm.moduleCacheHit',
-  WasmModuleCacheInvalid: 'v8.wasm.moduleCacheInvalid',
 
   FrameStartedLoading: 'FrameStartedLoading',
   CommitLoad: 'CommitLoad',
diff --git a/third_party/blink/renderer/modules/animationworklet/animation_worklet.cc b/third_party/blink/renderer/modules/animationworklet/animation_worklet.cc
index 3753be8..193b79f 100644
--- a/third_party/blink/renderer/modules/animationworklet/animation_worklet.cc
+++ b/third_party/blink/renderer/modules/animationworklet/animation_worklet.cc
@@ -28,20 +28,25 @@
 AnimationWorklet::~AnimationWorklet() = default;
 
 bool AnimationWorklet::NeedsToCreateGlobalScope() {
-  // For now, create only one global scope per document.
-  // TODO(nhiroki): Revisit this later.
-  return GetNumberOfGlobalScopes() == 0;
+  return GetNumberOfGlobalScopes() <
+         AnimationWorkletProxyClient::kNumStatelessGlobalScopes;
 }
 
 WorkletGlobalScopeProxy* AnimationWorklet::CreateGlobalScope() {
   DCHECK(NeedsToCreateGlobalScope());
 
-  Document* document = To<Document>(GetExecutionContext());
-  AnimationWorkletProxyClient* proxy_client =
-      AnimationWorkletProxyClient::FromDocument(document, worklet_id_);
+  if (!proxy_client_) {
+    // TODO(kevers|majidvp): Consider refactoring so that proxy client
+    // initialization can move to the constructor. Currently, initialization
+    // in the constructor leads to test failures as the document frame has not
+    // been initialized at the time of the constructor call.
+    Document* document = To<Document>(GetExecutionContext());
+    proxy_client_ =
+        AnimationWorkletProxyClient::FromDocument(document, worklet_id_);
+  }
 
   WorkerClients* worker_clients = WorkerClients::Create();
-  ProvideAnimationWorkletProxyClientTo(worker_clients, proxy_client);
+  ProvideAnimationWorkletProxyClientTo(worker_clients, proxy_client_);
 
   AnimationWorkletMessagingProxy* proxy =
       MakeGarbageCollected<AnimationWorkletMessagingProxy>(
@@ -58,6 +63,7 @@
 
 void AnimationWorklet::Trace(blink::Visitor* visitor) {
   Worklet::Trace(visitor);
+  visitor->Trace(proxy_client_);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/animationworklet/animation_worklet.h b/third_party/blink/renderer/modules/animationworklet/animation_worklet.h
index aa4abd1..efc751d 100644
--- a/third_party/blink/renderer/modules/animationworklet/animation_worklet.h
+++ b/third_party/blink/renderer/modules/animationworklet/animation_worklet.h
@@ -6,6 +6,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_ANIMATIONWORKLET_ANIMATION_WORKLET_H_
 
 #include "third_party/blink/renderer/core/workers/worklet.h"
+#include "third_party/blink/renderer/modules/animationworklet/animation_worklet_proxy_client.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
 #include "third_party/blink/renderer/platform/graphics/animation_worklet_mutators_state.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
@@ -38,6 +39,8 @@
   // Implements Worklet.
   bool NeedsToCreateGlobalScope() final;
   WorkletGlobalScopeProxy* CreateGlobalScope() final;
+
+  Member<AnimationWorkletProxyClient> proxy_client_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/animationworklet/animation_worklet_global_scope.cc b/third_party/blink/renderer/modules/animationworklet/animation_worklet_global_scope.cc
index 9865edad..5fee2ef 100644
--- a/third_party/blink/renderer/modules/animationworklet/animation_worklet_global_scope.cc
+++ b/third_party/blink/renderer/modules/animationworklet/animation_worklet_global_scope.cc
@@ -4,8 +4,6 @@
 
 #include "third_party/blink/renderer/modules/animationworklet/animation_worklet_global_scope.h"
 
-#include "base/metrics/histogram_macros.h"
-#include "base/timer/elapsed_timer.h"
 #include "third_party/blink/renderer/bindings/core/v8/serialization/serialized_script_value.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_object_parser.h"
 #include "third_party/blink/renderer/bindings/core/v8/worker_or_worklet_script_controller.h"
@@ -80,21 +78,17 @@
   return animator;
 }
 
-std::unique_ptr<AnimationWorkletOutput> AnimationWorkletGlobalScope::Mutate(
-    const AnimationWorkletInput& mutator_input) {
-  base::ElapsedTimer timer;
+void AnimationWorkletGlobalScope::UpdateAnimatorsList(
+    const AnimationWorkletInput& input) {
   DCHECK(IsContextThread());
 
   ScriptState* script_state = ScriptController()->GetScriptState();
   ScriptState::Scope scope(script_state);
 
-  std::unique_ptr<AnimationWorkletOutput> result =
-      std::make_unique<AnimationWorkletOutput>();
-
-  for (const auto& worklet_animation_id : mutator_input.removed_animations)
+  for (const auto& worklet_animation_id : input.removed_animations)
     animators_.erase(worklet_animation_id.animation_id);
 
-  for (const auto& animation : mutator_input.added_and_updated_animations) {
+  for (const auto& animation : input.added_and_updated_animations) {
     int id = animation.worklet_animation_id.animation_id;
     DCHECK(!animators_.Contains(id));
     const String name =
@@ -104,44 +98,54 @@
     WorkletAnimationOptions* options =
         static_cast<WorkletAnimationOptions*>(animation.options.get());
 
-    Animator* animator =
-        CreateAnimatorFor(id, name, options, animation.num_effects);
-    if (!animator)
-      continue;
-
-    UpdateAnimation(animator, script_state, animation.worklet_animation_id,
-                    animation.current_time, result.get());
+    CreateAnimatorFor(id, name, options, animation.num_effects);
   }
+}
 
-  for (const auto& animation : mutator_input.updated_animations) {
+void AnimationWorkletGlobalScope::UpdateAnimators(
+    const AnimationWorkletInput& input,
+    AnimationWorkletOutput* output,
+    bool (*predicate)(Animator*)) {
+  DCHECK(IsContextThread());
+
+  ScriptState* script_state = ScriptController()->GetScriptState();
+  ScriptState::Scope scope(script_state);
+
+  for (const auto& animation : input.added_and_updated_animations) {
     int id = animation.worklet_animation_id.animation_id;
     Animator* animator = animators_.at(id);
     // We don't try to create an animator if there isn't any.
-    if (!animator)
+    // This can only happen if constructing an animator instance has failed
+    // e.g., the constructor throws an exception.
+    if (!animator || !predicate(animator))
       continue;
 
     UpdateAnimation(animator, script_state, animation.worklet_animation_id,
-                    animation.current_time, result.get());
+                    animation.current_time, output);
   }
 
-  for (const auto& worklet_animation_id : mutator_input.peeked_animations) {
+  for (const auto& animation : input.updated_animations) {
+    int id = animation.worklet_animation_id.animation_id;
+    Animator* animator = animators_.at(id);
+    // We don't try to create an animator if there isn't any.
+    if (!animator || !predicate(animator))
+      continue;
+
+    UpdateAnimation(animator, script_state, animation.worklet_animation_id,
+                    animation.current_time, output);
+  }
+
+  for (const auto& worklet_animation_id : input.peeked_animations) {
     int id = worklet_animation_id.animation_id;
     Animator* animator = animators_.at(id);
-    if (!animator)
+    if (!animator || !predicate(animator))
       continue;
 
     AnimationWorkletDispatcherOutput::AnimationState animation_output(
         worklet_animation_id);
     animation_output.local_times = animator->GetLocalTimes();
-    result->animations.push_back(animation_output);
+    output->animations.push_back(animation_output);
   }
-
-  UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES(
-      "Animation.AnimationWorklet.GlobalScope.MutateDuration", timer.Elapsed(),
-      base::TimeDelta::FromMicroseconds(1),
-      base::TimeDelta::FromMilliseconds(100), 50);
-
-  return result;
 }
 
 void AnimationWorkletGlobalScope::RegisterWithProxyClientIfNeeded() {
@@ -150,7 +154,7 @@
 
   if (AnimationWorkletProxyClient* proxy_client =
           AnimationWorkletProxyClient::From(Clients())) {
-    proxy_client->SetGlobalScope(this);
+    proxy_client->AddGlobalScope(this);
     registered_ = true;
   }
 }
@@ -194,6 +198,8 @@
   AnimatorDefinition* definition =
       MakeGarbageCollected<AnimatorDefinition>(isolate, constructor, animate);
 
+  // TODO(https://crbug.com/923063): Ensure worklet definitions are compatible
+  // across global scopes.
   animator_definitions_.Set(name, definition);
   // TODO(yigu): Currently one animator name is synced back per registration.
   // Eventually all registered names should be synced in batch once a module
diff --git a/third_party/blink/renderer/modules/animationworklet/animation_worklet_global_scope.h b/third_party/blink/renderer/modules/animationworklet/animation_worklet_global_scope.h
index 90c7d00..b524d8e5 100644
--- a/third_party/blink/renderer/modules/animationworklet/animation_worklet_global_scope.h
+++ b/third_party/blink/renderer/modules/animationworklet/animation_worklet_global_scope.h
@@ -44,8 +44,12 @@
   void Dispose() override;
   bool IsAnimationWorkletGlobalScope() const final { return true; }
 
-  // Invokes the |animate| function of all of its active animators.
-  std::unique_ptr<AnimationWorkletOutput> Mutate(const AnimationWorkletInput&);
+  void UpdateAnimatorsList(const AnimationWorkletInput&);
+
+  // Invokes the |animate| function of selected animators.
+  void UpdateAnimators(const AnimationWorkletInput&,
+                       AnimationWorkletOutput*,
+                       bool (*predicate)(Animator*));
 
   // Registers a animator definition with the given name and constructor.
   void registerAnimator(const String& name,
diff --git a/third_party/blink/renderer/modules/animationworklet/animation_worklet_global_scope_test.cc b/third_party/blink/renderer/modules/animationworklet/animation_worklet_global_scope_test.cc
index 718d367b..a91df9a5 100644
--- a/third_party/blink/renderer/modules/animationworklet/animation_worklet_global_scope_test.cc
+++ b/third_party/blink/renderer/modules/animationworklet/animation_worklet_global_scope_test.cc
@@ -40,17 +40,28 @@
  public:
   MockAnimationWorkletProxyClient()
       : AnimationWorkletProxyClient(0, nullptr, nullptr, nullptr, nullptr),
-        did_set_global_scope_(false) {}
-  void SetGlobalScope(WorkletGlobalScope*) override {
-    did_set_global_scope_ = true;
+        did_add_global_scope_(false) {}
+  void AddGlobalScope(WorkletGlobalScope*) override {
+    did_add_global_scope_ = true;
   }
   void SynchronizeAnimatorName(const String&) override{};
-  bool did_set_global_scope() { return did_set_global_scope_; }
+  bool did_add_global_scope() { return did_add_global_scope_; }
 
  private:
-  bool did_set_global_scope_;
+  bool did_add_global_scope_;
 };
 
+std::unique_ptr<AnimationWorkletOutput> ProxyClientMutate(
+    AnimationWorkletInput& state,
+    AnimationWorkletGlobalScope* global_scope) {
+  std::unique_ptr<AnimationWorkletOutput> output =
+      std::make_unique<AnimationWorkletOutput>();
+  global_scope->UpdateAnimatorsList(state);
+  global_scope->UpdateAnimators(state, output.get(),
+                                [](Animator*) { return true; });
+  return output;
+}
+
 }  // namespace
 
 class AnimationWorkletGlobalScopeTest : public PageTestBase {
@@ -226,8 +237,8 @@
                                                     nullptr, 1);
 
     std::unique_ptr<AnimationWorkletOutput> output =
-        global_scope->Mutate(state);
-    EXPECT_TRUE(output);
+        ProxyClientMutate(state, global_scope);
+    EXPECT_EQ(output->animations.size(), 1ul);
 
     ScriptValue constructed_after =
         global_scope->ScriptController()->EvaluateAndReturnValueForTest(
@@ -278,8 +289,7 @@
                                                     nullptr, 1);
 
     std::unique_ptr<AnimationWorkletOutput> output =
-        global_scope->Mutate(state);
-    EXPECT_TRUE(output);
+        ProxyClientMutate(state, global_scope);
 
     EXPECT_EQ(output->animations.size(), 1ul);
     EXPECT_EQ(output->animations[0].local_times[0],
@@ -322,19 +332,23 @@
     state.updated_animations.push_back({animation_id, 5000});
     EXPECT_EQ(state.added_and_updated_animations.size(), 0u);
     EXPECT_EQ(state.updated_animations.size(), 1u);
-    global_scope->Mutate(state);
+
+    std::unique_ptr<AnimationWorkletOutput> output =
+        ProxyClientMutate(state, global_scope);
     EXPECT_EQ(global_scope->GetAnimatorsSizeForTest(), 0u);
 
     state.removed_animations.push_back(animation_id);
     EXPECT_EQ(state.added_and_updated_animations.size(), 0u);
     EXPECT_EQ(state.removed_animations.size(), 1u);
-    global_scope->Mutate(state);
+
+    output = ProxyClientMutate(state, global_scope);
     EXPECT_EQ(global_scope->GetAnimatorsSizeForTest(), 0u);
 
     state.added_and_updated_animations.push_back(
         {animation_id, "test", 5000, nullptr, 1});
     EXPECT_EQ(state.added_and_updated_animations.size(), 1u);
-    global_scope->Mutate(state);
+
+    output = ProxyClientMutate(state, global_scope);
     EXPECT_EQ(global_scope->GetAnimatorsSizeForTest(), 1u);
     waitable_event->Signal();
   }
@@ -372,26 +386,38 @@
     state.added_and_updated_animations.push_back(
         {animation_id, "test", 5000, nullptr, 1});
     EXPECT_EQ(state.added_and_updated_animations.size(), 1u);
-    global_scope->Mutate(state);
+
+    std::unique_ptr<AnimationWorkletOutput> output =
+        ProxyClientMutate(state, global_scope);
     EXPECT_EQ(global_scope->GetAnimatorsSizeForTest(), 1u);
 
     state.added_and_updated_animations.clear();
     state.updated_animations.push_back({animation_id, 6000});
     EXPECT_EQ(state.added_and_updated_animations.size(), 0u);
     EXPECT_EQ(state.updated_animations.size(), 1u);
-    global_scope->Mutate(state);
+
+    output = ProxyClientMutate(state, global_scope);
     EXPECT_EQ(global_scope->GetAnimatorsSizeForTest(), 1u);
 
     state.updated_animations.clear();
     state.removed_animations.push_back(animation_id);
     EXPECT_EQ(state.updated_animations.size(), 0u);
     EXPECT_EQ(state.removed_animations.size(), 1u);
-    global_scope->Mutate(state);
+
+    output = ProxyClientMutate(state, global_scope);
     EXPECT_EQ(global_scope->GetAnimatorsSizeForTest(), 0u);
 
     waitable_event->Signal();
   }
 
+  void AddGlobalScopeForTesting(WorkerThread* thread,
+                                AnimationWorkletProxyClient* proxy_client,
+                                WaitableEvent* waitable_event) {
+    proxy_client->AddGlobalScopeForTesting(
+        To<WorkletGlobalScope>(thread->GlobalScope()));
+    waitable_event->Signal();
+  }
+
  private:
   // Returns false when a script evaluation error happens.
   bool EvaluateScriptModule(AnimationWorkletGlobalScope* global_scope,
@@ -447,7 +473,7 @@
       CreateAnimationAndPaintWorkletThread(proxy_client);
   // Animation worklet global scope (AWGS) should not register itself upon
   // creation.
-  EXPECT_FALSE(proxy_client->did_set_global_scope());
+  EXPECT_FALSE(proxy_client->did_add_global_scope());
 
   WaitableEvent waitable_event;
   String source_code =
@@ -467,10 +493,84 @@
   waitable_event.Wait();
 
   // AWGS should register itself first time an animator is registered with it.
-  EXPECT_TRUE(proxy_client->did_set_global_scope());
+  EXPECT_TRUE(proxy_client->did_add_global_scope());
 
   worklet->Terminate();
   worklet->WaitForShutdownForTesting();
 }
 
+TEST_F(AnimationWorkletGlobalScopeTest, SelectGlobalScope) {
+  AnimationWorkletProxyClient* proxy_client =
+      MakeGarbageCollected<MockAnimationWorkletProxyClient>();
+
+  // Global scopes must be created on worker threads.
+  std::unique_ptr<WorkerThread> first_worklet =
+      CreateAnimationAndPaintWorkletThread(proxy_client);
+  std::unique_ptr<WorkerThread> second_worklet =
+      CreateAnimationAndPaintWorkletThread(proxy_client);
+
+  ASSERT_NE(first_worklet, second_worklet);
+
+  // Register global scopes with proxy client. This step must be performed on
+  // the worker threads.
+  WaitableEvent waitable_event;
+  PostCrossThreadTask(
+      *first_worklet->GetTaskRunner(TaskType::kInternalTest), FROM_HERE,
+      CrossThreadBind(
+          &AnimationWorkletGlobalScopeTest::AddGlobalScopeForTesting,
+          CrossThreadUnretained(this),
+          CrossThreadUnretained(first_worklet.get()),
+          CrossThreadPersistent<AnimationWorkletProxyClient>(proxy_client),
+          CrossThreadUnretained(&waitable_event)));
+  waitable_event.Wait();
+
+  waitable_event.Reset();
+  PostCrossThreadTask(
+      *second_worklet->GetTaskRunner(TaskType::kInternalTest), FROM_HERE,
+      CrossThreadBind(
+          &AnimationWorkletGlobalScopeTest::AddGlobalScopeForTesting,
+          CrossThreadUnretained(this),
+          CrossThreadUnretained(second_worklet.get()),
+          CrossThreadPersistent<AnimationWorkletProxyClient>(proxy_client),
+          CrossThreadUnretained(&waitable_event)));
+  waitable_event.Wait();
+
+  AnimationWorkletGlobalScope* stateful_global_scope =
+      proxy_client->global_scopes_[0];
+  AnimationWorkletGlobalScope* first_stateless_global_scope =
+      proxy_client->global_scopes_[0];
+  AnimationWorkletGlobalScope* second_stateless_global_scope =
+      proxy_client->global_scopes_[1];
+
+  // Initialize switch countdown to 1, to force a switch in the stateless
+  // global scope on the second call.
+  proxy_client->next_global_scope_switch_countdown_ = 1;
+  EXPECT_EQ(proxy_client->SelectStatefulGlobalScope(), stateful_global_scope);
+  EXPECT_EQ(proxy_client->SelectStatelessGlobalScope(),
+            first_stateless_global_scope);
+  EXPECT_EQ(proxy_client->SelectStatefulGlobalScope(), stateful_global_scope);
+  EXPECT_EQ(proxy_client->SelectStatelessGlobalScope(),
+            second_stateless_global_scope);
+
+  // Increase countdown and verify that the switchover adjusts as expected.
+  proxy_client->next_global_scope_switch_countdown_ = 3;
+  EXPECT_EQ(proxy_client->SelectStatefulGlobalScope(), stateful_global_scope);
+  EXPECT_EQ(proxy_client->SelectStatelessGlobalScope(),
+            second_stateless_global_scope);
+  EXPECT_EQ(proxy_client->SelectStatefulGlobalScope(), stateful_global_scope);
+  EXPECT_EQ(proxy_client->SelectStatelessGlobalScope(),
+            second_stateless_global_scope);
+  EXPECT_EQ(proxy_client->SelectStatefulGlobalScope(), stateful_global_scope);
+  EXPECT_EQ(proxy_client->SelectStatelessGlobalScope(),
+            second_stateless_global_scope);
+  EXPECT_EQ(proxy_client->SelectStatefulGlobalScope(), stateful_global_scope);
+  EXPECT_EQ(proxy_client->SelectStatelessGlobalScope(),
+            first_stateless_global_scope);
+
+  first_worklet->Terminate();
+  first_worklet->WaitForShutdownForTesting();
+  second_worklet->Terminate();
+  second_worklet->WaitForShutdownForTesting();
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/animationworklet/animation_worklet_proxy_client.cc b/third_party/blink/renderer/modules/animationworklet/animation_worklet_proxy_client.cc
index ed1c349..ff5f1056 100644
--- a/third_party/blink/renderer/modules/animationworklet/animation_worklet_proxy_client.cc
+++ b/third_party/blink/renderer/modules/animationworklet/animation_worklet_proxy_client.cc
@@ -4,6 +4,10 @@
 
 #include "third_party/blink/renderer/modules/animationworklet/animation_worklet_proxy_client.h"
 
+#include <memory>
+
+#include "base/metrics/histogram_macros.h"
+#include "base/timer/elapsed_timer.h"
 #include "third_party/blink/renderer/core/animation/worklet_animation_controller.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/frame/web_frame_widget_base.h"
@@ -14,9 +18,20 @@
 
 namespace blink {
 
+namespace {
+
+static const wtf_size_t kMaxMutateCountToSwitch = 10u;
+static const wtf_size_t kStatefulGlobalScopeIndex = 0u;
+
+}  // end namespace
+
+/* static */
 const char AnimationWorkletProxyClient::kSupplementName[] =
     "AnimationWorkletProxyClient";
 
+/* static */
+const wtf_size_t AnimationWorkletProxyClient::kNumStatelessGlobalScopes = 2u;
+
 AnimationWorkletProxyClient::AnimationWorkletProxyClient(
     int worklet_id,
     base::WeakPtr<AnimationWorkletMutatorDispatcherImpl>
@@ -25,7 +40,10 @@
     base::WeakPtr<AnimationWorkletMutatorDispatcherImpl>
         main_thread_mutator_dispatcher,
     scoped_refptr<base::SingleThreadTaskRunner> main_thread_mutator_runner)
-    : worklet_id_(worklet_id), state_(RunState::kUninitialized) {
+    : worklet_id_(worklet_id),
+      state_(RunState::kUninitialized),
+      next_global_scope_switch_countdown_(0),
+      current_stateless_global_scope_index_(0) {
   DCHECK(IsMainThread());
   mutator_items_.emplace_back(std::move(compositor_mutator_dispatcher),
                               std::move(compositor_mutator_runner));
@@ -59,18 +77,27 @@
   }
 }
 
-void AnimationWorkletProxyClient::SetGlobalScope(
+void AnimationWorkletProxyClient::AddGlobalScope(
     WorkletGlobalScope* global_scope) {
   DCHECK(global_scope);
   DCHECK(global_scope->IsContextThread());
   if (state_ == RunState::kDisposed)
     return;
-  DCHECK(state_ == RunState::kUninitialized);
 
-  global_scope_ = static_cast<AnimationWorkletGlobalScope*>(global_scope);
+  global_scopes_.push_back(To<AnimationWorkletGlobalScope>(global_scope));
+
+  if (state_ != RunState::kUninitialized) {
+    return;
+  }
+
+  // Wait for all global scopes to load before proceeding with registration.
+  if (global_scopes_.size() < kNumStatelessGlobalScopes) {
+    return;
+  }
+
   // TODO(majidvp): Add an AnimationWorklet task type when the spec is final.
   scoped_refptr<base::SingleThreadTaskRunner> global_scope_runner =
-      global_scope_->GetThread()->GetTaskRunner(TaskType::kMiscPlatformAPI);
+      global_scope->GetThread()->GetTaskRunner(TaskType::kMiscPlatformAPI);
   state_ = RunState::kWorking;
 
   for (auto& mutator_item : mutator_items_) {
@@ -98,22 +125,18 @@
                           WrapCrossThreadPersistent(this)));
     }
 
-    DCHECK(global_scope_);
-    DCHECK(global_scope_->IsContextThread());
-
     // At worklet scope termination break the reference cycle between
     // AnimationWorkletGlobalScope and AnimationWorkletProxyClient.
-    global_scope_ = nullptr;
+    global_scopes_.clear();
   }
 
   mutator_items_.clear();
-
-  DCHECK(state_ != RunState::kDisposed);
   state_ = RunState::kDisposed;
 }
 
 std::unique_ptr<AnimationWorkletOutput> AnimationWorkletProxyClient::Mutate(
     std::unique_ptr<AnimationWorkletInput> input) {
+  base::ElapsedTimer timer;
   DCHECK(input);
 #if DCHECK_IS_ON()
   DCHECK(input->ValidateId(worklet_id_))
@@ -121,16 +144,57 @@
       << worklet_id_;
 #endif
 
-  if (!global_scope_)
-    return nullptr;
+  // Create or destroy instances of animators on each global scope.
+  for (auto global_scope : global_scopes_) {
+    global_scope->UpdateAnimatorsList(*input);
+  }
 
-  auto output = global_scope_->Mutate(*input);
+  std::unique_ptr<AnimationWorkletOutput> output =
+      std::make_unique<AnimationWorkletOutput>();
 
-  // TODO(petermayo): https://crbug.com/791280 PostCrossThreadTask to supply
-  // this rather than return it.
+  // Assume animators are stateful.
+  // TODO(https://crbug.com/914918): Implement filter for detecting stateless
+  // and stateful animators. Call mutate on stateful and stateless global
+  // scopes with appropriate predicates.
+  AnimationWorkletGlobalScope* stateful_global_scope =
+      SelectStatefulGlobalScope();
+  DCHECK(stateful_global_scope);
+  stateful_global_scope->UpdateAnimators(*input, output.get(),
+                                         [](Animator*) { return true; });
+
+  UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES(
+      "Animation.AnimationWorklet.MutateDuration", timer.Elapsed(),
+      base::TimeDelta::FromMicroseconds(1), base::TimeDelta::FromSeconds(10),
+      50);
+
   return output;
 }
 
+AnimationWorkletGlobalScope*
+AnimationWorkletProxyClient::SelectStatelessGlobalScope() {
+  if (--next_global_scope_switch_countdown_ < 0) {
+    current_stateless_global_scope_index_ =
+        (++current_stateless_global_scope_index_ % global_scopes_.size());
+    // Introduce an element of randomness in the switching interval to make
+    // stateful dependences easier to spot.
+    next_global_scope_switch_countdown_ =
+        base::RandInt(0, kMaxMutateCountToSwitch - 1);
+  }
+  return global_scopes_[current_stateless_global_scope_index_];
+}
+
+AnimationWorkletGlobalScope*
+AnimationWorkletProxyClient::SelectStatefulGlobalScope() {
+  return global_scopes_[kStatefulGlobalScopeIndex];
+}
+
+void AnimationWorkletProxyClient::AddGlobalScopeForTesting(
+    WorkletGlobalScope* global_scope) {
+  DCHECK(global_scope);
+  DCHECK(global_scope->IsContextThread());
+  global_scopes_.push_back(To<AnimationWorkletGlobalScope>(global_scope));
+}
+
 // static
 AnimationWorkletProxyClient* AnimationWorkletProxyClient::FromDocument(
     Document* document,
diff --git a/third_party/blink/renderer/modules/animationworklet/animation_worklet_proxy_client.h b/third_party/blink/renderer/modules/animationworklet/animation_worklet_proxy_client.h
index 193707f..7f91dc3 100644
--- a/third_party/blink/renderer/modules/animationworklet/animation_worklet_proxy_client.h
+++ b/third_party/blink/renderer/modules/animationworklet/animation_worklet_proxy_client.h
@@ -34,6 +34,7 @@
 
  public:
   static const char kSupplementName[];
+  static const wtf_size_t kNumStatelessGlobalScopes;
 
   // This client is hooked to the given |mutatee|, on the given
   // |mutatee_runner|.
@@ -46,7 +47,7 @@
   void Trace(blink::Visitor*) override;
 
   virtual void SynchronizeAnimatorName(const String& animator_name);
-  virtual void SetGlobalScope(WorkletGlobalScope*);
+  virtual void AddGlobalScope(WorkletGlobalScope*);
   void Dispose();
 
   // AnimationWorkletMutator:
@@ -55,10 +56,25 @@
   std::unique_ptr<AnimationWorkletOutput> Mutate(
       std::unique_ptr<AnimationWorkletInput> input) override;
 
+  void AddGlobalScopeForTesting(WorkletGlobalScope*);
+
   static AnimationWorkletProxyClient* FromDocument(Document*, int worklet_id);
   static AnimationWorkletProxyClient* From(WorkerClients*);
 
  private:
+  FRIEND_TEST_ALL_PREFIXES(AnimationWorkletGlobalScopeTest, SelectGlobalScope);
+
+  // Separate global scope selectors are used instead of overriding
+  // Worklet::SelectGlobalScope since two different selection mechanisms are
+  // required in order to support statefulness and enforce statelessness
+  // depending on the animators.
+  // The stateless global scope periodically switches in order to enforce
+  // stateless behavior. Prior state is lost on each switch to global scope.
+  AnimationWorkletGlobalScope* SelectStatelessGlobalScope();
+  // The stateful global scope remains fixed to preserve state between mutate
+  // calls.
+  AnimationWorkletGlobalScope* SelectStatefulGlobalScope();
+
   const int worklet_id_;
 
   struct MutatorItem {
@@ -72,9 +88,12 @@
   };
   WTF::Vector<MutatorItem> mutator_items_;
 
-  CrossThreadPersistent<AnimationWorkletGlobalScope> global_scope_;
+  Vector<CrossThreadPersistent<AnimationWorkletGlobalScope>> global_scopes_;
 
   enum RunState { kUninitialized, kWorking, kDisposed } state_;
+
+  int next_global_scope_switch_countdown_;
+  wtf_size_t current_stateless_global_scope_index_;
 };
 
 void MODULES_EXPORT
diff --git a/third_party/blink/renderer/modules/clipboard/clipboard_promise.cc b/third_party/blink/renderer/modules/clipboard/clipboard_promise.cc
index 6f2f6da..f6571686 100644
--- a/third_party/blink/renderer/modules/clipboard/clipboard_promise.cc
+++ b/third_party/blink/renderer/modules/clipboard/clipboard_promise.cc
@@ -121,9 +121,8 @@
 
 PermissionService* ClipboardPromise::GetPermissionService() {
   if (!permission_service_) {
-    ConnectToPermissionService(
-        ExecutionContext::From(script_state_),
-        mojo::MakeRequest(&permission_service_, GetTaskRunner()));
+    ConnectToPermissionService(ExecutionContext::From(script_state_),
+                               mojo::MakeRequest(&permission_service_));
   }
   return permission_service_.get();
 }
diff --git a/third_party/blink/renderer/modules/geolocation/geolocation.cc b/third_party/blink/renderer/modules/geolocation/geolocation.cc
index 2395e2a..cdc7edc 100644
--- a/third_party/blink/renderer/modules/geolocation/geolocation.cc
+++ b/third_party/blink/renderer/modules/geolocation/geolocation.cc
@@ -465,7 +465,7 @@
   GetFrame()->GetInterfaceProvider().GetInterface(&geolocation_service_,
                                                   invalidator, task_runner);
   geolocation_service_->CreateGeolocation(
-      MakeRequest(&geolocation_, invalidator, task_runner),
+      MakeRequest(&geolocation_, invalidator),
       LocalFrame::HasTransientUserActivation(GetFrame()));
 
   geolocation_.set_connection_error_handler(WTF::Bind(
diff --git a/third_party/blink/renderer/modules/mediastream/media_devices.cc b/third_party/blink/renderer/modules/mediastream/media_devices.cc
index 34ab6196..223370b 100644
--- a/third_party/blink/renderer/modules/mediastream/media_devices.cc
+++ b/third_party/blink/renderer/modules/mediastream/media_devices.cc
@@ -336,9 +336,8 @@
 const mojom::blink::MediaDevicesDispatcherHostPtr&
 MediaDevices::GetDispatcherHost(LocalFrame* frame) {
   if (!dispatcher_host_) {
-    // See https://bit.ly/2S0zRAS for task types
-    frame->GetInterfaceProvider().GetInterface(mojo::MakeRequest(
-        &dispatcher_host_, frame->GetTaskRunner(TaskType::kMiscPlatformAPI)));
+    frame->GetInterfaceProvider().GetInterface(
+        mojo::MakeRequest(&dispatcher_host_));
     dispatcher_host_.set_connection_error_handler(
         WTF::Bind(&MediaDevices::OnDispatcherHostConnectionError,
                   WrapWeakPersistent(this)));
diff --git a/third_party/blink/renderer/modules/notifications/notification_manager.cc b/third_party/blink/renderer/modules/notifications/notification_manager.cc
index 029090c..8ccdb3c 100644
--- a/third_party/blink/renderer/modules/notifications/notification_manager.cc
+++ b/third_party/blink/renderer/modules/notifications/notification_manager.cc
@@ -69,11 +69,8 @@
   ExecutionContext* context = ExecutionContext::From(script_state);
 
   if (!permission_service_) {
-    // See https://bit.ly/2S0zRAS for task types.
-    ConnectToPermissionService(
-        context,
-        mojo::MakeRequest(&permission_service_,
-                          context->GetTaskRunner(TaskType::kMiscPlatformAPI)));
+    ConnectToPermissionService(context,
+                               mojo::MakeRequest(&permission_service_));
     permission_service_.set_connection_error_handler(
         WTF::Bind(&NotificationManager::OnPermissionServiceConnectionError,
                   WrapWeakPersistent(this)));
@@ -228,10 +225,7 @@
 NotificationManager::GetNotificationService() {
   if (!notification_service_) {
     if (auto* provider = GetSupplementable()->GetInterfaceProvider()) {
-      // See https://bit.ly/2S0zRAS for task types.
-      provider->GetInterface(mojo::MakeRequest(
-          &notification_service_,
-          GetSupplementable()->GetTaskRunner(TaskType::kMiscPlatformAPI)));
+      provider->GetInterface(mojo::MakeRequest(&notification_service_));
 
       notification_service_.set_connection_error_handler(
           WTF::Bind(&NotificationManager::OnNotificationServiceConnectionError,
diff --git a/third_party/blink/renderer/modules/payments/payment_instruments.cc b/third_party/blink/renderer/modules/payments/payment_instruments.cc
index b339f895..d67dd5b2 100644
--- a/third_party/blink/renderer/modules/payments/payment_instruments.cc
+++ b/third_party/blink/renderer/modules/payments/payment_instruments.cc
@@ -279,11 +279,8 @@
 mojom::blink::PermissionService* PaymentInstruments::GetPermissionService(
     ScriptState* script_state) {
   if (!permission_service_) {
-    ExecutionContext* context = ExecutionContext::From(script_state);
-    ConnectToPermissionService(
-        context,
-        mojo::MakeRequest(&permission_service_,
-                          context->GetTaskRunner(TaskType::kUserInteraction)));
+    ConnectToPermissionService(ExecutionContext::From(script_state),
+                               mojo::MakeRequest(&permission_service_));
   }
   return permission_service_.get();
 }
diff --git a/third_party/blink/renderer/modules/payments/payment_request.cc b/third_party/blink/renderer/modules/payments/payment_request.cc
index 48d34e8..62ea7cdc8 100644
--- a/third_party/blink/renderer/modules/payments/payment_request.cc
+++ b/third_party/blink/renderer/modules/payments/payment_request.cc
@@ -36,6 +36,7 @@
 #include "third_party/blink/renderer/core/html/html_iframe_element.h"
 #include "third_party/blink/renderer/core/inspector/console_message.h"
 #include "third_party/blink/renderer/core/inspector/console_types.h"
+#include "third_party/blink/renderer/core/origin_trials/origin_trials.h"
 #include "third_party/blink/renderer/modules/event_target_modules_names.h"
 #include "third_party/blink/renderer/modules/payments/address_errors.h"
 #include "third_party/blink/renderer/modules/payments/android_pay_method_data.h"
@@ -890,7 +891,14 @@
                                            "Cannot query payment request"));
   }
 
-  payment_provider_->HasEnrolledInstrument();
+  bool per_method_quota =
+      origin_trials::PerMethodCanMakePaymentQuotaEnabled(GetExecutionContext());
+  if (per_method_quota) {
+    UseCounter::Count(GetExecutionContext(),
+                      WebFeature::kPerMethodCanMakePaymentQuota);
+  }
+
+  payment_provider_->HasEnrolledInstrument(per_method_quota);
 
   has_enrolled_instrument_resolver_ =
       ScriptPromiseResolver::Create(script_state);
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_quic_stream.cc b/third_party/blink/renderer/modules/peerconnection/rtc_quic_stream.cc
index ca89b40..6acfef6 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_quic_stream.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_quic_stream.cc
@@ -13,8 +13,6 @@
 
 // 6 MB allows a reasonable amount to buffer on the read and write side.
 // TODO(https://crbug.com/874296): Consider exposing these configurations.
-// TODO(shampson): Investigate why wpt get slow throughput when this value
-// is higher (24 MB).
 const uint32_t RTCQuicStream::kWriteBufferSize = 6 * 1024 * 1024;
 const uint32_t RTCQuicStream::kReadBufferSize = 6 * 1024 * 1024;
 
diff --git a/third_party/blink/renderer/modules/quota/storage_manager.cc b/third_party/blink/renderer/modules/quota/storage_manager.cc
index caa1458..764d93a 100644
--- a/third_party/blink/renderer/modules/quota/storage_manager.cc
+++ b/third_party/blink/renderer/modules/quota/storage_manager.cc
@@ -147,11 +147,8 @@
 PermissionService& StorageManager::GetPermissionService(
     ExecutionContext* execution_context) {
   if (!permission_service_) {
-    // See https://bit.ly/2S0zRAS for task types.
-    ConnectToPermissionService(
-        execution_context, mojo::MakeRequest(&permission_service_,
-                                             execution_context->GetTaskRunner(
-                                                 TaskType::kMiscPlatformAPI)));
+    ConnectToPermissionService(execution_context,
+                               mojo::MakeRequest(&permission_service_));
     permission_service_.set_connection_error_handler(
         WTF::Bind(&StorageManager::PermissionServiceConnectionError,
                   WrapWeakPersistent(this)));
@@ -174,11 +171,8 @@
 mojom::blink::QuotaDispatcherHost& StorageManager::GetQuotaHost(
     ExecutionContext* execution_context) {
   if (!quota_host_) {
-    // See https://bit.ly/2S0zRAS for task types.
-    ConnectToQuotaDispatcherHost(
-        execution_context,
-        mojo::MakeRequest(&quota_host_, execution_context->GetTaskRunner(
-                                            TaskType::kMiscPlatformAPI)));
+    ConnectToQuotaDispatcherHost(execution_context,
+                                 mojo::MakeRequest(&quota_host_));
   }
   return *quota_host_;
 }
diff --git a/third_party/blink/renderer/modules/worklet/animation_and_paint_worklet_thread_test.cc b/third_party/blink/renderer/modules/worklet/animation_and_paint_worklet_thread_test.cc
index f021033a..1c54add 100644
--- a/third_party/blink/renderer/modules/worklet/animation_and_paint_worklet_thread_test.cc
+++ b/third_party/blink/renderer/modules/worklet/animation_and_paint_worklet_thread_test.cc
@@ -40,7 +40,7 @@
  public:
   TestAnimationWorkletProxyClient()
       : AnimationWorkletProxyClient(0, nullptr, nullptr, nullptr, nullptr){};
-  void SetGlobalScope(WorkletGlobalScope*) override {}
+  void AddGlobalScope(WorkletGlobalScope*) override {}
 };
 
 }  // namespace
diff --git a/third_party/blink/renderer/platform/fonts/font.cc b/third_party/blink/renderer/platform/fonts/font.cc
index ef63a49..46448d2 100644
--- a/third_party/blink/renderer/platform/fonts/font.cc
+++ b/third_party/blink/renderer/platform/fonts/font.cc
@@ -290,8 +290,8 @@
     SkScalar* offset_intercepts_buffer = nullptr;
     if (intercepts_buffer)
       offset_intercepts_buffer = &intercepts_buffer[num_intervals];
-    num_intervals += paint.getTextBlobIntercepts(
-        blob_info.blob.get(), bounds_array, offset_intercepts_buffer);
+    num_intervals += blob_info.blob->getIntercepts(
+        bounds_array, offset_intercepts_buffer, &paint);
   }
   return num_intervals;
 }
diff --git a/third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.cc b/third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.cc
index 851be76f..b41708f 100644
--- a/third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.cc
+++ b/third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.cc
@@ -4,6 +4,7 @@
 
 #include "third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.h"
 
+#include "gpu/GLES2/gl2extchromium.h"
 #include "gpu/command_buffer/client/gles2_interface.h"
 #include "gpu/command_buffer/common/sync_token.h"
 #include "third_party/blink/public/platform/platform.h"
@@ -39,17 +40,19 @@
     unsigned texture_id,
     base::WeakPtr<WebGraphicsContext3DProviderWrapper>&&
         context_provider_wrapper,
-    IntSize mailbox_size) {
+    IntSize mailbox_size,
+    MailboxType mailbox_type) {
   return base::AdoptRef(new AcceleratedStaticBitmapImage(
       mailbox, sync_token, texture_id, std::move(context_provider_wrapper),
-      mailbox_size));
+      mailbox_size, mailbox_type));
 }
 
 AcceleratedStaticBitmapImage::AcceleratedStaticBitmapImage(
     sk_sp<SkImage> image,
     base::WeakPtr<WebGraphicsContext3DProviderWrapper>&&
         context_provider_wrapper)
-    : paint_image_content_id_(cc::PaintImage::GetNextContentId()) {
+    : paint_image_content_id_(cc::PaintImage::GetNextContentId()),
+      mailbox_type_(MailboxType::kDeprecatedMailbox) {
   CHECK(image && image->isTextureBacked());
   texture_holder_ = std::make_unique<SkiaTextureHolder>(
       std::move(image), std::move(context_provider_wrapper));
@@ -61,8 +64,10 @@
     unsigned texture_id,
     base::WeakPtr<WebGraphicsContext3DProviderWrapper>&&
         context_provider_wrapper,
-    IntSize mailbox_size)
-    : paint_image_content_id_(cc::PaintImage::GetNextContentId()) {
+    IntSize mailbox_size,
+    MailboxType mailbox_type)
+    : paint_image_content_id_(cc::PaintImage::GetNextContentId()),
+      mailbox_type_(mailbox_type) {
   texture_holder_ = std::make_unique<MailboxTextureHolder>(
       mailbox, sync_token, texture_id, std::move(context_provider_wrapper),
       mailbox_size);
@@ -166,14 +171,25 @@
   // Get a texture id that |destProvider| knows about and copy from it.
   dest_gl->WaitSyncTokenCHROMIUM(
       texture_holder_->GetSyncToken().GetConstData());
-  GLuint source_texture_id = dest_gl->CreateAndConsumeTextureCHROMIUM(
-      texture_holder_->GetMailbox().name);
+  GLuint source_texture_id;
+  if (mailbox_type_ == MailboxType::kSharedImageId) {
+    source_texture_id = dest_gl->CreateAndTexStorage2DSharedImageCHROMIUM(
+        texture_holder_->GetMailbox().name);
+    dest_gl->BeginSharedImageAccessDirectCHROMIUM(
+        source_texture_id, GL_SHARED_IMAGE_ACCESS_MODE_READ_CHROMIUM);
+  } else {
+    source_texture_id = dest_gl->CreateAndConsumeTextureCHROMIUM(
+        texture_holder_->GetMailbox().name);
+  }
   dest_gl->CopySubTextureCHROMIUM(
       source_texture_id, 0, dest_target, dest_texture_id, 0, dest_point.X(),
       dest_point.Y(), source_sub_rectangle.X(), source_sub_rectangle.Y(),
       source_sub_rectangle.Width(), source_sub_rectangle.Height(),
       unpack_flip_y ? GL_FALSE : GL_TRUE, GL_FALSE,
       unpack_premultiply_alpha ? GL_FALSE : GL_TRUE);
+  if (mailbox_type_ == MailboxType::kSharedImageId) {
+    dest_gl->EndSharedImageAccessDirectCHROMIUM(source_texture_id);
+  }
   // This drops the |destGL| context's reference on our |m_mailbox|, but it's
   // still held alive by our SkImage.
   dest_gl->DeleteTextures(1, &source_texture_id);
diff --git a/third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.h b/third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.h
index 7fc90d2..d3c4ae9 100644
--- a/third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.h
+++ b/third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.h
@@ -23,21 +23,27 @@
 class PLATFORM_EXPORT AcceleratedStaticBitmapImage final
     : public StaticBitmapImage {
  public:
+  enum class MailboxType { kSharedImageId, kDeprecatedMailbox };
+
   ~AcceleratedStaticBitmapImage() override;
   // SkImage with a texture backing.
   static scoped_refptr<AcceleratedStaticBitmapImage> CreateFromSkImage(
       sk_sp<SkImage>,
       base::WeakPtr<WebGraphicsContext3DProviderWrapper>);
+
   // Can specify the GrContext that created the texture backing. Ideally all
   // callers would use this option. The |mailbox| is a name for the texture
-  // backing, allowing other contexts to use the same backing.
+  // backing, allowing other contexts to use the same backing. |mailbox_type|
+  // indicates whether |mailbox| is a SharedImage identifier or a deprecated
+  // mailbox (generated via ProduceTextureDirectCHROMIUM).
   static scoped_refptr<AcceleratedStaticBitmapImage>
   CreateFromWebGLContextImage(
       const gpu::Mailbox&,
       const gpu::SyncToken&,
       unsigned texture_id,
       base::WeakPtr<WebGraphicsContext3DProviderWrapper>&&,
-      IntSize mailbox_size);
+      IntSize mailbox_size,
+      MailboxType mailbox_type = MailboxType::kDeprecatedMailbox);
 
   bool CurrentFrameKnownToBeOpaque() override;
   IntSize Size() const override;
@@ -99,7 +105,8 @@
       const gpu::SyncToken&,
       unsigned texture_id,
       base::WeakPtr<WebGraphicsContext3DProviderWrapper>&&,
-      IntSize mailbox_size);
+      IntSize mailbox_size,
+      MailboxType mailbox_type);
 
   void CreateImageFromMailboxIfNeeded();
   void WaitSyncTokenIfNeeded();
@@ -115,6 +122,8 @@
   scoped_refptr<base::SingleThreadTaskRunner> original_skia_image_task_runner_;
   base::WeakPtr<WebGraphicsContext3DProviderWrapper>
       original_skia_image_context_provider_wrapper_;
+
+  const MailboxType mailbox_type_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/graphics/decoding_image_generator.cc b/third_party/blink/renderer/platform/graphics/decoding_image_generator.cc
index 6c0aef5..db1c46b 100644
--- a/third_party/blink/renderer/platform/graphics/decoding_image_generator.cc
+++ b/third_party/blink/renderer/platform/graphics/decoding_image_generator.cc
@@ -197,6 +197,9 @@
 
   TRACE_EVENT0("blink", "DecodingImageGenerator::queryYUVA8");
 
+  // TODO(crbug.com/915707): Set the colorspace based on image type.
+  // Can pass |color_space| to GetYUVComponentSizes and query a method
+  // in ImageDecoder (to be added) to get the proper value.
   if (color_space)
     *color_space = kJPEG_SkYUVColorSpace;
 
diff --git a/third_party/blink/renderer/platform/graphics/gpu/DEPS b/third_party/blink/renderer/platform/graphics/gpu/DEPS
index adce483a..5effcc4e 100644
--- a/third_party/blink/renderer/platform/graphics/gpu/DEPS
+++ b/third_party/blink/renderer/platform/graphics/gpu/DEPS
@@ -1,12 +1,15 @@
 include_rules = [
+    "+components/viz/test/test_context_provider.h",
     "+mojo/public/cpp/bindings/binding.h",
     "+mojo/public/cpp/system/platform_handle.h",
     "+device/vr/public/mojom/vr_service.mojom-blink.h",
     "+gpu/command_buffer/client/gles2_interface.h",
     "+gpu/command_buffer/client/gles2_interface_stub.h",
+    "+gpu/command_buffer/client/shared_image_interface.h",
     "+gpu/command_buffer/common/capabilities.h",
     "+gpu/command_buffer/common/gpu_memory_buffer_support.h",
     "+gpu/command_buffer/common/mailbox_holder.h",
+    "+gpu/command_buffer/common/shared_image_usage.h",
     "+gpu/config/gpu_driver_bug_workaround_type.h",
     "+gpu/config/gpu_feature_info.h",
     "+ui/gfx/gpu_fence.h",
diff --git a/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc b/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc
index 06fd08f6..f0a7225 100644
--- a/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc
+++ b/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc
@@ -43,8 +43,10 @@
 #include "gpu/GLES2/gl2extchromium.h"
 #include "gpu/command_buffer/client/gles2_interface.h"
 #include "gpu/command_buffer/client/gpu_memory_buffer_manager.h"
+#include "gpu/command_buffer/client/shared_image_interface.h"
 #include "gpu/command_buffer/common/capabilities.h"
 #include "gpu/command_buffer/common/gpu_memory_buffer_support.h"
+#include "gpu/command_buffer/common/shared_image_usage.h"
 #include "gpu/config/gpu_driver_bug_workaround_type.h"
 #include "gpu/config/gpu_feature_info.h"
 #include "third_party/blink/public/platform/platform.h"
@@ -438,6 +440,10 @@
         size_.Height(), GL_FALSE, GL_FALSE, GL_FALSE);
   }
 
+  // Signal we will no longer access |color_buffer_for_mailbox| before exporting
+  // it.
+  gl_->EndSharedImageAccessDirectCHROMIUM(color_buffer_for_mailbox->texture_id);
+
   // Put colorBufferForMailbox into its mailbox, and populate its
   // produceSyncToken with that point.
   {
@@ -460,7 +466,7 @@
 
   // Populate the output mailbox and callback.
   {
-    bool is_overlay_candidate = color_buffer_for_mailbox->image_id != 0;
+    bool is_overlay_candidate = !!color_buffer_for_mailbox->gpu_memory_buffer;
     *out_resource = viz::TransferableResource::MakeGLOverlay(
         color_buffer_for_mailbox->mailbox, GL_LINEAR, texture_target_,
         color_buffer_for_mailbox->produce_sync_token, gfx::Size(size_),
@@ -567,7 +573,7 @@
   // TODO(danakj): Instead of using PrepareTransferableResourceInternal(), we
   // could just use the actual texture id and avoid needing to produce/consume a
   // mailbox.
-  GLuint texture_id = gl_->CreateAndConsumeTextureCHROMIUM(
+  GLuint texture_id = gl_->CreateAndTexStorage2DSharedImageCHROMIUM(
       transferable_resource.mailbox_holder.mailbox.name);
 
   if (out_release_callback) {
@@ -597,7 +603,8 @@
   // in DrawingBuffer.
   return AcceleratedStaticBitmapImage::CreateFromWebGLContextImage(
       sk_image_mailbox, sk_image_sync_token, texture_id,
-      context_provider_->GetWeakPtr(), size_);
+      context_provider_->GetWeakPtr(), size_,
+      AcceleratedStaticBitmapImage::MailboxType::kSharedImageId);
 }
 
 scoped_refptr<DrawingBuffer::ColorBuffer> DrawingBuffer::CreateOrRecycleColorBuffer() {
@@ -608,6 +615,8 @@
     if (recycled->receive_sync_token.HasData())
       gl_->WaitSyncTokenCHROMIUM(recycled->receive_sync_token.GetData());
     DCHECK(recycled->size == size_);
+    gl_->BeginSharedImageAccessDirectCHROMIUM(
+        recycled->texture_id, GL_SHARED_IMAGE_ACCESS_MODE_READWRITE_CHROMIUM);
     return recycled;
   }
   return CreateColorBuffer(size_);
@@ -632,49 +641,24 @@
     DrawingBuffer* drawing_buffer,
     const IntSize& size,
     GLuint texture_id,
-    GLuint image_id,
-    std::unique_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer)
+    std::unique_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer,
+    gpu::Mailbox mailbox)
     : drawing_buffer(drawing_buffer),
       size(size),
       texture_id(texture_id),
-      image_id(image_id),
-      gpu_memory_buffer(std::move(gpu_memory_buffer)) {
-  gpu::gles2::GLES2Interface* gl = drawing_buffer->ContextGL();
-  gl->ProduceTextureDirectCHROMIUM(texture_id, mailbox.name);
-}
+      gpu_memory_buffer(std::move(gpu_memory_buffer)),
+      mailbox(mailbox) {}
 
 DrawingBuffer::ColorBuffer::~ColorBuffer() {
   gpu::gles2::GLES2Interface* gl = drawing_buffer->gl_;
-  GLenum texture_target = drawing_buffer->texture_target_;
-  if (receive_sync_token.HasData())
-    gl->WaitSyncTokenCHROMIUM(receive_sync_token.GetConstData());
-  if (image_id) {
-    gl->BindTexture(texture_target, texture_id);
-    gl->ReleaseTexImage2DCHROMIUM(texture_target, image_id);
-    if (rgb_workaround_texture_id) {
-      gl->BindTexture(texture_target, rgb_workaround_texture_id);
-      gl->ReleaseTexImage2DCHROMIUM(texture_target, image_id);
-    }
-    gl->DestroyImageCHROMIUM(image_id);
-    switch (texture_target) {
-      case GL_TEXTURE_2D:
-        // Restore the texture binding for GL_TEXTURE_2D, since the client will
-        // expect the previous state.
-        if (drawing_buffer->client_)
-          drawing_buffer->client_->DrawingBufferClientRestoreTexture2DBinding();
-        break;
-      case GC3D_TEXTURE_RECTANGLE_ARB:
-        // Rectangle textures aren't exposed to WebGL, so don't bother
-        // restoring this state (there is no meaningful way to restore it).
-        break;
-      default:
-        NOTREACHED();
-        break;
-    }
-    gpu_memory_buffer.reset();
-  }
+  gpu::SharedImageInterface* sii =
+      drawing_buffer->ContextProvider()->SharedImageInterface();
+
+  sii->DestroySharedImage(receive_sync_token, mailbox);
+  gpu_memory_buffer.reset();
   gl->DeleteTextures(1, &texture_id);
   if (rgb_workaround_texture_id) {
+    sii->DestroySharedImage(receive_sync_token, rgb_workaround_mailbox);
     // Avoid deleting this texture if it was unused.
     gl->DeleteTextures(1, &rgb_workaround_texture_id);
   }
@@ -866,25 +850,23 @@
   // through a mailbox first.
   gpu::Mailbox mailbox;
   gpu::SyncToken produce_sync_token;
+  GLuint texture_id_to_restore_access = 0;
   if (src_buffer == kFrontBuffer && front_color_buffer_) {
     mailbox = front_color_buffer_->mailbox;
     produce_sync_token = front_color_buffer_->produce_sync_token;
   } else {
+    GLuint texture_id = 0;
     if (premultiplied_alpha_false_texture_) {
-      // If this texture exists, then it holds the rendering results at this
-      // point, rather than back_color_buffer_. back_color_buffer_ receives the
-      // contents of this texture later, premultiplying alpha into the color
-      // channels. We lazily produce a mailbox for it.
-      if (premultiplied_alpha_false_mailbox_.IsZero()) {
-        src_gl->ProduceTextureDirectCHROMIUM(
-            premultiplied_alpha_false_texture_,
-            premultiplied_alpha_false_mailbox_.name);
-      }
+      DCHECK(!premultiplied_alpha_false_mailbox_.IsZero());
       mailbox = premultiplied_alpha_false_mailbox_;
+      texture_id = premultiplied_alpha_false_texture_;
     } else {
       mailbox = back_color_buffer_->mailbox;
+      texture_id = back_color_buffer_->texture_id;
     }
+    src_gl->EndSharedImageAccessDirectCHROMIUM(texture_id);
     src_gl->GenUnverifiedSyncTokenCHROMIUM(produce_sync_token.GetData());
+    texture_id_to_restore_access = texture_id;
   }
 
   if (!produce_sync_token.HasData()) {
@@ -893,7 +875,9 @@
   }
 
   dst_gl->WaitSyncTokenCHROMIUM(produce_sync_token.GetConstData());
-  GLuint src_texture = dst_gl->CreateAndConsumeTextureCHROMIUM(mailbox.name);
+
+  GLuint src_texture =
+      dst_gl->CreateAndTexStorage2DSharedImageCHROMIUM(mailbox.name);
 
   GLboolean unpack_premultiply_alpha_needed = GL_FALSE;
   GLboolean unpack_unpremultiply_alpha_needed = GL_FALSE;
@@ -902,19 +886,25 @@
   else if (want_alpha_channel_ && !premultiplied_alpha_ && premultiply_alpha)
     unpack_premultiply_alpha_needed = GL_TRUE;
 
+  dst_gl->BeginSharedImageAccessDirectCHROMIUM(
+      src_texture, GL_SHARED_IMAGE_ACCESS_MODE_READ_CHROMIUM);
   dst_gl->CopySubTextureCHROMIUM(
       src_texture, 0, dst_texture_target, dst_texture, 0,
       dst_texture_offset.X(), dst_texture_offset.Y(), src_sub_rectangle.X(),
       src_sub_rectangle.Y(), src_sub_rectangle.Width(),
       src_sub_rectangle.Height(), flip_y, unpack_premultiply_alpha_needed,
       unpack_unpremultiply_alpha_needed);
-
+  dst_gl->EndSharedImageAccessDirectCHROMIUM(src_texture);
   dst_gl->DeleteTextures(1, &src_texture);
 
   gpu::SyncToken sync_token;
   dst_gl->GenUnverifiedSyncTokenCHROMIUM(sync_token.GetData());
   src_gl->WaitSyncTokenCHROMIUM(sync_token.GetData());
-
+  if (texture_id_to_restore_access) {
+    src_gl->BeginSharedImageAccessDirectCHROMIUM(
+        texture_id_to_restore_access,
+        GL_SHARED_IMAGE_ACCESS_MODE_READWRITE_CHROMIUM);
+  }
   return true;
 }
 
@@ -977,7 +967,12 @@
     gl_->DeleteRenderbuffers(1, &depth_stencil_buffer_);
 
   if (premultiplied_alpha_false_texture_) {
+    gl_->EndSharedImageAccessDirectCHROMIUM(premultiplied_alpha_false_texture_);
     gl_->DeleteTextures(1, &premultiplied_alpha_false_texture_);
+    gpu::SharedImageInterface* sii = ContextProvider()->SharedImageInterface();
+    gpu::SyncToken sync_token;
+    gl_->GenUnverifiedSyncTokenCHROMIUM(sync_token.GetData());
+    sii->DestroySharedImage(sync_token, premultiplied_alpha_false_mailbox_);
     premultiplied_alpha_false_mailbox_.SetZero();
   }
 
@@ -1009,42 +1004,36 @@
   // via CopySubTextureCHROMIUM, performing the premultiplication step then.
   if (ShouldUseChromiumImage() && allocate_alpha_channel_ &&
       !premultiplied_alpha_) {
+    gpu::SharedImageInterface* sii = ContextProvider()->SharedImageInterface();
     state_restorer_->SetTextureBindingDirty();
     // TODO(kbr): unify with code in CreateColorBuffer.
     if (premultiplied_alpha_false_texture_) {
+      gl_->EndSharedImageAccessDirectCHROMIUM(
+          premultiplied_alpha_false_texture_);
       gl_->DeleteTextures(1, &premultiplied_alpha_false_texture_);
+      gpu::SyncToken sync_token;
+      gl_->GenUnverifiedSyncTokenCHROMIUM(sync_token.GetData());
+      sii->DestroySharedImage(sync_token, premultiplied_alpha_false_mailbox_);
       premultiplied_alpha_false_mailbox_.SetZero();
       premultiplied_alpha_false_texture_ = 0;
     }
-    gl_->GenTextures(1, &premultiplied_alpha_false_texture_);
-    // The command decoder forbids allocating "real" OpenGL textures with the
-    // GL_TEXTURE_RECTANGLE_ARB target. Allocate this temporary texture with
-    // type GL_TEXTURE_2D all the time. CopySubTextureCHROMIUM can handle
-    // copying between 2D and rectangular textures.
-    gl_->BindTexture(GL_TEXTURE_2D, premultiplied_alpha_false_texture_);
-    if (storage_texture_supported_) {
-      GLenum internal_storage_format = GL_RGBA8;
-      if (use_half_float_storage_) {
-        internal_storage_format = GL_RGBA16F_EXT;
-      }
-      gl_->TexStorage2DEXT(GL_TEXTURE_2D, 1, internal_storage_format,
-                           size.Width(), size.Height());
-    } else {
-      GLenum internal_format = GL_RGBA;
-      GLenum format = internal_format;
-      GLenum data_type = GL_UNSIGNED_BYTE;
-      if (use_half_float_storage_) {
-        if (webgl_version_ > kWebGL1) {
-          internal_format = GL_RGBA16F;
-          data_type = GL_HALF_FLOAT;
-        } else {
-          internal_format = GL_RGBA;
-          data_type = GL_HALF_FLOAT_OES;
-        }
-      }
-      gl_->TexImage2D(GL_TEXTURE_2D, 0, internal_format, size.Width(),
-                      size.Height(), 0, format, data_type, nullptr);
-    }
+    viz::ResourceFormat format;
+    if (use_half_float_storage_)
+      format = viz::RGBA_F16;
+    else
+      format = viz::RGBA_8888;
+    premultiplied_alpha_false_mailbox_ = sii->CreateSharedImage(
+        format, static_cast<gfx::Size>(size), storage_color_space_,
+        gpu::SHARED_IMAGE_USAGE_GLES2 |
+            gpu::SHARED_IMAGE_USAGE_GLES2_FRAMEBUFFER_HINT);
+    gpu::SyncToken sync_token = sii->GenUnverifiedSyncToken();
+    gl_->WaitSyncTokenCHROMIUM(sync_token.GetConstData());
+    premultiplied_alpha_false_texture_ =
+        gl_->CreateAndTexStorage2DSharedImageCHROMIUM(
+            premultiplied_alpha_false_mailbox_.name);
+    gl_->BeginSharedImageAccessDirectCHROMIUM(
+        premultiplied_alpha_false_texture_,
+        GL_SHARED_IMAGE_ACCESS_MODE_READWRITE_CHROMIUM);
   }
 
   AttachColorBufferToReadFramebuffer();
@@ -1413,12 +1402,14 @@
   state_restorer_->SetFramebufferBindingDirty();
   state_restorer_->SetTextureBindingDirty();
 
-  // Select the parameters for the texture object. Allocate the backing
-  // GpuMemoryBuffer and GLImage, if one is going to be used.
-  GLuint image_id = 0;
-  std::unique_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer;
+  gpu::SharedImageInterface* sii = ContextProvider()->SharedImageInterface();
   gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager =
       Platform::Current()->GetGpuMemoryBufferManager();
+
+  gpu::Mailbox mailbox;
+  GLuint texture_id = 0;
+  std::unique_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer;
+
   if (ShouldUseChromiumImage()) {
     gfx::BufferFormat buffer_format;
     if (allocate_alpha_channel_) {
@@ -1433,67 +1424,57 @@
         buffer_format = gfx::BufferFormat::BGRX_8888;
       }
     }
+    // TODO(crbug.com/911176): When RGB emulation is not needed, we should use
+    // the non-GMB CreateSharedImage with gpu::SHARED_IMAGE_USAGE_SCANOUT in
+    // order to allocate the GMB service-side and avoid a synchronous round-trip
+    // to the browser process here.
     gpu_memory_buffer = gpu_memory_buffer_manager->CreateGpuMemoryBuffer(
         gfx::Size(size), buffer_format, gfx::BufferUsage::SCANOUT,
         gpu::kNullSurfaceHandle);
+
     if (gpu_memory_buffer) {
-      gpu_memory_buffer->SetColorSpace(storage_color_space_);
-      const GLenum gl_format = allocate_alpha_channel_ ? GL_RGBA : GL_RGB;
-
-      image_id =
-          gl_->CreateImageCHROMIUM(gpu_memory_buffer->AsClientBuffer(),
-                                   size.Width(), size.Height(), gl_format);
-      if (!image_id)
-        gpu_memory_buffer.reset();
+      mailbox = sii->CreateSharedImage(
+          gpu_memory_buffer.get(), gpu_memory_buffer_manager,
+          storage_color_space_,
+          gpu::SHARED_IMAGE_USAGE_GLES2 |
+              gpu::SHARED_IMAGE_USAGE_GLES2_FRAMEBUFFER_HINT |
+              gpu::SHARED_IMAGE_USAGE_DISPLAY |
+              gpu::SHARED_IMAGE_USAGE_SCANOUT);
     }
   }
 
-  // Allocate the texture for this object.
-  GLuint texture_id = 0;
-  {
-    gl_->GenTextures(1, &texture_id);
-    gl_->BindTexture(texture_target_, texture_id);
-    gl_->TexParameteri(texture_target_, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
-    gl_->TexParameteri(texture_target_, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
-    gl_->TexParameteri(texture_target_, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
-    gl_->TexParameteri(texture_target_, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
-  }
-
-  // If this is GpuMemoryBuffer-backed, then bind the texture to the
-  // GpuMemoryBuffer's GLImage. Otherwise, allocate ordinary texture storage.
-  if (image_id) {
-    gl_->BindTexImage2DCHROMIUM(texture_target_, image_id);
-  } else {
-    if (storage_texture_supported_) {
-      GLenum internal_storage_format =
-          allocate_alpha_channel_ ? GL_RGBA8 : GL_RGB8;
-      if (use_half_float_storage_) {
-        DCHECK(want_alpha_channel_);
-        internal_storage_format = GL_RGBA16F_EXT;
-      }
-      gl_->TexStorage2DEXT(GL_TEXTURE_2D, 1, internal_storage_format,
-                           size.Width(), size.Height());
+  // Create a normal SharedImage if GpuMemoryBuffer is not needed or the
+  // allocation above failed.
+  if (!gpu_memory_buffer) {
+    viz::ResourceFormat format;
+    if (allocate_alpha_channel_) {
+      if (use_half_float_storage_)
+        format = viz::RGBA_F16;
+      else
+        format = viz::RGBA_8888;
     } else {
-      GLenum internal_format = allocate_alpha_channel_ ? GL_RGBA : GL_RGB;
-      GLenum format = internal_format;
-      GLenum data_type = GL_UNSIGNED_BYTE;
-      if (use_half_float_storage_) {
-        DCHECK(want_alpha_channel_);
-        if (webgl_version_ > kWebGL1) {
-          internal_format = GL_RGBA16F;
-          data_type = GL_HALF_FLOAT;
-        } else {
-          internal_format = GL_RGBA;
-          data_type = GL_HALF_FLOAT_OES;
-        }
-      }
-      gl_->TexImage2D(texture_target_, 0, internal_format, size.Width(),
-                      size.Height(), 0, format, data_type, nullptr);
+      DCHECK(!use_half_float_storage_);
+      format = viz::RGBX_8888;
     }
+
+    mailbox = sii->CreateSharedImage(
+        format, static_cast<gfx::Size>(size), storage_color_space_,
+        gpu::SHARED_IMAGE_USAGE_GLES2 |
+            gpu::SHARED_IMAGE_USAGE_GLES2_FRAMEBUFFER_HINT |
+            gpu::SHARED_IMAGE_USAGE_DISPLAY);
   }
 
-  // Clear the alpha channel if this is RGB emulated.
-  if (image_id && !want_alpha_channel_ && have_alpha_channel_) {
+  // Import the allocated SharedImage into GL.
+  gpu::SyncToken sync_token = sii->GenUnverifiedSyncToken();
+  gl_->WaitSyncTokenCHROMIUM(sync_token.GetConstData());
+  texture_id = gl_->CreateAndTexStorage2DSharedImageCHROMIUM(mailbox.name);
+  gl_->BindTexture(texture_target_, texture_id);
+  gl_->BeginSharedImageAccessDirectCHROMIUM(
+      texture_id, GL_SHARED_IMAGE_ACCESS_MODE_READWRITE_CHROMIUM);
+
+  // Clear the alpha channel if we allocated a GpuMemoryBuffer and RGB emulation
+  // is required.
+  if (gpu_memory_buffer && !want_alpha_channel_ && have_alpha_channel_) {
     GLuint fbo = 0;
 
     state_restorer_->SetClearStateDirty();
@@ -1510,8 +1491,8 @@
     gl_->DeleteFramebuffers(1, &fbo);
   }
 
-  return base::AdoptRef(new ColorBuffer(this, size, texture_id, image_id,
-                                        std::move(gpu_memory_buffer)));
+  return base::AdoptRef(new ColorBuffer(this, size, texture_id,
+                                        std::move(gpu_memory_buffer), mailbox));
 }
 
 void DrawingBuffer::AttachColorBufferToReadFramebuffer() {
@@ -1573,7 +1554,7 @@
 
   // If for some reason the back buffer doesn't exist or doesn't have a
   // CHROMIUM_image, don't proceed with this workaround.
-  if (!back_color_buffer_ || !back_color_buffer_->image_id)
+  if (!back_color_buffer_ || !back_color_buffer_->gpu_memory_buffer)
     return false;
 
   // Before allowing the BlitFramebuffer call to go through, it's necessary
@@ -1589,21 +1570,24 @@
   GLuint rgb_texture = back_color_buffer_->rgb_workaround_texture_id;
   DCHECK_EQ(texture_target_, GC3D_TEXTURE_RECTANGLE_ARB);
   if (!rgb_texture) {
-    gl_->GenTextures(1, &rgb_texture);
-    gl_->BindTexture(texture_target_, rgb_texture);
-    gl_->TexParameteri(texture_target_, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
-    gl_->TexParameteri(texture_target_, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
-    gl_->TexParameteri(texture_target_, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
-    gl_->TexParameteri(texture_target_, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
-
-    // Bind this texture to the CHROMIUM_image instance that the color
-    // buffer owns. This is an expensive operation, so it's important that
-    // the result be cached.
-    gl_->BindTexImage2DWithInternalformatCHROMIUM(texture_target_, GL_RGB,
-                                                  back_color_buffer_->image_id);
+    gpu::SharedImageInterface* sii = ContextProvider()->SharedImageInterface();
+    gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager =
+        Platform::Current()->GetGpuMemoryBufferManager();
+    back_color_buffer_->rgb_workaround_mailbox = sii->CreateSharedImage(
+        back_color_buffer_->gpu_memory_buffer.get(), gpu_memory_buffer_manager,
+        storage_color_space_,
+        gpu::SHARED_IMAGE_USAGE_GLES2 |
+            gpu::SHARED_IMAGE_USAGE_GLES2_FRAMEBUFFER_HINT |
+            gpu::SHARED_IMAGE_USAGE_DISPLAY | gpu::SHARED_IMAGE_USAGE_SCANOUT |
+            gpu::SHARED_IMAGE_USAGE_RGB_EMULATION);
+    gl_->WaitSyncTokenCHROMIUM(sii->GenUnverifiedSyncToken().GetConstData());
+    rgb_texture = gl_->CreateAndTexStorage2DSharedImageCHROMIUM(
+        back_color_buffer_->rgb_workaround_mailbox.name);
     back_color_buffer_->rgb_workaround_texture_id = rgb_texture;
   }
 
+  gl_->BeginSharedImageAccessDirectCHROMIUM(
+      rgb_texture, GL_SHARED_IMAGE_ACCESS_MODE_READWRITE_CHROMIUM);
   gl_->FramebufferTexture2D(GL_DRAW_FRAMEBUFFER_ANGLE, GL_COLOR_ATTACHMENT0,
                             texture_target_, rgb_texture, 0);
   return true;
@@ -1613,7 +1597,9 @@
   // This will only be called if SetupRGBEmulationForBlitFramebuffer was.
   // Put the framebuffer back the way it was, and clear the alpha channel.
   DCHECK(back_color_buffer_);
-  DCHECK(back_color_buffer_->image_id);
+  DCHECK(back_color_buffer_->gpu_memory_buffer);
+  gl_->EndSharedImageAccessDirectCHROMIUM(
+      back_color_buffer_->rgb_workaround_texture_id);
   gl_->FramebufferTexture2D(GL_DRAW_FRAMEBUFFER_ANGLE, GL_COLOR_ATTACHMENT0,
                             texture_target_, back_color_buffer_->texture_id, 0);
   // Clear the alpha channel.
diff --git a/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.h b/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.h
index 82a8e39..616722f 100644
--- a/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.h
+++ b/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.h
@@ -333,8 +333,8 @@
     ColorBuffer(DrawingBuffer*,
                 const IntSize&,
                 GLuint texture_id,
-                GLuint image_id,
-                std::unique_ptr<gfx::GpuMemoryBuffer>);
+                std::unique_ptr<gfx::GpuMemoryBuffer>,
+                gpu::Mailbox mailbox);
     ~ColorBuffer();
 
     // The owning DrawingBuffer. Note that DrawingBuffer is explicitly destroyed
@@ -343,7 +343,6 @@
     scoped_refptr<DrawingBuffer> drawing_buffer;
     const IntSize size;
     const GLuint texture_id = 0;
-    const GLuint image_id = 0;
     std::unique_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer;
 
     // If we're emulating an RGB back buffer using an RGBA Chromium
@@ -356,6 +355,9 @@
     // situation (the alpha channel is zeroed), requiring more fixups.
     GLuint rgb_workaround_texture_id = 0;
 
+    // The mailbox for |rgb_workaround_texture_id|.
+    gpu::Mailbox rgb_workaround_mailbox;
+
     // The mailbox used to send this buffer to the compositor.
     gpu::Mailbox mailbox;
 
diff --git a/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer_test.cc b/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer_test.cc
index 655e3765a..052bd2b 100644
--- a/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer_test.cc
+++ b/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer_test.cc
@@ -53,6 +53,10 @@
 
 namespace blink {
 
+MATCHER_P(SyncTokenEq, token, "") {
+  return *reinterpret_cast<const gpu::SyncToken*>(arg) == token;
+}
+
 class DrawingBufferTest : public Test {
  protected:
   void SetUp() override { Init(kDisableMultisampling); }
@@ -143,7 +147,9 @@
 }
 
 TEST_F(DrawingBufferTest, VerifyResizingProperlyAffectsResources) {
-  GLES2InterfaceForTests* gl_ = drawing_buffer_->ContextGLForTests();
+  viz::TestSharedImageInterface* sii =
+      drawing_buffer_->SharedImageInterfaceForTests();
+
   VerifyStateWasRestored();
   viz::TransferableResource resource;
   std::unique_ptr<viz::SingleReleaseCallback> release_callback;
@@ -156,7 +162,7 @@
   EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(nullptr, &resource,
                                                            &release_callback));
   VerifyStateWasRestored();
-  EXPECT_EQ(initial_size, gl_->MostRecentlyProducedSize());
+  EXPECT_EQ(static_cast<gfx::Size>(initial_size), sii->MostRecentSize());
 
   // Resize to 100x50.
   drawing_buffer_->Resize(alternate_size);
@@ -168,7 +174,7 @@
   EXPECT_TRUE(drawing_buffer_->MarkContentsChanged());
   EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(nullptr, &resource,
                                                            &release_callback));
-  EXPECT_EQ(alternate_size, gl_->MostRecentlyProducedSize());
+  EXPECT_EQ(static_cast<gfx::Size>(alternate_size), sii->MostRecentSize());
   VerifyStateWasRestored();
 
   // Reset to initial size.
@@ -182,7 +188,7 @@
   EXPECT_TRUE(drawing_buffer_->MarkContentsChanged());
   EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(nullptr, &resource,
                                                            &release_callback));
-  EXPECT_EQ(initial_size, gl_->MostRecentlyProducedSize());
+  EXPECT_EQ(static_cast<gfx::Size>(initial_size), sii->MostRecentSize());
   VerifyStateWasRestored();
 
   // Prepare one final resource and verify that it's the correct size.
@@ -191,7 +197,7 @@
   EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(nullptr, &resource,
                                                            &release_callback));
   VerifyStateWasRestored();
-  EXPECT_EQ(initial_size, gl_->MostRecentlyProducedSize());
+  EXPECT_EQ(static_cast<gfx::Size>(initial_size), sii->MostRecentSize());
   release_callback->Run(gpu::SyncToken(), false /* lostResource */);
   drawing_buffer_->BeginDestruction();
 }
@@ -349,31 +355,36 @@
 
   // Produce resources.
   EXPECT_FALSE(drawing_buffer_->MarkContentsChanged());
-  EXPECT_EQ(gpu::SyncToken(), gl_->MostRecentlyWaitedSyncToken());
   EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(nullptr, &resource,
                                                            &release_callback));
-  // PrepareTransferableResource() does not wait for any sync point.
-  EXPECT_EQ(gpu::SyncToken(), gl_->MostRecentlyWaitedSyncToken());
 
+  // Pretend to release the resource. We should not wait for the sync token yet
+  // because the returned buffer is not recycled.
   gpu::SyncToken wait_sync_token;
   gl_->GenSyncTokenCHROMIUM(wait_sync_token.GetData());
+  EXPECT_CALL(*gl_, WaitSyncTokenCHROMIUMMock(SyncTokenEq(wait_sync_token)))
+      .Times(0);
   release_callback->Run(wait_sync_token, false /* lostResource */);
-  // m_drawingBuffer will wait for the sync point when recycling.
-  EXPECT_EQ(gpu::SyncToken(), gl_->MostRecentlyWaitedSyncToken());
+  testing::Mock::VerifyAndClearExpectations(gl_);
 
+  // The returned buffer will be recycled in PrepareTransferrableResource. Make
+  // sure we wait for the sync token now.
+  EXPECT_CALL(*gl_, WaitSyncTokenCHROMIUMMock(SyncTokenEq(wait_sync_token)))
+      .Times(1);
   EXPECT_TRUE(drawing_buffer_->MarkContentsChanged());
   EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(nullptr, &resource,
                                                            &release_callback));
-  // m_drawingBuffer waits for the sync point when recycling in
-  // PrepareTransferableResource().
-  EXPECT_EQ(wait_sync_token, gl_->MostRecentlyWaitedSyncToken());
+  testing::Mock::VerifyAndClearExpectations(gl_);
 
-  drawing_buffer_->BeginDestruction();
+  // Release the resource and begin destruction. We will not wait on the sync
+  // token because the buffer is not reused. SharedImageInterface, however, will
+  // wait on the sync token before destruction.
   gl_->GenSyncTokenCHROMIUM(wait_sync_token.GetData());
+  EXPECT_CALL(*gl_, WaitSyncTokenCHROMIUMMock(SyncTokenEq(wait_sync_token)))
+      .Times(0);
   release_callback->Run(wait_sync_token, false /* lostResource */);
-  // m_drawingBuffer waits for the sync point because the destruction is in
-  // progress.
-  EXPECT_EQ(wait_sync_token, gl_->MostRecentlyWaitedSyncToken());
+  drawing_buffer_->BeginDestruction();
+  testing::Mock::VerifyAndClearExpectations(gl_);
 }
 
 class DrawingBufferImageChromiumTest : public DrawingBufferTest,
@@ -392,8 +403,7 @@
         std::make_unique<WebGraphicsContext3DProviderForTests>(std::move(gl));
     GLES2InterfaceForTests* gl_ =
         static_cast<GLES2InterfaceForTests*>(provider->ContextGL());
-    image_id0_ = gl_->NextImageIdToBeCreated();
-    EXPECT_CALL(*gl_, BindTexImage2DMock(image_id0_)).Times(1);
+    EXPECT_CALL(*gl_, CreateAndTexStorage2DSharedImageCHROMIUMMock(_)).Times(1);
     bool gpu_compositing = true;
     drawing_buffer_ = DrawingBufferForTests::Create(
         std::move(provider), gpu_compositing, gl_, initial_size,
@@ -414,92 +424,141 @@
 
 TEST_F(DrawingBufferImageChromiumTest, VerifyResizingReallocatesImages) {
   GLES2InterfaceForTests* gl_ = drawing_buffer_->ContextGLForTests();
+  viz::TestSharedImageInterface* sii =
+      drawing_buffer_->SharedImageInterfaceForTests();
+
   viz::TransferableResource resource;
   std::unique_ptr<viz::SingleReleaseCallback> release_callback;
 
   IntSize initial_size(kInitialWidth, kInitialHeight);
   IntSize alternate_size(kInitialWidth, kAlternateHeight);
 
-  GLuint image_id1 = gl_->NextImageIdToBeCreated();
-  EXPECT_CALL(*gl_, BindTexImage2DMock(image_id1)).Times(1);
-  // Produce one resource at size 100x100.
+  // There should be currently one back buffer and therefore one SharedImage.
+  gpu::Mailbox mailbox1;
+  mailbox1.SetName(gl_->last_imported_shared_image()->name);
+  EXPECT_EQ(1u, sii->shared_image_count());
+  EXPECT_TRUE(sii->CheckSharedImageExists(mailbox1));
+
+  // Produce one resource at size 100x100. This should create another buffer and
+  // therefore another SharedImage.
+  EXPECT_CALL(*gl_, CreateAndTexStorage2DSharedImageCHROMIUMMock(_)).Times(1);
   EXPECT_FALSE(drawing_buffer_->MarkContentsChanged());
   EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(nullptr, &resource,
                                                            &release_callback));
-  EXPECT_EQ(initial_size, gl_->MostRecentlyProducedSize());
+  EXPECT_EQ(static_cast<gfx::Size>(initial_size), sii->MostRecentSize());
   EXPECT_TRUE(resource.is_overlay_candidate);
   EXPECT_EQ(static_cast<gfx::Size>(initial_size), resource.size);
   testing::Mock::VerifyAndClearExpectations(gl_);
   VerifyStateWasRestored();
+  gpu::Mailbox mailbox2;
+  mailbox2.SetName(gl_->last_imported_shared_image()->name);
+  EXPECT_EQ(2u, sii->shared_image_count());
+  EXPECT_TRUE(sii->CheckSharedImageExists(mailbox1));
+  EXPECT_TRUE(sii->CheckSharedImageExists(mailbox2));
+  EXPECT_EQ(mailbox2, resource.mailbox_holder.mailbox);
 
-  GLuint image_id2 = gl_->NextImageIdToBeCreated();
-  EXPECT_CALL(*gl_, BindTexImage2DMock(image_id2)).Times(1);
-  EXPECT_CALL(*gl_, DestroyImageMock(image_id0_)).Times(1);
-  EXPECT_CALL(*gl_, ReleaseTexImage2DMock(image_id0_)).Times(1);
-  EXPECT_CALL(*gl_, DestroyImageMock(image_id1)).Times(1);
-  EXPECT_CALL(*gl_, ReleaseTexImage2DMock(image_id1)).Times(1);
-  // Resize to 100x50.
+  // Resize to 100x50. The current backbuffer must be destroyed. The exported
+  // resource should stay alive. A new backbuffer must be created.
+  EXPECT_CALL(*gl_, CreateAndTexStorage2DSharedImageCHROMIUMMock(_)).Times(1);
   drawing_buffer_->Resize(alternate_size);
   VerifyStateWasRestored();
-  release_callback->Run(gpu::SyncToken(), false /* lostResource */);
-  VerifyStateWasRestored();
+  gpu::Mailbox mailbox3;
+  mailbox3.SetName(gl_->last_imported_shared_image()->name);
+  EXPECT_EQ(2u, sii->shared_image_count());
+  EXPECT_FALSE(sii->CheckSharedImageExists(mailbox1));
+  EXPECT_TRUE(sii->CheckSharedImageExists(mailbox2));
+  EXPECT_TRUE(sii->CheckSharedImageExists(mailbox3));
   testing::Mock::VerifyAndClearExpectations(gl_);
 
-  GLuint image_id3 = gl_->NextImageIdToBeCreated();
-  EXPECT_CALL(*gl_, BindTexImage2DMock(image_id3)).Times(1);
-  // Produce a resource at this size.
+  // Return the exported resource. Now it should get destroyed too.
+  release_callback->Run(gpu::SyncToken(), false /* lostResource */);
+  VerifyStateWasRestored();
+  EXPECT_EQ(1u, sii->shared_image_count());
+  EXPECT_FALSE(sii->CheckSharedImageExists(mailbox1));
+  EXPECT_FALSE(sii->CheckSharedImageExists(mailbox2));
+  EXPECT_TRUE(sii->CheckSharedImageExists(mailbox3));
+
+  // Produce a resource at the new size.
+  EXPECT_CALL(*gl_, CreateAndTexStorage2DSharedImageCHROMIUMMock(_)).Times(1);
   EXPECT_TRUE(drawing_buffer_->MarkContentsChanged());
   EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(nullptr, &resource,
                                                            &release_callback));
-  EXPECT_EQ(alternate_size, gl_->MostRecentlyProducedSize());
+  EXPECT_EQ(static_cast<gfx::Size>(alternate_size), sii->MostRecentSize());
   EXPECT_TRUE(resource.is_overlay_candidate);
   EXPECT_EQ(static_cast<gfx::Size>(alternate_size), resource.size);
+  gpu::Mailbox mailbox4;
+  mailbox4.SetName(gl_->last_imported_shared_image()->name);
+  EXPECT_EQ(2u, sii->shared_image_count());
+  EXPECT_TRUE(sii->CheckSharedImageExists(mailbox3));
+  EXPECT_TRUE(sii->CheckSharedImageExists(mailbox4));
+  EXPECT_EQ(mailbox4, resource.mailbox_holder.mailbox);
   testing::Mock::VerifyAndClearExpectations(gl_);
 
-  GLuint image_id4 = gl_->NextImageIdToBeCreated();
-  EXPECT_CALL(*gl_, BindTexImage2DMock(image_id4)).Times(1);
-  EXPECT_CALL(*gl_, DestroyImageMock(image_id2)).Times(1);
-  EXPECT_CALL(*gl_, ReleaseTexImage2DMock(image_id2)).Times(1);
-  EXPECT_CALL(*gl_, DestroyImageMock(image_id3)).Times(1);
-  EXPECT_CALL(*gl_, ReleaseTexImage2DMock(image_id3)).Times(1);
-  // Reset to initial size.
+  // Reset to initial size. The exported resource has to stay alive, but the
+  // current back buffer must be destroyed and a new one with the right size
+  // must be created.
+  EXPECT_CALL(*gl_, CreateAndTexStorage2DSharedImageCHROMIUMMock(_)).Times(1);
   drawing_buffer_->Resize(initial_size);
   VerifyStateWasRestored();
+  gpu::Mailbox mailbox5;
+  mailbox5.SetName(gl_->last_imported_shared_image()->name);
+  EXPECT_EQ(2u, sii->shared_image_count());
+  EXPECT_FALSE(sii->CheckSharedImageExists(mailbox3));
+  EXPECT_TRUE(sii->CheckSharedImageExists(mailbox4));
+  EXPECT_TRUE(sii->CheckSharedImageExists(mailbox5));
+  testing::Mock::VerifyAndClearExpectations(gl_);
+
+  // Return the exported resource. Now it will be destroyed too.
   release_callback->Run(gpu::SyncToken(), false /* lostResource */);
   VerifyStateWasRestored();
-  testing::Mock::VerifyAndClearExpectations(gl_);
+  EXPECT_EQ(1u, sii->shared_image_count());
+  EXPECT_FALSE(sii->CheckSharedImageExists(mailbox3));
+  EXPECT_FALSE(sii->CheckSharedImageExists(mailbox4));
+  EXPECT_TRUE(sii->CheckSharedImageExists(mailbox5));
 
-  GLuint image_id5 = gl_->NextImageIdToBeCreated();
-  EXPECT_CALL(*gl_, BindTexImage2DMock(image_id5)).Times(1);
   // Prepare another resource and verify that it's the correct size.
+  EXPECT_CALL(*gl_, CreateAndTexStorage2DSharedImageCHROMIUMMock(_)).Times(1);
   EXPECT_TRUE(drawing_buffer_->MarkContentsChanged());
   EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(nullptr, &resource,
                                                            &release_callback));
-  EXPECT_EQ(initial_size, gl_->MostRecentlyProducedSize());
+  EXPECT_EQ(static_cast<gfx::Size>(initial_size), sii->MostRecentSize());
   EXPECT_TRUE(resource.is_overlay_candidate);
   EXPECT_EQ(static_cast<gfx::Size>(initial_size), resource.size);
   testing::Mock::VerifyAndClearExpectations(gl_);
+  gpu::Mailbox mailbox6;
+  mailbox6.SetName(gl_->last_imported_shared_image()->name);
+  EXPECT_EQ(2u, sii->shared_image_count());
+  EXPECT_TRUE(sii->CheckSharedImageExists(mailbox5));
+  EXPECT_TRUE(sii->CheckSharedImageExists(mailbox6));
 
-  // Prepare one final resource and verify that it's the correct size.
+  // Prepare one final resource and verify that it's the correct size. We should
+  // recycle the previously exported resource and avoid allocating a new
+  // SharedImage.
   release_callback->Run(gpu::SyncToken(), false /* lostResource */);
   EXPECT_TRUE(drawing_buffer_->MarkContentsChanged());
   EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(nullptr, &resource,
                                                            &release_callback));
-  EXPECT_EQ(initial_size, gl_->MostRecentlyProducedSize());
+  EXPECT_EQ(static_cast<gfx::Size>(initial_size), sii->MostRecentSize());
   EXPECT_TRUE(resource.is_overlay_candidate);
   EXPECT_EQ(static_cast<gfx::Size>(initial_size), resource.size);
   release_callback->Run(gpu::SyncToken(), false /* lostResource */);
+  EXPECT_EQ(2u, sii->shared_image_count());
+  EXPECT_TRUE(sii->CheckSharedImageExists(mailbox5));
+  EXPECT_TRUE(sii->CheckSharedImageExists(mailbox6));
 
-  EXPECT_CALL(*gl_, DestroyImageMock(image_id5)).Times(1);
-  EXPECT_CALL(*gl_, ReleaseTexImage2DMock(image_id5)).Times(1);
-  EXPECT_CALL(*gl_, DestroyImageMock(image_id4)).Times(1);
-  EXPECT_CALL(*gl_, ReleaseTexImage2DMock(image_id4)).Times(1);
   drawing_buffer_->BeginDestruction();
-  testing::Mock::VerifyAndClearExpectations(gl_);
+  testing::Mock::VerifyAndClearExpectations(sii);
+  EXPECT_EQ(0u, sii->shared_image_count());
 }
 
 TEST_F(DrawingBufferImageChromiumTest, AllocationFailure) {
   GLES2InterfaceForTests* gl_ = drawing_buffer_->ContextGLForTests();
+  viz::TestGpuMemoryBufferManager* gmb_manager =
+      static_cast<viz::TestGpuMemoryBufferManager*>(
+          Platform::Current()->GetGpuMemoryBufferManager());
+  viz::TestSharedImageInterface* sii =
+      drawing_buffer_->SharedImageInterfaceForTests();
+
   viz::TransferableResource resource1;
   std::unique_ptr<viz::SingleReleaseCallback> release_callback1;
   viz::TransferableResource resource2;
@@ -507,33 +566,43 @@
   viz::TransferableResource resource3;
   std::unique_ptr<viz::SingleReleaseCallback> release_callback3;
 
-  // Request a resource. An image should already be created. Everything works
-  // as expected.
-  EXPECT_CALL(*gl_, BindTexImage2DMock(_)).Times(1);
+  // Request a resource. A SharedImage should already be created. Everything
+  // works as expected.
+  EXPECT_CALL(*gl_, CreateAndTexStorage2DSharedImageCHROMIUMMock(_)).Times(1);
   EXPECT_FALSE(drawing_buffer_->MarkContentsChanged());
   EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(nullptr, &resource1,
                                                            &release_callback1));
   EXPECT_TRUE(resource1.is_overlay_candidate);
+  gpu::Mailbox mailbox1;
+  mailbox1.SetName(gl_->last_imported_shared_image()->name);
+  EXPECT_TRUE(sii->CheckSharedImageExists(mailbox1));
   testing::Mock::VerifyAndClearExpectations(gl_);
   VerifyStateWasRestored();
 
-  // Force image CHROMIUM creation failure. Request another resource. It should
+  // Force GpuMemoryBuffer creation failure. Request another resource. It should
   // still be provided, but this time with allowOverlay = false.
-  gl_->SetCreateImageChromiumFail(true);
+  EXPECT_CALL(*gl_, CreateAndTexStorage2DSharedImageCHROMIUMMock(_)).Times(1);
+  gmb_manager->SetFailOnCreate(true);
   EXPECT_TRUE(drawing_buffer_->MarkContentsChanged());
   EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(nullptr, &resource2,
                                                            &release_callback2));
   EXPECT_FALSE(resource2.is_overlay_candidate);
+  gpu::Mailbox mailbox2;
+  mailbox2.SetName(gl_->last_imported_shared_image()->name);
+  EXPECT_TRUE(sii->CheckSharedImageExists(mailbox2));
   VerifyStateWasRestored();
 
-  // Check that if image CHROMIUM starts working again, resources are
-  // correctly created with allowOverlay = true.
-  EXPECT_CALL(*gl_, BindTexImage2DMock(_)).Times(1);
-  gl_->SetCreateImageChromiumFail(false);
+  // Check that if GpuMemoryBuffer allocation starts working again, resources
+  // are correctly created with allowOverlay = true.
+  EXPECT_CALL(*gl_, CreateAndTexStorage2DSharedImageCHROMIUMMock(_)).Times(1);
+  gmb_manager->SetFailOnCreate(false);
   EXPECT_TRUE(drawing_buffer_->MarkContentsChanged());
   EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(nullptr, &resource3,
                                                            &release_callback3));
   EXPECT_TRUE(resource3.is_overlay_candidate);
+  gpu::Mailbox mailbox3;
+  mailbox3.SetName(gl_->last_imported_shared_image()->name);
+  EXPECT_TRUE(sii->CheckSharedImageExists(mailbox3));
   testing::Mock::VerifyAndClearExpectations(gl_);
   VerifyStateWasRestored();
 
@@ -541,10 +610,10 @@
   release_callback2->Run(gpu::SyncToken(), false /* lostResource */);
   release_callback3->Run(gpu::SyncToken(), false /* lostResource */);
 
-  EXPECT_CALL(*gl_, DestroyImageMock(_)).Times(3);
-  EXPECT_CALL(*gl_, ReleaseTexImage2DMock(_)).Times(3);
   drawing_buffer_->BeginDestruction();
-  testing::Mock::VerifyAndClearExpectations(gl_);
+  EXPECT_FALSE(sii->CheckSharedImageExists(mailbox1));
+  EXPECT_FALSE(sii->CheckSharedImageExists(mailbox2));
+  EXPECT_FALSE(sii->CheckSharedImageExists(mailbox3));
 }
 
 class DepthStencilTrackingGLES2Interface
@@ -709,10 +778,11 @@
   gpu::SyncToken wait_sync_token;
   gl_->GenSyncTokenCHROMIUM(wait_sync_token.GetData());
   drawing_buffer_->SetIsHidden(true);
-  release_callback->Run(wait_sync_token, false /* lostResource */);
   // m_drawingBuffer deletes resource immediately when hidden.
-
-  EXPECT_EQ(wait_sync_token, gl_->MostRecentlyWaitedSyncToken());
+  EXPECT_CALL(*gl_, WaitSyncTokenCHROMIUMMock(SyncTokenEq(wait_sync_token)))
+      .Times(0);
+  release_callback->Run(wait_sync_token, false /* lostResource */);
+  testing::Mock::VerifyAndClearExpectations(gl_);
 
   drawing_buffer_->BeginDestruction();
 }
diff --git a/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer_test_helpers.h b/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer_test_helpers.h
index d5e83b84..b6920cb6 100644
--- a/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer_test_helpers.h
+++ b/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer_test_helpers.h
@@ -7,6 +7,7 @@
 
 #include "build/build_config.h"
 #include "cc/test/stub_decode_cache.h"
+#include "components/viz/test/test_context_provider.h"
 #include "gpu/command_buffer/common/capabilities.h"
 #include "gpu/config/gpu_feature_info.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -58,12 +59,16 @@
       sk_sp<SkColorSpace> color_space) override {
     return &image_decode_cache_;
   }
+  viz::TestSharedImageInterface* SharedImageInterface() override {
+    return &test_shared_image_interface_;
+  }
 
  private:
   cc::StubDecodeCache image_decode_cache_;
   std::unique_ptr<gpu::gles2::GLES2Interface> gl_;
   gpu::Capabilities capabilities_;
   gpu::GpuFeatureInfo gpu_feature_info_;
+  viz::TestSharedImageInterface test_shared_image_interface_;
 };
 
 // The target to use when binding a texture to a Chromium image.
@@ -173,10 +178,11 @@
         break;
     }
   }
-
+  MOCK_METHOD1(WaitSyncTokenCHROMIUMMock, void(const GLbyte* sync_token));
   void WaitSyncTokenCHROMIUM(const GLbyte* sync_token) override {
     memcpy(&most_recently_waited_sync_token_, sync_token,
            sizeof(most_recently_waited_sync_token_));
+    WaitSyncTokenCHROMIUMMock(sync_token);
   }
 
   GLenum CheckFramebufferStatus(GLenum target) override {
@@ -273,6 +279,19 @@
       textures[i] = id++;
   }
 
+  MOCK_METHOD1(CreateAndTexStorage2DSharedImageCHROMIUMMock,
+               void(const GLbyte*));
+  GLuint CreateAndTexStorage2DSharedImageCHROMIUM(
+      const GLbyte* mailbox) override {
+    CreateAndTexStorage2DSharedImageCHROMIUMMock(mailbox);
+    GLuint texture_id;
+    GenTextures(1, &texture_id);
+    last_imported_shared_image_.SetZero();
+    last_imported_shared_image_.SetName(
+        reinterpret_cast<const gpu::Mailbox*>(mailbox)->name);
+    return texture_id;
+  }
+
   // DrawingBuffer::Client implementation.
   bool DrawingBufferClientIsBoundForDraw() override {
     return !state_.draw_framebuffer_binding;
@@ -351,6 +370,10 @@
               saved_state_.pixel_pack_buffer_binding);
   }
 
+  gpu::Mailbox* last_imported_shared_image() {
+    return &last_imported_shared_image_;
+  }
+
  private:
   std::map<GLenum, GLuint> bound_textures_;
 
@@ -387,6 +410,7 @@
   HashMap<GLuint, IntSize> texture_sizes_;
   HashMap<GLuint, IntSize> image_sizes_;
   HashMap<GLuint, GLuint> image_to_texture_map_;
+  gpu::Mailbox last_imported_shared_image_;
 };
 
 class DrawingBufferForTests : public DrawingBuffer {
@@ -443,6 +467,11 @@
     return static_cast<GLES2InterfaceForTests*>(ContextGL());
   }
 
+  viz::TestSharedImageInterface* SharedImageInterfaceForTests() {
+    return static_cast<viz::TestSharedImageInterface*>(
+        ContextProvider()->SharedImageInterface());
+  }
+
   bool* live_;
 
   int RecycledBitmapCount() { return recycled_bitmaps_.size(); }
diff --git a/third_party/blink/renderer/platform/graphics/image_frame_generator.cc b/third_party/blink/renderer/platform/graphics/image_frame_generator.cc
index 5fb6687..501639c 100644
--- a/third_party/blink/renderer/platform/graphics/image_frame_generator.cc
+++ b/third_party/blink/renderer/platform/graphics/image_frame_generator.cc
@@ -158,7 +158,7 @@
   if (decode_failed_)
     return false;
 
-  TRACE_EVENT1("blink", "ImageFrameGenerator::decodeToYUV", "frame index",
+  TRACE_EVENT1("blink", "ImageFrameGenerator::DecodeToYUV", "frame index",
                static_cast<int>(index));
 
   if (!planes || !planes[0] || !planes[1] || !planes[2] || !row_bytes ||
@@ -181,7 +181,8 @@
   DCHECK(decoder->CanDecodeToYUV());
 
   if (decoder->DecodeToYUV()) {
-    SetHasAlpha(0, false);  // YUV is always opaque
+    // TODO(crbug.com/910276): Set this properly for alpha support.
+    SetHasAlpha(index, false);
     return true;
   }
 
diff --git a/third_party/blink/renderer/platform/graphics/test/DEPS b/third_party/blink/renderer/platform/graphics/test/DEPS
index 3a676d7..09a7050 100644
--- a/third_party/blink/renderer/platform/graphics/test/DEPS
+++ b/third_party/blink/renderer/platform/graphics/test/DEPS
@@ -1,5 +1,6 @@
 include_rules = [
     "+components/viz/test/test_gpu_memory_buffer_manager.h",
+    "+components/viz/test/test_context_provider.h",
     "+gpu/command_buffer/client/gles2_interface_stub.h",
-    "+gpu/config/gpu_feature_info.h"
+    "+gpu/config/gpu_feature_info.h",
 ]
diff --git a/third_party/blink/renderer/platform/graphics/test/fake_web_graphics_context_3d_provider.h b/third_party/blink/renderer/platform/graphics/test/fake_web_graphics_context_3d_provider.h
index 54e63456..d0d7906 100644
--- a/third_party/blink/renderer/platform/graphics/test/fake_web_graphics_context_3d_provider.h
+++ b/third_party/blink/renderer/platform/graphics/test/fake_web_graphics_context_3d_provider.h
@@ -7,6 +7,7 @@
 
 #include "cc/test/stub_decode_cache.h"
 #include "cc/tiles/image_decode_cache.h"
+#include "components/viz/test/test_context_provider.h"
 #include "gpu/command_buffer/client/gles2_interface.h"
 #include "gpu/command_buffer/common/capabilities.h"
 #include "gpu/config/gpu_feature_info.h"
@@ -58,10 +59,14 @@
       sk_sp<SkColorSpace> color_space) override {
     return image_decode_cache_;
   }
+  viz::TestSharedImageInterface* SharedImageInterface() override {
+    return &test_shared_image_interface_;
+  }
 
  private:
   cc::StubDecodeCache stub_image_decode_cache_;
 
+  viz::TestSharedImageInterface test_shared_image_interface_;
   gpu::gles2::GLES2Interface* gl_;
   sk_sp<GrContext> gr_context_;
   gpu::Capabilities capabilities_;
diff --git a/third_party/blink/renderer/platform/graphics/video_frame_submitter.cc b/third_party/blink/renderer/platform/graphics/video_frame_submitter.cc
index b49055e5..07cd4f3 100644
--- a/third_party/blink/renderer/platform/graphics/video_frame_submitter.cc
+++ b/third_party/blink/renderer/platform/graphics/video_frame_submitter.cc
@@ -174,8 +174,10 @@
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   TRACE_EVENT0("media", "VideoFrameSubmitter::OnBeginFrame");
 
+  // Don't call UpdateCurrentFrame() for MISSED BeginFrames. Also don't call it
+  // after StopRendering() has been called (forbidden by API contract).
   viz::BeginFrameAck current_begin_frame_ack(args, false);
-  if (args.type == viz::BeginFrameArgs::MISSED) {
+  if (args.type == viz::BeginFrameArgs::MISSED || !is_rendering_) {
     compositor_frame_sink_->DidNotProduceFrame(current_begin_frame_ack);
     return;
   }
@@ -199,10 +201,7 @@
   //
   // Not submitting a frame when waiting for a previous ack saves memory by
   // not building up unused remote side resources. See https://crbug.com/830828.
-  //
-  // TODO(dalecurtis): Can |is_rendering_| ever be false here? Presumably if
-  // StopRendering() is called above we will not have gotten a BeginFrame.
-  if (!is_rendering_ || waiting_for_compositor_ack_ ||
+  if (waiting_for_compositor_ack_ ||
       !SubmitFrame(current_begin_frame_ack, std::move(video_frame))) {
     compositor_frame_sink_->DidNotProduceFrame(current_begin_frame_ack);
     return;
diff --git a/third_party/blink/renderer/platform/graphics/video_frame_submitter_test.cc b/third_party/blink/renderer/platform/graphics/video_frame_submitter_test.cc
index 3b70570d..3f7e0dc 100644
--- a/third_party/blink/renderer/platform/graphics/video_frame_submitter_test.cc
+++ b/third_party/blink/renderer/platform/graphics/video_frame_submitter_test.cc
@@ -233,6 +233,56 @@
   EXPECT_TRUE(IsRendering());
 }
 
+TEST_F(VideoFrameSubmitterTest, StopRenderingSkipsUpdateCurrentFrame) {
+  EXPECT_FALSE(IsRendering());
+  EXPECT_CALL(*sink_, SetNeedsBeginFrame(true));
+
+  submitter_->StartRendering();
+
+  scoped_task_environment_.RunUntilIdle();
+
+  EXPECT_TRUE(IsRendering());
+
+  // OnBeginFrame() submits one frame.
+  EXPECT_CALL(*video_frame_provider_, GetCurrentFrame())
+      .WillOnce(Return(media::VideoFrame::CreateFrame(
+          media::PIXEL_FORMAT_YV12, gfx::Size(8, 8), gfx::Rect(gfx::Size(8, 8)),
+          gfx::Size(8, 8), base::TimeDelta())));
+  EXPECT_CALL(*video_frame_provider_, UpdateCurrentFrame(_, _))
+      .WillOnce(Return(true));
+  EXPECT_CALL(*sink_, DoSubmitCompositorFrame(_, _));
+  EXPECT_CALL(*video_frame_provider_, PutCurrentFrame());
+  EXPECT_CALL(*resource_provider_, AppendQuads(_, _, _, _));
+  EXPECT_CALL(*resource_provider_, PrepareSendToParent(_, _));
+  EXPECT_CALL(*resource_provider_, ReleaseFrameResources());
+
+  viz::BeginFrameArgs args = begin_frame_source_->CreateBeginFrameArgs(
+      BEGINFRAME_FROM_HERE, now_src_.get());
+  submitter_->OnBeginFrame(args, {});
+  scoped_task_environment_.RunUntilIdle();
+
+  // StopRendering submits one more frame.
+  EXPECT_CALL(*video_frame_provider_, GetCurrentFrame())
+      .WillOnce(Return(media::VideoFrame::CreateFrame(
+          media::PIXEL_FORMAT_YV12, gfx::Size(8, 8), gfx::Rect(gfx::Size(8, 8)),
+          gfx::Size(8, 8), base::TimeDelta())));
+  EXPECT_CALL(*sink_, DoSubmitCompositorFrame(_, _));
+  EXPECT_CALL(*video_frame_provider_, PutCurrentFrame());
+  EXPECT_CALL(*sink_, SetNeedsBeginFrame(false));
+  EXPECT_CALL(*resource_provider_, AppendQuads(_, _, _, _));
+  EXPECT_CALL(*resource_provider_, PrepareSendToParent(_, _));
+  EXPECT_CALL(*resource_provider_, ReleaseFrameResources());
+  submitter_->StopRendering();
+  scoped_task_environment_.RunUntilIdle();
+
+  // No frames should be produced after StopRendering().
+  EXPECT_CALL(*sink_, DidNotProduceFrame(_));
+  begin_frame_source_->CreateBeginFrameArgs(BEGINFRAME_FROM_HERE,
+                                            now_src_.get());
+  submitter_->OnBeginFrame(args, {});
+  scoped_task_environment_.RunUntilIdle();
+}
+
 TEST_F(VideoFrameSubmitterTest, StopUsingProviderNullsProvider) {
   EXPECT_FALSE(IsRendering());
   EXPECT_EQ(video_frame_provider_.get(), GetProvider());
diff --git a/third_party/blink/renderer/platform/heap/heap_allocator.h b/third_party/blink/renderer/platform/heap/heap_allocator.h
index 01d96002..64c50638 100644
--- a/third_party/blink/renderer/platform/heap/heap_allocator.h
+++ b/third_party/blink/renderer/platform/heap/heap_allocator.h
@@ -9,6 +9,7 @@
 #include "third_party/blink/renderer/platform/bindings/script_wrappable_marking_visitor.h"
 #include "third_party/blink/renderer/platform/heap/heap.h"
 #include "third_party/blink/renderer/platform/heap/heap_buildflags.h"
+#include "third_party/blink/renderer/platform/heap/heap_compact.h"
 #include "third_party/blink/renderer/platform/heap/marking_visitor.h"
 #include "third_party/blink/renderer/platform/heap/trace_traits.h"
 #include "third_party/blink/renderer/platform/wtf/allocator.h"
@@ -406,6 +407,11 @@
       !std::is_trivially_destructible<T>::value,
       "Finalization of trivially destructible classes should not happen.");
   HeapObjectHeader* header = HeapObjectHeader::FromPayload(pointer);
+
+  // TODO(keishi): Speculative check for crbug.com/918064
+  CHECK(!ThreadState::Current()->Heap().Compaction()->RangeHasInteriors(
+      header->Payload(), header->PayloadSize()));
+
   // Use the payload size as recorded by the heap to determine how many
   // elements to finalize.
   size_t length = header->PayloadSize() / sizeof(T);
@@ -435,6 +441,9 @@
       !std::is_trivially_destructible<Value>::value,
       "Finalization of trivially destructible classes should not happen.");
   HeapObjectHeader* header = HeapObjectHeader::FromPayload(pointer);
+  // TODO(keishi): Speculative check for crbug.com/918064
+  CHECK(!ThreadState::Current()->Heap().Compaction()->RangeHasInteriors(
+      header->Payload(), header->PayloadSize()));
   // Use the payload size as recorded by the heap to determine how many
   // elements to finalize.
   size_t length = header->PayloadSize() / sizeof(Value);
diff --git a/third_party/blink/renderer/platform/heap/heap_compact.cc b/third_party/blink/renderer/platform/heap/heap_compact.cc
index 055f2dc..311daac 100644
--- a/third_party/blink/renderer/platform/heap/heap_compact.cc
+++ b/third_party/blink/renderer/platform/heap/heap_compact.cc
@@ -130,6 +130,25 @@
       fixup_callbacks_.erase(it);
   }
 
+  bool RangeHasInteriors(Address address, size_t size) {
+    if (!interiors_)
+      return false;
+
+    SparseHeapBitmap* range = interiors_->HasRange(address, size);
+    if (LIKELY(!range))
+      return false;
+
+    for (size_t offset = 0; offset < size; offset += sizeof(void*)) {
+      MovableReference* slot =
+          reinterpret_cast<MovableReference*>(address + offset);
+      if (range->IsSet(reinterpret_cast<Address>(slot)))
+        return true;
+    }
+
+    NOTREACHED();
+    return false;
+  }
+
   void RelocateInteriorFixups(Address from, Address to, size_t size) {
     SparseHeapBitmap* range = interiors_->HasRange(from, size);
     if (LIKELY(!range))
@@ -494,6 +513,10 @@
   Fixups().Relocate(from, to);
 }
 
+bool HeapCompact::RangeHasInteriors(Address address, size_t size) {
+  return fixups_ && fixups_->RangeHasInteriors(address, size);
+}
+
 void HeapCompact::StartThreadCompaction() {
   if (!do_compact_)
     return;
diff --git a/third_party/blink/renderer/platform/heap/heap_compact.h b/third_party/blink/renderer/platform/heap/heap_compact.h
index ec0ef44..a504be64 100644
--- a/third_party/blink/renderer/platform/heap/heap_compact.h
+++ b/third_party/blink/renderer/platform/heap/heap_compact.h
@@ -123,6 +123,9 @@
   // (Called by the sweep compaction pass.)
   void Relocate(Address from, Address to);
 
+  // Returns true if range has registered interiors.
+  bool RangeHasInteriors(Address, size_t);
+
   // For unit testing only: arrange for a compaction GC to be triggered
   // next time a non-conservative GC is run. Sets the compact-next flag
   // to the new value, returning old.
diff --git a/third_party/blink/renderer/platform/loader/BUILD.gn b/third_party/blink/renderer/platform/loader/BUILD.gn
index edc8699..7510344 100644
--- a/third_party/blink/renderer/platform/loader/BUILD.gn
+++ b/third_party/blink/renderer/platform/loader/BUILD.gn
@@ -83,6 +83,9 @@
     "fetch/resource_status.h",
     "fetch/resource_timing_info.cc",
     "fetch/resource_timing_info.h",
+    "fetch/response_body_loader.cc",
+    "fetch/response_body_loader.h",
+    "fetch/response_body_loader_client.h",
     "fetch/script_cached_metadata_handler.cc",
     "fetch/script_cached_metadata_handler.h",
     "fetch/script_fetch_options.cc",
@@ -140,6 +143,7 @@
     "fetch/resource_request_test.cc",
     "fetch/resource_response_test.cc",
     "fetch/resource_test.cc",
+    "fetch/response_body_loader_test.cc",
     "fetch/source_keyed_cached_metadata_handler_test.cc",
     "ftp_directory_listing_test.cc",
     "link_header_test.cc",
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_loader.cc b/third_party/blink/renderer/platform/loader/fetch/resource_loader.cc
index fe5f7740..59082c14 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_loader.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_loader.cc
@@ -335,6 +335,12 @@
       !RuntimeEnabledFeatures::WasmCodeCacheEnabled())
     return false;
 
+  // TODO(crbug.com/867347): Enable fetching of code caches on non-main threads
+  // once code cache has its own mojo interface. Currently it is using
+  // RenderMessageFilter that is not available on non-main threads.
+  if (!IsMainThread())
+    return false;
+
   const ResourceRequest& request = resource_->GetResourceRequest();
   if (!request.Url().ProtocolIsInHTTPFamily())
     return false;
@@ -1095,6 +1101,10 @@
     return;
   DCHECK_GE(response_out.ToResourceResponse().EncodedBodyLength(), 0);
 
+  // TODO(crbug.com/867347): Enable fetching of code caches synchronously
+  // once code cache has its own mojo interface. Currently it is using
+  // RenderMessageFilter that is not available on non-main threads.
+
   // Follow the async case convention of not calling DidReceiveData or
   // appending data to m_resource if the response body is empty. Copying the
   // empty buffer is a noop in most cases, but is destructive in the case of
diff --git a/third_party/blink/renderer/platform/loader/fetch/response_body_loader.cc b/third_party/blink/renderer/platform/loader/fetch/response_body_loader.cc
new file mode 100644
index 0000000..3133fb37
--- /dev/null
+++ b/third_party/blink/renderer/platform/loader/fetch/response_body_loader.cc
@@ -0,0 +1,187 @@
+// 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 "third_party/blink/renderer/platform/loader/fetch/response_body_loader.h"
+
+#include <algorithm>
+#include <utility>
+#include "third_party/blink/renderer/platform/loader/fetch/fetch_context.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_loader.h"
+#include "third_party/blink/renderer/platform/shared_buffer.h"
+
+namespace blink {
+
+constexpr size_t ResponseBodyLoader::kMaxNumConsumedBytesInTask;
+
+ResponseBodyLoader::ResponseBodyLoader(
+    BytesConsumer& bytes_consumer,
+    ResponseBodyLoaderClient& client,
+    scoped_refptr<base::SingleThreadTaskRunner> task_runner)
+    : bytes_consumer_(bytes_consumer),
+      client_(client),
+      task_runner_(std::move(task_runner)) {
+  bytes_consumer_->SetClient(this);
+}
+
+mojo::ScopedDataPipeConsumerHandle ResponseBodyLoader::DrainAsDataPipe(
+    ResponseBodyLoaderClient** client) {
+  DCHECK(!started_);
+
+  *client = nullptr;
+  if (drained_ || aborted_) {
+    return {};
+  }
+
+  DCHECK(bytes_consumer_);
+  auto data_pipe = bytes_consumer_->DrainAsDataPipe();
+
+  if (data_pipe) {
+    drained_ = true;
+    bytes_consumer_ = nullptr;
+    *client = this;
+  }
+
+  return data_pipe;
+}
+
+void ResponseBodyLoader::DidReceiveData(base::span<const char> data) {
+  if (aborted_)
+    return;
+
+  client_->DidReceiveData(data);
+}
+
+void ResponseBodyLoader::DidFinishLoadingBody() {
+  if (aborted_)
+    return;
+
+  if (suspended_) {
+    finish_signal_is_pending_ = true;
+    return;
+  }
+
+  finish_signal_is_pending_ = false;
+  client_->DidFinishLoadingBody();
+}
+
+void ResponseBodyLoader::DidFailLoadingBody() {
+  if (aborted_)
+    return;
+
+  if (suspended_) {
+    fail_signal_is_pending_ = true;
+    return;
+  }
+
+  fail_signal_is_pending_ = false;
+  client_->DidFailLoadingBody();
+}
+
+void ResponseBodyLoader::Start() {
+  DCHECK(!started_);
+  DCHECK(!drained_);
+
+  started_ = true;
+  OnStateChange();
+}
+
+void ResponseBodyLoader::Abort() {
+  if (aborted_)
+    return;
+
+  aborted_ = true;
+
+  if (bytes_consumer_ && !in_two_phase_read_)
+    bytes_consumer_->Cancel();
+}
+
+void ResponseBodyLoader::Suspend() {
+  if (aborted_)
+    return;
+
+  DCHECK(!suspended_);
+  suspended_ = true;
+}
+
+void ResponseBodyLoader::Resume() {
+  if (aborted_)
+    return;
+
+  DCHECK(suspended_);
+  suspended_ = false;
+
+  if (finish_signal_is_pending_) {
+    task_runner_->PostTask(
+        FROM_HERE, base::BindOnce(&ResponseBodyLoader::DidFinishLoadingBody,
+                                  WrapPersistent(this)));
+  } else if (fail_signal_is_pending_) {
+    task_runner_->PostTask(
+        FROM_HERE, base::BindOnce(&ResponseBodyLoader::DidFailLoadingBody,
+                                  WrapPersistent(this)));
+  } else {
+    task_runner_->PostTask(FROM_HERE,
+                           base::BindOnce(&ResponseBodyLoader::OnStateChange,
+                                          WrapPersistent(this)));
+  }
+}
+
+void ResponseBodyLoader::OnStateChange() {
+  if (!started_)
+    return;
+
+  size_t num_bytes_consumed = 0;
+
+  while (!aborted_ && !suspended_) {
+    if (kMaxNumConsumedBytesInTask == num_bytes_consumed) {
+      // We've already consumed many bytes in this task. Defer the remaining
+      // to the next task.
+      task_runner_->PostTask(FROM_HERE,
+                             base::BindOnce(&ResponseBodyLoader::OnStateChange,
+                                            WrapPersistent(this)));
+      return;
+    }
+
+    const char* buffer = nullptr;
+    size_t available = 0;
+    auto result = bytes_consumer_->BeginRead(&buffer, &available);
+    if (result == BytesConsumer::Result::kShouldWait)
+      return;
+    if (result == BytesConsumer::Result::kOk) {
+      in_two_phase_read_ = true;
+
+      available =
+          std::min(available, kMaxNumConsumedBytesInTask - num_bytes_consumed);
+      DidReceiveData(base::make_span(buffer, available));
+      result = bytes_consumer_->EndRead(available);
+      in_two_phase_read_ = false;
+      num_bytes_consumed += available;
+
+      if (aborted_) {
+        // As we cannot call Cancel in two-phase read, we need to call it here.
+        bytes_consumer_->Cancel();
+      }
+    }
+    DCHECK_NE(result, BytesConsumer::Result::kShouldWait);
+    if (result == BytesConsumer::Result::kDone) {
+      DidFinishLoadingBody();
+      return;
+    }
+    if (result != BytesConsumer::Result::kOk) {
+      DidFailLoadingBody();
+      Abort();
+      return;
+    }
+  }
+}
+
+void ResponseBodyLoader::Trace(Visitor* visitor) {
+  visitor->Trace(bytes_consumer_);
+  visitor->Trace(client_);
+  ResponseBodyLoaderDrainableInterface::Trace(visitor);
+  ResponseBodyLoaderClient::Trace(visitor);
+  BytesConsumer::Client::Trace(visitor);
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/loader/fetch/response_body_loader.h b/third_party/blink/renderer/platform/loader/fetch/response_body_loader.h
new file mode 100644
index 0000000..08a2fba
--- /dev/null
+++ b/third_party/blink/renderer/platform/loader/fetch/response_body_loader.h
@@ -0,0 +1,120 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RESPONSE_BODY_LOADER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RESPONSE_BODY_LOADER_H_
+
+#include "base/containers/span.h"
+#include "base/memory/scoped_refptr.h"
+#include "mojo/public/cpp/system/data_pipe.h"
+#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
+#include "third_party/blink/renderer/platform/heap/member.h"
+#include "third_party/blink/renderer/platform/loader/fetch/bytes_consumer.h"
+#include "third_party/blink/renderer/platform/loader/fetch/response_body_loader_client.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace base {
+class SingleThreadTaskRunner;
+}  // namespace base
+
+namespace blink {
+
+class ResponseBodyLoader;
+
+// See ResponseBodyLoader for details. This is a virtual interface to expose
+// only DrainAsDataPipe function.
+class PLATFORM_EXPORT ResponseBodyLoaderDrainableInterface
+    : public GarbageCollectedFinalized<ResponseBodyLoaderDrainableInterface> {
+ public:
+  virtual ~ResponseBodyLoaderDrainableInterface() = default;
+
+  // Drains the response body and returns it. This function must be called
+  // before calling Start(). This function may return an invalid handle when
+  // it is unable to convert the body to a data pipe, even when the body itself
+  // is valid. In that case, this function is no-op.
+  // If this function returns a valid handle, the caller is responsible for
+  // reading the body and providing the information to the client this
+  // function provides.
+  virtual mojo::ScopedDataPipeConsumerHandle DrainAsDataPipe(
+      ResponseBodyLoaderClient** client) = 0;
+
+  virtual void Trace(Visitor*) {}
+};
+
+// ResponseBodyLoader reads the response body and reports the contents to the
+// associated client. There are two ways:
+//  - By calling Start(), ResponseBodyLoader reads the response body. and
+//    reports the contents to the client. Abort() aborts reading.
+//  - By calling DrainAsDataPipe, a user can "drain" the contents from
+//    ResponseBodyLoader. The caller is responsible for reading the body and
+//    providing the information to the client this function provides.
+// A ResponseBodyLoader is bound to the thread on which it is created.
+class PLATFORM_EXPORT ResponseBodyLoader final
+    : public ResponseBodyLoaderDrainableInterface,
+      private ResponseBodyLoaderClient,
+      private BytesConsumer::Client {
+  USING_GARBAGE_COLLECTED_MIXIN(ResponseBodyLoader);
+
+ public:
+  ResponseBodyLoader(BytesConsumer&,
+                     ResponseBodyLoaderClient&,
+                     scoped_refptr<base::SingleThreadTaskRunner>);
+
+  // ResponseBodyLoaderDrainableInterface implementation.
+  mojo::ScopedDataPipeConsumerHandle DrainAsDataPipe(
+      ResponseBodyLoaderClient**) override;
+
+  // Starts loading.
+  void Start();
+
+  // Aborts loading. This is expected to be called from the client's side, and
+  // does not report the failure to the client. This doesn't affect a
+  // drained data pipe.
+  void Abort();
+
+  // Suspendes loading.
+  void Suspend();
+
+  // Resumes loading.
+  void Resume();
+
+  bool IsAborted() const { return aborted_; }
+  bool IsSuspended() const { return suspended_; }
+  bool IsDrained() const { return drained_; }
+
+  void Trace(Visitor*) override;
+
+  // The maximal number of bytes consumed in a task. When there are more bytes
+  // in the data pipe, they will be consumed in following tasks. Setting a too
+  // small number will generate ton of tasks but setting a too large number will
+  // lead to thread janks. Also, some clients cannot handle too large chunks
+  // (512k for example).
+  static constexpr size_t kMaxNumConsumedBytesInTask = 64 * 1024;
+
+ private:
+  // ResponseBodyLoaderClient implementation.
+  void DidReceiveData(base::span<const char> data) override;
+  void DidFinishLoadingBody() override;
+  void DidFailLoadingBody() override;
+
+  // BytesConsumer::Client implementation.
+  void OnStateChange() override;
+  String DebugName() const override { return "ResponseBodyLoader"; }
+
+  Member<BytesConsumer> bytes_consumer_;
+  const Member<ResponseBodyLoaderClient> client_;
+  const scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+  bool started_ = false;
+  bool aborted_ = false;
+  bool suspended_ = false;
+  bool drained_ = false;
+  bool finish_signal_is_pending_ = false;
+  bool fail_signal_is_pending_ = false;
+  bool in_two_phase_read_ = false;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RESPONSE_BODY_LOADER_H_
diff --git a/third_party/blink/renderer/platform/loader/fetch/response_body_loader_client.h b/third_party/blink/renderer/platform/loader/fetch/response_body_loader_client.h
new file mode 100644
index 0000000..0710ea62
--- /dev/null
+++ b/third_party/blink/renderer/platform/loader/fetch/response_body_loader_client.h
@@ -0,0 +1,29 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RESPONSE_BODY_LOADER_CLIENT_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RESPONSE_BODY_LOADER_CLIENT_H_
+
+namespace blink {
+
+// A ResponseBodyLoaderClient receives signals for loading a response body.
+class ResponseBodyLoaderClient : public GarbageCollectedMixin {
+ public:
+  ~ResponseBodyLoaderClient() = default;
+
+  // Called when reading a chunk, with the chunk.
+  virtual void DidReceiveData(base::span<const char> data) = 0;
+
+  // Called when finishing reading the entire body. This must be the last
+  // signal.
+  virtual void DidFinishLoadingBody() = 0;
+
+  // Called when seeing an error while reading the body. This must be the last
+  // signal.
+  virtual void DidFailLoadingBody() = 0;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RESPONSE_BODY_LOADER_CLIENT_H_
diff --git a/third_party/blink/renderer/platform/loader/fetch/response_body_loader_test.cc b/third_party/blink/renderer/platform/loader/fetch/response_body_loader_test.cc
new file mode 100644
index 0000000..5a974f4
--- /dev/null
+++ b/third_party/blink/renderer/platform/loader/fetch/response_body_loader_test.cc
@@ -0,0 +1,412 @@
+// 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 "third_party/blink/renderer/platform/loader/fetch/response_body_loader.h"
+
+#include <memory>
+#include <string>
+#include <utility>
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/heap/persistent.h"
+#include "third_party/blink/renderer/platform/loader/fetch/data_pipe_bytes_consumer.h"
+#include "third_party/blink/renderer/platform/loader/testing/replaying_bytes_consumer.h"
+#include "third_party/blink/renderer/platform/scheduler/test/fake_task_runner.h"
+
+namespace blink {
+
+namespace {
+
+class ResponseBodyLoaderTest : public testing::Test {
+ protected:
+  using Command = ReplayingBytesConsumer::Command;
+  class TestClient final : public GarbageCollectedFinalized<TestClient>,
+                           public ResponseBodyLoaderClient {
+    USING_GARBAGE_COLLECTED_MIXIN(TestClient);
+
+   public:
+    enum class Option {
+      kNone,
+      kAbortOnDidReceiveData,
+      kSuspendOnDidReceiveData,
+    };
+
+    TestClient() : TestClient(Option::kNone) {}
+    TestClient(Option option) : option_(option) {}
+    virtual ~TestClient() {}
+
+    String GetData() const { return data_; }
+    bool LoadingIsFinished() { return finished_; }
+    bool LoadingIsFailed() { return failed_; }
+
+    void DidReceiveData(base::span<const char> data) override {
+      DCHECK(!finished_);
+      DCHECK(!failed_);
+      data_.append(String(data.data(), data.size()));
+      switch (option_) {
+        case Option::kNone:
+          break;
+        case Option::kAbortOnDidReceiveData:
+          loader_->Abort();
+          break;
+        case Option::kSuspendOnDidReceiveData:
+          loader_->Suspend();
+          break;
+      }
+    }
+    void DidFinishLoadingBody() override {
+      DCHECK(!finished_);
+      DCHECK(!failed_);
+      finished_ = true;
+    }
+    void DidFailLoadingBody() override {
+      DCHECK(!finished_);
+      DCHECK(!failed_);
+      failed_ = true;
+    }
+
+    void SetLoader(ResponseBodyLoader& loader) { loader_ = loader; }
+    void Trace(Visitor* visitor) override { visitor->Trace(loader_); }
+
+   private:
+    const Option option_;
+    Member<ResponseBodyLoader> loader_;
+    String data_;
+    bool finished_ = false;
+    bool failed_ = false;
+  };
+};
+
+TEST_F(ResponseBodyLoaderTest, Load) {
+  auto task_runner = base::MakeRefCounted<scheduler::FakeTaskRunner>();
+  auto* consumer = MakeGarbageCollected<ReplayingBytesConsumer>(task_runner);
+  consumer->Add(Command(Command::kData, "he"));
+  consumer->Add(Command(Command::kWait));
+  consumer->Add(Command(Command::kData, "llo"));
+  consumer->Add(Command(Command::kDone));
+
+  auto* client = MakeGarbageCollected<TestClient>();
+  auto* body_loader =
+      MakeGarbageCollected<ResponseBodyLoader>(*consumer, *client, task_runner);
+
+  EXPECT_FALSE(client->LoadingIsFinished());
+  EXPECT_FALSE(client->LoadingIsFailed());
+  EXPECT_EQ(String(), client->GetData());
+
+  body_loader->Start();
+
+  EXPECT_FALSE(client->LoadingIsFinished());
+  EXPECT_FALSE(client->LoadingIsFailed());
+  EXPECT_EQ("he", client->GetData());
+
+  task_runner->RunUntilIdle();
+
+  EXPECT_TRUE(client->LoadingIsFinished());
+  EXPECT_FALSE(client->LoadingIsFailed());
+  EXPECT_EQ("hello", client->GetData());
+}
+
+TEST_F(ResponseBodyLoaderTest, LoadFailure) {
+  auto task_runner = base::MakeRefCounted<scheduler::FakeTaskRunner>();
+  auto* consumer = MakeGarbageCollected<ReplayingBytesConsumer>(task_runner);
+  consumer->Add(Command(Command::kData, "he"));
+  consumer->Add(Command(Command::kWait));
+  consumer->Add(Command(Command::kData, "llo"));
+  consumer->Add(Command(Command::kError));
+
+  auto* client = MakeGarbageCollected<TestClient>();
+  auto* body_loader =
+      MakeGarbageCollected<ResponseBodyLoader>(*consumer, *client, task_runner);
+
+  EXPECT_FALSE(client->LoadingIsFinished());
+  EXPECT_FALSE(client->LoadingIsFailed());
+  EXPECT_EQ(String(), client->GetData());
+
+  body_loader->Start();
+
+  EXPECT_FALSE(client->LoadingIsFinished());
+  EXPECT_FALSE(client->LoadingIsFailed());
+  EXPECT_EQ("he", client->GetData());
+
+  task_runner->RunUntilIdle();
+
+  EXPECT_FALSE(client->LoadingIsFinished());
+  EXPECT_TRUE(client->LoadingIsFailed());
+  EXPECT_EQ("hello", client->GetData());
+}
+
+TEST_F(ResponseBodyLoaderTest, LoadWithDataAndDone) {
+  auto task_runner = base::MakeRefCounted<scheduler::FakeTaskRunner>();
+  auto* consumer = MakeGarbageCollected<ReplayingBytesConsumer>(task_runner);
+  consumer->Add(Command(Command::kData, "he"));
+  consumer->Add(Command(Command::kWait));
+  consumer->Add(Command(Command::kDataAndDone, "llo"));
+
+  auto* client = MakeGarbageCollected<TestClient>();
+  auto* body_loader =
+      MakeGarbageCollected<ResponseBodyLoader>(*consumer, *client, task_runner);
+
+  EXPECT_FALSE(client->LoadingIsFinished());
+  EXPECT_FALSE(client->LoadingIsFailed());
+  EXPECT_EQ(String(), client->GetData());
+
+  body_loader->Start();
+
+  EXPECT_FALSE(client->LoadingIsFinished());
+  EXPECT_FALSE(client->LoadingIsFailed());
+  EXPECT_EQ("he", client->GetData());
+
+  task_runner->RunUntilIdle();
+
+  EXPECT_TRUE(client->LoadingIsFinished());
+  EXPECT_FALSE(client->LoadingIsFailed());
+  EXPECT_EQ("hello", client->GetData());
+}
+
+TEST_F(ResponseBodyLoaderTest, Abort) {
+  auto task_runner = base::MakeRefCounted<scheduler::FakeTaskRunner>();
+  auto* consumer = MakeGarbageCollected<ReplayingBytesConsumer>(task_runner);
+  consumer->Add(Command(Command::kData, "he"));
+  consumer->Add(Command(Command::kWait));
+  consumer->Add(Command(Command::kData, "llo"));
+  consumer->Add(Command(Command::kDone));
+
+  auto* client = MakeGarbageCollected<TestClient>(
+      TestClient::Option::kAbortOnDidReceiveData);
+  auto* body_loader =
+      MakeGarbageCollected<ResponseBodyLoader>(*consumer, *client, task_runner);
+  client->SetLoader(*body_loader);
+
+  EXPECT_FALSE(client->LoadingIsFinished());
+  EXPECT_FALSE(client->LoadingIsFailed());
+  EXPECT_EQ(String(), client->GetData());
+  EXPECT_FALSE(body_loader->IsAborted());
+
+  body_loader->Start();
+
+  EXPECT_FALSE(client->LoadingIsFinished());
+  EXPECT_FALSE(client->LoadingIsFailed());
+  EXPECT_EQ("he", client->GetData());
+  EXPECT_TRUE(body_loader->IsAborted());
+
+  task_runner->RunUntilIdle();
+
+  EXPECT_FALSE(client->LoadingIsFinished());
+  EXPECT_FALSE(client->LoadingIsFailed());
+  EXPECT_EQ("he", client->GetData());
+  EXPECT_TRUE(body_loader->IsAborted());
+}
+
+TEST_F(ResponseBodyLoaderTest, Suspend) {
+  auto task_runner = base::MakeRefCounted<scheduler::FakeTaskRunner>();
+  auto* consumer = MakeGarbageCollected<ReplayingBytesConsumer>(task_runner);
+  consumer->Add(Command(Command::kData, "h"));
+  consumer->Add(Command(Command::kDataAndDone, "ello"));
+
+  auto* client = MakeGarbageCollected<TestClient>(
+      TestClient::Option::kSuspendOnDidReceiveData);
+  auto* body_loader =
+      MakeGarbageCollected<ResponseBodyLoader>(*consumer, *client, task_runner);
+  client->SetLoader(*body_loader);
+
+  EXPECT_FALSE(client->LoadingIsFinished());
+  EXPECT_FALSE(client->LoadingIsFailed());
+  EXPECT_EQ(String(), client->GetData());
+  EXPECT_FALSE(body_loader->IsSuspended());
+
+  body_loader->Start();
+
+  EXPECT_FALSE(client->LoadingIsFinished());
+  EXPECT_FALSE(client->LoadingIsFailed());
+  EXPECT_EQ("h", client->GetData());
+  EXPECT_TRUE(body_loader->IsSuspended());
+
+  task_runner->RunUntilIdle();
+
+  EXPECT_FALSE(client->LoadingIsFinished());
+  EXPECT_FALSE(client->LoadingIsFailed());
+  EXPECT_EQ("h", client->GetData());
+  EXPECT_TRUE(body_loader->IsSuspended());
+
+  body_loader->Resume();
+
+  EXPECT_FALSE(client->LoadingIsFinished());
+  EXPECT_FALSE(client->LoadingIsFailed());
+  EXPECT_EQ("h", client->GetData());
+  EXPECT_FALSE(body_loader->IsSuspended());
+
+  task_runner->RunUntilIdle();
+
+  EXPECT_FALSE(client->LoadingIsFinished());
+  EXPECT_FALSE(client->LoadingIsFailed());
+  EXPECT_EQ("hello", client->GetData());
+  EXPECT_TRUE(body_loader->IsSuspended());
+
+  body_loader->Resume();
+
+  EXPECT_FALSE(client->LoadingIsFinished());
+  EXPECT_FALSE(client->LoadingIsFailed());
+  EXPECT_EQ("hello", client->GetData());
+  EXPECT_FALSE(body_loader->IsSuspended());
+
+  task_runner->RunUntilIdle();
+
+  EXPECT_TRUE(client->LoadingIsFinished());
+  EXPECT_FALSE(client->LoadingIsFailed());
+  EXPECT_EQ("hello", client->GetData());
+  EXPECT_FALSE(body_loader->IsSuspended());
+}
+
+TEST_F(ResponseBodyLoaderTest, ReadTooBigBuffer) {
+  auto task_runner = base::MakeRefCounted<scheduler::FakeTaskRunner>();
+  auto* consumer = MakeGarbageCollected<ReplayingBytesConsumer>(task_runner);
+  constexpr auto kMax = ResponseBodyLoader::kMaxNumConsumedBytesInTask;
+
+  consumer->Add(Command(Command::kData, std::string(kMax - 1, 'a').data()));
+  consumer->Add(Command(Command::kData, std::string(2, 'b').data()));
+  consumer->Add(Command(Command::kWait));
+  consumer->Add(Command(Command::kData, std::string(kMax, 'c').data()));
+  consumer->Add(Command(Command::kData, std::string(kMax + 3, 'd').data()));
+  consumer->Add(Command(Command::kDone));
+
+  auto* client = MakeGarbageCollected<TestClient>();
+  auto* body_loader =
+      MakeGarbageCollected<ResponseBodyLoader>(*consumer, *client, task_runner);
+
+  EXPECT_FALSE(client->LoadingIsFinished());
+  EXPECT_FALSE(client->LoadingIsFailed());
+  EXPECT_EQ(String(), client->GetData());
+
+  body_loader->Start();
+
+  EXPECT_FALSE(client->LoadingIsFinished());
+  EXPECT_FALSE(client->LoadingIsFailed());
+  EXPECT_EQ((std::string(kMax - 1, 'a') + 'b').data(), client->GetData());
+
+  task_runner->RunUntilIdle();
+
+  EXPECT_TRUE(client->LoadingIsFinished());
+  EXPECT_FALSE(client->LoadingIsFailed());
+  EXPECT_EQ((std::string(kMax - 1, 'a') + "bb" + std::string(kMax, 'c') +
+             std::string(kMax + 3, 'd'))
+                .data(),
+            client->GetData());
+}
+
+TEST_F(ResponseBodyLoaderTest, NotDrainable) {
+  auto task_runner = base::MakeRefCounted<scheduler::FakeTaskRunner>();
+  auto* consumer = MakeGarbageCollected<ReplayingBytesConsumer>(task_runner);
+  consumer->Add(Command(Command::kData, "he"));
+  consumer->Add(Command(Command::kWait));
+  consumer->Add(Command(Command::kData, "llo"));
+  consumer->Add(Command(Command::kDone));
+
+  auto* client = MakeGarbageCollected<TestClient>();
+  auto* body_loader =
+      MakeGarbageCollected<ResponseBodyLoader>(*consumer, *client, task_runner);
+
+  ResponseBodyLoaderClient* intermediate_client = nullptr;
+  auto data_pipe = body_loader->DrainAsDataPipe(&intermediate_client);
+
+  ASSERT_FALSE(data_pipe);
+  EXPECT_FALSE(intermediate_client);
+  EXPECT_FALSE(body_loader->IsDrained());
+
+  // We can start loading.
+
+  EXPECT_FALSE(client->LoadingIsFinished());
+  EXPECT_FALSE(client->LoadingIsFailed());
+  EXPECT_EQ(String(), client->GetData());
+
+  body_loader->Start();
+
+  EXPECT_FALSE(client->LoadingIsFinished());
+  EXPECT_FALSE(client->LoadingIsFailed());
+  EXPECT_EQ("he", client->GetData());
+
+  task_runner->RunUntilIdle();
+
+  EXPECT_TRUE(client->LoadingIsFinished());
+  EXPECT_FALSE(client->LoadingIsFailed());
+  EXPECT_EQ("hello", client->GetData());
+}
+
+TEST_F(ResponseBodyLoaderTest, DrainAsDataPipe) {
+  mojo::ScopedDataPipeConsumerHandle consumer_end;
+  mojo::ScopedDataPipeProducerHandle producer_end;
+  auto result = mojo::CreateDataPipe(nullptr, &producer_end, &consumer_end);
+
+  ASSERT_EQ(result, MOJO_RESULT_OK);
+
+  DataPipeBytesConsumer::CompletionNotifier* completion_notifier = nullptr;
+
+  auto task_runner = base::MakeRefCounted<scheduler::FakeTaskRunner>();
+  auto* consumer = MakeGarbageCollected<DataPipeBytesConsumer>(
+      task_runner, std::move(consumer_end), &completion_notifier);
+  auto* client = MakeGarbageCollected<TestClient>();
+
+  auto* body_loader =
+      MakeGarbageCollected<ResponseBodyLoader>(*consumer, *client, task_runner);
+
+  ResponseBodyLoaderClient* client_for_draining = nullptr;
+  auto data_pipe = body_loader->DrainAsDataPipe(&client_for_draining);
+
+  ASSERT_TRUE(data_pipe);
+  ASSERT_TRUE(client);
+  EXPECT_TRUE(body_loader->IsDrained());
+
+  client_for_draining->DidReceiveData(base::make_span("xyz", 3));
+  client_for_draining->DidReceiveData(base::make_span("abc", 3));
+
+  EXPECT_FALSE(client->LoadingIsFinished());
+  EXPECT_FALSE(client->LoadingIsFailed());
+  EXPECT_EQ("xyzabc", client->GetData());
+
+  client_for_draining->DidFinishLoadingBody();
+
+  EXPECT_TRUE(client->LoadingIsFinished());
+  EXPECT_FALSE(client->LoadingIsFailed());
+  EXPECT_EQ("xyzabc", client->GetData());
+}
+
+TEST_F(ResponseBodyLoaderTest, DrainAsDataPipeAndReportError) {
+  mojo::ScopedDataPipeConsumerHandle consumer_end;
+  mojo::ScopedDataPipeProducerHandle producer_end;
+  auto result = mojo::CreateDataPipe(nullptr, &producer_end, &consumer_end);
+
+  ASSERT_EQ(result, MOJO_RESULT_OK);
+
+  DataPipeBytesConsumer::CompletionNotifier* completion_notifier = nullptr;
+
+  auto task_runner = base::MakeRefCounted<scheduler::FakeTaskRunner>();
+  auto* consumer = MakeGarbageCollected<DataPipeBytesConsumer>(
+      task_runner, std::move(consumer_end), &completion_notifier);
+  auto* client = MakeGarbageCollected<TestClient>();
+
+  auto* body_loader =
+      MakeGarbageCollected<ResponseBodyLoader>(*consumer, *client, task_runner);
+
+  ResponseBodyLoaderClient* client_for_draining = nullptr;
+  auto data_pipe = body_loader->DrainAsDataPipe(&client_for_draining);
+
+  ASSERT_TRUE(data_pipe);
+  ASSERT_TRUE(client);
+  EXPECT_TRUE(body_loader->IsDrained());
+
+  client_for_draining->DidReceiveData(base::make_span("xyz", 3));
+  client_for_draining->DidReceiveData(base::make_span("abc", 3));
+
+  EXPECT_FALSE(client->LoadingIsFinished());
+  EXPECT_FALSE(client->LoadingIsFailed());
+  EXPECT_EQ("xyzabc", client->GetData());
+
+  client_for_draining->DidFailLoadingBody();
+
+  EXPECT_FALSE(client->LoadingIsFinished());
+  EXPECT_TRUE(client->LoadingIsFailed());
+  EXPECT_EQ("xyzabc", client->GetData());
+}
+
+}  // namespace
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index 4e3fe38..51ebcd5 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -1007,6 +1007,11 @@
       status: "experimental",
     },
     {
+      name: "PerMethodCanMakePaymentQuota",
+      origin_trial_feature_name: "PerMethodCanMakePaymentQuota",
+      status: "experimental",
+    },
+    {
       name: "PermissionDelegation",
       status: "test",
     },
diff --git a/third_party/blink/web_tests/FlagExpectations/enable-blink-features=LayoutNG b/third_party/blink/web_tests/FlagExpectations/enable-blink-features=LayoutNG
index f1f0bd5..1fc146f 100644
--- a/third_party/blink/web_tests/FlagExpectations/enable-blink-features=LayoutNG
+++ b/third_party/blink/web_tests/FlagExpectations/enable-blink-features=LayoutNG
@@ -261,12 +261,18 @@
 crbug.com/591099 external/wpt/fetch/api/redirect/redirect-count.any.worker.html [ Pass ]
 crbug.com/591099 external/wpt/fetch/api/request/request-keepalive-quota.html?include=slow-2 [ Pass ]
 crbug.com/591099 external/wpt/fullscreen/api/element-ready-check-containing-iframe-manual.html [ Pass ]
-crbug.com/591099 external/wpt/geolocation-API/PositionOptions.https.html [ Failure Pass ]
+crbug.com/591099 external/wpt/geolocation-API/PositionOptions.https.html [ Failure ]
 crbug.com/591099 external/wpt/html/browsers/origin/cross-origin-objects/cross-origin-objects.html [ Crash Pass ]
 crbug.com/591099 external/wpt/html/browsers/the-window-object/window-open-noopener.html?_parent [ Pass ]
 crbug.com/591099 external/wpt/html/browsers/the-window-object/window-open-noopener.html?_top [ Pass ]
 crbug.com/591099 external/wpt/html/semantics/embedded-content/the-embed-element/embed-represent-nothing-02.html [ Pass ]
 crbug.com/845902 external/wpt/quirks/line-height-trailing-collapsable-whitespace.html [ Pass ]
+crbug.com/591099 external/wpt/wasm/jsapi/constructor/instantiate.any.html [ Failure ]
+crbug.com/591099 external/wpt/wasm/jsapi/constructor/instantiate.any.worker.html [ Failure ]
+crbug.com/591099 external/wpt/wasm/webapi/instantiateStreaming.any.html [ Failure ]
+crbug.com/591099 external/wpt/wasm/webapi/instantiateStreaming.any.serviceworker.html [ Failure ]
+crbug.com/591099 external/wpt/wasm/webapi/instantiateStreaming.any.sharedworker.html [ Failure ]
+crbug.com/591099 external/wpt/wasm/webapi/instantiateStreaming.any.worker.html [ Failure ]
 crbug.com/591099 external/wpt/webmessaging/with-ports/018.html [ Pass ]
 crbug.com/591099 external/wpt/webmessaging/without-ports/018.html [ Pass ]
 crbug.com/591099 fast/backgrounds/quirks-mode-line-box-backgrounds.html [ Failure ]
@@ -290,7 +296,10 @@
 crbug.com/591099 fast/writing-mode/percentage-height-orthogonal-writing-modes.html [ Failure ]
 crbug.com/591099 fast/writing-mode/table-percent-width-quirk.html [ Pass ]
 crbug.com/591099 http/tests/devtools/tracing-session-id.js [ Failure Pass ]
+crbug.com/591099 http/tests/devtools/tracing/console-timeline.js [ Crash Pass Timeout ]
+crbug.com/591099 http/tests/devtools/tracing/timeline-misc/timeline-grouped-invalidations.js [ Failure Pass Timeout ]
 crbug.com/591099 http/tests/html/validation-bubble-oopif-clip.html [ Failure Pass ]
+crbug.com/591099 http/tests/images/feature-policy-unoptimized-images-cached-image.html [ Failure Pass ]
 crbug.com/591099 http/tests/images/image-decode-in-frame.html [ Pass ]
 crbug.com/591099 http/tests/images/restyle-decode-error.html [ Failure ]
 crbug.com/591099 http/tests/media/autoplay/document-user-activation-cross-origin-feature-policy-disabled.html [ Failure Pass ]
diff --git a/third_party/blink/web_tests/SlowTests b/third_party/blink/web_tests/SlowTests
index 93233d7..e55b134 100644
--- a/third_party/blink/web_tests/SlowTests
+++ b/third_party/blink/web_tests/SlowTests
@@ -92,10 +92,8 @@
 crbug.com/420008 virtual/threaded/http/tests/devtools/tracing/ [ Slow ]
 crbug.com/902685 http/tests/devtools/isolated-code-cache/same-origin-test.js [ Slow ]
 crbug.com/902685 http/tests/devtools/isolated-code-cache/cross-origin-test.js [ Slow ]
-crbug.com/902685 http/tests/devtools/wasm-isolated-code-cache/wasm-cache-test.js [ Slow ]
 crbug.com/902685 virtual/site-isolated-code-cache/http/tests/devtools/isolated-code-cache/same-origin-test.js [ Slow ]
 crbug.com/902685 virtual/site-isolated-code-cache/http/tests/devtools/isolated-code-cache/cross-origin-test.js [ Slow ]
-crbug.com/902685 virtual/wasm-site-isolated-code-cache/http/tests/devtools/wasm-isolated-code-cache/wasm-cache-test.js [ Slow ]
 crbug.com/902685 virtual/not-site-per-process/http/tests/devtools/isolated-code-cache/same-origin-test.js [ Slow ]
 crbug.com/902685 virtual/not-site-per-process/http/tests/devtools/isolated-code-cache/cross-origin-test.js [ Slow ]
 
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 577d999..00c3d7b 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -2770,6 +2770,7 @@
 # Working on getting the CSP tests going:
 crbug.com/694525 external/wpt/content-security-policy/connect-src/worker-from-guid.sub.html [ Skip ]
 crbug.com/694525 external/wpt/content-security-policy/connect-src/worker-connect-src-blocked.sub.html [ Skip ]
+crbug.com/694525 external/wpt/content-security-policy/navigation/to-javascript-parent-initiated-child-csp.html [ Skip ]
 crbug.com/694525 external/wpt/content-security-policy/form-action/form-action-src-redirect-blocked.sub.html [ Skip ]
 crbug.com/694525 external/wpt/content-security-policy/reporting-api [ Skip ]
 crbug.com/694525 external/wpt/content-security-policy/script-src/script-src-1_10.html [ Skip ]
@@ -5852,3 +5853,6 @@
 crbug.com/v8/8319 external/wpt/wasm/webapi/instantiateStreaming.any.serviceworker.html [ Failure ]
 crbug.com/v8/8319 external/wpt/wasm/webapi/instantiateStreaming.any.sharedworker.html [ Failure ]
 crbug.com/v8/8319 external/wpt/wasm/webapi/instantiateStreaming.any.worker.html [ Failure ]
+
+crbug.com/v8/8319 external/wpt/wasm/jsapi/module/customSections.any.html [ Pass Failure ]
+crbug.com/v8/8319 external/wpt/wasm/jsapi/module/customSections.any.worker.html [ Pass Failure ]
diff --git a/third_party/blink/web_tests/VirtualTestSuites b/third_party/blink/web_tests/VirtualTestSuites
index a6d90ec..3b92d6c 100644
--- a/third_party/blink/web_tests/VirtualTestSuites
+++ b/third_party/blink/web_tests/VirtualTestSuites
@@ -778,13 +778,6 @@
              "--site-per-process"]
   },
   {
-    "prefix": "wasm-site-isolated-code-cache",
-    "base": "http/tests/devtools/wasm-isolated-code-cache",
-    "args": ["--enable-features=IsolatedCodeCache,WasmCodeCache",
-             "--disable-features=WebAssemblyBaseline",
-             "--site-per-process"]
-  },
-  {
     "prefix": "not-site-per-process",
     "base": "http/tests/devtools/isolated-code-cache",
     "args": ["--disable-site-isolation-trials"]
diff --git a/third_party/blink/web_tests/animations/animationworklet/animator-registration-expected.txt b/third_party/blink/web_tests/animations/animationworklet/animator-registration-expected.txt
index fb35e6e3..8b13789 100644
--- a/third_party/blink/web_tests/animations/animationworklet/animator-registration-expected.txt
+++ b/third_party/blink/web_tests/animations/animationworklet/animator-registration-expected.txt
@@ -1,4 +1 @@
-CONSOLE MESSAGE: line 5: [object DOMException]
-CONSOLE MESSAGE: line 4: TypeError: Failed to execute 'registerAnimator' on 'AnimationWorkletGlobalScope': The callback provided as parameter 2 is not a function.
-CONSOLE MESSAGE: line 4: TypeError: Failed to execute 'registerAnimator' on 'AnimationWorkletGlobalScope': The empty string is not a valid name.
 
diff --git a/third_party/blink/web_tests/animations/animationworklet/animator-registration.html b/third_party/blink/web_tests/animations/animationworklet/animator-registration.html
index 4559df7..d766cd6 100644
--- a/third_party/blink/web_tests/animations/animationworklet/animator-registration.html
+++ b/third_party/blink/web_tests/animations/animationworklet/animator-registration.html
@@ -6,31 +6,55 @@
 https://codesearch.chromium.org/chromium/src/third_party/blink/renderer/bindings/core/v8/worker_or_worklet_script_controller.cc?sq=package:chromium&dr&l=320
 -->
 <script id="duplicate" type="text/worklet">
+const expectedError = "A class with name:'duplicate' is already registered";
+let receivedError = undefined;
 try {
   registerAnimator("duplicate", class { animate() {} });
   registerAnimator("duplicate", class { animate() {} });
-} catch(e) { console.log(e); }
+} catch(e) {
+  receivedError = e.toString();
+}
+if (!receivedError || receivedError.indexOf(expectedError) < 0)
+  console.log(`FAIL: expected=${expectedError}, received=${receivedError}`);
 </script>
 
 <script id="no_class" type="text/worklet">
+const expectedError = "The callback provided as parameter 2 is not a function";
+let receivedError = undefined;
 try {
   registerAnimator("no_class", "");
-} catch(e) { console.log(e); }
+} catch(e) {
+  receivedError = e.toString();
+}
+if (!receivedError || receivedError.indexOf(expectedError) < 0)
+  console.log(`FAIL: expected=${expectedError}, received=${receivedError}`);
 </script>
 
 <script id="empty_string" type="text/worklet">
+const expectedError = "The empty string is not a valid name";
+let receivedError = undefined;
 try {
   registerAnimator("", class { animate() {} });
-} catch(e) { console.log(e); }
+} catch(e) {
+  receivedError = e.toString();
+}
+if (!receivedError || receivedError.indexOf(expectedError) < 0)
+  console.log(`FAIL: expected=${expectedError}, received=${receivedError}`);
 </script>
 
-<script id="empty_string" type="text/worklet">
-registerAnimator("test", class {
-  constructor() {
-    console.log("test animator constructor called.");
-  }
-  animate() {}
-});
+<script id="legit" type="text/worklet">
+let receivedError = undefined;
+try {
+  registerAnimator("test", class {
+    constructor() {}
+    animate() {}
+  });
+} catch(e) {
+  // Exception should not be thrown.
+  receivedError = "An error occurred";
+}
+if (receivedError)
+  console.log(`FAIL: unexpected error: ${receivedError}`);
 </script>
 
 <script src="resources/animation-worklet-tests.js"></script>
diff --git a/third_party/blink/web_tests/animations/animationworklet/resources/animation-worklet-tests.js b/third_party/blink/web_tests/animations/animationworklet/resources/animation-worklet-tests.js
index 56ef8af..a06dea8 100644
--- a/third_party/blink/web_tests/animations/animationworklet/resources/animation-worklet-tests.js
+++ b/third_party/blink/web_tests/animations/animationworklet/resources/animation-worklet-tests.js
@@ -54,4 +54,3 @@
 
   runTests(testcases);
 }
-
diff --git a/third_party/blink/web_tests/external/wpt/content-security-policy/navigation/to-javascript-parent-initiated-child-csp.html b/third_party/blink/web_tests/external/wpt/content-security-policy/navigation/to-javascript-parent-initiated-child-csp.html
index d2289257..f4daf182 100644
--- a/third_party/blink/web_tests/external/wpt/content-security-policy/navigation/to-javascript-parent-initiated-child-csp.html
+++ b/third_party/blink/web_tests/external/wpt/content-security-policy/navigation/to-javascript-parent-initiated-child-csp.html
@@ -8,11 +8,13 @@
 <div onclick="frames[0].location.href = 'javascript:parent.postMessage(\'executed\', \'*\')'" id="special_div"></div>
 <script>
   var t = async_test("Should have executed the javascript url");
-  window.onmessage = t.step_func(function(e) {
-    if (e.data == "executed")
-      t.done();
+  frames[0].addEventListener('load', () => {
+    window.onmessage = t.step_func(function(e) {
+      if (e.data == "executed")
+        t.done();
+    });
+    window.addEventListener('securitypolicyviolation', t.unreached_func("Should not have raised a violation event"));
+    document.getElementById('special_div').click();
   });
-  window.addEventListener('securitypolicyviolation', t.unreached_func("Should not have raised a violation event"));
-  document.getElementById('special_div').click();
 </script>
 </body>
diff --git a/third_party/blink/web_tests/external/wpt/content-security-policy/navigation/to-javascript-parent-initiated-parent-csp.html b/third_party/blink/web_tests/external/wpt/content-security-policy/navigation/to-javascript-parent-initiated-parent-csp.html
index 4cbb37e..8aa8884 100644
--- a/third_party/blink/web_tests/external/wpt/content-security-policy/navigation/to-javascript-parent-initiated-parent-csp.html
+++ b/third_party/blink/web_tests/external/wpt/content-security-policy/navigation/to-javascript-parent-initiated-parent-csp.html
@@ -9,14 +9,16 @@
 <div onclick="frames[0].location.href = 'javascript:parent.postMessage(\'executed\', \'*\')'" id="special_div"></div>
 <script nonce='abc'>
   var t = async_test("Should not have executed the javascript url");
-  window.onmessage = t.step_func(function(e) {
-    if (e.data == "executed")
-      assert_true(false, "Javascript url executed");
+  frames[0].addEventListener('load', () => {
+    window.onmessage = t.step_func(function(e) {
+      if (e.data == "executed")
+        assert_true(false, "Javascript url executed");
+    });
+    window.addEventListener('securitypolicyviolation', t.step_func_done(function(e) {
+      assert_equals(e.blockedURI, 'inline');
+      assert_equals(e.violatedDirective, 'script-src-attr');
+    }));
+    document.getElementById('special_div').click();
   });
-  window.addEventListener('securitypolicyviolation', t.step_func_done(function(e) {
-    assert_equals(e.blockedURI, 'inline');
-    assert_equals(e.violatedDirective, 'script-src-attr');
-  }));
-  document.getElementById('special_div').click();
 </script>
 </body>
diff --git a/third_party/blink/web_tests/external/wpt/webrtc-quic/RTCQuicStream.https.html b/third_party/blink/web_tests/external/wpt/webrtc-quic/RTCQuicStream.https.html
index b62377f..563d74a 100644
--- a/third_party/blink/web_tests/external/wpt/webrtc-quic/RTCQuicStream.https.html
+++ b/third_party/blink/web_tests/external/wpt/webrtc-quic/RTCQuicStream.https.html
@@ -500,18 +500,6 @@
   return data;
 }
 
-// Writes |amount| of bytes to the given RTCQuicStream in maxWriteBufferedAmount
-// chunks.
-async function writeGeneratedData(stream, amount) {
-  const data = generateData(Math.min(stream.maxWriteBufferedAmount, amount));
-  while (amount > 0) {
-    const chunkSize = Math.min(stream.maxWriteBufferedAmount, amount);
-    await stream.waitForWriteBufferedAmountBelow(0);
-    stream.write({ data: data.subarray(0, chunkSize) });
-    amount -= chunkSize;
-  }
-}
-
 promise_test(async t => {
   const [ localQuicTransport, remoteQuicTransport ] =
       await makeTwoConnectedQuicTransports(t);
@@ -539,22 +527,6 @@
   const [ localQuicTransport, remoteQuicTransport ] =
       await makeTwoConnectedQuicTransports(t);
   const localStream = localQuicTransport.createStream();
-  const remoteWatcher = new EventWatcher(t, remoteQuicTransport, 'quicstream');
-  writeGeneratedData(localStream, localStream.maxReadBufferedAmount);
-  const { stream: remoteStream } = await remoteWatcher.wait_for('quicstream');
-  await remoteStream.waitForReadable(localStream.maxReadBufferedAmount);
-  const readBuffer = new Uint8Array(localStream.maxReadBufferedAmount);
-  assert_object_equals(
-      remoteStream.readInto(readBuffer),
-      { amount: localStream.maxReadBufferedAmount, finished: false } );
-  assert_array_equals(
-      readBuffer, generateData(localStream.maxReadBufferedAmount));
-}, 'Read maxReadBufferedAmount bytes all at once.');
-
-promise_test(async t => {
-  const [ localQuicTransport, remoteQuicTransport ] =
-      await makeTwoConnectedQuicTransports(t);
-  const localStream = localQuicTransport.createStream();
   localStream.write({ finish: true });
   const remoteWatcher = new EventWatcher(t, remoteQuicTransport, 'quicstream');
   const { stream: remoteStream } = await remoteWatcher.wait_for('quicstream');
diff --git a/third_party/blink/web_tests/fast/events/pointerevents/mouseevent_key_pressed.html b/third_party/blink/web_tests/fast/events/pointerevents/mouseevent_key_pressed.html
new file mode 100644
index 0000000..60bab81
--- /dev/null
+++ b/third_party/blink/web_tests/fast/events/pointerevents/mouseevent_key_pressed.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <meta charset="utf-8" />
+        <title>Mouse events with keys pressed</title>
+        <link rel="author" title="Google" href="http://www.google.com/" />
+        <script src="../../../resources/testharness.js"></script>
+        <script src="../../../resources/testharnessreport.js"></script>
+        <script>
+            var testMouseKeyPressed = async_test('Tests that the mouse events with some keys pressed.');
+            var activeKeys = false;
+            window.addEventListener('pointermove', function(e) {
+                activeKeys = e.getModifierState("Alt") && e.getModifierState("Control") && e.getModifierState("Meta") && e.getModifierState("Shift") && e.getModifierState("CapsLock") && e.getModifierState("NumLock") && e.getModifierState("AltGraph");
+            });
+
+            function callbackValidKeysPressed() {
+                if (activeKeys) {
+                    testMouseKeyPressed.done();
+                }
+            }
+
+            function inject_input() {
+                // First press Alt, Control, Meta, Shift, CapsLock, NumLock, AltGraph keys and then send a mouse move.
+                if (window.chrome && chrome.gpuBenchmarking) {
+                    var pointerActions =
+                        [{source: "mouse", id: 0,
+                          actions: [
+                            { name: "pointerMove", x: 100, y: 100, keys: "Alt, Control, Meta, Shift, CapsLock, NumLock, AltGraph" }]}];
+                    chrome.gpuBenchmarking.pointerActionSequence(pointerActions, callbackValidKeysPressed);
+                }
+            }
+        </script>
+
+    </head>
+    <body id="target" onload="inject_input()">
+        <h4>Test Description: Tests that the mouse events with some keys pressed.
+            <ol>
+                <li>Press Alt, Control, Meta, Shift, CapsLock, NumLock, AltGraph keys and move the mouse</li>
+            </ol>
+        </h4>
+    </body>
+</html>
diff --git a/third_party/blink/web_tests/fast/forms/number/number-spinbutton-changeevent-trigger-without-mousemove.html b/third_party/blink/web_tests/fast/forms/number/number-spinbutton-changeevent-trigger-without-mousemove.html
new file mode 100644
index 0000000..929f6c2
--- /dev/null
+++ b/third_party/blink/web_tests/fast/forms/number/number-spinbutton-changeevent-trigger-without-mousemove.html
@@ -0,0 +1,91 @@
+<!DOCTYPE>
+<html>
+<body>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/testdriver.js"></script>
+<script src="../../../resources/testdriver-vendor.js"></script>
+<script src="../../../external/wpt/resources/testdriver-actions.js"></script>
+<input type="number" id="num1" onchange="handleChangeEvent(this);" oninput="handleInputEvent(this);">
+<div id="div1" style="width:10px;height:10px; margin:50px; background-color: red"></div>
+</body>
+<script>
+var changeEventCounter = 0;
+var inputEventCounter = 0;
+var mouseMoveCount = 0;
+
+num1.value = 0;
+x = num1.offsetLeft + num1.offsetWidth - 10;
+y = num1.offsetTop + num1.offsetHeight / 4
+
+function handleChangeEvent(element) {
+    ++changeEventCounter;
+}
+
+function handleInputEvent(element) {
+    ++inputEventCounter;
+}
+
+div1.addEventListener("pointermove", function(e) {
+  ++mouseMoveCount;
+})
+promise_test (async() => {
+    num1.focus();
+
+    await new test_driver.Actions()
+                         .pointerMove(x, y)
+                         .pointerDown()
+                         .pointerUp()
+                         .pointerDown()
+                         .pointerUp()
+                         .pointerDown()
+                         .pointerUp()
+                         .send();
+
+    assert_equals(inputEventCounter,  3, "input event");
+    assert_equals(changeEventCounter, 3, "change event");
+    assert_equals(num1.value, '3', "number field value");
+}, "Test that send 3 click without move in between dispatch input and change events correctly");
+
+promise_test (async() => {
+    await new test_driver.Actions()
+                         .pointerMove(0, 0, {origin: div1})
+                         .pointerDown()
+                         .pointerUp()
+                         .send();
+
+    assert_equals(inputEventCounter,  3, "input event");
+    assert_equals(changeEventCounter, 3, "change event");
+    assert_equals(num1.value, '3', "number field value");
+    assert_equals(mouseMoveCount, 1)
+}, "pointer move and click elsewhere, and it's not captured by spin button");
+
+promise_test (async() => {
+    num1.focus();
+    await new test_driver.Actions()
+                            .pointerMove(x, y)
+                            .pointerDown()
+                            .pointerMove(x - 10, y - 10)
+                            .send();
+
+    assert_equals(inputEventCounter,  4, "input event");
+    assert_equals(changeEventCounter, 4, "change event");
+    assert_equals(num1.value, '4', "number field value");
+}, "Mouse down at spin button and move out dispatch input and change events correctly");
+
+promise_test (async() => {
+    await new test_driver.Actions()
+                         .pointerMove(0, 0, {origin: div1})
+                         .pointerDown()
+                         .pointerUp()
+                         .send();
+
+    assert_equals(inputEventCounter,  4, "input event");
+    assert_equals(changeEventCounter, 4, "change event");
+    assert_equals(num1.value, '4', "number field value");
+    assert_equals(mouseMoveCount, 2)
+}, "click elsewhere is not captured by spin button");
+
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/http/tests/devtools/wasm-isolated-code-cache/wasm-cache-test-expected.txt b/third_party/blink/web_tests/http/tests/devtools/wasm-isolated-code-cache/wasm-cache-test-expected.txt
deleted file mode 100644
index 4b10d7a..0000000
--- a/third_party/blink/web_tests/http/tests/devtools/wasm-isolated-code-cache/wasm-cache-test-expected.txt
+++ /dev/null
@@ -1,88 +0,0 @@
-Tests V8 code cache for WebAssembly resources.
-
----First navigation - produce and consume code cache ------
-
-v8.wasm.streamFromResponseCallback Properties:
-{
-    data : {
-    }
-    endTime : <number>
-    startTime : <number>
-    type : "v8.wasm.streamFromResponseCallback"
-}
-v8.wasm.streamFromResponseCallback Properties:
-{
-    data : {
-    }
-    endTime : <number>
-    startTime : <number>
-    type : "v8.wasm.streamFromResponseCallback"
-}
-v8.wasm.streamFromResponseCallback Properties:
-{
-    data : {
-    }
-    endTime : <number>
-    startTime : <number>
-    type : "v8.wasm.streamFromResponseCallback"
-}
-v8.wasm.streamFromResponseCallback Properties:
-{
-    data : {
-    }
-    endTime : <number>
-    startTime : <number>
-    type : "v8.wasm.streamFromResponseCallback"
-}
-v8.wasm.streamFromResponseCallback Properties:
-{
-    data : {
-    }
-    endTime : <number>
-    startTime : <number>
-    type : "v8.wasm.streamFromResponseCallback"
-}
-
---- Second navigation - from a different origin ------
-
-v8.wasm.streamFromResponseCallback Properties:
-{
-    data : {
-    }
-    endTime : <number>
-    startTime : <number>
-    type : "v8.wasm.streamFromResponseCallback"
-}
-v8.wasm.streamFromResponseCallback Properties:
-{
-    data : {
-    }
-    endTime : <number>
-    startTime : <number>
-    type : "v8.wasm.streamFromResponseCallback"
-}
-v8.wasm.streamFromResponseCallback Properties:
-{
-    data : {
-    }
-    endTime : <number>
-    startTime : <number>
-    type : "v8.wasm.streamFromResponseCallback"
-}
-v8.wasm.streamFromResponseCallback Properties:
-{
-    data : {
-    }
-    endTime : <number>
-    startTime : <number>
-    type : "v8.wasm.streamFromResponseCallback"
-}
-v8.wasm.streamFromResponseCallback Properties:
-{
-    data : {
-    }
-    endTime : <number>
-    startTime : <number>
-    type : "v8.wasm.streamFromResponseCallback"
-}
-
diff --git a/third_party/blink/web_tests/http/tests/devtools/wasm-isolated-code-cache/wasm-cache-test.js b/third_party/blink/web_tests/http/tests/devtools/wasm-isolated-code-cache/wasm-cache-test.js
deleted file mode 100644
index 8d90680..0000000
--- a/third_party/blink/web_tests/http/tests/devtools/wasm-isolated-code-cache/wasm-cache-test.js
+++ /dev/null
@@ -1,85 +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.
-
-(async function() {
-  TestRunner.addResult(`Tests V8 code cache for WebAssembly resources.\n`);
-  await TestRunner.loadModule('performance_test_runner');
-  await TestRunner.showPanel('timeline');
-
-  // Clear browser cache to avoid any existing entries for the fetched
-  // scripts in the cache.
-  SDK.multitargetNetworkManager.clearBrowserCache();
-
-  function runTests() {
-    // Loads a WASM module that is smaller than the threshold twice. It should
-    // compile and not be cached.
-    function loadSmallWasmModule(iframe_window) {
-      const url = 'http://127.0.0.1:8000/wasm/resources/load-wasm.php?name=small.wasm&cors'
-      return iframe_window.instantiateModule(url)
-        .then(() => iframe_window.instantiateModule(url));
-    }
-    // Loads a WASM module that is larger than the caching threshold. It
-    // should be cached the first run and bypass compilation the second.
-    function loadLargeWasmModule(iframe_window) {
-      const url = 'http://127.0.0.1:8000/wasm/resources/load-wasm.php?name=large.wasm&cors'
-      return iframe_window.instantiateModule(url)
-        .then(() => iframe_window.instantiateModule(url));
-    }
-    // Load the same large WASM module with a different URL. It should miss
-    // the cache, compile and be cached.
-    function loadOtherLargeWasmModule(iframe_window) {
-      const url = 'http://localhost:8000/wasm/resources/load-wasm.php?name=large.wasm&cors'
-      return iframe_window.instantiateModule(url);
-    }
-
-    let script = document.createElement('script');
-    script.type = 'module';
-    script.text = 'window.finishTest()';
-    document.body.appendChild(script);
-
-    const frameId = 'frame_id';
-    const iframe_window = document.getElementById(frameId).contentWindow;
-
-    // These functions must be called in this order.
-    return loadSmallWasmModule(iframe_window)
-      .then(() => loadLargeWasmModule(iframe_window))
-      .then(() => loadOtherLargeWasmModule(iframe_window));
-  }
-
-  await TestRunner.evaluateInPagePromise(runTests.toString());
-
-  TestRunner.addResult(
-      '---First navigation - produce and consume code cache ------\n');
-
-  // Create a same origin iframe.
-  const scope = 'http://127.0.0.1:8000/wasm/resources/wasm-cache-iframe.html';
-  await TestRunner.addIframe(scope, {id: 'frame_id'});
-
-  await PerformanceTestRunner.invokeAsyncWithTimeline('runTests');
-
-  const events = new Set([
-    TimelineModel.TimelineModel.RecordType.WasmStreamFromResponseCallback,
-    TimelineModel.TimelineModel.RecordType.WasmCompiledModule,
-    TimelineModel.TimelineModel.RecordType.WasmCachedModule,
-    TimelineModel.TimelineModel.RecordType.WasmModuleCacheHit,
-    TimelineModel.TimelineModel.RecordType.WasmModuleCacheInvalid]);
-  const tracingModel = PerformanceTestRunner.tracingModel();
-
-  tracingModel.sortedProcesses().forEach(p => p.sortedThreads().forEach(t =>
-      t.events().filter(event => events.has(event.name)).forEach(PerformanceTestRunner.printTraceEventProperties)));
-
-  // Second navigation
-  TestRunner.addResult(
-      '\n--- Second navigation - from a different origin ------\n');
-
-  const other_scope = 'http://localhost:8000/wasm/resources/wasm-cache-iframe.html';
-  await TestRunner.addIframe(other_scope, {id: 'frame_id'});
-
-  await PerformanceTestRunner.invokeAsyncWithTimeline('runTests');
-
-  tracingModel.sortedProcesses().forEach(p => p.sortedThreads().forEach(t =>
-      t.events().filter(event => events.has(event.name)).forEach(PerformanceTestRunner.printTraceEventProperties)));
-
-  TestRunner.completeTest();
-})();
diff --git a/third_party/blink/web_tests/http/tests/origin_trials/webexposed/animationworklet-origin-trial-interfaces-worklet-scope-expected.txt b/third_party/blink/web_tests/http/tests/origin_trials/webexposed/animationworklet-origin-trial-interfaces-worklet-scope-expected.txt
index 33995cd7..a5f1340 100644
--- a/third_party/blink/web_tests/http/tests/origin_trials/webexposed/animationworklet-origin-trial-interfaces-worklet-scope-expected.txt
+++ b/third_party/blink/web_tests/http/tests/origin_trials/webexposed/animationworklet-origin-trial-interfaces-worklet-scope-expected.txt
@@ -38,4 +38,43 @@
 CONSOLE MESSAGE: line 153:     attribute globalThis
 CONSOLE MESSAGE: line 153:     method gc
 CONSOLE MESSAGE: line 153:     method registerAnimator
+CONSOLE MESSAGE: line 153: interface AnimationWorkletGlobalScope : WorkletGlobalScope
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153: interface ByteLengthQueuingStrategy
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     method size
+CONSOLE MESSAGE: line 153: interface CountQueuingStrategy
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     method size
+CONSOLE MESSAGE: line 153: interface EffectProxy
+CONSOLE MESSAGE: line 153:     getter localTime
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     setter localTime
+CONSOLE MESSAGE: line 153: interface ReadableStream
+CONSOLE MESSAGE: line 153:     getter locked
+CONSOLE MESSAGE: line 153:     method cancel
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     method getReader
+CONSOLE MESSAGE: line 153:     method pipeThrough
+CONSOLE MESSAGE: line 153:     method pipeTo
+CONSOLE MESSAGE: line 153:     method tee
+CONSOLE MESSAGE: line 153: interface TransformStream
+CONSOLE MESSAGE: line 153:     getter readable
+CONSOLE MESSAGE: line 153:     getter writable
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153: interface WorkletGlobalScope
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153: interface WorkletGroupEffectProxy
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     method getChildren
+CONSOLE MESSAGE: line 153: interface WritableStream
+CONSOLE MESSAGE: line 153:     getter locked
+CONSOLE MESSAGE: line 153:     method abort
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     method getWriter
+CONSOLE MESSAGE: line 153: global object
+CONSOLE MESSAGE: line 153:     attribute console
+CONSOLE MESSAGE: line 153:     attribute globalThis
+CONSOLE MESSAGE: line 153:     method gc
+CONSOLE MESSAGE: line 153:     method registerAnimator
 
diff --git a/third_party/blink/web_tests/http/tests/wasm/resources/large.wasm b/third_party/blink/web_tests/http/tests/wasm/resources/large.wasm
deleted file mode 100644
index fe5e206..0000000
--- a/third_party/blink/web_tests/http/tests/wasm/resources/large.wasm
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/http/tests/wasm/resources/load-wasm.php b/third_party/blink/web_tests/http/tests/wasm/resources/load-wasm.php
index 14967b94..9b1ac01 100644
--- a/third_party/blink/web_tests/http/tests/wasm/resources/load-wasm.php
+++ b/third_party/blink/web_tests/http/tests/wasm/resources/load-wasm.php
@@ -4,16 +4,7 @@
     if (isset($_GET['name'])) {
        $fileName = $_GET['name'];
     }
-    $last_modified = "Tue, 18 Dec 2018 23:15:53 GMT";
-    if (isset($_GET['last-modified'])) {
-       $last_modified = $_GET['last-modified'];
-    }
 
     header("Content-Type: application/wasm");
-    header("Last-Modified: ".$last_modified);
-
-    if (isset($_GET['cors'])) {
-      header("Access-Control-Allow-Origin: *");
-    }
     require($fileName);
 ?>
diff --git a/third_party/blink/web_tests/http/tests/wasm/resources/small.wasm b/third_party/blink/web_tests/http/tests/wasm/resources/small.wasm
deleted file mode 100644
index f8b2262..0000000
--- a/third_party/blink/web_tests/http/tests/wasm/resources/small.wasm
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/http/tests/wasm/resources/wasm-cache-iframe.html b/third_party/blink/web_tests/http/tests/wasm/resources/wasm-cache-iframe.html
deleted file mode 100644
index 8a62a7aa..0000000
--- a/third_party/blink/web_tests/http/tests/wasm/resources/wasm-cache-iframe.html
+++ /dev/null
@@ -1,10 +0,0 @@
-<script src="../../../resources/testharness.js"></script>
-<script>
-// Instantiate a WASM test module and make sure it works.
-function instantiateModule(url)
-{
-  return WebAssembly.instantiateStreaming(fetch(url)).then(
-  	({module, instance}) => assert_equals(instance.exports.exported_func(), 42));
-}
-</script>
-
diff --git a/third_party/blink/web_tests/http/tests/worklet/animation-worklet-csp-eval-expected.txt b/third_party/blink/web_tests/http/tests/worklet/animation-worklet-csp-eval-expected.txt
index 53e750b7d..55bc307 100644
--- a/third_party/blink/web_tests/http/tests/worklet/animation-worklet-csp-eval-expected.txt
+++ b/third_party/blink/web_tests/http/tests/worklet/animation-worklet-csp-eval-expected.txt
@@ -1,5 +1,7 @@
 CONSOLE ERROR: line 1: Uncaught EvalError: Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of script in the following Content Security Policy directive: "script-src 'self' 'unsafe-inline'".
 
+CONSOLE ERROR: line 1: Uncaught EvalError: Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of script in the following Content Security Policy directive: "script-src 'self' 'unsafe-inline'".
+
 This is a testharness.js-based test.
 PASS eval() call on the worklet should be blocked because the script-src unsafe-eval directive is not specified.
 PASS eval() call on the worklet should not be blocked because the script-src unsafe-eval directive allows it.
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/atsui-spacing-features-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/atsui-spacing-features-expected.png
index 9b33c06e..be1b136 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/atsui-spacing-features-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/atsui-spacing-features-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/emphasis-complex-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/emphasis-complex-expected.png
index 2c60d328..6b4b59a 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/emphasis-complex-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/emphasis-complex-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/international/hebrew-vowels-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/international/hebrew-vowels-expected.png
index d30d19e6..a38de1b 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/international/hebrew-vowels-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/international/hebrew-vowels-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.11/fast/text/emphasis-complex-expected.png b/third_party/blink/web_tests/platform/mac-mac10.11/fast/text/emphasis-complex-expected.png
index 39cf5de..4e9c333 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.11/fast/text/emphasis-complex-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.11/fast/text/emphasis-complex-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.11/svg/text/surrogate-pair-queries-expected.png b/third_party/blink/web_tests/platform/mac-mac10.11/svg/text/surrogate-pair-queries-expected.png
index 5528978..9fa7679e 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.11/svg/text/surrogate-pair-queries-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.11/svg/text/surrogate-pair-queries-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/atsui-spacing-features-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/atsui-spacing-features-expected.png
index f284f95..76378a9 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/atsui-spacing-features-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/atsui-spacing-features-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/emphasis-complex-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/emphasis-complex-expected.png
index 4c83e26..3f94b96 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/emphasis-complex-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/emphasis-complex-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/international/hebrew-vowels-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/international/hebrew-vowels-expected.png
index cb21b18..567b6937 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/international/hebrew-vowels-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/international/hebrew-vowels-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/svg/text/combining-character-queries-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/svg/text/combining-character-queries-expected.png
index 3a5b6ca4..cc78f133 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.12/svg/text/combining-character-queries-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/svg/text/combining-character-queries-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/svg/text/ligature-queries-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/svg/text/ligature-queries-expected.png
index cfb9c164..2f331b4 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.12/svg/text/ligature-queries-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/svg/text/ligature-queries-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/svg/text/surrogate-pair-queries-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/svg/text/surrogate-pair-queries-expected.png
index 7e27ed5..c160869 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.12/svg/text/surrogate-pair-queries-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/svg/text/surrogate-pair-queries-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/text/atsui-spacing-features-expected.png b/third_party/blink/web_tests/platform/mac/fast/text/atsui-spacing-features-expected.png
index d111d3b..e6069343 100644
--- a/third_party/blink/web_tests/platform/mac/fast/text/atsui-spacing-features-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/text/atsui-spacing-features-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/text/emphasis-complex-expected.png b/third_party/blink/web_tests/platform/mac/fast/text/emphasis-complex-expected.png
index 9b35c0d..b3180b01 100644
--- a/third_party/blink/web_tests/platform/mac/fast/text/emphasis-complex-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/text/emphasis-complex-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/text/international/hebrew-vowels-expected.png b/third_party/blink/web_tests/platform/mac/fast/text/international/hebrew-vowels-expected.png
index 0f0502c..3cbf0fe7c 100644
--- a/third_party/blink/web_tests/platform/mac/fast/text/international/hebrew-vowels-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/text/international/hebrew-vowels-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/svg/text/combining-character-queries-expected.png b/third_party/blink/web_tests/platform/mac/svg/text/combining-character-queries-expected.png
index 673523c2..aaed109 100644
--- a/third_party/blink/web_tests/platform/mac/svg/text/combining-character-queries-expected.png
+++ b/third_party/blink/web_tests/platform/mac/svg/text/combining-character-queries-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/svg/text/ligature-queries-expected.png b/third_party/blink/web_tests/platform/mac/svg/text/ligature-queries-expected.png
index 52cde31..ceafb5e 100644
--- a/third_party/blink/web_tests/platform/mac/svg/text/ligature-queries-expected.png
+++ b/third_party/blink/web_tests/platform/mac/svg/text/ligature-queries-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/wasm-site-isolated-code-cache/http/tests/devtools/wasm-isolated-code-cache/README.txt b/third_party/blink/web_tests/virtual/wasm-site-isolated-code-cache/http/tests/devtools/wasm-isolated-code-cache/README.txt
deleted file mode 100644
index 4ebf844c..0000000
--- a/third_party/blink/web_tests/virtual/wasm-site-isolated-code-cache/http/tests/devtools/wasm-isolated-code-cache/README.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-# This suite runs the tests in http/tests/devtools/wasm-isolated-code-cache with
-# --enable-features=IsolatedCodeCache,WasmCodeCache --site-per-process.
-# This feature is required for security to enforce site isolation on V8
-# code caches. Tracking bug: crbug.com/812168
diff --git a/third_party/blink/web_tests/virtual/wasm-site-isolated-code-cache/http/tests/devtools/wasm-isolated-code-cache/wasm-cache-test-expected.txt b/third_party/blink/web_tests/virtual/wasm-site-isolated-code-cache/http/tests/devtools/wasm-isolated-code-cache/wasm-cache-test-expected.txt
deleted file mode 100644
index c0d97ff..0000000
--- a/third_party/blink/web_tests/virtual/wasm-site-isolated-code-cache/http/tests/devtools/wasm-isolated-code-cache/wasm-cache-test-expected.txt
+++ /dev/null
@@ -1,216 +0,0 @@
-Tests V8 code cache for WebAssembly resources.
-
----First navigation - produce and consume code cache ------
-
-v8.wasm.streamFromResponseCallback Properties:
-{
-    data : {
-    }
-    endTime : <number>
-    startTime : <number>
-    type : "v8.wasm.streamFromResponseCallback"
-}
-v8.wasm.compiledModule Properties:
-{
-    data : {
-        url : http://127.0.0.1:8000/wasm/resources/load-wasm.php?name=small.wasm&cors
-    }
-    endTime : <number>
-    startTime : <number>
-    type : "v8.wasm.compiledModule"
-}
-v8.wasm.streamFromResponseCallback Properties:
-{
-    data : {
-    }
-    endTime : <number>
-    startTime : <number>
-    type : "v8.wasm.streamFromResponseCallback"
-}
-v8.wasm.compiledModule Properties:
-{
-    data : {
-        url : http://127.0.0.1:8000/wasm/resources/load-wasm.php?name=small.wasm&cors
-    }
-    endTime : <number>
-    startTime : <number>
-    type : "v8.wasm.compiledModule"
-}
-v8.wasm.streamFromResponseCallback Properties:
-{
-    data : {
-    }
-    endTime : <number>
-    startTime : <number>
-    type : "v8.wasm.streamFromResponseCallback"
-}
-v8.wasm.compiledModule Properties:
-{
-    data : {
-        url : http://127.0.0.1:8000/wasm/resources/load-wasm.php?name=large.wasm&cors
-    }
-    endTime : <number>
-    startTime : <number>
-    type : "v8.wasm.compiledModule"
-}
-v8.wasm.cachedModule Properties:
-{
-    data : {
-        producedCacheSize : <number>
-    }
-    endTime : <number>
-    startTime : <number>
-    type : "v8.wasm.cachedModule"
-}
-v8.wasm.streamFromResponseCallback Properties:
-{
-    data : {
-    }
-    endTime : <number>
-    startTime : <number>
-    type : "v8.wasm.streamFromResponseCallback"
-}
-v8.wasm.moduleCacheHit Properties:
-{
-    data : {
-        consumedCacheSize : <number>
-        url : http://127.0.0.1:8000/wasm/resources/load-wasm.php?name=large.wasm&cors
-    }
-    endTime : <number>
-    startTime : <number>
-    type : "v8.wasm.moduleCacheHit"
-}
-v8.wasm.streamFromResponseCallback Properties:
-{
-    data : {
-    }
-    endTime : <number>
-    startTime : <number>
-    type : "v8.wasm.streamFromResponseCallback"
-}
-v8.wasm.compiledModule Properties:
-{
-    data : {
-        url : http://localhost:8000/wasm/resources/load-wasm.php?name=large.wasm&cors
-    }
-    endTime : <number>
-    startTime : <number>
-    type : "v8.wasm.compiledModule"
-}
-v8.wasm.cachedModule Properties:
-{
-    data : {
-        producedCacheSize : <number>
-    }
-    endTime : <number>
-    startTime : <number>
-    type : "v8.wasm.cachedModule"
-}
-
---- Second navigation - from a different origin ------
-
-v8.wasm.streamFromResponseCallback Properties:
-{
-    data : {
-    }
-    endTime : <number>
-    startTime : <number>
-    type : "v8.wasm.streamFromResponseCallback"
-}
-v8.wasm.compiledModule Properties:
-{
-    data : {
-        url : http://127.0.0.1:8000/wasm/resources/load-wasm.php?name=small.wasm&cors
-    }
-    endTime : <number>
-    startTime : <number>
-    type : "v8.wasm.compiledModule"
-}
-v8.wasm.streamFromResponseCallback Properties:
-{
-    data : {
-    }
-    endTime : <number>
-    startTime : <number>
-    type : "v8.wasm.streamFromResponseCallback"
-}
-v8.wasm.compiledModule Properties:
-{
-    data : {
-        url : http://127.0.0.1:8000/wasm/resources/load-wasm.php?name=small.wasm&cors
-    }
-    endTime : <number>
-    startTime : <number>
-    type : "v8.wasm.compiledModule"
-}
-v8.wasm.streamFromResponseCallback Properties:
-{
-    data : {
-    }
-    endTime : <number>
-    startTime : <number>
-    type : "v8.wasm.streamFromResponseCallback"
-}
-v8.wasm.compiledModule Properties:
-{
-    data : {
-        url : http://127.0.0.1:8000/wasm/resources/load-wasm.php?name=large.wasm&cors
-    }
-    endTime : <number>
-    startTime : <number>
-    type : "v8.wasm.compiledModule"
-}
-v8.wasm.cachedModule Properties:
-{
-    data : {
-        producedCacheSize : <number>
-    }
-    endTime : <number>
-    startTime : <number>
-    type : "v8.wasm.cachedModule"
-}
-v8.wasm.streamFromResponseCallback Properties:
-{
-    data : {
-    }
-    endTime : <number>
-    startTime : <number>
-    type : "v8.wasm.streamFromResponseCallback"
-}
-v8.wasm.moduleCacheHit Properties:
-{
-    data : {
-        consumedCacheSize : <number>
-        url : http://127.0.0.1:8000/wasm/resources/load-wasm.php?name=large.wasm&cors
-    }
-    endTime : <number>
-    startTime : <number>
-    type : "v8.wasm.moduleCacheHit"
-}
-v8.wasm.streamFromResponseCallback Properties:
-{
-    data : {
-    }
-    endTime : <number>
-    startTime : <number>
-    type : "v8.wasm.streamFromResponseCallback"
-}
-v8.wasm.compiledModule Properties:
-{
-    data : {
-        url : http://localhost:8000/wasm/resources/load-wasm.php?name=large.wasm&cors
-    }
-    endTime : <number>
-    startTime : <number>
-    type : "v8.wasm.compiledModule"
-}
-v8.wasm.cachedModule Properties:
-{
-    data : {
-        producedCacheSize : <number>
-    }
-    endTime : <number>
-    startTime : <number>
-    type : "v8.wasm.cachedModule"
-}
-
diff --git a/third_party/harfbuzz-ng/README.chromium b/third_party/harfbuzz-ng/README.chromium
index 5074be7..fb242411e 100644
--- a/third_party/harfbuzz-ng/README.chromium
+++ b/third_party/harfbuzz-ng/README.chromium
@@ -1,9 +1,9 @@
 Name: harfbuzz-ng
 Short Name: harfbuzz-ng
 URL: http://harfbuzz.org
-Version: 2.3.0-155
-Date: 20190124
-Revision: 36fb2b4da9718a86978fa07c99ba4345f7ca9b4b
+Version: 2.3.0-181
+Date: 20190129
+Revision: fe532923101586e316b300d419a337d357cd93da
 Security Critical: yes
 License: MIT
 License File: src/COPYING
diff --git a/third_party/ink/README.chromium b/third_party/ink/README.chromium
index 9cf9474..2dff8751 100644
--- a/third_party/ink/README.chromium
+++ b/third_party/ink/README.chromium
@@ -1,7 +1,7 @@
 Name: Google Ink
 Short Name: ink
 URL: https://github.com/google/ink
-Version: 230250745
+Version: 231281015
 License: Apache 2.0
 Security Critical: yes
 
diff --git a/third_party/ink/build/ink_lib_binary.js.sha1 b/third_party/ink/build/ink_lib_binary.js.sha1
index 90c75fd..12cde60f 100644
--- a/third_party/ink/build/ink_lib_binary.js.sha1
+++ b/third_party/ink/build/ink_lib_binary.js.sha1
@@ -1 +1 @@
-dd1031228cad37ca2de73e238c57f92ddb5599e8
\ No newline at end of file
+48f820ad74fdd7d30da7438bdb5f2a71e1729e65
\ No newline at end of file
diff --git a/third_party/ink/build/ink_lib_externs.js.sha1 b/third_party/ink/build/ink_lib_externs.js.sha1
index 81cfbff..086f9fb 100644
--- a/third_party/ink/build/ink_lib_externs.js.sha1
+++ b/third_party/ink/build/ink_lib_externs.js.sha1
@@ -1 +1 @@
-b23214c40d27afd815b297307c444f0f7a8aae5d
\ No newline at end of file
+6af491924edea3b15c0e5f35102b469f7ab50d03
\ No newline at end of file
diff --git a/third_party/ink/build/wasm/glcore_base.js.sha1 b/third_party/ink/build/wasm/glcore_base.js.sha1
index 78cafe0..dbc150f2 100644
--- a/third_party/ink/build/wasm/glcore_base.js.sha1
+++ b/third_party/ink/build/wasm/glcore_base.js.sha1
@@ -1 +1 @@
-d82d4e4ca932f33422d54f91a760c2283c15c386
\ No newline at end of file
+529bc90cbda84c1c1ea684f3b74203932e6897da
\ No newline at end of file
diff --git a/third_party/ink/build/wasm/glcore_base.wasm.sha1 b/third_party/ink/build/wasm/glcore_base.wasm.sha1
index a943dd0..9278463 100644
--- a/third_party/ink/build/wasm/glcore_base.wasm.sha1
+++ b/third_party/ink/build/wasm/glcore_base.wasm.sha1
@@ -1 +1 @@
-81f6395d9c2a455c655065c58b80b064197829e2
\ No newline at end of file
+a27172fe2f09c9c5c194219754beb0e7433f6264
\ No newline at end of file
diff --git a/third_party/ink/build/wasm/glcore_wasm_bootstrap_compiled.js.sha1 b/third_party/ink/build/wasm/glcore_wasm_bootstrap_compiled.js.sha1
index 840d1485..5a7db281 100644
--- a/third_party/ink/build/wasm/glcore_wasm_bootstrap_compiled.js.sha1
+++ b/third_party/ink/build/wasm/glcore_wasm_bootstrap_compiled.js.sha1
@@ -1 +1 @@
-63ac7c111db8e8a67ead5b95e4e43c8344b6e394
\ No newline at end of file
+c742f9bc02eeb6189d07d7acb893af6ec7c0f49e
\ No newline at end of file
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 9a6c1ad..0f0d5d8 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -21469,6 +21469,7 @@
   <int value="2770" label="NoSysexWebMIDIWithoutPermission"/>
   <int value="2771" label="NoSysexWebMIDIOnInsecureOrigin"/>
   <int value="2772" label="ApplicationCacheInstalledButNoManifest"/>
+  <int value="2773" label="PerMethodCanMakePaymentQuota"/>
 </enum>
 
 <enum name="FeaturePolicyFeature">
@@ -31211,6 +31212,7 @@
   <int value="-978700508" label="disable-simplified-fullscreen-ui"/>
   <int value="-977770313" label="AndroidSurfaceControl:enabled"/>
   <int value="-977476498" label="disable-eol-notification"/>
+  <int value="-975787829" label="AutofillProfileServerValidation:enabled"/>
   <int value="-974378602" label="WebAuthenticationCable:disabled"/>
   <int value="-973509424" label="NewTabPageIcons:disabled"/>
   <int value="-972737445" label="ArcUseAuthEndpoint:disabled"/>
@@ -31514,6 +31516,7 @@
   <int value="-416660617" label="EnforceTLS13Downgrade:disabled"/>
   <int value="-415186532" label="AndroidSiteSettingsUIRefresh:enabled"/>
   <int value="-410852857" label="ImprovedA2HS:disabled"/>
+  <int value="-408769228" label="ArcGraphicBuffersVisualizationTool:disabled"/>
   <int value="-406850932" label="EnableEmojiContextMenu:enabled"/>
   <int value="-405380243" label="enable-encryption-migration"/>
   <int value="-401170566"
@@ -31561,6 +31564,7 @@
   <int value="-340622848" label="disable-javascript-harmony-shipping"/>
   <int value="-340255045" label="allow-nacl-socket-api"/>
   <int value="-340023285" label="WebNFC:enabled"/>
+  <int value="-339103761" label="ArcGraphicBuffersVisualizationTool:enabled"/>
   <int value="-338978205" label="NavigationMojoResponse:enabled"/>
   <int value="-329727402" label="disable-files-quick-view"/>
   <int value="-328361990" label="enable-experimental-extension-apis"/>
@@ -32740,6 +32744,7 @@
   <int value="1762320532" label="AutofillKeyboardAccessory:enabled"/>
   <int value="1766676896" label="affiliation-based-matching:disabled"/>
   <int value="1767411597" label="DisallowUnsafeHttpDownloads:enabled"/>
+  <int value="1768759000" label="AutofillProfileServerValidation:disabled"/>
   <int value="1772454319" label="enable-storage-manager"/>
   <int value="1775475563" label="malware-interstitial-v3"/>
   <int value="1776475705" label="show-composited-layer-borders"/>
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 6bcc0de..ccfc08a 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -3139,6 +3139,10 @@
 
 <histogram name="Animation.AnimationWorklet.GlobalScope.MutateDuration"
     units="microseconds" expires_after="M77">
+  <obsolete>
+    Deprecated as of 01/2019. Replaced with
+    Animation.AnimationWorklet.MutateDuration.
+  </obsolete>
   <owner>majidvp@chromium.org</owner>
   <owner>animations-dev@chromium.org</owner>
   <summary>
@@ -3147,6 +3151,17 @@
   </summary>
 </histogram>
 
+<histogram name="Animation.AnimationWorklet.MutateDuration"
+    units="microseconds" expires_after="M77">
+  <owner>majidvp@chromium.org</owner>
+  <owner>kevers@chromium.org</owner>
+  <owner>animations-dev@chromium.org</owner>
+  <summary>
+    The time it takes for an AnimationWorklet to produce a mutation update. This
+    includes all V8 script execution cost.
+  </summary>
+</histogram>
+
 <histogram name="AppBanners.BeforeInstallEvent"
     enum="AppBannersBeforeInstallEvent">
   <owner>dominickn@chromium.org</owner>
@@ -33211,8 +33226,10 @@
   </summary>
 </histogram>
 
-<histogram name="Extensions.ExternalWarningUninstallationResult" enum="Boolean"
-    expires_after="2018-08-30">
+<histogram name="Extensions.ExternalWarningUninstallationResult" enum="Boolean">
+  <obsolete>
+    Expired 2018-08.
+  </obsolete>
   <owner>rdevlin.cronin@chromium.org</owner>
   <summary>
     Whether or not the uninstallation of an external extension succeeded.
@@ -37812,7 +37829,7 @@
 </histogram>
 
 <histogram name="Geolocation.HeaderSentOrNot" enum="GeolocationHeaderSentOrNot">
-  <owner>kiyun@chromium.org</owner>
+  <owner>kiyun@google.com</owner>
   <owner>mvanouwerkerk@chromium.org</owner>
   <owner>newt@chromium.org</owner>
   <summary>
@@ -46511,7 +46528,7 @@
 </histogram>
 
 <histogram name="Media.Audio.Render.StreamBrokerDisconnectReason2"
-    enum="AudioOutputStreamDisconnectReason2" expires_after="2019-02-01">
+    enum="AudioOutputStreamDisconnectReason2" expires_after="2019-06-01">
   <owner>jonasolsson@chromium.org</owner>
   <owner>maxmorin@chromium.org</owner>
   <owner>olka@chromium.org</owner>
@@ -46522,7 +46539,7 @@
 
 <histogram
     name="Media.Audio.Render.StreamBrokerDocumentDestroyedAwaitingCreatedTime"
-    units="ms" expires_after="2019-02-01">
+    units="ms" expires_after="2019-06-01">
   <owner>olka@chromium.org</owner>
   <owner>marinaciocea@chromium.org</owner>
   <owner>maxmorin@chromium.org</owner>
@@ -46534,7 +46551,7 @@
 </histogram>
 
 <histogram name="Media.Audio.Render.StreamBrokerStreamCreationTime" units="ms"
-    expires_after="2019-02-01">
+    expires_after="2019-06-01">
   <owner>olka@chromium.org</owner>
   <owner>marinaciocea@chromium.org</owner>
   <owner>maxmorin@chromium.org</owner>
@@ -46765,7 +46782,7 @@
 </histogram>
 
 <histogram name="Media.AudioOutputController.LifeTime" units="ms"
-    expires_after="2019-02-01">
+    expires_after="2019-06-01">
   <owner>olka@chromium.org</owner>
   <owner>marinaciocea@chromium.org</owner>
   <owner>maxmorin@chromium.org</owner>
@@ -47320,6 +47337,16 @@
   </summary>
 </histogram>
 
+<histogram name="Media.CodecImage.ImageReaderGLOwner.FrameTimedOut"
+    enum="BooleanTimedOut" expires_after="M76">
+  <owner>ericrk@chromium.org</owner>
+  <owner>liberato@chromium.org</owner>
+  <summary>
+    Whether we exceeded the timeout in ImageReaderGLOwner::
+    WaitForFrameAvailable and will proceed without a frame ready.
+  </summary>
+</histogram>
+
 <histogram name="Media.CodecImage.ImageReaderGLOwner.WaitTimeForFrame"
     units="ms">
   <owner>vikassoni@chromium.org</owner>
@@ -47330,6 +47357,16 @@
   </summary>
 </histogram>
 
+<histogram name="Media.CodecImage.SurfaceTextureGLOwner.FrameTimedOut"
+    enum="BooleanTimedOut" expires_after="M76">
+  <owner>ericrk@chromium.org</owner>
+  <owner>liberato@chromium.org</owner>
+  <summary>
+    Whether we exceeded the timeout in SurfaceTextureGLOwner::
+    WaitForFrameAvailable and will proceed without a frame ready.
+  </summary>
+</histogram>
+
 <histogram name="Media.CodecImage.SurfaceTextureGLOwner.WaitTimeForFrame"
     units="ms">
   <owner>liberato@chromium.org</owner>
@@ -50141,7 +50178,7 @@
 </histogram>
 
 <histogram name="Media.VideoRotation" enum="VideoRotation">
-  <owner>suderman@chromium.org</owner>
+  <owner>suderman@google.com</owner>
   <summary>Metadata rotation in mp4 videos. Emitted during demuxing.</summary>
 </histogram>
 
@@ -62803,7 +62840,7 @@
   <obsolete>
     Deprecated 02/2018. Use Net.QuicSession.PublicResetAddressMismatch2.
   </obsolete>
-  <owner>wtc@chromium.org</owner>
+  <owner>wtc@google.com</owner>
   <summary>
     When a public reset packet is received, whether the client IP address and
     port number in it differ from the client IP address and port number in the
@@ -62816,7 +62853,7 @@
 
 <histogram name="Net.QuicSession.PublicResetAddressMismatch2"
     enum="QuicAddressMismatch">
-  <owner>wtc@chromium.org</owner>
+  <owner>wtc@google.com</owner>
   <summary>
     When a public reset packet is received, whether the client IP address and
     port number in it differ from the client IP address and port number in the
@@ -67780,7 +67817,7 @@
 </histogram>
 
 <histogram name="Network.Shill.Wifi.SignalAtDisconnect" units="negative dBm">
-  <owner>silberst@chromium.org</owner>
+  <owner>silberst@google.com</owner>
   <summary>
     Chrome OS network metric indicating the negative of the dBm received signal
     strength recorded at the time of a WiFi disconnect.
@@ -74593,7 +74630,7 @@
 
 <histogram name="Omnibox.QueryIosLocationAuthorizationStatus"
     enum="IosLocationAuthorizationStatus">
-  <owner>kiyun@chromium.org</owner>
+  <owner>kiyun@google.com</owner>
   <summary>
     For iOS, whether the application is authorized to use location services when
     the user enters a search query into the Omnibox.
@@ -102564,7 +102601,7 @@
 </histogram>
 
 <histogram name="ServiceWorker.FetchEvent.Fallback.Time" units="ms">
-  <owner>jeremyarcher@chromium.org</owner>
+  <owner>jeremyarcher@google.com</owner>
   <summary>
     The time taken between dispatching a FetchEvent to a Service Worker and
     receiving a fallback-to-network reply.
@@ -102572,7 +102609,7 @@
 </histogram>
 
 <histogram name="ServiceWorker.FetchEvent.HasResponse.Time" units="ms">
-  <owner>jeremyarcher@chromium.org</owner>
+  <owner>jeremyarcher@google.com</owner>
   <summary>
     The time taken between dispatching a FetchEvent to a Service Worker and
     receiving a response. Includes the time for the respondWith() promise to
diff --git a/ui/aura/test/mus/test_window_tree.cc b/ui/aura/test/mus/test_window_tree.cc
index 50144e8..0d3fde9b 100644
--- a/ui/aura/test/mus/test_window_tree.cc
+++ b/ui/aura/test/mus/test_window_tree.cc
@@ -340,6 +340,7 @@
                                ws::Id transport_window_id,
                                ui::Cursor cursor) {
   OnChangeReceived(change_id);
+  last_cursor_ = cursor;
 }
 
 void TestWindowTree::SetWindowTextInputState(
diff --git a/ui/aura/test/mus/test_window_tree.h b/ui/aura/test/mus/test_window_tree.h
index 6cd57205..02e56ec1 100644
--- a/ui/aura/test/mus/test_window_tree.h
+++ b/ui/aura/test/mus/test_window_tree.h
@@ -113,6 +113,8 @@
     return last_touch_hit_test_insets_;
   }
 
+  const ui::Cursor& last_cursor() const { return last_cursor_; }
+
   const base::Optional<viz::LocalSurfaceId>& last_local_surface_id() const {
     return last_local_surface_id_;
   }
@@ -294,6 +296,7 @@
 
   ws::mojom::WindowTreeClient* client_ = nullptr;
 
+  ui::Cursor last_cursor_ = ui::CursorType::kNull;
   base::Optional<base::flat_map<std::string, std::vector<uint8_t>>>
       last_new_window_properties_;
 
diff --git a/ui/ozone/platform/drm/gpu/gbm_surfaceless.cc b/ui/ozone/platform/drm/gpu/gbm_surfaceless.cc
index 6ee9357..e359f6ac 100644
--- a/ui/ozone/platform/drm/gpu/gbm_surfaceless.cc
+++ b/ui/ozone/platform/drm/gpu/gbm_surfaceless.cc
@@ -148,12 +148,7 @@
   // TODO: the following should be replaced by a per surface flush as it gets
   // implemented in GL drivers.
   EGLSyncKHR fence = InsertFence(has_implicit_external_sync_);
-  if (!fence) {
-    completion_callback.Run(gfx::SwapResult::SWAP_FAILED, nullptr);
-    // Notify the caller, the buffer is never presented on a screen.
-    presentation_callback.Run(gfx::PresentationFeedback::Failure());
-    return;
-  }
+  CHECK_NE(fence, EGL_NO_SYNC_KHR) << "eglCreateSyncKHR failed";
 
   base::OnceClosure fence_wait_task =
       base::BindOnce(&WaitForFence, GetDisplay(), fence);
diff --git a/ui/ozone/platform/wayland/BUILD.gn b/ui/ozone/platform/wayland/BUILD.gn
index efd5e2d55..39b326f 100644
--- a/ui/ozone/platform/wayland/BUILD.gn
+++ b/ui/ozone/platform/wayland/BUILD.gn
@@ -147,12 +147,75 @@
   ]
 }
 
-source_set("wayland_unittests") {
+source_set("test_support") {
   testonly = true
 
   sources = [
     "fake_server.cc",
     "fake_server.h",
+    "test/constants.h",
+    "test/global_object.cc",
+    "test/global_object.h",
+    "test/mock_buffer.cc",
+    "test/mock_buffer.h",
+    "test/mock_surface.cc",
+    "test/mock_surface.h",
+    "test/mock_xdg_popup.cc",
+    "test/mock_xdg_popup.h",
+    "test/mock_xdg_shell.cc",
+    "test/mock_xdg_shell.h",
+    "test/mock_xdg_surface.cc",
+    "test/mock_xdg_surface.h",
+    "test/mock_zwp_linux_buffer_params.cc",
+    "test/mock_zwp_linux_buffer_params.h",
+    "test/mock_zwp_linux_dmabuf.cc",
+    "test/mock_zwp_linux_dmabuf.h",
+    "test/mock_zwp_text_input.cc",
+    "test/mock_zwp_text_input.h",
+    "test/server_object.cc",
+    "test/server_object.h",
+    "test/test_compositor.cc",
+    "test/test_compositor.h",
+    "test/test_data_device.cc",
+    "test/test_data_device.h",
+    "test/test_data_device_manager.cc",
+    "test/test_data_device_manager.h",
+    "test/test_data_offer.cc",
+    "test/test_data_offer.h",
+    "test/test_data_source.cc",
+    "test/test_data_source.h",
+    "test/test_keyboard.cc",
+    "test/test_keyboard.h",
+    "test/test_output.cc",
+    "test/test_output.h",
+    "test/test_pointer.cc",
+    "test/test_pointer.h",
+    "test/test_positioner.cc",
+    "test/test_positioner.h",
+    "test/test_seat.cc",
+    "test/test_seat.h",
+    "test/test_touch.cc",
+    "test/test_touch.h",
+    "test/test_zwp_text_input_manager.cc",
+    "test/test_zwp_text_input_manager.h",
+  ]
+
+  deps = [
+    "//base:base",
+    "//testing/gmock",
+    "//third_party/wayland:wayland_server",
+    "//third_party/wayland-protocols:linux_dmabuf_protocol",
+    "//third_party/wayland-protocols:text_input_protocol",
+    "//third_party/wayland-protocols:xdg_shell_protocol",
+    "//ui/gfx/geometry:geometry",
+  ]
+}
+
+source_set("wayland_unittests") {
+  testonly = true
+
+  sources = [
+    "wayland_buffer_manager_unittest.cc",
     "wayland_connection_unittest.cc",
     "wayland_data_device_unittest.cc",
     "wayland_input_method_context_unittest.cc",
@@ -167,10 +230,13 @@
   ]
 
   deps = [
+    ":test_support",
     ":wayland",
+    "//build/config/linux/libdrm",
     "//testing/gmock",
     "//testing/gtest",
     "//third_party/wayland:wayland_server",
+    "//third_party/wayland-protocols:linux_dmabuf_protocol",
     "//third_party/wayland-protocols:text_input_protocol",
     "//third_party/wayland-protocols:xdg_shell_protocol",
     "//ui/base",
diff --git a/ui/ozone/platform/wayland/fake_server.cc b/ui/ozone/platform/wayland/fake_server.cc
index 2571181..61410c37 100644
--- a/ui/ozone/platform/wayland/fake_server.cc
+++ b/ui/ozone/platform/wayland/fake_server.cc
@@ -4,1127 +4,18 @@
 
 #include "ui/ozone/platform/wayland/fake_server.h"
 
+#include <stdlib.h>
 #include <sys/socket.h>
-#include <text-input-unstable-v1-server-protocol.h>
 #include <wayland-server.h>
-#include <xdg-shell-unstable-v5-server-protocol.h>
-#include <xdg-shell-unstable-v6-server-protocol.h>
 
 #include "base/bind.h"
 #include "base/files/file_util.h"
 #include "base/files/scoped_file.h"
-#include "base/memory/ptr_util.h"
-#include "base/message_loop/message_loop.h"
-#include "base/posix/eintr_wrapper.h"
 #include "base/run_loop.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/task_runner_util.h"
 
 namespace wl {
-namespace {
-
-constexpr uint32_t kCompositorVersion = 4;
-constexpr uint32_t kOutputVersion = 2;
-constexpr uint32_t kDataDeviceManagerVersion = 3;
-constexpr uint32_t kSeatVersion = 4;
-constexpr uint32_t kTextInputManagerVersion = 1;
-constexpr uint32_t kXdgShellVersion = 1;
-
-bool ResourceHasImplementation(wl_resource* resource,
-                               const wl_interface* interface,
-                               const void* impl) {
-  return wl_resource_instance_of(resource, interface, impl);
-}
-
-template <class T>
-T* GetUserDataAs(wl_resource* resource) {
-  return static_cast<T*>(wl_resource_get_user_data(resource));
-}
-
-template <class T>
-std::unique_ptr<T> TakeUserDataAs(wl_resource* resource) {
-  std::unique_ptr<T> user_data = base::WrapUnique(GetUserDataAs<T>(resource));
-  // Make sure ServerObject doesn't try to destroy the resource twice.
-  ServerObject::OnResourceDestroyed(resource);
-  wl_resource_set_user_data(resource, nullptr);
-  return user_data;
-}
-
-template <class T>
-void DestroyUserData(wl_resource* resource) {
-  TakeUserDataAs<T>(resource);
-}
-
-// TODO(msisov): Move all the callers to use this template implementation set
-// helper with automatic user data destruction.
-template <class T>
-void SetImplementation(wl_resource* resource,
-                       const void* implementation,
-                       std::unique_ptr<T> user_data) {
-  wl_resource_set_implementation(resource, implementation, user_data.release(),
-                                 DestroyUserData<T>);
-}
-
-// Deprecated. Going to be removed.
-template <class T>
-void SetImplementation(wl_resource* resource,
-                       const void* implementation,
-                       T* user_data) {
-  wl_resource_set_implementation(resource, implementation, user_data,
-                                 &ServerObject::OnResourceDestroyed);
-}
-
-void DestroyResource(wl_client* client, wl_resource* resource) {
-  wl_resource_destroy(resource);
-}
-
-void WriteDataOnWorkerThread(base::ScopedFD fd, const std::string& utf8_text) {
-  if (!base::WriteFileDescriptor(fd.get(), utf8_text.data(), utf8_text.size()))
-    LOG(ERROR) << "Failed to write selection data to clipboard.";
-}
-
-std::vector<uint8_t> ReadDataOnWorkerThread(base::ScopedFD fd) {
-  constexpr size_t kChunkSize = 1024;
-  std::vector<uint8_t> bytes;
-  while (true) {
-    uint8_t chunk[kChunkSize];
-    ssize_t bytes_read = HANDLE_EINTR(read(fd.get(), chunk, kChunkSize));
-    if (bytes_read > 0) {
-      bytes.insert(bytes.end(), chunk, chunk + bytes_read);
-      continue;
-    }
-    if (!bytes_read)
-      return bytes;
-    if (bytes_read < 0) {
-      LOG(ERROR) << "Failed to read selection data from clipboard.";
-      return std::vector<uint8_t>();
-    }
-  }
-}
-
-void CreatePipe(base::ScopedFD* read_pipe, base::ScopedFD* write_pipe) {
-  int raw_pipe[2];
-  PCHECK(0 == pipe(raw_pipe));
-  read_pipe->reset(raw_pipe[0]);
-  write_pipe->reset(raw_pipe[1]);
-}
-
-// wl_compositor
-
-void CreateSurface(wl_client* client, wl_resource* resource, uint32_t id) {
-  auto* compositor = GetUserDataAs<MockCompositor>(resource);
-  wl_resource* surface_resource = wl_resource_create(
-      client, &wl_surface_interface, wl_resource_get_version(resource), id);
-  if (!surface_resource) {
-    wl_client_post_no_memory(client);
-    return;
-  }
-  compositor->AddSurface(std::make_unique<MockSurface>(surface_resource));
-}
-
-const struct wl_compositor_interface compositor_impl = {
-    &CreateSurface,  // create_surface
-    nullptr,         // create_region
-};
-
-// wl_surface
-
-void Attach(wl_client* client,
-            wl_resource* resource,
-            wl_resource* buffer_resource,
-            int32_t x,
-            int32_t y) {
-  GetUserDataAs<MockSurface>(resource)->Attach(buffer_resource, x, y);
-}
-
-void Damage(wl_client* client,
-            wl_resource* resource,
-            int32_t x,
-            int32_t y,
-            int32_t width,
-            int32_t height) {
-  GetUserDataAs<MockSurface>(resource)->Damage(x, y, width, height);
-}
-
-void Commit(wl_client* client, wl_resource* resource) {
-  GetUserDataAs<MockSurface>(resource)->Commit();
-}
-
-const struct wl_surface_interface surface_impl = {
-    &DestroyResource,  // destroy
-    &Attach,           // attach
-    &Damage,           // damage
-    nullptr,           // frame
-    nullptr,           // set_opaque_region
-    nullptr,           // set_input_region
-    &Commit,           // commit
-    nullptr,           // set_buffer_transform
-    nullptr,           // set_buffer_scale
-    nullptr,           // damage_buffer
-};
-
-// xdg_shell
-
-void UseUnstableVersion(wl_client* client,
-                        wl_resource* resource,
-                        int32_t version) {
-  GetUserDataAs<MockXdgShell>(resource)->UseUnstableVersion(version);
-}
-
-// xdg_shell and zxdg_shell_v6
-
-void Pong(wl_client* client, wl_resource* resource, uint32_t serial) {
-  GetUserDataAs<MockXdgShell>(resource)->Pong(serial);
-}
-
-// xdg_shell
-
-void GetXdgSurfaceV5(wl_client* client,
-                     wl_resource* resource,
-                     uint32_t id,
-                     wl_resource* surface_resource);
-
-void GetXdgPopupV5(struct wl_client* client,
-                   struct wl_resource* resource,
-                   uint32_t id,
-                   struct wl_resource* surface,
-                   struct wl_resource* parent,
-                   struct wl_resource* seat,
-                   uint32_t serial,
-                   int32_t x,
-                   int32_t y);
-
-const struct xdg_shell_interface xdg_shell_impl = {
-    &DestroyResource,     // destroy
-    &UseUnstableVersion,  // use_unstable_version
-    &GetXdgSurfaceV5,     // get_xdg_surface
-    &GetXdgPopupV5,       // get_xdg_popup
-    &Pong,                // pong
-};
-
-// zxdg_shell_v6
-
-void GetXdgSurfaceV6(wl_client* client,
-                     wl_resource* resource,
-                     uint32_t id,
-                     wl_resource* surface_resource);
-
-void CreatePositioner(wl_client* client,
-                      struct wl_resource* resource,
-                      uint32_t id);
-
-const struct zxdg_shell_v6_interface zxdg_shell_v6_impl = {
-    &DestroyResource,   // destroy
-    &CreatePositioner,  // create_positioner
-    &GetXdgSurfaceV6,   // get_xdg_surface
-    &Pong,              // pong
-};
-
-// zxdg_positioner_v6
-
-void SetSize(struct wl_client* wl_client,
-             struct wl_resource* resource,
-             int32_t width,
-             int32_t height) {
-  if (width < 1 || height < 1) {
-    wl_resource_post_error(resource, ZXDG_POSITIONER_V6_ERROR_INVALID_INPUT,
-                           "width and height must be positive and non-zero");
-    return;
-  }
-
-  GetUserDataAs<MockPositioner>(resource)->set_size(gfx::Size(width, height));
-}
-
-void SetAnchorRect(struct wl_client* client,
-                   struct wl_resource* resource,
-                   int32_t x,
-                   int32_t y,
-                   int32_t width,
-                   int32_t height) {
-  if (width < 1 || height < 1) {
-    wl_resource_post_error(resource, ZXDG_POSITIONER_V6_ERROR_INVALID_INPUT,
-                           "width and height must be positive and non-zero");
-    return;
-  }
-
-  GetUserDataAs<MockPositioner>(resource)->set_anchor_rect(
-      gfx::Rect(x, y, width, height));
-}
-
-void SetAnchor(struct wl_client* wl_client,
-               struct wl_resource* resource,
-               uint32_t anchor) {
-  if (((anchor & ZXDG_POSITIONER_V6_ANCHOR_LEFT) &&
-       (anchor & ZXDG_POSITIONER_V6_ANCHOR_RIGHT)) ||
-      ((anchor & ZXDG_POSITIONER_V6_ANCHOR_TOP) &&
-       (anchor & ZXDG_POSITIONER_V6_ANCHOR_BOTTOM))) {
-    wl_resource_post_error(resource, ZXDG_POSITIONER_V6_ERROR_INVALID_INPUT,
-                           "same-axis values are not allowed");
-    return;
-  }
-
-  GetUserDataAs<MockPositioner>(resource)->set_anchor(anchor);
-}
-
-void SetGravity(struct wl_client* client,
-                struct wl_resource* resource,
-                uint32_t gravity) {
-  if (((gravity & ZXDG_POSITIONER_V6_GRAVITY_LEFT) &&
-       (gravity & ZXDG_POSITIONER_V6_GRAVITY_RIGHT)) ||
-      ((gravity & ZXDG_POSITIONER_V6_GRAVITY_TOP) &&
-       (gravity & ZXDG_POSITIONER_V6_GRAVITY_BOTTOM))) {
-    wl_resource_post_error(resource, ZXDG_POSITIONER_V6_ERROR_INVALID_INPUT,
-                           "same-axis values are not allowed");
-    return;
-  }
-
-  GetUserDataAs<MockPositioner>(resource)->set_gravity(gravity);
-}
-
-const struct zxdg_positioner_v6_interface zxdg_positioner_v6_impl = {
-    &DestroyResource,  // destroy
-    &SetSize,          // set_size
-    &SetAnchorRect,    // set_anchor_rect
-    &SetAnchor,        // set_anchor
-    &SetGravity,       // set_gravity
-    nullptr,           // set_constraint_adjustment
-    nullptr,           // set_offset
-};
-
-// wl_data_device
-
-void DataDeviceStartDrag(wl_client* client,
-                         wl_resource* resource,
-                         wl_resource* source,
-                         wl_resource* origin,
-                         wl_resource* icon,
-                         uint32_t serial) {
-  NOTIMPLEMENTED();
-}
-
-void DataDeviceSetSelection(wl_client* client,
-                            wl_resource* resource,
-                            wl_resource* data_source,
-                            uint32_t serial) {
-  GetUserDataAs<MockDataDevice>(resource)->SetSelection(
-      data_source ? GetUserDataAs<MockDataSource>(data_source) : nullptr,
-      serial);
-}
-
-void DataDeviceRelease(wl_client* client, wl_resource* resource) {
-  wl_resource_destroy(resource);
-}
-
-const struct wl_data_device_interface data_device_impl = {
-    &DataDeviceStartDrag, &DataDeviceSetSelection, &DataDeviceRelease};
-
-// wl_data_device_manager
-
-void CreateDataSource(wl_client* client, wl_resource* resource, uint32_t id) {
-  wl_resource* data_source_resource = wl_resource_create(
-      client, &wl_data_source_interface, wl_resource_get_version(resource), id);
-  if (!data_source_resource) {
-    wl_client_post_no_memory(client);
-    return;
-  }
-
-  std::unique_ptr<MockDataSource> data_source(
-      new MockDataSource(data_source_resource));
-
-  auto* data_device_manager = GetUserDataAs<MockDataDeviceManager>(resource);
-  data_device_manager->set_data_source(std::move(data_source));
-}
-
-void GetDataDevice(wl_client* client,
-                   wl_resource* resource,
-                   uint32_t id,
-                   wl_resource* seat_resource) {
-  wl_resource* data_device_resource = wl_resource_create(
-      client, &wl_data_device_interface, wl_resource_get_version(resource), id);
-  if (!data_device_resource) {
-    wl_client_post_no_memory(client);
-    return;
-  }
-
-  std::unique_ptr<MockDataDevice> data_device(
-      new MockDataDevice(client, data_device_resource));
-
-  auto* data_device_manager = GetUserDataAs<MockDataDeviceManager>(resource);
-  data_device_manager->set_data_device(std::move(data_device));
-}
-
-const struct wl_data_device_manager_interface data_device_manager_impl = {
-    &CreateDataSource, &GetDataDevice};
-
-// wl_data_offer
-
-void DataOfferAccept(wl_client* client,
-                     wl_resource* resource,
-                     uint32_t serial,
-                     const char* mime_type) {
-  NOTIMPLEMENTED();
-}
-
-void DataOfferReceive(wl_client* client,
-                      wl_resource* resource,
-                      const char* mime_type,
-                      int fd) {
-  GetUserDataAs<MockDataOffer>(resource)->Receive(mime_type,
-                                                  base::ScopedFD(fd));
-}
-
-void DataOfferDestroy(wl_client* client, wl_resource* resource) {
-  wl_resource_destroy(resource);
-}
-
-void DataOfferFinish(wl_client* client, wl_resource* resource) {
-  NOTIMPLEMENTED();
-}
-
-void DataOfferSetActions(wl_client* client,
-                         wl_resource* resource,
-                         uint32_t dnd_actions,
-                         uint32_t preferred_action) {
-  NOTIMPLEMENTED();
-}
-
-const struct wl_data_offer_interface data_offer_impl = {
-    DataOfferAccept, DataOfferReceive, DataOfferDestroy, DataOfferFinish,
-    DataOfferSetActions};
-
-// wl_data_source
-
-void DataSourceOffer(wl_client* client,
-                     wl_resource* resource,
-                     const char* mime_type) {
-  GetUserDataAs<MockDataSource>(resource)->Offer(mime_type);
-}
-
-void DataSourceDestroy(wl_client* client, wl_resource* resource) {
-  wl_resource_destroy(resource);
-}
-
-void SetActions(wl_client* client,
-                wl_resource* resource,
-                uint32_t dnd_actions) {
-  NOTIMPLEMENTED();
-}
-
-const struct wl_data_source_interface data_source_impl = {
-    DataSourceOffer, DataSourceDestroy, SetActions};
-
-// wl_seat
-
-void GetPointer(wl_client* client, wl_resource* resource, uint32_t id) {
-  wl_resource* pointer_resource = wl_resource_create(
-      client, &wl_pointer_interface, wl_resource_get_version(resource), id);
-  if (!pointer_resource) {
-    wl_client_post_no_memory(client);
-    return;
-  }
-  auto* seat = GetUserDataAs<MockSeat>(resource);
-  seat->set_pointer(std::make_unique<MockPointer>(pointer_resource));
-}
-
-void GetKeyboard(wl_client* client, wl_resource* resource, uint32_t id) {
-  wl_resource* keyboard_resource = wl_resource_create(
-      client, &wl_keyboard_interface, wl_resource_get_version(resource), id);
-  if (!keyboard_resource) {
-    wl_client_post_no_memory(client);
-    return;
-  }
-  auto* seat = GetUserDataAs<MockSeat>(resource);
-  seat->set_keyboard(std::make_unique<MockKeyboard>(keyboard_resource));
-}
-
-void GetTouch(wl_client* client, wl_resource* resource, uint32_t id) {
-  wl_resource* touch_resource = wl_resource_create(
-      client, &wl_touch_interface, wl_resource_get_version(resource), id);
-  if (!touch_resource) {
-    wl_client_post_no_memory(client);
-    return;
-  }
-  auto* seat = GetUserDataAs<MockSeat>(resource);
-  seat->set_touch(std::make_unique<MockTouch>(touch_resource));
-}
-
-const struct wl_seat_interface seat_impl = {
-    &GetPointer,       // get_pointer
-    &GetKeyboard,      // get_keyboard
-    &GetTouch,         // get_touch,
-    &DestroyResource,  // release
-};
-
-// wl_keyboard
-
-const struct wl_keyboard_interface keyboard_impl = {
-    &DestroyResource,  // release
-};
-
-// wl_pointer
-
-const struct wl_pointer_interface pointer_impl = {
-    nullptr,           // set_cursor
-    &DestroyResource,  // release
-};
-
-// wl_touch
-
-const struct wl_touch_interface touch_impl = {
-    &DestroyResource,  // release
-};
-
-// zwp_text_input_v1
-
-void TextInputV1Activate(wl_client* client,
-                         wl_resource* resource,
-                         wl_resource* seat,
-                         wl_resource* surface) {
-  static_cast<MockZwpTextInput*>(wl_resource_get_user_data(resource))
-      ->Activate(surface);
-}
-
-void TextInputV1Deactivate(wl_client* client,
-                           wl_resource* resource,
-                           wl_resource* seat) {
-  static_cast<MockZwpTextInput*>(wl_resource_get_user_data(resource))
-      ->Deactivate();
-}
-
-void TextInputV1ShowInputPanel(wl_client* client, wl_resource* resource) {
-  static_cast<MockZwpTextInput*>(wl_resource_get_user_data(resource))
-      ->ShowInputPanel();
-}
-
-void TextInputV1HideInputPanel(wl_client* client, wl_resource* resource) {
-  static_cast<MockZwpTextInput*>(wl_resource_get_user_data(resource))
-      ->HideInputPanel();
-}
-
-void TextInputV1Reset(wl_client* client, wl_resource* resource) {
-  static_cast<MockZwpTextInput*>(wl_resource_get_user_data(resource))->Reset();
-}
-
-void TextInputV1SetCursorRectangle(wl_client* client,
-                                   wl_resource* resource,
-                                   int32_t x,
-                                   int32_t y,
-                                   int32_t width,
-                                   int32_t height) {
-  static_cast<MockZwpTextInput*>(wl_resource_get_user_data(resource))
-      ->SetCursorRect(x, y, width, height);
-}
-
-const struct zwp_text_input_v1_interface zwp_text_input_v1_impl = {
-    &TextInputV1Activate,            // activate
-    &TextInputV1Deactivate,          // deactivate
-    &TextInputV1ShowInputPanel,      // show_input_panel
-    &TextInputV1HideInputPanel,      // hide_input_panel
-    &TextInputV1Reset,               // reset
-    nullptr,                         // set_surrounding_text
-    nullptr,                         // set_content_type
-    &TextInputV1SetCursorRectangle,  // set_cursor_rectangle
-    nullptr,                         // set_preferred_language
-    nullptr,                         // commit_state
-    nullptr,                         // invoke_action
-};
-
-// zwp_text_input_manager_v1
-
-void CreateTextInput(struct wl_client* client,
-                     struct wl_resource* resource,
-                     uint32_t id) {
-  auto* im =
-      static_cast<MockTextInputManagerV1*>(wl_resource_get_user_data(resource));
-  wl_resource* text_resource =
-      wl_resource_create(client, &zwp_text_input_v1_interface,
-                         wl_resource_get_version(resource), id);
-  if (!text_resource) {
-    wl_client_post_no_memory(client);
-    return;
-  }
-  im->text_input.reset(
-      new MockZwpTextInput(text_resource, &zwp_text_input_v1_impl));
-}
-
-const struct zwp_text_input_manager_v1_interface
-    zwp_text_input_manager_v1_impl = {
-        &CreateTextInput,  // create_text_input
-};
-
-// xdg_surface, zxdg_surface_v6 and zxdg_toplevel shared methods.
-
-void SetTitle(wl_client* client, wl_resource* resource, const char* title) {
-  GetUserDataAs<MockXdgSurface>(resource)->SetTitle(title);
-}
-
-void SetAppId(wl_client* client, wl_resource* resource, const char* app_id) {
-  GetUserDataAs<MockXdgSurface>(resource)->SetAppId(app_id);
-}
-
-void Move(wl_client* client,
-          wl_resource* resource,
-          wl_resource* seat,
-          uint32_t serial) {
-  GetUserDataAs<MockXdgSurface>(resource)->Move(serial);
-}
-
-void Resize(wl_client* client,
-            wl_resource* resource,
-            wl_resource* seat,
-            uint32_t serial,
-            uint32_t edges) {
-  GetUserDataAs<MockXdgSurface>(resource)->Resize(serial, edges);
-}
-
-void AckConfigure(wl_client* client, wl_resource* resource, uint32_t serial) {
-  GetUserDataAs<MockXdgSurface>(resource)->AckConfigure(serial);
-}
-
-void SetWindowGeometry(wl_client* client,
-                       wl_resource* resource,
-                       int32_t x,
-                       int32_t y,
-                       int32_t width,
-                       int32_t height) {
-  GetUserDataAs<MockXdgSurface>(resource)->SetWindowGeometry(x, y, width,
-                                                             height);
-}
-
-void SetMaximized(wl_client* client, wl_resource* resource) {
-  GetUserDataAs<MockXdgSurface>(resource)->SetMaximized();
-}
-
-void UnsetMaximized(wl_client* client, wl_resource* resource) {
-  GetUserDataAs<MockXdgSurface>(resource)->UnsetMaximized();
-}
-
-void SetFullscreen(wl_client* client,
-                   wl_resource* resource,
-                   wl_resource* output) {
-  GetUserDataAs<MockXdgSurface>(resource)->SetFullscreen();
-}
-
-void UnsetFullscreen(wl_client* client, wl_resource* resource) {
-  GetUserDataAs<MockXdgSurface>(resource)->UnsetFullscreen();
-}
-
-void SetMinimized(wl_client* client, wl_resource* resource) {
-  GetUserDataAs<MockXdgSurface>(resource)->SetMinimized();
-}
-
-const struct xdg_surface_interface xdg_surface_impl = {
-    &DestroyResource,    // destroy
-    nullptr,             // set_parent
-    &SetTitle,           // set_title
-    &SetAppId,           // set_app_id
-    nullptr,             // show_window_menu
-    &Move,               // move
-    &Resize,             // resize
-    &AckConfigure,       // ack_configure
-    &SetWindowGeometry,  // set_window_geometry
-    &SetMaximized,       // set_maximized
-    &UnsetMaximized,     // set_unmaximized
-    &SetFullscreen,      // set_fullscreen
-    &UnsetFullscreen,    // unset_fullscreen
-    &SetMinimized,       // set_minimized
-};
-
-// xdg_popup_v5
-
-const struct xdg_popup_interface xdg_popup_impl = {
-    &DestroyResource,  // destroy
-};
-
-// zxdg_popup_v6
-
-void Grab(struct wl_client* client,
-          struct wl_resource* resource,
-          struct wl_resource* seat,
-          uint32_t serial) {
-  GetUserDataAs<MockXdgPopup>(resource)->Grab(serial);
-}
-
-const struct zxdg_popup_v6_interface zxdg_popup_v6_impl = {
-    &DestroyResource,  // destroy
-    &Grab,             // grab
-};
-
-// zxdg_surface specific interface
-
-void GetTopLevel(wl_client* client, wl_resource* resource, uint32_t id) {
-  auto* surface = GetUserDataAs<MockXdgSurface>(resource);
-  if (surface->xdg_toplevel()) {
-    wl_resource_post_error(resource, ZXDG_SURFACE_V6_ERROR_ALREADY_CONSTRUCTED,
-                           "surface has already been constructed");
-    return;
-  }
-  wl_resource* xdg_toplevel_resource =
-      wl_resource_create(client, &zxdg_toplevel_v6_interface, 1, id);
-  if (!xdg_toplevel_resource) {
-    wl_client_post_no_memory(client);
-    return;
-  }
-  surface->set_xdg_toplevel(
-      std::make_unique<MockXdgTopLevel>(xdg_toplevel_resource));
-}
-
-void GetZXdgPopupV6(struct wl_client* client,
-                    struct wl_resource* resource,
-                    uint32_t id,
-                    struct wl_resource* parent,
-                    struct wl_resource* positioner) {
-  auto* mock_xdg_surface = GetUserDataAs<MockXdgSurface>(resource);
-  wl_resource* current_resource = mock_xdg_surface->resource();
-  if (current_resource &&
-      (ResourceHasImplementation(current_resource, &zxdg_popup_v6_interface,
-                                 &zxdg_popup_v6_impl) ||
-       ResourceHasImplementation(current_resource,
-                                 &zxdg_positioner_v6_interface,
-                                 &zxdg_positioner_v6_impl))) {
-    wl_resource_post_error(resource, ZXDG_SURFACE_V6_ERROR_ALREADY_CONSTRUCTED,
-                           "surface has already been constructed");
-    return;
-  }
-
-  auto* mock_positioner = GetUserDataAs<MockPositioner>(positioner);
-  if (mock_positioner->size().width() == 0 ||
-      mock_positioner->anchor_rect().width() == 0) {
-    wl_resource_post_error(resource, ZXDG_SHELL_V6_ERROR_INVALID_POSITIONER,
-                           "Positioner object is not complete");
-    return;
-  }
-
-  wl_resource* popup_resource = wl_resource_create(
-      client, &zxdg_popup_v6_interface, wl_resource_get_version(resource), id);
-  if (!popup_resource) {
-    wl_client_post_no_memory(client);
-    return;
-  }
-
-  SetImplementation(
-      popup_resource, &zxdg_popup_v6_impl,
-      std::make_unique<MockXdgPopup>(popup_resource, &zxdg_popup_v6_impl));
-}
-
-const struct zxdg_surface_v6_interface zxdg_surface_v6_impl = {
-    &DestroyResource,  // destroy
-    &GetTopLevel,      // get_toplevel
-    &GetZXdgPopupV6,   // get_popup
-
-    &SetWindowGeometry,  // set_window_geometry
-    &AckConfigure,       // ack_configure
-};
-
-const struct zxdg_toplevel_v6_interface zxdg_toplevel_v6_impl = {
-    &DestroyResource,  // destroy
-    nullptr,           // set_parent
-    &SetTitle,         // set_title
-    &SetAppId,         // set_app_id
-    nullptr,           // show_window_menu
-    &Move,             // move
-    &Resize,           // resize
-    nullptr,           // set_max_size
-    nullptr,           // set_min_size
-    &SetMaximized,     // set_maximized
-    &UnsetMaximized,   // set_unmaximized
-    &SetFullscreen,    // set_fullscreen
-    &UnsetFullscreen,  // unset_fullscreen
-    &SetMinimized,     // set_minimized
-};
-
-void GetXdgSurfaceImpl(wl_client* client,
-                       wl_resource* resource,
-                       uint32_t id,
-                       wl_resource* surface_resource,
-                       const struct wl_interface* interface,
-                       const void* implementation) {
-  auto* surface = GetUserDataAs<MockSurface>(surface_resource);
-  if (surface->xdg_surface()) {
-    uint32_t xdg_error = implementation == &xdg_surface_impl
-                             ? XDG_SHELL_ERROR_ROLE
-                             : ZXDG_SHELL_V6_ERROR_ROLE;
-    wl_resource_post_error(resource, xdg_error, "surface already has a role");
-    return;
-  }
-  wl_resource* xdg_surface_resource = wl_resource_create(
-      client, interface, wl_resource_get_version(resource), id);
-
-  if (!xdg_surface_resource) {
-    wl_client_post_no_memory(client);
-    return;
-  }
-  surface->set_xdg_surface(
-      std::make_unique<MockXdgSurface>(xdg_surface_resource, implementation));
-}
-
-// xdg_shell
-
-void GetXdgSurfaceV5(wl_client* client,
-                     wl_resource* resource,
-                     uint32_t id,
-                     wl_resource* surface_resource) {
-  GetXdgSurfaceImpl(client, resource, id, surface_resource,
-                    &xdg_surface_interface, &xdg_surface_impl);
-}
-
-void GetXdgPopupV5(struct wl_client* client,
-                   struct wl_resource* resource,
-                   uint32_t id,
-                   struct wl_resource* surface,
-                   struct wl_resource* parent,
-                   struct wl_resource* seat,
-                   uint32_t serial,
-                   int32_t x,
-                   int32_t y) {
-  auto* mock_surface = GetUserDataAs<MockSurface>(surface);
-  if (mock_surface->resource() &&
-      ResourceHasImplementation(mock_surface->resource(), &xdg_popup_interface,
-                                &xdg_popup_impl)) {
-    wl_resource_post_error(resource, XDG_SHELL_ERROR_ROLE,
-                           "surface has already assigned a role");
-    return;
-  }
-
-  wl_resource* popup_resource = wl_resource_create(
-      client, &xdg_popup_interface, wl_resource_get_version(resource), id);
-  if (!popup_resource) {
-    wl_client_post_no_memory(client);
-    return;
-  }
-
-  SetImplementation(
-      popup_resource, &xdg_popup_impl,
-      std::make_unique<MockXdgPopup>(popup_resource, &xdg_popup_impl));
-}
-
-// zxdg_shell_v6
-
-void GetXdgSurfaceV6(wl_client* client,
-                     wl_resource* resource,
-                     uint32_t id,
-                     wl_resource* surface_resource) {
-  GetXdgSurfaceImpl(client, resource, id, surface_resource,
-                    &zxdg_surface_v6_interface, &zxdg_surface_v6_impl);
-}
-
-void CreatePositioner(wl_client* client,
-                      struct wl_resource* resource,
-                      uint32_t id) {
-  wl_resource* positioner_resource =
-      wl_resource_create(client, &zxdg_positioner_v6_interface,
-                         wl_resource_get_version(resource), id);
-  if (!positioner_resource) {
-    wl_client_post_no_memory(client);
-    return;
-  }
-
-  SetImplementation(positioner_resource, &zxdg_positioner_v6_impl,
-                    std::make_unique<MockPositioner>(positioner_resource));
-}
-
-}  // namespace
-
-ServerObject::ServerObject(wl_resource* resource) : resource_(resource) {}
-
-ServerObject::~ServerObject() {
-  if (resource_)
-    wl_resource_destroy(resource_);
-}
-
-// static
-void ServerObject::OnResourceDestroyed(wl_resource* resource) {
-  auto* obj = GetUserDataAs<ServerObject>(resource);
-  obj->resource_ = nullptr;
-}
-
-MockXdgSurface::MockXdgSurface(wl_resource* resource,
-                               const void* implementation)
-    : ServerObject(resource) {
-  SetImplementation(resource, implementation, this);
-}
-
-MockXdgSurface::~MockXdgSurface() {}
-
-MockXdgTopLevel::MockXdgTopLevel(wl_resource* resource)
-    : MockXdgSurface(resource, &zxdg_surface_v6_impl) {
-  SetImplementation(resource, &zxdg_toplevel_v6_impl, this);
-}
-
-MockXdgTopLevel::~MockXdgTopLevel() {}
-
-MockPositioner::MockPositioner(wl_resource* resource)
-    : ServerObject(resource) {}
-
-MockPositioner::~MockPositioner() {}
-
-MockXdgPopup::MockXdgPopup(wl_resource* resource, const void* implementation)
-    : ServerObject(resource) {}
-
-MockXdgPopup::~MockXdgPopup() {}
-
-MockSurface::MockSurface(wl_resource* resource) : ServerObject(resource) {
-  SetImplementation(resource, &surface_impl, this);
-}
-
-MockSurface::~MockSurface() {
-  if (xdg_surface_ && xdg_surface_->resource())
-    wl_resource_destroy(xdg_surface_->resource());
-}
-
-MockSurface* MockSurface::FromResource(wl_resource* resource) {
-  if (!wl_resource_instance_of(resource, &wl_surface_interface, &surface_impl))
-    return nullptr;
-  return GetUserDataAs<MockSurface>(resource);
-}
-
-MockPointer::MockPointer(wl_resource* resource) : ServerObject(resource) {
-  SetImplementation(resource, &pointer_impl, this);
-}
-
-MockPointer::~MockPointer() {}
-
-MockKeyboard::MockKeyboard(wl_resource* resource) : ServerObject(resource) {
-  SetImplementation(resource, &keyboard_impl, this);
-}
-
-MockKeyboard::~MockKeyboard() {}
-
-MockTouch::MockTouch(wl_resource* resource) : ServerObject(resource) {
-  SetImplementation(resource, &touch_impl, this);
-}
-
-MockTouch::~MockTouch() {}
-
-MockZwpTextInput::MockZwpTextInput(wl_resource* resource,
-                                   const void* implementation)
-    : ServerObject(resource) {
-  wl_resource_set_implementation(resource, implementation, this,
-                                 &ServerObject::OnResourceDestroyed);
-}
-
-MockZwpTextInput::~MockZwpTextInput() {}
-
-MockDataOffer::MockDataOffer(wl_resource* resource)
-    : ServerObject(resource),
-      io_thread_("Worker thread"),
-      write_data_weak_ptr_factory_(this) {
-  SetImplementation(resource, &data_offer_impl, this);
-
-  base::Thread::Options options;
-  options.message_loop_type = base::MessageLoop::TYPE_IO;
-  io_thread_.StartWithOptions(options);
-}
-
-MockDataOffer::~MockDataOffer() {}
-
-void MockDataOffer::Receive(const std::string& mime_type, base::ScopedFD fd) {
-  DCHECK(fd.is_valid());
-  std::string text_data;
-  if (mime_type == kTextMimeTypeUtf8)
-    text_data = kSampleClipboardText;
-  else if (mime_type == kTextMimeTypeText)
-    text_data = kSampleTextForDragAndDrop;
-
-  io_thread_.task_runner()->PostTask(
-      FROM_HERE,
-      base::BindOnce(&WriteDataOnWorkerThread, std::move(fd), text_data));
-}
-
-void MockDataOffer::OnOffer(const std::string& mime_type) {
-  wl_data_offer_send_offer(resource(), mime_type.c_str());
-}
-
-MockDataDevice::MockDataDevice(wl_client* client, wl_resource* resource)
-    : ServerObject(resource), client_(client) {
-  SetImplementation(resource, &data_device_impl, this);
-}
-
-MockDataDevice::~MockDataDevice() {}
-
-void MockDataDevice::SetSelection(MockDataSource* data_source,
-                                  uint32_t serial) {
-  NOTIMPLEMENTED();
-}
-
-MockDataOffer* MockDataDevice::OnDataOffer() {
-  wl_resource* data_offer_resource =
-      wl_resource_create(client_, &wl_data_offer_interface,
-                         wl_resource_get_version(resource()), 0);
-  data_offer_.reset(new MockDataOffer(data_offer_resource));
-  wl_data_device_send_data_offer(resource(), data_offer_resource);
-
-  return GetUserDataAs<MockDataOffer>(data_offer_resource);
-}
-
-void MockDataDevice::OnEnter(uint32_t serial,
-                             wl_resource* surface,
-                             wl_fixed_t x,
-                             wl_fixed_t y,
-                             MockDataOffer& data_offer) {
-  wl_data_device_send_enter(resource(), serial, surface, x, y,
-                            data_offer.resource());
-}
-
-void MockDataDevice::OnLeave() {
-  wl_data_device_send_leave(resource());
-}
-
-void MockDataDevice::OnMotion(uint32_t time, wl_fixed_t x, wl_fixed_t y) {
-  wl_data_device_send_motion(resource(), time, x, y);
-}
-
-void MockDataDevice::OnDrop() {
-  wl_data_device_send_drop(resource());
-}
-
-void MockDataDevice::OnSelection(MockDataOffer& data_offer) {
-  wl_data_device_send_selection(resource(), data_offer.resource());
-}
-
-MockDataSource::MockDataSource(wl_resource* resource)
-    : ServerObject(resource),
-      io_thread_("Worker thread"),
-      read_data_weak_ptr_factory_(this) {
-  SetImplementation(resource, &data_source_impl, this);
-
-  base::Thread::Options options;
-  options.message_loop_type = base::MessageLoop::TYPE_IO;
-  io_thread_.StartWithOptions(options);
-}
-
-MockDataSource::~MockDataSource() {}
-
-void MockDataSource::Offer(const std::string& mime_type) {
-  NOTIMPLEMENTED();
-}
-
-void MockDataSource::ReadData(ReadDataCallback callback) {
-  base::ScopedFD read_fd;
-  base::ScopedFD write_fd;
-  CreatePipe(&read_fd, &write_fd);
-
-  wl_data_source_send_send(resource(), kTextMimeTypeUtf8, write_fd.get());
-
-  base::PostTaskAndReplyWithResult(
-      io_thread_.task_runner().get(), FROM_HERE,
-      base::BindOnce(&ReadDataOnWorkerThread, std::move(read_fd)),
-      base::BindOnce(&MockDataSource::DataReadCb,
-                     read_data_weak_ptr_factory_.GetWeakPtr(),
-                     std::move(callback)));
-}
-
-void MockDataSource::DataReadCb(ReadDataCallback callback,
-                                const std::vector<uint8_t>& data) {
-  std::move(callback).Run(data);
-}
-
-void MockDataSource::OnCancelled() {
-  wl_data_source_send_cancelled(resource());
-}
-
-void GlobalDeleter::operator()(wl_global* global) {
-  wl_global_destroy(global);
-}
-
-Global::Global(const wl_interface* interface,
-               const void* implementation,
-               uint32_t version)
-    : interface_(interface),
-      implementation_(implementation),
-      version_(version) {}
-
-Global::~Global() {}
-
-bool Global::Initialize(wl_display* display) {
-  global_.reset(wl_global_create(display, interface_, version_, this, &Bind));
-  return global_ != nullptr;
-}
-
-void Global::DestroyGlobal() {
-  global_.reset();
-}
-
-// static
-void Global::Bind(wl_client* client,
-                  void* data,
-                  uint32_t version,
-                  uint32_t id) {
-  auto* global = static_cast<Global*>(data);
-  wl_resource* resource = wl_resource_create(
-      client, global->interface_, std::min(version, global->version_), id);
-  if (!resource) {
-    wl_client_post_no_memory(client);
-    return;
-  }
-  if (!global->resource_)
-    global->resource_ = resource;
-  SetImplementation(resource, global->implementation_, global);
-  global->OnBind();
-}
-
-// static
-void Global::OnResourceDestroyed(wl_resource* resource) {
-  auto* global = GetUserDataAs<Global>(resource);
-  if (global->resource_ == resource)
-    global->resource_ = nullptr;
-}
-
-MockCompositor::MockCompositor()
-    : Global(&wl_compositor_interface, &compositor_impl, kCompositorVersion) {}
-
-MockCompositor::~MockCompositor() {}
-
-void MockCompositor::AddSurface(std::unique_ptr<MockSurface> surface) {
-  surfaces_.push_back(std::move(surface));
-}
-
-MockDataDeviceManager::MockDataDeviceManager()
-    : Global(&wl_data_device_manager_interface,
-             &data_device_manager_impl,
-             kDataDeviceManagerVersion) {}
-
-MockDataDeviceManager::~MockDataDeviceManager() {}
-
-MockOutput::MockOutput()
-    : Global(&wl_output_interface, nullptr, kOutputVersion) {}
-
-MockOutput::~MockOutput() {}
-
-// Notify clients of the change for output position.
-void MockOutput::OnBind() {
-  if (rect_.IsEmpty())
-    return;
-
-  const char* kUnknownMake = "unknown";
-  const char* kUnknownModel = "unknown";
-  wl_output_send_geometry(resource(), rect_.x(), rect_.y(), 0, 0, 0,
-                          kUnknownMake, kUnknownModel, 0);
-  wl_output_send_mode(resource(), WL_OUTPUT_MODE_CURRENT, rect_.width(),
-                      rect_.height(), 0);
-  wl_output_send_done(resource());
-}
-
-MockSeat::MockSeat() : Global(&wl_seat_interface, &seat_impl, kSeatVersion) {}
-
-MockSeat::~MockSeat() {}
-
-MockXdgShell::MockXdgShell()
-    : Global(&xdg_shell_interface, &xdg_shell_impl, kXdgShellVersion) {}
-
-MockXdgShell::~MockXdgShell() {}
-
-MockXdgShellV6::MockXdgShellV6()
-    : Global(&zxdg_shell_v6_interface, &zxdg_shell_v6_impl, kXdgShellVersion) {}
-
-MockXdgShellV6::~MockXdgShellV6() {}
-
-MockTextInputManagerV1::MockTextInputManagerV1()
-    : Global(&zwp_text_input_manager_v1_interface,
-             &zwp_text_input_manager_v1_impl,
-             kTextInputManagerVersion) {}
-
-MockTextInputManagerV1::~MockTextInputManagerV1() {}
 
 void DisplayDeleter::operator()(wl_display* display) {
   wl_display_destroy(display);
@@ -1178,6 +69,8 @@
   }
   if (!zwp_text_input_manager_v1_.Initialize(display_.get()))
     return false;
+  if (!zwp_linux_dmabuf_v1_.Initialize(display_.get()))
+    return false;
 
   client_ = wl_client_create(display_.get(), server_fd.get());
   if (!client_)
@@ -1214,7 +107,7 @@
 }
 
 std::unique_ptr<base::MessagePump> FakeServer::CreateMessagePump() {
-  auto pump = base::WrapUnique(new base::MessagePumpLibevent);
+  auto pump = std::make_unique<base::MessagePumpLibevent>();
   pump->WatchFileDescriptor(wl_event_loop_get_fd(event_loop_), true,
                             base::MessagePumpLibevent::WATCH_READ, &controller_,
                             this);
diff --git a/ui/ozone/platform/wayland/fake_server.h b/ui/ozone/platform/wayland/fake_server.h
index 7af277b..11a4ebe0 100644
--- a/ui/ozone/platform/wayland/fake_server.h
+++ b/ui/ozone/platform/wayland/fake_server.h
@@ -5,430 +5,30 @@
 #ifndef UI_OZONE_PLATFORM_WAYLAND_FAKE_SERVER_H_
 #define UI_OZONE_PLATFORM_WAYLAND_FAKE_SERVER_H_
 
+#include <memory>
+#include <vector>
+
 #include <wayland-server-core.h>
 
-#include "base/bind.h"
-#include "base/files/scoped_file.h"
 #include "base/message_loop/message_pump_libevent.h"
 #include "base/synchronization/waitable_event.h"
 #include "base/threading/thread.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "ui/gfx/geometry/rect.h"
+#include "ui/ozone/platform/wayland/test/global_object.h"
+#include "ui/ozone/platform/wayland/test/mock_xdg_shell.h"
+#include "ui/ozone/platform/wayland/test/mock_zwp_linux_dmabuf.h"
+#include "ui/ozone/platform/wayland/test/test_compositor.h"
+#include "ui/ozone/platform/wayland/test/test_data_device_manager.h"
+#include "ui/ozone/platform/wayland/test/test_output.h"
+#include "ui/ozone/platform/wayland/test/test_seat.h"
+#include "ui/ozone/platform/wayland/test/test_zwp_text_input_manager.h"
 
 struct wl_client;
 struct wl_display;
 struct wl_event_loop;
-struct wl_global;
 struct wl_resource;
 
 namespace wl {
 
-constexpr char kTextMimeTypeUtf8[] = "text/plain;charset=utf-8";
-constexpr char kTextMimeTypeText[] = "text/plain";
-constexpr char kSampleClipboardText[] = "This is a sample text for clipboard.";
-constexpr char kSampleTextForDragAndDrop[] =
-    "This is a sample text for drag-and-drop.";
-
-// Base class for managing the life cycle of server objects.
-class ServerObject {
- public:
-  explicit ServerObject(wl_resource* resource);
-  virtual ~ServerObject();
-
-  wl_resource* resource() { return resource_; }
-
-  static void OnResourceDestroyed(wl_resource* resource);
-
- private:
-  wl_resource* resource_;
-
-  DISALLOW_COPY_AND_ASSIGN(ServerObject);
-};
-
-class MockXdgTopLevel;
-
-// Manage xdg_surface, zxdg_surface_v6 and zxdg_toplevel for providing desktop
-// UI.
-class MockXdgSurface : public ServerObject {
- public:
-  MockXdgSurface(wl_resource* resource, const void* implementation);
-  ~MockXdgSurface() override;
-
-  // These mock methods are shared between xdg_surface and zxdg_toplevel
-  // surface.
-  MOCK_METHOD1(SetParent, void(wl_resource* parent));
-  MOCK_METHOD1(SetTitle, void(const char* title));
-  MOCK_METHOD1(SetAppId, void(const char* app_id));
-  MOCK_METHOD1(Move, void(uint32_t serial));
-  MOCK_METHOD2(Resize, void(uint32_t serial, uint32_t edges));
-  MOCK_METHOD1(AckConfigure, void(uint32_t serial));
-  MOCK_METHOD4(SetWindowGeometry,
-               void(int32_t x, int32_t y, int32_t width, int32_t height));
-  MOCK_METHOD0(SetMaximized, void());
-  MOCK_METHOD0(UnsetMaximized, void());
-  MOCK_METHOD0(SetFullscreen, void());
-  MOCK_METHOD0(UnsetFullscreen, void());
-  MOCK_METHOD0(SetMinimized, void());
-
-  void set_xdg_toplevel(std::unique_ptr<MockXdgTopLevel> xdg_toplevel) {
-    xdg_toplevel_ = std::move(xdg_toplevel);
-  }
-  MockXdgTopLevel* xdg_toplevel() { return xdg_toplevel_.get(); }
-
- private:
-  // Used when xdg v6 is used.
-  std::unique_ptr<MockXdgTopLevel> xdg_toplevel_;
-
-  DISALLOW_COPY_AND_ASSIGN(MockXdgSurface);
-};
-
-// Manage zxdg_toplevel for providing desktop UI.
-class MockXdgTopLevel : public MockXdgSurface {
- public:
-  explicit MockXdgTopLevel(wl_resource* resource);
-  ~MockXdgTopLevel() override;
-
-  // TODO(msisov): mock other zxdg_toplevel specific methods once implementation
-  // is done. example: MOCK_METHOD2(SetMaxSize, void(int32_t width, int32_t
-  // height());
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(MockXdgTopLevel);
-};
-
-// A mocked positioner object, which provides a collection of rules of a child
-// surface relative to a parent surface.
-class MockPositioner : public ServerObject {
- public:
-  explicit MockPositioner(wl_resource* resource);
-  ~MockPositioner() override;
-
-  void set_size(gfx::Size size) { size_ = size; }
-  gfx::Size size() const { return size_; }
-
-  void set_anchor_rect(gfx::Rect anchor_rect) { anchor_rect_ = anchor_rect; }
-  gfx::Rect anchor_rect() const { return anchor_rect_; }
-
-  void set_anchor(uint32_t anchor) { anchor_ = anchor; }
-
-  void set_gravity(uint32_t gravity) { gravity_ = gravity; }
-
- private:
-  gfx::Rect anchor_rect_;
-  gfx::Size size_;
-  uint32_t anchor_;
-  uint32_t gravity_;
-
-  DISALLOW_COPY_AND_ASSIGN(MockPositioner);
-};
-
-class MockXdgPopup : public ServerObject {
- public:
-  MockXdgPopup(wl_resource* resource, const void* implementation);
-  ~MockXdgPopup() override;
-
-  MOCK_METHOD1(Grab, void(uint32_t serial));
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(MockXdgPopup);
-};
-
-// Manage client surface
-class MockSurface : public ServerObject {
- public:
-  explicit MockSurface(wl_resource* resource);
-  ~MockSurface() override;
-
-  static MockSurface* FromResource(wl_resource* resource);
-
-  MOCK_METHOD3(Attach, void(wl_resource* buffer, int32_t x, int32_t y));
-  MOCK_METHOD4(Damage,
-               void(int32_t x, int32_t y, int32_t width, int32_t height));
-  MOCK_METHOD0(Commit, void());
-
-  void set_xdg_surface(std::unique_ptr<MockXdgSurface> xdg_surface) {
-    xdg_surface_ = std::move(xdg_surface);
-  }
-  MockXdgSurface* xdg_surface() { return xdg_surface_.get(); }
-
- private:
-  std::unique_ptr<MockXdgSurface> xdg_surface_;
-
-  DISALLOW_COPY_AND_ASSIGN(MockSurface);
-};
-
-class MockPointer : public ServerObject {
- public:
-  explicit MockPointer(wl_resource* resource);
-  ~MockPointer() override;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(MockPointer);
-};
-
-class MockKeyboard : public ServerObject {
- public:
-  explicit MockKeyboard(wl_resource* resource);
-  ~MockKeyboard() override;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(MockKeyboard);
-};
-
-class MockTouch : public ServerObject {
- public:
-  explicit MockTouch(wl_resource* resource);
-  ~MockTouch() override;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(MockTouch);
-};
-
-// Manage zwp_text_input_v1.
-class MockZwpTextInput : public ServerObject {
- public:
-  MockZwpTextInput(wl_resource* resource, const void* implementation);
-  ~MockZwpTextInput() override;
-
-  MOCK_METHOD0(Reset, void());
-  MOCK_METHOD1(Activate, void(wl_resource* window));
-  MOCK_METHOD0(Deactivate, void());
-  MOCK_METHOD0(ShowInputPanel, void());
-  MOCK_METHOD0(HideInputPanel, void());
-  MOCK_METHOD4(SetCursorRect,
-               void(int32_t x, int32_t y, int32_t width, int32_t height));
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(MockZwpTextInput);
-};
-
-class MockDataOffer : public ServerObject {
- public:
-  explicit MockDataOffer(wl_resource* resource);
-  ~MockDataOffer() override;
-
-  void Receive(const std::string& mime_type, base::ScopedFD fd);
-  void OnOffer(const std::string& mime_type);
-
- private:
-  base::Thread io_thread_;
-  base::WeakPtrFactory<MockDataOffer> write_data_weak_ptr_factory_;
-};
-
-class MockDataSource : public ServerObject {
- public:
-  explicit MockDataSource(wl_resource* resource);
-  ~MockDataSource() override;
-
-  void Offer(const std::string& mime_type);
-
-  using ReadDataCallback =
-      base::OnceCallback<void(const std::vector<uint8_t>&)>;
-  void ReadData(ReadDataCallback);
-
-  void OnCancelled();
-
- private:
-  void DataReadCb(ReadDataCallback callback, const std::vector<uint8_t>& data);
-
-  base::Thread io_thread_;
-  base::WeakPtrFactory<MockDataSource> read_data_weak_ptr_factory_;
-
-  DISALLOW_COPY_AND_ASSIGN(MockDataSource);
-};
-
-class MockDataDevice : public ServerObject {
- public:
-  MockDataDevice(wl_client* client, wl_resource* resource);
-  ~MockDataDevice() override;
-
-  void SetSelection(MockDataSource* data_source, uint32_t serial);
-
-  MockDataOffer* OnDataOffer();
-  void OnEnter(uint32_t serial,
-               wl_resource* surface,
-               wl_fixed_t x,
-               wl_fixed_t y,
-               MockDataOffer& data_offer);
-  void OnLeave();
-  void OnMotion(uint32_t time, wl_fixed_t x, wl_fixed_t y);
-  void OnDrop();
-  void OnSelection(MockDataOffer& data_offer);
-
- private:
-  std::unique_ptr<MockDataOffer> data_offer_;
-  wl_client* client_ = nullptr;
-
-  DISALLOW_COPY_AND_ASSIGN(MockDataDevice);
-};
-
-struct GlobalDeleter {
-  void operator()(wl_global* global);
-};
-
-// Base class for managing the life cycle of global objects:
-// It presents a global object used to emit global events to all clients.
-class Global {
- public:
-  Global(const wl_interface* interface,
-         const void* implementation,
-         uint32_t version);
-  virtual ~Global();
-
-  // Create a global object.
-  bool Initialize(wl_display* display);
-
-  // Can be used by clients to explicitly destroy global objects and send
-  // global_remove event.
-  void DestroyGlobal();
-
-  // Called from Bind() to send additional information to clients.
-  virtual void OnBind() {}
-
-  // The first bound resource to this global, which is usually all that is
-  // useful when testing a simple client.
-  wl_resource* resource() { return resource_; }
-
-  // Send the global event to clients.
-  static void Bind(wl_client* client,
-                   void* data,
-                   uint32_t version,
-                   uint32_t id);
-  static void OnResourceDestroyed(wl_resource* resource);
-
- private:
-  std::unique_ptr<wl_global, GlobalDeleter> global_;
-
-  const wl_interface* interface_;
-  const void* implementation_;
-  const uint32_t version_;
-  wl_resource* resource_ = nullptr;
-
-  DISALLOW_COPY_AND_ASSIGN(Global);
-};
-
-// Manage wl_compositor object.
-class MockCompositor : public Global {
- public:
-  MockCompositor();
-  ~MockCompositor() override;
-
-  void AddSurface(std::unique_ptr<MockSurface> surface);
-
- private:
-  std::vector<std::unique_ptr<MockSurface>> surfaces_;
-
-  DISALLOW_COPY_AND_ASSIGN(MockCompositor);
-};
-
-// Manage wl_data_device_manager object.
-class MockDataDeviceManager : public Global {
- public:
-  MockDataDeviceManager();
-  ~MockDataDeviceManager() override;
-
-  MockDataDevice* data_device() { return data_device_.get(); }
-  void set_data_device(std::unique_ptr<MockDataDevice> data_device) {
-    data_device_ = std::move(data_device);
-  }
-
-  MockDataSource* data_source() { return data_source_.get(); }
-  void set_data_source(std::unique_ptr<MockDataSource> data_source) {
-    data_source_ = std::move(data_source);
-  }
-
- private:
-  std::unique_ptr<MockDataDevice> data_device_;
-  std::unique_ptr<MockDataSource> data_source_;
-
-  DISALLOW_COPY_AND_ASSIGN(MockDataDeviceManager);
-};
-
-// Handle wl_output object.
-class MockOutput : public Global {
- public:
-  MockOutput();
-  ~MockOutput() override;
-  void SetRect(const gfx::Rect rect) { rect_ = rect; }
-  const gfx::Rect GetRect() { return rect_; }
-  void OnBind() override;
-
- private:
-  gfx::Rect rect_;
-
-  DISALLOW_COPY_AND_ASSIGN(MockOutput);
-};
-
-// Manage wl_seat object. A seat is a group of keyboards, pointer and touch
-// devices. This object is published as a global during start up, or when such a
-// device is hot plugged. A seat typically has a pointer and maintains a
-// keyboard focus and a pointer focus.
-// https://people.freedesktop.org/~whot/wayland-doxygen/wayland/Server/structwl__seat__interface.html
-class MockSeat : public Global {
- public:
-  MockSeat();
-  ~MockSeat() override;
-
-  void set_pointer(std::unique_ptr<MockPointer> pointer) {
-    pointer_ = std::move(pointer);
-  }
-  MockPointer* pointer() { return pointer_.get(); }
-
-  void set_keyboard(std::unique_ptr<MockKeyboard> keyboard) {
-    keyboard_ = std::move(keyboard);
-  }
-  MockKeyboard* keyboard() { return keyboard_.get(); }
-
-  void set_touch(std::unique_ptr<MockTouch> touch) {
-    touch_ = std::move(touch);
-  }
-  MockTouch* touch() { return touch_.get(); }
-
- private:
-  std::unique_ptr<MockPointer> pointer_;
-  std::unique_ptr<MockKeyboard> keyboard_;
-  std::unique_ptr<MockTouch> touch_;
-
-  DISALLOW_COPY_AND_ASSIGN(MockSeat);
-};
-
-// Manage xdg_shell object.
-class MockXdgShell : public Global {
- public:
-  MockXdgShell();
-  ~MockXdgShell() override;
-
-  MOCK_METHOD1(UseUnstableVersion, void(int32_t version));
-  MOCK_METHOD1(Pong, void(uint32_t serial));
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(MockXdgShell);
-};
-
-// Manage zxdg_shell_v6 object.
-class MockXdgShellV6 : public Global {
- public:
-  MockXdgShellV6();
-  ~MockXdgShellV6() override;
-
-  MOCK_METHOD1(Pong, void(uint32_t serial));
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(MockXdgShellV6);
-};
-
-// Manage zwp_text_input_manager_v1 object.
-class MockTextInputManagerV1 : public Global {
- public:
-  MockTextInputManagerV1();
-  ~MockTextInputManagerV1() override;
-
-  std::unique_ptr<MockZwpTextInput> text_input;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(MockTextInputManagerV1);
-};
-
 struct DisplayDeleter {
   void operator()(wl_display* display);
 };
@@ -458,22 +58,23 @@
     return resource ? T::FromResource(resource) : nullptr;
   }
 
-  MockOutput* CreateAndInitializeOutput() {
-    auto output = std::make_unique<MockOutput>();
+  TestOutput* CreateAndInitializeOutput() {
+    auto output = std::make_unique<TestOutput>();
     output->Initialize(display());
 
-    MockOutput* output_ptr = output.get();
+    TestOutput* output_ptr = output.get();
     globals_.push_back(std::move(output));
     return output_ptr;
   }
 
-  MockDataDeviceManager* data_device_manager() { return &data_device_manager_; }
-  MockSeat* seat() { return &seat_; }
+  TestDataDeviceManager* data_device_manager() { return &data_device_manager_; }
+  TestSeat* seat() { return &seat_; }
   MockXdgShell* xdg_shell() { return &xdg_shell_; }
-  MockOutput* output() { return &output_; }
-  MockTextInputManagerV1* text_input_manager_v1() {
+  TestOutput* output() { return &output_; }
+  TestZwpTextInputManagerV1* text_input_manager_v1() {
     return &zwp_text_input_manager_v1_;
   }
+  MockZwpLinuxDmabufV1* zwp_linux_dmabuf_v1() { return &zwp_linux_dmabuf_v1_; }
 
   wl_display* display() const { return display_.get(); }
 
@@ -494,15 +95,16 @@
   base::WaitableEvent resume_event_;
 
   // Represent Wayland global objects
-  MockCompositor compositor_;
-  MockDataDeviceManager data_device_manager_;
-  MockOutput output_;
-  MockSeat seat_;
+  TestCompositor compositor_;
+  TestDataDeviceManager data_device_manager_;
+  TestOutput output_;
+  TestSeat seat_;
   MockXdgShell xdg_shell_;
-  MockXdgShellV6 zxdg_shell_v6_;
-  MockTextInputManagerV1 zwp_text_input_manager_v1_;
+  MockZxdgShellV6 zxdg_shell_v6_;
+  TestZwpTextInputManagerV1 zwp_text_input_manager_v1_;
+  MockZwpLinuxDmabufV1 zwp_linux_dmabuf_v1_;
 
-  std::vector<std::unique_ptr<Global>> globals_;
+  std::vector<std::unique_ptr<GlobalObject>> globals_;
 
   base::MessagePumpLibevent::FdWatchController controller_;
 
diff --git a/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.cc b/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.cc
index ce339c39..2958f64 100644
--- a/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.cc
+++ b/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.cc
@@ -106,12 +106,7 @@
   // TODO: the following should be replaced by a per surface flush as it gets
   // implemented in GL drivers.
   EGLSyncKHR fence = InsertFence(has_implicit_external_sync_);
-  if (!fence) {
-    completion_callback.Run(gfx::SwapResult::SWAP_FAILED, nullptr);
-    // Notify the caller, the buffer is never presented on a screen.
-    presentation_callback.Run(gfx::PresentationFeedback::Failure());
-    return;
-  }
+  CHECK_NE(fence, EGL_NO_SYNC_KHR) << "eglCreateSyncKHR failed";
 
   base::OnceClosure fence_wait_task =
       base::BindOnce(&WaitForFence, GetDisplay(), fence);
diff --git a/ui/ozone/platform/wayland/test/constants.h b/ui/ozone/platform/wayland/test/constants.h
new file mode 100644
index 0000000..e06dc71
--- /dev/null
+++ b/ui/ozone/platform/wayland/test/constants.h
@@ -0,0 +1,18 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_WAYLAND_TEST_CONSTANTS_H_
+#define UI_OZONE_PLATFORM_WAYLAND_TEST_CONSTANTS_H_
+
+namespace wl {
+
+constexpr char kSampleClipboardText[] = "This is a sample text for clipboard.";
+constexpr char kSampleTextForDragAndDrop[] =
+    "This is a sample text for drag-and-drop.";
+constexpr char kTextMimeTypeUtf8[] = "text/plain;charset=utf-8";
+constexpr char kTextMimeTypeText[] = "text/plain";
+
+}  // namespace wl
+
+#endif  // UI_OZONE_PLATFORM_WAYLAND_TEST_CONSTANTS_H_
diff --git a/ui/ozone/platform/wayland/test/global_object.cc b/ui/ozone/platform/wayland/test/global_object.cc
new file mode 100644
index 0000000..0c11ef5
--- /dev/null
+++ b/ui/ozone/platform/wayland/test/global_object.cc
@@ -0,0 +1,63 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/platform/wayland/test/global_object.h"
+
+#include <algorithm>
+
+#include <wayland-server-core.h>
+
+#include "ui/ozone/platform/wayland/test/server_object.h"
+
+namespace wl {
+
+void GlobalObject::Deleter::operator()(wl_global* global) {
+  wl_global_destroy(global);
+}
+
+GlobalObject::GlobalObject(const wl_interface* interface,
+                           const void* implementation,
+                           uint32_t version)
+    : interface_(interface),
+      implementation_(implementation),
+      version_(version) {}
+
+GlobalObject::~GlobalObject() {}
+
+bool GlobalObject::Initialize(wl_display* display) {
+  global_.reset(wl_global_create(display, interface_, version_, this, &Bind));
+  return global_ != nullptr;
+}
+
+void GlobalObject::DestroyGlobal() {
+  global_.reset();
+}
+
+// static
+void GlobalObject::Bind(wl_client* client,
+                        void* data,
+                        uint32_t version,
+                        uint32_t id) {
+  auto* global = static_cast<GlobalObject*>(data);
+  wl_resource* resource = wl_resource_create(
+      client, global->interface_, std::min(version, global->version_), id);
+  if (!resource) {
+    wl_client_post_no_memory(client);
+    return;
+  }
+  if (!global->resource_)
+    global->resource_ = resource;
+  wl_resource_set_implementation(resource, global->implementation_, global,
+                                 &GlobalObject::OnResourceDestroyed);
+  global->OnBind();
+}
+
+// static
+void GlobalObject::OnResourceDestroyed(wl_resource* resource) {
+  auto* global = GetUserDataAs<GlobalObject>(resource);
+  if (global->resource_ == resource)
+    global->resource_ = nullptr;
+}
+
+}  // namespace wl
diff --git a/ui/ozone/platform/wayland/test/global_object.h b/ui/ozone/platform/wayland/test/global_object.h
new file mode 100644
index 0000000..0a7a1ad
--- /dev/null
+++ b/ui/ozone/platform/wayland/test/global_object.h
@@ -0,0 +1,67 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_WAYLAND_TEST_GLOBAL_OBJECT_H_
+#define UI_OZONE_PLATFORM_WAYLAND_TEST_GLOBAL_OBJECT_H_
+
+#include <memory>
+
+#include "base/macros.h"
+
+struct wl_client;
+struct wl_display;
+struct wl_global;
+struct wl_interface;
+struct wl_resource;
+
+namespace wl {
+
+// Base class for managing the lifecycle of global objects.
+// Represents a global object used to emit global events to all clients.
+class GlobalObject {
+ public:
+  GlobalObject(const wl_interface* interface,
+               const void* implementation,
+               uint32_t version);
+  virtual ~GlobalObject();
+
+  // Creates a global object.
+  bool Initialize(wl_display* display);
+
+  // Can be used by clients to explicitly destroy global objects and send
+  // global_remove event.
+  void DestroyGlobal();
+
+  // Called from Bind() to send additional information to clients.
+  virtual void OnBind() {}
+
+  // The first resource bound to this global, which is usually all that is
+  // useful when testing a simple client.
+  wl_resource* resource() const { return resource_; }
+
+  // Sends the global event to clients.
+  static void Bind(wl_client* client,
+                   void* data,
+                   uint32_t version,
+                   uint32_t id);
+  static void OnResourceDestroyed(wl_resource* resource);
+
+ private:
+  struct Deleter {
+    void operator()(wl_global* global);
+  };
+
+  std::unique_ptr<wl_global, Deleter> global_;
+
+  const wl_interface* interface_;
+  const void* implementation_;
+  const uint32_t version_;
+  wl_resource* resource_ = nullptr;
+
+  DISALLOW_COPY_AND_ASSIGN(GlobalObject);
+};
+
+}  // namespace wl
+
+#endif  // UI_OZONE_PLATFORM_WAYLAND_TEST_GLOBAL_OBJECT_H_
diff --git a/ui/ozone/platform/wayland/test/mock_buffer.cc b/ui/ozone/platform/wayland/test/mock_buffer.cc
new file mode 100644
index 0000000..b31f928
--- /dev/null
+++ b/ui/ozone/platform/wayland/test/mock_buffer.cc
@@ -0,0 +1,21 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/platform/wayland/test/mock_buffer.h"
+
+namespace wl {
+
+const struct wl_buffer_interface kMockWlBufferImpl = {&DestroyResource};
+
+MockBuffer::MockBuffer(wl_resource* resource, std::vector<base::ScopedFD>&& fds)
+    : ServerObject(resource), fds_(std::move(fds)) {}
+
+MockBuffer::~MockBuffer() {
+  for (auto& fd : fds_) {
+    LOG(WARNING) << "Will close FD: " << fd.get();
+    fd.reset();
+  }
+}
+
+}  // namespace wl
diff --git a/ui/ozone/platform/wayland/test/mock_buffer.h b/ui/ozone/platform/wayland/test/mock_buffer.h
new file mode 100644
index 0000000..94f565f
--- /dev/null
+++ b/ui/ozone/platform/wayland/test/mock_buffer.h
@@ -0,0 +1,38 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_WAYLAND_TEST_MOCK_BUFFER_H_
+#define UI_OZONE_PLATFORM_WAYLAND_TEST_MOCK_BUFFER_H_
+
+#include <linux-dmabuf-unstable-v1-server-protocol.h>
+
+#include "base/files/scoped_file.h"
+#include "base/macros.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "ui/ozone/platform/wayland/test/server_object.h"
+
+struct wl_client;
+struct wl_resource;
+
+namespace wl {
+
+extern const struct wl_buffer_interface kMockWlBufferImpl;
+
+// Manage wl_buffer object.
+class MockBuffer : public ServerObject {
+ public:
+  MockBuffer(wl_resource* resource, std::vector<base::ScopedFD>&& fds);
+  ~MockBuffer() override;
+
+  MOCK_METHOD2(Destroy, void(wl_client* client, wl_resource* resource));
+
+ private:
+  std::vector<base::ScopedFD> fds_;
+
+  DISALLOW_COPY_AND_ASSIGN(MockBuffer);
+};
+
+}  // namespace wl
+
+#endif  // UI_OZONE_PLATFORM_WAYLAND_TEST_MOCK_BUFFER_H_
diff --git a/ui/ozone/platform/wayland/test/mock_surface.cc b/ui/ozone/platform/wayland/test/mock_surface.cc
new file mode 100644
index 0000000..7f266b69
--- /dev/null
+++ b/ui/ozone/platform/wayland/test/mock_surface.cc
@@ -0,0 +1,61 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/platform/wayland/test/mock_surface.h"
+
+namespace wl {
+
+namespace {
+
+void Attach(wl_client* client,
+            wl_resource* resource,
+            wl_resource* buffer_resource,
+            int32_t x,
+            int32_t y) {
+  GetUserDataAs<MockSurface>(resource)->Attach(buffer_resource, x, y);
+}
+
+void Damage(wl_client* client,
+            wl_resource* resource,
+            int32_t x,
+            int32_t y,
+            int32_t width,
+            int32_t height) {
+  GetUserDataAs<MockSurface>(resource)->Damage(x, y, width, height);
+}
+
+void Commit(wl_client* client, wl_resource* resource) {
+  GetUserDataAs<MockSurface>(resource)->Commit();
+}
+
+}  // namespace
+
+const struct wl_surface_interface kMockSurfaceImpl = {
+    &DestroyResource,  // destroy
+    &Attach,           // attach
+    &Damage,           // damage
+    nullptr,           // frame
+    nullptr,           // set_opaque_region
+    nullptr,           // set_input_region
+    &Commit,           // commit
+    nullptr,           // set_buffer_transform
+    nullptr,           // set_buffer_scale
+    nullptr,           // damage_buffer
+};
+
+MockSurface::MockSurface(wl_resource* resource) : ServerObject(resource) {}
+
+MockSurface::~MockSurface() {
+  if (xdg_surface_ && xdg_surface_->resource())
+    wl_resource_destroy(xdg_surface_->resource());
+}
+
+MockSurface* MockSurface::FromResource(wl_resource* resource) {
+  if (!ResourceHasImplementation(resource, &wl_surface_interface,
+                                 &kMockSurfaceImpl))
+    return nullptr;
+  return GetUserDataAs<MockSurface>(resource);
+}
+
+}  // namespace wl
diff --git a/ui/ozone/platform/wayland/test/mock_surface.h b/ui/ozone/platform/wayland/test/mock_surface.h
new file mode 100644
index 0000000..a4ba746
--- /dev/null
+++ b/ui/ozone/platform/wayland/test/mock_surface.h
@@ -0,0 +1,47 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_WAYLAND_TEST_MOCK_SURFACE_H_
+#define UI_OZONE_PLATFORM_WAYLAND_TEST_MOCK_SURFACE_H_
+
+#include <wayland-server-protocol-core.h>
+
+#include "base/macros.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "ui/ozone/platform/wayland/test/mock_xdg_surface.h"
+#include "ui/ozone/platform/wayland/test/server_object.h"
+
+struct wl_resource;
+
+namespace wl {
+
+extern const struct wl_surface_interface kMockSurfaceImpl;
+
+// Manage client surface
+class MockSurface : public ServerObject {
+ public:
+  explicit MockSurface(wl_resource* resource);
+  ~MockSurface() override;
+
+  static MockSurface* FromResource(wl_resource* resource);
+
+  MOCK_METHOD3(Attach, void(wl_resource* buffer, int32_t x, int32_t y));
+  MOCK_METHOD4(Damage,
+               void(int32_t x, int32_t y, int32_t width, int32_t height));
+  MOCK_METHOD0(Commit, void());
+
+  void set_xdg_surface(std::unique_ptr<MockXdgSurface> xdg_surface) {
+    xdg_surface_ = std::move(xdg_surface);
+  }
+  MockXdgSurface* xdg_surface() const { return xdg_surface_.get(); }
+
+ private:
+  std::unique_ptr<MockXdgSurface> xdg_surface_;
+
+  DISALLOW_COPY_AND_ASSIGN(MockSurface);
+};
+
+}  // namespace wl
+
+#endif  // UI_OZONE_PLATFORM_WAYLAND_TEST_MOCK_SURFACE_H_
diff --git a/ui/ozone/platform/wayland/test/mock_xdg_popup.cc b/ui/ozone/platform/wayland/test/mock_xdg_popup.cc
new file mode 100644
index 0000000..cecb5a9
--- /dev/null
+++ b/ui/ozone/platform/wayland/test/mock_xdg_popup.cc
@@ -0,0 +1,33 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/platform/wayland/test/mock_xdg_popup.h"
+
+namespace wl {
+
+namespace {
+
+void Grab(struct wl_client* client,
+          struct wl_resource* resource,
+          struct wl_resource* seat,
+          uint32_t serial) {
+  GetUserDataAs<MockXdgPopup>(resource)->Grab(serial);
+}
+
+}  // namespace
+
+const struct xdg_popup_interface kXdgPopupImpl = {
+    &DestroyResource,  // destroy
+};
+
+const struct zxdg_popup_v6_interface kZxdgPopupV6Impl = {
+    &DestroyResource,  // destroy
+    &Grab,             // grab
+};
+
+MockXdgPopup::MockXdgPopup(wl_resource* resource) : ServerObject(resource) {}
+
+MockXdgPopup::~MockXdgPopup() {}
+
+}  // namespace wl
diff --git a/ui/ozone/platform/wayland/test/mock_xdg_popup.h b/ui/ozone/platform/wayland/test/mock_xdg_popup.h
new file mode 100644
index 0000000..4ece023c
--- /dev/null
+++ b/ui/ozone/platform/wayland/test/mock_xdg_popup.h
@@ -0,0 +1,35 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_WAYLAND_TEST_MOCK_XDG_POPUP_H_
+#define UI_OZONE_PLATFORM_WAYLAND_TEST_MOCK_XDG_POPUP_H_
+
+#include <xdg-shell-unstable-v5-server-protocol.h>
+#include <xdg-shell-unstable-v6-server-protocol.h>
+
+#include "base/macros.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "ui/ozone/platform/wayland/test/server_object.h"
+
+struct wl_resource;
+
+namespace wl {
+
+extern const struct xdg_popup_interface kXdgPopupImpl;
+extern const struct zxdg_popup_v6_interface kZxdgPopupV6Impl;
+
+class MockXdgPopup : public ServerObject {
+ public:
+  MockXdgPopup(wl_resource* resource);
+  ~MockXdgPopup() override;
+
+  MOCK_METHOD1(Grab, void(uint32_t serial));
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockXdgPopup);
+};
+
+}  // namespace wl
+
+#endif  // UI_OZONE_PLATFORM_WAYLAND_TEST_MOCK_XDG_POPUP_H_
diff --git a/ui/ozone/platform/wayland/test/mock_xdg_shell.cc b/ui/ozone/platform/wayland/test/mock_xdg_shell.cc
new file mode 100644
index 0000000..e009d3cc
--- /dev/null
+++ b/ui/ozone/platform/wayland/test/mock_xdg_shell.cc
@@ -0,0 +1,147 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/platform/wayland/test/mock_xdg_shell.h"
+
+#include "ui/ozone/platform/wayland/test/mock_surface.h"
+#include "ui/ozone/platform/wayland/test/mock_xdg_popup.h"
+#include "ui/ozone/platform/wayland/test/server_object.h"
+#include "ui/ozone/platform/wayland/test/test_positioner.h"
+
+namespace wl {
+
+namespace {
+
+constexpr uint32_t kXdgShellVersion = 1;
+
+void GetXdgSurfaceImpl(wl_client* client,
+                       wl_resource* resource,
+                       uint32_t id,
+                       wl_resource* surface_resource,
+                       const struct wl_interface* interface,
+                       const void* implementation) {
+  auto* surface = GetUserDataAs<MockSurface>(surface_resource);
+  if (surface->xdg_surface()) {
+    uint32_t xdg_error = implementation == &kMockXdgSurfaceImpl
+                             ? XDG_SHELL_ERROR_ROLE
+                             : ZXDG_SHELL_V6_ERROR_ROLE;
+    wl_resource_post_error(resource, xdg_error, "surface already has a role");
+    return;
+  }
+  wl_resource* xdg_surface_resource = wl_resource_create(
+      client, interface, wl_resource_get_version(resource), id);
+
+  if (!xdg_surface_resource) {
+    wl_client_post_no_memory(client);
+    return;
+  }
+  surface->set_xdg_surface(
+      std::make_unique<MockXdgSurface>(xdg_surface_resource, implementation));
+}
+
+void UseUnstableVersion(wl_client* client,
+                        wl_resource* resource,
+                        int32_t version) {
+  GetUserDataAs<MockXdgShell>(resource)->UseUnstableVersion(version);
+}
+
+void GetXdgSurface(wl_client* client,
+                   wl_resource* resource,
+                   uint32_t id,
+                   wl_resource* surface_resource) {
+  GetXdgSurfaceImpl(client, resource, id, surface_resource,
+                    &xdg_surface_interface, &kMockXdgSurfaceImpl);
+}
+
+void GetXdgPopup(struct wl_client* client,
+                 struct wl_resource* resource,
+                 uint32_t id,
+                 struct wl_resource* surface,
+                 struct wl_resource* parent,
+                 struct wl_resource* seat,
+                 uint32_t serial,
+                 int32_t x,
+                 int32_t y) {
+  auto* mock_surface = GetUserDataAs<MockSurface>(surface);
+  if (mock_surface->resource() &&
+      ResourceHasImplementation(mock_surface->resource(), &xdg_popup_interface,
+                                &kXdgPopupImpl)) {
+    wl_resource_post_error(resource, XDG_SHELL_ERROR_ROLE,
+                           "surface has already assigned a role");
+    return;
+  }
+
+  wl_resource* popup_resource = wl_resource_create(
+      client, &xdg_popup_interface, wl_resource_get_version(resource), id);
+  if (!popup_resource) {
+    wl_client_post_no_memory(client);
+    return;
+  }
+
+  SetImplementation(popup_resource, &kXdgPopupImpl,
+                    std::make_unique<MockXdgPopup>(popup_resource));
+}
+
+void Pong(wl_client* client, wl_resource* resource, uint32_t serial) {
+  GetUserDataAs<MockXdgShell>(resource)->Pong(serial);
+}
+
+void CreatePositioner(wl_client* client,
+                      struct wl_resource* resource,
+                      uint32_t id) {
+  wl_resource* positioner_resource =
+      wl_resource_create(client, &zxdg_positioner_v6_interface,
+                         wl_resource_get_version(resource), id);
+  if (!positioner_resource) {
+    wl_client_post_no_memory(client);
+    return;
+  }
+
+  SetImplementation(positioner_resource, &kTestZxdgPositionerV6Impl,
+                    std::make_unique<TestPositioner>(positioner_resource));
+}
+
+void GetXdgSurfaceV6(wl_client* client,
+                     wl_resource* resource,
+                     uint32_t id,
+                     wl_resource* surface_resource) {
+  GetXdgSurfaceImpl(client, resource, id, surface_resource,
+                    &zxdg_surface_v6_interface, &kMockZxdgSurfaceV6Impl);
+}
+
+void PongV6(wl_client* client, wl_resource* resource, uint32_t serial) {
+  GetUserDataAs<MockZxdgShellV6>(resource)->Pong(serial);
+}
+
+}  // namespace
+
+const struct xdg_shell_interface kMockXdgShellImpl = {
+    &DestroyResource,     // destroy
+    &UseUnstableVersion,  // use_unstable_version
+    &GetXdgSurface,       // get_xdg_surface
+    &GetXdgPopup,         // get_xdg_popup
+    &Pong,                // pong
+};
+
+const struct zxdg_shell_v6_interface kMockZxdgShellV6Impl = {
+    &DestroyResource,   // destroy
+    &CreatePositioner,  // create_positioner
+    &GetXdgSurfaceV6,   // get_xdg_surface
+    &PongV6,            // pong
+};
+
+MockXdgShell::MockXdgShell()
+    : GlobalObject(&xdg_shell_interface, &kMockXdgShellImpl, kXdgShellVersion) {
+}
+
+MockXdgShell::~MockXdgShell() {}
+
+MockZxdgShellV6::MockZxdgShellV6()
+    : GlobalObject(&zxdg_shell_v6_interface,
+                   &kMockZxdgShellV6Impl,
+                   kXdgShellVersion) {}
+
+MockZxdgShellV6::~MockZxdgShellV6() {}
+
+}  // namespace wl
diff --git a/ui/ozone/platform/wayland/test/mock_xdg_shell.h b/ui/ozone/platform/wayland/test/mock_xdg_shell.h
new file mode 100644
index 0000000..1fc81d8
--- /dev/null
+++ b/ui/ozone/platform/wayland/test/mock_xdg_shell.h
@@ -0,0 +1,47 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_WAYLAND_TEST_MOCK_XDG_SHELL_H_
+#define UI_OZONE_PLATFORM_WAYLAND_TEST_MOCK_XDG_SHELL_H_
+
+#include <xdg-shell-unstable-v5-server-protocol.h>
+#include <xdg-shell-unstable-v6-server-protocol.h>
+
+#include "base/macros.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "ui/ozone/platform/wayland/test/global_object.h"
+
+namespace wl {
+
+extern const struct xdg_shell_interface kMockXdgShellImpl;
+extern const struct zxdg_shell_v6_interface kMockZxdgShellV6Impl;
+
+// Manage xdg_shell object.
+class MockXdgShell : public GlobalObject {
+ public:
+  MockXdgShell();
+  ~MockXdgShell() override;
+
+  MOCK_METHOD1(UseUnstableVersion, void(int32_t version));
+  MOCK_METHOD1(Pong, void(uint32_t serial));
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockXdgShell);
+};
+
+// Manage zxdg_shell_v6 object.
+class MockZxdgShellV6 : public GlobalObject {
+ public:
+  MockZxdgShellV6();
+  ~MockZxdgShellV6() override;
+
+  MOCK_METHOD1(Pong, void(uint32_t serial));
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockZxdgShellV6);
+};
+
+}  // namespace wl
+
+#endif  // UI_OZONE_PLATFORM_WAYLAND_TEST_MOCK_XDG_SHELL_H_
diff --git a/ui/ozone/platform/wayland/test/mock_xdg_surface.cc b/ui/ozone/platform/wayland/test/mock_xdg_surface.cc
new file mode 100644
index 0000000..5d853cc
--- /dev/null
+++ b/ui/ozone/platform/wayland/test/mock_xdg_surface.cc
@@ -0,0 +1,183 @@
+// 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 <xdg-shell-unstable-v6-server-protocol.h>
+
+#include "ui/ozone/platform/wayland/test/mock_xdg_popup.h"
+#include "ui/ozone/platform/wayland/test/mock_xdg_surface.h"
+#include "ui/ozone/platform/wayland/test/test_positioner.h"
+
+namespace wl {
+
+void SetTitle(wl_client* client, wl_resource* resource, const char* title) {
+  GetUserDataAs<MockXdgSurface>(resource)->SetTitle(title);
+}
+
+void SetAppId(wl_client* client, wl_resource* resource, const char* app_id) {
+  GetUserDataAs<MockXdgSurface>(resource)->SetAppId(app_id);
+}
+
+void Move(wl_client* client,
+          wl_resource* resource,
+          wl_resource* seat,
+          uint32_t serial) {
+  GetUserDataAs<MockXdgSurface>(resource)->Move(serial);
+}
+
+void Resize(wl_client* client,
+            wl_resource* resource,
+            wl_resource* seat,
+            uint32_t serial,
+            uint32_t edges) {
+  GetUserDataAs<MockXdgSurface>(resource)->Resize(serial, edges);
+}
+
+void AckConfigure(wl_client* client, wl_resource* resource, uint32_t serial) {
+  GetUserDataAs<MockXdgSurface>(resource)->AckConfigure(serial);
+}
+
+void SetWindowGeometry(wl_client* client,
+                       wl_resource* resource,
+                       int32_t x,
+                       int32_t y,
+                       int32_t width,
+                       int32_t height) {
+  GetUserDataAs<MockXdgSurface>(resource)->SetWindowGeometry(x, y, width,
+                                                             height);
+}
+
+void SetMaximized(wl_client* client, wl_resource* resource) {
+  GetUserDataAs<MockXdgSurface>(resource)->SetMaximized();
+}
+
+void UnsetMaximized(wl_client* client, wl_resource* resource) {
+  GetUserDataAs<MockXdgSurface>(resource)->UnsetMaximized();
+}
+
+void SetFullscreen(wl_client* client,
+                   wl_resource* resource,
+                   wl_resource* output) {
+  GetUserDataAs<MockXdgSurface>(resource)->SetFullscreen();
+}
+
+void UnsetFullscreen(wl_client* client, wl_resource* resource) {
+  GetUserDataAs<MockXdgSurface>(resource)->UnsetFullscreen();
+}
+
+void SetMinimized(wl_client* client, wl_resource* resource) {
+  GetUserDataAs<MockXdgSurface>(resource)->SetMinimized();
+}
+
+void GetTopLevel(wl_client* client, wl_resource* resource, uint32_t id) {
+  auto* surface = GetUserDataAs<MockXdgSurface>(resource);
+  if (surface->xdg_toplevel()) {
+    wl_resource_post_error(resource, ZXDG_SURFACE_V6_ERROR_ALREADY_CONSTRUCTED,
+                           "surface has already been constructed");
+    return;
+  }
+  wl_resource* xdg_toplevel_resource =
+      wl_resource_create(client, &zxdg_toplevel_v6_interface, 1, id);
+  if (!xdg_toplevel_resource) {
+    wl_client_post_no_memory(client);
+    return;
+  }
+  surface->set_xdg_toplevel(
+      std::make_unique<MockXdgTopLevel>(xdg_toplevel_resource));
+}
+
+void GetZXdgPopupV6(struct wl_client* client,
+                    struct wl_resource* resource,
+                    uint32_t id,
+                    struct wl_resource* parent,
+                    struct wl_resource* positioner_resource) {
+  auto* mock_xdg_surface = GetUserDataAs<MockXdgSurface>(resource);
+  wl_resource* current_resource = mock_xdg_surface->resource();
+  if (current_resource &&
+      (ResourceHasImplementation(current_resource, &zxdg_popup_v6_interface,
+                                 &kZxdgPopupV6Impl) ||
+       ResourceHasImplementation(current_resource,
+                                 &zxdg_positioner_v6_interface,
+                                 &kTestZxdgPositionerV6Impl))) {
+    wl_resource_post_error(resource, ZXDG_SURFACE_V6_ERROR_ALREADY_CONSTRUCTED,
+                           "surface has already been constructed");
+    return;
+  }
+
+  auto* positioner = GetUserDataAs<TestPositioner>(positioner_resource);
+  if (positioner->size().width() == 0 ||
+      positioner->anchor_rect().width() == 0) {
+    wl_resource_post_error(resource, ZXDG_SHELL_V6_ERROR_INVALID_POSITIONER,
+                           "Positioner object is not complete");
+    return;
+  }
+
+  wl_resource* popup_resource = wl_resource_create(
+      client, &zxdg_popup_v6_interface, wl_resource_get_version(resource), id);
+  if (!popup_resource) {
+    wl_client_post_no_memory(client);
+    return;
+  }
+
+  SetImplementation(popup_resource, &kZxdgPopupV6Impl,
+                    std::make_unique<MockXdgPopup>(popup_resource));
+}
+
+const struct xdg_surface_interface kMockXdgSurfaceImpl = {
+    &DestroyResource,    // destroy
+    nullptr,             // set_parent
+    &SetTitle,           // set_title
+    &SetAppId,           // set_app_id
+    nullptr,             // show_window_menu
+    &Move,               // move
+    &Resize,             // resize
+    &AckConfigure,       // ack_configure
+    &SetWindowGeometry,  // set_window_geometry
+    &SetMaximized,       // set_maximized
+    &UnsetMaximized,     // set_unmaximized
+    &SetFullscreen,      // set_fullscreen
+    &UnsetFullscreen,    // unset_fullscreen
+    &SetMinimized,       // set_minimized
+};
+
+const struct zxdg_surface_v6_interface kMockZxdgSurfaceV6Impl = {
+    &DestroyResource,    // destroy
+    &GetTopLevel,        // get_toplevel
+    &GetZXdgPopupV6,     // get_popup
+    &SetWindowGeometry,  // set_window_geometry
+    &AckConfigure,       // ack_configure
+};
+
+const struct zxdg_toplevel_v6_interface kMockZxdgToplevelV6Impl = {
+    &DestroyResource,  // destroy
+    nullptr,           // set_parent
+    &SetTitle,         // set_title
+    &SetAppId,         // set_app_id
+    nullptr,           // show_window_menu
+    &Move,             // move
+    &Resize,           // resize
+    nullptr,           // set_max_size
+    nullptr,           // set_min_size
+    &SetMaximized,     // set_maximized
+    &UnsetMaximized,   // set_unmaximized
+    &SetFullscreen,    // set_fullscreen
+    &UnsetFullscreen,  // unset_fullscreen
+    &SetMinimized,     // set_minimized
+};
+
+MockXdgSurface::MockXdgSurface(wl_resource* resource,
+                               const void* implementation)
+    : ServerObject(resource) {
+  SetImplementationUnretained(resource, implementation, this);
+}
+
+MockXdgSurface::~MockXdgSurface() {}
+
+MockXdgTopLevel::MockXdgTopLevel(wl_resource* resource)
+    : MockXdgSurface(resource, &kMockZxdgSurfaceV6Impl) {
+  SetImplementationUnretained(resource, &kMockZxdgToplevelV6Impl, this);
+}
+
+MockXdgTopLevel::~MockXdgTopLevel() {}
+
+}  // namespace wl
diff --git a/ui/ozone/platform/wayland/test/mock_xdg_surface.h b/ui/ozone/platform/wayland/test/mock_xdg_surface.h
new file mode 100644
index 0000000..3793f25b
--- /dev/null
+++ b/ui/ozone/platform/wayland/test/mock_xdg_surface.h
@@ -0,0 +1,76 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_WAYLAND_TEST_MOCK_XDG_SURFACE_H_
+#define UI_OZONE_PLATFORM_WAYLAND_TEST_MOCK_XDG_SURFACE_H_
+
+#include <xdg-shell-unstable-v5-server-protocol.h>
+#include <xdg-shell-unstable-v6-server-protocol.h>
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "ui/ozone/platform/wayland/test/server_object.h"
+
+struct wl_resource;
+
+namespace wl {
+
+extern const struct xdg_surface_interface kMockXdgSurfaceImpl;
+extern const struct zxdg_surface_v6_interface kMockZxdgSurfaceV6Impl;
+extern const struct zxdg_toplevel_v6_interface kMockZxdgToplevelV6Impl;
+
+class MockXdgTopLevel;
+
+// Manage xdg_surface, zxdg_surface_v6 and zxdg_toplevel for providing desktop
+// UI.
+class MockXdgSurface : public ServerObject {
+ public:
+  MockXdgSurface(wl_resource* resource, const void* implementation);
+  ~MockXdgSurface() override;
+
+  // These mock methods are shared between xdg_surface and zxdg_toplevel
+  // surface.
+  MOCK_METHOD1(SetParent, void(wl_resource* parent));
+  MOCK_METHOD1(SetTitle, void(const char* title));
+  MOCK_METHOD1(SetAppId, void(const char* app_id));
+  MOCK_METHOD1(Move, void(uint32_t serial));
+  MOCK_METHOD2(Resize, void(uint32_t serial, uint32_t edges));
+  MOCK_METHOD1(AckConfigure, void(uint32_t serial));
+  MOCK_METHOD4(SetWindowGeometry,
+               void(int32_t x, int32_t y, int32_t width, int32_t height));
+  MOCK_METHOD0(SetMaximized, void());
+  MOCK_METHOD0(UnsetMaximized, void());
+  MOCK_METHOD0(SetFullscreen, void());
+  MOCK_METHOD0(UnsetFullscreen, void());
+  MOCK_METHOD0(SetMinimized, void());
+
+  void set_xdg_toplevel(std::unique_ptr<MockXdgTopLevel> xdg_toplevel) {
+    xdg_toplevel_ = std::move(xdg_toplevel);
+  }
+  MockXdgTopLevel* xdg_toplevel() const { return xdg_toplevel_.get(); }
+
+ private:
+  // Used when xdg v6 is used.
+  std::unique_ptr<MockXdgTopLevel> xdg_toplevel_;
+
+  DISALLOW_COPY_AND_ASSIGN(MockXdgSurface);
+};
+
+// Manage zxdg_toplevel for providing desktop UI.
+class MockXdgTopLevel : public MockXdgSurface {
+ public:
+  explicit MockXdgTopLevel(wl_resource* resource);
+  ~MockXdgTopLevel() override;
+
+  // TODO(msisov): mock other zxdg_toplevel specific methods once
+  // implementation
+  // is done. example: MOCK_METHOD2(SetMaxSize, void(int32_t width, int32_t
+  // height());
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockXdgTopLevel);
+};
+
+}  // namespace wl
+
+#endif  // UI_OZONE_PLATFORM_WAYLAND_TEST_MOCK_XDG_SURFACE_H_
diff --git a/ui/ozone/platform/wayland/test/mock_zwp_linux_buffer_params.cc b/ui/ozone/platform/wayland/test/mock_zwp_linux_buffer_params.cc
new file mode 100644
index 0000000..e740c8c
--- /dev/null
+++ b/ui/ozone/platform/wayland/test/mock_zwp_linux_buffer_params.cc
@@ -0,0 +1,71 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/platform/wayland/test/mock_zwp_linux_buffer_params.h"
+
+#include "ui/ozone/platform/wayland/test/mock_buffer.h"
+
+namespace wl {
+
+namespace {
+
+void Add(wl_client* client,
+         wl_resource* resource,
+         int32_t fd,
+         uint32_t plane_idx,
+         uint32_t offset,
+         uint32_t stride,
+         uint32_t modifier_hi,
+         uint32_t modifier_lo) {
+  auto* buffer_params = GetUserDataAs<MockZwpLinuxBufferParamsV1>(resource);
+
+  buffer_params->fds_.emplace_back(fd);
+  buffer_params->Add(client, resource, fd, plane_idx, offset, stride,
+                     modifier_hi, modifier_lo);
+}
+
+void Create(wl_client* client,
+            wl_resource* resource,
+            int32_t width,
+            int32_t height,
+            uint32_t format,
+            uint32_t flags) {
+  auto* buffer_params = GetUserDataAs<MockZwpLinuxBufferParamsV1>(resource);
+
+  wl_resource* buffer_resource =
+      wl_resource_create(client, &wl_buffer_interface, 1, 0);
+
+  SetImplementation(buffer_resource, &kMockWlBufferImpl,
+                    std::make_unique<MockBuffer>(
+                        buffer_resource, std::move(buffer_params->fds_)));
+
+  zwp_linux_buffer_params_v1_send_created(resource, buffer_resource);
+
+  GetUserDataAs<MockZwpLinuxBufferParamsV1>(resource)->Create(
+      client, resource, width, height, format, flags);
+}
+
+void CreateImmed(wl_client* client,
+                 wl_resource* resource,
+                 uint32_t buffer_id,
+                 int32_t width,
+                 int32_t height,
+                 uint32_t format,
+                 uint32_t flags) {
+  GetUserDataAs<MockZwpLinuxBufferParamsV1>(resource)->CreateImmed(
+      client, resource, buffer_id, width, height, format, flags);
+}
+
+}  // namespace
+
+const struct zwp_linux_buffer_params_v1_interface
+    kMockZwpLinuxBufferParamsV1Impl = {&DestroyResource, &Add, &Create,
+                                       &CreateImmed};
+
+MockZwpLinuxBufferParamsV1::MockZwpLinuxBufferParamsV1(wl_resource* resource)
+    : ServerObject(resource) {}
+
+MockZwpLinuxBufferParamsV1::~MockZwpLinuxBufferParamsV1() {}
+
+}  // namespace wl
diff --git a/ui/ozone/platform/wayland/test/mock_zwp_linux_buffer_params.h b/ui/ozone/platform/wayland/test/mock_zwp_linux_buffer_params.h
new file mode 100644
index 0000000..57b4b92
--- /dev/null
+++ b/ui/ozone/platform/wayland/test/mock_zwp_linux_buffer_params.h
@@ -0,0 +1,63 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_WAYLAND_TEST_MOCK_ZWP_LINUX_BUFFER_PARAMS_H_
+#define UI_OZONE_PLATFORM_WAYLAND_TEST_MOCK_ZWP_LINUX_BUFFER_PARAMS_H_
+
+#include <linux-dmabuf-unstable-v1-server-protocol.h>
+
+#include "base/files/scoped_file.h"
+#include "base/macros.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "ui/ozone/platform/wayland/test/server_object.h"
+
+struct wl_client;
+struct wl_resource;
+
+namespace wl {
+
+extern const struct zwp_linux_buffer_params_v1_interface
+    kMockZwpLinuxBufferParamsV1Impl;
+
+// Manage zwp_linux_buffer_params_v1
+class MockZwpLinuxBufferParamsV1 : public ServerObject {
+ public:
+  MockZwpLinuxBufferParamsV1(wl_resource* resource);
+  ~MockZwpLinuxBufferParamsV1();
+
+  MOCK_METHOD2(Destroy, void(wl_client* client, wl_resource* resource));
+  MOCK_METHOD8(Add,
+               void(wl_client* client,
+                    wl_resource* resource,
+                    int32_t fd,
+                    uint32_t plane_idx,
+                    uint32_t offset,
+                    uint32_t stride,
+                    uint32_t modifier_hi,
+                    uint32_t modifier_lo));
+  MOCK_METHOD6(Create,
+               void(wl_client* client,
+                    wl_resource* resource,
+                    int32_t width,
+                    int32_t height,
+                    uint32_t format,
+                    uint32_t flags));
+  MOCK_METHOD7(CreateImmed,
+               void(wl_client* client,
+                    wl_resource* resource,
+                    uint32_t buffer_id,
+                    int32_t width,
+                    int32_t height,
+                    uint32_t format,
+                    uint32_t flags));
+
+  std::vector<base::ScopedFD> fds_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockZwpLinuxBufferParamsV1);
+};
+
+}  // namespace wl
+
+#endif  // UI_OZONE_PLATFORM_WAYLAND_TEST_MOCK_ZWP_LINUX_BUFFER_PARAMS_H_
diff --git a/ui/ozone/platform/wayland/test/mock_zwp_linux_dmabuf.cc b/ui/ozone/platform/wayland/test/mock_zwp_linux_dmabuf.cc
new file mode 100644
index 0000000..79a852c
--- /dev/null
+++ b/ui/ozone/platform/wayland/test/mock_zwp_linux_dmabuf.cc
@@ -0,0 +1,45 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/platform/wayland/test/mock_zwp_linux_dmabuf.h"
+
+#include <linux-dmabuf-unstable-v1-server-protocol.h>
+#include <wayland-server-core.h>
+
+#include "ui/ozone/platform/wayland/test/mock_buffer.h"
+#include "ui/ozone/platform/wayland/test/mock_zwp_linux_buffer_params.h"
+
+namespace wl {
+
+namespace {
+
+constexpr uint32_t kLinuxDmabufVersion = 1;
+
+void CreateParams(wl_client* client, wl_resource* resource, uint32_t id) {
+  wl_resource* buffer_params_resource =
+      wl_resource_create(client, &zwp_linux_buffer_params_v1_interface,
+                         wl_resource_get_version(resource), id);
+  SetImplementation(
+      buffer_params_resource, &kMockZwpLinuxBufferParamsV1Impl,
+      std::make_unique<MockZwpLinuxBufferParamsV1>(buffer_params_resource));
+
+  GetUserDataAs<MockZwpLinuxDmabufV1>(resource)->CreateParams(client, resource,
+                                                              id);
+}
+
+}  // namespace
+
+const struct zwp_linux_dmabuf_v1_interface kMockZwpLinuxDmabufV1Impl = {
+    &DestroyResource,  // destroy
+    &CreateParams,     // create_params
+};
+
+MockZwpLinuxDmabufV1::MockZwpLinuxDmabufV1()
+    : GlobalObject(&zwp_linux_dmabuf_v1_interface,
+                   &kMockZwpLinuxDmabufV1Impl,
+                   kLinuxDmabufVersion) {}
+
+MockZwpLinuxDmabufV1::~MockZwpLinuxDmabufV1() {}
+
+}  // namespace wl
diff --git a/ui/ozone/platform/wayland/test/mock_zwp_linux_dmabuf.h b/ui/ozone/platform/wayland/test/mock_zwp_linux_dmabuf.h
new file mode 100644
index 0000000..f63b92d
--- /dev/null
+++ b/ui/ozone/platform/wayland/test/mock_zwp_linux_dmabuf.h
@@ -0,0 +1,39 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_WAYLAND_TEST_MOCK_ZWP_LINUX_DMABUF_H_
+#define UI_OZONE_PLATFORM_WAYLAND_TEST_MOCK_ZWP_LINUX_DMABUF_H_
+
+#include <linux-dmabuf-unstable-v1-server-protocol.h>
+
+#include "base/macros.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "ui/ozone/platform/wayland/test/global_object.h"
+
+struct wl_client;
+struct wl_resource;
+
+namespace wl {
+
+extern const struct zwp_linux_dmabuf_v1_interface kMockZwpLinuxDmabufV1Impl;
+
+// Manage zwp_linux_dmabuf_v1 object.
+class MockZwpLinuxDmabufV1 : public GlobalObject {
+ public:
+  MockZwpLinuxDmabufV1();
+  ~MockZwpLinuxDmabufV1() override;
+
+  MOCK_METHOD2(Destroy, void(wl_client* client, wl_resource* resource));
+  MOCK_METHOD3(CreateParams,
+               void(wl_client* client,
+                    wl_resource* resource,
+                    uint32_t params_id));
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockZwpLinuxDmabufV1);
+};
+
+}  // namespace wl
+
+#endif  // UI_OZONE_PLATFORM_WAYLAND_TEST_MOCK_ZWP_LINUX_DMABUF_H_
diff --git a/ui/ozone/platform/wayland/test/mock_zwp_text_input.cc b/ui/ozone/platform/wayland/test/mock_zwp_text_input.cc
new file mode 100644
index 0000000..f8a8cca
--- /dev/null
+++ b/ui/ozone/platform/wayland/test/mock_zwp_text_input.cc
@@ -0,0 +1,66 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/platform/wayland/test/mock_zwp_text_input.h"
+
+namespace wl {
+
+namespace {
+
+void TextInputV1Activate(wl_client* client,
+                         wl_resource* resource,
+                         wl_resource* seat,
+                         wl_resource* surface) {
+  GetUserDataAs<MockZwpTextInput>(resource)->Activate(surface);
+}
+
+void TextInputV1Deactivate(wl_client* client,
+                           wl_resource* resource,
+                           wl_resource* seat) {
+  GetUserDataAs<MockZwpTextInput>(resource)->Deactivate();
+}
+
+void TextInputV1ShowInputPanel(wl_client* client, wl_resource* resource) {
+  GetUserDataAs<MockZwpTextInput>(resource)->ShowInputPanel();
+}
+
+void TextInputV1HideInputPanel(wl_client* client, wl_resource* resource) {
+  GetUserDataAs<MockZwpTextInput>(resource)->HideInputPanel();
+}
+
+void TextInputV1Reset(wl_client* client, wl_resource* resource) {
+  GetUserDataAs<MockZwpTextInput>(resource)->Reset();
+}
+
+void TextInputV1SetCursorRectangle(wl_client* client,
+                                   wl_resource* resource,
+                                   int32_t x,
+                                   int32_t y,
+                                   int32_t width,
+                                   int32_t height) {
+  GetUserDataAs<MockZwpTextInput>(resource)->SetCursorRect(x, y, width, height);
+}
+
+}  // namespace
+
+const struct zwp_text_input_v1_interface kMockZwpTextInputV1Impl = {
+    &TextInputV1Activate,            // activate
+    &TextInputV1Deactivate,          // deactivate
+    &TextInputV1ShowInputPanel,      // show_input_panel
+    &TextInputV1HideInputPanel,      // hide_input_panel
+    &TextInputV1Reset,               // reset
+    nullptr,                         // set_surrounding_text
+    nullptr,                         // set_content_type
+    &TextInputV1SetCursorRectangle,  // set_cursor_rectangle
+    nullptr,                         // set_preferred_language
+    nullptr,                         // commit_state
+    nullptr,                         // invoke_action
+};
+
+MockZwpTextInput::MockZwpTextInput(wl_resource* resource)
+    : ServerObject(resource) {}
+
+MockZwpTextInput::~MockZwpTextInput() {}
+
+}  // namespace wl
diff --git a/ui/ozone/platform/wayland/test/mock_zwp_text_input.h b/ui/ozone/platform/wayland/test/mock_zwp_text_input.h
new file mode 100644
index 0000000..aba9761
--- /dev/null
+++ b/ui/ozone/platform/wayland/test/mock_zwp_text_input.h
@@ -0,0 +1,40 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_WAYLAND_TEST_MOCK_ZWP_TEXT_INPUT_H_
+#define UI_OZONE_PLATFORM_WAYLAND_TEST_MOCK_ZWP_TEXT_INPUT_H_
+
+#include <text-input-unstable-v1-server-protocol.h>
+
+#include "base/macros.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "ui/ozone/platform/wayland/test/server_object.h"
+
+struct wl_resource;
+
+namespace wl {
+
+extern const struct zwp_text_input_v1_interface kMockZwpTextInputV1Impl;
+
+// Manage zwp_text_input_v1.
+class MockZwpTextInput : public ServerObject {
+ public:
+  MockZwpTextInput(wl_resource* resource);
+  ~MockZwpTextInput() override;
+
+  MOCK_METHOD0(Reset, void());
+  MOCK_METHOD1(Activate, void(wl_resource* window));
+  MOCK_METHOD0(Deactivate, void());
+  MOCK_METHOD0(ShowInputPanel, void());
+  MOCK_METHOD0(HideInputPanel, void());
+  MOCK_METHOD4(SetCursorRect,
+               void(int32_t x, int32_t y, int32_t width, int32_t height));
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockZwpTextInput);
+};
+
+}  // namespace wl
+
+#endif  // UI_OZONE_PLATFORM_WAYLAND_TEST_MOCK_ZWP_TEXT_INPUT_H_
diff --git a/ui/ozone/platform/wayland/test/server_object.cc b/ui/ozone/platform/wayland/test/server_object.cc
new file mode 100644
index 0000000..0fcb719
--- /dev/null
+++ b/ui/ozone/platform/wayland/test/server_object.cc
@@ -0,0 +1,32 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/platform/wayland/test/server_object.h"
+
+namespace wl {
+
+bool ResourceHasImplementation(wl_resource* resource,
+                               const wl_interface* interface,
+                               const void* impl) {
+  return wl_resource_instance_of(resource, interface, impl);
+}
+
+void DestroyResource(wl_client* client, wl_resource* resource) {
+  wl_resource_destroy(resource);
+}
+
+ServerObject::ServerObject(wl_resource* resource) : resource_(resource) {}
+
+ServerObject::~ServerObject() {
+  if (resource_)
+    wl_resource_destroy(resource_);
+}
+
+// static
+void ServerObject::OnResourceDestroyed(wl_resource* resource) {
+  auto* obj = GetUserDataAs<ServerObject>(resource);
+  obj->resource_ = nullptr;
+}
+
+}  // namespace wl
diff --git a/ui/ozone/platform/wayland/test/server_object.h b/ui/ozone/platform/wayland/test/server_object.h
new file mode 100644
index 0000000..5d670d48
--- /dev/null
+++ b/ui/ozone/platform/wayland/test/server_object.h
@@ -0,0 +1,81 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_WAYLAND_TEST_SERVER_OBJECT_H_
+#define UI_OZONE_PLATFORM_WAYLAND_TEST_SERVER_OBJECT_H_
+
+#include <memory>
+
+#include "base/macros.h"
+
+#include <wayland-server-core.h>
+
+struct wl_client;
+struct wl_resource;
+
+namespace wl {
+
+// Base class for managing the life cycle of server objects.
+class ServerObject {
+ public:
+  explicit ServerObject(wl_resource* resource);
+  virtual ~ServerObject();
+
+  wl_resource* resource() const { return resource_; }
+
+  static void OnResourceDestroyed(wl_resource* resource);
+
+ private:
+  wl_resource* resource_;
+
+  DISALLOW_COPY_AND_ASSIGN(ServerObject);
+};
+
+template <class T>
+T* GetUserDataAs(wl_resource* resource) {
+  return static_cast<T*>(wl_resource_get_user_data(resource));
+}
+
+template <class T>
+std::unique_ptr<T> TakeUserDataAs(wl_resource* resource) {
+  std::unique_ptr<T> user_data(GetUserDataAs<T>(resource));
+  // Make sure ServerObject doesn't try to destroy the resource twice.
+  ServerObject::OnResourceDestroyed(resource);
+  wl_resource_set_user_data(resource, nullptr);
+  return user_data;
+}
+
+template <class T>
+void DestroyUserData(wl_resource* resource) {
+  TakeUserDataAs<T>(resource);
+}
+
+template <class T>
+void SetImplementation(wl_resource* resource,
+                       const void* implementation,
+                       std::unique_ptr<T> user_data) {
+  wl_resource_set_implementation(resource, implementation, user_data.release(),
+                                 DestroyUserData<T>);
+}
+
+// Does not transfer ownership of the user_data.  Use with caution.  The only
+// legitimate purpose is setting more than one implementation to the same user
+// data.
+template <class T>
+void SetImplementationUnretained(wl_resource* resource,
+                                 const void* implementation,
+                                 T* user_data) {
+  wl_resource_set_implementation(resource, implementation, user_data,
+                                 &ServerObject::OnResourceDestroyed);
+}
+
+bool ResourceHasImplementation(wl_resource* resource,
+                               const wl_interface* interface,
+                               const void* impl);
+
+void DestroyResource(wl_client* client, wl_resource* resource);
+
+}  // namespace wl
+
+#endif  // UI_OZONE_PLATFORM_WAYLAND_TEST_SERVER_OBJECT_H_
diff --git a/ui/ozone/platform/wayland/test/test_compositor.cc b/ui/ozone/platform/wayland/test/test_compositor.cc
new file mode 100644
index 0000000..a8195d9
--- /dev/null
+++ b/ui/ozone/platform/wayland/test/test_compositor.cc
@@ -0,0 +1,49 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/platform/wayland/test/test_compositor.h"
+
+#include <wayland-server-core.h>
+
+#include "ui/ozone/platform/wayland/test/mock_surface.h"
+#include "ui/ozone/platform/wayland/test/server_object.h"
+
+namespace wl {
+
+namespace {
+
+constexpr uint32_t kCompositorVersion = 4;
+
+void CreateSurface(wl_client* client, wl_resource* resource, uint32_t id) {
+  auto* compositor = GetUserDataAs<TestCompositor>(resource);
+  wl_resource* surface_resource = wl_resource_create(
+      client, &wl_surface_interface, wl_resource_get_version(resource), id);
+  if (!surface_resource) {
+    wl_client_post_no_memory(client);
+    return;
+  }
+  SetImplementation(surface_resource, &kMockSurfaceImpl,
+                    std::make_unique<MockSurface>(surface_resource));
+  compositor->AddSurface(GetUserDataAs<MockSurface>(surface_resource));
+}
+
+}  // namespace
+
+const struct wl_compositor_interface kTestCompositorImpl = {
+    &CreateSurface,  // create_surface
+    nullptr,         // create_region
+};
+
+TestCompositor::TestCompositor()
+    : GlobalObject(&wl_compositor_interface,
+                   &kTestCompositorImpl,
+                   kCompositorVersion) {}
+
+TestCompositor::~TestCompositor() {}
+
+void TestCompositor::AddSurface(MockSurface* surface) {
+  surfaces_.push_back(surface);
+}
+
+}  // namespace wl
diff --git a/ui/ozone/platform/wayland/test/test_compositor.h b/ui/ozone/platform/wayland/test/test_compositor.h
new file mode 100644
index 0000000..1b54cfb
--- /dev/null
+++ b/ui/ozone/platform/wayland/test/test_compositor.h
@@ -0,0 +1,33 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_WAYLAND_TEST_TEST_COMPOSITOR_H_
+#define UI_OZONE_PLATFORM_WAYLAND_TEST_TEST_COMPOSITOR_H_
+
+#include <vector>
+
+#include "base/macros.h"
+#include "ui/ozone/platform/wayland/test/global_object.h"
+
+namespace wl {
+
+class MockSurface;
+
+// Manage wl_compositor object.
+class TestCompositor : public GlobalObject {
+ public:
+  TestCompositor();
+  ~TestCompositor() override;
+
+  void AddSurface(MockSurface* surface);
+
+ private:
+  std::vector<MockSurface*> surfaces_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestCompositor);
+};
+
+}  // namespace wl
+
+#endif  // UI_OZONE_PLATFORM_WAYLAND_TEST_TEST_COMPOSITOR_H_
diff --git a/ui/ozone/platform/wayland/test/test_data_device.cc b/ui/ozone/platform/wayland/test/test_data_device.cc
new file mode 100644
index 0000000..11b5807568
--- /dev/null
+++ b/ui/ozone/platform/wayland/test/test_data_device.cc
@@ -0,0 +1,90 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/platform/wayland/test/test_data_device.h"
+
+#include <wayland-server-core.h>
+
+#include "base/logging.h"
+#include "ui/ozone/platform/wayland/test/test_data_offer.h"
+
+namespace wl {
+
+namespace {
+
+void DataDeviceStartDrag(wl_client* client,
+                         wl_resource* resource,
+                         wl_resource* source,
+                         wl_resource* origin,
+                         wl_resource* icon,
+                         uint32_t serial) {
+  NOTIMPLEMENTED();
+}
+
+void DataDeviceSetSelection(wl_client* client,
+                            wl_resource* resource,
+                            wl_resource* data_source,
+                            uint32_t serial) {
+  GetUserDataAs<TestDataDevice>(resource)->SetSelection(
+      data_source ? GetUserDataAs<TestDataSource>(data_source) : nullptr,
+      serial);
+}
+
+void DataDeviceRelease(wl_client* client, wl_resource* resource) {
+  wl_resource_destroy(resource);
+}
+
+}  // namespace
+
+const struct wl_data_device_interface kTestDataDeviceImpl = {
+    &DataDeviceStartDrag, &DataDeviceSetSelection, &DataDeviceRelease};
+
+TestDataDevice::TestDataDevice(wl_client* client, wl_resource* resource)
+    : ServerObject(resource), client_(client) {}
+
+TestDataDevice::~TestDataDevice() {}
+
+void TestDataDevice::SetSelection(TestDataSource* data_source,
+                                  uint32_t serial) {
+  NOTIMPLEMENTED();
+}
+
+TestDataOffer* TestDataDevice::OnDataOffer() {
+  wl_resource* data_offer_resource =
+      wl_resource_create(client_, &wl_data_offer_interface,
+                         wl_resource_get_version(resource()), 0);
+  SetImplementation(data_offer_resource, &kTestDataOfferImpl,
+                    std::make_unique<TestDataOffer>(data_offer_resource));
+  data_offer_ = GetUserDataAs<TestDataOffer>(data_offer_resource);
+  wl_data_device_send_data_offer(resource(), data_offer_resource);
+
+  return GetUserDataAs<TestDataOffer>(data_offer_resource);
+}
+
+void TestDataDevice::OnEnter(uint32_t serial,
+                             wl_resource* surface,
+                             wl_fixed_t x,
+                             wl_fixed_t y,
+                             TestDataOffer* data_offer) {
+  wl_data_device_send_enter(resource(), serial, surface, x, y,
+                            data_offer->resource());
+}
+
+void TestDataDevice::OnLeave() {
+  wl_data_device_send_leave(resource());
+}
+
+void TestDataDevice::OnMotion(uint32_t time, wl_fixed_t x, wl_fixed_t y) {
+  wl_data_device_send_motion(resource(), time, x, y);
+}
+
+void TestDataDevice::OnDrop() {
+  wl_data_device_send_drop(resource());
+}
+
+void TestDataDevice::OnSelection(TestDataOffer* data_offer) {
+  wl_data_device_send_selection(resource(), data_offer->resource());
+}
+
+}  // namespace wl
diff --git a/ui/ozone/platform/wayland/test/test_data_device.h b/ui/ozone/platform/wayland/test/test_data_device.h
new file mode 100644
index 0000000..91a90c1
--- /dev/null
+++ b/ui/ozone/platform/wayland/test/test_data_device.h
@@ -0,0 +1,50 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_WAYLAND_TEST_TEST_DATA_DEVICE_H_
+#define UI_OZONE_PLATFORM_WAYLAND_TEST_TEST_DATA_DEVICE_H_
+
+#include <wayland-server-protocol-core.h>
+
+#include "base/macros.h"
+#include "ui/ozone/platform/wayland/test/server_object.h"
+
+struct wl_client;
+struct wl_resource;
+
+namespace wl {
+
+extern const struct wl_data_device_interface kTestDataDeviceImpl;
+
+class TestDataOffer;
+class TestDataSource;
+
+class TestDataDevice : public ServerObject {
+ public:
+  TestDataDevice(wl_client* client, wl_resource* resource);
+  ~TestDataDevice() override;
+
+  void SetSelection(TestDataSource* data_source, uint32_t serial);
+
+  TestDataOffer* OnDataOffer();
+  void OnEnter(uint32_t serial,
+               wl_resource* surface,
+               wl_fixed_t x,
+               wl_fixed_t y,
+               TestDataOffer* data_offer);
+  void OnLeave();
+  void OnMotion(uint32_t time, wl_fixed_t x, wl_fixed_t y);
+  void OnDrop();
+  void OnSelection(TestDataOffer* data_offer);
+
+ private:
+  TestDataOffer* data_offer_;
+  wl_client* client_ = nullptr;
+
+  DISALLOW_COPY_AND_ASSIGN(TestDataDevice);
+};
+
+}  // namespace wl
+
+#endif  // UI_OZONE_PLATFORM_WAYLAND_TEST_TEST_DATA_DEVICE_H_
diff --git a/ui/ozone/platform/wayland/test/test_data_device_manager.cc b/ui/ozone/platform/wayland/test/test_data_device_manager.cc
new file mode 100644
index 0000000..2ae86abb
--- /dev/null
+++ b/ui/ozone/platform/wayland/test/test_data_device_manager.cc
@@ -0,0 +1,66 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/platform/wayland/test/test_data_device_manager.h"
+
+#include <wayland-server-core.h>
+
+#include "ui/ozone/platform/wayland/test/test_data_device.h"
+#include "ui/ozone/platform/wayland/test/test_data_source.h"
+
+namespace wl {
+
+namespace {
+
+constexpr uint32_t kDataDeviceManagerVersion = 3;
+
+void CreateDataSource(wl_client* client, wl_resource* resource, uint32_t id) {
+  wl_resource* data_source_resource = wl_resource_create(
+      client, &wl_data_source_interface, wl_resource_get_version(resource), id);
+  if (!data_source_resource) {
+    wl_client_post_no_memory(client);
+    return;
+  }
+
+  SetImplementation(data_source_resource, &kTestDataSourceImpl,
+                    std::make_unique<TestDataSource>(data_source_resource));
+
+  auto* data_device_manager = GetUserDataAs<TestDataDeviceManager>(resource);
+  data_device_manager->set_data_source(
+      GetUserDataAs<TestDataSource>(data_source_resource));
+}
+
+void GetDataDevice(wl_client* client,
+                   wl_resource* resource,
+                   uint32_t id,
+                   wl_resource* seat_resource) {
+  wl_resource* data_device_resource = wl_resource_create(
+      client, &wl_data_device_interface, wl_resource_get_version(resource), id);
+  if (!data_device_resource) {
+    wl_client_post_no_memory(client);
+    return;
+  }
+
+  SetImplementation(
+      data_device_resource, &kTestDataDeviceImpl,
+      std::make_unique<TestDataDevice>(client, data_device_resource));
+
+  auto* data_device_manager = GetUserDataAs<TestDataDeviceManager>(resource);
+  data_device_manager->set_data_device(
+      GetUserDataAs<TestDataDevice>(data_device_resource));
+}
+
+}  // namespace
+
+const struct wl_data_device_manager_interface kTestDataDeviceManagerImpl = {
+    &CreateDataSource, &GetDataDevice};
+
+TestDataDeviceManager::TestDataDeviceManager()
+    : GlobalObject(&wl_data_device_manager_interface,
+                   &kTestDataDeviceManagerImpl,
+                   kDataDeviceManagerVersion) {}
+
+TestDataDeviceManager::~TestDataDeviceManager() {}
+
+}  // namespace wl
diff --git a/ui/ozone/platform/wayland/test/test_data_device_manager.h b/ui/ozone/platform/wayland/test/test_data_device_manager.h
new file mode 100644
index 0000000..e274637
--- /dev/null
+++ b/ui/ozone/platform/wayland/test/test_data_device_manager.h
@@ -0,0 +1,45 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_WAYLAND_TEST_TEST_DATA_DEVICE_MANAGER_H_
+#define UI_OZONE_PLATFORM_WAYLAND_TEST_TEST_DATA_DEVICE_MANAGER_H_
+
+#include <wayland-server-protocol-core.h>
+
+#include "base/macros.h"
+#include "ui/ozone/platform/wayland/test/global_object.h"
+
+namespace wl {
+
+extern const struct wl_data_device_manager_interface kTestDataDeviceManagerImpl;
+
+class TestDataDevice;
+class TestDataSource;
+
+// Manage wl_data_device_manager object.
+class TestDataDeviceManager : public GlobalObject {
+ public:
+  TestDataDeviceManager();
+  ~TestDataDeviceManager() override;
+
+  TestDataDevice* data_device() const { return data_device_; }
+  void set_data_device(TestDataDevice* data_device) {
+    data_device_ = data_device;
+  }
+
+  TestDataSource* data_source() const { return data_source_; }
+  void set_data_source(TestDataSource* data_source) {
+    data_source_ = data_source;
+  }
+
+ private:
+  TestDataDevice* data_device_;
+  TestDataSource* data_source_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestDataDeviceManager);
+};
+
+}  // namespace wl
+
+#endif  // UI_OZONE_PLATFORM_WAYLAND_TEST_TEST_DATA_DEVICE_MANAGER_H_
diff --git a/ui/ozone/platform/wayland/test/test_data_offer.cc b/ui/ozone/platform/wayland/test/test_data_offer.cc
new file mode 100644
index 0000000..43fbdbb8
--- /dev/null
+++ b/ui/ozone/platform/wayland/test/test_data_offer.cc
@@ -0,0 +1,87 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/platform/wayland/test/test_data_offer.h"
+
+#include <wayland-server-core.h>
+
+#include "base/bind.h"
+#include "base/files/file_util.h"
+#include "base/logging.h"
+#include "ui/ozone/platform/wayland/test/constants.h"
+
+namespace wl {
+
+namespace {
+
+void WriteDataOnWorkerThread(base::ScopedFD fd, const std::string& utf8_text) {
+  if (!base::WriteFileDescriptor(fd.get(), utf8_text.data(), utf8_text.size()))
+    LOG(ERROR) << "Failed to write selection data to clipboard.";
+}
+
+void DataOfferAccept(wl_client* client,
+                     wl_resource* resource,
+                     uint32_t serial,
+                     const char* mime_type) {
+  NOTIMPLEMENTED();
+}
+
+void DataOfferReceive(wl_client* client,
+                      wl_resource* resource,
+                      const char* mime_type,
+                      int fd) {
+  GetUserDataAs<TestDataOffer>(resource)->Receive(mime_type,
+                                                  base::ScopedFD(fd));
+}
+
+void DataOfferDestroy(wl_client* client, wl_resource* resource) {
+  wl_resource_destroy(resource);
+}
+
+void DataOfferFinish(wl_client* client, wl_resource* resource) {
+  NOTIMPLEMENTED();
+}
+
+void DataOfferSetActions(wl_client* client,
+                         wl_resource* resource,
+                         uint32_t dnd_actions,
+                         uint32_t preferred_action) {
+  NOTIMPLEMENTED();
+}
+
+}  // namespace
+
+const struct wl_data_offer_interface kTestDataOfferImpl = {
+    DataOfferAccept, DataOfferReceive, DataOfferDestroy, DataOfferFinish,
+    DataOfferSetActions};
+
+TestDataOffer::TestDataOffer(wl_resource* resource)
+    : ServerObject(resource),
+      io_thread_("Worker thread"),
+      write_data_weak_ptr_factory_(this) {
+  base::Thread::Options options;
+  options.message_loop_type = base::MessageLoop::TYPE_IO;
+  io_thread_.StartWithOptions(options);
+}
+
+TestDataOffer::~TestDataOffer() {}
+
+void TestDataOffer::Receive(const std::string& mime_type, base::ScopedFD fd) {
+  DCHECK(fd.is_valid());
+  std::string text_data;
+  if (mime_type == kTextMimeTypeUtf8)
+    text_data = kSampleClipboardText;
+  else if (mime_type == kTextMimeTypeText)
+    text_data = kSampleTextForDragAndDrop;
+
+  io_thread_.task_runner()->PostTask(
+      FROM_HERE,
+      base::BindOnce(&WriteDataOnWorkerThread, std::move(fd), text_data));
+}
+
+void TestDataOffer::OnOffer(const std::string& mime_type) {
+  wl_data_offer_send_offer(resource(), mime_type.c_str());
+}
+
+}  // namespace wl
diff --git a/ui/ozone/platform/wayland/test/test_data_offer.h b/ui/ozone/platform/wayland/test/test_data_offer.h
new file mode 100644
index 0000000..54bc5239
--- /dev/null
+++ b/ui/ozone/platform/wayland/test/test_data_offer.h
@@ -0,0 +1,40 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_WAYLAND_TEST_TEST_DATA_OFFER_H_
+#define UI_OZONE_PLATFORM_WAYLAND_TEST_TEST_DATA_OFFER_H_
+
+#include <string>
+
+#include <wayland-server-protocol-core.h>
+
+#include "base/files/scoped_file.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/threading/thread.h"
+#include "ui/ozone/platform/wayland/test/server_object.h"
+
+struct wl_resource;
+
+namespace wl {
+
+extern const struct wl_data_offer_interface kTestDataOfferImpl;
+
+class TestDataOffer : public ServerObject {
+ public:
+  explicit TestDataOffer(wl_resource* resource);
+  ~TestDataOffer() override;
+
+  void Receive(const std::string& mime_type, base::ScopedFD fd);
+  void OnOffer(const std::string& mime_type);
+
+ private:
+  // TODO(adunaev): get rid of this in favor of using a task runner.
+  base::Thread io_thread_;
+  base::WeakPtrFactory<TestDataOffer> write_data_weak_ptr_factory_;
+};
+
+}  // namespace wl
+
+#endif  // UI_OZONE_PLATFORM_WAYLAND_TEST_TEST_DATA_OFFER_H_
diff --git a/ui/ozone/platform/wayland/test/test_data_source.cc b/ui/ozone/platform/wayland/test/test_data_source.cc
new file mode 100644
index 0000000..a6d15fee
--- /dev/null
+++ b/ui/ozone/platform/wayland/test/test_data_source.cc
@@ -0,0 +1,106 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/platform/wayland/test/test_data_source.h"
+
+#include <wayland-server-core.h>
+
+#include "base/bind.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_file.h"
+#include "base/logging.h"
+#include "base/task_runner_util.h"
+#include "ui/ozone/platform/wayland/test/constants.h"
+
+namespace wl {
+
+namespace {
+
+std::vector<uint8_t> ReadDataOnWorkerThread(base::ScopedFD fd) {
+  constexpr size_t kChunkSize = 1024;
+  std::vector<uint8_t> bytes;
+  while (true) {
+    uint8_t chunk[kChunkSize];
+    ssize_t bytes_read = HANDLE_EINTR(read(fd.get(), chunk, kChunkSize));
+    if (bytes_read > 0) {
+      bytes.insert(bytes.end(), chunk, chunk + bytes_read);
+      continue;
+    }
+    if (!bytes_read)
+      return bytes;
+    if (bytes_read < 0) {
+      LOG(ERROR) << "Failed to read selection data from clipboard.";
+      return std::vector<uint8_t>();
+    }
+  }
+}
+
+void CreatePipe(base::ScopedFD* read_pipe, base::ScopedFD* write_pipe) {
+  int raw_pipe[2];
+  PCHECK(0 == pipe(raw_pipe));
+  read_pipe->reset(raw_pipe[0]);
+  write_pipe->reset(raw_pipe[1]);
+}
+
+void DataSourceOffer(wl_client* client,
+                     wl_resource* resource,
+                     const char* mime_type) {
+  GetUserDataAs<TestDataSource>(resource)->Offer(mime_type);
+}
+
+void DataSourceDestroy(wl_client* client, wl_resource* resource) {
+  wl_resource_destroy(resource);
+}
+
+void SetActions(wl_client* client,
+                wl_resource* resource,
+                uint32_t dnd_actions) {
+  NOTIMPLEMENTED();
+}
+
+}  // namespace
+
+const struct wl_data_source_interface kTestDataSourceImpl = {
+    DataSourceOffer, DataSourceDestroy, SetActions};
+
+TestDataSource::TestDataSource(wl_resource* resource)
+    : ServerObject(resource),
+      io_thread_("Worker thread"),
+      read_data_weak_ptr_factory_(this) {
+  base::Thread::Options options;
+  options.message_loop_type = base::MessageLoop::TYPE_IO;
+  io_thread_.StartWithOptions(options);
+}
+
+TestDataSource::~TestDataSource() {}
+
+void TestDataSource::Offer(const std::string& mime_type) {
+  NOTIMPLEMENTED();
+}
+
+void TestDataSource::ReadData(ReadDataCallback callback) {
+  base::ScopedFD read_fd;
+  base::ScopedFD write_fd;
+  CreatePipe(&read_fd, &write_fd);
+
+  wl_data_source_send_send(resource(), kTextMimeTypeUtf8, write_fd.get());
+
+  base::PostTaskAndReplyWithResult(
+      io_thread_.task_runner().get(), FROM_HERE,
+      base::BindOnce(&ReadDataOnWorkerThread, std::move(read_fd)),
+      base::BindOnce(&TestDataSource::DataReadCb,
+                     read_data_weak_ptr_factory_.GetWeakPtr(),
+                     std::move(callback)));
+}
+
+void TestDataSource::DataReadCb(ReadDataCallback callback,
+                                const std::vector<uint8_t>& data) {
+  std::move(callback).Run(data);
+}
+
+void TestDataSource::OnCancelled() {
+  wl_data_source_send_cancelled(resource());
+}
+
+}  // namespace wl
diff --git a/ui/ozone/platform/wayland/test/test_data_source.h b/ui/ozone/platform/wayland/test/test_data_source.h
new file mode 100644
index 0000000..ff9467c
--- /dev/null
+++ b/ui/ozone/platform/wayland/test/test_data_source.h
@@ -0,0 +1,48 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_WAYLAND_TEST_TEST_DATA_SOURCE_H_
+#define UI_OZONE_PLATFORM_WAYLAND_TEST_TEST_DATA_SOURCE_H_
+
+#include <string>
+#include <vector>
+
+#include <wayland-server-protocol-core.h>
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/threading/thread.h"
+#include "ui/ozone/platform/wayland/test/server_object.h"
+
+struct wl_resource;
+
+namespace wl {
+
+extern const struct wl_data_source_interface kTestDataSourceImpl;
+
+class TestDataSource : public ServerObject {
+ public:
+  explicit TestDataSource(wl_resource* resource);
+  ~TestDataSource() override;
+
+  void Offer(const std::string& mime_type);
+
+  using ReadDataCallback =
+      base::OnceCallback<void(const std::vector<uint8_t>&)>;
+  void ReadData(ReadDataCallback);
+
+  void OnCancelled();
+
+ private:
+  void DataReadCb(ReadDataCallback callback, const std::vector<uint8_t>& data);
+
+  base::Thread io_thread_;
+  base::WeakPtrFactory<TestDataSource> read_data_weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestDataSource);
+};
+
+}  // namespace wl
+
+#endif  // UI_OZONE_PLATFORM_WAYLAND_TEST_TEST_DATA_SOURCE_H_
diff --git a/ui/ozone/platform/wayland/test/test_keyboard.cc b/ui/ozone/platform/wayland/test/test_keyboard.cc
new file mode 100644
index 0000000..378c1e7
--- /dev/null
+++ b/ui/ozone/platform/wayland/test/test_keyboard.cc
@@ -0,0 +1,18 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/platform/wayland/test/test_keyboard.h"
+
+namespace wl {
+
+const struct wl_pointer_interface kTestKeyboardImpl = {
+    nullptr,           // set_cursor
+    &DestroyResource,  // release
+};
+
+TestKeyboard::TestKeyboard(wl_resource* resource) : ServerObject(resource) {}
+
+TestKeyboard::~TestKeyboard() = default;
+
+}  // namespace wl
diff --git a/ui/ozone/platform/wayland/test/test_keyboard.h b/ui/ozone/platform/wayland/test/test_keyboard.h
new file mode 100644
index 0000000..2c332e2
--- /dev/null
+++ b/ui/ozone/platform/wayland/test/test_keyboard.h
@@ -0,0 +1,30 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_WAYLAND_TEST_TEST_KEYBOARD_H_
+#define UI_OZONE_PLATFORM_WAYLAND_TEST_TEST_KEYBOARD_H_
+
+#include <wayland-server-protocol.h>
+
+#include "base/macros.h"
+#include "ui/ozone/platform/wayland/test/server_object.h"
+
+struct wl_resource;
+
+namespace wl {
+
+extern const struct wl_pointer_interface kTestKeyboardImpl;
+
+class TestKeyboard : public ServerObject {
+ public:
+  explicit TestKeyboard(wl_resource* resource);
+  ~TestKeyboard() override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(TestKeyboard);
+};
+
+}  // namespace wl
+
+#endif  // UI_OZONE_PLATFORM_WAYLAND_TEST_TEST_KEYBOARD_H_
diff --git a/ui/ozone/platform/wayland/test/test_output.cc b/ui/ozone/platform/wayland/test/test_output.cc
new file mode 100644
index 0000000..4abdc319
--- /dev/null
+++ b/ui/ozone/platform/wayland/test/test_output.cc
@@ -0,0 +1,34 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/platform/wayland/test/test_output.h"
+
+#include <wayland-server-protocol-core.h>
+
+namespace wl {
+
+namespace {
+constexpr uint32_t kOutputVersion = 2;
+}
+
+TestOutput::TestOutput()
+    : GlobalObject(&wl_output_interface, nullptr, kOutputVersion) {}
+
+TestOutput::~TestOutput() = default;
+
+// Notify clients of the change for output position.
+void TestOutput::OnBind() {
+  if (rect_.IsEmpty())
+    return;
+
+  const char* kUnknownMake = "unknown";
+  const char* kUnknownModel = "unknown";
+  wl_output_send_geometry(resource(), rect_.x(), rect_.y(), 0, 0, 0,
+                          kUnknownMake, kUnknownModel, 0);
+  wl_output_send_mode(resource(), WL_OUTPUT_MODE_CURRENT, rect_.width(),
+                      rect_.height(), 0);
+  wl_output_send_done(resource());
+}
+
+}  // namespace wl
diff --git a/ui/ozone/platform/wayland/test/test_output.h b/ui/ozone/platform/wayland/test/test_output.h
new file mode 100644
index 0000000..bbd6848c
--- /dev/null
+++ b/ui/ozone/platform/wayland/test/test_output.h
@@ -0,0 +1,31 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_WAYLAND_TEST_TEST_OUTPUT_H_
+#define UI_OZONE_PLATFORM_WAYLAND_TEST_TEST_OUTPUT_H_
+
+#include "base/macros.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/ozone/platform/wayland/test/global_object.h"
+
+namespace wl {
+
+// Handle wl_output object.
+class TestOutput : public GlobalObject {
+ public:
+  TestOutput();
+  ~TestOutput() override;
+  void SetRect(const gfx::Rect rect) { rect_ = rect; }
+  const gfx::Rect GetRect() { return rect_; }
+  void OnBind() override;
+
+ private:
+  gfx::Rect rect_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestOutput);
+};
+
+}  // namespace wl
+
+#endif  // UI_OZONE_PLATFORM_WAYLAND_TEST_TEST_OUTPUT_H_
diff --git a/ui/ozone/platform/wayland/test/test_pointer.cc b/ui/ozone/platform/wayland/test/test_pointer.cc
new file mode 100644
index 0000000..ed2c9c0
--- /dev/null
+++ b/ui/ozone/platform/wayland/test/test_pointer.cc
@@ -0,0 +1,18 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/platform/wayland/test/test_pointer.h"
+
+namespace wl {
+
+const struct wl_pointer_interface kTestPointerImpl = {
+    nullptr,           // set_cursor
+    &DestroyResource,  // release
+};
+
+TestPointer::TestPointer(wl_resource* resource) : ServerObject(resource) {}
+
+TestPointer::~TestPointer() = default;
+
+}  // namespace wl
diff --git a/ui/ozone/platform/wayland/test/test_pointer.h b/ui/ozone/platform/wayland/test/test_pointer.h
new file mode 100644
index 0000000..7099fe2e
--- /dev/null
+++ b/ui/ozone/platform/wayland/test/test_pointer.h
@@ -0,0 +1,30 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_WAYLAND_TEST_TEST_POINTER_H_
+#define UI_OZONE_PLATFORM_WAYLAND_TEST_TEST_POINTER_H_
+
+#include <wayland-server-protocol.h>
+
+#include "base/macros.h"
+#include "ui/ozone/platform/wayland/test/server_object.h"
+
+struct wl_resource;
+
+namespace wl {
+
+extern const struct wl_pointer_interface kTestPointerImpl;
+
+class TestPointer : public ServerObject {
+ public:
+  explicit TestPointer(wl_resource* resource);
+  ~TestPointer() override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(TestPointer);
+};
+
+}  // namespace wl
+
+#endif  // UI_OZONE_PLATFORM_WAYLAND_TEST_MOCK_POINTER_H_
diff --git a/ui/ozone/platform/wayland/test/test_positioner.cc b/ui/ozone/platform/wayland/test/test_positioner.cc
new file mode 100644
index 0000000..e576711
--- /dev/null
+++ b/ui/ozone/platform/wayland/test/test_positioner.cc
@@ -0,0 +1,89 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/platform/wayland/test/test_positioner.h"
+
+#include <wayland-server-core.h>
+
+namespace wl {
+
+namespace {
+
+void SetSize(struct wl_client* wl_client,
+             struct wl_resource* resource,
+             int32_t width,
+             int32_t height) {
+  if (width < 1 || height < 1) {
+    wl_resource_post_error(resource, ZXDG_POSITIONER_V6_ERROR_INVALID_INPUT,
+                           "width and height must be positive and non-zero");
+    return;
+  }
+
+  GetUserDataAs<TestPositioner>(resource)->set_size(gfx::Size(width, height));
+}
+
+void SetAnchorRect(struct wl_client* client,
+                   struct wl_resource* resource,
+                   int32_t x,
+                   int32_t y,
+                   int32_t width,
+                   int32_t height) {
+  if (width < 1 || height < 1) {
+    wl_resource_post_error(resource, ZXDG_POSITIONER_V6_ERROR_INVALID_INPUT,
+                           "width and height must be positive and non-zero");
+    return;
+  }
+
+  GetUserDataAs<TestPositioner>(resource)->set_anchor_rect(
+      gfx::Rect(x, y, width, height));
+}
+
+void SetAnchor(struct wl_client* wl_client,
+               struct wl_resource* resource,
+               uint32_t anchor) {
+  if (((anchor & ZXDG_POSITIONER_V6_ANCHOR_LEFT) &&
+       (anchor & ZXDG_POSITIONER_V6_ANCHOR_RIGHT)) ||
+      ((anchor & ZXDG_POSITIONER_V6_ANCHOR_TOP) &&
+       (anchor & ZXDG_POSITIONER_V6_ANCHOR_BOTTOM))) {
+    wl_resource_post_error(resource, ZXDG_POSITIONER_V6_ERROR_INVALID_INPUT,
+                           "same-axis values are not allowed");
+    return;
+  }
+
+  GetUserDataAs<TestPositioner>(resource)->set_anchor(anchor);
+}
+
+void SetGravity(struct wl_client* client,
+                struct wl_resource* resource,
+                uint32_t gravity) {
+  if (((gravity & ZXDG_POSITIONER_V6_GRAVITY_LEFT) &&
+       (gravity & ZXDG_POSITIONER_V6_GRAVITY_RIGHT)) ||
+      ((gravity & ZXDG_POSITIONER_V6_GRAVITY_TOP) &&
+       (gravity & ZXDG_POSITIONER_V6_GRAVITY_BOTTOM))) {
+    wl_resource_post_error(resource, ZXDG_POSITIONER_V6_ERROR_INVALID_INPUT,
+                           "same-axis values are not allowed");
+    return;
+  }
+
+  GetUserDataAs<TestPositioner>(resource)->set_gravity(gravity);
+}
+
+}  // namespace
+
+const struct zxdg_positioner_v6_interface kTestZxdgPositionerV6Impl = {
+    &DestroyResource,  // destroy
+    &SetSize,          // set_size
+    &SetAnchorRect,    // set_anchor_rect
+    &SetAnchor,        // set_anchor
+    &SetGravity,       // set_gravity
+    nullptr,           // set_constraint_adjustment
+    nullptr,           // set_offset
+};
+
+TestPositioner::TestPositioner(wl_resource* resource)
+    : ServerObject(resource) {}
+
+TestPositioner::~TestPositioner() {}
+
+}  // namespace wl
diff --git a/ui/ozone/platform/wayland/test/test_positioner.h b/ui/ozone/platform/wayland/test/test_positioner.h
new file mode 100644
index 0000000..58a3ff4
--- /dev/null
+++ b/ui/ozone/platform/wayland/test/test_positioner.h
@@ -0,0 +1,49 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_WAYLAND_TEST_TEST_POSITIONER_H_
+#define UI_OZONE_PLATFORM_WAYLAND_TEST_TEST_POSITIONER_H_
+
+#include <xdg-shell-unstable-v6-server-protocol.h>
+
+#include "base/macros.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/size.h"
+#include "ui/ozone/platform/wayland/test/server_object.h"
+
+struct wl_resource;
+
+namespace wl {
+
+extern const struct zxdg_positioner_v6_interface kTestZxdgPositionerV6Impl;
+
+// A simple positioner object that provides a collection of rules of a child
+// surface relative to a parent surface.
+class TestPositioner : public ServerObject {
+ public:
+  explicit TestPositioner(wl_resource* resource);
+  ~TestPositioner() override;
+
+  void set_size(gfx::Size size) { size_ = size; }
+  gfx::Size size() const { return size_; }
+
+  void set_anchor_rect(gfx::Rect anchor_rect) { anchor_rect_ = anchor_rect; }
+  gfx::Rect anchor_rect() const { return anchor_rect_; }
+
+  void set_anchor(uint32_t anchor) { anchor_ = anchor; }
+
+  void set_gravity(uint32_t gravity) { gravity_ = gravity; }
+
+ private:
+  gfx::Rect anchor_rect_;
+  gfx::Size size_;
+  uint32_t anchor_;
+  uint32_t gravity_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestPositioner);
+};
+
+}  // namespace wl
+
+#endif  // UI_OZONE_PLATFORM_WAYLAND_TEST_TEST_POSITIONER_H_
diff --git a/ui/ozone/platform/wayland/test/test_seat.cc b/ui/ozone/platform/wayland/test/test_seat.cc
new file mode 100644
index 0000000..2f2071a
--- /dev/null
+++ b/ui/ozone/platform/wayland/test/test_seat.cc
@@ -0,0 +1,73 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/platform/wayland/test/test_seat.h"
+
+#include "ui/ozone/platform/wayland/test/test_keyboard.h"
+#include "ui/ozone/platform/wayland/test/test_pointer.h"
+#include "ui/ozone/platform/wayland/test/test_touch.h"
+
+namespace wl {
+
+namespace {
+
+constexpr uint32_t kSeatVersion = 4;
+
+void GetPointer(wl_client* client, wl_resource* resource, uint32_t id) {
+  wl_resource* pointer_resource = wl_resource_create(
+      client, &wl_pointer_interface, wl_resource_get_version(resource), id);
+  if (!pointer_resource) {
+    wl_client_post_no_memory(client);
+    return;
+  }
+  SetImplementation(pointer_resource, &kTestPointerImpl,
+                    std::make_unique<TestPointer>(pointer_resource));
+
+  auto* seat = GetUserDataAs<TestSeat>(resource);
+  seat->set_pointer(GetUserDataAs<TestPointer>(pointer_resource));
+}
+
+void GetKeyboard(wl_client* client, wl_resource* resource, uint32_t id) {
+  wl_resource* keyboard_resource = wl_resource_create(
+      client, &wl_keyboard_interface, wl_resource_get_version(resource), id);
+  if (!keyboard_resource) {
+    wl_client_post_no_memory(client);
+    return;
+  }
+  SetImplementation(keyboard_resource, &kTestKeyboardImpl,
+                    std::make_unique<TestKeyboard>(keyboard_resource));
+
+  auto* seat = GetUserDataAs<TestSeat>(resource);
+  seat->set_keyboard(GetUserDataAs<TestKeyboard>(keyboard_resource));
+}
+
+void GetTouch(wl_client* client, wl_resource* resource, uint32_t id) {
+  wl_resource* touch_resource = wl_resource_create(
+      client, &wl_touch_interface, wl_resource_get_version(resource), id);
+  if (!touch_resource) {
+    wl_client_post_no_memory(client);
+    return;
+  }
+  SetImplementation(touch_resource, &kTestTouchImpl,
+                    std::make_unique<TestTouch>(touch_resource));
+
+  auto* seat = GetUserDataAs<TestSeat>(resource);
+  seat->set_touch(GetUserDataAs<TestTouch>(touch_resource));
+}
+
+}  // namespace
+
+const struct wl_seat_interface kTestSeatImpl = {
+    &GetPointer,       // get_pointer
+    &GetKeyboard,      // get_keyboard
+    &GetTouch,         // get_touch,
+    &DestroyResource,  // release
+};
+
+TestSeat::TestSeat()
+    : GlobalObject(&wl_seat_interface, &kTestSeatImpl, kSeatVersion) {}
+
+TestSeat::~TestSeat() {}
+
+}  // namespace wl
diff --git a/ui/ozone/platform/wayland/test/test_seat.h b/ui/ozone/platform/wayland/test/test_seat.h
new file mode 100644
index 0000000..8a33d87
--- /dev/null
+++ b/ui/ozone/platform/wayland/test/test_seat.h
@@ -0,0 +1,51 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_WAYLAND_TEST_TEST_SEAT_H_
+#define UI_OZONE_PLATFORM_WAYLAND_TEST_TEST_SEAT_H_
+
+#include <wayland-server-protocol-core.h>
+
+#include "base/macros.h"
+#include "ui/ozone/platform/wayland/test/global_object.h"
+
+namespace wl {
+
+extern const struct wl_seat_interface kTestSeatImpl;
+
+class TestKeyboard;
+class TestPointer;
+class TestTouch;
+
+// Manages a global wl_seat object.
+// A seat groups keyboard, pointer, and touch devices.  This object is
+// published as a global during start up, or when such a device is hot plugged.
+// A seat typically has a pointer and maintains a keyboard focus and a pointer
+// focus.
+// https://people.freedesktop.org/~whot/wayland-doxygen/wayland/Server/structwl__seat__interface.html
+class TestSeat : public GlobalObject {
+ public:
+  TestSeat();
+  ~TestSeat() override;
+
+  void set_pointer(TestPointer* pointer) { pointer_ = pointer; }
+  TestPointer* pointer() const { return pointer_; }
+
+  void set_keyboard(TestKeyboard* keyboard) { keyboard_ = keyboard; }
+  TestKeyboard* keyboard() const { return keyboard_; }
+
+  void set_touch(TestTouch* touch) { touch_ = touch; }
+  TestTouch* touch() const { return touch_; }
+
+ private:
+  TestPointer* pointer_;
+  TestKeyboard* keyboard_;
+  TestTouch* touch_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestSeat);
+};
+
+}  // namespace wl
+
+#endif  // UI_OZONE_PLATFORM_WAYLAND_TEST_TEST_SEAT_H_
diff --git a/ui/ozone/platform/wayland/test/test_touch.cc b/ui/ozone/platform/wayland/test/test_touch.cc
new file mode 100644
index 0000000..ce730f63
--- /dev/null
+++ b/ui/ozone/platform/wayland/test/test_touch.cc
@@ -0,0 +1,18 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/platform/wayland/test/test_touch.h"
+
+namespace wl {
+
+const struct wl_pointer_interface kTestTouchImpl = {
+    nullptr,           // set_cursor
+    &DestroyResource,  // release
+};
+
+TestTouch::TestTouch(wl_resource* resource) : ServerObject(resource) {}
+
+TestTouch::~TestTouch() = default;
+
+}  // namespace wl
diff --git a/ui/ozone/platform/wayland/test/test_touch.h b/ui/ozone/platform/wayland/test/test_touch.h
new file mode 100644
index 0000000..8be4aaa6
--- /dev/null
+++ b/ui/ozone/platform/wayland/test/test_touch.h
@@ -0,0 +1,30 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_WAYLAND_TEST_TEST_TOUCH_H_
+#define UI_OZONE_PLATFORM_WAYLAND_TEST_TEST_TOUCH_H_
+
+#include <wayland-server-protocol.h>
+
+#include "base/macros.h"
+#include "ui/ozone/platform/wayland/test/server_object.h"
+
+struct wl_resource;
+
+namespace wl {
+
+extern const struct wl_pointer_interface kTestTouchImpl;
+
+class TestTouch : public ServerObject {
+ public:
+  explicit TestTouch(wl_resource* resource);
+  ~TestTouch() override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(TestTouch);
+};
+
+}  // namespace wl
+
+#endif  // UI_OZONE_PLATFORM_WAYLAND_TEST_TEST_TOUCH_H_
diff --git a/ui/ozone/platform/wayland/test/test_zwp_text_input_manager.cc b/ui/ozone/platform/wayland/test/test_zwp_text_input_manager.cc
new file mode 100644
index 0000000..19868a76
--- /dev/null
+++ b/ui/ozone/platform/wayland/test/test_zwp_text_input_manager.cc
@@ -0,0 +1,48 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/platform/wayland/test/test_zwp_text_input_manager.h"
+
+#include <wayland-server-core.h>
+
+#include "ui/ozone/platform/wayland/test/mock_zwp_text_input.h"
+
+namespace wl {
+
+namespace {
+
+constexpr uint32_t kTextInputManagerVersion = 1;
+
+void CreateTextInput(struct wl_client* client,
+                     struct wl_resource* resource,
+                     uint32_t id) {
+  auto* im = static_cast<TestZwpTextInputManagerV1*>(
+      wl_resource_get_user_data(resource));
+  wl_resource* text_resource =
+      wl_resource_create(client, &zwp_text_input_v1_interface,
+                         wl_resource_get_version(resource), id);
+  if (!text_resource) {
+    wl_client_post_no_memory(client);
+    return;
+  }
+  SetImplementation(text_resource, &kMockZwpTextInputV1Impl,
+                    std::make_unique<MockZwpTextInput>(text_resource));
+  im->set_text_input(GetUserDataAs<MockZwpTextInput>(text_resource));
+}
+
+}  // namespace
+
+const struct zwp_text_input_manager_v1_interface
+    kTestZwpTextInputManagerV1Impl = {
+        &CreateTextInput,  // create_text_input
+};
+
+TestZwpTextInputManagerV1::TestZwpTextInputManagerV1()
+    : GlobalObject(&zwp_text_input_manager_v1_interface,
+                   &kTestZwpTextInputManagerV1Impl,
+                   kTextInputManagerVersion) {}
+
+TestZwpTextInputManagerV1::~TestZwpTextInputManagerV1() {}
+
+}  // namespace wl
diff --git a/ui/ozone/platform/wayland/test/test_zwp_text_input_manager.h b/ui/ozone/platform/wayland/test/test_zwp_text_input_manager.h
new file mode 100644
index 0000000..cfb065d
--- /dev/null
+++ b/ui/ozone/platform/wayland/test/test_zwp_text_input_manager.h
@@ -0,0 +1,39 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_WAYLAND_TEST_TEST_ZWP_TEXT_INPUT_MANAGER_H_
+#define UI_OZONE_PLATFORM_WAYLAND_TEST_TEST_ZWP_TEXT_INPUT_MANAGER_H_
+
+#include <text-input-unstable-v1-server-protocol.h>
+
+#include "base/macros.h"
+#include "ui/ozone/platform/wayland/test/global_object.h"
+
+namespace wl {
+
+extern const struct zwp_text_input_manager_v1_interface
+    kTestZwpTextInputManagerV1Impl;
+
+class MockZwpTextInput;
+
+// Manage zwp_text_input_manager_v1 object.
+class TestZwpTextInputManagerV1 : public GlobalObject {
+ public:
+  TestZwpTextInputManagerV1();
+  ~TestZwpTextInputManagerV1() override;
+
+  void set_text_input(MockZwpTextInput* text_input) {
+    text_input_ = text_input;
+  }
+  MockZwpTextInput* text_input() const { return text_input_; }
+
+ private:
+  MockZwpTextInput* text_input_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestZwpTextInputManagerV1);
+};
+
+}  // namespace wl
+
+#endif  // UI_OZONE_PLATFORM_WAYLAND_TEST_TEST_ZWP_TEXT_INPUT_MANAGER_H_
diff --git a/ui/ozone/platform/wayland/wayland_buffer_manager.h b/ui/ozone/platform/wayland/wayland_buffer_manager.h
index 482b98a..50e44bb 100644
--- a/ui/ozone/platform/wayland/wayland_buffer_manager.h
+++ b/ui/ozone/platform/wayland/wayland_buffer_manager.h
@@ -10,6 +10,7 @@
 
 #include "base/containers/flat_map.h"
 #include "base/files/file.h"
+#include "base/gtest_prod_util.h"
 #include "base/macros.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/native_widget_types.h"
@@ -73,6 +74,8 @@
   void ClearState();
 
  private:
+  FRIEND_TEST_ALL_PREFIXES(WaylandBufferManagerTest, ValidateDataFromGpu);
+
   // This is an internal helper representation of a wayland buffer object, which
   // the GPU process creates when CreateBuffer is called. It's used for
   // asynchronous buffer creation and stores |params| parameter to find out,
diff --git a/ui/ozone/platform/wayland/wayland_buffer_manager_unittest.cc b/ui/ozone/platform/wayland/wayland_buffer_manager_unittest.cc
new file mode 100644
index 0000000..79e3785
--- /dev/null
+++ b/ui/ozone/platform/wayland/wayland_buffer_manager_unittest.cc
@@ -0,0 +1,151 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/platform/wayland/wayland_buffer_manager.h"
+
+#include <memory>
+
+#include <drm_fourcc.h>
+
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/ozone/platform/wayland/wayland_test.h"
+
+using testing::_;
+
+namespace ui {
+
+namespace {
+
+constexpr uint32_t kWidth = 1024;
+constexpr uint32_t kHeight = 768;
+
+}  // namespace
+
+class WaylandBufferManagerTest : public WaylandTest {
+ public:
+  WaylandBufferManagerTest() = default;
+  ~WaylandBufferManagerTest() override = default;
+
+ protected:
+  base::File MakeTempFile() {
+    base::FilePath temp_path;
+    EXPECT_TRUE(base::CreateTemporaryFile(&temp_path));
+    return base::File(temp_path, base::File::FLAG_READ |
+                                     base::File::FLAG_WRITE |
+                                     base::File::FLAG_CREATE_ALWAYS);
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(WaylandBufferManagerTest);
+};
+
+TEST_P(WaylandBufferManagerTest, ValidateDataFromGpu) {
+  struct InputData {
+    bool has_file = false;
+    uint32_t width = 0;
+    uint32_t height = 0;
+    uint32_t planes_count = 0;
+    std::vector<uint32_t> strides;
+    std::vector<uint32_t> offsets;
+    std::vector<uint64_t> modifiers;
+    uint32_t format = 0;
+    uint32_t buffer_id = 0;
+  };
+
+  constexpr uint32_t kExistingBufferId = 1;
+  constexpr uint32_t kNonExistingBufferId = 2;
+
+  // Create a buffer through the connection's interface so it gets
+  // registered with the given ID.
+  // This must be the only buffer that is asked to be created.
+  EXPECT_CALL(*server_.zwp_linux_dmabuf_v1(), CreateParams(_, _, _)).Times(1);
+
+  connection_->CreateZwpLinuxDmabuf(MakeTempFile(), kWidth, kHeight, {1}, {2},
+                                    DRM_FORMAT_R8, {3}, 1, kExistingBufferId);
+  Sync();
+
+  const InputData kBadInputs[] = {
+      // All zeros.
+      {},
+      // Valid file but zeros everywhereelse.
+      {true},
+      // Valid file, invalid size, zeros elsewhere.
+      {true, kWidth},
+      {true, 0, kHeight},
+      // Valid file and size but zeros in other fields.
+      {true, kWidth, kHeight},
+      // Vectors have different lengths.
+      {true, kWidth, kHeight, 1, {1}, {2, 3}, {4, 5, 6}},
+      // Vectors have same lengths but strides have a zero.
+      {true, kWidth, kHeight, 1, {0}, {2}, {6}},
+      // Vectors are valid but buffer format is not.
+      {true, kWidth, kHeight, 1, {1}, {2}, {6}},
+      // Everything is correct but the buffer ID is zero.
+      {true, kWidth, kHeight, 1, {1}, {2}, {6}, DRM_FORMAT_R8},
+      // Everything is correct but the buffer ID .
+      {true,
+       kWidth,
+       kHeight,
+       1,
+       {1},
+       {2},
+       {6},
+       DRM_FORMAT_R8,
+       kExistingBufferId},
+  };
+
+  WaylandBufferManager* manager = connection_->buffer_manager_for_tests();
+  ASSERT_TRUE(manager);
+
+  auto temp_file = MakeTempFile();
+  for (const auto& bad : kBadInputs) {
+    base::File dummy;
+    EXPECT_FALSE(manager->ValidateDataFromGpu(
+        bad.has_file ? temp_file : dummy, bad.width, bad.height, bad.strides,
+        bad.offsets, bad.format, bad.modifiers, bad.planes_count,
+        bad.buffer_id));
+    EXPECT_FALSE(manager->error_message().empty());
+  }
+
+  EXPECT_TRUE(manager->ValidateDataFromGpu(temp_file, kWidth, kHeight, {1}, {2},
+                                           DRM_FORMAT_R8, {3}, 1,
+                                           kNonExistingBufferId));
+
+  connection_->DestroyZwpLinuxDmabuf(kExistingBufferId);
+}
+
+TEST_P(WaylandBufferManagerTest, CreateAndDestroyBuffer) {
+  WaylandBufferManager* manager = connection_->buffer_manager_for_tests();
+  ASSERT_TRUE(manager);
+
+  const uint32_t kBufferId1 = 1;
+  const uint32_t kBufferId2 = 2;
+
+  EXPECT_CALL(*server_.zwp_linux_dmabuf_v1(), CreateParams(_, _, _)).Times(2);
+
+  EXPECT_TRUE(manager->CreateBuffer(MakeTempFile(), kWidth, kHeight, {1}, {2},
+                                    DRM_FORMAT_R8, {3}, 1, kBufferId1));
+  EXPECT_FALSE(manager->CreateBuffer(MakeTempFile(), kWidth, kHeight, {1}, {2},
+                                     DRM_FORMAT_R8, {3}, 1, kBufferId1));
+  EXPECT_FALSE(manager->DestroyBuffer(kBufferId2));
+  EXPECT_TRUE(manager->CreateBuffer(MakeTempFile(), kWidth, kHeight, {1}, {2},
+                                    DRM_FORMAT_R8, {3}, 1, kBufferId2));
+
+  EXPECT_TRUE(manager->DestroyBuffer(kBufferId1));
+  EXPECT_FALSE(manager->DestroyBuffer(kBufferId1));
+  EXPECT_TRUE(manager->DestroyBuffer(kBufferId2));
+  EXPECT_FALSE(manager->DestroyBuffer(kBufferId2));
+}
+
+INSTANTIATE_TEST_CASE_P(XdgVersionV5Test,
+                        WaylandBufferManagerTest,
+                        ::testing::Values(kXdgShellV5));
+INSTANTIATE_TEST_CASE_P(XdgVersionV6Test,
+                        WaylandBufferManagerTest,
+                        ::testing::Values(kXdgShellV6));
+
+}  // namespace ui
diff --git a/ui/ozone/platform/wayland/wayland_connection.h b/ui/ozone/platform/wayland/wayland_connection.h
index 6c4c081b..f7e0962 100644
--- a/ui/ozone/platform/wayland/wayland_connection.h
+++ b/ui/ozone/platform/wayland/wayland_connection.h
@@ -81,6 +81,9 @@
   zwp_text_input_manager_v1* text_input_manager_v1() {
     return text_input_manager_v1_.get();
   }
+  WaylandBufferManager* buffer_manager_for_tests() {
+    return buffer_manager_.get();
+  }
 
   WaylandWindow* GetWindow(gfx::AcceleratedWidget widget);
   WaylandWindow* GetCurrentFocusedWindow();
diff --git a/ui/ozone/platform/wayland/wayland_data_device_unittest.cc b/ui/ozone/platform/wayland/wayland_data_device_unittest.cc
index 8b3bbcf..d3e33fa7 100644
--- a/ui/ozone/platform/wayland/wayland_data_device_unittest.cc
+++ b/ui/ozone/platform/wayland/wayland_data_device_unittest.cc
@@ -10,6 +10,12 @@
 #include "ui/base/dragdrop/os_exchange_data.h"
 #include "ui/events/base_event_utils.h"
 #include "ui/ozone/platform/wayland/fake_server.h"
+#include "ui/ozone/platform/wayland/test/constants.h"
+#include "ui/ozone/platform/wayland/test/mock_surface.h"
+#include "ui/ozone/platform/wayland/test/test_data_device.h"
+#include "ui/ozone/platform/wayland/test/test_data_device_manager.h"
+#include "ui/ozone/platform/wayland/test/test_data_offer.h"
+#include "ui/ozone/platform/wayland/test/test_data_source.h"
 #include "ui/ozone/platform/wayland/wayland_test.h"
 #include "ui/ozone/public/platform_clipboard.h"
 
@@ -77,7 +83,7 @@
   }
 
  protected:
-  wl::MockDataDeviceManager* data_device_manager_;
+  wl::TestDataDeviceManager* data_device_manager_;
   std::unique_ptr<MockClipboardClient> clipboard_client_;
 
   DISALLOW_COPY_AND_ASSIGN(WaylandDataDeviceManagerTest);
@@ -104,7 +110,7 @@
   // focused and compositor sends data_device data to it.
   auto* data_offer = data_device_manager_->data_device()->OnDataOffer();
   data_offer->OnOffer(wl::kTextMimeTypeUtf8);
-  data_device_manager_->data_device()->OnSelection(*data_offer);
+  data_device_manager_->data_device()->OnSelection(data_offer);
   Sync();
 
   // The client requests to reading clipboard data from the server.
@@ -168,7 +174,7 @@
   // The server sends an enter event.
   data_device_manager_->data_device()->OnEnter(
       1002, surface_->resource(), wl_fixed_from_int(entered_point.x()),
-      wl_fixed_from_int(entered_point.y()), *data_offer);
+      wl_fixed_from_int(entered_point.y()), data_offer);
 
   int64_t time =
       (ui::EventTimeForNow() - base::TimeTicks()).InMilliseconds() & UINT32_MAX;
diff --git a/ui/ozone/platform/wayland/wayland_input_method_context_unittest.cc b/ui/ozone/platform/wayland/wayland_input_method_context_unittest.cc
index c4f90e0..bd19f70 100644
--- a/ui/ozone/platform/wayland/wayland_input_method_context_unittest.cc
+++ b/ui/ozone/platform/wayland/wayland_input_method_context_unittest.cc
@@ -11,13 +11,15 @@
 #include "ui/base/ime/linux/linux_input_method_context.h"
 #include "ui/events/event.h"
 #include "ui/ozone/platform/wayland/fake_server.h"
+#include "ui/ozone/platform/wayland/test/mock_surface.h"
+#include "ui/ozone/platform/wayland/test/mock_zwp_text_input.h"
 #include "ui/ozone/platform/wayland/wayland_input_method_context.h"
 #include "ui/ozone/platform/wayland/wayland_input_method_context_factory.h"
 #include "ui/ozone/platform/wayland/wayland_test.h"
 #include "ui/ozone/platform/wayland/wayland_window.h"
 
-using ::testing::SaveArg;
 using ::testing::_;
+using ::testing::SaveArg;
 
 namespace ui {
 
@@ -69,7 +71,7 @@
 
     Sync();
 
-    zwp_text_input_ = server_.text_input_manager_v1()->text_input.get();
+    zwp_text_input_ = server_.text_input_manager_v1()->text_input();
     window_->set_keyboard_focus(true);
 
     ASSERT_TRUE(connection_->text_input_manager_v1());
diff --git a/ui/ozone/platform/wayland/wayland_keyboard_unittest.cc b/ui/ozone/platform/wayland/wayland_keyboard_unittest.cc
index 054bfc7..e33fb33 100644
--- a/ui/ozone/platform/wayland/wayland_keyboard_unittest.cc
+++ b/ui/ozone/platform/wayland/wayland_keyboard_unittest.cc
@@ -11,6 +11,8 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/events/event.h"
 #include "ui/ozone/platform/wayland/fake_server.h"
+#include "ui/ozone/platform/wayland/test/mock_surface.h"
+#include "ui/ozone/platform/wayland/test/test_keyboard.h"
 #include "ui/ozone/platform/wayland/wayland_test.h"
 
 #if BUILDFLAG(USE_XKBCOMMON)
@@ -61,7 +63,7 @@
   }
 
  protected:
-  wl::MockKeyboard* keyboard_;
+  wl::TestKeyboard* keyboard_;
 
  private:
 #if BUILDFLAG(USE_XKBCOMMON)
diff --git a/ui/ozone/platform/wayland/wayland_pointer_unittest.cc b/ui/ozone/platform/wayland/wayland_pointer_unittest.cc
index 88082f63..2461a2b 100644
--- a/ui/ozone/platform/wayland/wayland_pointer_unittest.cc
+++ b/ui/ozone/platform/wayland/wayland_pointer_unittest.cc
@@ -9,6 +9,8 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/events/event.h"
 #include "ui/ozone/platform/wayland/fake_server.h"
+#include "ui/ozone/platform/wayland/test/mock_surface.h"
+#include "ui/ozone/platform/wayland/test/test_pointer.h"
 #include "ui/ozone/platform/wayland/wayland_test.h"
 #include "ui/ozone/platform/wayland/wayland_window.h"
 #include "ui/ozone/test/mock_platform_window_delegate.h"
@@ -36,7 +38,7 @@
   }
 
  protected:
-  wl::MockPointer* pointer_;
+  wl::TestPointer* pointer_;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(WaylandPointerTest);
diff --git a/ui/ozone/platform/wayland/wayland_screen_unittest.cc b/ui/ozone/platform/wayland/wayland_screen_unittest.cc
index 0b76334..9055e383 100644
--- a/ui/ozone/platform/wayland/wayland_screen_unittest.cc
+++ b/ui/ozone/platform/wayland/wayland_screen_unittest.cc
@@ -7,6 +7,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/display/display_observer.h"
 #include "ui/ozone/platform/wayland/fake_server.h"
+#include "ui/ozone/platform/wayland/test/mock_surface.h"
 #include "ui/ozone/platform/wayland/wayland_connection.h"
 #include "ui/ozone/platform/wayland/wayland_output_manager.h"
 #include "ui/ozone/platform/wayland/wayland_screen.h"
@@ -110,7 +111,7 @@
     EXPECT_EQ(display_for_widget.id(), expected_display_id);
   }
 
-  wl::MockOutput* output_ = nullptr;
+  wl::TestOutput* output_ = nullptr;
   WaylandOutputManager* output_manager_ = nullptr;
 
   std::unique_ptr<WaylandScreen> platform_screen_;
@@ -142,7 +143,7 @@
       platform_screen_->GetPrimaryDisplay().id();
 
   // Add a second display.
-  wl::MockOutput* output2 = server_.CreateAndInitializeOutput();
+  wl::TestOutput* output2 = server_.CreateAndInitializeOutput();
 
   Sync();
 
@@ -295,7 +296,7 @@
   const display::Display primary_display =
       platform_screen_->GetPrimaryDisplay();
 
-  wl::MockOutput* output2 = server_.CreateAndInitializeOutput();
+  wl::TestOutput* output2 = server_.CreateAndInitializeOutput();
 
   Sync();
 
@@ -366,7 +367,7 @@
       platform_screen_->GetPrimaryDisplay();
 
   // Create an additional display.
-  wl::MockOutput* output2 = server_.CreateAndInitializeOutput();
+  wl::TestOutput* output2 = server_.CreateAndInitializeOutput();
 
   Sync();
 
diff --git a/ui/ozone/platform/wayland/wayland_surface_factory_unittest.cc b/ui/ozone/platform/wayland/wayland_surface_factory_unittest.cc
index f684bcb4..876b51c 100644
--- a/ui/ozone/platform/wayland/wayland_surface_factory_unittest.cc
+++ b/ui/ozone/platform/wayland/wayland_surface_factory_unittest.cc
@@ -2,10 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "ui/ozone/platform/wayland/wayland_surface_factory.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/skia/include/core/SkSurface.h"
 #include "ui/ozone/platform/wayland/fake_server.h"
-#include "ui/ozone/platform/wayland/wayland_surface_factory.h"
+#include "ui/ozone/platform/wayland/test/mock_surface.h"
 #include "ui/ozone/platform/wayland/wayland_test.h"
 #include "ui/ozone/platform/wayland/wayland_window.h"
 #include "ui/ozone/public/surface_ozone_canvas.h"
diff --git a/ui/ozone/platform/wayland/wayland_test.cc b/ui/ozone/platform/wayland/wayland_test.cc
index 8db8147..60e3d19 100644
--- a/ui/ozone/platform/wayland/wayland_test.cc
+++ b/ui/ozone/platform/wayland/wayland_test.cc
@@ -6,6 +6,7 @@
 
 #include "base/run_loop.h"
 #include "ui/events/ozone/layout/keyboard_layout_engine_manager.h"
+#include "ui/ozone/platform/wayland/test/mock_surface.h"
 #include "ui/platform_window/platform_window_init_properties.h"
 
 #if BUILDFLAG(USE_XKBCOMMON)
diff --git a/ui/ozone/platform/wayland/wayland_test.h b/ui/ozone/platform/wayland/wayland_test.h
index 7c21090..22a58d0 100644
--- a/ui/ozone/platform/wayland/wayland_test.h
+++ b/ui/ozone/platform/wayland/wayland_test.h
@@ -18,6 +18,10 @@
 #include "ui/events/ozone/layout/xkb/xkb_evdev_codes.h"
 #endif
 
+namespace wl {
+class MockSurface;
+}  // namespace wl
+
 namespace ui {
 
 const uint32_t kXdgShellV5 = 5;
diff --git a/ui/ozone/platform/wayland/wayland_touch_unittest.cc b/ui/ozone/platform/wayland/wayland_touch_unittest.cc
index 43f6121..e641fdbe 100644
--- a/ui/ozone/platform/wayland/wayland_touch_unittest.cc
+++ b/ui/ozone/platform/wayland/wayland_touch_unittest.cc
@@ -9,6 +9,8 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/events/event.h"
 #include "ui/ozone/platform/wayland/fake_server.h"
+#include "ui/ozone/platform/wayland/test/mock_surface.h"
+#include "ui/ozone/platform/wayland/test/test_touch.h"
 #include "ui/ozone/platform/wayland/wayland_test.h"
 #include "ui/ozone/platform/wayland/wayland_window.h"
 #include "ui/ozone/test/mock_platform_window_delegate.h"
@@ -51,7 +53,7 @@
     EXPECT_EQ(event_type, key_event->type());
   }
 
-  wl::MockTouch* touch_;
+  wl::TestTouch* touch_;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(WaylandTouchTest);
diff --git a/ui/ozone/platform/wayland/wayland_window_unittest.cc b/ui/ozone/platform/wayland/wayland_window_unittest.cc
index f0e35c90..9e1c0fe 100644
--- a/ui/ozone/platform/wayland/wayland_window_unittest.cc
+++ b/ui/ozone/platform/wayland/wayland_window_unittest.cc
@@ -17,6 +17,8 @@
 #include "ui/events/base_event_utils.h"
 #include "ui/events/event.h"
 #include "ui/ozone/platform/wayland/fake_server.h"
+#include "ui/ozone/platform/wayland/test/mock_surface.h"
+#include "ui/ozone/platform/wayland/test/test_pointer.h"
 #include "ui/ozone/platform/wayland/wayland_test.h"
 #include "ui/ozone/platform/wayland/wayland_util.h"
 #include "ui/ozone/test/mock_platform_window_delegate.h"
@@ -523,7 +525,7 @@
 
   Sync();
 
-  wl::MockPointer* pointer = server_.seat()->pointer();
+  wl::TestPointer* pointer = server_.seat()->pointer();
   ASSERT_TRUE(pointer);
 
   wl_pointer_send_enter(pointer->resource(), 1, surface_->resource(), 0, 0);
diff --git a/ui/views/mus/BUILD.gn b/ui/views/mus/BUILD.gn
index 729871c..2699ec48 100644
--- a/ui/views/mus/BUILD.gn
+++ b/ui/views/mus/BUILD.gn
@@ -19,6 +19,8 @@
     "ax_remote_host.h",
     "ax_tree_source_mus.cc",
     "ax_tree_source_mus.h",
+    "cursor_manager_owner.cc",
+    "cursor_manager_owner.h",
     "desktop_window_tree_host_mus.cc",
     "desktop_window_tree_host_mus.h",
     "mus_client.cc",
diff --git a/ui/views/mus/cursor_manager_owner.cc b/ui/views/mus/cursor_manager_owner.cc
new file mode 100644
index 0000000..69356a4
--- /dev/null
+++ b/ui/views/mus/cursor_manager_owner.cc
@@ -0,0 +1,87 @@
+// 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.
+
+#include "ui/views/mus/cursor_manager_owner.h"
+
+#include <memory>
+
+#include "ui/aura/client/cursor_client.h"
+#include "ui/aura/mus/window_port_mus.h"
+#include "ui/base/cursor/cursor.h"
+#include "ui/wm/core/cursor_manager.h"
+#include "ui/wm/core/native_cursor_manager.h"
+#include "ui/wm/core/native_cursor_manager_delegate.h"
+
+namespace views {
+
+namespace {
+
+class NativeCursorManagerMus : public wm::NativeCursorManager {
+ public:
+  explicit NativeCursorManagerMus(aura::Window* window) : window_(window) {}
+  ~NativeCursorManagerMus() override = default;
+
+ private:
+  // wm::NativeCursorManager:
+  void SetDisplay(const display::Display& display,
+                  wm::NativeCursorManagerDelegate* delegate) override {
+    // We ignore this entirely, as cursor are set on the client.
+  }
+  void SetCursor(ui::Cursor cursor,
+                 wm::NativeCursorManagerDelegate* delegate) override {
+    aura::WindowPortMus::Get(window_)->SetCursor(cursor);
+    delegate->CommitCursor(cursor);
+  }
+
+  void SetVisibility(bool visible,
+                     wm::NativeCursorManagerDelegate* delegate) override {
+    delegate->CommitVisibility(visible);
+
+    if (visible) {
+      SetCursor(delegate->GetCursor(), delegate);
+    } else {
+      aura::WindowPortMus::Get(window_)->SetCursor(
+          ui::Cursor(ui::CursorType::kNone));
+    }
+  }
+  void SetCursorSize(ui::CursorSize cursor_size,
+                     wm::NativeCursorManagerDelegate* delegate) override {
+    // TODO(erg): For now, ignore the difference between SET_NORMAL and
+    // SET_LARGE here. This feels like a thing that mus should decide instead.
+    //
+    // Also, it's NOTIMPLEMENTED() in the desktop version!? Including not
+    // acknowledging the call in the delegate.
+    NOTIMPLEMENTED();
+  }
+  void SetMouseEventsEnabled(
+      bool enabled,
+      wm::NativeCursorManagerDelegate* delegate) override {
+    // TODO(erg): How do we actually implement this?
+    //
+    // Mouse event dispatch is potentially done in a different process,
+    // definitely in a different mojo service. Each app is fairly locked down.
+    delegate->CommitMouseEventsEnabled(enabled);
+    NOTIMPLEMENTED();
+  }
+
+  aura::Window* window_;
+
+  DISALLOW_COPY_AND_ASSIGN(NativeCursorManagerMus);
+};
+
+}  // namespace
+
+CursorManagerOwner::CursorManagerOwner(aura::Window* window)
+    : cursor_manager_(std::make_unique<wm::CursorManager>(
+          std::make_unique<NativeCursorManagerMus>(window))) {
+  tracker_.Add(window);
+  aura::client::SetCursorClient(window, cursor_manager_.get());
+}
+
+CursorManagerOwner::~CursorManagerOwner() {
+  if (!tracker_.windows().empty())
+    aura::client::SetCursorClient(tracker_.Pop(), nullptr);
+}
+
+}  // namespace views
diff --git a/ui/views/mus/cursor_manager_owner.h b/ui/views/mus/cursor_manager_owner.h
new file mode 100644
index 0000000..ece09198
--- /dev/null
+++ b/ui/views/mus/cursor_manager_owner.h
@@ -0,0 +1,40 @@
+// 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.
+
+#ifndef UI_VIEWS_MUS_CURSOR_MANAGER_OWNER_H_
+#define UI_VIEWS_MUS_CURSOR_MANAGER_OWNER_H_
+
+#include <memory>
+
+#include "ui/aura/window_tracker.h"
+#include "ui/views/mus/mus_export.h"
+
+namespace aura {
+class Window;
+}
+
+namespace wm {
+class CursorManager;
+}
+
+namespace views {
+
+// This class owns the cursor manager which can communicate with the window
+// server and keeps it as the cursor-client for the specified window as far as
+// this class lives.
+class VIEWS_MUS_EXPORT CursorManagerOwner {
+ public:
+  explicit CursorManagerOwner(aura::Window* window);
+  ~CursorManagerOwner();
+
+ private:
+  aura::WindowTracker tracker_;
+  std::unique_ptr<wm::CursorManager> cursor_manager_;
+
+  DISALLOW_COPY_AND_ASSIGN(CursorManagerOwner);
+};
+
+}  // namespace views
+
+#endif  // UI_VIEWS_MUS_CURSOR_MANAGER_OWNER_H_
diff --git a/ui/views/mus/desktop_window_tree_host_mus.cc b/ui/views/mus/desktop_window_tree_host_mus.cc
index 7bd09f5e..d7659e0 100644
--- a/ui/views/mus/desktop_window_tree_host_mus.cc
+++ b/ui/views/mus/desktop_window_tree_host_mus.cc
@@ -9,13 +9,11 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/client/capture_client.h"
-#include "ui/aura/client/cursor_client.h"
 #include "ui/aura/client/drag_drop_client.h"
 #include "ui/aura/client/focus_client.h"
 #include "ui/aura/client/transient_window_client.h"
 #include "ui/aura/env.h"
 #include "ui/aura/mus/focus_synchronizer.h"
-#include "ui/aura/mus/window_port_mus.h"
 #include "ui/aura/mus/window_tree_client.h"
 #include "ui/aura/mus/window_tree_host_mus.h"
 #include "ui/aura/mus/window_tree_host_mus_init_params.h"
@@ -29,14 +27,13 @@
 #include "ui/gfx/geometry/vector2d_conversions.h"
 #include "ui/views/accessibility/view_accessibility.h"
 #include "ui/views/corewm/tooltip_aura.h"
+#include "ui/views/mus/cursor_manager_owner.h"
 #include "ui/views/mus/mus_client.h"
 #include "ui/views/mus/mus_property_mirror.h"
 #include "ui/views/mus/window_manager_frame_values.h"
 #include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h"
 #include "ui/views/widget/native_widget_aura.h"
 #include "ui/views/widget/widget_delegate.h"
-#include "ui/wm/core/cursor_manager.h"
-#include "ui/wm/core/native_cursor_manager.h"
 #include "ui/wm/core/window_util.h"
 #include "ui/wm/public/activation_client.h"
 
@@ -157,62 +154,6 @@
   DISALLOW_COPY_AND_ASSIGN(ClientSideNonClientFrameView);
 };
 
-class NativeCursorManagerMus : public wm::NativeCursorManager {
- public:
-  explicit NativeCursorManagerMus(aura::Window* window) : window_(window) {}
-  ~NativeCursorManagerMus() override {}
-
-  // wm::NativeCursorManager:
-  void SetDisplay(const display::Display& display,
-                  wm::NativeCursorManagerDelegate* delegate) override {
-    // We ignore this entirely, as cursor are set on the client.
-  }
-
-  void SetCursor(ui::Cursor cursor,
-                 wm::NativeCursorManagerDelegate* delegate) override {
-    aura::WindowPortMus::Get(window_)->SetCursor(cursor);
-    delegate->CommitCursor(cursor);
-  }
-
-  void SetVisibility(bool visible,
-                     wm::NativeCursorManagerDelegate* delegate) override {
-    delegate->CommitVisibility(visible);
-
-    if (visible) {
-      SetCursor(delegate->GetCursor(), delegate);
-    } else {
-      aura::WindowPortMus::Get(window_)->SetCursor(
-          ui::Cursor(ui::CursorType::kNone));
-    }
-  }
-
-  void SetCursorSize(ui::CursorSize cursor_size,
-                     wm::NativeCursorManagerDelegate* delegate) override {
-    // TODO(erg): For now, ignore the difference between SET_NORMAL and
-    // SET_LARGE here. This feels like a thing that mus should decide instead.
-    //
-    // Also, it's NOTIMPLEMENTED() in the desktop version!? Including not
-    // acknowledging the call in the delegate.
-    NOTIMPLEMENTED();
-  }
-
-  void SetMouseEventsEnabled(
-      bool enabled,
-      wm::NativeCursorManagerDelegate* delegate) override {
-    // TODO(erg): How do we actually implement this?
-    //
-    // Mouse event dispatch is potentially done in a different process,
-    // definitely in a different mojo service. Each app is fairly locked down.
-    delegate->CommitMouseEventsEnabled(enabled);
-    NOTIMPLEMENTED();
-  }
-
- private:
-  aura::Window* window_;
-
-  DISALLOW_COPY_AND_ASSIGN(NativeCursorManagerMus);
-};
-
 void OnMoveLoopEnd(bool* out_success,
                    base::Closure quit_closure,
                    bool in_success) {
@@ -290,10 +231,6 @@
 DesktopWindowTreeHostMus::~DesktopWindowTreeHostMus() {
   window_tree_host_window_observer_.reset();
 
-  // The cursor-client can be accessed during WindowTreeHostMus tear-down. So
-  // the cursor-client needs to be unset on the root-window before
-  // |cursor_manager_| is destroyed.
-  aura::client::SetCursorClient(window(), nullptr);
   content_window()->RemoveObserver(this);
   MusClient::Get()->RemoveObserver(this);
   MusClient::Get()->window_tree_client()->focus_synchronizer()->RemoveObserver(
@@ -389,9 +326,7 @@
     SetBoundsInDIP(params.bounds);
   }
 
-  cursor_manager_ = std::make_unique<wm::CursorManager>(
-      std::make_unique<NativeCursorManagerMus>(window()));
-  aura::client::SetCursorClient(window(), cursor_manager_.get());
+  cursor_manager_owner_ = std::make_unique<CursorManagerOwner>(window());
   InitHost();
 
   NativeWidgetAura::SetShadowElevationFromInitParams(window(), params);
diff --git a/ui/views/mus/desktop_window_tree_host_mus.h b/ui/views/mus/desktop_window_tree_host_mus.h
index 9daf24b3..4674a08 100644
--- a/ui/views/mus/desktop_window_tree_host_mus.h
+++ b/ui/views/mus/desktop_window_tree_host_mus.h
@@ -19,12 +19,10 @@
 #include "ui/views/widget/desktop_aura/desktop_window_tree_host.h"
 #include "ui/views/widget/widget.h"
 
-namespace wm {
-class CursorManager;
-}
-
 namespace views {
 
+class CursorManagerOwner;
+
 class VIEWS_MUS_EXPORT DesktopWindowTreeHostMus
     : public DesktopWindowTreeHost,
       public MusClientObserver,
@@ -186,7 +184,7 @@
 
   bool is_active_ = false;
 
-  std::unique_ptr<wm::CursorManager> cursor_manager_;
+  std::unique_ptr<CursorManagerOwner> cursor_manager_owner_;
 
   bool auto_update_client_area_ = true;
 
diff --git a/ui/views/mus/remote_view/remote_view_provider.cc b/ui/views/mus/remote_view/remote_view_provider.cc
index 01c87c6d..2eab082 100644
--- a/ui/views/mus/remote_view/remote_view_provider.cc
+++ b/ui/views/mus/remote_view/remote_view_provider.cc
@@ -15,6 +15,7 @@
 #include "ui/aura/window.h"
 #include "ui/aura/window_observer.h"
 #include "ui/gfx/geometry/size.h"
+#include "ui/views/mus/cursor_manager_owner.h"
 #include "ui/views/mus/mus_client.h"
 #include "ui/views/widget/desktop_aura/desktop_screen_position_client.h"
 
@@ -140,6 +141,7 @@
   embedding_window_observer_ = std::make_unique<EmbeddingWindowObserver>(
       window, base::BindRepeating(&RemoteViewProvider::OnEmbeddingWindowResized,
                                   base::Unretained(this)));
+  cursor_manager_owner_ = std::make_unique<CursorManagerOwner>(window);
   OnEmbeddingWindowResized(window->bounds().size());
   window->AddChild(embedded_);
 
@@ -150,6 +152,7 @@
 void RemoteViewProvider::OnUnembed() {
   screen_position_client_.reset();
   embedding_window_observer_.reset();
+  cursor_manager_owner_.reset();
   embed_root_.reset();
 
   if (on_unembed_callback_)
diff --git a/ui/views/mus/remote_view/remote_view_provider.h b/ui/views/mus/remote_view/remote_view_provider.h
index ac3722e..aa2574b 100644
--- a/ui/views/mus/remote_view/remote_view_provider.h
+++ b/ui/views/mus/remote_view/remote_view_provider.h
@@ -24,6 +24,7 @@
 
 namespace views {
 
+class CursorManagerOwner;
 class DesktopScreenPositionClient;
 
 namespace test {
@@ -96,6 +97,8 @@
   // aura::Window::GetBoundsInScreen() work for any descendants.
   std::unique_ptr<DesktopScreenPositionClient> screen_position_client_;
 
+  std::unique_ptr<CursorManagerOwner> cursor_manager_owner_;
+
   DISALLOW_COPY_AND_ASSIGN(RemoteViewProvider);
 };
 
diff --git a/ui/views/mus/remote_view/remote_view_provider_unittest.cc b/ui/views/mus/remote_view/remote_view_provider_unittest.cc
index 0ff8e8f3..e3fc0485 100644
--- a/ui/views/mus/remote_view/remote_view_provider_unittest.cc
+++ b/ui/views/mus/remote_view/remote_view_provider_unittest.cc
@@ -12,6 +12,7 @@
 #include "base/run_loop.h"
 #include "base/test/bind_test_util.h"
 #include "base/unguessable_token.h"
+#include "ui/aura/client/cursor_client.h"
 #include "ui/aura/client/focus_change_observer.h"
 #include "ui/aura/mus/window_mus.h"
 #include "ui/aura/test/aura_test_base.h"
@@ -221,4 +222,17 @@
   EXPECT_TRUE(observer.on_window_focused_called());
 }
 
+TEST_F(RemoteViewProviderTest, Cursor) {
+  aura::Window* embedder = SimulateEmbed();
+  ASSERT_TRUE(embedder);
+
+  auto* cursor_client =
+      aura::client::GetCursorClient(embedded_->GetRootWindow());
+  ASSERT_TRUE(cursor_client);
+
+  EXPECT_NE(window_tree()->last_cursor(), ui::CursorType::kHand);
+  cursor_client->SetCursor(ui::CursorType::kHand);
+  EXPECT_EQ(window_tree()->last_cursor(), ui::CursorType::kHand);
+}
+
 }  // namespace views