diff --git a/DEPS b/DEPS
index 0c35ba34..bdb4bfb7 100644
--- a/DEPS
+++ b/DEPS
@@ -129,11 +129,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': 'f82158fb7fdc233ea32598d795a656e7a08adb1e',
+  'skia_revision': 'd521ce39e99f99651be0ff2ba46376b60b6bc7d9',
   # 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': 'ba65b118eb24c527a0669d3ce40031b124e6fb20',
+  'v8_revision': 'c6fbb279f3137a8cdc09ae48eb0b84744004079a',
   # 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.
@@ -196,7 +196,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': 'ed5998919f4b97464cb0f6cd640ad78f101a498d',
+  'catapult_revision': 'd7f22e29c4a1be844b5eb8153cb3b95775f2af7e',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -252,7 +252,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': 'b68af7ca8e027c54341dfc9b19c3df80fa07a78e',
+  'spv_tools_revision': 'dca3ea5e1719ef996111fa53582fc592d5f891ed',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -268,7 +268,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'dawn_revision': '8e97b4c8a5126a36ff03b7f6581b45b6d322a331',
+  'dawn_revision': '15d4c2e63b7281d622eba063c615be73358af260',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -1183,7 +1183,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' +  '5ee4ec84de3c8760a2982824262803caca10e1b3',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' +  '999374532cec467910846b6af52df01aae7278ea',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + 'ac0d98b5cee6c024b0cffeb4f8f45b6fc5ccdb78',
@@ -1354,7 +1354,7 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + '6f0b34abee8dba611c253738d955c59f703c147a',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '637bed5f8d90c6990687b4de03350609cf907783',
+    Var('webrtc_git') + '/src.git' + '@' + 'e670fd97951a967283f74ff9b313dc16aa19e7af',
 
   'src/third_party/xdg-utils': {
       'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d',
@@ -1395,7 +1395,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@352cf25552e6945de83f895ddc9f821bfa27304e',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@75c4cbfa717ae8cb1a946886dce1c863bfc7833b',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/android_webview/browser/gfx/parent_output_surface.cc b/android_webview/browser/gfx/parent_output_surface.cc
index 24b3e718..4b4c44f 100644
--- a/android_webview/browser/gfx/parent_output_surface.cc
+++ b/android_webview/browser/gfx/parent_output_surface.cc
@@ -113,4 +113,7 @@
   return 0;
 }
 
+void ParentOutputSurface::SetUpdateVSyncParametersCallback(
+    viz::UpdateVSyncParametersCallback callback) {}
+
 }  // namespace android_webview
diff --git a/android_webview/browser/gfx/parent_output_surface.h b/android_webview/browser/gfx/parent_output_surface.h
index 95d60d9..12bda6e 100644
--- a/android_webview/browser/gfx/parent_output_surface.h
+++ b/android_webview/browser/gfx/parent_output_surface.h
@@ -45,6 +45,8 @@
   unsigned GetOverlayTextureId() const override;
   gfx::BufferFormat GetOverlayBufferFormat() const override;
   unsigned UpdateGpuFence() override;
+  void SetUpdateVSyncParametersCallback(
+      viz::UpdateVSyncParametersCallback callback) override;
 
  private:
   void OnPresentation(const gfx::PresentationFeedback& feedback);
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index f4156f3b..1234f9cf 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -1205,6 +1205,8 @@
     "wm/tablet_mode/tablet_mode_window_state.h",
     "wm/top_level_window_factory.cc",
     "wm/top_level_window_factory.h",
+    "wm/toplevel_window_event_handler.cc",
+    "wm/toplevel_window_event_handler.h",
     "wm/video_detector.cc",
     "wm/video_detector.h",
     "wm/widget_finder.cc",
@@ -1242,8 +1244,6 @@
     "wm/wm_shadow_controller_delegate.h",
     "wm/wm_snap_to_pixel_layout_manager.cc",
     "wm/wm_snap_to_pixel_layout_manager.h",
-    "wm/wm_toplevel_window_event_handler.cc",
-    "wm/wm_toplevel_window_event_handler.h",
     "wm/wm_window_animations.cc",
     "wm/wm_window_animations.h",
     "wm/work_area_insets.cc",
diff --git a/ash/shell.cc b/ash/shell.cc
index 87e6738..a46c6e7 100644
--- a/ash/shell.cc
+++ b/ash/shell.cc
@@ -155,6 +155,7 @@
 #include "ash/wm/system_modal_container_layout_manager.h"
 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
 #include "ash/wm/tablet_mode/tablet_mode_window_manager.h"
+#include "ash/wm/toplevel_window_event_handler.h"
 #include "ash/wm/video_detector.h"
 #include "ash/wm/window_animations.h"
 #include "ash/wm/window_cycle_controller.h"
@@ -162,7 +163,6 @@
 #include "ash/wm/window_properties.h"
 #include "ash/wm/window_util.h"
 #include "ash/wm/wm_shadow_controller_delegate.h"
-#include "ash/wm/wm_toplevel_window_event_handler.h"
 #include "ash/wm/workspace_controller.h"
 #include "ash/ws/ax_ash_window_utils.h"
 #include "ash/ws/window_service_owner.h"
diff --git a/ash/system/power/power_event_observer.cc b/ash/system/power/power_event_observer.cc
index 7959a9ca..fc1f4a1 100644
--- a/ash/system/power/power_event_observer.cc
+++ b/ash/system/power/power_event_observer.cc
@@ -30,9 +30,8 @@
 
 namespace {
 
-void OnSuspendDisplaysCompleted(base::OnceClosure suspend_callback,
-                                bool status) {
-  std::move(suspend_callback).Run();
+void OnSuspendDisplaysCompleted(base::UnguessableToken token, bool status) {
+  chromeos::PowerManagerClient::Get()->UnblockSuspend(token);
 }
 
 // Returns whether the screen should be locked when device is suspended.
@@ -233,7 +232,7 @@
 
   // If suspending, run pending animations to the end immediately, as there is
   // no point in waiting for them to finish given that the device is suspending.
-  if (displays_suspended_callback_)
+  if (block_suspend_token_)
     EndPendingWallpaperAnimations();
 
   // The |compositor_watcher_| is owned by this, and the callback passed to it
@@ -248,9 +247,9 @@
     power_manager::SuspendImminent::Reason reason) {
   suspend_in_progress_ = true;
 
-  displays_suspended_callback_ =
-      chromeos::PowerManagerClient::Get()->GetSuspendReadinessCallback(
-          FROM_HERE);
+  block_suspend_token_ = base::UnguessableToken::Create();
+  chromeos::PowerManagerClient::Get()->BlockSuspend(block_suspend_token_,
+                                                    "PowerEventObserver");
 
   // Stop compositing immediately if
   // * the screen lock flow has already completed
@@ -284,7 +283,7 @@
   // animation to complete, clear the blocker since the suspend has already
   // completed.  This prevents rendering requests from being blocked after a
   // resume if the lock screen took too long to show.
-  displays_suspended_callback_.Reset();
+  block_suspend_token_ = {};
 
   StartRootWindowCompositors();
 }
@@ -295,11 +294,10 @@
 
     // The screen is now locked but the pending suspend, if any, will be blocked
     // until all the animations have completed.
-    if (displays_suspended_callback_) {
+    if (block_suspend_token_)
       VLOG(1) << "Screen locked due to suspend";
-    } else {
+    else
       VLOG(1) << "Screen locked without suspend";
-    }
   } else {
     lock_state_ = LockState::kUnlocked;
     compositor_watcher_.reset();
@@ -314,7 +312,7 @@
       if (ShouldLockOnSuspend()) {
         lock_state_ = LockState::kLocking;
         Shell::Get()->lock_state_controller()->LockWithoutAnimation();
-      } else if (displays_suspended_callback_) {
+      } else if (block_suspend_token_) {
         StopCompositingAndSuspendDisplays();
       }
     }
@@ -330,7 +328,7 @@
 }
 
 void PowerEventObserver::StopCompositingAndSuspendDisplays() {
-  DCHECK(displays_suspended_callback_);
+  DCHECK(block_suspend_token_);
   DCHECK(!compositor_watcher_.get());
   for (aura::Window* window : Shell::GetAllRootWindows()) {
     ui::Compositor* compositor = window->GetHost()->compositor();
@@ -340,8 +338,8 @@
   ui::UserActivityDetector::Get()->OnDisplayPowerChanging();
 
   Shell::Get()->display_configurator()->SuspendDisplays(
-      base::Bind(&OnSuspendDisplaysCompleted,
-                 base::Passed(&displays_suspended_callback_)));
+      base::Bind(&OnSuspendDisplaysCompleted, block_suspend_token_));
+  block_suspend_token_ = {};
 }
 
 void PowerEventObserver::EndPendingWallpaperAnimations() {
@@ -357,7 +355,7 @@
   compositor_watcher_.reset();
   lock_state_ = LockState::kLocked;
 
-  if (displays_suspended_callback_)
+  if (block_suspend_token_)
     StopCompositingAndSuspendDisplays();
 }
 
diff --git a/ash/system/power/power_event_observer.h b/ash/system/power/power_event_observer.h
index 0f1a0b0..4c49d21 100644
--- a/ash/system/power/power_event_observer.h
+++ b/ash/system/power/power_event_observer.h
@@ -9,9 +9,9 @@
 
 #include "ash/ash_export.h"
 #include "ash/session/session_observer.h"
-#include "base/callback.h"
 #include "base/compiler_specific.h"
 #include "base/macros.h"
+#include "base/unguessable_token.h"
 #include "chromeos/dbus/power/power_manager_client.h"
 
 namespace ui {
@@ -78,11 +78,11 @@
   void StartRootWindowCompositors();
 
   // Sets all root window compositors' visibility to false, and then suspends
-  // displays. It will run |display_suspended_callback_| once displays are
-  // suspended.
-  // This should only be called when it's safe to stop compositing -
-  // either if the screen is not expected to get locked, or all compositors
-  // have gone through compositing cycle after the screen was locked.
+  // displays. It will run unblock suspend via |block_suspend_token_| once
+  // displays are suspended. This should only be called when it's safe to stop
+  // compositing - either if the screen is not expected to get locked, or all
+  // compositors have gone through compositing cycle after the screen was
+  // locked.
   void StopCompositingAndSuspendDisplays();
 
   // If any of the root windows have pending wallpaper animations, it stops
@@ -106,13 +106,13 @@
   // compositors are in state in which it's safe to proceed with suspend.
   std::unique_ptr<ui::CompositorObserver> compositor_watcher_;
 
-  // Callback set when device suspend is delayed due to a screen lock - suspend
+  // Token set when device suspend is delayed due to a screen lock - suspend
   // should be continued when the screen lock finishes showing and display
   // compositors pick up screen lock changes. All compositors should be stopped
-  // prior to calling this - call StopCompositingAndSuspendDisplays() instead of
-  // runnig this callback directly.
-  // This will only be set while the device is suspending.
-  base::OnceClosure displays_suspended_callback_;
+  // prior to unblocking and clearing this - call
+  // StopCompositingAndSuspendDisplays(). This will only be set while the device
+  // is suspending.
+  base::UnguessableToken block_suspend_token_;
 
   DISALLOW_COPY_AND_ASSIGN(PowerEventObserver);
 };
diff --git a/ash/wm/tablet_mode/tablet_mode_window_drag_controller.cc b/ash/wm/tablet_mode/tablet_mode_window_drag_controller.cc
index 2ebf639a..b69dd01 100644
--- a/ash/wm/tablet_mode/tablet_mode_window_drag_controller.cc
+++ b/ash/wm/tablet_mode/tablet_mode_window_drag_controller.cc
@@ -7,8 +7,8 @@
 #include "ash/shell.h"
 #include "ash/wm/tablet_mode/tablet_mode_browser_window_drag_delegate.h"
 #include "ash/wm/tablet_mode/tablet_mode_window_drag_metrics.h"
+#include "ash/wm/toplevel_window_event_handler.h"
 #include "ash/wm/window_util.h"
-#include "ash/wm/wm_toplevel_window_event_handler.h"
 #include "ui/wm/core/coordinate_conversion.h"
 #include "ui/wm/core/cursor_manager.h"
 
diff --git a/ash/wm/tablet_mode/tablet_mode_window_drag_delegate.h b/ash/wm/tablet_mode/tablet_mode_window_drag_delegate.h
index 3acfcc9..1e66a6c 100644
--- a/ash/wm/tablet_mode/tablet_mode_window_drag_delegate.h
+++ b/ash/wm/tablet_mode/tablet_mode_window_drag_delegate.h
@@ -10,7 +10,7 @@
 #include "ash/public/cpp/presentation_time_recorder.h"
 #include "ash/public/cpp/window_properties.h"
 #include "ash/wm/splitview/split_view_controller.h"
-#include "ash/wm/wm_toplevel_window_event_handler.h"
+#include "ash/wm/toplevel_window_event_handler.h"
 #include "base/memory/weak_ptr.h"
 #include "base/optional.h"
 #include "ui/aura/window_occlusion_tracker.h"
diff --git a/ash/wm/wm_toplevel_window_event_handler.cc b/ash/wm/toplevel_window_event_handler.cc
similarity index 99%
rename from ash/wm/wm_toplevel_window_event_handler.cc
rename to ash/wm/toplevel_window_event_handler.cc
index eddb86c..11e22b1 100644
--- a/ash/wm/wm_toplevel_window_event_handler.cc
+++ b/ash/wm/toplevel_window_event_handler.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ash/wm/wm_toplevel_window_event_handler.h"
+#include "ash/wm/toplevel_window_event_handler.h"
 
 #include "ash/public/cpp/app_types.h"
 #include "ash/shell.h"
diff --git a/ash/wm/wm_toplevel_window_event_handler.h b/ash/wm/toplevel_window_event_handler.h
similarity index 97%
rename from ash/wm/wm_toplevel_window_event_handler.h
rename to ash/wm/toplevel_window_event_handler.h
index 9ba0d5a..a9c48be 100644
--- a/ash/wm/wm_toplevel_window_event_handler.h
+++ b/ash/wm/toplevel_window_event_handler.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef ASH_WM_WM_TOPLEVEL_WINDOW_EVENT_HANDLER_H_
-#define ASH_WM_WM_TOPLEVEL_WINDOW_EVENT_HANDLER_H_
+#ifndef ASH_WM_TOPLEVEL_WINDOW_EVENT_HANDLER_H_
+#define ASH_WM_TOPLEVEL_WINDOW_EVENT_HANDLER_H_
 
 #include <memory>
 
@@ -177,4 +177,4 @@
 
 }  // namespace ash
 
-#endif  // ASH_WM_WM_TOPLEVEL_WINDOW_EVENT_HANDLER_H_
+#endif  // ASH_WM_TOPLEVEL_WINDOW_EVENT_HANDLER_H_
diff --git a/ash/wm/toplevel_window_event_handler_unittest.cc b/ash/wm/toplevel_window_event_handler_unittest.cc
index 8faec88..96d3ddb9 100644
--- a/ash/wm/toplevel_window_event_handler_unittest.cc
+++ b/ash/wm/toplevel_window_event_handler_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ash/wm/wm_toplevel_window_event_handler.h"
+#include "ash/wm/toplevel_window_event_handler.h"
 
 #include "ash/display/screen_orientation_controller.h"
 #include "ash/display/screen_orientation_controller_test_api.h"
diff --git a/ash/ws/window_service_delegate_impl.cc b/ash/ws/window_service_delegate_impl.cc
index 14e8379..c217e4f 100644
--- a/ash/ws/window_service_delegate_impl.cc
+++ b/ash/ws/window_service_delegate_impl.cc
@@ -14,9 +14,9 @@
 #include "ash/wm/non_client_frame_controller.h"
 #include "ash/wm/resize_shadow_controller.h"
 #include "ash/wm/top_level_window_factory.h"
+#include "ash/wm/toplevel_window_event_handler.h"
 #include "ash/wm/window_finder.h"
 #include "ash/wm/window_util.h"
-#include "ash/wm/wm_toplevel_window_event_handler.h"
 #include "ash/ws/ash_window_manager.h"
 #include "ash/ws/multi_user_window_manager_bridge.h"
 #include "base/bind.h"
diff --git a/ash/ws/window_service_delegate_impl_unittest.cc b/ash/ws/window_service_delegate_impl_unittest.cc
index ff4f83db..2062c425 100644
--- a/ash/ws/window_service_delegate_impl_unittest.cc
+++ b/ash/ws/window_service_delegate_impl_unittest.cc
@@ -9,8 +9,8 @@
 #include "ash/wm/desks/desks_util.h"
 #include "ash/wm/resize_shadow.h"
 #include "ash/wm/resize_shadow_controller.h"
+#include "ash/wm/toplevel_window_event_handler.h"
 #include "ash/wm/window_state.h"
-#include "ash/wm/wm_toplevel_window_event_handler.h"
 #include "ash/ws/window_service_owner.h"
 #include "base/run_loop.h"
 #include "base/strings/string_number_conversions.h"
diff --git a/base/callback_helpers.h b/base/callback_helpers.h
index c695adf..c0ca4db 100644
--- a/base/callback_helpers.h
+++ b/base/callback_helpers.h
@@ -10,6 +10,7 @@
 #ifndef BASE_CALLBACK_HELPERS_H_
 #define BASE_CALLBACK_HELPERS_H_
 
+#include <type_traits>
 #include <utility>
 
 #include "base/atomicops.h"
@@ -21,16 +22,39 @@
 
 namespace base {
 
-// Prefer std::move() over ResetAndReturn().
-template <typename Functor>
-OnceCallback<Functor> ResetAndReturn(OnceCallback<Functor>* cb) {
-  auto ret = std::move(*cb);
-  DCHECK(!*cb);
-  return ret;
-}
+namespace internal {
 
-template <typename Functor>
-RepeatingCallback<Functor> ResetAndReturn(RepeatingCallback<Functor>* cb) {
+template <typename T>
+struct IsBaseCallbackImpl : std::false_type {};
+
+template <typename R, typename... Args>
+struct IsBaseCallbackImpl<OnceCallback<R(Args...)>> : std::true_type {};
+
+template <typename R, typename... Args>
+struct IsBaseCallbackImpl<RepeatingCallback<R(Args...)>> : std::true_type {};
+
+}  // namespace internal
+
+template <typename T>
+using IsBaseCallback = internal::IsBaseCallbackImpl<std::decay_t<T>>;
+
+// SFINAE friendly enabler allowing to overload methods for both Repeating and
+// OnceCallbacks.
+//
+// Usage:
+// template <template <typename> class CallbackType,
+//           ... other template args ...,
+//           typename = EnableIfIsBaseCallback<CallbackType>>
+// void DoStuff(CallbackType<...> cb, ...);
+template <template <typename> class CallbackType>
+using EnableIfIsBaseCallback =
+    std::enable_if_t<IsBaseCallback<CallbackType<void()>>::value>;
+
+// Prefer std::move() over ResetAndReturn().
+template <template <typename> class CallbackType,
+          typename RunType,
+          typename = EnableIfIsBaseCallback<CallbackType>>
+CallbackType<RunType> ResetAndReturn(CallbackType<RunType>* cb) {
   auto ret = std::move(*cb);
   DCHECK(!*cb);
   return ret;
diff --git a/base/callback_helpers_unittest.cc b/base/callback_helpers_unittest.cc
index 1c1102d..c8f0b95b 100644
--- a/base/callback_helpers_unittest.cc
+++ b/base/callback_helpers_unittest.cc
@@ -4,12 +4,45 @@
 
 #include "base/callback_helpers.h"
 
+#include <functional>
+
 #include "base/bind.h"
 #include "base/callback.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace {
 
+TEST(CallbackHelpersTest, IsBaseCallback) {
+  // Check that base::Closures and references to them are considered
+  // base::Callbacks.
+  static_assert(base::IsBaseCallback<base::OnceClosure>::value, "");
+  static_assert(base::IsBaseCallback<base::RepeatingClosure>::value, "");
+  static_assert(base::IsBaseCallback<base::OnceClosure&&>::value, "");
+  static_assert(base::IsBaseCallback<const base::RepeatingClosure&>::value, "");
+
+  // Check that base::Callbacks with a given RunType and references to them are
+  // considered base::Callbacks.
+  static_assert(base::IsBaseCallback<base::OnceCallback<int(int)>>::value, "");
+  static_assert(base::IsBaseCallback<base::RepeatingCallback<int(int)>>::value,
+                "");
+  static_assert(base::IsBaseCallback<base::OnceCallback<int(int)>&&>::value,
+                "");
+  static_assert(
+      base::IsBaseCallback<const base::RepeatingCallback<int(int)>&>::value,
+      "");
+
+  // Check that POD types are not considered base::Callbacks.
+  static_assert(!base::IsBaseCallback<bool>::value, "");
+  static_assert(!base::IsBaseCallback<int>::value, "");
+  static_assert(!base::IsBaseCallback<double>::value, "");
+
+  // Check that the closely related std::function is not considered a
+  // base::Callback.
+  static_assert(!base::IsBaseCallback<std::function<void()>>::value, "");
+  static_assert(!base::IsBaseCallback<const std::function<void()>&>::value, "");
+  static_assert(!base::IsBaseCallback<std::function<void()>&&>::value, "");
+}
+
 void Increment(int* value) {
   (*value)++;
 }
diff --git a/base/files/file_util.h b/base/files/file_util.h
index 75f0785..860d885 100644
--- a/base/files/file_util.h
+++ b/base/files/file_util.h
@@ -375,6 +375,14 @@
 // Appends |data| to |fd|. Does not close |fd| when done.  Returns true iff
 // |size| bytes of |data| were written to |fd|.
 BASE_EXPORT bool WriteFileDescriptor(const int fd, const char* data, int size);
+
+// Allocates disk space for the file referred to by |fd| for the byte range
+// starting at |offset| and continuing for |size| bytes. The file size will be
+// changed if |offset|+|len| is greater than the file size. Zeros will fill the
+// new space.
+// After a successful call, subsequent writes into the specified range are
+// guaranteed not to fail because of lack of disk space.
+BASE_EXPORT bool AllocateFileRegion(File* file, int64_t offset, size_t size);
 #endif
 
 // Appends |data| to |filename|.  Returns true iff |size| bytes of |data| were
diff --git a/base/files/file_util_posix.cc b/base/files/file_util_posix.cc
index 17b984e..c05a09bd 100644
--- a/base/files/file_util_posix.cc
+++ b/base/files/file_util_posix.cc
@@ -22,6 +22,7 @@
 #include <unistd.h>
 
 #include "base/base_switches.h"
+#include "base/bits.h"
 #include "base/command_line.h"
 #include "base/containers/stack.h"
 #include "base/environment.h"
@@ -870,6 +871,78 @@
   return true;
 }
 
+bool AllocateFileRegion(File* file, int64_t offset, size_t size) {
+  DCHECK(file);
+
+  // Explicitly extend |file| to the maximum size. Zeros will fill the new
+  // space. It is assumed that the existing file is fully realized as
+  // otherwise the entire file would have to be read and possibly written.
+  const int64_t original_file_len = file->GetLength();
+  if (original_file_len < 0) {
+    DPLOG(ERROR) << "fstat " << file->GetPlatformFile();
+    return false;
+  }
+
+  // Increase the actual length of the file, if necessary. This can fail if
+  // the disk is full and the OS doesn't support sparse files.
+  const int64_t new_file_len = offset + size;
+  if (!file->SetLength(std::max(original_file_len, new_file_len))) {
+    DPLOG(ERROR) << "ftruncate " << file->GetPlatformFile();
+    return false;
+  }
+
+  // Realize the extent of the file so that it can't fail (and crash) later
+  // when trying to write to a memory page that can't be created. This can
+  // fail if the disk is full and the file is sparse.
+
+  // First try the more effective platform-specific way of allocating the disk
+  // space. It can fail because the filesystem doesn't support it. In that case,
+  // use the manual method below.
+
+#if defined(OS_LINUX)
+  if (HANDLE_EINTR(fallocate(file->GetPlatformFile(), 0, offset, size)) != -1)
+    return true;
+  DPLOG(ERROR) << "fallocate";
+#elif defined(OS_MACOSX)
+  // MacOS doesn't support fallocate even though their new APFS filesystem
+  // does support sparse files. It does, however, have the functionality
+  // available via fcntl.
+  // See also: https://openradar.appspot.com/32720223
+  fstore_t params = {F_ALLOCATEALL, F_PEOFPOSMODE, offset, size, 0};
+  if (fcntl(file->GetPlatformFile(), F_PREALLOCATE, &params) != -1)
+    return true;
+  DPLOG(ERROR) << "F_PREALLOCATE";
+#endif
+
+  // Manually realize the extended file by writing bytes to it at intervals.
+  int64_t block_size = 512;  // Start with something safe.
+  struct stat statbuf;
+  if (fstat(file->GetPlatformFile(), &statbuf) == 0 && statbuf.st_blksize > 0 &&
+      base::bits::IsPowerOfTwo(statbuf.st_blksize)) {
+    block_size = statbuf.st_blksize;
+  }
+
+  // Write starting at the next block boundary after the old file length.
+  const int64_t extension_start =
+      base::bits::Align(original_file_len, block_size);
+  for (int64_t i = extension_start; i < new_file_len; i += block_size) {
+    char existing_byte;
+    if (HANDLE_EINTR(pread(file->GetPlatformFile(), &existing_byte, 1, i)) !=
+        1) {
+      return false;  // Can't read? Not viable.
+    }
+    if (existing_byte != 0) {
+      continue;  // Block has data so must already exist.
+    }
+    if (HANDLE_EINTR(pwrite(file->GetPlatformFile(), &existing_byte, 1, i)) !=
+        1) {
+      return false;  // Can't write? Not viable.
+    }
+  }
+
+  return true;
+}
+
 #if !defined(OS_NACL_NONSFI)
 
 bool AppendToFile(const FilePath& filename, const char* data, int size) {
diff --git a/base/files/file_util_unittest.cc b/base/files/file_util_unittest.cc
index 35f9056a..26fd381 100644
--- a/base/files/file_util_unittest.cc
+++ b/base/files/file_util_unittest.cc
@@ -2522,6 +2522,75 @@
   EXPECT_TRUE(GetShmemTempDir(false, &dir));
   EXPECT_TRUE(DirectoryExists(dir));
 }
+
+TEST_F(FileUtilTest, AllocateFileRegionTest_ZeroOffset) {
+  const int kTestFileLength = 9;
+  char test_data[] = "test_data";
+  FilePath file_path = temp_dir_.GetPath().Append(
+      FILE_PATH_LITERAL("allocate_file_region_test_zero_offset"));
+  WriteFile(file_path, test_data, kTestFileLength);
+
+  File file(file_path, base::File::FLAG_OPEN | base::File::FLAG_READ |
+                           base::File::FLAG_WRITE);
+  ASSERT_TRUE(file.IsValid());
+  ASSERT_EQ(file.GetLength(), kTestFileLength);
+
+  const int kExtendedFileLength = 23;
+  ASSERT_TRUE(AllocateFileRegion(&file, 0, kExtendedFileLength));
+  EXPECT_EQ(file.GetLength(), kExtendedFileLength);
+
+  char data_read[32];
+  int bytes_read = file.Read(0, data_read, kExtendedFileLength);
+  EXPECT_EQ(bytes_read, kExtendedFileLength);
+  for (int i = 0; i < kTestFileLength; ++i)
+    EXPECT_EQ(test_data[i], data_read[i]);
+  for (int i = kTestFileLength; i < kExtendedFileLength; ++i)
+    EXPECT_EQ(0, data_read[i]);
+}
+
+TEST_F(FileUtilTest, AllocateFileRegionTest_NonZeroOffset) {
+  const int kTestFileLength = 9;
+  char test_data[] = "test_data";
+  FilePath file_path = temp_dir_.GetPath().Append(
+      FILE_PATH_LITERAL("allocate_file_region_test_non_zero_offset"));
+  WriteFile(file_path, test_data, kTestFileLength);
+
+  File file(file_path, base::File::FLAG_OPEN | base::File::FLAG_READ |
+                           base::File::FLAG_WRITE);
+  ASSERT_TRUE(file.IsValid());
+  ASSERT_EQ(file.GetLength(), kTestFileLength);
+
+  const int kExtensionOffset = 5;
+  const int kExtensionSize = 10;
+  ASSERT_TRUE(AllocateFileRegion(&file, kExtensionOffset, kExtensionSize));
+  const int kExtendedFileLength = kExtensionOffset + kExtensionSize;
+  EXPECT_EQ(file.GetLength(), kExtendedFileLength);
+
+  char data_read[32];
+  int bytes_read = file.Read(0, data_read, kExtendedFileLength);
+  EXPECT_EQ(bytes_read, kExtendedFileLength);
+  for (int i = 0; i < kTestFileLength; ++i)
+    EXPECT_EQ(test_data[i], data_read[i]);
+  for (int i = kTestFileLength; i < kExtendedFileLength; ++i)
+    EXPECT_EQ(0, data_read[i]);
+}
+
+TEST_F(FileUtilTest, AllocateFileRegionTest_DontTruncate) {
+  const int kTestFileLength = 9;
+  char test_data[] = "test_data";
+  FilePath file_path = temp_dir_.GetPath().Append(
+      FILE_PATH_LITERAL("allocate_file_region_test_dont_truncate"));
+  WriteFile(file_path, test_data, kTestFileLength);
+
+  File file(file_path, base::File::FLAG_OPEN | base::File::FLAG_READ |
+                           base::File::FLAG_WRITE);
+  ASSERT_TRUE(file.IsValid());
+  ASSERT_EQ(file.GetLength(), kTestFileLength);
+
+  const int kTruncatedFileLength = 4;
+  ASSERT_TRUE(AllocateFileRegion(&file, 0, kTruncatedFileLength));
+  EXPECT_EQ(file.GetLength(), kTestFileLength);
+}
 #endif
 
 TEST_F(FileUtilTest, GetHomeDirTest) {
diff --git a/base/files/memory_mapped_file_posix.cc b/base/files/memory_mapped_file_posix.cc
index c9cdd56..fb7d6fa8 100644
--- a/base/files/memory_mapped_file_posix.cc
+++ b/base/files/memory_mapped_file_posix.cc
@@ -11,15 +11,12 @@
 #include <sys/stat.h>
 #include <unistd.h>
 
+#include "base/files/file_util.h"
 #include "base/logging.h"
 #include "base/numerics/safe_conversions.h"
 #include "base/threading/scoped_blocking_call.h"
 #include "build/build_config.h"
 
-#if defined(OS_ANDROID)
-#include <android/api-level.h>
-#endif
-
 namespace base {
 
 MemoryMappedFile::MemoryMappedFile() : data_(nullptr), length_(0) {}
@@ -82,79 +79,8 @@
     case READ_WRITE_EXTEND:
       flags |= PROT_READ | PROT_WRITE;
 
-      const int64_t new_file_len = region.offset + region.size;
-
-      // POSIX won't auto-extend the file when it is written so it must first
-      // be explicitly extended to the maximum size. Zeros will fill the new
-      // space. It is assumed that the existing file is fully realized as
-      // otherwise the entire file would have to be read and possibly written.
-      const int64_t original_file_len = file_.GetLength();
-      if (original_file_len < 0) {
-        DPLOG(ERROR) << "fstat " << file_.GetPlatformFile();
+      if (!AllocateFileRegion(&file_, region.offset, region.size))
         return false;
-      }
-
-      // Increase the actual length of the file, if necessary. This can fail if
-      // the disk is full and the OS doesn't support sparse files.
-      if (!file_.SetLength(std::max(original_file_len, new_file_len))) {
-        DPLOG(ERROR) << "ftruncate " << file_.GetPlatformFile();
-        return false;
-      }
-
-      // Realize the extent of the file so that it can't fail (and crash) later
-      // when trying to write to a memory page that can't be created. This can
-      // fail if the disk is full and the file is sparse.
-      bool do_manual_extension = false;
-
-#if defined(OS_ANDROID) && __ANDROID_API__ < 21
-      // Only Android API>=21 supports the fallocate call. Older versions need
-      // to manually extend the file by writing zeros at block intervals.
-      do_manual_extension = true;
-#elif defined(OS_MACOSX)
-      // MacOS doesn't support fallocate even though their new APFS filesystem
-      // does support sparse files. It does, however, have the functionality
-      // available via fcntl.
-      // See also: https://openradar.appspot.com/32720223
-      fstore_t params = {F_ALLOCATEALL, F_PEOFPOSMODE, region.offset,
-                         region.size, 0};
-      if (fcntl(file_.GetPlatformFile(), F_PREALLOCATE, &params) != 0) {
-        DPLOG(ERROR) << "F_PREALLOCATE";
-        // This can fail because the filesystem doesn't support it so don't
-        // give up just yet. Try the manual method below.
-        do_manual_extension = true;
-      }
-#else
-      if (posix_fallocate(file_.GetPlatformFile(), region.offset,
-                          region.size) != 0) {
-        DPLOG(ERROR) << "posix_fallocate";
-        // This can fail because the filesystem doesn't support it so don't
-        // give up just yet. Try the manual method below.
-        do_manual_extension = true;
-      }
-#endif
-
-      // Manually realize the extended file by writing bytes to it at intervals.
-      if (do_manual_extension) {
-        int64_t block_size = 512;  // Start with something safe.
-        struct stat statbuf;
-        if (fstat(file_.GetPlatformFile(), &statbuf) == 0 &&
-            statbuf.st_blksize > 0) {
-          block_size = statbuf.st_blksize;
-        }
-
-        // Write starting at the next block boundary after the old file length.
-        const int64_t extension_start =
-            (original_file_len + block_size - 1) & ~(block_size - 1);
-        for (int64_t i = extension_start; i < new_file_len; i += block_size) {
-          char existing_byte;
-          if (pread(file_.GetPlatformFile(), &existing_byte, 1, i) != 1)
-            return false;  // Can't read? Not viable.
-          if (existing_byte != 0)
-            continue;  // Block has data so must already exist.
-          if (pwrite(file_.GetPlatformFile(), &existing_byte, 1, i) != 1)
-            return false;  // Can't write? Not viable.
-        }
-      }
 
       break;
   }
diff --git a/base/memory/platform_shared_memory_region_posix.cc b/base/memory/platform_shared_memory_region_posix.cc
index f577c81..0b9c36a 100644
--- a/base/memory/platform_shared_memory_region_posix.cc
+++ b/base/memory/platform_shared_memory_region_posix.cc
@@ -8,6 +8,7 @@
 #include <sys/mman.h>
 #include <sys/stat.h>
 
+#include "base/files/file.h"
 #include "base/files/file_util.h"
 #include "base/threading/thread_restrictions.h"
 #include "build/build_config.h"
@@ -229,11 +230,10 @@
   if (!GetShmemTempDir(false /* executable */, &directory))
     return {};
 
-  ScopedFD fd;
   FilePath path;
-  fd.reset(CreateAndOpenFdForTemporaryFileInDir(directory, &path));
+  File shm_file(CreateAndOpenFdForTemporaryFileInDir(directory, &path));
 
-  if (!fd.is_valid()) {
+  if (!shm_file.IsValid()) {
     PLOG(ERROR) << "Creating shared memory in " << path.value() << " failed";
     FilePath dir = path.DirName();
     if (access(dir.value().c_str(), W_OK | X_OK) < 0) {
@@ -261,24 +261,12 @@
     }
   }
 
-#if defined(OS_LINUX)
-  // Unlike ftruncate(), fallocate() always allocates disk space, so we get an
-  // error if the disk is full. https://crbug.com/951431
-  if (HANDLE_EINTR(fallocate(fd.get(), 0, 0, size)) != 0) {
-    PLOG(ERROR) << "Failed to reserve " << size << " bytes for shared memory.";
+  if (!AllocateFileRegion(&shm_file, 0, size))
     return {};
-  }
-#else
-  // fallocate() is a Linux-specific syscall, fallback to ftruncate().
-  if (HANDLE_EINTR(ftruncate(fd.get(), size)) != 0) {
-    DPLOG(ERROR) << "ftruncate() failed";
-    return {};
-  }
-#endif
 
   if (readonly_fd.is_valid()) {
     struct stat stat = {};
-    if (fstat(fd.get(), &stat) != 0) {
+    if (fstat(shm_file.GetPlatformFile(), &stat) != 0) {
       DPLOG(ERROR) << "fstat(fd) failed";
       return {};
     }
@@ -296,8 +284,9 @@
     }
   }
 
-  return PlatformSharedMemoryRegion({std::move(fd), std::move(readonly_fd)},
-                                    mode, size, UnguessableToken::Create());
+  return PlatformSharedMemoryRegion(
+      {ScopedFD(shm_file.TakePlatformFile()), std::move(readonly_fd)}, mode,
+      size, UnguessableToken::Create());
 #endif  // !defined(OS_NACL)
 }
 
diff --git a/base/task/cancelable_task_tracker.h b/base/task/cancelable_task_tracker.h
index 1ccd1fd..f2fc970 100644
--- a/base/task/cancelable_task_tracker.h
+++ b/base/task/cancelable_task_tracker.h
@@ -44,6 +44,7 @@
 #include "base/base_export.h"
 #include "base/bind.h"
 #include "base/callback.h"
+#include "base/callback_helpers.h"
 #include "base/containers/small_map.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
@@ -79,11 +80,19 @@
                           OnceClosure task,
                           OnceClosure reply);
 
-  template <typename TaskReturnType, typename ReplyArgType>
+  // Though RepeatingCallback is convertible to OnceCallback, we need a
+  // CallbackType template since we can not use template deduction and object
+  // conversion at once on the overload resolution.
+  // TODO(crbug.com/714018): Update all callers of the RepeatingCallback version
+  // to use OnceCallback and remove the CallbackType template.
+  template <template <typename> class CallbackType,
+            typename TaskReturnType,
+            typename ReplyArgType,
+            typename = EnableIfIsBaseCallback<CallbackType>>
   TaskId PostTaskAndReplyWithResult(TaskRunner* task_runner,
                                     const Location& from_here,
-                                    OnceCallback<TaskReturnType()> task,
-                                    OnceCallback<void(ReplyArgType)> reply) {
+                                    CallbackType<TaskReturnType()> task,
+                                    CallbackType<void(ReplyArgType)> reply) {
     auto* result = new std::unique_ptr<TaskReturnType>();
     return PostTaskAndReply(
         task_runner, from_here,
@@ -93,24 +102,6 @@
                  std::move(reply), Owned(result)));
   }
 
-  // RepeatingCallback version of PostTaskWithTraitsAndReplyWithResult above.
-  // Though RepeatingCallback is convertible to OnceCallback, we need this since
-  // we can not use template deduction and object conversion at once on the
-  // overload resolution.
-  // TODO(tzik): Update all callers of the RepeatingCallback version to use
-  // OnceCallback.
-  template <typename TaskReturnType, typename ReplyArgType>
-  TaskId PostTaskAndReplyWithResult(
-      TaskRunner* task_runner,
-      const Location& from_here,
-      RepeatingCallback<TaskReturnType()> task,
-      RepeatingCallback<void(ReplyArgType)> reply) {
-    return PostTaskAndReplyWithResult(
-        task_runner, from_here,
-        static_cast<OnceCallback<TaskReturnType()>>(std::move(task)),
-        static_cast<OnceCallback<void(ReplyArgType)>>(std::move(reply)));
-  }
-
   // Creates a tracked TaskId and an associated IsCanceledCallback. Client can
   // later call TryCancel() with the returned TaskId, and run |is_canceled_cb|
   // from any thread to check whether the TaskId is canceled.
diff --git a/base/task/post_task.h b/base/task/post_task.h
index c646cd5..bdb1f9d 100644
--- a/base/task/post_task.h
+++ b/base/task/post_task.h
@@ -11,6 +11,7 @@
 #include "base/base_export.h"
 #include "base/bind.h"
 #include "base/callback.h"
+#include "base/callback_helpers.h"
 #include "base/location.h"
 #include "base/memory/ref_counted.h"
 #include "base/post_task_and_reply_with_result_internal.h"
@@ -94,29 +95,23 @@
 
 // Equivalent to calling PostTaskWithTraitsAndReplyWithResult with default
 // TaskTraits.
-template <typename TaskReturnType, typename ReplyArgType>
+//
+// Though RepeatingCallback is convertible to OnceCallback, we need a
+// CallbackType template since we can not use template deduction and object
+// conversion at once on the overload resolution.
+// TODO(crbug.com/714018): Update all callers of the RepeatingCallback version
+// to use OnceCallback and remove the CallbackType template.
+template <template <typename> class CallbackType,
+          typename TaskReturnType,
+          typename ReplyArgType,
+          typename = EnableIfIsBaseCallback<CallbackType>>
 bool PostTaskAndReplyWithResult(const Location& from_here,
-                                OnceCallback<TaskReturnType()> task,
-                                OnceCallback<void(ReplyArgType)> reply) {
+                                CallbackType<TaskReturnType()> task,
+                                CallbackType<void(ReplyArgType)> reply) {
   return PostTaskWithTraitsAndReplyWithResult(
       from_here, TaskTraits(), std::move(task), std::move(reply));
 }
 
-// RepeatingCallback version of PostTaskAndReplyWithResult above.
-// Though RepeatingCallback is convertible to OnceCallback, we need this since
-// we can not use template deduction and object conversion at once on the
-// overload resolution.
-// TODO(tzik): Update all callers of the RepeatingCallback version to use
-// OnceCallback.
-template <typename TaskReturnType, typename ReplyArgType>
-bool PostTaskAndReplyWithResult(const Location& from_here,
-                                RepeatingCallback<TaskReturnType()> task,
-                                RepeatingCallback<void(ReplyArgType)> reply) {
-  return PostTaskAndReplyWithResult(
-      from_here, OnceCallback<TaskReturnType()>(std::move(task)),
-      OnceCallback<void(ReplyArgType)>(std::move(reply)));
-}
-
 // Posts |task| with specific |traits|. Returns false if the task definitely
 // won't run because of current shutdown state.
 BASE_EXPORT bool PostTaskWithTraits(const Location& from_here,
@@ -149,12 +144,21 @@
 // or thread and same TaskTraits if applicable) when |task| completes. Returns
 // false if the task definitely won't run because of current shutdown state. Can
 // only be called when SequencedTaskRunnerHandle::IsSet().
-template <typename TaskReturnType, typename ReplyArgType>
+//
+// Though RepeatingCallback is convertible to OnceCallback, we need a
+// CallbackType template since we can not use template deduction and object
+// conversion at once on the overload resolution.
+// TODO(crbug.com/714018): Update all callers of the RepeatingCallback version
+// to use OnceCallback and remove the CallbackType template.
+template <template <typename> class CallbackType,
+          typename TaskReturnType,
+          typename ReplyArgType,
+          typename = EnableIfIsBaseCallback<CallbackType>>
 bool PostTaskWithTraitsAndReplyWithResult(
     const Location& from_here,
     const TaskTraits& traits,
-    OnceCallback<TaskReturnType()> task,
-    OnceCallback<void(ReplyArgType)> reply) {
+    CallbackType<TaskReturnType()> task,
+    CallbackType<void(ReplyArgType)> reply) {
   auto* result = new std::unique_ptr<TaskReturnType>();
   return PostTaskWithTraitsAndReply(
       from_here, traits,
@@ -164,23 +168,6 @@
                std::move(reply), Owned(result)));
 }
 
-// RepeatingCallback version of PostTaskWithTraitsAndReplyWithResult above.
-// Though RepeatingCallback is convertible to OnceCallback, we need this since
-// we can not use template deduction and object conversion at once on the
-// overload resolution.
-// TODO(tzik): Update all callers of the RepeatingCallback version to use
-// OnceCallback.
-template <typename TaskReturnType, typename ReplyArgType>
-bool PostTaskWithTraitsAndReplyWithResult(
-    const Location& from_here,
-    const TaskTraits& traits,
-    RepeatingCallback<TaskReturnType()> task,
-    RepeatingCallback<void(ReplyArgType)> reply) {
-  return PostTaskWithTraitsAndReplyWithResult(
-      from_here, traits, OnceCallback<TaskReturnType()>(std::move(task)),
-      OnceCallback<void(ReplyArgType)>(std::move(reply)));
-}
-
 // Returns a TaskRunner whose PostTask invocations result in scheduling tasks
 // using |traits|. Tasks may run in any order and in parallel.
 BASE_EXPORT scoped_refptr<TaskRunner> CreateTaskRunnerWithTraits(
diff --git a/base/task_runner_util.h b/base/task_runner_util.h
index 14c536d..05894ab 100644
--- a/base/task_runner_util.h
+++ b/base/task_runner_util.h
@@ -11,6 +11,7 @@
 #include "base/bind.h"
 #include "base/bind_helpers.h"
 #include "base/callback.h"
+#include "base/callback_helpers.h"
 #include "base/logging.h"
 #include "base/post_task_and_reply_with_result_internal.h"
 #include "base/task_runner.h"
@@ -31,11 +32,20 @@
 //     FROM_HERE,
 //     BindOnce(&DoWorkAndReturn),
 //     BindOnce(&Callback));
-template <typename TaskReturnType, typename ReplyArgType>
+//
+// Though RepeatingCallback is convertible to OnceCallback, we need a
+// CallbackType template since we can not use template deduction and object
+// conversion at once on the overload resolution.
+// TODO(crbug.com/714018): Update all callers of the RepeatingCallback version
+// to use OnceCallback and remove the CallbackType template.
+template <template <typename> class CallbackType,
+          typename TaskReturnType,
+          typename ReplyArgType,
+          typename = EnableIfIsBaseCallback<CallbackType>>
 bool PostTaskAndReplyWithResult(TaskRunner* task_runner,
                                 const Location& from_here,
-                                OnceCallback<TaskReturnType()> task,
-                                OnceCallback<void(ReplyArgType)> reply) {
+                                CallbackType<TaskReturnType()> task,
+                                CallbackType<void(ReplyArgType)> reply) {
   DCHECK(task);
   DCHECK(reply);
   // std::unique_ptr used to avoid the need of a default constructor.
@@ -48,22 +58,6 @@
                std::move(reply), Owned(result)));
 }
 
-// RepeatingCallback version of PostTaskAndReplyWithResult above.
-// Though RepeatingCallback is convertible to OnceCallback, we need this since
-// we cannot use template deduction and object conversion at once on the
-// overload resolution.
-// TODO(crbug.com/714018): Update all callers of the RepeatingCallback version
-// to use OnceCallback.
-template <typename TaskReturnType, typename ReplyArgType>
-bool PostTaskAndReplyWithResult(TaskRunner* task_runner,
-                                const Location& from_here,
-                                RepeatingCallback<TaskReturnType()> task,
-                                RepeatingCallback<void(ReplyArgType)> reply) {
-  return PostTaskAndReplyWithResult(
-      task_runner, from_here, OnceCallback<TaskReturnType()>(std::move(task)),
-      OnceCallback<void(ReplyArgType)>(std::move(reply)));
-}
-
 }  // namespace base
 
 #endif  // BASE_TASK_RUNNER_UTIL_H_
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index d62699e..2181b63 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-8915146190128167296
\ No newline at end of file
+8915116829918854048
\ No newline at end of file
diff --git a/cc/layers/layer_impl.cc b/cc/layers/layer_impl.cc
index 36a262b3..5d4c278 100644
--- a/cc/layers/layer_impl.cc
+++ b/cc/layers/layer_impl.cc
@@ -514,6 +514,12 @@
 }
 
 gfx::Size LayerImpl::bounds() const {
+  // As an optimization, we do not need to include the viewport bounds delta if
+  // the layer is not a viewport layer.
+  if (viewport_layer_type_ == NOT_VIEWPORT_LAYER) {
+    DCHECK(ViewportBoundsDelta().IsZero());
+    return bounds_;
+  }
   auto viewport_bounds_delta = gfx::ToCeiledVector2d(ViewportBoundsDelta());
   return gfx::Size(bounds_.width() + viewport_bounds_delta.x(),
                    bounds_.height() + viewport_bounds_delta.y());
diff --git a/cc/paint/paint_op_buffer.cc b/cc/paint/paint_op_buffer.cc
index 7c047cd1..22d86d8 100644
--- a/cc/paint/paint_op_buffer.cc
+++ b/cc/paint/paint_op_buffer.cc
@@ -1261,11 +1261,26 @@
   canvas->drawImage(decoded_image.image().get(), op->left, op->top, &paint);
 }
 
-// TODO(xidachen): ensure paint worklet generated images are correctly handled.
 void DrawImageRectOp::RasterWithFlags(const DrawImageRectOp* op,
                                       const PaintFlags* flags,
                                       SkCanvas* canvas,
                                       const PlaybackParams& params) {
+  // TODO(crbug.com/931704): make sure to support the case where paint worklet
+  // generated images are used in other raster work such as canvas2d.
+  if (op->image.IsPaintWorklet()) {
+    DCHECK(params.image_provider);
+    ImageProvider::ScopedResult result =
+        params.image_provider->GetRasterContent(DrawImage(op->image));
+
+    DCHECK(IsScaleAdjustmentIdentity(op->scale_adjustment));
+    SkAutoCanvasRestore save_restore(canvas, true);
+    canvas->concat(
+        SkMatrix::MakeRectToRect(op->src, op->dst, SkMatrix::kFill_ScaleToFit));
+    canvas->clipRect(op->src);
+    result.paint_record()->Playback(canvas, params);
+    return;
+  }
+
   // TODO(enne): Probably PaintCanvas should just use the skia enum directly.
   SkCanvas::SrcRectConstraint skconstraint =
       static_cast<SkCanvas::SrcRectConstraint>(op->constraint);
diff --git a/cc/paint/paint_op_buffer_unittest.cc b/cc/paint/paint_op_buffer_unittest.cc
index 0f6f7b74..25f96990 100644
--- a/cc/paint/paint_op_buffer_unittest.cc
+++ b/cc/paint/paint_op_buffer_unittest.cc
@@ -439,6 +439,18 @@
   EXPECT_TRUE(buffer.HasDiscardableImages());
 }
 
+TEST(PaintOpBufferTest, DiscardableImagesTracking_PaintWorkletImageRect) {
+  scoped_refptr<TestPaintWorkletInput> input =
+      base::MakeRefCounted<TestPaintWorkletInput>(gfx::SizeF(32.0f, 32.0f));
+  PaintOpBuffer buffer;
+  PaintImage image = CreatePaintWorkletPaintImage(input);
+  SkRect src = SkRect::MakeEmpty();
+  SkRect dst = SkRect::MakeEmpty();
+  buffer.push<DrawImageRectOp>(image, src, dst, nullptr,
+                               PaintCanvas::kStrict_SrcRectConstraint);
+  EXPECT_TRUE(buffer.HasDiscardableImages());
+}
+
 TEST(PaintOpBufferTest, DiscardableImagesTracking_DrawImageRect) {
   PaintOpBuffer buffer;
   PaintImage image = CreateDiscardablePaintImage(gfx::Size(100, 100));
@@ -2916,6 +2928,172 @@
   blink_buffer.Playback(&canvas, PlaybackParams(&provider));
 }
 
+TEST(PaintOpBufferTest, RasterPaintWorkletImageRectBasicCase) {
+  sk_sp<PaintOpBuffer> paint_worklet_buffer = sk_make_sp<PaintOpBuffer>();
+  PaintFlags noop_flags;
+  SkRect savelayer_rect = SkRect::MakeXYWH(0, 0, 100, 100);
+  paint_worklet_buffer->push<TranslateOp>(8.0f, 8.0f);
+  paint_worklet_buffer->push<SaveLayerOp>(&savelayer_rect, &noop_flags);
+  PaintFlags draw_flags;
+  draw_flags.setColor(0u);
+  SkRect rect = SkRect::MakeXYWH(0, 0, 100, 100);
+  paint_worklet_buffer->push<DrawRectOp>(rect, draw_flags);
+
+  MockImageProvider provider;
+  provider.SetRecord(paint_worklet_buffer);
+
+  PaintOpBuffer blink_buffer;
+  scoped_refptr<TestPaintWorkletInput> input =
+      base::MakeRefCounted<TestPaintWorkletInput>(gfx::SizeF(100, 100));
+  PaintImage image = CreatePaintWorkletPaintImage(input);
+  SkRect src = SkRect::MakeXYWH(0, 0, 100, 100);
+  SkRect dst = SkRect::MakeXYWH(0, 0, 100, 100);
+  blink_buffer.push<DrawImageRectOp>(image, src, dst, nullptr,
+                                     PaintCanvas::kStrict_SrcRectConstraint);
+
+  testing::StrictMock<MockCanvas> canvas;
+  testing::Sequence s;
+
+  EXPECT_CALL(canvas, willSave()).InSequence(s);
+  EXPECT_CALL(canvas, willSave()).InSequence(s);
+  EXPECT_CALL(canvas, didConcat(SkMatrix::MakeTrans(8.0f, 8.0f)));
+  EXPECT_CALL(canvas, OnSaveLayer()).InSequence(s);
+  EXPECT_CALL(canvas, OnDrawRectWithColor(0u));
+  EXPECT_CALL(canvas, willRestore()).InSequence(s);
+  EXPECT_CALL(canvas, willRestore()).InSequence(s);
+  EXPECT_CALL(canvas, willRestore()).InSequence(s);
+
+  blink_buffer.Playback(&canvas, PlaybackParams(&provider));
+}
+
+TEST(PaintOpBufferTest, RasterPaintWorkletImageRectTranslated) {
+  sk_sp<PaintOpBuffer> paint_worklet_buffer = sk_make_sp<PaintOpBuffer>();
+  PaintFlags noop_flags;
+  SkRect savelayer_rect = SkRect::MakeXYWH(0, 0, 10, 10);
+  paint_worklet_buffer->push<SaveLayerOp>(&savelayer_rect, &noop_flags);
+  PaintFlags draw_flags;
+  draw_flags.setFilterQuality(kLow_SkFilterQuality);
+  PaintImage paint_image = CreateDiscardablePaintImage(gfx::Size(10, 10));
+  paint_worklet_buffer->push<DrawImageOp>(paint_image, 0.0f, 0.0f, &draw_flags);
+
+  std::vector<SkSize> src_rect_offset = {SkSize::MakeEmpty()};
+  std::vector<SkSize> scale_adjustment = {SkSize::Make(0.2f, 0.2f)};
+  std::vector<SkFilterQuality> quality = {kHigh_SkFilterQuality};
+  MockImageProvider provider(src_rect_offset, scale_adjustment, quality);
+  provider.SetRecord(paint_worklet_buffer);
+
+  PaintOpBuffer blink_buffer;
+  scoped_refptr<TestPaintWorkletInput> input =
+      base::MakeRefCounted<TestPaintWorkletInput>(gfx::SizeF(100, 100));
+  PaintImage image = CreatePaintWorkletPaintImage(input);
+  SkRect src = SkRect::MakeXYWH(0, 0, 100, 100);
+  SkRect dst = SkRect::MakeXYWH(5, 7, 100, 100);
+  blink_buffer.push<DrawImageRectOp>(image, src, dst, nullptr,
+                                     PaintCanvas::kStrict_SrcRectConstraint);
+
+  testing::StrictMock<MockCanvas> canvas;
+  testing::Sequence s;
+
+  EXPECT_CALL(canvas, willSave()).InSequence(s);
+  EXPECT_CALL(canvas, OnSaveLayer()).InSequence(s);
+  EXPECT_CALL(canvas, didConcat(SkMatrix::MakeTrans(5.0f, 7.0f)));
+  EXPECT_CALL(canvas, willSave()).InSequence(s);
+  EXPECT_CALL(canvas, didConcat(MatchesInvScale(scale_adjustment[0])));
+  EXPECT_CALL(canvas, onDrawImage(NonLazyImage(), 0.0f, 0.0f,
+                                  MatchesQuality(quality[0])));
+  EXPECT_CALL(canvas, willRestore()).InSequence(s);
+  EXPECT_CALL(canvas, willRestore()).InSequence(s);
+  EXPECT_CALL(canvas, willRestore()).InSequence(s);
+
+  blink_buffer.Playback(&canvas, PlaybackParams(&provider));
+}
+
+TEST(PaintOpBufferTest, RasterPaintWorkletImageRectScaled) {
+  sk_sp<PaintOpBuffer> paint_worklet_buffer = sk_make_sp<PaintOpBuffer>();
+  PaintFlags noop_flags;
+  SkRect savelayer_rect = SkRect::MakeXYWH(0, 0, 10, 10);
+  paint_worklet_buffer->push<SaveLayerOp>(&savelayer_rect, &noop_flags);
+  PaintFlags draw_flags;
+  draw_flags.setFilterQuality(kLow_SkFilterQuality);
+  PaintImage paint_image = CreateDiscardablePaintImage(gfx::Size(10, 10));
+  paint_worklet_buffer->push<DrawImageOp>(paint_image, 0.0f, 0.0f, &draw_flags);
+
+  std::vector<SkSize> src_rect_offset = {SkSize::MakeEmpty()};
+  std::vector<SkSize> scale_adjustment = {SkSize::Make(0.2f, 0.2f)};
+  std::vector<SkFilterQuality> quality = {kHigh_SkFilterQuality};
+  MockImageProvider provider(src_rect_offset, scale_adjustment, quality);
+  provider.SetRecord(paint_worklet_buffer);
+
+  PaintOpBuffer blink_buffer;
+  scoped_refptr<TestPaintWorkletInput> input =
+      base::MakeRefCounted<TestPaintWorkletInput>(gfx::SizeF(100, 100));
+  PaintImage image = CreatePaintWorkletPaintImage(input);
+  SkRect src = SkRect::MakeXYWH(0, 0, 100, 100);
+  SkRect dst = SkRect::MakeXYWH(0, 0, 200, 150);
+  blink_buffer.push<DrawImageRectOp>(image, src, dst, nullptr,
+                                     PaintCanvas::kStrict_SrcRectConstraint);
+
+  testing::StrictMock<MockCanvas> canvas;
+  testing::Sequence s;
+
+  EXPECT_CALL(canvas, willSave()).InSequence(s);
+  EXPECT_CALL(canvas, OnSaveLayer()).InSequence(s);
+  EXPECT_CALL(canvas, didConcat(SkMatrix::MakeScale(2.f, 1.5f)));
+  EXPECT_CALL(canvas, willSave()).InSequence(s);
+  EXPECT_CALL(canvas, didConcat(MatchesInvScale(scale_adjustment[0])));
+  EXPECT_CALL(canvas, onDrawImage(NonLazyImage(), 0.0f, 0.0f,
+                                  MatchesQuality(quality[0])));
+  EXPECT_CALL(canvas, willRestore()).InSequence(s);
+  EXPECT_CALL(canvas, willRestore()).InSequence(s);
+  EXPECT_CALL(canvas, willRestore()).InSequence(s);
+
+  blink_buffer.Playback(&canvas, PlaybackParams(&provider));
+}
+
+TEST(PaintOpBufferTest, RasterPaintWorkletImageRectClipped) {
+  sk_sp<PaintOpBuffer> paint_worklet_buffer = sk_make_sp<PaintOpBuffer>();
+  PaintFlags noop_flags;
+  SkRect savelayer_rect = SkRect::MakeXYWH(0, 0, 60, 60);
+  paint_worklet_buffer->push<SaveLayerOp>(&savelayer_rect, &noop_flags);
+  PaintFlags draw_flags;
+  draw_flags.setFilterQuality(kLow_SkFilterQuality);
+  PaintImage paint_image = CreateDiscardablePaintImage(gfx::Size(10, 10));
+  // One rect inside the src-rect, one outside.
+  paint_worklet_buffer->push<DrawImageOp>(paint_image, 0.0f, 0.0f, &draw_flags);
+  paint_worklet_buffer->push<DrawImageOp>(paint_image, 50.0f, 50.0f,
+                                          &draw_flags);
+
+  std::vector<SkSize> src_rect_offset = {SkSize::MakeEmpty()};
+  std::vector<SkSize> scale_adjustment = {SkSize::Make(0.2f, 0.2f)};
+  std::vector<SkFilterQuality> quality = {kHigh_SkFilterQuality};
+  MockImageProvider provider(src_rect_offset, scale_adjustment, quality);
+  provider.SetRecord(paint_worklet_buffer);
+
+  PaintOpBuffer blink_buffer;
+  scoped_refptr<TestPaintWorkletInput> input =
+      base::MakeRefCounted<TestPaintWorkletInput>(gfx::SizeF(100, 100));
+  PaintImage image = CreatePaintWorkletPaintImage(input);
+  SkRect src = SkRect::MakeXYWH(0, 0, 20, 20);
+  SkRect dst = SkRect::MakeXYWH(0, 0, 20, 20);
+  blink_buffer.push<DrawImageRectOp>(image, src, dst, nullptr,
+                                     PaintCanvas::kStrict_SrcRectConstraint);
+
+  testing::StrictMock<MockCanvas> canvas;
+  testing::Sequence s;
+
+  EXPECT_CALL(canvas, willSave()).InSequence(s);
+  EXPECT_CALL(canvas, OnSaveLayer()).InSequence(s);
+  EXPECT_CALL(canvas, willSave()).InSequence(s);
+  EXPECT_CALL(canvas, didConcat(MatchesInvScale(scale_adjustment[0])));
+  EXPECT_CALL(canvas, onDrawImage(NonLazyImage(), 0.0f, 0.0f,
+                                  MatchesQuality(quality[0])));
+  EXPECT_CALL(canvas, willRestore()).InSequence(s);
+  EXPECT_CALL(canvas, willRestore()).InSequence(s);
+  EXPECT_CALL(canvas, willRestore()).InSequence(s);
+
+  blink_buffer.Playback(&canvas, PlaybackParams(&provider));
+}
+
 TEST(PaintOpBufferTest, ReplacesImagesFromProvider) {
   std::vector<SkSize> src_rect_offset = {
       SkSize::MakeEmpty(), SkSize::Make(2.0f, 2.0f), SkSize::Make(3.0f, 3.0f)};
diff --git a/cc/paint/render_surface_filters.cc b/cc/paint/render_surface_filters.cc
index d82fdd06..0a9cb8b 100644
--- a/cc/paint/render_surface_filters.cc
+++ b/cc/paint/render_surface_filters.cc
@@ -11,6 +11,7 @@
 #include "cc/paint/filter_operation.h"
 #include "cc/paint/filter_operations.h"
 #include "cc/paint/paint_filter.h"
+#include "third_party/skia/include/core/SkColorFilter.h"
 #include "third_party/skia/include/core/SkImageFilter.h"
 #include "third_party/skia/include/core/SkRegion.h"
 #include "third_party/skia/include/effects/SkAlphaThresholdFilter.h"
@@ -26,31 +27,31 @@
 
 namespace {
 
-void GetBrightnessMatrix(float amount, SkScalar matrix[20]) {
+void GetBrightnessMatrix(float amount, float matrix[20]) {
   // Spec implementation
   // (http://dvcs.w3.org/hg/FXTF/raw-file/tip/filters/index.html#brightnessEquivalent)
   // <feFunc[R|G|B] type="linear" slope="[amount]">
-  memset(matrix, 0, 20 * sizeof(SkScalar));
+  memset(matrix, 0, 20 * sizeof(float));
   matrix[0] = matrix[6] = matrix[12] = amount;
   matrix[18] = 1.f;
 }
 
-void GetSaturatingBrightnessMatrix(float amount, SkScalar matrix[20]) {
+void GetSaturatingBrightnessMatrix(float amount, float matrix[20]) {
   // Legacy implementation used by internal clients.
   // <feFunc[R|G|B] type="linear" intercept="[amount]"/>
-  memset(matrix, 0, 20 * sizeof(SkScalar));
+  memset(matrix, 0, 20 * sizeof(float));
   matrix[0] = matrix[6] = matrix[12] = matrix[18] = 1.f;
-  matrix[4] = matrix[9] = matrix[14] = amount * 255.f;
+  matrix[4] = matrix[9] = matrix[14] = amount;
 }
 
-void GetContrastMatrix(float amount, SkScalar matrix[20]) {
-  memset(matrix, 0, 20 * sizeof(SkScalar));
+void GetContrastMatrix(float amount, float matrix[20]) {
+  memset(matrix, 0, 20 * sizeof(float));
   matrix[0] = matrix[6] = matrix[12] = amount;
-  matrix[4] = matrix[9] = matrix[14] = (-0.5f * amount + 0.5f) * 255.f;
+  matrix[4] = matrix[9] = matrix[14] = (-0.5f * amount + 0.5f);
   matrix[18] = 1.f;
 }
 
-void GetSaturateMatrix(float amount, SkScalar matrix[20]) {
+void GetSaturateMatrix(float amount, float matrix[20]) {
   // Note, these values are computed to ensure MatrixNeedsClamping is false
   // for amount in [0..1]
   matrix[0] = 0.213f + 0.787f * amount;
@@ -69,7 +70,7 @@
   matrix[18] = 1.f;
 }
 
-void GetHueRotateMatrix(float hue, SkScalar matrix[20]) {
+void GetHueRotateMatrix(float hue, float matrix[20]) {
   float cos_hue = cosf(hue * base::kPiFloat / 180.f);
   float sin_hue = sinf(hue * base::kPiFloat / 180.f);
   matrix[0] = 0.213f + cos_hue * 0.787f - sin_hue * 0.213f;
@@ -89,20 +90,20 @@
   matrix[19] = 0.f;
 }
 
-void GetInvertMatrix(float amount, SkScalar matrix[20]) {
-  memset(matrix, 0, 20 * sizeof(SkScalar));
+void GetInvertMatrix(float amount, float matrix[20]) {
+  memset(matrix, 0, 20 * sizeof(float));
   matrix[0] = matrix[6] = matrix[12] = 1.f - 2.f * amount;
-  matrix[4] = matrix[9] = matrix[14] = amount * 255.f;
+  matrix[4] = matrix[9] = matrix[14] = amount;
   matrix[18] = 1.f;
 }
 
-void GetOpacityMatrix(float amount, SkScalar matrix[20]) {
-  memset(matrix, 0, 20 * sizeof(SkScalar));
+void GetOpacityMatrix(float amount, float matrix[20]) {
+  memset(matrix, 0, 20 * sizeof(float));
   matrix[0] = matrix[6] = matrix[12] = 1.f;
   matrix[18] = amount;
 }
 
-void GetGrayscaleMatrix(float amount, SkScalar matrix[20]) {
+void GetGrayscaleMatrix(float amount, float matrix[20]) {
   // Note, these values are computed to ensure MatrixNeedsClamping is false
   // for amount in [0..1]
   matrix[0] = 0.2126f + 0.7874f * amount;
@@ -124,7 +125,7 @@
   matrix[18] = 1.f;
 }
 
-void GetSepiaMatrix(float amount, SkScalar matrix[20]) {
+void GetSepiaMatrix(float amount, float matrix[20]) {
   matrix[0] = 0.393f + 0.607f * amount;
   matrix[1] = 0.769f - 0.769f * amount;
   matrix[2] = 0.189f - 0.189f * amount;
@@ -144,10 +145,10 @@
   matrix[18] = 1.f;
 }
 
-sk_sp<PaintFilter> CreateMatrixImageFilter(const SkScalar matrix[20],
+sk_sp<PaintFilter> CreateMatrixImageFilter(const float matrix[20],
                                            sk_sp<PaintFilter> input) {
-  return sk_make_sp<ColorFilterPaintFilter>(
-      SkColorFilters::MatrixRowMajor255(matrix), std::move(input));
+  return sk_make_sp<ColorFilterPaintFilter>(SkColorFilters::Matrix(matrix),
+                                            std::move(input));
 }
 
 }  // namespace
@@ -157,7 +158,7 @@
     const gfx::SizeF& size,
     const gfx::Vector2dF& offset) {
   sk_sp<PaintFilter> image_filter;
-  SkScalar matrix[20];
+  float matrix[20];
   for (size_t i = 0; i < filters.size(); ++i) {
     const FilterOperation& op = filters.at(i);
     switch (op.type()) {
@@ -271,7 +272,7 @@
           has_input = !!color_paint_filter->input();
         }
 
-        if (cf && cf->asColorMatrix(matrix) && !has_input) {
+        if (cf && cf->asAColorMatrix(matrix) && !has_input) {
           image_filter =
               CreateMatrixImageFilter(matrix, std::move(image_filter));
         } else if (image_filter) {
diff --git a/cc/test/pixel_test.cc b/cc/test/pixel_test.cc
index fa4412f..39e438b6f 100644
--- a/cc/test/pixel_test.cc
+++ b/cc/test/pixel_test.cc
@@ -20,7 +20,6 @@
 #include "cc/test/pixel_test_utils.h"
 #include "cc/test/test_in_process_context_provider.h"
 #include "components/viz/client/client_resource_provider.h"
-#include "components/viz/common/display/update_vsync_parameters_callback.h"
 #include "components/viz/common/frame_sinks/begin_frame_source.h"
 #include "components/viz/common/frame_sinks/copy_output_request.h"
 #include "components/viz/common/frame_sinks/copy_output_result.h"
@@ -69,7 +68,6 @@
                                  bool flipped_output_surface)
       : SkiaOutputSurfaceImpl(gpu_service,
                               surface_handle,
-                              viz::UpdateVSyncParametersCallback(),
                               renderer_settings),
         flipped_output_surface_(flipped_output_surface) {}
 
@@ -182,7 +180,15 @@
       SkImageInfo::MakeN32Premul(result_bitmap_->width(),
                                  result_bitmap_->height()),
       ref_pixels->data(), result_bitmap_->width() * sizeof(SkColor));
-  return comparator.Compare(*result_bitmap_, ref_pixels_bitmap);
+  bool result = comparator.Compare(*result_bitmap_, ref_pixels_bitmap);
+  if (!result) {
+    std::string res_bmp_data_url = GetPNGDataUrl(*result_bitmap_);
+    std::string ref_bmp_data_url = GetPNGDataUrl(ref_pixels_bitmap);
+    LOG(ERROR) << "Pixels do not match!";
+    LOG(ERROR) << "Actual: " << res_bmp_data_url;
+    LOG(ERROR) << "Expected: " << ref_bmp_data_url;
+  }
+  return result;
 }
 
 void PixelTest::ReadbackResult(base::OnceClosure quit_run_loop,
diff --git a/cc/test/pixel_test_output_surface.cc b/cc/test/pixel_test_output_surface.cc
index 24040f13..d8d58f1 100644
--- a/cc/test/pixel_test_output_surface.cc
+++ b/cc/test/pixel_test_output_surface.cc
@@ -110,4 +110,7 @@
   return 0;
 }
 
+void PixelTestOutputSurface::SetUpdateVSyncParametersCallback(
+    viz::UpdateVSyncParametersCallback callback) {}
+
 }  // namespace cc
diff --git a/cc/test/pixel_test_output_surface.h b/cc/test/pixel_test_output_surface.h
index 8b2acd3..215c11c9 100644
--- a/cc/test/pixel_test_output_surface.h
+++ b/cc/test/pixel_test_output_surface.h
@@ -39,6 +39,8 @@
   gfx::BufferFormat GetOverlayBufferFormat() const override;
   uint32_t GetFramebufferCopyTextureFormat() override;
   unsigned UpdateGpuFence() override;
+  void SetUpdateVSyncParametersCallback(
+      viz::UpdateVSyncParametersCallback callback) override;
 
   void set_has_external_stencil_test(bool has_test) {
     external_stencil_test_ = has_test;
diff --git a/cc/trees/layer_tree_host_pixeltest_filters.cc b/cc/trees/layer_tree_host_pixeltest_filters.cc
index 32c04b5..12ee8775 100644
--- a/cc/trees/layer_tree_host_pixeltest_filters.cc
+++ b/cc/trees/layer_tree_host_pixeltest_filters.cc
@@ -54,17 +54,17 @@
     // This matrix swaps the red and green channels, and has a slight
     // translation in the alpha component, so that it affects transparent
     // pixels.
-    SkScalar matrix[20] = {
+    float matrix[20] = {
         0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f,
-        0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 20.0f,
+        0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 20 / 255.0f,
     };
 
     FilterOperations filters;
     SkImageFilter::CropRect cropRect(
         SkRect::MakeXYWH(-40000, -40000, 80000, 80000));
     filters.Append(FilterOperation::CreateReferenceFilter(
-        sk_make_sp<ColorFilterPaintFilter>(
-            SkColorFilters::MatrixRowMajor255(matrix), nullptr, &cropRect)));
+        sk_make_sp<ColorFilterPaintFilter>(SkColorFilters::Matrix(matrix),
+                                           nullptr, &cropRect)));
     filter_layer->SetFilters(filters);
     background->SetMasksToBounds(masks_to_bounds);
     background->AddChild(filter_layer);
@@ -381,16 +381,16 @@
       CreateSolidColorLayer(gfx::Rect(200, 200), SK_ColorRED);
   background->AddChild(foreground);
 
-  SkScalar matrix[20];
+  float matrix[20];
   memset(matrix, 0, 20 * sizeof(matrix[0]));
   // This filter does a red-blue swap, so the foreground becomes blue.
-  matrix[2] = matrix[6] = matrix[10] = matrix[18] = SK_Scalar1;
+  matrix[2] = matrix[6] = matrix[10] = matrix[18] = 1.0f;
   // We filter only the bottom 200x100 pixels of the foreground.
   SkImageFilter::CropRect crop_rect(SkRect::MakeXYWH(0, 100, 200, 100));
   FilterOperations filters;
   filters.Append(
       FilterOperation::CreateReferenceFilter(sk_make_sp<ColorFilterPaintFilter>(
-          SkColorFilters::MatrixRowMajor255(matrix), nullptr, &crop_rect)));
+          SkColorFilters::Matrix(matrix), nullptr, &crop_rect)));
 
   // Make the foreground layer's render surface be clipped by the background
   // layer.
@@ -417,16 +417,16 @@
       CreateSolidColorLayer(gfx::Rect(200, 200), SK_ColorRED);
   background->AddChild(foreground);
 
-  SkScalar matrix[20];
+  float matrix[20];
   memset(matrix, 0, 20 * sizeof(matrix[0]));
   // This filter does a red-blue swap, so the foreground becomes blue.
-  matrix[2] = matrix[6] = matrix[10] = matrix[18] = SK_Scalar1;
+  matrix[2] = matrix[6] = matrix[10] = matrix[18] = 1.0f;
   // Set up a crop rec to filter the bottom 200x100 pixels of the foreground.
   SkImageFilter::CropRect crop_rect(SkRect::MakeXYWH(0, 100, 200, 100));
   FilterOperations filters;
   filters.Append(
       FilterOperation::CreateReferenceFilter(sk_make_sp<ColorFilterPaintFilter>(
-          SkColorFilters::MatrixRowMajor255(matrix), nullptr, &crop_rect)));
+          SkColorFilters::Matrix(matrix), nullptr, &crop_rect)));
 
   // Make the foreground layer's render surface be clipped by the background
   // layer.
diff --git a/cc/trees/layer_tree_host_unittest.cc b/cc/trees/layer_tree_host_unittest.cc
index 734a2f75..6a959cd 100644
--- a/cc/trees/layer_tree_host_unittest.cc
+++ b/cc/trees/layer_tree_host_unittest.cc
@@ -351,7 +351,8 @@
 
 // No single thread test because the commit goes directly to the active tree in
 // single thread mode, so notify ready to activate is skipped.
-MULTI_THREAD_TEST_F(LayerTreeHostTestReadyToActivateNonEmpty);
+// Flaky: https://crbug.com/947673
+// MULTI_THREAD_TEST_F(LayerTreeHostTestReadyToActivateNonEmpty);
 
 // Test if the LTHI receives ReadyToDraw notifications from the TileManager when
 // no raster tasks get scheduled.
diff --git a/chrome/android/features/keyboard_accessory/BUILD.gn b/chrome/android/features/keyboard_accessory/BUILD.gn
index d03685c..bd670cc 100644
--- a/chrome/android/features/keyboard_accessory/BUILD.gn
+++ b/chrome/android/features/keyboard_accessory/BUILD.gn
@@ -8,6 +8,7 @@
   sources = [
     "internal/java/src/org/chromium/chrome/browser/keyboard_accessory/AutofillKeyboardAccessoryViewBridge.java",
     "internal/java/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingComponentBridge.java",
+    "public/java/src/org/chromium/chrome/browser/keyboard_accessory/data/UserInfoField.java",
   ]
   jni_package = "chrome"
 }
diff --git a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingComponentBridge.java b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingComponentBridge.java
index c002353c..6419e5d 100644
--- a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingComponentBridge.java
+++ b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingComponentBridge.java
@@ -16,6 +16,7 @@
 import org.chromium.chrome.browser.keyboard_accessory.data.KeyboardAccessoryData.FooterCommand;
 import org.chromium.chrome.browser.keyboard_accessory.data.KeyboardAccessoryData.UserInfo;
 import org.chromium.chrome.browser.keyboard_accessory.data.PropertyProvider;
+import org.chromium.chrome.browser.keyboard_accessory.data.UserInfoField;
 import org.chromium.content_public.browser.WebContents;
 import org.chromium.ui.base.WindowAndroid;
 
@@ -115,19 +116,19 @@
     @CalledByNative
     private void addFieldToUserInfo(Object objUserInfo, String displayText, String a11yDescription,
             boolean isObfuscated, boolean selectable) {
-        Callback<UserInfo.Field> callback = null;
+        Callback<UserInfoField> callback = null;
         if (selectable) {
             callback = (field) -> {
                 assert mNativeView != 0 : "Controller was destroyed but the bridge wasn't!";
                 ManualFillingMetricsRecorder.recordSuggestionSelected(AccessoryTabType.PASSWORDS,
                         field.isObfuscated() ? AccessorySuggestionType.PASSWORD
                                              : AccessorySuggestionType.USERNAME);
-                nativeOnFillingTriggered(mNativeView, field.isObfuscated(), field.getDisplayText());
+                nativeOnFillingTriggered(mNativeView, field.isObfuscated(), field);
             };
         }
         ((UserInfo) objUserInfo)
                 .getFields()
-                .add(new UserInfo.Field(displayText, a11yDescription, isObfuscated, callback));
+                .add(new UserInfoField(displayText, a11yDescription, isObfuscated, callback));
     }
 
     @CalledByNative
@@ -155,7 +156,7 @@
     private native void nativeOnFaviconRequested(long nativeManualFillingViewAndroid,
             int desiredSizeInPx, Callback<Bitmap> faviconCallback);
     private native void nativeOnFillingTriggered(
-            long nativeManualFillingViewAndroid, boolean isObfuscated, String textToFill);
+            long nativeManualFillingViewAndroid, boolean isObfuscated, UserInfoField userInfoField);
     private native void nativeOnOptionSelected(
             long nativeManualFillingViewAndroid, String selectedOption);
     private native void nativeOnGenerationRequested(long nativeManualFillingViewAndroid);
diff --git a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/AccessorySheetTabMetricsRecorder.java b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/AccessorySheetTabMetricsRecorder.java
index 891eb93..5b6bdcc 100644
--- a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/AccessorySheetTabMetricsRecorder.java
+++ b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/AccessorySheetTabMetricsRecorder.java
@@ -11,6 +11,7 @@
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.chrome.browser.keyboard_accessory.AccessoryTabType;
 import org.chromium.chrome.browser.keyboard_accessory.data.KeyboardAccessoryData.UserInfo;
+import org.chromium.chrome.browser.keyboard_accessory.data.UserInfoField;
 import org.chromium.ui.modelutil.ListModel;
 
 /**
@@ -39,7 +40,7 @@
         for (int i = 0; i < suggestionList.size(); ++i) {
             if (getType(suggestionList.get(i)) == PASSWORD_INFO) {
                 UserInfo info = (UserInfo) suggestionList.get(i).getDataPiece();
-                for (UserInfo.Field field : info.getFields()) {
+                for (UserInfoField field : info.getFields()) {
                     if (field.isSelectable()) ++interactiveSuggestions;
                 }
             }
diff --git a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/PasswordAccessorySheetModernViewBinder.java b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/PasswordAccessorySheetModernViewBinder.java
index 014a9af9..c706c1b 100644
--- a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/PasswordAccessorySheetModernViewBinder.java
+++ b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/PasswordAccessorySheetModernViewBinder.java
@@ -10,6 +10,7 @@
 
 import org.chromium.chrome.browser.keyboard_accessory.R;
 import org.chromium.chrome.browser.keyboard_accessory.data.KeyboardAccessoryData;
+import org.chromium.chrome.browser.keyboard_accessory.data.UserInfoField;
 import org.chromium.chrome.browser.keyboard_accessory.sheet_tabs.AccessorySheetTabModel.AccessorySheetDataPiece;
 import org.chromium.chrome.browser.keyboard_accessory.sheet_tabs.AccessorySheetTabViewBinder.ElementViewHolder;
 import org.chromium.ui.modelutil.ListModel;
@@ -57,7 +58,7 @@
             }
         }
 
-        void bindChipView(ChipView chip, KeyboardAccessoryData.UserInfo.Field field) {
+        void bindChipView(ChipView chip, UserInfoField field) {
             chip.getPrimaryTextView().setTransformationMethod(
                     field.isObfuscated() ? new PasswordTransformationMethod() : null);
             chip.getPrimaryTextView().setText(field.getDisplayText());
diff --git a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/PasswordAccessorySheetViewBinder.java b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/PasswordAccessorySheetViewBinder.java
index d33e000a..73e0119 100644
--- a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/PasswordAccessorySheetViewBinder.java
+++ b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/PasswordAccessorySheetViewBinder.java
@@ -23,6 +23,7 @@
 import org.chromium.chrome.browser.keyboard_accessory.R;
 import org.chromium.chrome.browser.keyboard_accessory.data.KeyboardAccessoryData;
 import org.chromium.chrome.browser.keyboard_accessory.data.KeyboardAccessoryData.FooterCommand;
+import org.chromium.chrome.browser.keyboard_accessory.data.UserInfoField;
 import org.chromium.chrome.browser.keyboard_accessory.sheet_tabs.AccessorySheetTabModel.AccessorySheetDataPiece;
 import org.chromium.chrome.browser.keyboard_accessory.sheet_tabs.AccessorySheetTabViewBinder.ElementViewHolder;
 import org.chromium.ui.modelutil.ListModel;
@@ -113,7 +114,7 @@
             ViewCompat.setPaddingRelative(password, 2 * mPadding + mIconSize, 0, mPadding, 0);
         }
 
-        private void bindTextView(TextView text, KeyboardAccessoryData.UserInfo.Field field) {
+        private void bindTextView(TextView text, UserInfoField field) {
             text.setTransformationMethod(
                     field.isObfuscated() ? new PasswordTransformationMethod() : null);
             // With transformation, the character set forces a LTR gravity. Therefore, invert it:
diff --git a/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/PasswordAccessorySheetModernViewTest.java b/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/PasswordAccessorySheetModernViewTest.java
index dd4fac4..12d8579f 100644
--- a/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/PasswordAccessorySheetModernViewTest.java
+++ b/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/PasswordAccessorySheetModernViewTest.java
@@ -30,6 +30,7 @@
 import org.chromium.chrome.browser.keyboard_accessory.R;
 import org.chromium.chrome.browser.keyboard_accessory.data.KeyboardAccessoryData;
 import org.chromium.chrome.browser.keyboard_accessory.data.KeyboardAccessoryData.UserInfo;
+import org.chromium.chrome.browser.keyboard_accessory.data.UserInfoField;
 import org.chromium.chrome.browser.keyboard_accessory.sheet_component.AccessorySheetCoordinator;
 import org.chromium.chrome.browser.keyboard_accessory.sheet_tabs.AccessorySheetTabModel.AccessorySheetDataPiece;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
@@ -112,9 +113,9 @@
         assertThat(mView.get().getChildCount(), is(0));
 
         UserInfo testInfo = new UserInfo(null);
-        testInfo.addField(new UserInfo.Field(
+        testInfo.addField(new UserInfoField(
                 "Name Suggestion", "Name Suggestion", false, item -> clicked.set(true)));
-        testInfo.addField(new UserInfo.Field(
+        testInfo.addField(new UserInfoField(
                 "Password Suggestion", "Password Suggestion", true, item -> clicked.set(true)));
         TestThreadUtils.runOnUiThreadBlocking(() -> {
             mModel.add(new AccessorySheetDataPiece(
diff --git a/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/PasswordAccessorySheetViewTest.java b/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/PasswordAccessorySheetViewTest.java
index 07558b5..dbd5836 100644
--- a/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/PasswordAccessorySheetViewTest.java
+++ b/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/PasswordAccessorySheetViewTest.java
@@ -31,6 +31,7 @@
 import org.chromium.chrome.browser.keyboard_accessory.R;
 import org.chromium.chrome.browser.keyboard_accessory.data.KeyboardAccessoryData;
 import org.chromium.chrome.browser.keyboard_accessory.data.KeyboardAccessoryData.UserInfo;
+import org.chromium.chrome.browser.keyboard_accessory.data.UserInfoField;
 import org.chromium.chrome.browser.keyboard_accessory.sheet_component.AccessorySheetCoordinator;
 import org.chromium.chrome.browser.keyboard_accessory.sheet_tabs.AccessorySheetTabModel.AccessorySheetDataPiece;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
@@ -124,9 +125,9 @@
         assertThat(mView.get().getChildCount(), is(0));
 
         UserInfo testInfo = new UserInfo(null);
-        testInfo.addField(new UserInfo.Field(
+        testInfo.addField(new UserInfoField(
                 "Name Suggestion", "Name Suggestion", false, item -> clicked.set(true)));
-        testInfo.addField(new UserInfo.Field(
+        testInfo.addField(new UserInfoField(
                 "Password Suggestion", "Password Suggestion", true, item -> clicked.set(true)));
         TestThreadUtils.runOnUiThreadBlocking(() -> {
             mModel.add(new AccessorySheetDataPiece(
diff --git a/chrome/android/features/keyboard_accessory/junit/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingControllerTest.java b/chrome/android/features/keyboard_accessory/junit/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingControllerTest.java
index 3510106..dccd860 100644
--- a/chrome/android/features/keyboard_accessory/junit/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingControllerTest.java
+++ b/chrome/android/features/keyboard_accessory/junit/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingControllerTest.java
@@ -69,6 +69,7 @@
 import org.chromium.chrome.browser.keyboard_accessory.data.KeyboardAccessoryData.Action;
 import org.chromium.chrome.browser.keyboard_accessory.data.KeyboardAccessoryData.UserInfo;
 import org.chromium.chrome.browser.keyboard_accessory.data.PropertyProvider;
+import org.chromium.chrome.browser.keyboard_accessory.data.UserInfoField;
 import org.chromium.chrome.browser.keyboard_accessory.sheet_component.AccessorySheetCoordinator;
 import org.chromium.chrome.browser.keyboard_accessory.sheet_tabs.PasswordAccessorySheetCoordinator;
 import org.chromium.chrome.browser.tab.Tab;
@@ -189,8 +190,8 @@
             AccessorySheetData sheetData =
                     new AccessorySheetData(FallbackSheetType.PASSWORD, "Passwords");
             UserInfo userInfo = new UserInfo(null);
-            userInfo.addField(new UserInfo.Field("(No username)", "No username", false, null));
-            userInfo.addField(new UserInfo.Field(passwordString, "Password", true, null));
+            userInfo.addField(new UserInfoField("(No username)", "No username", false, null));
+            userInfo.addField(new UserInfoField(passwordString, "Password", true, null));
             sheetData.getUserInfoList().add(userInfo);
             mAccessorySheetDataProvider.notifyObservers(sheetData);
         }
diff --git a/chrome/android/features/keyboard_accessory/junit/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/PasswordAccessorySheetControllerTest.java b/chrome/android/features/keyboard_accessory/junit/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/PasswordAccessorySheetControllerTest.java
index 63840b1..2a6ba34 100644
--- a/chrome/android/features/keyboard_accessory/junit/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/PasswordAccessorySheetControllerTest.java
+++ b/chrome/android/features/keyboard_accessory/junit/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/PasswordAccessorySheetControllerTest.java
@@ -44,6 +44,7 @@
 import org.chromium.chrome.browser.keyboard_accessory.data.KeyboardAccessoryData.FooterCommand;
 import org.chromium.chrome.browser.keyboard_accessory.data.KeyboardAccessoryData.UserInfo;
 import org.chromium.chrome.browser.keyboard_accessory.data.PropertyProvider;
+import org.chromium.chrome.browser.keyboard_accessory.data.UserInfoField;
 import org.chromium.ui.modelutil.ListObservable;
 
 import java.util.HashMap;
@@ -126,9 +127,9 @@
         final AccessorySheetData testData =
                 new AccessorySheetData(FallbackSheetType.PASSWORD, "Passwords for this site");
         testData.getUserInfoList().add(new UserInfo(null));
-        testData.getUserInfoList().get(0).addField(new UserInfo.Field("Name", "Name", false, null));
+        testData.getUserInfoList().get(0).addField(new UserInfoField("Name", "Name", false, null));
         testData.getUserInfoList().get(0).addField(
-                new UserInfo.Field("Password", "Password for Name", true, field -> {}));
+                new UserInfoField("Password", "Password for Name", true, field -> {}));
         testData.getFooterCommands().add(new FooterCommand("Manage passwords", result -> {}));
 
         mCoordinator.registerDataProvider(testProvider);
@@ -162,9 +163,9 @@
 
         // As soon UserInfo is available, discard the title.
         testData.getUserInfoList().add(new UserInfo(null));
-        testData.getUserInfoList().get(0).addField(new UserInfo.Field("Name", "Name", false, null));
+        testData.getUserInfoList().get(0).addField(new UserInfoField("Name", "Name", false, null));
         testData.getUserInfoList().get(0).addField(
-                new UserInfo.Field("Password", "Password for Name", true, field -> {}));
+                new UserInfoField("Password", "Password for Name", true, field -> {}));
         testProvider.notifyObservers(testData);
 
         assertThat(mSheetDataPieces.size(), is(2));
@@ -205,16 +206,16 @@
 
         // If the tab is shown with X interactive item, record "X" samples.
         UserInfo userInfo1 = new UserInfo(null);
-        userInfo1.addField(new UserInfo.Field("Interactive 1", "", false, (v) -> {}));
-        userInfo1.addField(new UserInfo.Field("Non-Interactive 1", "", true, null));
+        userInfo1.addField(new UserInfoField("Interactive 1", "", false, (v) -> {}));
+        userInfo1.addField(new UserInfoField("Non-Interactive 1", "", true, null));
         accessorySheetData.getUserInfoList().add(userInfo1);
         UserInfo userInfo2 = new UserInfo(null);
-        userInfo2.addField(new UserInfo.Field("Interactive 2", "", false, (v) -> {}));
-        userInfo2.addField(new UserInfo.Field("Non-Interactive 2", "", true, null));
+        userInfo2.addField(new UserInfoField("Interactive 2", "", false, (v) -> {}));
+        userInfo2.addField(new UserInfoField("Non-Interactive 2", "", true, null));
         accessorySheetData.getUserInfoList().add(userInfo2);
         UserInfo userInfo3 = new UserInfo(null);
-        userInfo3.addField(new UserInfo.Field("Interactive 3", "", false, (v) -> {}));
-        userInfo3.addField(new UserInfo.Field("Non-Interactive 3", "", true, null));
+        userInfo3.addField(new UserInfoField("Interactive 3", "", false, (v) -> {}));
+        userInfo3.addField(new UserInfoField("Non-Interactive 3", "", true, null));
         accessorySheetData.getUserInfoList().add(userInfo3);
         testProvider.notifyObservers(accessorySheetData);
         mCoordinator.onTabShown();
diff --git a/chrome/android/features/keyboard_accessory/public/BUILD.gn b/chrome/android/features/keyboard_accessory/public/BUILD.gn
index d9371680..2bc922b 100644
--- a/chrome/android/features/keyboard_accessory/public/BUILD.gn
+++ b/chrome/android/features/keyboard_accessory/public/BUILD.gn
@@ -13,9 +13,10 @@
   ]
   java_files = [
     "java/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingComponent.java",
+    "java/src/org/chromium/chrome/browser/keyboard_accessory/data/KeyboardAccessoryData.java",
     "java/src/org/chromium/chrome/browser/keyboard_accessory/data/Provider.java",
     "java/src/org/chromium/chrome/browser/keyboard_accessory/data/PropertyProvider.java",
-    "java/src/org/chromium/chrome/browser/keyboard_accessory/data/KeyboardAccessoryData.java",
+    "java/src/org/chromium/chrome/browser/keyboard_accessory/data/UserInfoField.java",
   ]
   srcjar_deps = [ ":java_enums_srcjar" ]
 }
diff --git a/chrome/android/features/keyboard_accessory/public/java/src/org/chromium/chrome/browser/keyboard_accessory/data/KeyboardAccessoryData.java b/chrome/android/features/keyboard_accessory/public/java/src/org/chromium/chrome/browser/keyboard_accessory/data/KeyboardAccessoryData.java
index 2a828fe..9c80b76 100644
--- a/chrome/android/features/keyboard_accessory/public/java/src/org/chromium/chrome/browser/keyboard_accessory/data/KeyboardAccessoryData.java
+++ b/chrome/android/features/keyboard_accessory/public/java/src/org/chromium/chrome/browser/keyboard_accessory/data/KeyboardAccessoryData.java
@@ -176,7 +176,7 @@
      * (username + password), to be shown on the manual fallback UI.
      */
     public final static class UserInfo {
-        private final List<Field> mFields = new ArrayList<>();
+        private final List<UserInfoField> mFields = new ArrayList<>();
         private final @Nullable FaviconProvider mFaviconProvider;
 
         /**
@@ -191,69 +191,6 @@
             void fetchFavicon(@Px int desiredSize, Callback<Bitmap> favicon);
         }
 
-        /**
-         * Represents an item (either selectable or not) presented on the UI, such as the username
-         * or a credit card number.
-         */
-        public final static class Field {
-            private final String mDisplayText;
-            private final String mA11yDescription;
-            private final boolean mIsObfuscated;
-            private final Callback<Field> mCallback;
-
-            /**
-             * Creates a new Field.
-             * @param displayText The text to display. Plain text if |isObfuscated| is false.
-             * @param a11yDescription The description used for accessibility.
-             * @param isObfuscated If true, the displayed caption is transformed into stars.
-             * @param callback Called when the user taps the suggestions.
-             */
-            public Field(String displayText, String a11yDescription, boolean isObfuscated,
-                    Callback<Field> callback) {
-                mDisplayText = displayText;
-                mA11yDescription = a11yDescription;
-                mIsObfuscated = isObfuscated;
-                mCallback = callback;
-            }
-
-            /**
-             * Returns the text to be displayed on the UI.
-             */
-            public String getDisplayText() {
-                return mDisplayText;
-            }
-
-            /**
-             * Returns a translated description that can be used for accessibility.
-             */
-            public String getA11yDescription() {
-                return mA11yDescription;
-            }
-
-            /**
-             * Returns whether the user can interact with the selected suggestion. For example,
-             * this is false if this is a password suggestion on a non-password input field.
-             */
-            public boolean isSelectable() {
-                return mCallback != null;
-            }
-
-            /**
-             * Returns true if obfuscation should be applied to the item's caption, for example to
-             * hide passwords.
-             */
-            public boolean isObfuscated() {
-                return mIsObfuscated;
-            }
-
-            /**
-             * The delegate is called when the Item is selected by a user.
-             */
-            public void triggerSelection() {
-                if (mCallback != null) mCallback.onResult(this);
-            }
-        }
-
         public UserInfo(@Nullable FaviconProvider faviconProvider) {
             mFaviconProvider = faviconProvider;
         }
@@ -262,14 +199,14 @@
          * Adds a new field to the group.
          * @param field The field to be added.
          */
-        public void addField(Field field) {
+        public void addField(UserInfoField field) {
             mFields.add(field);
         }
 
         /**
          * Returns the list of fields in this group.
          */
-        public List<Field> getFields() {
+        public List<UserInfoField> getFields() {
             return mFields;
         }
 
diff --git a/chrome/android/features/keyboard_accessory/public/java/src/org/chromium/chrome/browser/keyboard_accessory/data/UserInfoField.java b/chrome/android/features/keyboard_accessory/public/java/src/org/chromium/chrome/browser/keyboard_accessory/data/UserInfoField.java
new file mode 100644
index 0000000..0e44b3a
--- /dev/null
+++ b/chrome/android/features/keyboard_accessory/public/java/src/org/chromium/chrome/browser/keyboard_accessory/data/UserInfoField.java
@@ -0,0 +1,74 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.keyboard_accessory.data;
+
+import org.chromium.base.Callback;
+import org.chromium.base.annotations.CalledByNative;
+
+/**
+ * Represents an item (either selectable or not) presented on the UI, such as the username
+ * or a credit card number.
+ */
+public final class UserInfoField {
+    private final String mDisplayText;
+    private final String mA11yDescription;
+    private final boolean mIsObfuscated;
+    private final Callback<UserInfoField> mCallback;
+
+    /**
+     * @param displayText The text to display. Plain text if |isObfuscated| is false.
+     * @param a11yDescription The description used for accessibility.
+     * @param isObfuscated If true, the displayed caption is transformed into stars.
+     * @param callback Called when the user taps the suggestions.
+     */
+    public UserInfoField(String displayText, String a11yDescription, boolean isObfuscated,
+            Callback<UserInfoField> callback) {
+        mDisplayText = displayText;
+        mA11yDescription = a11yDescription;
+        mIsObfuscated = isObfuscated;
+        mCallback = callback;
+    }
+
+    /**
+     * Returns the text to be displayed on the UI.
+     */
+    @CalledByNative
+    public String getDisplayText() {
+        return mDisplayText;
+    }
+
+    /**
+     * Returns a translated description that can be used for accessibility.
+     */
+    @CalledByNative
+    public String getA11yDescription() {
+        return mA11yDescription;
+    }
+
+    /**
+     * Returns whether the user can interact with the selected suggestion. For example,
+     * this is false if this is a password suggestion on a non-password input field.
+     */
+    @CalledByNative
+    public boolean isSelectable() {
+        return mCallback != null;
+    }
+
+    /**
+     * Returns true if obfuscation should be applied to the item's caption, for example to
+     * hide passwords.
+     */
+    @CalledByNative
+    public boolean isObfuscated() {
+        return mIsObfuscated;
+    }
+
+    /**
+     * The delegate is called when the Item is selected by a user.
+     */
+    public void triggerSelection() {
+        if (mCallback != null) mCallback.onResult(this);
+    }
+}
diff --git a/chrome/android/features/tab_ui/BUILD.gn b/chrome/android/features/tab_ui/BUILD.gn
index b22fce0..8e21108 100644
--- a/chrome/android/features/tab_ui/BUILD.gn
+++ b/chrome/android/features/tab_ui/BUILD.gn
@@ -5,6 +5,14 @@
 import("//build/config/android/rules.gni")
 import("//chrome/common/features.gni")
 
+android_resources("java_resources") {
+  resource_dirs = [ "java/res" ]
+  deps = [
+    "//chrome/android:chrome_app_java_resources",
+  ]
+  custom_package = "org.chromium.chrome.tab_ui"
+}
+
 android_library("java") {
   java_files = [
     "java/src/org/chromium/chrome/browser/tasks/tab_groups/TabGroupUtils.java",
@@ -38,6 +46,10 @@
     "java/src/org/chromium/chrome/browser/tasks/tab_management/TabListFaviconProvider.java",
   ]
 
+  deps = [
+    ":java_resources",
+  ]
+
   classpath_deps = [
     "//base:base_java",
     "//chrome/android:chrome_java",
diff --git a/chrome/android/java/res/drawable-hdpi/tabstrip_selected.png b/chrome/android/features/tab_ui/java/res/drawable-hdpi/tabstrip_selected.png
similarity index 100%
rename from chrome/android/java/res/drawable-hdpi/tabstrip_selected.png
rename to chrome/android/features/tab_ui/java/res/drawable-hdpi/tabstrip_selected.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-mdpi/tabstrip_selected.png b/chrome/android/features/tab_ui/java/res/drawable-mdpi/tabstrip_selected.png
similarity index 100%
rename from chrome/android/java/res/drawable-mdpi/tabstrip_selected.png
rename to chrome/android/features/tab_ui/java/res/drawable-mdpi/tabstrip_selected.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-xhdpi/tabstrip_selected.png b/chrome/android/features/tab_ui/java/res/drawable-xhdpi/tabstrip_selected.png
similarity index 100%
rename from chrome/android/java/res/drawable-xhdpi/tabstrip_selected.png
rename to chrome/android/features/tab_ui/java/res/drawable-xhdpi/tabstrip_selected.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-xxhdpi/tabstrip_selected.png b/chrome/android/features/tab_ui/java/res/drawable-xxhdpi/tabstrip_selected.png
similarity index 100%
rename from chrome/android/java/res/drawable-xxhdpi/tabstrip_selected.png
rename to chrome/android/features/tab_ui/java/res/drawable-xxhdpi/tabstrip_selected.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-xxxhdpi/tabstrip_selected.png b/chrome/android/features/tab_ui/java/res/drawable-xxxhdpi/tabstrip_selected.png
similarity index 100%
rename from chrome/android/java/res/drawable-xxxhdpi/tabstrip_selected.png
rename to chrome/android/features/tab_ui/java/res/drawable-xxxhdpi/tabstrip_selected.png
Binary files differ
diff --git a/chrome/android/java/res/drawable/selected_tab_background.xml b/chrome/android/features/tab_ui/java/res/drawable/selected_tab_background.xml
similarity index 100%
rename from chrome/android/java/res/drawable/selected_tab_background.xml
rename to chrome/android/features/tab_ui/java/res/drawable/selected_tab_background.xml
diff --git a/chrome/android/java/res/drawable/tab_grid_card_background.xml b/chrome/android/features/tab_ui/java/res/drawable/tab_grid_card_background.xml
similarity index 100%
rename from chrome/android/java/res/drawable/tab_grid_card_background.xml
rename to chrome/android/features/tab_ui/java/res/drawable/tab_grid_card_background.xml
diff --git a/chrome/android/java/res/drawable/tabstrip_favicon_background.xml b/chrome/android/features/tab_ui/java/res/drawable/tabstrip_favicon_background.xml
similarity index 100%
rename from chrome/android/java/res/drawable/tabstrip_favicon_background.xml
rename to chrome/android/features/tab_ui/java/res/drawable/tabstrip_favicon_background.xml
diff --git a/chrome/android/java/res/layout/bottom_tab_grid_toolbar.xml b/chrome/android/features/tab_ui/java/res/layout/bottom_tab_grid_toolbar.xml
similarity index 100%
rename from chrome/android/java/res/layout/bottom_tab_grid_toolbar.xml
rename to chrome/android/features/tab_ui/java/res/layout/bottom_tab_grid_toolbar.xml
diff --git a/chrome/android/java/res/layout/bottom_tab_strip_toolbar.xml b/chrome/android/features/tab_ui/java/res/layout/bottom_tab_strip_toolbar.xml
similarity index 100%
rename from chrome/android/java/res/layout/bottom_tab_strip_toolbar.xml
rename to chrome/android/features/tab_ui/java/res/layout/bottom_tab_strip_toolbar.xml
diff --git a/chrome/android/java/res/layout/tab_grid_card_item.xml b/chrome/android/features/tab_ui/java/res/layout/tab_grid_card_item.xml
similarity index 100%
rename from chrome/android/java/res/layout/tab_grid_card_item.xml
rename to chrome/android/features/tab_ui/java/res/layout/tab_grid_card_item.xml
diff --git a/chrome/android/java/res/layout/tab_list_recycler_view_layout.xml b/chrome/android/features/tab_ui/java/res/layout/tab_list_recycler_view_layout.xml
similarity index 100%
rename from chrome/android/java/res/layout/tab_list_recycler_view_layout.xml
rename to chrome/android/features/tab_ui/java/res/layout/tab_list_recycler_view_layout.xml
diff --git a/chrome/android/java/res/layout/tab_strip_item.xml b/chrome/android/features/tab_ui/java/res/layout/tab_strip_item.xml
similarity index 100%
rename from chrome/android/java/res/layout/tab_strip_item.xml
rename to chrome/android/features/tab_ui/java/res/layout/tab_strip_item.xml
diff --git a/chrome/android/features/tab_ui/java/res/values/dimens.xml b/chrome/android/features/tab_ui/java/res/values/dimens.xml
new file mode 100644
index 0000000..487a5fc
--- /dev/null
+++ b/chrome/android/features/tab_ui/java/res/values/dimens.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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. -->
+<resources xmlns:tools="http://schemas.android.com/tools">
+    <dimen name="tab_grid_favicon_size">32dp</dimen>
+    <dimen name="tab_list_selected_inset">7dp</dimen>
+    <dimen name="tab_list_selected_inset_kitkat">2dp</dimen>
+    <dimen name="tab_list_card_padding">8dp</dimen>
+    <dimen name="tab_list_mini_card_radius">4dp</dimen>
+    <dimen name="tab_list_mini_card_frame_size">1dp</dimen>
+    <dimen name="tab_grid_thumbnail_card_default_size">152dp</dimen>
+    <dimen name="tab_grid_thumbnail_favicon_frame_padding">16dp</dimen>
+    <dimen name="tab_grid_thumbnail_favicon_padding">24dp</dimen>
+    <dimen name="tab_grid_thumbnail_favicon_background_radius">3dp</dimen>
+    <dimen name="tab_grid_thumbnail_favicon_background_padding">2dp</dimen>
+    <dimen name="tab_grid_thumbnail_favicon_background_down_shift">2dp</dimen>
+    <dimen name="swipe_to_dismiss_threshold">72dp</dimen>
+</resources>
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/MultiThumbnailCardProvider.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/MultiThumbnailCardProvider.java
index c5eeaa7..74745363 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/MultiThumbnailCardProvider.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/MultiThumbnailCardProvider.java
@@ -18,11 +18,11 @@
 import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.base.Callback;
 import org.chromium.base.task.PostTask;
-import org.chromium.chrome.R;
 import org.chromium.chrome.browser.compositor.layouts.content.TabContentManager;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
+import org.chromium.chrome.tab_ui.R;
 import org.chromium.content_public.browser.UiThreadTaskTraits;
 
 import java.util.ArrayList;
@@ -183,21 +183,21 @@
         // Initialize Paints to use.
         mEmptyThumbnailPaint = new Paint();
         mEmptyThumbnailPaint.setStyle(Paint.Style.FILL);
-        mEmptyThumbnailPaint.setColor(
-                ApiCompatibilityUtils.getColor(context.getResources(), R.color.modern_grey_100));
+        mEmptyThumbnailPaint.setColor(ApiCompatibilityUtils.getColor(
+                context.getResources(), org.chromium.chrome.R.color.modern_grey_100));
         mEmptyThumbnailPaint.setAntiAlias(true);
 
         mThumbnailFramePaint = new Paint();
         mThumbnailFramePaint.setStyle(Paint.Style.STROKE);
         mThumbnailFramePaint.setStrokeWidth(
                 context.getResources().getDimension(R.dimen.tab_list_mini_card_frame_size));
-        mThumbnailFramePaint.setColor(
-                ApiCompatibilityUtils.getColor(context.getResources(), R.color.modern_grey_300));
+        mThumbnailFramePaint.setColor(ApiCompatibilityUtils.getColor(
+                context.getResources(), org.chromium.chrome.R.color.modern_grey_300));
         mThumbnailFramePaint.setAntiAlias(true);
 
         mTextPaint = new Paint();
-        mTextPaint.setTextSize(
-                context.getResources().getDimension(R.dimen.compositor_tab_title_text_size));
+        mTextPaint.setTextSize(context.getResources().getDimension(
+                org.chromium.chrome.R.dimen.compositor_tab_title_text_size));
         mTextPaint.setFakeBoldText(true);
         mTextPaint.setAntiAlias(true);
         mTextPaint.setTextAlign(Paint.Align.CENTER);
@@ -212,7 +212,8 @@
                 0,
                 context.getResources().getDimension(
                         R.dimen.tab_grid_thumbnail_favicon_background_down_shift),
-                context.getResources().getColor(R.color.modern_grey_800_alpha_38));
+                context.getResources().getColor(
+                        org.chromium.chrome.R.color.modern_grey_800_alpha_38));
 
         // Initialize Rects for thumbnails.
         float thumbnailPadding = context.getResources().getDimension(R.dimen.tab_list_card_padding);
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/SilenceLintErrors.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/SilenceLintErrors.java
index e3714ddd..0035890 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/SilenceLintErrors.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/SilenceLintErrors.java
@@ -11,30 +11,21 @@
  */
 /* package */ class SilenceLintErrors {
     // TODO(yusufo): Add these resources to the DFM
-    private int[] mRes = new int[] {R.dimen.tab_grid_favicon_size,
-            R.string.tab_management_module_title,
+    private int[] mRes = new int[] {
             R.string.iph_tab_groups_quickly_compare_pages_text,
             R.string.iph_tab_groups_tap_to_see_another_tab_text,
-            R.string.iph_tab_groups_your_tabs_together_text, R.string.bottom_tab_grid_description,
-            R.string.bottom_tab_grid_opened_half, R.string.bottom_tab_grid_opened_full,
-            R.string.bottom_tab_grid_closed, R.dimen.tab_list_selected_inset,
-            R.layout.tab_strip_item, R.drawable.selected_tab_background,
-            R.drawable.tab_grid_card_background, R.layout.tab_grid_card_item,
-            R.layout.tab_list_recycler_view_layout, R.layout.bottom_tab_grid_toolbar,
-            R.string.bottom_tab_grid_new_tab, R.string.bottom_tab_grid_new_tab,
+            R.string.iph_tab_groups_your_tabs_together_text,
+            R.string.bottom_tab_grid_description,
+            R.string.bottom_tab_grid_opened_half,
+            R.string.bottom_tab_grid_opened_full,
+            R.string.bottom_tab_grid_closed,
+            R.string.bottom_tab_grid_new_tab,
+            R.string.bottom_tab_grid_new_tab,
             R.plurals.bottom_tab_grid_title_placeholder,
             R.string.iph_tab_groups_tap_to_see_another_tab_accessibility_text,
             R.string.accessibility_bottom_tab_strip_expand_tab_sheet,
-            R.layout.bottom_tab_strip_toolbar, R.drawable.tabstrip_selected,
-            R.dimen.tab_list_card_padding, R.dimen.tab_list_mini_card_text_size,
-            R.dimen.tab_list_mini_card_frame_size, R.dimen.tab_list_mini_card_radius,
-            R.drawable.tabstrip_favicon_background, R.dimen.swipe_to_dismiss_threshold,
-            R.dimen.tab_grid_thumbnail_card_default_size, R.dimen.tab_list_selected_inset_kitkat,
-            R.dimen.tab_grid_thumbnail_favicon_padding,
-            R.dimen.tab_grid_thumbnail_favicon_frame_padding,
-            R.dimen.tab_grid_thumbnail_favicon_background_radius,
-            R.dimen.tab_grid_thumbnail_favicon_background_padding,
-            R.dimen.tab_grid_thumbnail_favicon_background_down_shift};
+            R.string.accessibility_bottom_tab_grid_close_tab_sheet,
+    };
 
     private SilenceLintErrors() {}
 }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridSheetToolbarCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridSheetToolbarCoordinator.java
index 6548ef3..557c080 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridSheetToolbarCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridSheetToolbarCoordinator.java
@@ -9,8 +9,8 @@
 import android.view.View;
 import android.view.ViewGroup;
 
-import org.chromium.chrome.R;
 import org.chromium.chrome.browser.lifecycle.Destroyable;
+import org.chromium.chrome.tab_ui.R;
 import org.chromium.ui.modelutil.PropertyModel;
 import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
 
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewBinder.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewBinder.java
index 96050bc..250e20ef 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewBinder.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewBinder.java
@@ -15,7 +15,7 @@
 import android.view.View;
 
 import org.chromium.base.Callback;
-import org.chromium.chrome.R;
+import org.chromium.chrome.tab_ui.R;
 import org.chromium.ui.modelutil.PropertyKey;
 import org.chromium.ui.modelutil.PropertyModel;
 
@@ -41,7 +41,7 @@
             String title = item.get(TabProperties.TITLE);
             holder.title.setText(title);
             holder.closeButton.setContentDescription(holder.itemView.getResources().getString(
-                    R.string.accessibility_tabstrip_btn_close_tab, title));
+                    org.chromium.chrome.R.string.accessibility_tabstrip_btn_close_tab, title));
         } else if (TabProperties.IS_SELECTED == propertyKey) {
             Resources res = holder.itemView.getResources();
             Resources.Theme theme = holder.itemView.getContext().getTheme();
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewHolder.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewHolder.java
index 18a89e2..39e270b 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewHolder.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewHolder.java
@@ -13,7 +13,7 @@
 import android.widget.TextView;
 
 import org.chromium.base.ApiCompatibilityUtils;
-import org.chromium.chrome.R;
+import org.chromium.chrome.tab_ui.R;
 import org.chromium.ui.widget.ButtonCompat;
 
 /**
@@ -36,15 +36,15 @@
         this.favicon = itemView.findViewById(R.id.tab_favicon);
         this.closeButton = itemView.findViewById(R.id.close_button);
         DrawableCompat.setTint(this.closeButton.getDrawable(),
-                ApiCompatibilityUtils.getColor(itemView.getResources(), R.color.light_icon_color));
+                ApiCompatibilityUtils.getColor(
+                        itemView.getResources(), org.chromium.chrome.R.color.light_icon_color));
         this.createGroupButton = itemView.findViewById(R.id.create_group_button);
         this.backgroundView = itemView.findViewById(R.id.background_view);
     }
 
     public static TabGridViewHolder create(ViewGroup parent, int itemViewType) {
-        View view =
-                LayoutInflater.from(parent.getContext())
-                        .inflate(org.chromium.chrome.R.layout.tab_grid_card_item, parent, false);
+        View view = LayoutInflater.from(parent.getContext())
+                            .inflate(R.layout.tab_grid_card_item, parent, false);
         return new TabGridViewHolder(view);
     }
 
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiToolbarView.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiToolbarView.java
index a2ec9d73..87657a0 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiToolbarView.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiToolbarView.java
@@ -13,7 +13,7 @@
 import android.widget.TextView;
 
 import org.chromium.base.ApiCompatibilityUtils;
-import org.chromium.chrome.R;
+import org.chromium.chrome.tab_ui.R;
 import org.chromium.ui.widget.ChromeImageView;
 
 /**
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListCoordinator.java
index d202496..12fcb47 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListCoordinator.java
@@ -15,12 +15,12 @@
 import android.view.LayoutInflater;
 import android.view.ViewGroup;
 
-import org.chromium.chrome.R;
 import org.chromium.chrome.browser.lifecycle.Destroyable;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.browser.tasks.tab_groups.TabGroupUtils;
+import org.chromium.chrome.tab_ui.R;
 import org.chromium.components.feature_engagement.FeatureConstants;
 import org.chromium.ui.modelutil.PropertyKey;
 import org.chromium.ui.modelutil.PropertyModel;
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabStripToolbarCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabStripToolbarCoordinator.java
index a69b64a..f819ccd 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabStripToolbarCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabStripToolbarCoordinator.java
@@ -9,8 +9,8 @@
 import android.view.View;
 import android.view.ViewGroup;
 
-import org.chromium.chrome.R;
 import org.chromium.chrome.browser.lifecycle.Destroyable;
+import org.chromium.chrome.tab_ui.R;
 import org.chromium.ui.modelutil.PropertyModel;
 import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
 
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabStripViewBinder.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabStripViewBinder.java
index a26896a..72c89ae 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabStripViewBinder.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabStripViewBinder.java
@@ -10,7 +10,7 @@
 import android.support.v7.widget.RecyclerView.ViewHolder;
 import android.widget.FrameLayout;
 
-import org.chromium.chrome.R;
+import org.chromium.chrome.tab_ui.R;
 import org.chromium.ui.modelutil.PropertyKey;
 import org.chromium.ui.modelutil.PropertyModel;
 
@@ -45,13 +45,13 @@
                     item.get(TabProperties.TAB_CLOSED_LISTENER).run(holder.getTabId());
                 });
                 holder.button.setContentDescription(holder.itemView.getContext().getString(
-                        R.string.accessibility_tabstrip_btn_close_tab, title));
+                        org.chromium.chrome.R.string.accessibility_tabstrip_btn_close_tab, title));
             } else {
                 holder.button.setOnClickListener(view -> {
                     item.get(TabProperties.TAB_SELECTED_LISTENER).run(holder.getTabId());
                 });
                 holder.button.setContentDescription(holder.itemView.getContext().getString(
-                        R.string.accessibility_tabstrip_tab, title));
+                        org.chromium.chrome.R.string.accessibility_tabstrip_tab, title));
             }
         } else if (TabProperties.FAVICON == propertyKey) {
             Drawable faviconDrawable = item.get(TabProperties.FAVICON);
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabStripViewHolder.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabStripViewHolder.java
index d199c08..0a1c9550 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabStripViewHolder.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabStripViewHolder.java
@@ -10,7 +10,7 @@
 import android.view.ViewGroup;
 import android.widget.ImageButton;
 
-import org.chromium.chrome.R;
+import org.chromium.chrome.tab_ui.R;
 
 /**
  * {@link RecyclerView.ViewHolder} for tab strip.
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridContainerViewBinderTest.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridContainerViewBinderTest.java
index fd6613b..c4c968fe 100644
--- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridContainerViewBinderTest.java
+++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridContainerViewBinderTest.java
@@ -22,7 +22,7 @@
 import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.base.test.util.CallbackHelper;
 import org.chromium.base.test.util.DisabledTest;
-import org.chromium.chrome.R;
+import org.chromium.chrome.tab_ui.R;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.ui.DummyUiActivity;
 import org.chromium.chrome.test.ui.DummyUiActivityTestCase;
@@ -207,14 +207,14 @@
         mContainerModel.set(TabListContainerProperties.IS_INCOGNITO, true);
         assertThat(mRecyclerView.getBackground(), instanceOf(ColorDrawable.class));
         assertThat(((ColorDrawable) mRecyclerView.getBackground()).getColor(),
-                equalTo(ApiCompatibilityUtils.getColor(
-                        mRecyclerView.getResources(), R.color.incognito_modern_primary_color)));
+                equalTo(ApiCompatibilityUtils.getColor(mRecyclerView.getResources(),
+                        org.chromium.chrome.R.color.incognito_modern_primary_color)));
 
         mContainerModel.set(TabListContainerProperties.IS_INCOGNITO, false);
         assertThat(mRecyclerView.getBackground(), instanceOf(ColorDrawable.class));
         assertThat(((ColorDrawable) mRecyclerView.getBackground()).getColor(),
-                equalTo(ApiCompatibilityUtils.getColor(
-                        mRecyclerView.getResources(), R.color.modern_primary_color)));
+                equalTo(ApiCompatibilityUtils.getColor(mRecyclerView.getResources(),
+                        org.chromium.chrome.R.color.modern_primary_color)));
     }
 
     @Test
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabListViewHolderTest.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabListViewHolderTest.java
index ad0092fe..19a7c2a 100644
--- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabListViewHolderTest.java
+++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabListViewHolderTest.java
@@ -23,8 +23,8 @@
 import org.junit.runner.RunWith;
 
 import org.chromium.base.Callback;
-import org.chromium.chrome.R;
 import org.chromium.chrome.browser.tab.Tab;
+import org.chromium.chrome.tab_ui.R;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.ui.DummyUiActivityTestCase;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
diff --git a/chrome/android/java/res/values/dimens.xml b/chrome/android/java/res/values/dimens.xml
index ecbe8a6..16544e7 100644
--- a/chrome/android/java/res/values/dimens.xml
+++ b/chrome/android/java/res/values/dimens.xml
@@ -597,22 +597,6 @@
     <!-- Explicit Language Ask Prompt dimensions -->
     <dimen name="explicit_ask_checkbox_end_padding">4dp</dimen>
 
-    <!-- Tab List dimensions -->
-    <dimen name="tab_grid_favicon_size">32dp</dimen>
-    <dimen name="tab_list_selected_inset">7dp</dimen>
-    <dimen name="tab_list_selected_inset_kitkat">2dp</dimen>
-    <dimen name="tab_list_card_padding">8dp</dimen>
-    <dimen name="tab_list_mini_card_radius">4dp</dimen>
-    <dimen name="tab_list_mini_card_frame_size">1dp</dimen>
-    <dimen name="tab_list_mini_card_text_size">12sp</dimen>
-    <dimen name="tab_grid_thumbnail_card_default_size">152dp</dimen>
-    <dimen name="tab_grid_thumbnail_favicon_frame_padding">16dp</dimen>
-    <dimen name="tab_grid_thumbnail_favicon_padding">24dp</dimen>
-    <dimen name="tab_grid_thumbnail_favicon_background_radius">3dp</dimen>
-    <dimen name="tab_grid_thumbnail_favicon_background_padding">2dp</dimen>
-    <dimen name="tab_grid_thumbnail_favicon_background_down_shift">2dp</dimen>
-    <dimen name="swipe_to_dismiss_threshold">72dp</dimen>
-
     <!-- RadioButtonWithDescription dimensions -->
     <dimen name="radio_button_with_description_lateral_padding">16dp</dimen>
     <dimen name="radio_button_with_description_vertical_padding">10dp</dimen>
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/AutofillUiUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/AutofillUiUtils.java
index 85bedb5..019bb97 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/AutofillUiUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/AutofillUiUtils.java
@@ -118,6 +118,7 @@
                     || (!monthInput.isFocused() && didFocusOnMonth)) {
                 return ErrorType.EXPIRATION_MONTH;
             }
+            // If year was focused before, proceed to check if year is valid.
             if (!didFocusOnYear) {
                 return ErrorType.NOT_ENOUGH_INFO;
             }
@@ -131,7 +132,10 @@
             }
             return ErrorType.NOT_ENOUGH_INFO;
         }
-
+        // Year is valid but month is still being edited.
+        if (month == -1) {
+            return ErrorType.NOT_ENOUGH_INFO;
+        }
         if (year == thisYear && month < thisMonth) {
             return ErrorType.EXPIRATION_DATE;
         }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/autofill/AutofillUiUtilsTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/autofill/AutofillUiUtilsTest.java
index c43e899f..9611a50 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/autofill/AutofillUiUtilsTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/autofill/AutofillUiUtilsTest.java
@@ -148,6 +148,32 @@
     @Test
     @SmallTest
     @SuppressLint("SetTextI18n")
+    public void testExpirationDateErrorWithMonthBeingEditedAndValidYearReturnsNotEnoughInfo() {
+        mMonthInput.setText("");
+        mMonthInput.requestFocus();
+        mYearInput.setText(String.valueOf(mTwoDigitThisYear + 1));
+
+        int errorType = getExpirationDateErrorForUserEnteredMonthAndYear();
+
+        Assert.assertEquals(ErrorType.NOT_ENOUGH_INFO, errorType);
+    }
+
+    @Test
+    @SmallTest
+    @SuppressLint("SetTextI18n")
+    public void testExpirationDateErrorWithMonthSetToZeroAndValidYearReturnsNotEnoughInfo() {
+        mMonthInput.setText("0");
+        mMonthInput.requestFocus();
+        mYearInput.setText(String.valueOf(mTwoDigitThisYear + 1));
+
+        int errorType = getExpirationDateErrorForUserEnteredMonthAndYear();
+
+        Assert.assertEquals(ErrorType.NOT_ENOUGH_INFO, errorType);
+    }
+
+    @Test
+    @SmallTest
+    @SuppressLint("SetTextI18n")
     public void testGetMonthWithNonNumericInputReturnsNegativeOne() {
         mMonthInput.setText("MM");
 
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/autofill/OWNERS b/chrome/android/junit/src/org/chromium/chrome/browser/autofill/OWNERS
new file mode 100644
index 0000000..b946bf67
--- /dev/null
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/autofill/OWNERS
@@ -0,0 +1 @@
+file://chrome/android/java/src/org/chromium/chrome/browser/autofill/OWNERS
\ No newline at end of file
diff --git a/chrome/android/profiles/newest.txt b/chrome/android/profiles/newest.txt
index 92e1fb8..e1cac02 100644
--- a/chrome/android/profiles/newest.txt
+++ b/chrome/android/profiles/newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-76.0.3776.0_rc-r1-merged.afdo.bz2
\ No newline at end of file
+chromeos-chrome-amd64-76.0.3777.0_rc-r1-merged.afdo.bz2
\ No newline at end of file
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 5e268e4..fd54901 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -2573,6 +2573,7 @@
       "android/webapps/webapp_registry.h",
       "android/widget/thumbnail_generator.cc",
       "android/widget/thumbnail_generator.h",
+      "autofill/accessory_controller.h",
       "autofill/android/personal_data_manager_android.cc",
       "autofill/android/personal_data_manager_android.h",
       "autofill/android/phone_number_util_android.cc",
@@ -2581,6 +2582,8 @@
       "autofill/manual_filling_controller.h",
       "autofill/manual_filling_controller_impl.cc",
       "autofill/manual_filling_controller_impl.h",
+      "autofill/manual_filling_utils.cc",
+      "autofill/manual_filling_utils.h",
       "autofill/manual_filling_view_interface.h",
       "banners/app_banner_infobar_delegate_android.cc",
       "banners/app_banner_infobar_delegate_android.h",
diff --git a/chrome/browser/autofill/accessory_controller.h b/chrome/browser/autofill/accessory_controller.h
new file mode 100644
index 0000000..a96f8ef5
--- /dev/null
+++ b/chrome/browser/autofill/accessory_controller.h
@@ -0,0 +1,26 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_AUTOFILL_ACCESSORY_CONTROLLER_H_
+#define CHROME_BROWSER_AUTOFILL_ACCESSORY_CONTROLLER_H_
+
+#include <vector>
+
+#include "components/autofill/core/browser/accessory_sheet_data.h"
+
+// Interface for the portions of type-specific manual filling controllers (e.g.,
+// password, credit card) which interact with the generic
+// ManualFillingController.
+class AccessoryController {
+ public:
+  virtual ~AccessoryController() = default;
+
+  // Triggered when a user selects an item for filling. This handler is
+  // responsible for propagating it so that it ultimately ends up in the form
+  // in the content area.
+  virtual void OnFillingTriggered(
+      const autofill::UserInfo::Field& selection) = 0;
+};
+
+#endif  // CHROME_BROWSER_AUTOFILL_ACCESSORY_CONTROLLER_H_
diff --git a/chrome/browser/autofill/manual_filling_controller.h b/chrome/browser/autofill/manual_filling_controller.h
index 28eec4e..261b19fa1 100644
--- a/chrome/browser/autofill/manual_filling_controller.h
+++ b/chrome/browser/autofill/manual_filling_controller.h
@@ -91,8 +91,8 @@
   // Called by the UI code to request that |text_to_fill| is to be filled into
   // the currently focused field. Forwards the request to a type-specific
   // accessory controller.
-  virtual void OnFillingTriggered(bool is_password,
-                                  const base::string16& text_to_fill) = 0;
+  virtual void OnFillingTriggered(
+      const autofill::UserInfo::Field& selection) = 0;
 
   // Called by the UI code because a user triggered the |selected_option|,
   // such as "Manage passwords..."
diff --git a/chrome/browser/autofill/manual_filling_controller_impl.cc b/chrome/browser/autofill/manual_filling_controller_impl.cc
index c5575bd..ac859f4 100644
--- a/chrome/browser/autofill/manual_filling_controller_impl.cc
+++ b/chrome/browser/autofill/manual_filling_controller_impl.cc
@@ -98,10 +98,9 @@
 }
 
 void ManualFillingControllerImpl::OnFillingTriggered(
-    bool is_password,
-    const base::string16& text_to_fill) {
+    const autofill::UserInfo::Field& selection) {
   DCHECK(pwd_controller_);
-  pwd_controller_->OnFillingTriggered(is_password, text_to_fill);
+  pwd_controller_->OnFillingTriggered(selection);
 }
 
 void ManualFillingControllerImpl::OnOptionSelected(
diff --git a/chrome/browser/autofill/manual_filling_controller_impl.h b/chrome/browser/autofill/manual_filling_controller_impl.h
index 2dc04c3e..04eaf5e 100644
--- a/chrome/browser/autofill/manual_filling_controller_impl.h
+++ b/chrome/browser/autofill/manual_filling_controller_impl.h
@@ -32,8 +32,7 @@
   void ShowWhenKeyboardIsVisible(FillingSource source) override;
   void Hide(FillingSource source) override;
   void OnAutomaticGenerationStatusChanged(bool available) override;
-  void OnFillingTriggered(bool is_password,
-                          const base::string16& text_to_fill) override;
+  void OnFillingTriggered(const autofill::UserInfo::Field& selection) override;
   void OnOptionSelected(const base::string16& selected_option) const override;
   void OnGenerationRequested() override;
   void GetFavicon(
diff --git a/chrome/browser/autofill/manual_filling_controller_impl_unittest.cc b/chrome/browser/autofill/manual_filling_controller_impl_unittest.cc
index c3037b4c..50c8fe16 100644
--- a/chrome/browser/autofill/manual_filling_controller_impl_unittest.cc
+++ b/chrome/browser/autofill/manual_filling_controller_impl_unittest.cc
@@ -41,7 +41,7 @@
   MOCK_METHOD0(DidNavigateMainFrame, void());
   MOCK_METHOD2(GetFavicon,
                void(int, base::OnceCallback<void(const gfx::Image&)>));
-  MOCK_METHOD2(OnFillingTriggered, void(bool, const base::string16&));
+  MOCK_METHOD1(OnFillingTriggered, void(const autofill::UserInfo::Field&));
   MOCK_CONST_METHOD1(OnOptionSelected, void(const base::string16&));
 };
 
@@ -68,7 +68,7 @@
   MockPasswordAccessoryView() = default;
 
   MOCK_METHOD1(OnItemsAvailable, void(const autofill::AccessorySheetData&));
-  MOCK_METHOD1(OnFillingTriggered, void(const base::string16&));
+  MOCK_METHOD1(OnFillingTriggered, void(const autofill::UserInfo::Field&));
   MOCK_METHOD0(OnViewDestroyed, void());
   MOCK_METHOD1(OnAutomaticGenerationStatusChanged, void(bool));
   MOCK_METHOD0(CloseAccessorySheet, void());
@@ -202,12 +202,11 @@
 TEST_F(ManualFillingControllerTest, OnFillingTriggered) {
   const char kTextToFill[] = "TextToFill";
   const base::string16 text_to_fill(base::ASCIIToUTF16(kTextToFill));
+  const autofill::UserInfo::Field field(text_to_fill, text_to_fill, false,
+                                        true);
 
-  EXPECT_CALL(mock_pwd_controller_, OnFillingTriggered(true, text_to_fill));
-  controller()->OnFillingTriggered(true, text_to_fill);
-
-  EXPECT_CALL(mock_pwd_controller_, OnFillingTriggered(false, text_to_fill));
-  controller()->OnFillingTriggered(false, text_to_fill);
+  EXPECT_CALL(mock_pwd_controller_, OnFillingTriggered(field));
+  controller()->OnFillingTriggered(field);
 }
 
 TEST_F(ManualFillingControllerTest, OnGenerationRequested) {
diff --git a/chrome/browser/autofill/manual_filling_utils.cc b/chrome/browser/autofill/manual_filling_utils.cc
new file mode 100644
index 0000000..28eaa679
--- /dev/null
+++ b/chrome/browser/autofill/manual_filling_utils.cc
@@ -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.
+
+#include "chrome/browser/autofill/manual_filling_utils.h"
+
+#include <utility>
+
+#include "chrome/grit/generated_resources.h"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace autofill {
+
+AccessorySheetData CreateAccessorySheetData(base::string16 title,
+                                            std::vector<UserInfo> user_info) {
+  // TODO(crbug.com/902425): Remove hard-coded enum value
+  AccessorySheetData data(FallbackSheetType::PASSWORD, std::move(title));
+  for (auto& i : user_info) {
+    data.add_user_info(std::move(i));
+  }
+
+  // TODO(crbug.com/902425): Generalize options (both adding to footer, and
+  // handling selection).
+  base::string16 manage_passwords_title = l10n_util::GetStringUTF16(
+      IDS_PASSWORD_MANAGER_ACCESSORY_ALL_PASSWORDS_LINK);
+  data.add_footer_command(FooterCommand(manage_passwords_title));
+
+  return data;
+}
+
+}  // namespace autofill
diff --git a/chrome/browser/autofill/manual_filling_utils.h b/chrome/browser/autofill/manual_filling_utils.h
new file mode 100644
index 0000000..1dbcf3d
--- /dev/null
+++ b/chrome/browser/autofill/manual_filling_utils.h
@@ -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.
+
+#ifndef CHROME_BROWSER_AUTOFILL_MANUAL_FILLING_UTILS_H_
+#define CHROME_BROWSER_AUTOFILL_MANUAL_FILLING_UTILS_H_
+
+#include "components/autofill/core/browser/accessory_sheet_data.h"
+
+namespace autofill {
+
+// Creates an AccessorySheetData defining the data to be shown in the filling
+// UI.
+AccessorySheetData CreateAccessorySheetData(base::string16 title,
+                                            std::vector<UserInfo> user_info);
+
+}  // namespace autofill
+
+#endif  // CHROME_BROWSER_AUTOFILL_MANUAL_FILLING_UTILS_H_
diff --git a/chrome/browser/autofill/mock_manual_filling_controller.h b/chrome/browser/autofill/mock_manual_filling_controller.h
index 98d84da..c7407ff9 100644
--- a/chrome/browser/autofill/mock_manual_filling_controller.h
+++ b/chrome/browser/autofill/mock_manual_filling_controller.h
@@ -24,8 +24,7 @@
   MOCK_METHOD1(Hide, void(ManualFillingController::FillingSource));
   MOCK_METHOD2(GetFavicon,
                void(int, base::OnceCallback<void(const gfx::Image&)>));
-  MOCK_METHOD2(OnFillingTriggered,
-               void(bool is_password, const base::string16&));
+  MOCK_METHOD1(OnFillingTriggered, void(const autofill::UserInfo::Field&));
   MOCK_CONST_METHOD1(OnOptionSelected, void(const base::string16&));
   MOCK_METHOD0(OnGenerationRequested, void());
   MOCK_CONST_METHOD0(container_view, gfx::NativeView());
diff --git a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc
index f3f9e679..376b6ee5 100644
--- a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc
+++ b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc
@@ -1100,8 +1100,7 @@
 
   void OnRequest(RequestDetails details) override { NOTREACHED(); }
 
-  void QueueSignedExchangeReport(
-      const SignedExchangeReportDetails& details) override {
+  void QueueSignedExchangeReport(SignedExchangeReportDetails details) override {
     NOTREACHED();
   }
 
diff --git a/chrome/browser/chromeos/app_mode/app_session.cc b/chrome/browser/chromeos/app_mode/app_session.cc
index 15e1adda..5f37e50a 100644
--- a/chrome/browser/chromeos/app_mode/app_session.cc
+++ b/chrome/browser/chromeos/app_mode/app_session.cc
@@ -29,10 +29,9 @@
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/common/pref_names.h"
-#include "chromeos/dbus/dbus_thread_manager.h"
-#include "chromeos/dbus/power/power_manager_client.h"
 #include "chromeos/network/network_state.h"
 #include "chromeos/network/network_state_handler.h"
+#include "chromeos/services/power/public/cpp/power_manager_mojo_client.h"
 #include "components/prefs/pref_service.h"
 #include "components/user_manager/user_manager.h"
 #include "content/public/browser/browser_child_process_host_iterator.h"
@@ -46,7 +45,6 @@
 #include "extensions/browser/app_window/app_window.h"
 #include "extensions/browser/app_window/app_window_registry.h"
 #include "extensions/browser/guest_view/web_view/web_view_guest.h"
-#include "third_party/cros_system_api/dbus/service_constants.h"
 
 using extensions::AppWindow;
 using extensions::AppWindowRegistry;
@@ -63,7 +61,7 @@
 }
 
 void RebootDevice() {
-  PowerManagerClient::Get()->RequestRestart(
+  PowerManagerMojoClient::Get()->RequestRestart(
       power_manager::REQUEST_RESTART_OTHER, "kiosk app session");
 }
 
diff --git a/chrome/browser/chromeos/app_mode/kiosk_mode_idle_app_name_notification.cc b/chrome/browser/chromeos/app_mode/kiosk_mode_idle_app_name_notification.cc
index ff676377..a6738928 100644
--- a/chrome/browser/chromeos/app_mode/kiosk_mode_idle_app_name_notification.cc
+++ b/chrome/browser/chromeos/app_mode/kiosk_mode_idle_app_name_notification.cc
@@ -12,6 +12,7 @@
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/common/chrome_switches.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
+#include "chromeos/services/power/public/cpp/power_manager_mojo_client.h"
 #include "components/user_manager/user_manager.h"
 #include "extensions/browser/extension_system.h"
 #include "ui/base/user_activity/user_activity_detector.h"
@@ -63,7 +64,7 @@
   if (user_activity_detector && user_activity_detector->HasObserver(this))
     user_activity_detector->RemoveObserver(this);
 
-  auto* power_manager = chromeos::PowerManagerClient::Get();
+  auto* power_manager = chromeos::PowerManagerMojoClient::Get();
   if (power_manager && power_manager->HasObserver(this))
     power_manager->RemoveObserver(this);
 }
@@ -109,7 +110,7 @@
 void KioskModeIdleAppNameNotification::Start() {
   if (!ui::UserActivityDetector::Get()->HasObserver(this)) {
     ui::UserActivityDetector::Get()->AddObserver(this);
-    chromeos::PowerManagerClient::Get()->AddObserver(this);
+    chromeos::PowerManagerMojoClient::Get()->AddObserver(this);
   }
   ResetTimer();
 }
diff --git a/chrome/browser/chromeos/fileapi/external_file_url_util.cc b/chrome/browser/chromeos/fileapi/external_file_url_util.cc
index d2354cd..822c3f70 100644
--- a/chrome/browser/chromeos/fileapi/external_file_url_util.cc
+++ b/chrome/browser/chromeos/fileapi/external_file_url_util.cc
@@ -28,7 +28,8 @@
   return type == storage::kFileSystemTypeDrive ||
          type == storage::kFileSystemTypeDeviceMediaAsFileStorage ||
          type == storage::kFileSystemTypeProvided ||
-         type == storage::kFileSystemTypeArcContent || force;
+         type == storage::kFileSystemTypeArcContent ||
+         type == storage::kFileSystemTypeArcDocumentsProvider || force;
 }
 
 GURL FileSystemURLToExternalFileURL(
diff --git a/chrome/browser/chromeos/kiosk_next_home/intent_config_helper.cc b/chrome/browser/chromeos/kiosk_next_home/intent_config_helper.cc
index 0fc9a1a..b16d86b 100644
--- a/chrome/browser/chromeos/kiosk_next_home/intent_config_helper.cc
+++ b/chrome/browser/chromeos/kiosk_next_home/intent_config_helper.cc
@@ -83,8 +83,7 @@
     : ready_(false) {
   base::PostTaskWithTraitsAndReplyWithResult(
       FROM_HERE,
-      {base::MayBlock(), base::TaskPriority::USER_VISIBLE,
-       base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
+      {base::MayBlock(), base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
       base::BindOnce(
           [](std::unique_ptr<Delegate> read_config_delegate) {
             return read_config_delegate->GetJsonConfig();
diff --git a/chrome/browser/chromeos/policy/device_policy_decoder_chromeos.cc b/chrome/browser/chromeos/policy/device_policy_decoder_chromeos.cc
index cca7279..765dfc69 100644
--- a/chrome/browser/chromeos/policy/device_policy_decoder_chromeos.cc
+++ b/chrome/browser/chromeos/policy/device_policy_decoder_chromeos.cc
@@ -723,6 +723,14 @@
       SetJsonDevicePolicy(key::kDeviceUpdateStagingSchedule,
                           container.staging_schedule(), policies);
     }
+
+    if (container.has_device_quick_fix_build_token()) {
+      policies->Set(key::kDeviceQuickFixBuildToken, POLICY_LEVEL_MANDATORY,
+                    POLICY_SCOPE_MACHINE, POLICY_SOURCE_CLOUD,
+                    std::make_unique<base::Value>(
+                        container.device_quick_fix_build_token()),
+                    nullptr);
+    }
   }
 
   if (policy.has_allow_kiosk_app_control_chrome_version()) {
diff --git a/chrome/browser/chromeos/power/extension_event_observer.cc b/chrome/browser/chromeos/power/extension_event_observer.cc
index be2aa07..09a1e21 100644
--- a/chrome/browser/chromeos/power/extension_event_observer.cc
+++ b/chrome/browser/chromeos/power/extension_event_observer.cc
@@ -63,7 +63,6 @@
 
 ExtensionEventObserver::ExtensionEventObserver()
     : should_delay_suspend_(true),
-      suspend_is_pending_(false),
       suspend_keepalive_count_(0),
       weak_factory_(this) {
   registrar_.Add(this, chrome::NOTIFICATION_PROFILE_ADDED,
@@ -95,11 +94,11 @@
 
 void ExtensionEventObserver::SetShouldDelaySuspend(bool should_delay) {
   should_delay_suspend_ = should_delay;
-  if (!should_delay_suspend_ && suspend_is_pending_) {
+  if (!should_delay_suspend_ && block_suspend_token_) {
     // There is a suspend attempt pending but this class should no longer be
     // delaying it.  Immediately report readiness.
-    suspend_is_pending_ = false;
-    std::move(power_manager_callback_).Run();
+    PowerManagerClient::Get()->UnblockSuspend(block_suspend_token_);
+    block_suspend_token_ = {};
     suspend_readiness_callback_.Cancel();
   }
 }
@@ -216,8 +215,7 @@
 }
 
 void ExtensionEventObserver::SuspendDone(const base::TimeDelta& duration) {
-  suspend_is_pending_ = false;
-  power_manager_callback_.Reset();
+  block_suspend_token_ = {};
   suspend_readiness_callback_.Cancel();
 }
 
@@ -236,14 +234,13 @@
 }
 
 void ExtensionEventObserver::OnSuspendImminent(bool dark_suspend) {
-  if (suspend_is_pending_) {
-    LOG(WARNING) << "OnSuspendImminent called while previous suspend attempt "
-                 << "is still pending.";
-  }
+  DCHECK(block_suspend_token_.is_empty())
+      << "OnSuspendImminent called while previous suspend attempt "
+      << "is still pending.";
 
-  suspend_is_pending_ = true;
-  power_manager_callback_ =
-      PowerManagerClient::Get()->GetSuspendReadinessCallback(FROM_HERE);
+  block_suspend_token_ = base::UnguessableToken::Create();
+  PowerManagerClient::Get()->BlockSuspend(block_suspend_token_,
+                                          "ExtensionEventObserver");
 
   suspend_readiness_callback_.Reset(
       base::Bind(&ExtensionEventObserver::MaybeReportSuspendReadiness,
@@ -263,12 +260,11 @@
 }
 
 void ExtensionEventObserver::MaybeReportSuspendReadiness() {
-  if (!suspend_is_pending_ || suspend_keepalive_count_ > 0 ||
-      power_manager_callback_.is_null())
+  if (suspend_keepalive_count_ > 0 || block_suspend_token_.is_empty())
     return;
 
-  suspend_is_pending_ = false;
-  std::move(power_manager_callback_).Run();
+  PowerManagerClient::Get()->UnblockSuspend(block_suspend_token_);
+  block_suspend_token_ = {};
 }
 
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/power/extension_event_observer.h b/chrome/browser/chromeos/power/extension_event_observer.h
index d4fe0272..f3de4a1f 100644
--- a/chrome/browser/chromeos/power/extension_event_observer.h
+++ b/chrome/browser/chromeos/power/extension_event_observer.h
@@ -17,6 +17,7 @@
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "base/time/time.h"
+#include "base/unguessable_token.h"
 #include "chromeos/dbus/power/power_manager_client.h"
 #include "content/public/browser/notification_details.h"
 #include "content/public/browser/notification_observer.h"
@@ -122,9 +123,12 @@
   std::set<Profile*> active_profiles_;
 
   bool should_delay_suspend_;
-  bool suspend_is_pending_;
   int suspend_keepalive_count_;
-  base::OnceClosure power_manager_callback_;
+
+  // |this| blocks Power Manager suspend with this token. When the token is
+  // empty, |this| isn't blocking suspend.
+  base::UnguessableToken block_suspend_token_;
+
   base::CancelableClosure suspend_readiness_callback_;
 
   content::NotificationRegistrar registrar_;
diff --git a/chrome/browser/extensions/api/tabs/tabs_test.cc b/chrome/browser/extensions/api/tabs/tabs_test.cc
index 5843368e0..7c3059a 100644
--- a/chrome/browser/extensions/api/tabs/tabs_test.cc
+++ b/chrome/browser/extensions/api/tabs/tabs_test.cc
@@ -946,8 +946,9 @@
   CloseAppWindow(app_window);
 }
 
+// https://crbug.com/956870
 IN_PROC_BROWSER_TEST_F(ExtensionWindowLastFocusedTest,
-                       NoTabIdForDevToolsAndAppWindows) {
+                       DISABLED_NoTabIdForDevToolsAndAppWindows) {
   Browser* normal_browser = CreateBrowserWithEmptyTab(false);
   {
     ActivateBrowserWindow(normal_browser);
diff --git a/chrome/browser/media_galleries/win/mtp_device_delegate_impl_win.cc b/chrome/browser/media_galleries/win/mtp_device_delegate_impl_win.cc
index 7ee710e..945a7eb 100644
--- a/chrome/browser/media_galleries/win/mtp_device_delegate_impl_win.cc
+++ b/chrome/browser/media_galleries/win/mtp_device_delegate_impl_win.cc
@@ -320,7 +320,7 @@
   DCHECK(!device_location.empty());
   base::string16* pnp_device_id = new base::string16;
   base::string16* storage_object_id = new base::string16;
-  base::PostTaskWithTraitsAndReplyWithResult<bool>(
+  base::PostTaskWithTraitsAndReplyWithResult(
       FROM_HERE, {content::BrowserThread::UI},
       base::Bind(&GetStorageInfoOnUIThread, device_location,
                  base::Unretained(pnp_device_id),
diff --git a/chrome/browser/notifications/OWNERS b/chrome/browser/notifications/OWNERS
index 82f60d8f..02152be 100644
--- a/chrome/browser/notifications/OWNERS
+++ b/chrome/browser/notifications/OWNERS
@@ -1,5 +1,6 @@
 dewittj@chromium.org
 dimich@chromium.org
+knollr@chromium.org
 mukai@chromium.org
 peter@chromium.org
 stevenjb@chromium.org
diff --git a/chrome/browser/password_manager/password_accessory_controller.h b/chrome/browser/password_manager/password_accessory_controller.h
index c17752a..29792ff 100644
--- a/chrome/browser/password_manager/password_accessory_controller.h
+++ b/chrome/browser/password_manager/password_accessory_controller.h
@@ -7,11 +7,13 @@
 
 #include <map>
 #include <memory>
+#include <utility>
 
 #include "base/callback_forward.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "base/strings/string16.h"
+#include "chrome/browser/autofill/accessory_controller.h"
 #include "components/autofill/core/common/filling_status.h"
 #include "components/autofill/core/common/password_generation_util.h"
 #include "content/public/browser/web_contents_user_data.h"
@@ -30,10 +32,11 @@
 // On the first call, an instance is attached to |web_contents|, so it can be
 // returned by subsequent calls.
 class PasswordAccessoryController
-    : public base::SupportsWeakPtr<PasswordAccessoryController> {
+    : public base::SupportsWeakPtr<PasswordAccessoryController>,
+      public AccessoryController {
  public:
   PasswordAccessoryController() = default;
-  virtual ~PasswordAccessoryController() = default;
+  ~PasswordAccessoryController() override = default;
 
   // Returns true if the accessory controller may exist for |web_contents|.
   // Otherwise (e.g. if VR is enabled), it returns false.
@@ -88,11 +91,6 @@
       int desired_size_in_pixel,
       base::OnceCallback<void(const gfx::Image&)> icon_callback) = 0;
 
-  // Called by the UI code to request that |text_to_fill| is to be filled into
-  // the currently focused field.
-  virtual void OnFillingTriggered(bool is_password,
-                                  const base::string16& text_to_fill) = 0;
-
   // Called by the UI code because a user triggered the |selected_option|,
   // such as "Manage passwords..."
   // TODO(crbug.com/905669): Replace the string param with an enum to indicate
diff --git a/chrome/browser/password_manager/password_accessory_controller_impl.cc b/chrome/browser/password_manager/password_accessory_controller_impl.cc
index b793a9d..d312374c 100644
--- a/chrome/browser/password_manager/password_accessory_controller_impl.cc
+++ b/chrome/browser/password_manager/password_accessory_controller_impl.cc
@@ -12,6 +12,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/android/preferences/preferences_launcher.h"
 #include "chrome/browser/autofill/manual_filling_controller.h"
+#include "chrome/browser/autofill/manual_filling_utils.h"
 #include "chrome/browser/favicon/favicon_service_factory.h"
 #include "chrome/browser/password_manager/password_accessory_metrics_util.h"
 #include "chrome/browser/profiles/profile.h"
@@ -26,6 +27,7 @@
 #include "components/password_manager/content/browser/content_password_manager_driver_factory.h"
 #include "components/password_manager/core/browser/password_manager_driver.h"
 #include "components/password_manager/core/common/password_manager_features.h"
+#include "components/url_formatter/elide_url.h"
 #include "content/public/browser/web_contents.h"
 #include "ui/base/l10n/l10n_util.h"
 
@@ -35,8 +37,64 @@
 using autofill::UserInfo;
 using FillingSource = ManualFillingController::FillingSource;
 
+namespace {
+
+autofill::UserInfo Translate(bool current_field_is_password,
+                             const PasswordAccessorySuggestion& data) {
+  UserInfo user_info;
+
+  user_info.add_field(UserInfo::Field(
+      data.username, data.username, /*is_password=*/false,
+      /*selectable=*/data.username_selectable && !current_field_is_password));
+
+  user_info.add_field(UserInfo::Field(
+      data.password,
+      l10n_util::GetStringFUTF16(
+          IDS_PASSWORD_MANAGER_ACCESSORY_PASSWORD_DESCRIPTION, data.username),
+      /*is_password=*/true, /*selectable=*/current_field_is_password));
+
+  return user_info;
+}
+
+base::string16 GetTitle(bool has_suggestions, const url::Origin& origin) {
+  const base::string16 elided_url =
+      url_formatter::FormatOriginForSecurityDisplay(
+          origin, url_formatter::SchemeDisplay::OMIT_CRYPTOGRAPHIC);
+  return l10n_util::GetStringFUTF16(
+      has_suggestions
+          ? IDS_PASSWORD_MANAGER_ACCESSORY_PASSWORD_LIST_TITLE
+          : IDS_PASSWORD_MANAGER_ACCESSORY_PASSWORD_LIST_EMPTY_MESSAGE,
+      elided_url);
+}
+
+}  // namespace
+
 PasswordAccessoryControllerImpl::~PasswordAccessoryControllerImpl() = default;
 
+void PasswordAccessoryControllerImpl::OnFillingTriggered(
+    const autofill::UserInfo::Field& selection) {
+  if (!AppearsInSuggestions(selection.display_text(), selection.is_obfuscated(),
+                            GetFocusedFrameOrigin())) {
+    NOTREACHED() << "Tried to fill '" << selection.display_text() << "' into "
+                 << GetFocusedFrameOrigin();
+    return;  // Never fill across different origins!
+  }
+
+  password_manager::ContentPasswordManagerDriverFactory* factory =
+      password_manager::ContentPasswordManagerDriverFactory::FromWebContents(
+          web_contents_);
+  DCHECK(factory);
+  password_manager::ContentPasswordManagerDriver* driver =
+      factory->GetDriverForFrame(web_contents_->GetFocusedFrame());
+  if (!driver) {
+    return;
+  }  // |driver| can be NULL if the tab is being closed.
+  driver->FillIntoFocusedField(
+      selection.is_obfuscated(), selection.display_text(),
+      base::BindOnce(&PasswordAccessoryControllerImpl::OnFilledIntoFocusedField,
+                     base::AsWeakPtr<PasswordAccessoryControllerImpl>(this)));
+}
+
 // static
 bool PasswordAccessoryController::AllowedForWebContents(
     content::WebContents* web_contents) {
@@ -63,24 +121,6 @@
   return PasswordAccessoryControllerImpl::FromWebContents(web_contents);
 }
 
-struct PasswordAccessoryControllerImpl::SuggestionElementData {
-  SuggestionElementData(base::string16 password,
-                        base::string16 username,
-                        bool username_selectable)
-      : password(password),
-        username(username),
-        username_selectable(username_selectable) {}
-
-  // Password string to be used for this credential.
-  base::string16 password;
-
-  // Username string to be used for this credential.
-  base::string16 username;
-
-  // Decides whether the username is interactive (i.e. empty ones are not).
-  bool username_selectable;
-};
-
 struct PasswordAccessoryControllerImpl::FaviconRequestData {
   // List of requests waiting for favicons to be available.
   std::vector<base::OnceCallback<void(const gfx::Image&)>> pending_requests;
@@ -107,7 +147,7 @@
 void PasswordAccessoryControllerImpl::SavePasswordsForOrigin(
     const std::map<base::string16, const PasswordForm*>& best_matches,
     const url::Origin& origin) {
-  std::vector<SuggestionElementData>* suggestions =
+  std::vector<PasswordAccessorySuggestion>* suggestions =
       &origin_suggestions_[origin];
   suggestions->clear();
   for (const auto& pair : best_matches) {
@@ -142,13 +182,25 @@
     const url::Origin& origin,
     bool is_fillable,
     bool is_password_field) {
-  current_origin_ = is_fillable ? origin : url::Origin();
+  std::vector<UserInfo> info_to_add;
+
+  if (is_fillable) {
+    const std::vector<PasswordAccessorySuggestion> suggestions =
+        GetSuggestions();
+    info_to_add.resize(suggestions.size());
+    std::transform(suggestions.begin(), suggestions.end(), info_to_add.begin(),
+                   [is_password_field](const auto& x) {
+                     return Translate(is_password_field, x);
+                   });
+  }
+
+  bool has_suggestions = !info_to_add.empty();
+
   GetManualFillingController()->RefreshSuggestionsForField(
-      is_fillable, CreateAccessorySheetData(
-                       origin,
-                       is_fillable ? origin_suggestions_[origin]
-                                   : std::vector<SuggestionElementData>(),
-                       is_password_field));
+      is_fillable, autofill::CreateAccessorySheetData(
+                       GetTitle(has_suggestions, GetFocusedFrameOrigin()),
+                       std::move(info_to_add)));
+
   if (is_password_field) {
     GetManualFillingController()->ShowWhenKeyboardIsVisible(
         FillingSource::PASSWORD_FALLBACKS);
@@ -159,7 +211,6 @@
 
 void PasswordAccessoryControllerImpl::DidNavigateMainFrame() {
   favicon_tracker_.TryCancelAll();  // If there is a request pending, cancel it.
-  current_origin_ = url::Origin();
   icons_request_data_.clear();
   origin_suggestions_.clear();
 }
@@ -167,7 +218,8 @@
 void PasswordAccessoryControllerImpl::GetFavicon(
     int desired_size_in_pixel,
     base::OnceCallback<void(const gfx::Image&)> icon_callback) {
-  url::Origin origin = current_origin_;  // Copy origin in case it changes.
+  url::Origin origin =
+      GetFocusedFrameOrigin();  // Copy origin in case it changes.
   // Check whether this request can be immediately answered with a cached icon.
   // It is empty if there wasn't at least one request that found an icon yet.
   FaviconRequestData* icon_request = &icons_request_data_[origin];
@@ -198,32 +250,6 @@
       &favicon_tracker_);
 }
 
-void PasswordAccessoryControllerImpl::OnFillingTriggered(
-    bool is_password,
-    const base::string16& text_to_fill) {
-  content::RenderFrameHost* target = web_contents_->GetFocusedFrame();
-
-  const url::Origin& origin = target->GetLastCommittedOrigin();
-  if (!AppearsInSuggestions(text_to_fill, is_password, origin)) {
-    NOTREACHED() << "Tried to fill '" << text_to_fill << "' into " << origin;
-    return;  // Never fill across different origins!
-  }
-
-  password_manager::ContentPasswordManagerDriverFactory* factory =
-      password_manager::ContentPasswordManagerDriverFactory::FromWebContents(
-          web_contents_);
-  DCHECK(factory);
-  password_manager::ContentPasswordManagerDriver* driver =
-      factory->GetDriverForFrame(target);
-  if (!driver) {
-    return;
-  }  // |driver| can be NULL if the tab is being closed.
-  driver->FillIntoFocusedField(
-      is_password, text_to_fill,
-      base::BindOnce(&PasswordAccessoryControllerImpl::OnFilledIntoFocusedField,
-                     base::AsWeakPtr<PasswordAccessoryControllerImpl>(this)));
-}
-
 PasswordAccessoryControllerImpl::PasswordAccessoryControllerImpl(
     content::WebContents* web_contents)
     : web_contents_(web_contents),
@@ -241,46 +267,6 @@
       mf_controller_(std::move(mf_controller)),
       favicon_service_(favicon_service) {}
 
-// static
-AccessorySheetData PasswordAccessoryControllerImpl::CreateAccessorySheetData(
-    const url::Origin& origin,
-    const std::vector<SuggestionElementData>& suggestions,
-    bool is_password_field) {
-  // Create the title element
-  base::string16 passwords_title_str = l10n_util::GetStringFUTF16(
-      suggestions.empty()
-          ? IDS_PASSWORD_MANAGER_ACCESSORY_PASSWORD_LIST_EMPTY_MESSAGE
-          : IDS_PASSWORD_MANAGER_ACCESSORY_PASSWORD_LIST_TITLE,
-      base::ASCIIToUTF16(origin.host()));
-  AccessorySheetData data(autofill::FallbackSheetType::PASSWORD,
-                          passwords_title_str);
-
-  // Create a username and a password element for every suggestion.
-  for (const SuggestionElementData& suggestion : suggestions) {
-    UserInfo user_info;
-
-    user_info.add_field(UserInfo::Field(
-        suggestion.username, suggestion.username, /*is_password=*/false,
-        /*selectable=*/suggestion.username_selectable && !is_password_field));
-
-    user_info.add_field(UserInfo::Field(
-        suggestion.password,
-        l10n_util::GetStringFUTF16(
-            IDS_PASSWORD_MANAGER_ACCESSORY_PASSWORD_DESCRIPTION,
-            suggestion.username),
-        /*is_password=*/true, /*selectable=*/is_password_field));
-
-    data.add_user_info(std::move(user_info));
-  }
-
-  // Create the link to all passwords.
-  base::string16 manage_passwords_title = l10n_util::GetStringUTF16(
-      IDS_PASSWORD_MANAGER_ACCESSORY_ALL_PASSWORDS_LINK);
-  data.add_footer_command(FooterCommand(manage_passwords_title));
-
-  return data;
-}
-
 void PasswordAccessoryControllerImpl::OnImageFetched(
     url::Origin origin,
     const favicon_base::FaviconRawBitmapResult& bitmap_result) {
@@ -293,7 +279,7 @@
   }
   icon_request->cached_icon = image_result.image;
   // Only trigger all the callbacks if they still affect a displayed origin.
-  if (origin == current_origin_) {
+  if (origin == GetFocusedFrameOrigin()) {
     for (auto& callback : icon_request->pending_requests) {
       std::move(callback).Run(icon_request->cached_icon);
     }
@@ -305,7 +291,8 @@
     const base::string16& suggestion,
     bool is_password,
     const url::Origin& origin) const {
-  for (const SuggestionElementData& element : origin_suggestions_.at(origin)) {
+  for (const PasswordAccessorySuggestion& element :
+       origin_suggestions_.at(origin)) {
     const base::string16& candidate =
         is_password ? element.password : element.username;
     if (candidate == suggestion)
@@ -322,4 +309,16 @@
   return mf_controller_;
 }
 
+url::Origin PasswordAccessoryControllerImpl::GetFocusedFrameOrigin() const {
+  return web_contents_->GetFocusedFrame()->GetLastCommittedOrigin();
+}
+
+std::vector<PasswordAccessorySuggestion>
+PasswordAccessoryControllerImpl::GetSuggestions() const {
+  auto it = origin_suggestions_.find(GetFocusedFrameOrigin());
+  return it == origin_suggestions_.end()
+             ? std::vector<PasswordAccessorySuggestion>()
+             : it->second;
+}
+
 WEB_CONTENTS_USER_DATA_KEY_IMPL(PasswordAccessoryControllerImpl)
diff --git a/chrome/browser/password_manager/password_accessory_controller_impl.h b/chrome/browser/password_manager/password_accessory_controller_impl.h
index 2c3bd8572..0db305a 100644
--- a/chrome/browser/password_manager/password_accessory_controller_impl.h
+++ b/chrome/browser/password_manager/password_accessory_controller_impl.h
@@ -30,6 +30,24 @@
 
 class ManualFillingController;
 
+// Encapsulates the data needed from the password manager backend to
+// eventually render a username/password in the manual filling frontend.
+// TODO(crbug.com/941654): It's probably cleanest to use PasswordForm directly
+// instead of PasswordAccessorySuggestion as the type for the
+// AccessoryController.
+struct PasswordAccessorySuggestion {
+  PasswordAccessorySuggestion(base::string16 password,
+                              base::string16 username,
+                              bool username_selectable)
+      : password(std::move(password)),
+        username(std::move(username)),
+        username_selectable(username_selectable) {}
+
+  base::string16 password;
+  base::string16 username;
+  bool username_selectable;
+};
+
 // Use either PasswordAccessoryController::GetOrCreate or
 // PasswordAccessoryController::GetIfExisting to obtain instances of this class.
 // This class exists for every tab and should never store state based on the
@@ -40,6 +58,9 @@
  public:
   ~PasswordAccessoryControllerImpl() override;
 
+  // AccessoryController:
+  void OnFillingTriggered(const autofill::UserInfo::Field& selection) override;
+
   // PasswordAccessoryController:
   void SavePasswordsForOrigin(
       const std::map<base::string16, const autofill::PasswordForm*>&
@@ -53,8 +74,6 @@
   void GetFavicon(
       int desired_size_in_pixel,
       base::OnceCallback<void(const gfx::Image&)> icon_callback) override;
-  void OnFillingTriggered(bool is_password,
-                          const base::string16& text_to_fill) override;
   void OnOptionSelected(const base::string16& selected_option) const override;
 
   // Like |CreateForWebContents|, it creates the controller and attaches it to
@@ -66,9 +85,6 @@
       favicon::FaviconService* favicon_service);
 
  private:
-  // Data for a credential pair that is transformed into a suggestion.
-  struct SuggestionElementData;
-
   // Data allowing to cache favicons and favicon-related requests.
   struct FaviconRequestData;
 
@@ -83,13 +99,6 @@
       base::WeakPtr<ManualFillingController> mf_controller,
       favicon::FaviconService* favicon_service);
 
-  // Creates the view items based on the given |suggestions|.
-  // If |is_password_field| is false, password suggestions won't be interactive.
-  static autofill::AccessorySheetData CreateAccessorySheetData(
-      const url::Origin& origin,
-      const std::vector<SuggestionElementData>& suggestions,
-      bool is_password_field);
-
   // Handles a favicon response requested by |GetFavicon| and responds to
   // pending favicon requests with a (possibly empty) icon bitmap.
   void OnImageFetched(
@@ -105,21 +114,20 @@
   // |web_contents_|. The lazy initialization allows injecting mocks for tests.
   base::WeakPtr<ManualFillingController> GetManualFillingController();
 
+  url::Origin GetFocusedFrameOrigin() const;
+  std::vector<PasswordAccessorySuggestion> GetSuggestions() const;
+
   // ------------------------------------------------------------------------
   // Members - Make sure to NEVER store state related to a single frame here!
   // ------------------------------------------------------------------------
 
   // Contains the last set of credentials by origin.
-  std::map<url::Origin, std::vector<SuggestionElementData>> origin_suggestions_;
+  std::map<url::Origin, std::vector<PasswordAccessorySuggestion>>
+      origin_suggestions_;
 
   // The tab for which this class is scoped.
   content::WebContents* web_contents_;
 
-  // TODO(fhorschig): Make sure this works across frames
-  // The origin of the currently focused frame. It's used to ensure that
-  // favicons are not displayed across origins.
-  url::Origin current_origin_;
-
   // TODO(fhorschig): Find a way to use unordered_map with origin keys.
   // A cache for all favicons that were requested. This includes all iframes
   // for which the accessory was displayed.
diff --git a/chrome/browser/password_manager/password_accessory_controller_impl_unittest.cc b/chrome/browser/password_manager/password_accessory_controller_impl_unittest.cc
index c7055ece..c4c212a 100644
--- a/chrome/browser/password_manager/password_accessory_controller_impl_unittest.cc
+++ b/chrome/browser/password_manager/password_accessory_controller_impl_unittest.cc
@@ -205,6 +205,12 @@
   void SetUp() override {
     ChromeRenderViewHostTestHarness::SetUp();
     NavigateAndCommit(GURL(kExampleSite));
+    FocusWebContentsOnMainFrame();
+
+    ASSERT_TRUE(web_contents()->GetFocusedFrame());
+    ASSERT_EQ(url::Origin::Create(GURL(kExampleSite)),
+              web_contents()->GetFocusedFrame()->GetLastCommittedOrigin());
+
     PasswordAccessoryControllerImpl::CreateForWebContentsForTesting(
         web_contents(), mock_manual_filling_controller_.AsWeakPtr(),
         favicon_service());
@@ -663,12 +669,21 @@
   controller()->RefreshSuggestionsForField(
       url::Origin::Create(GURL(kExampleSite)), true, false);
 
-  // Right after starting the favicon request for example.com, another frame on
-  // the same site is focused. Even if the request is completed, the callback
-  // should not be called because the origin of the suggestions has changed.
+  // Right after starting the favicon request for example.com, a navigation
+  // changes the URL of the focused frame. Even if the request is completed,
+  // the callback should not be called because the origin of the suggestions
+  // has changed.
   EXPECT_CALL(*favicon_service(), GetRawFaviconForPageURL(GURL(kExampleSite), _,
                                                           kIconSize, _, _, _))
-      .WillOnce(favicon::PostReply<6>(favicon_base::FaviconRawBitmapResult()));
+      .WillOnce(testing::DoAll(
+          // Triggering a navigation at this moment ensures that the focused
+          // frame origin changes after the original origin has been sent to the
+          // favicon service, but before checking whether the origins match (and
+          // maybe invoking the callback).
+          testing::InvokeWithoutArgs([this]() {
+            this->NavigateAndCommit(GURL("https://other.frame.com/"));
+          }),
+          favicon::PostReply<6>(favicon_base::FaviconRawBitmapResult())));
   EXPECT_CALL(mock_callback, Run).Times(0);
   controller()->GetFavicon(kIconSize, mock_callback.Get());
   EXPECT_CALL(mock_manual_filling_controller_,
diff --git a/chrome/browser/previews/previews_lite_page_decider.cc b/chrome/browser/previews/previews_lite_page_decider.cc
index de12c60..036d3af6 100644
--- a/chrome/browser/previews/previews_lite_page_decider.cc
+++ b/chrome/browser/previews/previews_lite_page_decider.cc
@@ -50,7 +50,7 @@
 // Cleans up the given host blacklist by removing all stale (expiry has passed)
 // entries. If after removing all stale entries, the blacklist is still over
 // capacity, then remove the entry with the closest expiration.
-void RemoveStaleEntries(base::DictionaryValue* dict) {
+void RemoveStaleBlacklistEntries(base::DictionaryValue* dict) {
   std::vector<std::string> keys_to_delete;
 
   base::Time min_value = base::Time::Max();
@@ -405,7 +405,7 @@
   host_bypass_blacklist_->SetKey(
       host, base::Value((base::Time::Now() + duration).ToDoubleT()));
 
-  RemoveStaleEntries(host_bypass_blacklist_.get());
+  RemoveStaleBlacklistEntries(host_bypass_blacklist_.get());
   if (pref_service_)
     pref_service_->Set(kHostBlacklist, *host_bypass_blacklist_);
 }
diff --git a/chrome/browser/previews/previews_offline_helper.cc b/chrome/browser/previews/previews_offline_helper.cc
index d9cd2a21..977b2bd 100644
--- a/chrome/browser/previews/previews_offline_helper.cc
+++ b/chrome/browser/previews/previews_offline_helper.cc
@@ -51,7 +51,7 @@
 
 // Cleans up the given dictionary by removing all stale (expiry has passed)
 // entries.
-void RemoveStaleEntries(base::DictionaryValue* dict) {
+void RemoveStaleOfflinePageEntries(base::DictionaryValue* dict) {
   base::Time earliest_expiry = base::Time::Max();
   std::string earliest_key;
   std::vector<std::string> keys_to_delete;
@@ -85,8 +85,8 @@
   for (const std::string& key : keys_to_delete)
     dict->RemoveKey(key);
 
-  // RemoveStaleEntries is called for every new added page, so it's fine to just
-  // remove one at a time to keep the pref size below a threshold.
+  // RemoveStaleOfflinePageEntries is called for every new added page, so it's
+  // fine to just remove one at a time to keep the pref size below a threshold.
   if (dict->DictSize() > previews::params::OfflinePreviewsHelperMaxPrefSize()) {
     dict->RemoveKey(earliest_key);
   }
@@ -109,7 +109,7 @@
 
   // Tidy up the pref in case it's been a while since the last stale item
   // removal.
-  RemoveStaleEntries(available_pages_.get());
+  RemoveStaleOfflinePageEntries(available_pages_.get());
   UpdatePref();
 
 #if BUILDFLAG(ENABLE_OFFLINE_PAGES)
@@ -196,7 +196,7 @@
         base::Value(TimeToDictionaryValue(added_page.creation_time)));
   }
 
-  RemoveStaleEntries(available_pages_.get());
+  RemoveStaleOfflinePageEntries(available_pages_.get());
   UpdatePref();
 }
 
@@ -207,7 +207,7 @@
   // Has no effect if the url was never in the dictionary.
   available_pages_->RemoveKey(HashURL(page_info.url));
 
-  RemoveStaleEntries(available_pages_.get());
+  RemoveStaleOfflinePageEntries(available_pages_.get());
   UpdatePref();
 }
 
diff --git a/chrome/browser/resources/chromeos/camera/src/css/main.css b/chrome/browser/resources/chromeos/camera/src/css/main.css
index b852569d..0b2128c 100644
--- a/chrome/browser/resources/chromeos/camera/src/css/main.css
+++ b/chrome/browser/resources/chromeos/camera/src/css/main.css
@@ -230,18 +230,20 @@
 }
 
 .mode-item {
-  margin: 4px 0;
+  flex: 0 0 48px;
   position: relative;
 }
 
-label.mode-item>span {
+div.mode-item>span {
   border-radius: 13px / 50%;
   color: white;
   display: inline-block;
   font-family: 'Roboto', sans-serif;
   font-size: 14px;
   font-weight: 500;
-  padding: 8px 12px;
+  line-height: 32px;
+  margin: 8px 0;
+  padding: 0 12px;
   text-shadow: 2px 3px 3px rgba(32, 33, 36, 0.3);
 }
 
@@ -492,7 +494,7 @@
 #camera {
   --big-icon: 48px;
   --bottom-line: 56px;
-  --modes-height: 130px;
+  --modes-height: 140px;
   --modes-bottom: calc((var(--bottom-line) + (var(--big-icon) / 2)) + 24px);
   --small-icon: 40px;
 }
diff --git a/chrome/browser/resources/chromeos/camera/src/views/main.html b/chrome/browser/resources/chromeos/camera/src/views/main.html
index 63b6508b..8c36c92 100644
--- a/chrome/browser/resources/chromeos/camera/src/views/main.html
+++ b/chrome/browser/resources/chromeos/camera/src/views/main.html
@@ -57,34 +57,34 @@
         <div class="centered-overlay" id="camera-mode"></div>
       </div>
       <div id="modes-group" class="buttons right-stripe">
-        <label class="mode-item">
+        <div class="mode-item">
           <input type="radio" name="mode"
                data-mode="video-mode" tabindex="0"
                i18n-aria="switch_record_video_button">
           <span i18n-content="label_switch_record_video_button"
                 aria-hidden="true"></span>
-        </label>
-        <label class="mode-item">
+        </div>
+        <div class="mode-item">
           <input type="radio" name="mode"
                data-mode="photo-mode" tabindex="0"
                i18n-aria="switch_take_photo_button">
           <span i18n-content="label_switch_take_photo_button"
                 aria-hidden="true"></span>
-        </label>
-        <label class="mode-item">
+        </div>
+        <div class="mode-item">
           <input type="radio" name="mode"
                data-mode="square-mode" tabindex="0"
                i18n-aria="switch_take_square_photo_button">
           <span i18n-content="label_switch_take_square_photo_button"
                 aria-hidden="true"></span>
-        </label>
-        <label class="mode-item">
+        </div>
+        <div class="mode-item">
           <input type="radio" name="mode"
                data-mode="portrait-mode" tabindex="0"
                i18n-aria="switch_take_portrait_photo_button">
           <span i18n-content="label_switch_take_portrait_photo_button"
                 aria-hidden="true"></span>
-        </label>
+        </div>
       </div>
       <div id="shutters-group" class="buttons right-stripe circle">
         <button id="start-recordvideo" class="shutter" tabindex="0"
diff --git a/chrome/browser/sync/test/integration/two_client_apps_sync_test.cc b/chrome/browser/sync/test/integration/two_client_apps_sync_test.cc
index 31cbce9..6c9dbd73 100644
--- a/chrome/browser/sync/test/integration/two_client_apps_sync_test.cc
+++ b/chrome/browser/sync/test/integration/two_client_apps_sync_test.cc
@@ -67,7 +67,8 @@
 
   ~TwoClientAppsSyncTest() override {}
 
-  bool TestUsesSelfNotifications() override { return false; }
+  // Needed for AwaitQuiescence().
+  bool TestUsesSelfNotifications() override { return true; }
 
  private:
   DISALLOW_COPY_AND_ASSIGN(TwoClientAppsSyncTest);
@@ -311,6 +312,8 @@
 // have the same launch type values for the CWS.
 IN_PROC_BROWSER_TEST_P(TwoClientAppsSyncTest, E2E_ENABLED(UpdateLaunchType)) {
   ASSERT_TRUE(SetupSync());
+  // Wait until sync settles before we override the apps below.
+  ASSERT_TRUE(AwaitQuiescence());
   ASSERT_TRUE(AppsMatchChecker().Wait());
 
   // Change the launch type to window.
@@ -337,6 +340,8 @@
 
 IN_PROC_BROWSER_TEST_P(TwoClientAppsSyncTest, UnexpectedLaunchType) {
   ASSERT_TRUE(SetupSync());
+  // Wait until sync settles before we override the apps below.
+  ASSERT_TRUE(AwaitQuiescence());
   ASSERT_TRUE(AllProfilesHaveSameApps());
 
   extensions::SetLaunchType(GetProfile(1), extensions::kWebStoreAppId,
diff --git a/chrome/browser/sync/test/integration/two_client_custom_passphrase_sync_test.cc b/chrome/browser/sync/test/integration/two_client_custom_passphrase_sync_test.cc
index be2b2f2..28628ed 100644
--- a/chrome/browser/sync/test/integration/two_client_custom_passphrase_sync_test.cc
+++ b/chrome/browser/sync/test/integration/two_client_custom_passphrase_sync_test.cc
@@ -8,6 +8,7 @@
 #include "chrome/browser/sync/test/integration/profile_sync_service_harness.h"
 #include "chrome/browser/sync/test/integration/sync_integration_test_util.h"
 #include "chrome/browser/sync/test/integration/sync_test.h"
+#include "chrome/browser/sync/test/integration/updated_progress_marker_checker.h"
 #include "components/sync/base/sync_base_switches.h"
 #include "components/sync/engine/sync_engine_switches.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -91,16 +92,8 @@
   EXPECT_TRUE(WaitForBookmarksToMatchVerifier());
 }
 
-#if defined(OS_CHROMEOS)
-// https://crbug.com/956012
-#define MAYBE_SetPassphraseAndThenSetupSync \
-  DISABLED_SetPassphraseAndThenSetupSync
-#else
-#define MAYBE_SetPassphraseAndThenSetupSync SetPassphraseAndThenSetupSync
-#endif
-
 IN_PROC_BROWSER_TEST_F(TwoClientCustomPassphraseSyncTest,
-                       MAYBE_SetPassphraseAndThenSetupSync) {
+                       SetPassphraseAndThenSetupSync) {
   ASSERT_TRUE(SetupClients());
   ASSERT_TRUE(GetClient(kEncryptingClientId)->SetupSync());
 
@@ -111,6 +104,9 @@
   ASSERT_TRUE(
       PassphraseAcceptedChecker(GetSyncService(kEncryptingClientId)).Wait());
   AddTestBookmarksToClient(kEncryptingClientId);
+  // Wait for the client to commit the update.
+  ASSERT_TRUE(
+      UpdatedProgressMarkerChecker(GetSyncService(kEncryptingClientId)).Wait());
 
   // Set up a new sync client.
   ASSERT_TRUE(
diff --git a/chrome/browser/sync/test/integration/two_client_passwords_sync_test.cc b/chrome/browser/sync/test/integration/two_client_passwords_sync_test.cc
index e21a2fe7..e50358d 100644
--- a/chrome/browser/sync/test/integration/two_client_passwords_sync_test.cc
+++ b/chrome/browser/sync/test/integration/two_client_passwords_sync_test.cc
@@ -291,10 +291,6 @@
 // Flaky on TSAN: crbug.com/915219
 #define MAYBE_SetPassphraseAndThenSetupSync \
   DISABLED_SetPassphraseAndThenSetupSync
-#elif defined(OS_CHROMEOS)
-// https://crbug.com/956012
-#define MAYBE_SetPassphraseAndThenSetupSync \
-  DISABLED_SetPassphraseAndThenSetupSync
 #else
 #define MAYBE_SetPassphraseAndThenSetupSync SetPassphraseAndThenSetupSync
 #endif
@@ -306,6 +302,8 @@
   GetSyncService(0)->GetUserSettings()->SetEncryptionPassphrase(
       kValidPassphrase);
   ASSERT_TRUE(PassphraseAcceptedChecker(GetSyncService(0)).Wait());
+  // Wait for the client to commit the updates.
+  ASSERT_TRUE(UpdatedProgressMarkerChecker(GetSyncService(0)).Wait());
 
   // When client 1 hits a passphrase required state, we can infer that
   // client 0's passphrase has been committed. to the server.
diff --git a/chrome/browser/sync/test/integration/two_client_preferences_sync_test.cc b/chrome/browser/sync/test/integration/two_client_preferences_sync_test.cc
index 10f3ab60..06042542 100644
--- a/chrome/browser/sync/test/integration/two_client_preferences_sync_test.cc
+++ b/chrome/browser/sync/test/integration/two_client_preferences_sync_test.cc
@@ -40,7 +40,8 @@
         SyncTest(TWO_CLIENT) {}
   ~TwoClientPreferencesSyncTest() override {}
 
-  bool TestUsesSelfNotifications() override { return false; }
+  // Needed for AwaitQuiescence().
+  bool TestUsesSelfNotifications() override { return true; }
 
  private:
   DISALLOW_COPY_AND_ASSIGN(TwoClientPreferencesSyncTest);
@@ -49,6 +50,8 @@
 IN_PROC_BROWSER_TEST_P(TwoClientPreferencesSyncTest, E2E_ENABLED(Sanity)) {
   DisableVerifier();
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+  // Wait until sync settles before we override the prefs below.
+  ASSERT_TRUE(AwaitQuiescence());
   ASSERT_TRUE(StringPrefMatchChecker(prefs::kHomePage).Wait());
   const std::string new_home_page = base::StringPrintf(
       "https://example.com/%s", base::GenerateGUID().c_str());
diff --git a/chrome/browser/sync/test/integration/two_client_themes_sync_test.cc b/chrome/browser/sync/test/integration/two_client_themes_sync_test.cc
index a1d92816..a38d771 100644
--- a/chrome/browser/sync/test/integration/two_client_themes_sync_test.cc
+++ b/chrome/browser/sync/test/integration/two_client_themes_sync_test.cc
@@ -27,7 +27,8 @@
       : FeatureToggler(switches::kSyncPseudoUSSThemes), SyncTest(TWO_CLIENT) {}
   ~TwoClientThemesSyncTest() override {}
 
-  bool TestUsesSelfNotifications() override { return false; }
+  // Needed for AwaitQuiescence().
+  bool TestUsesSelfNotifications() override { return true; }
 
  private:
   DISALLOW_COPY_AND_ASSIGN(TwoClientThemesSyncTest);
@@ -39,6 +40,8 @@
 IN_PROC_BROWSER_TEST_P(TwoClientThemesSyncTest,
                        E2E_ENABLED(DefaultThenSyncCustom)) {
   ASSERT_TRUE(SetupSync());
+  // Wait until sync settles before we override the theme below.
+  AwaitQuiescence();
 
   ASSERT_FALSE(UsingCustomTheme(GetProfile(0)));
   ASSERT_FALSE(UsingCustomTheme(GetProfile(1)));
@@ -66,6 +69,8 @@
   SetCustomTheme(GetProfile(1));
 
   ASSERT_TRUE(SetupSync());
+  // Wait until sync settles before we override the theme below.
+  AwaitQuiescence();
 
   UseSystemTheme(GetProfile(0));
   ASSERT_TRUE(UsingSystemTheme(GetProfile(0)));
@@ -86,6 +91,8 @@
   SetCustomTheme(GetProfile(1));
 
   ASSERT_TRUE(SetupSync());
+  // Wait until sync settles before we override the theme below.
+  AwaitQuiescence();
 
   UseDefaultTheme(GetProfile(0));
   EXPECT_TRUE(UsingDefaultTheme(GetProfile(0)));
@@ -101,6 +108,8 @@
 // is intended to test steady-state scenarios.
 IN_PROC_BROWSER_TEST_P(TwoClientThemesSyncTest, E2E_ENABLED(CycleOptions)) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+  // Wait until sync settles before we override the theme below.
+  AwaitQuiescence();
 
   SetCustomTheme(GetProfile(0));
 
diff --git a/chrome/browser/sync/test/integration/two_client_user_events_sync_test.cc b/chrome/browser/sync/test/integration/two_client_user_events_sync_test.cc
index 2e3e3843..a780fda 100644
--- a/chrome/browser/sync/test/integration/two_client_user_events_sync_test.cc
+++ b/chrome/browser/sync/test/integration/two_client_user_events_sync_test.cc
@@ -8,6 +8,7 @@
 #include "chrome/browser/sync/test/integration/profile_sync_service_harness.h"
 #include "chrome/browser/sync/test/integration/sync_integration_test_util.h"
 #include "chrome/browser/sync/test/integration/sync_test.h"
+#include "chrome/browser/sync/test/integration/updated_progress_marker_checker.h"
 #include "chrome/browser/sync/test/integration/user_events_helper.h"
 #include "chrome/browser/sync/user_event_service_factory.h"
 #include "components/sync/protocol/user_event_specifics.pb.h"
@@ -61,8 +62,12 @@
   GetSyncService(kEncryptingClientId)
       ->GetUserSettings()
       ->SetEncryptionPassphrase("hunter2");
+  UpdatedProgressMarkerChecker update_checker(
+      GetSyncService(kEncryptingClientId));
   ASSERT_TRUE(
       PassphraseAcceptedChecker(GetSyncService(kEncryptingClientId)).Wait());
+  // Make sure the updates are committed before proceeding with the test.
+  ASSERT_TRUE(update_checker.Wait());
 
   // Record a user event on the second client before setting up sync (before
   // knowing it will be encrypted). This event should not get recorded while
diff --git a/chrome/browser/sync/test/integration/two_client_wallet_sync_test.cc b/chrome/browser/sync/test/integration/two_client_wallet_sync_test.cc
index 67ea5eb..17ae71c 100644
--- a/chrome/browser/sync/test/integration/two_client_wallet_sync_test.cc
+++ b/chrome/browser/sync/test/integration/two_client_wallet_sync_test.cc
@@ -60,7 +60,8 @@
   }
   ~TwoClientWalletSyncTest() override {}
 
-  bool TestUsesSelfNotifications() override { return false; }
+  // Needed for AwaitQuiescence().
+  bool TestUsesSelfNotifications() override { return true; }
 
   bool SetupSync() override {
     test_clock_.SetNow(kArbitraryDefaultTime);
@@ -505,6 +506,9 @@
        CreateSyncWalletAddress(/*name=*/"address-1", /*company=*/"Company-1"),
        CreateDefaultSyncPaymentsCustomerData()});
   ASSERT_TRUE(SetupSync());
+  // Wait until sync settles (for the wallet metadata) before we change the
+  // data again.
+  ASSERT_TRUE(AwaitQuiescence());
 
   // Grab the current address on the first client.
   std::vector<AutofillProfile*> server_addresses = GetServerProfiles(0);
@@ -553,6 +557,9 @@
        CreateSyncWalletAddress(/*name=*/"address-1", /*company=*/"Company-1"),
        CreateDefaultSyncPaymentsCustomerData()});
   ASSERT_TRUE(SetupSync());
+  // Wait until sync settles (for the wallet metadata) before we change the
+  // data again.
+  ASSERT_TRUE(AwaitQuiescence());
 
   // Grab the current card on the first client.
   std::vector<CreditCard*> credit_cards = GetServerCreditCards(0);
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 23e65c09..ce02205c 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -450,6 +450,7 @@
     "//components/password_manager/content/browser",
     "//components/password_manager/core/browser",
     "//components/payments/content:utils",
+    "//components/payments/content/icon",
     "//components/pdf/browser",
     "//components/policy/core/browser",
     "//components/pref_registry",
diff --git a/chrome/browser/ui/android/passwords/manual_filling_view_android.cc b/chrome/browser/ui/android/passwords/manual_filling_view_android.cc
index ed05d00..3e43edd 100644
--- a/chrome/browser/ui/android/passwords/manual_filling_view_android.cc
+++ b/chrome/browser/ui/android/passwords/manual_filling_view_android.cc
@@ -23,6 +23,7 @@
 #include "components/autofill/core/browser/accessory_sheet_data.h"
 #include "components/autofill/core/common/password_form.h"
 #include "jni/ManualFillingComponentBridge_jni.h"
+#include "jni/UserInfoField_jni.h"
 #include "ui/android/view_android.h"
 #include "ui/android/window_android.h"
 #include "ui/gfx/android/java_bitmap.h"
@@ -31,7 +32,9 @@
 using autofill::AccessorySheetData;
 using autofill::FooterCommand;
 using autofill::UserInfo;
+using base::android::ConvertJavaStringToUTF16;
 using base::android::ConvertUTF16ToJavaString;
+using base::android::JavaRef;
 using base::android::ScopedJavaLocalRef;
 
 ManualFillingViewAndroid::ManualFillingViewAndroid(
@@ -107,9 +110,9 @@
     JNIEnv* env,
     const base::android::JavaParamRef<jobject>& obj,
     jboolean isPassword,
-    const base::android::JavaParamRef<_jstring*>& textToFill) {
+    const base::android::JavaParamRef<jobject>& j_user_info_field) {
   controller_->OnFillingTriggered(
-      isPassword, base::android::ConvertJavaStringToUTF16(textToFill));
+      ConvertJavaUserInfoField(env, j_user_info_field));
 }
 
 void ManualFillingViewAndroid::OnOptionSelected(
@@ -166,6 +169,19 @@
   return j_tab_data;
 }
 
+UserInfo::Field ManualFillingViewAndroid::ConvertJavaUserInfoField(
+    JNIEnv* env,
+    const JavaRef<jobject>& j_field_to_convert) {
+  base::string16 display_text = ConvertJavaStringToUTF16(
+      env, Java_UserInfoField_getDisplayText(env, j_field_to_convert));
+  base::string16 a11y_description = ConvertJavaStringToUTF16(
+      env, Java_UserInfoField_getA11yDescription(env, j_field_to_convert));
+  bool is_obfuscated = Java_UserInfoField_isObfuscated(env, j_field_to_convert);
+  bool selectable = Java_UserInfoField_isSelectable(env, j_field_to_convert);
+  return UserInfo::Field(display_text, a11y_description, is_obfuscated,
+                         selectable);
+}
+
 // static
 void JNI_ManualFillingComponentBridge_CachePasswordSheetDataForTesting(
     JNIEnv* env,
diff --git a/chrome/browser/ui/android/passwords/manual_filling_view_android.h b/chrome/browser/ui/android/passwords/manual_filling_view_android.h
index 4decce8..093393dd 100644
--- a/chrome/browser/ui/android/passwords/manual_filling_view_android.h
+++ b/chrome/browser/ui/android/passwords/manual_filling_view_android.h
@@ -9,6 +9,7 @@
 
 #include "base/android/scoped_java_ref.h"
 #include "chrome/browser/autofill/manual_filling_view_interface.h"
+#include "components/autofill/core/browser/accessory_sheet_data.h"
 
 namespace gfx {
 class Image;
@@ -43,7 +44,7 @@
       JNIEnv* env,
       const base::android::JavaParamRef<jobject>& obj,
       jboolean isPassword,
-      const base::android::JavaParamRef<jstring>& textToFill);
+      const base::android::JavaParamRef<jobject>& j_user_info_field);
   void OnOptionSelected(
       JNIEnv* env,
       const base::android::JavaParamRef<jobject>& obj,
@@ -61,6 +62,10 @@
       JNIEnv* env,
       const autofill::AccessorySheetData& tab_data);
 
+  autofill::UserInfo::Field ConvertJavaUserInfoField(
+      JNIEnv* env,
+      const base::android::JavaRef<jobject>& j_field_to_convert);
+
   // The controller provides data for this view and owns it.
   ManualFillingController* controller_;
 
diff --git a/chrome/browser/ui/views/payments/payment_handler_web_flow_view_controller.cc b/chrome/browser/ui/views/payments/payment_handler_web_flow_view_controller.cc
index 2376598..b880021 100644
--- a/chrome/browser/ui/views/payments/payment_handler_web_flow_view_controller.cc
+++ b/chrome/browser/ui/views/payments/payment_handler_web_flow_view_controller.cc
@@ -16,6 +16,7 @@
 #include "chrome/browser/ui/views/payments/payment_request_dialog_view.h"
 #include "chrome/browser/ui/views/payments/payment_request_views_util.h"
 #include "chrome/grit/generated_resources.h"
+#include "components/payments/content/icon/icon_size.h"
 #include "components/payments/content/origin_security_checker.h"
 #include "components/web_modal/web_contents_modal_dialog_manager.h"
 #include "components/web_modal/web_contents_modal_dialog_manager_delegate.h"
@@ -114,18 +115,19 @@
     top_level_columns->AddColumn(views::GridLayout::LEADING,
                                  views::GridLayout::CENTER, 1.0,
                                  views::GridLayout::USE_PREF, 0, 0);
-    // Payment handler icon should be 32 pixels tall.
-    constexpr int kPaymentHandlerIconHeight = 32;
     const bool has_icon = icon_image_skia.width() && icon_image_skia.height();
     float adjusted_width = base::checked_cast<float>(icon_image_skia.width());
     if (has_icon) {
       adjusted_width =
-          adjusted_width * kPaymentHandlerIconHeight / icon_image_skia.height();
+          adjusted_width *
+          IconSizeCalculator::kPaymentAppDeviceIndependentIdealIconHeight /
+          icon_image_skia.height();
       // A column for the instrument icon.
       top_level_columns->AddColumn(
           views::GridLayout::LEADING, views::GridLayout::FILL,
           views::GridLayout::kFixedSize, views::GridLayout::FIXED,
-          adjusted_width, kPaymentHandlerIconHeight);
+          adjusted_width,
+          IconSizeCalculator::kPaymentAppDeviceIndependentIdealIconHeight);
       top_level_columns->AddPaddingColumn(views::GridLayout::kFixedSize, 8);
     }
 
@@ -135,8 +137,11 @@
       std::unique_ptr<views::ImageView> instrument_icon_view =
           CreateInstrumentIconView(/*icon_id=*/0, icon_image_skia,
                                    /*label=*/page_title);
-      instrument_icon_view->SetImageSize(
-          gfx::Size(adjusted_width, kPaymentHandlerIconHeight));
+      // We should set image size in density independent pixels here, since
+      // views::ImageView objects are rastered at the device scale factor.
+      instrument_icon_view->SetImageSize(gfx::Size(
+          adjusted_width,
+          IconSizeCalculator::kPaymentAppDeviceIndependentIdealIconHeight));
       top_level_layout->AddView(instrument_icon_view.release());
     }
   }
diff --git a/chrome/browser/ui/views/payments/payment_request_views_util.cc b/chrome/browser/ui/views/payments/payment_request_views_util.cc
index 4176b3e6..1ce4ad3 100644
--- a/chrome/browser/ui/views/payments/payment_request_views_util.cc
+++ b/chrome/browser/ui/views/payments/payment_request_views_util.cc
@@ -20,6 +20,7 @@
 #include "components/autofill/core/browser/credit_card.h"
 #include "components/autofill/core/browser/field_types.h"
 #include "components/autofill/core/browser/phone_number_i18n.h"
+#include "components/payments/content/icon/icon_size.h"
 #include "components/payments/core/payment_options_provider.h"
 #include "components/payments/core/payment_request_data_util.h"
 #include "components/payments/core/payments_profile_comparator.h"
@@ -249,11 +250,11 @@
     float ratio = 1;
     if (width && height)
       ratio = width / height;
-    // Other instrument icons should be 32 pixels high while preserving the
-    // image ratio.
-    constexpr int kPaymentHandlerIconHeight = 32;
-    icon_view->SetImageSize(gfx::Size(ratio * kPaymentHandlerIconHeight,
-                                      kPaymentHandlerIconHeight));
+    // We should set image size in density indepent pixels here, since
+    // views::ImageView objects are rastered at the device scale factor.
+    icon_view->SetImageSize(gfx::Size(
+        ratio * IconSizeCalculator::kPaymentAppDeviceIndependentIdealIconHeight,
+        IconSizeCalculator::kPaymentAppDeviceIndependentIdealIconHeight));
   } else {
     icon_view->SetImage(ui::ResourceBundle::GetSharedInstance()
                             .GetImageNamed(icon_resource_id)
diff --git a/chrome/browser/unified_consent/unified_consent_browsertest.cc b/chrome/browser/unified_consent/unified_consent_browsertest.cc
index caa7078..97d414e 100644
--- a/chrome/browser/unified_consent/unified_consent_browsertest.cc
+++ b/chrome/browser/unified_consent/unified_consent_browsertest.cc
@@ -9,6 +9,7 @@
 #include "chrome/browser/sync/profile_sync_service_factory.h"
 #include "chrome/browser/sync/test/integration/profile_sync_service_harness.h"
 #include "chrome/browser/sync/test/integration/sync_test.h"
+#include "chrome/browser/sync/test/integration/updated_progress_marker_checker.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/unified_consent/unified_consent_service_factory.h"
 #include "chrome/common/pref_names.h"
@@ -118,9 +119,8 @@
       "UnifiedConsent.SyncAndGoogleServicesSettings", 1);
 }
 
-// Flakes on all platforms. http://crbug.com/954167
 IN_PROC_BROWSER_TEST_F(UnifiedConsentBrowserTest,
-                       DISABLED_SettingsOptInTakeOverServicePrefChanges) {
+                       SettingsOptInTakeOverServicePrefChanges) {
   std::string pref_A = prefs::kSearchSuggestEnabled;
   std::string pref_B = prefs::kAlternateErrorPagesEnabled;
 
@@ -130,6 +130,8 @@
   // both prefs will be "off".
   GetProfile(0)->GetPrefs()->SetBoolean(pref_A, false);
   GetProfile(0)->GetPrefs()->SetBoolean(pref_B, false);
+  // Make sure the updates are committed before proceeding with the test.
+  ASSERT_TRUE(UpdatedProgressMarkerChecker(GetSyncService(0)).Wait());
 
   // Second client: Turn off both prefs while sync is off.
   GetProfile(1)->GetPrefs()->SetBoolean(pref_A, false);
diff --git a/chrome/common/url_constants.cc b/chrome/common/url_constants.cc
index 809b181..daa5cfe 100644
--- a/chrome/common/url_constants.cc
+++ b/chrome/common/url_constants.cc
@@ -160,7 +160,11 @@
     "https://www.chrome.com/manage";
 
 const char kManagedUiLearnMoreUrl[] =
+#if defined(OS_CHROMEOS)
     "https://support.google.com/chromebook/?p=is_chrome_managed";
+#else
+    "https://support.google.com/chrome/?p=is_chrome_managed";
+#endif
 
 const char kMyActivityUrlInClearBrowsingData[] =
     "https://myactivity.google.com/myactivity/?utm_source=chrome_cbd";
diff --git a/chromeos/components/power/dark_resume_controller.cc b/chromeos/components/power/dark_resume_controller.cc
index 0782ee9..891a977 100644
--- a/chromeos/components/power/dark_resume_controller.cc
+++ b/chromeos/components/power/dark_resume_controller.cc
@@ -31,8 +31,9 @@
 
 void DarkResumeController::DarkSuspendImminent() {
   DVLOG(1) << __func__;
-  suspend_readiness_cb_ =
-      PowerManagerClient::Get()->GetSuspendReadinessCallback(FROM_HERE);
+  block_suspend_token_ = base::UnguessableToken::Create();
+  PowerManagerClient::Get()->BlockSuspend(block_suspend_token_,
+                                          "DarkResumeController");
   // Schedule task that will check for any wake locks acquired in dark resume.
   DCHECK(!wake_lock_check_timer_.IsRunning());
   wake_lock_check_timer_.Start(
@@ -59,20 +60,20 @@
   // with dark resume.
   DVLOG(1) << __func__;
   // The observer is only registered once dark resume starts.
-  DCHECK(suspend_readiness_cb_);
-  std::move(suspend_readiness_cb_).Run();
+  DCHECK(block_suspend_token_);
+  PowerManagerClient::Get()->UnblockSuspend(block_suspend_token_);
+  block_suspend_token_ = {};
   ClearDarkResumeState();
 }
 
 bool DarkResumeController::IsDarkResumeStateSetForTesting() const {
-  return !suspend_readiness_cb_.is_null() &&
-         wake_lock_observer_binding_.is_bound();
+  return block_suspend_token_ && wake_lock_observer_binding_.is_bound();
 }
 
 bool DarkResumeController::IsDarkResumeStateClearedForTesting() const {
   return !weak_ptr_factory_.HasWeakPtrs() &&
          !wake_lock_check_timer_.IsRunning() &&
-         !hard_timeout_timer_.IsRunning() && suspend_readiness_cb_.is_null() &&
+         !hard_timeout_timer_.IsRunning() && !block_suspend_token_ &&
          !wake_lock_observer_binding_.is_bound();
 }
 
@@ -104,16 +105,17 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(dark_resume_tasks_sequence_checker_);
   hard_timeout_timer_.Stop();
   // Enough is enough. Tell power daemon it's okay to suspend.
-  DCHECK(suspend_readiness_cb_);
-  std::move(suspend_readiness_cb_).Run();
+  DCHECK(block_suspend_token_);
+  PowerManagerClient::Get()->UnblockSuspend(block_suspend_token_);
+  block_suspend_token_ = {};
   ClearDarkResumeState();
 }
 
 void DarkResumeController::ClearDarkResumeState() {
   DVLOG(1) << __func__;
-  // Reset the callback that is used to trigger a re-suspend. Won't be needed
+  // Reset the token that is used to trigger a re-suspend. Won't be needed
   // if the dark resume state machine is ending.
-  suspend_readiness_cb_.Reset();
+  block_suspend_token_ = {};
 
   // This automatically invalidates any WakeLockObserver and associated callback
   // in this case OnDeactivation.
diff --git a/chromeos/components/power/dark_resume_controller.h b/chromeos/components/power/dark_resume_controller.h
index 9655795..9853321 100644
--- a/chromeos/components/power/dark_resume_controller.h
+++ b/chromeos/components/power/dark_resume_controller.h
@@ -11,6 +11,7 @@
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "base/timer/timer.h"
+#include "base/unguessable_token.h"
 #include "chromeos/dbus/power/power_manager_client.h"
 #include "mojo/public/cpp/bindings/binding.h"
 #include "services/device/public/mojom/wake_lock.mojom.h"
@@ -68,11 +69,11 @@
   void OnWakeLockDeactivated(device::mojom::WakeLockType type) override;
 
   // Return true iff all dark resume related state is set i.e the suspend
-  // readiness callback is set and wake lock release event has observers.
+  // readiness token is set and wake lock release event has observers.
   bool IsDarkResumeStateSetForTesting() const;
 
   // Return true iff all dark resume related state is reset i.e. suspend
-  // readiness callback is null, wake lock release event has no observers,
+  // readiness token is empty, wake lock release event has no observers,
   // wake lock check timer is reset, hard timeout timer is reset and there are
   // no in flight tasks. This should be true when device exits dark resume
   // either by re-suspending or transitioning to full resume.
@@ -98,9 +99,9 @@
   // Not owned by this instance.
   service_manager::Connector* const connector_;
 
-  // Called when system is ready to supend after a DarkSupendImminent i.e.
+  // Used when system is ready to suspend after a DarkSupendImminent i.e.
   // after a dark resume.
-  base::OnceClosure suspend_readiness_cb_;
+  base::UnguessableToken block_suspend_token_;
 
   // The binding used to implement device::mojom::WakeLockObserver.
   mojo::Binding<device::mojom::WakeLockObserver> wake_lock_observer_binding_;
diff --git a/chromeos/dbus/power/fake_power_manager_client.cc b/chromeos/dbus/power/fake_power_manager_client.cc
index 9bf45d85..487c9abae 100644
--- a/chromeos/dbus/power/fake_power_manager_client.cc
+++ b/chromeos/dbus/power/fake_power_manager_client.cc
@@ -249,11 +249,16 @@
       FROM_HERE, base::BindOnce(std::move(callback), inactivity_delays_));
 }
 
-base::OnceClosure FakePowerManagerClient::GetSuspendReadinessCallback(
-    const base::Location& from_where) {
+void FakePowerManagerClient::BlockSuspend(const base::UnguessableToken& token,
+                                          const std::string& debug_info) {
   ++num_pending_suspend_readiness_callbacks_;
-  return base::BindOnce(&FakePowerManagerClient::HandleSuspendReadiness,
-                        base::Unretained(this));
+}
+
+void FakePowerManagerClient::UnblockSuspend(
+    const base::UnguessableToken& token) {
+  CHECK_GT(num_pending_suspend_readiness_callbacks_, 0);
+
+  --num_pending_suspend_readiness_callbacks_;
 }
 
 void FakePowerManagerClient::CreateArcTimers(
@@ -423,12 +428,6 @@
     observer.PowerChanged(*props_);
 }
 
-void FakePowerManagerClient::HandleSuspendReadiness() {
-  CHECK_GT(num_pending_suspend_readiness_callbacks_, 0);
-
-  --num_pending_suspend_readiness_callbacks_;
-}
-
 void FakePowerManagerClient::DeleteArcTimersInternal(const std::string& tag) {
   // Retrieve all timer ids associated with |tag|. Delete all timers associated
   // with these timer ids.
diff --git a/chromeos/dbus/power/fake_power_manager_client.h b/chromeos/dbus/power/fake_power_manager_client.h
index 1a376e55..c3a0a70 100644
--- a/chromeos/dbus/power/fake_power_manager_client.h
+++ b/chromeos/dbus/power/fake_power_manager_client.h
@@ -111,8 +111,9 @@
   void GetInactivityDelays(
       DBusMethodCallback<power_manager::PowerManagementPolicy::Delays> callback)
       override;
-  base::OnceClosure GetSuspendReadinessCallback(
-      const base::Location& from_where) override;
+  void BlockSuspend(const base::UnguessableToken& token,
+                    const std::string& debug_info) override;
+  void UnblockSuspend(const base::UnguessableToken& token) override;
   void CreateArcTimers(
       const std::string& tag,
       std::vector<std::pair<clockid_t, base::ScopedFD>> arc_timer_requests,
@@ -186,10 +187,6 @@
   }
 
  private:
-  // Callback that will be run by asynchronous suspend delays to report
-  // readiness.
-  void HandleSuspendReadiness();
-
   // Notifies |observers_| that |props_| has been updated.
   void NotifyObservers();
 
diff --git a/chromeos/dbus/power/power_manager_client.cc b/chromeos/dbus/power/power_manager_client.cc
index b94b799..f6b9d9c 100644
--- a/chromeos/dbus/power/power_manager_client.cc
+++ b/chromeos/dbus/power/power_manager_client.cc
@@ -23,6 +23,7 @@
 #include "base/strings/stringprintf.h"
 #include "base/threading/platform_thread.h"
 #include "base/timer/timer.h"
+#include "base/unguessable_token.h"
 #include "chromeos/dbus/power/fake_power_manager_client.h"
 #include "chromeos/dbus/power_manager/backlight.pb.h"
 #include "chromeos/dbus/power_manager/idle.pb.h"
@@ -452,17 +453,30 @@
                        weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
   }
 
-  base::OnceClosure GetSuspendReadinessCallback(
-      const base::Location& from_where) override {
+  void BlockSuspend(const base::UnguessableToken& token,
+                    const std::string& debug_info) override {
     DCHECK(OnOriginThread());
     DCHECK(suspend_is_pending_);
+    DCHECK(token);
 
-    const int callback_id = next_suspend_readiness_callback_id_++;
-    pending_suspend_readiness_callbacks_[callback_id] = from_where;
-    return base::BindOnce(
-        &PowerManagerClientImpl::HandleObserverSuspendReadiness,
-        weak_ptr_factory_.GetWeakPtr(), pending_suspend_id_,
-        suspending_from_dark_resume_, callback_id);
+    suspend_readiness_registry_[token.ToString()] = {
+        pending_suspend_id_, suspending_from_dark_resume_, debug_info};
+  }
+
+  void UnblockSuspend(const base::UnguessableToken& token) override {
+    DCHECK(OnOriginThread());
+    auto registration = suspend_readiness_registry_.find(token.ToString());
+
+    if (registration == suspend_readiness_registry_.end() ||
+        registration->second.suspend_id != pending_suspend_id_ ||
+        registration->second.suspending_from_dark_resume !=
+            suspending_from_dark_resume_ ||
+        !suspend_is_pending_) {
+      return;
+    }
+
+    suspend_readiness_registry_.erase(registration);
+    MaybeReportSuspendReadiness();
   }
 
   void CreateArcTimers(
@@ -843,12 +857,11 @@
     pending_suspend_id_ = proto.suspend_id();
     suspend_is_pending_ = true;
     suspending_from_dark_resume_ = in_dark_resume;
-    pending_suspend_readiness_callbacks_.clear();
+    suspend_readiness_registry_.clear();
 
     // Record the fact that observers are being notified to ensure that we don't
-    // report readiness prematurely if one of them calls
-    // GetSuspendReadinessCallback() and then runs the callback synchonously
-    // instead of asynchronously.
+    // report readiness prematurely if one of them calls BlockSuspend() and then
+    // runs UnblockSuspend() synchonously instead of asynchronously.
     notifying_observers_about_suspend_imminent_ = true;
     if (suspending_from_dark_resume_) {
       for (auto& observer : observers_)
@@ -897,11 +910,11 @@
 
     // powerd gives clients a limited amount of time to report suspend
     // readiness. Log the stragglers within Chrome to aid in debugging.
-    for (const auto it : pending_suspend_readiness_callbacks_) {
+    for (const auto it : suspend_readiness_registry_) {
       LOG(WARNING) << "Didn't report suspend readiness due to "
-                   << it.second.ToString();
+                   << it.second.debug_info;
     }
-    pending_suspend_readiness_callbacks_.clear();
+    suspend_readiness_registry_.clear();
 
     for (auto& observer : observers_)
       observer.SuspendDone(duration);
@@ -1032,22 +1045,6 @@
                        power_manager::kRegisterDarkSuspendDelayMethod));
   }
 
-  // Records the fact that an observer has finished doing asynchronous work
-  // that was blocking a pending suspend attempt and possibly reports
-  // suspend readiness to powerd.  Called by callbacks returned via
-  // GetSuspendReadinessCallback().
-  void HandleObserverSuspendReadiness(int32_t suspend_id,
-                                      bool in_dark_resume,
-                                      int callback_id) {
-    DCHECK(OnOriginThread());
-    if (!suspend_is_pending_ || suspend_id != pending_suspend_id_ ||
-        in_dark_resume != suspending_from_dark_resume_)
-      return;
-
-    pending_suspend_readiness_callbacks_.erase(callback_id);
-    MaybeReportSuspendReadiness();
-  }
-
   // Reports suspend readiness to powerd if no observers are still holding
   // suspend readiness callbacks.
   void MaybeReportSuspendReadiness() {
@@ -1058,7 +1055,7 @@
     if (notifying_observers_about_suspend_imminent_)
       return;
 
-    if (!pending_suspend_readiness_callbacks_.empty())
+    if (!suspend_readiness_registry_.empty())
       return;
 
     std::string method_name;
@@ -1123,13 +1120,15 @@
   // helps distinguish the context within which these variables are being used.
   bool suspending_from_dark_resume_ = false;
 
-  // Next ID to be assigned to a callback returned via
-  // GetSuspendReadinessCallback().
-  int next_suspend_readiness_callback_id_ = 1;
+  struct SuspendInfo {
+    int32_t suspend_id;
+    bool suspending_from_dark_resume;
+    std::string debug_info;
+  };
 
-  // Map from suspend readiness callback ID to the location of the code that
-  // requested the callback.
-  std::map<int, base::Location> pending_suspend_readiness_callbacks_;
+  // A map that holds BlockSuspend() registrations. It maps from the token (in
+  // string form) to details about the suspend.
+  std::unordered_map<std::string, SuspendInfo> suspend_readiness_registry_;
 
   // Inspected by MaybeReportSuspendReadiness() to avoid prematurely notifying
   // powerd about suspend readiness while |observers_|' SuspendImminent()
diff --git a/chromeos/dbus/power/power_manager_client.h b/chromeos/dbus/power/power_manager_client.h
index 5ea004b..b6f3a967a 100644
--- a/chromeos/dbus/power/power_manager_client.h
+++ b/chromeos/dbus/power/power_manager_client.h
@@ -24,6 +24,10 @@
 #include "chromeos/dbus/power_manager/suspend.pb.h"
 #include "third_party/cros_system_api/dbus/service_constants.h"
 
+namespace base {
+class UnguessableToken;
+}
+
 namespace dbus {
 class Bus;
 }
@@ -108,9 +112,9 @@
     // all observers' implementations of this method have finished running.
     //
     // If an observer wishes to asynchronously delay suspend,
-    // PowerManagerClient::GetSuspendReadinessCallback() may be called from
-    // within SuspendImminent().  The returned callback must be called once
-    // the observer is ready for suspend.
+    // PowerManagerClient::BlockSuspend() may be called from within
+    // SuspendImminent().  UnblockSuspend() must be called once the observer is
+    // ready for suspend.
     virtual void SuspendImminent(
         power_manager::SuspendImminent::Reason reason) {}
 
@@ -122,8 +126,8 @@
     // Called when the system is about to resuspend from a dark resume.  Like
     // SuspendImminent(), the suspend will be deferred until all observers have
     // finished running and those observers that wish to asynchronously delay
-    // the suspend should call PowerManagerClient::GetSuspendReadinessCallback()
-    // from within this method.  The returned callback should be run once the
+    // the suspend should call PowerManagerClient::BlockSuspend()
+    // from within this method.  UnblockSuspend() must be called once the
     // observer is ready for suspend.
     virtual void DarkSuspendImminent() {}
 
@@ -275,10 +279,17 @@
       DBusMethodCallback<power_manager::PowerManagementPolicy::Delays>
           callback) = 0;
 
-  // Returns a callback that can be called by an observer to report readiness
-  // for suspend. See Observer::SuspendImminent().
-  virtual base::OnceClosure GetSuspendReadinessCallback(
-      const base::Location& from_where) = 0;
+  // Used by client code to temporarily block an imminent suspend (for up to
+  // kSuspendDelayTimeoutMs, i.e. 5 seconds). See Observer::SuspendImminent.
+  // |debug_info| should be a human-readable string that assists in determining
+  // what code called BlockSuspend(). Afterwards, callers must release the block
+  // via UnblockSuspend.
+  virtual void BlockSuspend(const base::UnguessableToken& token,
+                            const std::string& debug_info) = 0;
+
+  // Used to indicate that the client code which passed |token| before is now
+  // ready for a suspend.
+  virtual void UnblockSuspend(const base::UnguessableToken& token) = 0;
 
   // Creates timers corresponding to clocks present in |arc_timer_requests|.
   // ScopedFDs are used to indicate timer expiration as described in
diff --git a/chromeos/dbus/power/power_manager_client_unittest.cc b/chromeos/dbus/power/power_manager_client_unittest.cc
index 181ad5c..51aeaec1 100644
--- a/chromeos/dbus/power/power_manager_client_unittest.cc
+++ b/chromeos/dbus/power/power_manager_client_unittest.cc
@@ -16,6 +16,7 @@
 #include "base/memory/weak_ptr.h"
 #include "base/message_loop/message_loop.h"
 #include "base/run_loop.h"
+#include "base/unguessable_token.h"
 #include "chromeos/dbus/power_manager/suspend.pb.h"
 #include "dbus/mock_bus.h"
 #include "dbus/mock_object_proxy.h"
@@ -92,47 +93,47 @@
   int num_suspend_imminent() const { return num_suspend_imminent_; }
   int num_suspend_done() const { return num_suspend_done_; }
   int num_dark_suspend_imminent() const { return num_dark_suspend_imminent_; }
-  base::OnceClosure suspend_readiness_callback() {
-    return std::move(suspend_readiness_callback_);
+  const base::UnguessableToken& block_suspend_token() const {
+    return block_suspend_token_;
   }
 
-  void set_take_suspend_readiness_callback(bool take_callback) {
-    take_suspend_readiness_callback_ = take_callback;
+  void set_should_block_suspend(bool take_callback) {
+    should_block_suspend_ = take_callback;
   }
-  void set_run_suspend_readiness_callback_immediately(bool run) {
-    run_suspend_readiness_callback_immediately_ = run;
+  void set_run_unblock_suspend_immediately(bool run) {
+    run_unblock_suspend_immediately_ = run;
   }
 
-  // Runs |suspend_readiness_callback_|.
-  bool RunSuspendReadinessCallback() WARN_UNUSED_RESULT {
-    if (suspend_readiness_callback_.is_null())
+  // Runs |block_suspend_token_|.
+  bool UnblockSuspend() WARN_UNUSED_RESULT {
+    if (block_suspend_token_.is_empty())
       return false;
 
-    std::move(suspend_readiness_callback_).Run();
+    client_->UnblockSuspend(block_suspend_token_);
     return true;
   }
 
   // PowerManagerClient::Observer:
   void SuspendImminent(power_manager::SuspendImminent::Reason reason) override {
     num_suspend_imminent_++;
-    if (take_suspend_readiness_callback_) {
-      suspend_readiness_callback_ =
-          client_->GetSuspendReadinessCallback(FROM_HERE);
+    if (should_block_suspend_) {
+      block_suspend_token_ = base::UnguessableToken::Create();
+      client_->BlockSuspend(block_suspend_token_, FROM_HERE.ToString());
     }
-    if (run_suspend_readiness_callback_immediately_)
-      CHECK(RunSuspendReadinessCallback());
+    if (run_unblock_suspend_immediately_)
+      CHECK(UnblockSuspend());
   }
   void SuspendDone(const base::TimeDelta& sleep_duration) override {
     num_suspend_done_++;
   }
   void DarkSuspendImminent() override {
     num_dark_suspend_imminent_++;
-    if (take_suspend_readiness_callback_) {
-      suspend_readiness_callback_ =
-          client_->GetSuspendReadinessCallback(FROM_HERE);
+    if (should_block_suspend_) {
+      block_suspend_token_ = base::UnguessableToken::Create();
+      client_->BlockSuspend(block_suspend_token_, FROM_HERE.ToString());
     }
-    if (run_suspend_readiness_callback_immediately_)
-      CHECK(RunSuspendReadinessCallback());
+    if (run_unblock_suspend_immediately_)
+      CHECK(UnblockSuspend());
   }
 
  private:
@@ -145,16 +146,16 @@
   int num_dark_suspend_imminent_ = 0;
 
   // Should SuspendImminent() and DarkSuspendImminent() call |client_|'s
-  // GetSuspendReadinessCallback() method?
-  bool take_suspend_readiness_callback_ = false;
+  // BlockSuspend() method?
+  bool should_block_suspend_ = false;
 
-  // Should SuspendImminent() and DarkSuspendImminent() run the suspend
-  // readiness callback synchronously after taking it? Only has an effect if
-  // |take_suspend_readiness_callback_| is true.
-  bool run_suspend_readiness_callback_immediately_ = false;
+  // Should SuspendImminent() and DarkSuspendImminent() unblock the suspend
+  // synchronously after blocking itit? Only has an effect if
+  // |should_block_suspend_| is true.
+  bool run_unblock_suspend_immediately_ = false;
 
-  // Callback returned by |client_|'s GetSuspendReadinessCallback() method.
-  base::OnceClosure suspend_readiness_callback_;
+  // When non-empty, the token for the outstanding block-suspend registration.
+  base::UnguessableToken block_suspend_token_;
 
   DISALLOW_COPY_AND_ASSIGN(TestObserver);
 };
@@ -386,9 +387,9 @@
 // callbacks.
 TEST_F(PowerManagerClientTest, ReportSuspendReadinessWithCallbacks) {
   TestObserver observer_1(client_);
-  observer_1.set_take_suspend_readiness_callback(true);
+  observer_1.set_should_block_suspend(true);
   TestObserver observer_2(client_);
-  observer_2.set_take_suspend_readiness_callback(true);
+  observer_2.set_should_block_suspend(true);
   TestObserver observer_3(client_);
 
   // When observers call GetSuspendReadinessCallback() from their
@@ -396,9 +397,9 @@
   // deferred until all callbacks are run.
   const int kSuspendId = 1;
   EmitSuspendImminentSignal(kSuspendImminent, kSuspendId);
-  EXPECT_TRUE(observer_1.RunSuspendReadinessCallback());
+  EXPECT_TRUE(observer_1.UnblockSuspend());
   ExpectSuspendReadiness(kHandleSuspendReadiness, kSuspendId, kSuspendDelayId);
-  EXPECT_TRUE(observer_2.RunSuspendReadinessCallback());
+  EXPECT_TRUE(observer_2.UnblockSuspend());
   EmitSuspendDoneSignal(kSuspendId);
   EXPECT_EQ(1, observer_1.num_suspend_done());
   EXPECT_EQ(1, observer_2.num_suspend_done());
@@ -409,7 +410,7 @@
 TEST_F(PowerManagerClientTest, NotifyRenderProcessManagerDelegate) {
   TestDelegate delegate(client_);
   TestObserver observer(client_);
-  observer.set_take_suspend_readiness_callback(true);
+  observer.set_should_block_suspend(true);
 
   const int kSuspendId = 1;
   EmitSuspendImminentSignal(kSuspendImminent, kSuspendId);
@@ -419,7 +420,7 @@
   // The RenderProcessManagerDelegate should be notified that suspend is
   // imminent only after observers have reported readiness.
   ExpectSuspendReadiness(kHandleSuspendReadiness, kSuspendId, kSuspendDelayId);
-  EXPECT_TRUE(observer.RunSuspendReadinessCallback());
+  EXPECT_TRUE(observer.UnblockSuspend());
   EXPECT_EQ(1, delegate.num_suspend_imminent());
   EXPECT_EQ(0, delegate.num_suspend_done());
 
@@ -434,7 +435,7 @@
 TEST_F(PowerManagerClientTest, ReportDarkSuspendReadiness) {
   TestDelegate delegate(client_);
   TestObserver observer(client_);
-  observer.set_take_suspend_readiness_callback(true);
+  observer.set_should_block_suspend(true);
 
   const int kSuspendId = 1;
   EmitSuspendImminentSignal(kSuspendImminent, kSuspendId);
@@ -442,7 +443,7 @@
   EXPECT_EQ(0, delegate.num_suspend_imminent());
 
   ExpectSuspendReadiness(kHandleSuspendReadiness, kSuspendId, kSuspendDelayId);
-  EXPECT_TRUE(observer.RunSuspendReadinessCallback());
+  EXPECT_TRUE(observer.UnblockSuspend());
   EXPECT_EQ(1, delegate.num_suspend_imminent());
 
   // The RenderProcessManagerDelegate shouldn't be notified about dark suspend
@@ -455,7 +456,7 @@
 
   ExpectSuspendReadiness(kHandleDarkSuspendReadiness, kDarkSuspendId,
                          kDarkSuspendDelayId);
-  EXPECT_TRUE(observer.RunSuspendReadinessCallback());
+  EXPECT_TRUE(observer.UnblockSuspend());
   EXPECT_EQ(0, delegate.num_suspend_done());
 
   EmitSuspendDoneSignal(kSuspendId);
@@ -468,7 +469,7 @@
 TEST_F(PowerManagerClientTest, SuspendCancelledWhileCallbackPending) {
   TestDelegate delegate(client_);
   TestObserver observer(client_);
-  observer.set_take_suspend_readiness_callback(true);
+  observer.set_should_block_suspend(true);
 
   const int kSuspendId = 1;
   EmitSuspendImminentSignal(kSuspendImminent, kSuspendId);
@@ -487,7 +488,7 @@
   // leave the renderers in a frozen state (http://crbug.com/646912). There's an
   // implicit expectation that powerd doesn't get notified about readiness here,
   // too.
-  EXPECT_TRUE(observer.RunSuspendReadinessCallback());
+  EXPECT_TRUE(observer.UnblockSuspend());
   EXPECT_EQ(0, delegate.num_suspend_imminent());
   EXPECT_EQ(0, delegate.num_suspend_done());
 }
@@ -497,12 +498,12 @@
 TEST_F(PowerManagerClientTest, SuspendDoneWhileDarkSuspendCallbackPending) {
   TestDelegate delegate(client_);
   TestObserver observer(client_);
-  observer.set_take_suspend_readiness_callback(true);
+  observer.set_should_block_suspend(true);
 
   const int kSuspendId = 1;
   EmitSuspendImminentSignal(kSuspendImminent, kSuspendId);
   ExpectSuspendReadiness(kHandleSuspendReadiness, kSuspendId, kSuspendDelayId);
-  EXPECT_TRUE(observer.RunSuspendReadinessCallback());
+  EXPECT_TRUE(observer.UnblockSuspend());
   EXPECT_EQ(1, delegate.num_suspend_imminent());
 
   const int kDarkSuspendId = 5;
@@ -517,7 +518,7 @@
   // Dark suspend readiness shouldn't be reported even if the callback runs at
   // this point, since the suspend attempt is already done. The delegate also
   // shouldn't receive any more calls.
-  EXPECT_TRUE(observer.RunSuspendReadinessCallback());
+  EXPECT_TRUE(observer.UnblockSuspend());
   EXPECT_EQ(1, delegate.num_suspend_imminent());
   EXPECT_EQ(1, delegate.num_suspend_done());
 }
@@ -527,27 +528,27 @@
 TEST_F(PowerManagerClientTest, DarkSuspendImminentWhileCallbackPending) {
   TestDelegate delegate(client_);
   TestObserver observer(client_);
-  observer.set_take_suspend_readiness_callback(true);
+  observer.set_should_block_suspend(true);
 
   // Announce that suspend is imminent and grab, but don't run, the readiness
   // callback.
   const int kSuspendId = 1;
   EmitSuspendImminentSignal(kSuspendImminent, kSuspendId);
   EXPECT_EQ(1, observer.num_suspend_imminent());
-  base::OnceClosure regular_callback = observer.suspend_readiness_callback();
+  base::UnguessableToken regular_token = observer.block_suspend_token();
 
   // Before readiness is reported, announce that dark suspend is imminent.
   const int kDarkSuspendId = 1;
   EmitSuspendImminentSignal(kDarkSuspendImminent, kDarkSuspendId);
   EXPECT_EQ(1, observer.num_dark_suspend_imminent());
-  base::OnceClosure dark_callback = observer.suspend_readiness_callback();
+  base::UnguessableToken dark_token = observer.block_suspend_token();
 
   // Complete the suspend attempt and run both of the earlier callbacks. Neither
   // should result in readiness being reported.
   EmitSuspendDoneSignal(kSuspendId);
   EXPECT_EQ(1, observer.num_suspend_done());
-  std::move(regular_callback).Run();
-  std::move(dark_callback).Run();
+  client_->UnblockSuspend(regular_token);
+  client_->UnblockSuspend(dark_token);
 }
 
 // Tests that PowerManagerClient handles a single observer that requests a
@@ -556,8 +557,8 @@
 // http://crosbug.com/p/58295
 TEST_F(PowerManagerClientTest, SyncCallbackWithSingleObserver) {
   TestObserver observer(client_);
-  observer.set_take_suspend_readiness_callback(true);
-  observer.set_run_suspend_readiness_callback_immediately(true);
+  observer.set_should_block_suspend(true);
+  observer.set_run_unblock_suspend_immediately(true);
 
   const int kSuspendId = 1;
   ExpectSuspendReadiness(kHandleSuspendReadiness, kSuspendId, kSuspendDelayId);
@@ -571,16 +572,16 @@
 // been notified and confirmed readiness.
 TEST_F(PowerManagerClientTest, SyncCallbackWithMultipleObservers) {
   TestObserver observer1(client_);
-  observer1.set_take_suspend_readiness_callback(true);
-  observer1.set_run_suspend_readiness_callback_immediately(true);
+  observer1.set_should_block_suspend(true);
+  observer1.set_run_unblock_suspend_immediately(true);
 
   TestObserver observer2(client_);
-  observer2.set_take_suspend_readiness_callback(true);
+  observer2.set_should_block_suspend(true);
 
   const int kSuspendId = 1;
   EmitSuspendImminentSignal(kSuspendImminent, kSuspendId);
   ExpectSuspendReadiness(kHandleSuspendReadiness, kSuspendId, kSuspendDelayId);
-  EXPECT_TRUE(observer2.RunSuspendReadinessCallback());
+  EXPECT_TRUE(observer2.UnblockSuspend());
   EmitSuspendDoneSignal(kSuspendId);
 }
 
diff --git a/chromeos/disks/suspend_unmount_manager.cc b/chromeos/disks/suspend_unmount_manager.cc
index f17cbf9..ddf6721 100644
--- a/chromeos/disks/suspend_unmount_manager.cc
+++ b/chromeos/disks/suspend_unmount_manager.cc
@@ -25,8 +25,8 @@
 
 SuspendUnmountManager::~SuspendUnmountManager() {
   PowerManagerClient::Get()->RemoveObserver(this);
-  if (!suspend_readiness_callback_.is_null())
-    std::move(suspend_readiness_callback_).Run();
+  if (block_suspend_token_)
+    PowerManagerClient::Get()->UnblockSuspend(block_suspend_token_);
 }
 
 void SuspendUnmountManager::SuspendImminent(
@@ -43,9 +43,10 @@
     }
   }
   for (const auto& mount_path : mount_paths) {
-    if (suspend_readiness_callback_.is_null()) {
-      suspend_readiness_callback_ =
-          PowerManagerClient::Get()->GetSuspendReadinessCallback(FROM_HERE);
+    if (block_suspend_token_.is_empty()) {
+      block_suspend_token_ = base::UnguessableToken::Create();
+      PowerManagerClient::Get()->BlockSuspend(block_suspend_token_,
+                                              "SuspendUnmountManager");
     }
     disk_mount_manager_->UnmountPath(
         mount_path, UNMOUNT_OPTIONS_NONE,
@@ -61,7 +62,7 @@
   unmounting_paths_.clear();
   disk_mount_manager_->EnsureMountInfoRefreshed(
       base::BindOnce(&OnRefreshCompleted), true /* force */);
-  suspend_readiness_callback_.Reset();
+  block_suspend_token_ = {};
 }
 
 void SuspendUnmountManager::OnUnmountComplete(const std::string& mount_path,
@@ -69,8 +70,10 @@
   // This can happen when unmount completes after suspend done is called.
   if (unmounting_paths_.erase(mount_path) != 1)
     return;
-  if (unmounting_paths_.empty() && !suspend_readiness_callback_.is_null())
-    std::move(suspend_readiness_callback_).Run();
+  if (unmounting_paths_.empty() && block_suspend_token_) {
+    PowerManagerClient::Get()->UnblockSuspend(block_suspend_token_);
+    block_suspend_token_ = {};
+  }
 }
 
 }  // namespace disks
diff --git a/chromeos/disks/suspend_unmount_manager.h b/chromeos/disks/suspend_unmount_manager.h
index 48714d9..e29cb08 100644
--- a/chromeos/disks/suspend_unmount_manager.h
+++ b/chromeos/disks/suspend_unmount_manager.h
@@ -10,6 +10,7 @@
 
 #include "base/component_export.h"
 #include "base/macros.h"
+#include "base/unguessable_token.h"
 #include "chromeos/dbus/cros_disks_client.h"
 #include "chromeos/dbus/power/power_manager_client.h"
 
@@ -40,7 +41,7 @@
   // The paths that the manager currently tries to unmount for suspend.
   std::set<std::string> unmounting_paths_;
 
-  base::OnceClosure suspend_readiness_callback_;
+  base::UnguessableToken block_suspend_token_;
 
   base::WeakPtrFactory<SuspendUnmountManager> weak_ptr_factory_;
 
diff --git a/chromeos/services/power/public/cpp/power_manager_mojo_client.cc b/chromeos/services/power/public/cpp/power_manager_mojo_client.cc
index cd88ec6..6220cf3 100644
--- a/chromeos/services/power/public/cpp/power_manager_mojo_client.cc
+++ b/chromeos/services/power/public/cpp/power_manager_mojo_client.cc
@@ -82,7 +82,9 @@
 
 void PowerManagerMojoClient::RequestRestart(
     power_manager::RequestRestartReason reason,
-    const std::string& description) {}
+    const std::string& description) {
+  controller_->RequestRestart(reason, description);
+}
 
 void PowerManagerMojoClient::RequestShutdown(
     power_manager::RequestShutdownReason reason,
@@ -114,10 +116,11 @@
     DBusMethodCallback<power_manager::PowerManagementPolicy::Delays> callback) {
 }
 
-base::OnceClosure PowerManagerMojoClient::GetSuspendReadinessCallback(
-    const base::Location& from_where) {
-  return base::OnceClosure();
-}
+void PowerManagerMojoClient::BlockSuspend(const base::UnguessableToken& token,
+                                          const std::string& debug_info) {}
+
+void PowerManagerMojoClient::UnblockSuspend(
+    const base::UnguessableToken& token) {}
 
 void PowerManagerMojoClient::CreateArcTimers(
     const std::string& tag,
@@ -152,6 +155,11 @@
     observer.KeyboardBrightnessChanged(change);
 }
 
+void PowerManagerMojoClient::SuspendDone(base::TimeDelta sleep_duration) {
+  for (auto& observer : observers_)
+    observer.SuspendDone(sleep_duration);
+}
+
 void PowerManagerMojoClient::InitAfterInterfaceBound() {
   DCHECK(controller_.is_bound());
   power::mojom::PowerManagerObserverAssociatedPtrInfo ptr_info;
diff --git a/chromeos/services/power/public/cpp/power_manager_mojo_client.h b/chromeos/services/power/public/cpp/power_manager_mojo_client.h
index bfffe88..f97f350a 100644
--- a/chromeos/services/power/public/cpp/power_manager_mojo_client.h
+++ b/chromeos/services/power/public/cpp/power_manager_mojo_client.h
@@ -59,8 +59,9 @@
   void GetInactivityDelays(
       DBusMethodCallback<power_manager::PowerManagementPolicy::Delays> callback)
       override;
-  base::OnceClosure GetSuspendReadinessCallback(
-      const base::Location& from_where) override;
+  void BlockSuspend(const base::UnguessableToken& token,
+                    const std::string& debug_info) override;
+  void UnblockSuspend(const base::UnguessableToken& token) override;
   void CreateArcTimers(
       const std::string& tag,
       std::vector<std::pair<clockid_t, base::ScopedFD>> arc_timer_requests,
@@ -78,6 +79,7 @@
       const power_manager::BacklightBrightnessChange& change) override;
   void KeyboardBrightnessChanged(
       const power_manager::BacklightBrightnessChange& change) override;
+  void SuspendDone(base::TimeDelta sleep_duration) override;
 
   power::mojom::PowerManagerControllerPtr* interface_ptr() {
     return &controller_;
diff --git a/chromeos/services/power/public/cpp/power_manager_mojo_controller.cc b/chromeos/services/power/public/cpp/power_manager_mojo_controller.cc
index 9954591..8be21bd 100644
--- a/chromeos/services/power/public/cpp/power_manager_mojo_controller.cc
+++ b/chromeos/services/power/public/cpp/power_manager_mojo_controller.cc
@@ -48,6 +48,12 @@
       std::move(callback)));
 }
 
+void PowerManagerMojoController::RequestRestart(
+    power_manager::RequestRestartReason reason,
+    const std::string& description) {
+  PowerManagerClient::Get()->RequestRestart(reason, description);
+}
+
 void PowerManagerMojoController::PowerManagerBecameAvailable(bool available) {
   client_->PowerManagerBecameAvailable(available);
 }
@@ -62,4 +68,9 @@
   client_->KeyboardBrightnessChanged(change);
 }
 
+void PowerManagerMojoController::SuspendDone(
+    const base::TimeDelta& sleep_duration) {
+  client_->SuspendDone(sleep_duration);
+}
+
 }  // namespace chromeos
diff --git a/chromeos/services/power/public/cpp/power_manager_mojo_controller.h b/chromeos/services/power/public/cpp/power_manager_mojo_controller.h
index 5e549345..b770bae 100644
--- a/chromeos/services/power/public/cpp/power_manager_mojo_controller.h
+++ b/chromeos/services/power/public/cpp/power_manager_mojo_controller.h
@@ -32,6 +32,8 @@
       const power_manager::SetBacklightBrightnessRequest& request) override;
   void GetScreenBrightnessPercent(
       GetScreenBrightnessPercentCallback callback) override;
+  void RequestRestart(power_manager::RequestRestartReason reason,
+                      const std::string& description) override;
 
   // PowerManagerClient::Observer:
   void PowerManagerBecameAvailable(bool available) override;
@@ -39,6 +41,7 @@
       const power_manager::BacklightBrightnessChange& change) override;
   void KeyboardBrightnessChanged(
       const power_manager::BacklightBrightnessChange& change) override;
+  void SuspendDone(const base::TimeDelta& sleep_duration) override;
 
  private:
   mojo::BindingSet<power::mojom::PowerManagerController> binding_set_;
diff --git a/chromeos/services/power/public/mojom/mojom_traits.cc b/chromeos/services/power/public/mojom/mojom_traits.cc
index 7533904..da5f2ca 100644
--- a/chromeos/services/power/public/mojom/mojom_traits.cc
+++ b/chromeos/services/power/public/mojom/mojom_traits.cc
@@ -7,6 +7,7 @@
 namespace mojo {
 
 using chromeos::power::mojom::BacklightBrightnessChangeCause;
+using chromeos::power::mojom::RequestRestartReason;
 using chromeos::power::mojom::SetBacklightBrightnessRequestCause;
 using chromeos::power::mojom::SetBacklightBrightnessRequestTransition;
 
@@ -199,4 +200,38 @@
   return true;
 }
 
+// static
+RequestRestartReason
+EnumTraits<RequestRestartReason, power_manager::RequestRestartReason>::ToMojom(
+    power_manager::RequestRestartReason reason) {
+  switch (reason) {
+    case power_manager::REQUEST_RESTART_FOR_USER:
+      return RequestRestartReason::kForUser;
+    case power_manager::REQUEST_RESTART_FOR_UPDATE:
+      return RequestRestartReason::kForUpdate;
+    case power_manager::REQUEST_RESTART_OTHER:
+      return RequestRestartReason::kOther;
+  }
+}
+
+// static
+bool EnumTraits<RequestRestartReason, power_manager::RequestRestartReason>::
+    FromMojom(chromeos::power::mojom::RequestRestartReason reason,
+              power_manager::RequestRestartReason* out) {
+  switch (reason) {
+    case RequestRestartReason::kForUser:
+      *out = power_manager::REQUEST_RESTART_FOR_USER;
+      return true;
+    case RequestRestartReason::kForUpdate:
+      *out = power_manager::REQUEST_RESTART_FOR_UPDATE;
+      return true;
+    case RequestRestartReason::kOther:
+      *out = power_manager::REQUEST_RESTART_OTHER;
+      return true;
+  }
+
+  NOTREACHED();
+  return false;
+}
+
 }  // namespace mojo
diff --git a/chromeos/services/power/public/mojom/mojom_traits.h b/chromeos/services/power/public/mojom/mojom_traits.h
index 3c0f3c5..47c205f 100644
--- a/chromeos/services/power/public/mojom/mojom_traits.h
+++ b/chromeos/services/power/public/mojom/mojom_traits.h
@@ -9,6 +9,7 @@
 #include "chromeos/services/power/public/mojom/power_manager.mojom-shared.h"
 #include "chromeos/services/power/public/mojom/power_manager.mojom.h"
 #include "mojo/public/cpp/bindings/enum_traits.h"
+#include "third_party/cros_system_api/dbus/power_manager/dbus-constants.h"
 
 namespace mojo {
 
@@ -83,6 +84,16 @@
       power_manager::BacklightBrightnessChange* out);
 };
 
+template <>
+struct EnumTraits<chromeos::power::mojom::RequestRestartReason,
+                  power_manager::RequestRestartReason> {
+  static chromeos::power::mojom::RequestRestartReason ToMojom(
+      power_manager::RequestRestartReason reason);
+
+  static bool FromMojom(chromeos::power::mojom::RequestRestartReason reason,
+                        power_manager::RequestRestartReason* out);
+};
+
 }  // namespace mojo
 
 #endif  // CHROMEOS_SERVICES_POWER_PUBLIC_MOJOM_MOJOM_TRAITS_H_
diff --git a/chromeos/services/power/public/mojom/power_manager.mojom b/chromeos/services/power/public/mojom/power_manager.mojom
index 6f6e18a..9c4d4ab 100644
--- a/chromeos/services/power/public/mojom/power_manager.mojom
+++ b/chromeos/services/power/public/mojom/power_manager.mojom
@@ -4,6 +4,8 @@
 
 module chromeos.power.mojom;
 
+import "mojo/public/mojom/base/time.mojom";
+
 enum SetBacklightBrightnessRequestTransition {
   kGradual,
   kInstant,
@@ -50,6 +52,12 @@
   BacklightBrightnessChangeCause cause;
 };
 
+enum RequestRestartReason {
+  kForUser,
+  kForUpdate,
+  kOther,
+};
+
 // The interface for handling out-of-process calls that query or adjust power
 // related state. The implementation relays calls to the D-Bus client, which
 // uses D-Bus to communicate with the Power Manager daemon. In Mash, this lives
@@ -61,6 +69,7 @@
   // PowerManagerClient interface. Refer to that for documentation.
   SetScreenBrightness(SetBacklightBrightnessRequest request);
   GetScreenBrightnessPercent() => (Brightness? brightness);
+  RequestRestart(RequestRestartReason reason, string description);
 };
 
 // An interface for observing changes to power state. The changes are dispatched
@@ -71,4 +80,5 @@
   PowerManagerBecameAvailable(bool available);
   ScreenBrightnessChanged(BacklightBrightnessChange change);
   KeyboardBrightnessChanged(BacklightBrightnessChange change);
+  SuspendDone(mojo_base.mojom.TimeDelta sleep_duration);
 };
diff --git a/chromeos/services/power/public/mojom/power_types.typemap b/chromeos/services/power/public/mojom/power_types.typemap
index bc7ae7bc9..d9d3599e 100644
--- a/chromeos/services/power/public/mojom/power_types.typemap
+++ b/chromeos/services/power/public/mojom/power_types.typemap
@@ -3,7 +3,10 @@
 # found in the LICENSE file.
 
 mojom = "//chromeos/services/power/public/mojom/power_manager.mojom"
-public_headers = [ "chromeos/dbus/power_manager/backlight.pb.h" ]
+public_headers = [
+  "chromeos/dbus/power_manager/backlight.pb.h",
+  "third_party/cros_system_api/dbus/power_manager/dbus-constants.h",
+]
 traits_headers = [ "//chromeos/services/power/public/mojom/mojom_traits.h" ]
 sources = [
   "//chromeos/services/power/public/mojom/mojom_traits.cc",
@@ -15,6 +18,7 @@
 type_mappings = [
   "chromeos.power.mojom.BacklightBrightnessChange=power_manager::BacklightBrightnessChange",
   "chromeos.power.mojom.BacklightBrightnessChangeCause=power_manager::BacklightBrightnessChange_Cause",
+  "chromeos.power.mojom.RequestRestartReason=power_manager::RequestRestartReason",
   "chromeos.power.mojom.SetBacklightBrightnessRequest=power_manager::SetBacklightBrightnessRequest",
   "chromeos.power.mojom.SetBacklightBrightnessRequestCause=power_manager::SetBacklightBrightnessRequest_Cause",
   "chromeos.power.mojom.SetBacklightBrightnessRequestTransition=power_manager::SetBacklightBrightnessRequest_Transition",
diff --git a/components/arc/common/net.mojom b/components/arc/common/net.mojom
index 8932523..731445ab 100644
--- a/components/arc/common/net.mojom
+++ b/components/arc/common/net.mojom
@@ -167,13 +167,18 @@
 // The physical network types exposed to ARC by the host,
 // corresponding to a subset of shill technology types defined
 // in platform2/system_api/dbus/shill/dbus-constants.h.
+// The missing shill technology types are:
+//  - WiMAX: obsolete WiFi standard, deprecated in Chrome OS in Q2 2019.
+//  - Bluetooth: ARC does not support host IP networks over Bluetooth.
+//  - PPPoE: ARC does not support host Point to Point networks.
+// Next Enum ID: 5
+// ID 4 was previously assigned to WiMAX.
 [Extensible]
 enum NetworkType {
-  CELLULAR,
-  ETHERNET,
-  VPN,
-  WIFI,
-  WIMAX,
+  CELLULAR = 0,
+  ETHERNET = 1,
+  VPN = 2,
+  WIFI = 3,
 };
 
 // Used by ARC to request a network configuration to be created on the host.
diff --git a/components/arc/net/arc_net_host_impl.cc b/components/arc/net/arc_net_host_impl.cc
index 1cf4fd78..19e5528 100644
--- a/components/arc/net/arc_net_host_impl.cc
+++ b/components/arc/net/arc_net_host_impl.cc
@@ -264,8 +264,6 @@
     DCHECK(wifi_dict);
     mojo->wifi = TranslateONCWifi(wifi_dict);
     mojo->tethering_client_state = GetWifiTetheringClientState(wifi_dict);
-  } else if (type == onc::network_type::kWimax) {
-    mojo->type = arc::mojom::NetworkType::WIMAX;
   } else {
     NOTREACHED() << "Unknown network type: " << type;
   }
diff --git a/components/arc/power/arc_power_bridge.cc b/components/arc/power/arc_power_bridge.cc
index ece21ae..b1768d0 100644
--- a/components/arc/power/arc_power_bridge.cc
+++ b/components/arc/power/arc_power_bridge.cc
@@ -174,9 +174,13 @@
   if (!power_instance)
     return;
 
-  power_instance->Suspend(
-      chromeos::PowerManagerClient::Get()->GetSuspendReadinessCallback(
-          FROM_HERE));
+  auto token = base::UnguessableToken::Create();
+  chromeos::PowerManagerClient::Get()->BlockSuspend(token, "ArcPowerBridge");
+  power_instance->Suspend(base::BindOnce(
+      [](base::UnguessableToken token) {
+        chromeos::PowerManagerClient::Get()->UnblockSuspend(token);
+      },
+      token));
 }
 
 void ArcPowerBridge::SuspendDone(const base::TimeDelta& sleep_duration) {
diff --git a/components/autofill/core/browser/accessory_sheet_data.cc b/components/autofill/core/browser/accessory_sheet_data.cc
index 75cce7f..e4ceb61 100644
--- a/components/autofill/core/browser/accessory_sheet_data.cc
+++ b/components/autofill/core/browser/accessory_sheet_data.cc
@@ -85,7 +85,8 @@
     default;
 
 bool AccessorySheetData::operator==(const AccessorySheetData& data) const {
-  return title_ == data.title_ && user_info_list_ == data.user_info_list_ &&
+  return sheet_type_ == data.sheet_type_ && title_ == data.title_ &&
+         user_info_list_ == data.user_info_list_ &&
          footer_commands_ == data.footer_commands_;
 }
 
diff --git a/components/autofill/core/browser/webdata/autofill_wallet_metadata_sync_bridge.cc b/components/autofill/core/browser/webdata/autofill_wallet_metadata_sync_bridge.cc
index 9960067..a711bc69 100644
--- a/components/autofill/core/browser/webdata/autofill_wallet_metadata_sync_bridge.cc
+++ b/components/autofill/core/browser/webdata/autofill_wallet_metadata_sync_bridge.cc
@@ -660,9 +660,15 @@
         break;
       }
       case EntityChange::ACTION_DELETE: {
-        cache_.erase(change->storage_key());
-        is_any_local_modified |= RemoveServerMetadata(
-            table, parsed_storage_key.type, parsed_storage_key.metadata_id);
+        // We intentionally ignore remote deletions in order to avoid
+        // delete-create ping pongs (if we delete metadata for address data
+        // entity that still locally exists, PDM will think the server address
+        // has not been converted to a local address yet and will trigger
+        // conversion that in turn triggers creating and committing the metadata
+        // entity again).
+        // This is safe because this client will delete the wallet_metadata
+        // entity locally as soon as the wallet_data entity gets deleted.
+        // Corner cases are handled by DeleteOldOrphanMetadata().
         break;
       }
     }
diff --git a/components/autofill/core/browser/webdata/autofill_wallet_metadata_sync_bridge_unittest.cc b/components/autofill/core/browser/webdata/autofill_wallet_metadata_sync_bridge_unittest.cc
index 6d10220e..47c76e9 100644
--- a/components/autofill/core/browser/webdata/autofill_wallet_metadata_sync_bridge_unittest.cc
+++ b/components/autofill/core/browser/webdata/autofill_wallet_metadata_sync_bridge_unittest.cc
@@ -1034,9 +1034,9 @@
                                    EqualsSpecifics(remote_card)));
 }
 
-// Test that remote deletions are properly propagated into the local database.
+// Test that remote deletions are ignored.
 TEST_F(AutofillWalletMetadataSyncBridgeTest,
-       RemoteDeletion_ShouldDeleteExistingLocalData) {
+       RemoteDeletion_ShouldNotDeleteExistingLocalData) {
   // Perform initial sync to create sync data & metadata.
   ResetBridge(/*initial_sync_done=*/false);
   WalletMetadataSpecifics profile =
@@ -1053,87 +1053,19 @@
   ASSERT_THAT(GetLocalSyncMetadataStorageKeys(),
               UnorderedElementsAre(kAddr1StorageKey, kCard1StorageKey));
 
-  // Now delete the profile. Changes should happen in the local database.
+  // Now delete the profile.
+  // We still need to commit the updated progress marker and sync metadata.
   EXPECT_CALL(*backend(), CommitChanges());
-  EXPECT_CALL(*backend(), NotifyOfMultipleAutofillChanges());
-  ReceiveTombstones({profile, card});
-
-  // Verify that both data and sync metadata is gone.
-  EXPECT_FALSE(real_processor()->IsTrackingEntityForTest(kAddr1StorageKey));
-  EXPECT_FALSE(real_processor()->IsTrackingEntityForTest(kCard1StorageKey));
-  EXPECT_THAT(GetLocalSyncMetadataStorageKeys(), IsEmpty());
-  EXPECT_THAT(GetAllLocalDataInclRestart(), IsEmpty());
-}
-
-// Test that remote deletions are properly handled even when the local data is
-// already deleted. We should still delete sync metadata both from the DB and
-// from the processor internal memory.
-TEST_F(AutofillWalletMetadataSyncBridgeTest,
-       RemoteDeletion_ShouldNotNotifyChangesWhenLocalDataIsGone) {
-  // Perform initial sync to create sync data & metadata.
-  ResetBridge(/*initial_sync_done=*/false);
-  WalletMetadataSpecifics profile =
-      CreateWalletMetadataSpecificsForAddressWithDetails(
-          kAddr1SpecificsId, /*use_count=*/10, /*use_date=*/20);
-  WalletMetadataSpecifics card =
-      CreateWalletMetadataSpecificsForCardWithDetails(
-          kCard1SpecificsId, /*use_count=*/30, /*use_date=*/40);
-  StartSyncing({profile, card});
-
-  // Clear the data from the local DB, sync metadata stays untouched both in the
-  // processor and in the local DB.
-  table()->SetServerProfiles({});
-  table()->SetServerCreditCards({});
-  ASSERT_TRUE(real_processor()->IsTrackingEntityForTest(kAddr1StorageKey));
-  ASSERT_TRUE(real_processor()->IsTrackingEntityForTest(kCard1StorageKey));
-  ASSERT_THAT(GetLocalSyncMetadataStorageKeys(),
-              UnorderedElementsAre(kAddr1StorageKey, kCard1StorageKey));
-
-  // Send deletions from the server. We should commit to write the new progress
-  // marker down.
-  EXPECT_CALL(*backend(), CommitChanges());
-  // Since the data is already deleted, it should not notify about changes. The
-  // entities should however get deleted from the processor.
+  // Changes should _not_ happen in the local autofill database.
   EXPECT_CALL(*backend(), NotifyOfMultipleAutofillChanges()).Times(0);
   ReceiveTombstones({profile, card});
 
-  // Verify that sync metadata is gone from both the processor and local DB.
+  // Verify that even though the processor does not track these entities any
+  // more and the sync metadata is gone, the actual data entities still exist in
+  // the local DB.
   EXPECT_FALSE(real_processor()->IsTrackingEntityForTest(kAddr1StorageKey));
   EXPECT_FALSE(real_processor()->IsTrackingEntityForTest(kCard1StorageKey));
   EXPECT_THAT(GetLocalSyncMetadataStorageKeys(), IsEmpty());
-  EXPECT_THAT(GetAllLocalDataInclRestart(), IsEmpty());
-}
-
-// Test that remote deletions are ignored if there is no sync metadata for the
-// entity.
-TEST_F(AutofillWalletMetadataSyncBridgeTest,
-       RemoteDeletion_ShouldNotDeleteLocalDataWithoutSyncMetadata) {
-  WalletMetadataSpecifics profile =
-      CreateWalletMetadataSpecificsForAddressWithDetails(
-          kAddr1SpecificsId, /*use_count=*/10, /*use_date=*/20);
-  WalletMetadataSpecifics card =
-      CreateWalletMetadataSpecificsForCardWithDetails(
-          kCard1SpecificsId, /*use_count=*/30, /*use_date=*/40);
-
-  table()->SetServerProfiles({CreateServerProfileFromSpecifics(profile)});
-  table()->SetServerCreditCards({CreateServerCreditCardFromSpecifics(card)});
-
-  // Do not perform initial sync. This way, no sync metadata gets created, the
-  // processor does not know about the entities and thus, it should not delete
-  // them.
-  ResetBridge(/*initial_sync_done=*/true);
-  StartSyncing({});
-  ASSERT_FALSE(real_processor()->IsTrackingEntityForTest(kAddr1StorageKey));
-  ASSERT_FALSE(real_processor()->IsTrackingEntityForTest(kCard1StorageKey));
-  ASSERT_THAT(GetLocalSyncMetadataStorageKeys(), IsEmpty());
-
-  // Send deletions from the server. We should commit to write the new progress
-  // marker down.
-  EXPECT_CALL(*backend(), CommitChanges());
-  // The actual deletion should get ignored.
-  EXPECT_CALL(*backend(), NotifyOfMultipleAutofillChanges()).Times(0);
-  ReceiveTombstones({profile, card});
-
   EXPECT_THAT(
       GetAllLocalDataInclRestart(),
       UnorderedElementsAre(EqualsSpecifics(profile), EqualsSpecifics(card)));
diff --git a/components/exo/client_controlled_shell_surface.cc b/components/exo/client_controlled_shell_surface.cc
index 10620ba..47362955 100644
--- a/components/exo/client_controlled_shell_surface.cc
+++ b/components/exo/client_controlled_shell_surface.cc
@@ -22,12 +22,12 @@
 #include "ash/shell.h"
 #include "ash/wm/client_controlled_state.h"
 #include "ash/wm/drag_details.h"
+#include "ash/wm/toplevel_window_event_handler.h"
 #include "ash/wm/window_positioning_utils.h"
 #include "ash/wm/window_properties.h"
 #include "ash/wm/window_state.h"
 #include "ash/wm/window_state_delegate.h"
 #include "ash/wm/window_util.h"
-#include "ash/wm/wm_toplevel_window_event_handler.h"
 #include "base/logging.h"
 #include "base/no_destructor.h"
 #include "base/strings/utf_string_conversions.h"
diff --git a/components/exo/shell_surface.cc b/components/exo/shell_surface.cc
index d037cd7..c5bcfff2 100644
--- a/components/exo/shell_surface.cc
+++ b/components/exo/shell_surface.cc
@@ -8,9 +8,9 @@
 #include "ash/public/cpp/window_state_type.h"
 #include "ash/shell.h"
 #include "ash/wm/desks/desks_util.h"
+#include "ash/wm/toplevel_window_event_handler.h"
 #include "ash/wm/window_resizer.h"
 #include "ash/wm/window_state.h"
-#include "ash/wm/wm_toplevel_window_event_handler.h"
 #include "base/bind.h"
 #include "base/logging.h"
 #include "base/strings/utf_string_conversions.h"
diff --git a/components/exo/shell_surface.h b/components/exo/shell_surface.h
index fb30af25..aea3623 100644
--- a/components/exo/shell_surface.h
+++ b/components/exo/shell_surface.h
@@ -5,8 +5,8 @@
 #ifndef COMPONENTS_EXO_SHELL_SURFACE_H_
 #define COMPONENTS_EXO_SHELL_SURFACE_H_
 
+#include "ash/wm/toplevel_window_event_handler.h"
 #include "ash/wm/window_state_observer.h"
-#include "ash/wm/wm_toplevel_window_event_handler.h"
 #include "base/containers/circular_deque.h"
 #include "base/macros.h"
 #include "components/exo/shell_surface_base.h"
diff --git a/components/nacl/loader/nacl_main_platform_delegate_mac.mm b/components/nacl/loader/nacl_main_platform_delegate_mac.mm
index f24dc4e..34832cb0 100644
--- a/components/nacl/loader/nacl_main_platform_delegate_mac.mm
+++ b/components/nacl/loader/nacl_main_platform_delegate_mac.mm
@@ -16,12 +16,6 @@
 
 void NaClMainPlatformDelegate::EnableSandbox(
     const content::MainFunctionParams& parameters) {
-  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
-          sandbox::switches::kSeatbeltClientName)) {
-    // Make sure the sandbox is actually enabled if the V2 flag is present.
-    CHECK(sandbox::Seatbelt::IsSandboxed());
-  } else {
-    CHECK(content::InitializeSandbox(service_manager::SANDBOX_TYPE_NACL_LOADER))
-        << "Error initializing sandbox for " << switches::kNaClLoaderProcess;
-  }
+  // The sandbox on macOS is enabled as soon as main() executes, so there is
+  // nothing to do here.
 }
diff --git a/components/omnibox/browser/autocomplete_match.cc b/components/omnibox/browser/autocomplete_match.cc
index 78432c9..15f8b68 100644
--- a/components/omnibox/browser/autocomplete_match.cc
+++ b/components/omnibox/browser/autocomplete_match.cc
@@ -738,8 +738,15 @@
 void AutocompleteMatch::ComputeStrippedDestinationURL(
     const AutocompleteInput& input,
     TemplateURLService* template_url_service) {
-  stripped_destination_url =
-      GURLToStrippedGURL(destination_url, input, template_url_service, keyword);
+  // Other than document suggestions, computing |stripped_destination_url| will
+  // have the same result during a match's lifecycle, so it's safe to skip
+  // re-computing it if it's already computed. Document suggestions'
+  // |stripped_url|s are pre-computed by the document provider, and overwriting
+  // them here would prevent potential deduping.
+  if (stripped_destination_url.is_empty()) {
+    stripped_destination_url = GURLToStrippedGURL(
+        destination_url, input, template_url_service, keyword);
+  }
 }
 
 void AutocompleteMatch::GetKeywordUIState(
diff --git a/components/password_manager/core/browser/sync/password_sync_bridge.cc b/components/password_manager/core/browser/sync/password_sync_bridge.cc
index ddeb0b09..5d26b87d 100644
--- a/components/password_manager/core/browser/sync/password_sync_bridge.cc
+++ b/components/password_manager/core/browser/sync/password_sync_bridge.cc
@@ -545,6 +545,12 @@
               metadata_change_list.get());
           break;
         case syncer::EntityChange::ACTION_UPDATE:
+          // TODO(mamir): This had been added to mitigate some potential issues
+          // in the login database. Once the underlying cause is verified, we
+          // should remove this check.
+          if (entity_change->storage_key().empty()) {
+            continue;
+          }
           changes = password_store_sync_->UpdateLoginSync(
               PasswordFromEntityChange(*entity_change, /*sync_time=*/time_now));
           if (changes.empty()) {
@@ -558,6 +564,12 @@
                  ParsePrimaryKey(entity_change->storage_key()));
           break;
         case syncer::EntityChange::ACTION_DELETE: {
+          // TODO(mamir): This had been added to mitigate some potential issues
+          // in the login database. Once the underlying cause is verified, we
+          // should remove this check.
+          if (entity_change->storage_key().empty()) {
+            continue;
+          }
           int primary_key = ParsePrimaryKey(entity_change->storage_key());
           changes =
               password_store_sync_->RemoveLoginByPrimaryKeySync(primary_key);
diff --git a/components/payments/content/BUILD.gn b/components/payments/content/BUILD.gn
index 0c55166..67fc6a0e 100644
--- a/components/payments/content/BUILD.gn
+++ b/components/payments/content/BUILD.gn
@@ -83,6 +83,7 @@
   deps = [
     ":content_common",
     "//components/autofill/core/browser",
+    "//components/payments/content/icon",
     "//components/payments/content/utility",
     "//components/payments/core",
     "//components/strings",
diff --git a/components/payments/content/icon/BUILD.gn b/components/payments/content/icon/BUILD.gn
new file mode 100644
index 0000000..6054837f
--- /dev/null
+++ b/components/payments/content/icon/BUILD.gn
@@ -0,0 +1,16 @@
+# 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/jumbo.gni")
+
+jumbo_static_library("icon") {
+  sources = [
+    "icon_size.cc",
+    "icon_size.h",
+  ]
+
+  deps = [
+    "//ui/display",
+  ]
+}
diff --git a/components/payments/content/icon/DEPS b/components/payments/content/icon/DEPS
new file mode 100644
index 0000000..6446557
--- /dev/null
+++ b/components/payments/content/icon/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  "+ui/display",
+]
diff --git a/components/payments/content/icon/icon_size.cc b/components/payments/content/icon/icon_size.cc
new file mode 100644
index 0000000..afc9832
--- /dev/null
+++ b/components/payments/content/icon/icon_size.cc
@@ -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.
+#include "components/payments/content/icon/icon_size.h"
+
+#include "ui/display/display.h"
+#include "ui/display/screen.h"
+
+namespace payments {
+
+namespace {
+
+float DeviceScaleFactor(gfx::NativeView view) {
+  display::Screen* screen = display::Screen::GetScreen();
+  DCHECK(screen);
+  return screen->GetDisplayNearestView(view).device_scale_factor();
+}
+
+}  // namespace
+
+// static
+int IconSizeCalculator::IdealIconHeight(gfx::NativeView view) {
+  return DeviceScaleFactor(view) * kPaymentAppDeviceIndependentIdealIconHeight;
+}
+
+// static
+int IconSizeCalculator::MinimumIconHeight() {
+  return 0;
+}
+
+}  // namespace payments
diff --git a/components/payments/content/icon/icon_size.h b/components/payments/content/icon/icon_size.h
new file mode 100644
index 0000000..141932b
--- /dev/null
+++ b/components/payments/content/icon/icon_size.h
@@ -0,0 +1,27 @@
+// 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 COMPONENTS_PAYMENTS_CONTENT_ICON_ICON_SIZE_H_
+#define COMPONENTS_PAYMENTS_CONTENT_ICON_ICON_SIZE_H_
+
+#include "base/macros.h"
+#include "ui/gfx/native_widget_types.h"
+
+namespace payments {
+
+// Calculates the ideal height in pixels for payment icons depending on the
+// screen resolution (32 * device_scale_factor).
+class IconSizeCalculator final {
+ public:
+  static int IdealIconHeight(gfx::NativeView view);
+  static int MinimumIconHeight();
+  static constexpr int kPaymentAppDeviceIndependentIdealIconHeight = 32;
+
+ private:
+  DISALLOW_IMPLICIT_CONSTRUCTORS(IconSizeCalculator);
+};
+
+}  // namespace payments
+
+#endif  // COMPONENTS_PAYMENTS_CONTENT_ICON_ICON_SIZE_H_
diff --git a/components/payments/content/installable_payment_app_crawler.cc b/components/payments/content/installable_payment_app_crawler.cc
index bca52aad..da415de 100644
--- a/components/payments/content/installable_payment_app_crawler.cc
+++ b/components/payments/content/installable_payment_app_crawler.cc
@@ -9,6 +9,7 @@
 #include "base/bind.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/task/post_task.h"
+#include "components/payments/content/icon/icon_size.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
@@ -372,22 +373,6 @@
     return;
   }
 
-  // TODO(crbug.com/782270): Choose appropriate icon size dynamically on
-  // different platforms.
-  const int kPaymentAppIdealIconSize = 32;
-  const int kPaymentAppMinimumIconSize = 0;
-  GURL best_icon_url = blink::ManifestIconSelector::FindBestMatchingIcon(
-      manifest_icons, kPaymentAppIdealIconSize, kPaymentAppMinimumIconSize,
-      content::ManifestIconDownloader::kMaxWidthToHeightRatio,
-      blink::Manifest::ImageResource::Purpose::ANY);
-  if (!best_icon_url.is_valid()) {
-    log_.Error("No suitable icon found in web app manifest \"" +
-               web_app_manifest_url.spec() +
-               "\" for payment handler manifest \"" +
-               method_manifest_url.spec() + "\".");
-    return;
-  }
-
   // Stop if the web_contents is gone.
   if (web_contents() == nullptr) {
     log_.Error(
@@ -398,10 +383,25 @@
     return;
   }
 
+  gfx::NativeView native_view = web_contents()->GetNativeView();
+  GURL best_icon_url = blink::ManifestIconSelector::FindBestMatchingIcon(
+      manifest_icons, IconSizeCalculator::IdealIconHeight(native_view),
+      IconSizeCalculator::MinimumIconHeight(),
+      content::ManifestIconDownloader::kMaxWidthToHeightRatio,
+      blink::Manifest::ImageResource::Purpose::ANY);
+  if (!best_icon_url.is_valid()) {
+    log_.Error("No suitable icon found in web app manifest \"" +
+               web_app_manifest_url.spec() +
+               "\" for payment handler manifest \"" +
+               method_manifest_url.spec() + "\".");
+    return;
+  }
+
   number_of_web_app_icons_to_download_and_decode_++;
   bool can_download_icon = content::ManifestIconDownloader::Download(
-      web_contents(), best_icon_url, kPaymentAppIdealIconSize,
-      kPaymentAppMinimumIconSize,
+      web_contents(), best_icon_url,
+      IconSizeCalculator::IdealIconHeight(native_view),
+      IconSizeCalculator::MinimumIconHeight(),
       base::BindOnce(
           &InstallablePaymentAppCrawler::OnPaymentWebAppIconDownloadAndDecoded,
           weak_ptr_factory_.GetWeakPtr(), method_manifest_url,
diff --git a/components/sync/nigori/nigori_model_type_processor.cc b/components/sync/nigori/nigori_model_type_processor.cc
index cc78a52..3516cb0 100644
--- a/components/sync/nigori/nigori_model_type_processor.cc
+++ b/components/sync/nigori/nigori_model_type_processor.cc
@@ -5,11 +5,14 @@
 #include "components/sync/nigori/nigori_model_type_processor.h"
 
 #include "base/threading/sequenced_task_runner_handle.h"
+#include "components/sync/base/data_type_histogram.h"
 #include "components/sync/base/time.h"
 #include "components/sync/engine/commit_queue.h"
 #include "components/sync/engine/model_type_processor_proxy.h"
 #include "components/sync/model_impl/processor_entity.h"
 #include "components/sync/nigori/nigori_sync_bridge.h"
+#include "components/sync/protocol/proto_memory_estimations.h"
+#include "components/sync/protocol/proto_value_conversions.h"
 
 namespace syncer {
 
@@ -231,18 +234,58 @@
 void NigoriModelTypeProcessor::GetAllNodesForDebugging(
     AllNodesCallback callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  NOTIMPLEMENTED();
+
+  std::unique_ptr<base::DictionaryValue> root_node;
+  std::unique_ptr<EntityData> entity_data = bridge_->GetData();
+  if (entity_data) {
+    if (entity_) {
+      const sync_pb::EntityMetadata& metadata = entity_->metadata();
+      // Set id value as directory, "s" means server.
+      entity_data->id = "s" + metadata.server_id();
+      entity_data->creation_time = ProtoTimeToTime(metadata.creation_time());
+      entity_data->modification_time =
+          ProtoTimeToTime(metadata.modification_time());
+    }
+    root_node = entity_data->ToDictionaryValue();
+    if (entity_) {
+      root_node->Set("metadata", EntityMetadataToValue(entity_->metadata()));
+    }
+  } else {
+    root_node = std::make_unique<base::DictionaryValue>();
+  }
+
+  // Function isTypeRootNode in sync_node_browser.js use PARENT_ID and
+  // UNIQUE_SERVER_TAG to check if the node is root node. isChildOf in
+  // sync_node_browser.js uses modelType to check if root node is parent of real
+  // data node. NON_UNIQUE_NAME will be the name of node to display.
+  root_node->SetString("ID", "NIGORI_ROOT");
+  root_node->SetString("PARENT_ID", "r");
+  root_node->SetString("UNIQUE_SERVER_TAG", "Nigori");
+  root_node->SetBoolean("IS_DIR", false);
+  root_node->SetString("modelType", "Nigori");
+  root_node->SetString("NON_UNIQUE_NAME", "Nigori");
+
+  auto all_nodes = std::make_unique<base::ListValue>();
+  all_nodes->Append(std::move(root_node));
+  std::move(callback).Run(syncer::NIGORI, std::move(all_nodes));
 }
 
 void NigoriModelTypeProcessor::GetStatusCountersForDebugging(
     StatusCountersCallback callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  NOTIMPLEMENTED();
+  StatusCounters counters;
+  counters.num_entries = entity_ ? 1 : 0;
+  counters.num_entries_and_tombstones = counters.num_entries;
+  std::move(callback).Run(syncer::NIGORI, counters);
 }
 
 void NigoriModelTypeProcessor::RecordMemoryUsageAndCountsHistograms() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  NOTIMPLEMENTED();
+  size_t memory_usage = 0;
+  memory_usage += EstimateMemoryUsage(model_type_state_);
+  memory_usage += entity_ ? entity_->EstimateMemoryUsage() : 0;
+  SyncRecordModelTypeMemoryHistogram(ModelType::NIGORI, memory_usage);
+  SyncRecordModelTypeCountHistogram(ModelType::NIGORI, entity_ ? 1 : 0);
 }
 
 void NigoriModelTypeProcessor::ModelReadyToSync(
diff --git a/components/tracing/common/trace_startup_config.cc b/components/tracing/common/trace_startup_config.cc
index d21058d..7e984cb 100644
--- a/components/tracing/common/trace_startup_config.cc
+++ b/components/tracing/common/trace_startup_config.cc
@@ -242,12 +242,10 @@
 
 bool TraceStartupConfig::EnableFromBackgroundTracing() {
 #if defined(OS_ANDROID)
-  // Tests can enable this value.
   is_enabled_from_background_tracing_ =
-      is_enabled_from_background_tracing_ ||
       base::android::GetBackgroundStartupTracingFlag();
 #else
-  // TODO(ssid): Implement saving setting to preference for next startup.
+  is_enabled_from_background_tracing_ = false;
 #endif
   // Do not set the flag to false if it's not enabled unnecessarily.
   if (!is_enabled_from_background_tracing_)
diff --git a/components/tracing/common/trace_startup_config.h b/components/tracing/common/trace_startup_config.h
index be13970..a1e4e34 100644
--- a/components/tracing/common/trace_startup_config.h
+++ b/components/tracing/common/trace_startup_config.h
@@ -17,8 +17,7 @@
 }  // namespace base
 
 namespace content {
-class CommandlineStartupTracingTest;
-class BackgroundStartupTracingTest;
+class StartupTracingControllerTest;
 }
 
 namespace tracing {
@@ -140,8 +139,7 @@
   // This allows constructor and destructor to be private and usable only
   // by the Singleton class.
   friend struct base::DefaultSingletonTraits<TraceStartupConfig>;
-  friend class content::CommandlineStartupTracingTest;
-  friend class content::BackgroundStartupTracingTest;
+  friend class content::StartupTracingControllerTest;
 
   TraceStartupConfig();
   ~TraceStartupConfig();
diff --git a/components/viz/service/compositor_frame_fuzzer/fuzzer_software_display_provider.cc b/components/viz/service/compositor_frame_fuzzer/fuzzer_software_display_provider.cc
index b55ef3e41..53ed9e7 100644
--- a/components/viz/service/compositor_frame_fuzzer/fuzzer_software_display_provider.cc
+++ b/components/viz/service/compositor_frame_fuzzer/fuzzer_software_display_provider.cc
@@ -105,7 +105,7 @@
                     : std::make_unique<SoftwareOutputDevice>();
 
   auto output_surface = std::make_unique<SoftwareOutputSurface>(
-      std::move(software_output_device), std::move(update_vsync_callback));
+      std::move(software_output_device));
 
   auto scheduler = std::make_unique<DisplayScheduler>(
       begin_frame_source_.get(), task_runner.get(),
diff --git a/components/viz/service/display/gl_renderer.cc b/components/viz/service/display/gl_renderer.cc
index 488edae2..faa61ce 100644
--- a/components/viz/service/display/gl_renderer.cc
+++ b/components/viz/service/display/gl_renderer.cc
@@ -1220,7 +1220,7 @@
       filter->asColorFilter(&colorfilter_rawptr);
       sk_sp<SkColorFilter> cf(colorfilter_rawptr);
 
-      if (cf && cf->asColorMatrix(params->color_matrix)) {
+      if (cf && cf->asAColorMatrix(params->color_matrix)) {
         // We have a color matrix at the root of the filter DAG; apply it
         // locally in the compositor and process the rest of the DAG (if any)
         // in Skia.
@@ -1453,11 +1453,10 @@
                           matrix);
   }
 
-  static const float kScale = 1.0f / 255.0f;
   if (current_program_->color_offset_location() != -1) {
     float offset[4];
     for (int i = 0; i < 4; ++i)
-      offset[i] = SkScalarToFloat(params->color_matrix[i * 5 + 4]) * kScale;
+      offset[i] = params->color_matrix[i * 5 + 4];
 
     gl_->Uniform4fv(current_program_->color_offset_location(), 1, offset);
   }
diff --git a/components/viz/service/display/gl_renderer_unittest.cc b/components/viz/service/display/gl_renderer_unittest.cc
index 1a4771a..b243a54 100644
--- a/components/viz/service/display/gl_renderer_unittest.cc
+++ b/components/viz/service/display/gl_renderer_unittest.cc
@@ -1787,7 +1787,7 @@
                                              child_context_provider_.get());
   ResourceId mapped_mask = resource_map[mask];
 
-  SkScalar matrix[20];
+  float matrix[20];
   float amount = 0.5f;
   matrix[0] = 0.213f + 0.787f * amount;
   matrix[1] = 0.715f - 0.715f * amount;
@@ -1805,8 +1805,8 @@
   matrix[18] = 1;
   cc::FilterOperations filters;
   filters.Append(cc::FilterOperation::CreateReferenceFilter(
-      sk_make_sp<cc::ColorFilterPaintFilter>(
-          SkColorFilters::MatrixRowMajor255(matrix), nullptr)));
+      sk_make_sp<cc::ColorFilterPaintFilter>(SkColorFilters::Matrix(matrix),
+                                             nullptr)));
 
   gfx::Transform transform_causing_aa;
   transform_causing_aa.Rotate(20.0);
@@ -2081,6 +2081,8 @@
   MOCK_CONST_METHOD0(GetOverlayBufferFormat, gfx::BufferFormat());
   MOCK_CONST_METHOD0(HasExternalStencilTest, bool());
   MOCK_METHOD0(ApplyExternalStencil, void());
+  MOCK_METHOD1(SetUpdateVSyncParametersCallback,
+               void(UpdateVSyncParametersCallback));
 };
 
 class MockOutputSurfaceTest : public GLRendererTest {
diff --git a/components/viz/service/display/output_surface.h b/components/viz/service/display/output_surface.h
index 00f7168..fe6bd91 100644
--- a/components/viz/service/display/output_surface.h
+++ b/components/viz/service/display/output_surface.h
@@ -11,6 +11,7 @@
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "base/threading/thread_checker.h"
+#include "components/viz/common/display/update_vsync_parameters_callback.h"
 #include "components/viz/common/gpu/context_provider.h"
 #include "components/viz/common/resources/returned_resource.h"
 #include "components/viz/service/display/overlay_candidate_validator.h"
@@ -49,7 +50,7 @@
     // Note: HasExternalStencilTest() must return false when an output surface
     // has been configured for stencil usage.
     bool supports_stencil = false;
-    // Whether this OutputSurface suppotrs post sub buffer or not.
+    // Whether this OutputSurface supports post sub buffer or not.
     bool supports_post_sub_buffer = false;
   };
 
@@ -132,6 +133,11 @@
   // can be passed directly to any related extension functions.
   virtual unsigned UpdateGpuFence() = 0;
 
+  // Sets callback to receive updated vsync parameters after SwapBuffers() if
+  // supported.
+  virtual void SetUpdateVSyncParametersCallback(
+      UpdateVSyncParametersCallback callback) = 0;
+
   // If set to true, the OutputSurface must deliver
   // OutputSurfaceclient::DidSwapWithSize notifications to its client.
   // OutputSurfaces which support delivering swap size notifications should
diff --git a/components/viz/service/display/overlay_unittest.cc b/components/viz/service/display/overlay_unittest.cc
index f0a7521..0923324 100644
--- a/components/viz/service/display/overlay_unittest.cc
+++ b/components/viz/service/display/overlay_unittest.cc
@@ -229,6 +229,8 @@
     return gfx::BufferFormat::RGBX_8888;
   }
   unsigned UpdateGpuFence() override { return 0; }
+  void SetUpdateVSyncParametersCallback(
+      UpdateVSyncParametersCallback callback) override {}
 
   void set_is_displayed_as_overlay_plane(bool value) {
     is_displayed_as_overlay_plane_ = value;
diff --git a/components/viz/service/display/renderer_pixeltest.cc b/components/viz/service/display/renderer_pixeltest.cc
index 00ff891f7..a855b21 100644
--- a/components/viz/service/display/renderer_pixeltest.cc
+++ b/components/viz/service/display/renderer_pixeltest.cc
@@ -2061,7 +2061,7 @@
   int child_pass_id = 2;
   gfx::Rect pass_rect(this->device_viewport_size_);
   gfx::Transform transform_to_root;
-  SkScalar matrix[20];
+  float matrix[20];
   float amount = 0.5f;
   matrix[0] = 0.213f + 0.787f * amount;
   matrix[1] = 0.715f - 0.715f * amount;
@@ -2079,8 +2079,8 @@
   matrix[18] = 1;
   cc::FilterOperations filters;
   filters.Append(cc::FilterOperation::CreateReferenceFilter(
-      sk_make_sp<cc::ColorFilterPaintFilter>(
-          SkColorFilters::MatrixRowMajor255(matrix), nullptr)));
+      sk_make_sp<cc::ColorFilterPaintFilter>(SkColorFilters::Matrix(matrix),
+                                             nullptr)));
 
   std::unique_ptr<RenderPass> child_pass =
       CreateTestRenderPass(child_pass_id, pass_rect, transform_to_root);
@@ -2261,29 +2261,29 @@
   int child_pass_id = 2;
   gfx::Rect pass_rect(this->device_viewport_size_);
   gfx::Transform transform_to_root;
-  SkScalar matrix[20];
+  float matrix[20];
   float amount = 0.5f;
   matrix[0] = 0.213f + 0.787f * amount;
   matrix[1] = 0.715f - 0.715f * amount;
   matrix[2] = 1.f - (matrix[0] + matrix[1]);
   matrix[3] = 0;
-  matrix[4] = 20.f;
+  matrix[4] = 20.f / 255;
   matrix[5] = 0.213f - 0.213f * amount;
   matrix[6] = 0.715f + 0.285f * amount;
   matrix[7] = 1.f - (matrix[5] + matrix[6]);
   matrix[8] = 0;
-  matrix[9] = 200.f;
+  matrix[9] = 200.f / 255;
   matrix[10] = 0.213f - 0.213f * amount;
   matrix[11] = 0.715f - 0.715f * amount;
   matrix[12] = 1.f - (matrix[10] + matrix[11]);
   matrix[13] = 0;
-  matrix[14] = 1.5f;
+  matrix[14] = 1.5f / 255;
   matrix[15] = matrix[16] = matrix[17] = matrix[19] = 0;
   matrix[18] = 1;
   cc::FilterOperations filters;
   filters.Append(cc::FilterOperation::CreateReferenceFilter(
-      sk_make_sp<cc::ColorFilterPaintFilter>(
-          SkColorFilters::MatrixRowMajor255(matrix), nullptr)));
+      sk_make_sp<cc::ColorFilterPaintFilter>(SkColorFilters::Matrix(matrix),
+                                             nullptr)));
 
   std::unique_ptr<RenderPass> child_pass =
       CreateTestRenderPass(child_pass_id, pass_rect, transform_to_root);
diff --git a/components/viz/service/display_embedder/gl_output_surface.cc b/components/viz/service/display_embedder/gl_output_surface.cc
index 80f1c450..117363e5 100644
--- a/components/viz/service/display_embedder/gl_output_surface.cc
+++ b/components/viz/service/display_embedder/gl_output_surface.cc
@@ -22,10 +22,9 @@
 namespace viz {
 
 GLOutputSurface::GLOutputSurface(
-    scoped_refptr<VizProcessContextProvider> context_provider,
-    UpdateVSyncParametersCallback update_vsync_callback)
+    scoped_refptr<VizProcessContextProvider> context_provider)
     : OutputSurface(context_provider),
-      wants_vsync_parameter_updates_(!update_vsync_callback.is_null()),
+      viz_context_provider_(context_provider),
       use_gpu_fence_(
           context_provider->ContextCapabilities().chromium_gpu_fence &&
           context_provider->ContextCapabilities()
@@ -40,8 +39,6 @@
   // can use.
   capabilities_.max_frames_pending =
       context_provider->ContextCapabilities().num_surface_buffers - 1;
-  context_provider->SetUpdateVSyncParametersCallback(
-      std::move(update_vsync_callback));
 }
 
 GLOutputSurface::~GLOutputSurface() {
@@ -202,4 +199,10 @@
   needs_swap_size_notifications_ = needs_swap_size_notifications;
 }
 
+void GLOutputSurface::SetUpdateVSyncParametersCallback(
+    UpdateVSyncParametersCallback callback) {
+  wants_vsync_parameter_updates_ = !callback.is_null();
+  viz_context_provider_->SetUpdateVSyncParametersCallback(std::move(callback));
+}
+
 }  // namespace viz
diff --git a/components/viz/service/display_embedder/gl_output_surface.h b/components/viz/service/display_embedder/gl_output_surface.h
index 3789fdb0..1820e59 100644
--- a/components/viz/service/display_embedder/gl_output_surface.h
+++ b/components/viz/service/display_embedder/gl_output_surface.h
@@ -19,8 +19,8 @@
 // swaps to an actual GL surface.
 class GLOutputSurface : public OutputSurface {
  public:
-  GLOutputSurface(scoped_refptr<VizProcessContextProvider> context_provider,
-                  UpdateVSyncParametersCallback update_vsync_callback);
+  explicit GLOutputSurface(
+      scoped_refptr<VizProcessContextProvider> context_provider);
   ~GLOutputSurface() override;
 
   // OutputSurface implementation
@@ -45,6 +45,8 @@
   unsigned UpdateGpuFence() override;
   void SetNeedsSwapSizeNotifications(
       bool needs_swap_size_notifications) override;
+  void SetUpdateVSyncParametersCallback(
+      UpdateVSyncParametersCallback callback) override;
 
  protected:
   OutputSurfaceClient* client() const { return client_; }
@@ -69,8 +71,9 @@
                                  const gpu::SwapBuffersCompleteParams& params);
   void OnPresentation(const gfx::PresentationFeedback& feedback);
 
+  scoped_refptr<VizProcessContextProvider> viz_context_provider_;
   OutputSurfaceClient* client_ = nullptr;
-  const bool wants_vsync_parameter_updates_;
+  bool wants_vsync_parameter_updates_ = false;
   ui::LatencyTracker latency_tracker_;
 
   bool set_draw_rectangle_for_frame_ = false;
diff --git a/components/viz/service/display_embedder/gl_output_surface_android.cc b/components/viz/service/display_embedder/gl_output_surface_android.cc
index 4bee327..18397c09 100644
--- a/components/viz/service/display_embedder/gl_output_surface_android.cc
+++ b/components/viz/service/display_embedder/gl_output_surface_android.cc
@@ -10,9 +10,8 @@
 
 GLOutputSurfaceAndroid::GLOutputSurfaceAndroid(
     scoped_refptr<VizProcessContextProvider> context_provider,
-    UpdateVSyncParametersCallback update_vsync_callback,
     bool allow_overlays)
-    : GLOutputSurface(context_provider, std::move(update_vsync_callback)) {
+    : GLOutputSurface(context_provider) {
   if (allow_overlays) {
     overlay_candidate_validator_ =
         std::make_unique<CompositorOverlayCandidateValidatorAndroid>();
diff --git a/components/viz/service/display_embedder/gl_output_surface_android.h b/components/viz/service/display_embedder/gl_output_surface_android.h
index 759d800..439838b 100644
--- a/components/viz/service/display_embedder/gl_output_surface_android.h
+++ b/components/viz/service/display_embedder/gl_output_surface_android.h
@@ -16,7 +16,6 @@
  public:
   GLOutputSurfaceAndroid(
       scoped_refptr<VizProcessContextProvider> context_provider,
-      UpdateVSyncParametersCallback update_vsync_callback,
       bool allow_overlays);
   ~GLOutputSurfaceAndroid() override;
 
diff --git a/components/viz/service/display_embedder/gl_output_surface_buffer_queue.cc b/components/viz/service/display_embedder/gl_output_surface_buffer_queue.cc
index 5b61a39..e367d73 100644
--- a/components/viz/service/display_embedder/gl_output_surface_buffer_queue.cc
+++ b/components/viz/service/display_embedder/gl_output_surface_buffer_queue.cc
@@ -20,10 +20,9 @@
 GLOutputSurfaceBufferQueue::GLOutputSurfaceBufferQueue(
     scoped_refptr<VizProcessContextProvider> context_provider,
     gpu::SurfaceHandle surface_handle,
-    UpdateVSyncParametersCallback update_vsync_callback,
     gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
     gfx::BufferFormat buffer_format)
-    : GLOutputSurface(context_provider, std::move(update_vsync_callback)) {
+    : GLOutputSurface(context_provider) {
   capabilities_.uses_default_gl_framebuffer = false;
   capabilities_.flipped_output_surface = true;
   // Set |max_frames_pending| to 2 for buffer_queue, which aligns scheduling
diff --git a/components/viz/service/display_embedder/gl_output_surface_buffer_queue.h b/components/viz/service/display_embedder/gl_output_surface_buffer_queue.h
index 6843772a4..ce392a3 100644
--- a/components/viz/service/display_embedder/gl_output_surface_buffer_queue.h
+++ b/components/viz/service/display_embedder/gl_output_surface_buffer_queue.h
@@ -34,7 +34,6 @@
   GLOutputSurfaceBufferQueue(
       scoped_refptr<VizProcessContextProvider> context_provider,
       gpu::SurfaceHandle surface_handle,
-      UpdateVSyncParametersCallback update_vsync_callback,
       gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
       gfx::BufferFormat buffer_format);
 
diff --git a/components/viz/service/display_embedder/gl_output_surface_buffer_queue_android.cc b/components/viz/service/display_embedder/gl_output_surface_buffer_queue_android.cc
index 8269aad1..4ab76d8 100644
--- a/components/viz/service/display_embedder/gl_output_surface_buffer_queue_android.cc
+++ b/components/viz/service/display_embedder/gl_output_surface_buffer_queue_android.cc
@@ -11,12 +11,10 @@
 GLOutputSurfaceBufferQueueAndroid::GLOutputSurfaceBufferQueueAndroid(
     scoped_refptr<VizProcessContextProvider> context_provider,
     gpu::SurfaceHandle surface_handle,
-    UpdateVSyncParametersCallback update_vsync_callback,
     gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
     gfx::BufferFormat buffer_format)
     : GLOutputSurfaceBufferQueue(context_provider,
                                  surface_handle,
-                                 std::move(update_vsync_callback),
                                  gpu_memory_buffer_manager,
                                  buffer_format) {}
 
diff --git a/components/viz/service/display_embedder/gl_output_surface_buffer_queue_android.h b/components/viz/service/display_embedder/gl_output_surface_buffer_queue_android.h
index 879f5c0..ebda140 100644
--- a/components/viz/service/display_embedder/gl_output_surface_buffer_queue_android.h
+++ b/components/viz/service/display_embedder/gl_output_surface_buffer_queue_android.h
@@ -17,7 +17,6 @@
   GLOutputSurfaceBufferQueueAndroid(
       scoped_refptr<VizProcessContextProvider> context_provider,
       gpu::SurfaceHandle surface_handle,
-      UpdateVSyncParametersCallback update_vsync_callback,
       gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
       gfx::BufferFormat buffer_format);
   ~GLOutputSurfaceBufferQueueAndroid() override;
diff --git a/components/viz/service/display_embedder/gl_output_surface_mac.cc b/components/viz/service/display_embedder/gl_output_surface_mac.cc
index 1c4f94d..f306ae94 100644
--- a/components/viz/service/display_embedder/gl_output_surface_mac.cc
+++ b/components/viz/service/display_embedder/gl_output_surface_mac.cc
@@ -11,12 +11,10 @@
 GLOutputSurfaceMac::GLOutputSurfaceMac(
     scoped_refptr<VizProcessContextProvider> context_provider,
     gpu::SurfaceHandle surface_handle,
-    UpdateVSyncParametersCallback update_vsync_callback,
     gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
     bool allow_overlays)
     : GLOutputSurfaceBufferQueue(context_provider,
                                  surface_handle,
-                                 std::move(update_vsync_callback),
                                  gpu_memory_buffer_manager,
                                  gfx::BufferFormat::RGBA_8888),
       overlay_validator_(
diff --git a/components/viz/service/display_embedder/gl_output_surface_mac.h b/components/viz/service/display_embedder/gl_output_surface_mac.h
index 093b1ad5..077be04 100644
--- a/components/viz/service/display_embedder/gl_output_surface_mac.h
+++ b/components/viz/service/display_embedder/gl_output_surface_mac.h
@@ -14,7 +14,6 @@
  public:
   GLOutputSurfaceMac(scoped_refptr<VizProcessContextProvider> context_provider,
                      gpu::SurfaceHandle surface_handle,
-                     UpdateVSyncParametersCallback update_vsync_callback,
                      gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
                      bool allow_overlays);
   ~GLOutputSurfaceMac() override;
diff --git a/components/viz/service/display_embedder/gl_output_surface_offscreen.cc b/components/viz/service/display_embedder/gl_output_surface_offscreen.cc
index 5cd270a..2ef563d 100644
--- a/components/viz/service/display_embedder/gl_output_surface_offscreen.cc
+++ b/components/viz/service/display_embedder/gl_output_surface_offscreen.cc
@@ -24,10 +24,8 @@
 }  // namespace
 
 GLOutputSurfaceOffscreen::GLOutputSurfaceOffscreen(
-    scoped_refptr<VizProcessContextProvider> context_provider,
-    UpdateVSyncParametersCallback update_vsync_callback)
-    : GLOutputSurface(context_provider, std::move(update_vsync_callback)),
-      weak_ptr_factory_(this) {}
+    scoped_refptr<VizProcessContextProvider> context_provider)
+    : GLOutputSurface(context_provider), weak_ptr_factory_(this) {}
 
 GLOutputSurfaceOffscreen::~GLOutputSurfaceOffscreen() {}
 
diff --git a/components/viz/service/display_embedder/gl_output_surface_offscreen.h b/components/viz/service/display_embedder/gl_output_surface_offscreen.h
index c39018b..fa6ca15 100644
--- a/components/viz/service/display_embedder/gl_output_surface_offscreen.h
+++ b/components/viz/service/display_embedder/gl_output_surface_offscreen.h
@@ -18,9 +18,8 @@
 // framebuffer.
 class GLOutputSurfaceOffscreen : public GLOutputSurface {
  public:
-  GLOutputSurfaceOffscreen(
-      scoped_refptr<VizProcessContextProvider> context_provider,
-      UpdateVSyncParametersCallback update_vsync_callback);
+  explicit GLOutputSurfaceOffscreen(
+      scoped_refptr<VizProcessContextProvider> context_provider);
   ~GLOutputSurfaceOffscreen() override;
 
   // OutputSurface implementation.
diff --git a/components/viz/service/display_embedder/gl_output_surface_ozone.cc b/components/viz/service/display_embedder/gl_output_surface_ozone.cc
index cd217c8..af942123 100644
--- a/components/viz/service/display_embedder/gl_output_surface_ozone.cc
+++ b/components/viz/service/display_embedder/gl_output_surface_ozone.cc
@@ -17,12 +17,10 @@
 GLOutputSurfaceOzone::GLOutputSurfaceOzone(
     scoped_refptr<VizProcessContextProvider> context_provider,
     gpu::SurfaceHandle surface_handle,
-    UpdateVSyncParametersCallback update_vsync_callback,
     gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
     std::vector<OverlayStrategy> strategies)
     : GLOutputSurfaceBufferQueue(context_provider,
                                  surface_handle,
-                                 std::move(update_vsync_callback),
                                  gpu_memory_buffer_manager,
                                  display::DisplaySnapshot::PrimaryFormat()) {
   if (!strategies.empty()) {
diff --git a/components/viz/service/display_embedder/gl_output_surface_ozone.h b/components/viz/service/display_embedder/gl_output_surface_ozone.h
index 1676c49..ba4b2f3 100644
--- a/components/viz/service/display_embedder/gl_output_surface_ozone.h
+++ b/components/viz/service/display_embedder/gl_output_surface_ozone.h
@@ -20,7 +20,6 @@
   GLOutputSurfaceOzone(
       scoped_refptr<VizProcessContextProvider> context_provider,
       gpu::SurfaceHandle surface_handle,
-      UpdateVSyncParametersCallback update_vsync_callback,
       gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
       std::vector<OverlayStrategy> strategies);
   ~GLOutputSurfaceOzone() override;
diff --git a/components/viz/service/display_embedder/gl_output_surface_win.cc b/components/viz/service/display_embedder/gl_output_surface_win.cc
index 117d9c8..e6cd5e3 100644
--- a/components/viz/service/display_embedder/gl_output_surface_win.cc
+++ b/components/viz/service/display_embedder/gl_output_surface_win.cc
@@ -10,9 +10,8 @@
 
 GLOutputSurfaceWin::GLOutputSurfaceWin(
     scoped_refptr<VizProcessContextProvider> context_provider,
-    UpdateVSyncParametersCallback update_vsync_callback,
     bool use_overlays)
-    : GLOutputSurface(context_provider, std::move(update_vsync_callback)) {
+    : GLOutputSurface(context_provider) {
   if (use_overlays) {
     overlay_validator_ =
         std::make_unique<CompositorOverlayCandidateValidatorWin>();
diff --git a/components/viz/service/display_embedder/gl_output_surface_win.h b/components/viz/service/display_embedder/gl_output_surface_win.h
index 67ef72d3..da4cf52f 100644
--- a/components/viz/service/display_embedder/gl_output_surface_win.h
+++ b/components/viz/service/display_embedder/gl_output_surface_win.h
@@ -17,7 +17,6 @@
 class GLOutputSurfaceWin : public GLOutputSurface {
  public:
   GLOutputSurfaceWin(scoped_refptr<VizProcessContextProvider> context_provider,
-                     UpdateVSyncParametersCallback update_vsync_callback,
                      bool use_overlays);
   ~GLOutputSurfaceWin() override;
 
diff --git a/components/viz/service/display_embedder/gpu_display_provider.cc b/components/viz/service/display_embedder/gpu_display_provider.cc
index 6ddc4d9..e57ba4be 100644
--- a/components/viz/service/display_embedder/gpu_display_provider.cc
+++ b/components/viz/service/display_embedder/gpu_display_provider.cc
@@ -120,8 +120,7 @@
 
   if (!gpu_compositing) {
     output_surface = std::make_unique<SoftwareOutputSurface>(
-        CreateSoftwareOutputDeviceForPlatform(surface_handle, display_client),
-        std::move(update_vsync_callback));
+        CreateSoftwareOutputDeviceForPlatform(surface_handle, display_client));
   } else if (renderer_settings.use_skia_renderer ||
              renderer_settings.use_skia_renderer_non_ddl) {
 #if defined(OS_MACOSX) || defined(OS_WIN)
@@ -157,8 +156,7 @@
 
     } else {
       output_surface = std::make_unique<SkiaOutputSurfaceImpl>(
-          gpu_service_impl_, surface_handle, std::move(update_vsync_callback),
-          renderer_settings);
+          gpu_service_impl_, surface_handle, renderer_settings);
     }
 #endif
   } else {
@@ -200,26 +198,24 @@
 
     if (surface_handle == gpu::kNullSurfaceHandle) {
       output_surface = std::make_unique<GLOutputSurfaceOffscreen>(
-          std::move(context_provider), std::move(update_vsync_callback));
+          std::move(context_provider));
     } else if (context_provider->ContextCapabilities().surfaceless) {
 #if defined(USE_OZONE)
       output_surface = std::make_unique<GLOutputSurfaceOzone>(
           std::move(context_provider), surface_handle,
-          std::move(update_vsync_callback), gpu_memory_buffer_manager_.get(),
+          gpu_memory_buffer_manager_.get(),
           renderer_settings.overlay_strategies);
 #elif defined(OS_MACOSX)
       output_surface = std::make_unique<GLOutputSurfaceMac>(
           std::move(context_provider), surface_handle,
-          std::move(update_vsync_callback), gpu_memory_buffer_manager_.get(),
-          renderer_settings.allow_overlays);
+          gpu_memory_buffer_manager_.get(), renderer_settings.allow_overlays);
 #elif defined(OS_ANDROID)
       auto buffer_format = context_provider->UseRGB565PixelFormat()
                                ? gfx::BufferFormat::BGR_565
                                : gfx::BufferFormat::RGBA_8888;
       output_surface = std::make_unique<GLOutputSurfaceBufferQueueAndroid>(
           std::move(context_provider), surface_handle,
-          std::move(update_vsync_callback), gpu_memory_buffer_manager_.get(),
-          buffer_format);
+          gpu_memory_buffer_manager_.get(), buffer_format);
 #else
       NOTREACHED();
 #endif
@@ -233,8 +229,7 @@
           capabilities.dc_layers && (capabilities.use_dc_overlays_for_video ||
                                      use_overlays_for_sw_protected_video);
       output_surface = std::make_unique<GLOutputSurfaceWin>(
-          std::move(context_provider), std::move(update_vsync_callback),
-          use_overlays);
+          std::move(context_provider), use_overlays);
 #elif defined(OS_ANDROID)
       const bool surface_control_enabled =
           task_executor_->gpu_feature_info()
@@ -244,11 +239,11 @@
              renderer_settings.backed_by_surface_texture);
 
       output_surface = std::make_unique<GLOutputSurfaceAndroid>(
-          std::move(context_provider), std::move(update_vsync_callback),
+          std::move(context_provider),
           !surface_control_enabled /* allow_overlays */);
 #else
-      output_surface = std::make_unique<GLOutputSurface>(
-          std::move(context_provider), std::move(update_vsync_callback));
+      output_surface =
+          std::make_unique<GLOutputSurface>(std::move(context_provider));
 #endif
     }
   }
@@ -256,6 +251,9 @@
   // If we need swap size notifications tell the output surface now.
   output_surface->SetNeedsSwapSizeNotifications(send_swap_size_notifications);
 
+  output_surface->SetUpdateVSyncParametersCallback(
+      std::move(update_vsync_callback));
+
   int max_frames_pending = output_surface->capabilities().max_frames_pending;
   DCHECK_GT(max_frames_pending, 0);
 
diff --git a/components/viz/service/display_embedder/skia_output_surface_impl.cc b/components/viz/service/display_embedder/skia_output_surface_impl.cc
index bcc5e4b9..45f09fc2 100644
--- a/components/viz/service/display_embedder/skia_output_surface_impl.cc
+++ b/components/viz/service/display_embedder/skia_output_surface_impl.cc
@@ -69,12 +69,10 @@
 SkiaOutputSurfaceImpl::SkiaOutputSurfaceImpl(
     GpuServiceImpl* gpu_service,
     gpu::SurfaceHandle surface_handle,
-    UpdateVSyncParametersCallback update_vsync_callback,
     const RendererSettings& renderer_settings)
     : gpu_service_(gpu_service),
       is_using_vulkan_(gpu_service->is_using_vulkan()),
       surface_handle_(surface_handle),
-      update_vsync_callback_(std::move(update_vsync_callback)),
       renderer_settings_(renderer_settings),
       weak_ptr_factory_(this) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
@@ -248,6 +246,11 @@
   needs_swap_size_notifications_ = needs_swap_size_notifications;
 }
 
+void SkiaOutputSurfaceImpl::SetUpdateVSyncParametersCallback(
+    UpdateVSyncParametersCallback callback) {
+  update_vsync_parameters_callback_ = std::move(callback);
+}
+
 SkCanvas* SkiaOutputSurfaceImpl::BeginPaintCurrentFrame() {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   // Make sure there is no unsubmitted PaintFrame or PaintRenderPass.
@@ -690,13 +693,13 @@
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   DCHECK(client_);
   client_->DidReceivePresentationFeedback(feedback);
-  if (update_vsync_callback_ &&
+  if (update_vsync_parameters_callback_ &&
       feedback.flags & gfx::PresentationFeedback::kVSync) {
     // TODO(brianderson): We should not be receiving 0 intervals.
-    update_vsync_callback_.Run(feedback.timestamp,
-                               feedback.interval.is_zero()
-                                   ? BeginFrameArgs::DefaultInterval()
-                                   : feedback.interval);
+    update_vsync_parameters_callback_.Run(
+        feedback.timestamp, feedback.interval.is_zero()
+                                ? BeginFrameArgs::DefaultInterval()
+                                : feedback.interval);
   }
 }
 
diff --git a/components/viz/service/display_embedder/skia_output_surface_impl.h b/components/viz/service/display_embedder/skia_output_surface_impl.h
index e4971fb..08ff06ef 100644
--- a/components/viz/service/display_embedder/skia_output_surface_impl.h
+++ b/components/viz/service/display_embedder/skia_output_surface_impl.h
@@ -48,7 +48,6 @@
  public:
   SkiaOutputSurfaceImpl(GpuServiceImpl* gpu_service,
                         gpu::SurfaceHandle surface_handle,
-                        UpdateVSyncParametersCallback update_vsync_callback,
                         const RendererSettings& renderer_settings);
   ~SkiaOutputSurfaceImpl() override;
 
@@ -74,6 +73,8 @@
   unsigned UpdateGpuFence() override;
   void SetNeedsSwapSizeNotifications(
       bool needs_swap_size_notifications) override;
+  void SetUpdateVSyncParametersCallback(
+      UpdateVSyncParametersCallback callback) override;
 
   // SkiaOutputSurface implementation:
   SkCanvas* BeginPaintCurrentFrame() override;
@@ -135,7 +136,7 @@
 
   const bool is_using_vulkan_;
   const gpu::SurfaceHandle surface_handle_;
-  UpdateVSyncParametersCallback update_vsync_callback_;
+  UpdateVSyncParametersCallback update_vsync_parameters_callback_;
   OutputSurfaceClient* client_ = nullptr;
 
   std::unique_ptr<base::WaitableEvent> initialize_waitable_event_;
diff --git a/components/viz/service/display_embedder/skia_output_surface_impl_non_ddl.cc b/components/viz/service/display_embedder/skia_output_surface_impl_non_ddl.cc
index 70bce5f..e4e197c 100644
--- a/components/viz/service/display_embedder/skia_output_surface_impl_non_ddl.cc
+++ b/components/viz/service/display_embedder/skia_output_surface_impl_non_ddl.cc
@@ -208,6 +208,11 @@
   NOTIMPLEMENTED();
 }
 
+void SkiaOutputSurfaceImplNonDDL::SetUpdateVSyncParametersCallback(
+    UpdateVSyncParametersCallback callback) {
+  NOTIMPLEMENTED();
+}
+
 SkCanvas* SkiaOutputSurfaceImplNonDDL::BeginPaintCurrentFrame() {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   DCHECK_EQ(current_render_pass_id_, 0u);
diff --git a/components/viz/service/display_embedder/skia_output_surface_impl_non_ddl.h b/components/viz/service/display_embedder/skia_output_surface_impl_non_ddl.h
index 9b4375e..e0511bc 100644
--- a/components/viz/service/display_embedder/skia_output_surface_impl_non_ddl.h
+++ b/components/viz/service/display_embedder/skia_output_surface_impl_non_ddl.h
@@ -83,6 +83,8 @@
   unsigned UpdateGpuFence() override;
   void SetNeedsSwapSizeNotifications(
       bool needs_swap_size_notifications) override;
+  void SetUpdateVSyncParametersCallback(
+      UpdateVSyncParametersCallback callback) override;
 
   // SkiaOutputSurface implementation:
   SkCanvas* BeginPaintCurrentFrame() override;
diff --git a/components/viz/service/display_embedder/skia_output_surface_impl_unittest.cc b/components/viz/service/display_embedder/skia_output_surface_impl_unittest.cc
index 22d3b35..f75e8b80 100644
--- a/components/viz/service/display_embedder/skia_output_surface_impl_unittest.cc
+++ b/components/viz/service/display_embedder/skia_output_surface_impl_unittest.cc
@@ -16,7 +16,6 @@
 #include "cc/test/fake_output_surface_client.h"
 #include "cc/test/pixel_test_utils.h"
 #include "components/viz/common/display/renderer_settings.h"
-#include "components/viz/common/display/update_vsync_parameters_callback.h"
 #include "components/viz/common/frame_sinks/copy_output_request.h"
 #include "components/viz/common/frame_sinks/copy_output_result.h"
 #include "components/viz/common/frame_sinks/copy_output_util.h"
@@ -211,8 +210,7 @@
 #endif
   }
   output_surface_ = std::make_unique<SkiaOutputSurfaceImpl>(
-      gpu_service_.get(), surface_handle_, UpdateVSyncParametersCallback(),
-      RendererSettings());
+      gpu_service_.get(), surface_handle_, RendererSettings());
   output_surface_->BindToClient(output_surface_client_.get());
 }
 
diff --git a/components/viz/service/display_embedder/software_output_surface.cc b/components/viz/service/display_embedder/software_output_surface.cc
index fe5cd0f..2763038 100644
--- a/components/viz/service/display_embedder/software_output_surface.cc
+++ b/components/viz/service/display_embedder/software_output_surface.cc
@@ -22,10 +22,8 @@
 namespace viz {
 
 SoftwareOutputSurface::SoftwareOutputSurface(
-    std::unique_ptr<SoftwareOutputDevice> software_device,
-    UpdateVSyncParametersCallback update_vsync_callback)
-    : OutputSurface(std::move(software_device)),
-      update_vsync_callback_(std::move(update_vsync_callback)) {}
+    std::unique_ptr<SoftwareOutputDevice> software_device)
+    : OutputSurface(std::move(software_device)) {}
 
 SoftwareOutputSurface::~SoftwareOutputSurface() = default;
 
@@ -77,7 +75,7 @@
       &SoftwareOutputSurface::SwapBuffersCallback, weak_factory_.GetWeakPtr()));
 
   gfx::VSyncProvider* vsync_provider = software_device()->GetVSyncProvider();
-  if (vsync_provider && update_vsync_callback_) {
+  if (vsync_provider && update_vsync_parameters_callback_) {
     vsync_provider->GetVSyncParameters(
         base::BindOnce(&SoftwareOutputSurface::UpdateVSyncParameters,
                        weak_factory_.GetWeakPtr()));
@@ -130,14 +128,19 @@
 
 void SoftwareOutputSurface::UpdateVSyncParameters(base::TimeTicks timebase,
                                                   base::TimeDelta interval) {
-  DCHECK(update_vsync_callback_);
+  DCHECK(update_vsync_parameters_callback_);
   refresh_timebase_ = timebase;
   refresh_interval_ = interval;
-  update_vsync_callback_.Run(timebase, interval);
+  update_vsync_parameters_callback_.Run(timebase, interval);
 }
 
 unsigned SoftwareOutputSurface::UpdateGpuFence() {
   return 0;
 }
 
+void SoftwareOutputSurface::SetUpdateVSyncParametersCallback(
+    UpdateVSyncParametersCallback callback) {
+  update_vsync_parameters_callback_ = std::move(callback);
+}
+
 }  // namespace viz
diff --git a/components/viz/service/display_embedder/software_output_surface.h b/components/viz/service/display_embedder/software_output_surface.h
index 04593bf..29c0b85 100644
--- a/components/viz/service/display_embedder/software_output_surface.h
+++ b/components/viz/service/display_embedder/software_output_surface.h
@@ -20,8 +20,8 @@
 
 class VIZ_SERVICE_EXPORT SoftwareOutputSurface : public OutputSurface {
  public:
-  SoftwareOutputSurface(std::unique_ptr<SoftwareOutputDevice> software_device,
-                        UpdateVSyncParametersCallback update_vsync_callback);
+  explicit SoftwareOutputSurface(
+      std::unique_ptr<SoftwareOutputDevice> software_device);
   ~SoftwareOutputSurface() override;
 
   // OutputSurface implementation.
@@ -44,6 +44,8 @@
   void ApplyExternalStencil() override;
   uint32_t GetFramebufferCopyTextureFormat() override;
   unsigned UpdateGpuFence() override;
+  void SetUpdateVSyncParametersCallback(
+      UpdateVSyncParametersCallback callback) override;
 
  private:
   void SwapBuffersCallback();
@@ -52,7 +54,7 @@
 
   OutputSurfaceClient* client_ = nullptr;
 
-  UpdateVSyncParametersCallback update_vsync_callback_;
+  UpdateVSyncParametersCallback update_vsync_parameters_callback_;
   base::TimeTicks refresh_timebase_;
   base::TimeDelta refresh_interval_ = BeginFrameArgs::DefaultInterval();
 
diff --git a/components/viz/test/fake_output_surface.cc b/components/viz/test/fake_output_surface.cc
index e472e85..e981783 100644
--- a/components/viz/test/fake_output_surface.cc
+++ b/components/viz/test/fake_output_surface.cc
@@ -105,4 +105,7 @@
   return gpu_fence_id_;
 }
 
+void FakeOutputSurface::SetUpdateVSyncParametersCallback(
+    UpdateVSyncParametersCallback callback) {}
+
 }  // namespace viz
diff --git a/components/viz/test/fake_output_surface.h b/components/viz/test/fake_output_surface.h
index aa4a2d7..d429c68 100644
--- a/components/viz/test/fake_output_surface.h
+++ b/components/viz/test/fake_output_surface.h
@@ -75,6 +75,8 @@
   unsigned GetOverlayTextureId() const override;
   gfx::BufferFormat GetOverlayBufferFormat() const override;
   unsigned UpdateGpuFence() override;
+  void SetUpdateVSyncParametersCallback(
+      UpdateVSyncParametersCallback callback) override;
 
   void set_framebuffer(GLint framebuffer, GLenum format) {
     framebuffer_ = framebuffer;
diff --git a/components/viz/test/fake_skia_output_surface.cc b/components/viz/test/fake_skia_output_surface.cc
index ca376bf..19c5462f 100644
--- a/components/viz/test/fake_skia_output_surface.cc
+++ b/components/viz/test/fake_skia_output_surface.cc
@@ -120,6 +120,11 @@
   NOTIMPLEMENTED();
 }
 
+void FakeSkiaOutputSurface::SetUpdateVSyncParametersCallback(
+    UpdateVSyncParametersCallback callback) {
+  NOTIMPLEMENTED();
+}
+
 SkCanvas* FakeSkiaOutputSurface::BeginPaintCurrentFrame() {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   auto& sk_surface = sk_surfaces_[0];
diff --git a/components/viz/test/fake_skia_output_surface.h b/components/viz/test/fake_skia_output_surface.h
index 19df347d..29e0fc83 100644
--- a/components/viz/test/fake_skia_output_surface.h
+++ b/components/viz/test/fake_skia_output_surface.h
@@ -52,6 +52,8 @@
   unsigned UpdateGpuFence() override;
   void SetNeedsSwapSizeNotifications(
       bool needs_swap_size_notifications) override;
+  void SetUpdateVSyncParametersCallback(
+      UpdateVSyncParametersCallback callback) override;
 
   // SkiaOutputSurface implementation:
   SkCanvas* BeginPaintCurrentFrame() override;
diff --git a/components/viz/test/test_context_provider.cc b/components/viz/test/test_context_provider.cc
index 12ce11c..69ddcd1 100644
--- a/components/viz/test/test_context_provider.cc
+++ b/components/viz/test/test_context_provider.cc
@@ -160,6 +160,7 @@
     const gpu::SyncToken& sync_token,
     const gpu::Mailbox& mailbox) {
   shared_images_.erase(mailbox);
+  most_recent_destroy_token_ = sync_token;
 }
 
 #if defined(OS_WIN)
@@ -180,15 +181,18 @@
 #endif  // OS_WIN
 
 gpu::SyncToken TestSharedImageInterface::GenVerifiedSyncToken() {
-  gpu::SyncToken sync_token(gpu::CommandBufferNamespace::GPU_IO,
-                            gpu::CommandBufferId(), ++release_id_);
-  sync_token.SetVerifyFlush();
-  return sync_token;
+  most_recent_generated_token_ =
+      gpu::SyncToken(gpu::CommandBufferNamespace::GPU_IO,
+                     gpu::CommandBufferId(), ++release_id_);
+  most_recent_generated_token_.SetVerifyFlush();
+  return most_recent_generated_token_;
 }
 
 gpu::SyncToken TestSharedImageInterface::GenUnverifiedSyncToken() {
-  return gpu::SyncToken(gpu::CommandBufferNamespace::GPU_IO,
-                        gpu::CommandBufferId(), ++release_id_);
+  most_recent_generated_token_ =
+      gpu::SyncToken(gpu::CommandBufferNamespace::GPU_IO,
+                     gpu::CommandBufferId(), ++release_id_);
+  return most_recent_generated_token_;
 }
 
 bool TestSharedImageInterface::CheckSharedImageExists(
diff --git a/components/viz/test/test_context_provider.h b/components/viz/test/test_context_provider.h
index b5eb723c..2258b12 100644
--- a/components/viz/test/test_context_provider.h
+++ b/components/viz/test/test_context_provider.h
@@ -77,11 +77,19 @@
 
   size_t shared_image_count() const { return shared_images_.size(); }
   const gfx::Size& MostRecentSize() const { return most_recent_size_; }
+  const gpu::SyncToken& MostRecentGeneratedToken() const {
+    return most_recent_generated_token_;
+  }
+  const gpu::SyncToken& MostRecentDestroyToken() const {
+    return most_recent_destroy_token_;
+  }
   bool CheckSharedImageExists(const gpu::Mailbox& mailbox) const;
 
  private:
   uint64_t release_id_ = 0;
   gfx::Size most_recent_size_;
+  gpu::SyncToken most_recent_generated_token_;
+  gpu::SyncToken most_recent_destroy_token_;
   base::flat_set<gpu::Mailbox> shared_images_;
 };
 
diff --git a/content/app/content_main_runner_impl.cc b/content/app/content_main_runner_impl.cc
index 4dc87d82..732dc46 100644
--- a/content/app/content_main_runner_impl.cc
+++ b/content/app/content_main_runner_impl.cc
@@ -86,6 +86,7 @@
 #elif defined(OS_MACOSX)
 #include "base/mac/mach_port_broker.h"
 #include "base/power_monitor/power_monitor_device_source.h"
+#include "sandbox/mac/seatbelt.h"
 #include "sandbox/mac/seatbelt_exec.h"
 #endif  // OS_WIN
 
@@ -812,19 +813,16 @@
             params.sandbox_info))
       return TerminateForFatalInitializationError();
 #elif defined(OS_MACOSX)
-    // Do not initialize the sandbox at this point if the V2
-    // sandbox is enabled for the process type.
+    // Only the GPU process still runs the V1 sandbox.
     bool v2_enabled = base::CommandLine::ForCurrentProcess()->HasSwitch(
         sandbox::switches::kSeatbeltClientName);
 
-    if (process_type == switches::kRendererProcess ||
-        process_type == switches::kPpapiPluginProcess || v2_enabled ||
-        delegate_->DelaySandboxInitialization(process_type)) {
-      // On OS X the renderer sandbox needs to be initialized later in the
-      // startup sequence in RendererMainPlatformDelegate::EnableSandbox().
-    } else {
-      if (!InitializeSandbox())
+    if (!v2_enabled && process_type == switches::kGpuProcess) {
+      if (!InitializeSandbox()) {
         return TerminateForFatalInitializationError();
+      }
+    } else if (v2_enabled) {
+      CHECK(sandbox::Seatbelt::IsSandboxed());
     }
 #endif
 
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index 7b01d3c..18761f1 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -59,6 +59,7 @@
     "//components/network_session_configurator/browser",
     "//components/offline_pages/buildflags",
     "//components/offline_pages/core/request_header",
+    "//components/payments/content/icon",
     "//components/rappor",
     "//components/services/filesystem:lib",
     "//components/services/leveldb:lib",
diff --git a/content/browser/accessibility/dump_accessibility_tree_browsertest.cc b/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
index 400e17c..6370dcf 100644
--- a/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
+++ b/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
@@ -1321,8 +1321,9 @@
   RunHtmlTest(FILE_PATH_LITERAL("iframe-coordinates.html"));
 }
 
+// https://crbug.com/956990
 IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest,
-                       AccessibilityIframeCoordinatesCrossProcess) {
+                       DISABLED_AccessibilityIframeCoordinatesCrossProcess) {
   RunHtmlTest(FILE_PATH_LITERAL("iframe-coordinates-cross-process.html"));
 }
 IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest, AccessibilityIframePadding) {
diff --git a/content/browser/after_startup_task_utils.cc b/content/browser/after_startup_task_utils.cc
index 2024105..66d25548 100644
--- a/content/browser/after_startup_task_utils.cc
+++ b/content/browser/after_startup_task_utils.cc
@@ -11,7 +11,7 @@
 namespace content {
 
 void SetBrowserStartupIsCompleteForTesting() {
-  content::BrowserTaskExecutor::NotifyBrowserStartupCompleted();
+  content::BrowserTaskExecutor::EnableBestEffortQueues();
   // Forward the message to ContentBrowserClient if one is registered (there are
   // many tests where one isn't but that's fine as that also means they get the
   // default ContentBrowserClient::IsBrowserStartupComplete() which is always
diff --git a/content/browser/browser_main_loop.cc b/content/browser/browser_main_loop.cc
index 6e3dfde10..7580d35 100644
--- a/content/browser/browser_main_loop.cc
+++ b/content/browser/browser_main_loop.cc
@@ -1212,7 +1212,7 @@
   // TODO(https://crbug.com/863341): Replace with a better API
   GetContentClient()->browser()->PostAfterStartupTask(
       FROM_HERE, base::SequencedTaskRunnerHandle::Get(), base::BindOnce([]() {
-        content::BrowserTaskExecutor::NotifyBrowserStartupCompleted();
+        content::BrowserTaskExecutor::EnableBestEffortQueues();
       }));
 }
 
@@ -1460,7 +1460,7 @@
 
   if (base::FeatureList::IsEnabled(features::kFontSrcLocalMatching)) {
     content::DWriteFontLookupTableBuilder::GetInstance()
-        ->SchedulePrepareFontUniqueNameTable();
+        ->SchedulePrepareFontUniqueNameTableIfNeeded();
   }
 #endif
 
diff --git a/content/browser/browsing_data/browsing_data_remover_impl_unittest.cc b/content/browser/browsing_data/browsing_data_remover_impl_unittest.cc
index a2adb6b..c43aaee 100644
--- a/content/browser/browsing_data/browsing_data_remover_impl_unittest.cc
+++ b/content/browser/browsing_data/browsing_data_remover_impl_unittest.cc
@@ -60,6 +60,7 @@
 #include "url/origin.h"
 
 #if BUILDFLAG(ENABLE_REPORTING)
+#include "net/network_error_logging/mock_persistent_nel_store.h"
 #include "net/network_error_logging/network_error_logging_service.h"
 #include "net/reporting/reporting_cache.h"
 #include "net/reporting/reporting_report.h"
@@ -1360,9 +1361,44 @@
                                 BrowsingDataRemover::DATA_TYPE_COOKIES, false);
 }
 
-// TODO(chlily): Use a PersistentNELStore and test that entries are removed from
-// it.
 TEST_F(BrowsingDataRemoverImplTest, RemoveNetworkErrorLogging) {
+  auto store = std::make_unique<net::MockPersistentNELStore>();
+  std::unique_ptr<net::NetworkErrorLoggingService> logging_service =
+      net::NetworkErrorLoggingService::Create(store.get());
+  BrowserContext::GetDefaultStoragePartition(GetBrowserContext())
+      ->GetURLRequestContext()
+      ->GetURLRequestContext()
+      ->set_network_error_logging_service(logging_service.get());
+
+  GURL domain("https://google.com");
+  logging_service->OnHeader(url::Origin::Create(domain),
+                            net::IPAddress(192, 168, 0, 1),
+                            "{\"report_to\":\"group\",\"max_age\":86400}");
+  store->FinishLoading(true /* load_success */);
+  store->Flush();
+
+  ASSERT_EQ(1u, logging_service->GetPolicyOriginsForTesting().size());
+  ASSERT_EQ(1, store->StoredPoliciesCount());
+
+  BlockUntilBrowsingDataRemoved(base::Time(), base::Time::Max(),
+                                BrowsingDataRemover::DATA_TYPE_COOKIES, false);
+
+  EXPECT_TRUE(logging_service->GetPolicyOriginsForTesting().empty());
+  EXPECT_EQ(0, store->StoredPoliciesCount());
+
+  // Check that the persistent store was told to delete the policy.
+  net::NetworkErrorLoggingService::NELPolicy deleted_policy;
+  deleted_policy.origin = url::Origin::Create(domain);
+  EXPECT_THAT(store->GetAllCommands(),
+              testing::Contains(net::MockPersistentNELStore::Command(
+                  net::MockPersistentNELStore::Command::Type::DELETE_NEL_POLICY,
+                  deleted_policy)));
+}
+
+// Test that removal of in-memory browsing data works without a persistent
+// store.
+TEST_F(BrowsingDataRemoverImplTest,
+       RemoveNetworkErrorLogging_NoPersistentStore) {
   std::unique_ptr<net::NetworkErrorLoggingService> logging_service =
       net::NetworkErrorLoggingService::Create(nullptr /* store */);
   BrowserContext::GetDefaultStoragePartition(GetBrowserContext())
@@ -1383,11 +1419,10 @@
   EXPECT_TRUE(logging_service->GetPolicyOriginsForTesting().empty());
 }
 
-// TODO(chlily): Use a PersistentNELStore and test that entries are removed from
-// it.
 TEST_F(BrowsingDataRemoverImplTest, RemoveNetworkErrorLogging_SpecificOrigins) {
+  auto store = std::make_unique<net::MockPersistentNELStore>();
   std::unique_ptr<net::NetworkErrorLoggingService> logging_service =
-      net::NetworkErrorLoggingService::Create(nullptr /* store */);
+      net::NetworkErrorLoggingService::Create(store.get());
   BrowserContext::GetDefaultStoragePartition(GetBrowserContext())
       ->GetURLRequestContext()
       ->GetURLRequestContext()
@@ -1409,8 +1444,11 @@
   logging_service->OnHeader(url::Origin::Create(domain4),
                             net::IPAddress(192, 168, 0, 1),
                             "{\"report_to\":\"group\",\"max_age\":86400}");
+  store->FinishLoading(true /* load_success */);
+  store->Flush();
 
   ASSERT_EQ(4u, logging_service->GetPolicyOriginsForTesting().size());
+  ASSERT_EQ(4, store->StoredPoliciesCount());
 
   std::unique_ptr<BrowsingDataFilterBuilder> filter_builder(
       BrowsingDataFilterBuilder::Create(BrowsingDataFilterBuilder::WHITELIST));
@@ -1426,6 +1464,21 @@
   EXPECT_THAT(policy_origins,
               UnorderedElementsAre(url::Origin::Create(domain2),
                                    url::Origin::Create(domain4)));
+  EXPECT_EQ(2, store->StoredPoliciesCount());
+
+  // Check that the persistent store was told to delete the policies.
+  net::NetworkErrorLoggingService::NELPolicy deleted_policy1;
+  deleted_policy1.origin = url::Origin::Create(domain1);
+  net::NetworkErrorLoggingService::NELPolicy deleted_policy2;
+  deleted_policy2.origin = url::Origin::Create(domain3);
+  EXPECT_THAT(store->GetAllCommands(),
+              testing::Contains(net::MockPersistentNELStore::Command(
+                  net::MockPersistentNELStore::Command::Type::DELETE_NEL_POLICY,
+                  deleted_policy1)));
+  EXPECT_THAT(store->GetAllCommands(),
+              testing::Contains(net::MockPersistentNELStore::Command(
+                  net::MockPersistentNELStore::Command::Type::DELETE_NEL_POLICY,
+                  deleted_policy2)));
 }
 
 TEST_F(BrowsingDataRemoverImplTest, RemoveNetworkErrorLogging_NoService) {
diff --git a/content/browser/compositor/browser_compositor_output_surface.cc b/content/browser/compositor/browser_compositor_output_surface.cc
index 24cfe2fb..08883fb3 100644
--- a/content/browser/compositor/browser_compositor_output_surface.cc
+++ b/content/browser/compositor/browser_compositor_output_surface.cc
@@ -20,21 +20,15 @@
 
 BrowserCompositorOutputSurface::BrowserCompositorOutputSurface(
     scoped_refptr<viz::ContextProvider> context_provider,
-    const viz::UpdateVSyncParametersCallback& update_vsync_parameters_callback,
     std::unique_ptr<viz::CompositorOverlayCandidateValidator>
         overlay_candidate_validator)
-    : OutputSurface(std::move(context_provider)),
-      update_vsync_parameters_callback_(update_vsync_parameters_callback),
-      reflector_(nullptr) {
+    : OutputSurface(std::move(context_provider)) {
   overlay_candidate_validator_ = std::move(overlay_candidate_validator);
 }
 
 BrowserCompositorOutputSurface::BrowserCompositorOutputSurface(
-    std::unique_ptr<viz::SoftwareOutputDevice> software_device,
-    const viz::UpdateVSyncParametersCallback& update_vsync_parameters_callback)
-    : OutputSurface(std::move(software_device)),
-      update_vsync_parameters_callback_(update_vsync_parameters_callback),
-      reflector_(nullptr) {}
+    std::unique_ptr<viz::SoftwareOutputDevice> software_device)
+    : OutputSurface(std::move(software_device)) {}
 
 BrowserCompositorOutputSurface::~BrowserCompositorOutputSurface() {
   if (reflector_)
@@ -67,4 +61,9 @@
 
 void BrowserCompositorOutputSurface::ApplyExternalStencil() {}
 
+void BrowserCompositorOutputSurface::SetUpdateVSyncParametersCallback(
+    viz::UpdateVSyncParametersCallback callback) {
+  update_vsync_parameters_callback_ = std::move(callback);
+}
+
 }  // namespace content
diff --git a/content/browser/compositor/browser_compositor_output_surface.h b/content/browser/compositor/browser_compositor_output_surface.h
index dda4ac0..e3fe1a9 100644
--- a/content/browser/compositor/browser_compositor_output_surface.h
+++ b/content/browser/compositor/browser_compositor_output_surface.h
@@ -35,6 +35,8 @@
   viz::OverlayCandidateValidator* GetOverlayCandidateValidator() const override;
   bool HasExternalStencilTest() const override;
   void ApplyExternalStencil() override;
+  void SetUpdateVSyncParametersCallback(
+      viz::UpdateVSyncParametersCallback callback) override;
 
   void SetReflector(ReflectorImpl* reflector);
 
@@ -45,19 +47,15 @@
   // Constructor used by the accelerated implementation.
   BrowserCompositorOutputSurface(
       scoped_refptr<viz::ContextProvider> context,
-      const viz::UpdateVSyncParametersCallback&
-          update_vsync_parameters_callback,
       std::unique_ptr<viz::CompositorOverlayCandidateValidator>
           overlay_candidate_validator);
 
   // Constructor used by the software implementation.
-  BrowserCompositorOutputSurface(
-      std::unique_ptr<viz::SoftwareOutputDevice> software_device,
-      const viz::UpdateVSyncParametersCallback&
-          update_vsync_parameters_callback);
+  explicit BrowserCompositorOutputSurface(
+      std::unique_ptr<viz::SoftwareOutputDevice> software_device);
 
-  const viz::UpdateVSyncParametersCallback update_vsync_parameters_callback_;
-  ReflectorImpl* reflector_;
+  viz::UpdateVSyncParametersCallback update_vsync_parameters_callback_;
+  ReflectorImpl* reflector_ = nullptr;
 
  private:
   std::unique_ptr<viz::CompositorOverlayCandidateValidator>
diff --git a/content/browser/compositor/gpu_browser_compositor_output_surface.cc b/content/browser/compositor/gpu_browser_compositor_output_surface.cc
index b9838196..762df71 100644
--- a/content/browser/compositor/gpu_browser_compositor_output_surface.cc
+++ b/content/browser/compositor/gpu_browser_compositor_output_surface.cc
@@ -26,11 +26,9 @@
 
 GpuBrowserCompositorOutputSurface::GpuBrowserCompositorOutputSurface(
     scoped_refptr<ws::ContextProviderCommandBuffer> context,
-    const viz::UpdateVSyncParametersCallback& update_vsync_parameters_callback,
     std::unique_ptr<viz::CompositorOverlayCandidateValidator>
         overlay_candidate_validator)
     : BrowserCompositorOutputSurface(std::move(context),
-                                     update_vsync_parameters_callback,
                                      std::move(overlay_candidate_validator)),
       weak_ptr_factory_(this) {
   if (capabilities_.uses_default_gl_framebuffer) {
diff --git a/content/browser/compositor/gpu_browser_compositor_output_surface.h b/content/browser/compositor/gpu_browser_compositor_output_surface.h
index 8c728be..a050aee 100644
--- a/content/browser/compositor/gpu_browser_compositor_output_surface.h
+++ b/content/browser/compositor/gpu_browser_compositor_output_surface.h
@@ -41,8 +41,6 @@
  public:
   GpuBrowserCompositorOutputSurface(
       scoped_refptr<ws::ContextProviderCommandBuffer> context,
-      const viz::UpdateVSyncParametersCallback&
-          update_vsync_parameters_callback,
       std::unique_ptr<viz::CompositorOverlayCandidateValidator>
           overlay_candidate_validator);
 
diff --git a/content/browser/compositor/gpu_output_surface_mac.cc b/content/browser/compositor/gpu_output_surface_mac.cc
index 91ebbee..eeace7b4 100644
--- a/content/browser/compositor/gpu_output_surface_mac.cc
+++ b/content/browser/compositor/gpu_output_surface_mac.cc
@@ -15,14 +15,12 @@
 GpuOutputSurfaceMac::GpuOutputSurfaceMac(
     scoped_refptr<ws::ContextProviderCommandBuffer> context,
     gpu::SurfaceHandle surface_handle,
-    const viz::UpdateVSyncParametersCallback& update_vsync_parameters_callback,
     std::unique_ptr<viz::CompositorOverlayCandidateValidator>
         overlay_candidate_validator,
     gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager)
     : GpuSurfacelessBrowserCompositorOutputSurface(
           std::move(context),
           surface_handle,
-          update_vsync_parameters_callback,
           std::move(overlay_candidate_validator),
           gfx::BufferFormat::RGBA_8888,
           gpu_memory_buffer_manager) {}
diff --git a/content/browser/compositor/gpu_output_surface_mac.h b/content/browser/compositor/gpu_output_surface_mac.h
index 573358a..b9e184f 100644
--- a/content/browser/compositor/gpu_output_surface_mac.h
+++ b/content/browser/compositor/gpu_output_surface_mac.h
@@ -16,8 +16,6 @@
  public:
   GpuOutputSurfaceMac(scoped_refptr<ws::ContextProviderCommandBuffer> context,
                       gpu::SurfaceHandle surface_handle,
-                      const viz::UpdateVSyncParametersCallback&
-                          update_vsync_parameters_callback,
                       std::unique_ptr<viz::CompositorOverlayCandidateValidator>
                           overlay_candidate_validator,
                       gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager);
diff --git a/content/browser/compositor/gpu_process_transport_factory.cc b/content/browser/compositor/gpu_process_transport_factory.cc
index 99c703a1..e5a43b9 100644
--- a/content/browser/compositor/gpu_process_transport_factory.cc
+++ b/content/browser/compositor/gpu_process_transport_factory.cc
@@ -437,8 +437,6 @@
     }
   }
 
-  auto vsync_callback = base::BindRepeating(
-      &ui::Compositor::SetDisplayVSyncParameters, compositor);
   std::unique_ptr<BrowserCompositorOutputSurface> display_output_surface;
   if (!use_gpu_compositing) {
     if (!is_gpu_compositing_disabled_ &&
@@ -453,15 +451,14 @@
     display_output_surface =
         std::make_unique<SoftwareBrowserCompositorOutputSurface>(
             CreateSoftwareOutputDevice(compositor->widget(),
-                                       compositor->task_runner()),
-            std::move(vsync_callback));
+                                       compositor->task_runner()));
   } else {
     DCHECK(context_provider);
     const auto& capabilities = context_provider->ContextCapabilities();
     if (data->surface_handle == gpu::kNullSurfaceHandle) {
       display_output_surface =
           std::make_unique<OffscreenBrowserCompositorOutputSurface>(
-              context_provider, std::move(vsync_callback),
+              context_provider,
               std::unique_ptr<viz::CompositorOverlayCandidateValidator>());
     } else if (capabilities.surfaceless) {
 #if defined(OS_MACOSX)
@@ -469,7 +466,7 @@
       bool disable_overlay_ca_layers =
           gpu_feature_info.IsWorkaroundEnabled(gpu::DISABLE_OVERLAY_CA_LAYERS);
       display_output_surface = std::make_unique<GpuOutputSurfaceMac>(
-          context_provider, data->surface_handle, vsync_callback,
+          context_provider, data->surface_handle,
           CreateOverlayCandidateValidator(compositor->widget(),
                                           disable_overlay_ca_layers),
           GetGpuMemoryBufferManager());
@@ -477,7 +474,7 @@
       DCHECK(capabilities.texture_format_bgra8888);
       auto gpu_output_surface =
           std::make_unique<GpuSurfacelessBrowserCompositorOutputSurface>(
-              context_provider, data->surface_handle, std::move(vsync_callback),
+              context_provider, data->surface_handle,
               CreateOverlayCandidateValidator(compositor->widget()),
               display::DisplaySnapshot::PrimaryFormat(),
               GetGpuMemoryBufferManager());
@@ -498,12 +495,15 @@
 #endif
       auto gpu_output_surface =
           std::make_unique<GpuBrowserCompositorOutputSurface>(
-              context_provider, std::move(vsync_callback),
-              std::move(validator));
+              context_provider, std::move(validator));
       display_output_surface = std::move(gpu_output_surface);
     }
   }
 
+  auto vsync_callback = base::BindRepeating(
+      &ui::Compositor::SetDisplayVSyncParameters, compositor);
+  display_output_surface->SetUpdateVSyncParametersCallback(vsync_callback);
+
   data->display_output_surface = display_output_surface.get();
   if (data->reflector)
     data->reflector->OnSourceSurfaceReady(data->display_output_surface);
diff --git a/content/browser/compositor/gpu_surfaceless_browser_compositor_output_surface.cc b/content/browser/compositor/gpu_surfaceless_browser_compositor_output_surface.cc
index 4d4f7136..bdcef70 100644
--- a/content/browser/compositor/gpu_surfaceless_browser_compositor_output_surface.cc
+++ b/content/browser/compositor/gpu_surfaceless_browser_compositor_output_surface.cc
@@ -22,14 +22,11 @@
     GpuSurfacelessBrowserCompositorOutputSurface(
         scoped_refptr<ws::ContextProviderCommandBuffer> context,
         gpu::SurfaceHandle surface_handle,
-        const viz::UpdateVSyncParametersCallback&
-            update_vsync_parameters_callback,
         std::unique_ptr<viz::CompositorOverlayCandidateValidator>
             overlay_candidate_validator,
         gfx::BufferFormat format,
         gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager)
     : GpuBrowserCompositorOutputSurface(std::move(context),
-                                        update_vsync_parameters_callback,
                                         std::move(overlay_candidate_validator)),
       use_gpu_fence_(
           context_provider_->ContextCapabilities().chromium_gpu_fence &&
diff --git a/content/browser/compositor/gpu_surfaceless_browser_compositor_output_surface.h b/content/browser/compositor/gpu_surfaceless_browser_compositor_output_surface.h
index af364ef..bfc610a 100644
--- a/content/browser/compositor/gpu_surfaceless_browser_compositor_output_surface.h
+++ b/content/browser/compositor/gpu_surfaceless_browser_compositor_output_surface.h
@@ -27,8 +27,6 @@
   GpuSurfacelessBrowserCompositorOutputSurface(
       scoped_refptr<ws::ContextProviderCommandBuffer> context,
       gpu::SurfaceHandle surface_handle,
-      const viz::UpdateVSyncParametersCallback&
-          update_vsync_parameters_callback,
       std::unique_ptr<viz::CompositorOverlayCandidateValidator>
           overlay_candidate_validator,
       gfx::BufferFormat format,
diff --git a/content/browser/compositor/offscreen_browser_compositor_output_surface.cc b/content/browser/compositor/offscreen_browser_compositor_output_surface.cc
index dfedf6a..bcbe12c2 100644
--- a/content/browser/compositor/offscreen_browser_compositor_output_surface.cc
+++ b/content/browser/compositor/offscreen_browser_compositor_output_surface.cc
@@ -32,12 +32,9 @@
 OffscreenBrowserCompositorOutputSurface::
     OffscreenBrowserCompositorOutputSurface(
         scoped_refptr<ws::ContextProviderCommandBuffer> context,
-        const viz::UpdateVSyncParametersCallback&
-            update_vsync_parameters_callback,
         std::unique_ptr<viz::CompositorOverlayCandidateValidator>
             overlay_candidate_validator)
     : BrowserCompositorOutputSurface(std::move(context),
-                                     update_vsync_parameters_callback,
                                      std::move(overlay_candidate_validator)),
       weak_ptr_factory_(this) {
   capabilities_.uses_default_gl_framebuffer = false;
diff --git a/content/browser/compositor/offscreen_browser_compositor_output_surface.h b/content/browser/compositor/offscreen_browser_compositor_output_surface.h
index 9751f1e..ac2786d 100644
--- a/content/browser/compositor/offscreen_browser_compositor_output_surface.h
+++ b/content/browser/compositor/offscreen_browser_compositor_output_surface.h
@@ -29,8 +29,6 @@
  public:
   OffscreenBrowserCompositorOutputSurface(
       scoped_refptr<ws::ContextProviderCommandBuffer> context,
-      const viz::UpdateVSyncParametersCallback&
-          update_vsync_parameters_callback,
       std::unique_ptr<viz::CompositorOverlayCandidateValidator>
           overlay_candidate_validator);
 
diff --git a/content/browser/compositor/reflector_impl_unittest.cc b/content/browser/compositor/reflector_impl_unittest.cc
index d3fc3f8..0352bf8 100644
--- a/content/browser/compositor/reflector_impl_unittest.cc
+++ b/content/browser/compositor/reflector_impl_unittest.cc
@@ -80,7 +80,6 @@
  public:
   TestOutputSurface(scoped_refptr<viz::ContextProvider> context_provider)
       : BrowserCompositorOutputSurface(std::move(context_provider),
-                                       viz::UpdateVSyncParametersCallback(),
                                        CreateTestValidatorOzone()) {}
 
   void SetFlip(bool flip) { capabilities_.flipped_output_surface = flip; }
diff --git a/content/browser/compositor/software_browser_compositor_output_surface.cc b/content/browser/compositor/software_browser_compositor_output_surface.cc
index 0f13e98..22412d9 100644
--- a/content/browser/compositor/software_browser_compositor_output_surface.cc
+++ b/content/browser/compositor/software_browser_compositor_output_surface.cc
@@ -21,10 +21,8 @@
 namespace content {
 
 SoftwareBrowserCompositorOutputSurface::SoftwareBrowserCompositorOutputSurface(
-    std::unique_ptr<viz::SoftwareOutputDevice> software_device,
-    const viz::UpdateVSyncParametersCallback& update_vsync_parameters_callback)
-    : BrowserCompositorOutputSurface(std::move(software_device),
-                                     update_vsync_parameters_callback),
+    std::unique_ptr<viz::SoftwareOutputDevice> software_device)
+    : BrowserCompositorOutputSurface(std::move(software_device)),
       weak_factory_(this) {}
 
 SoftwareBrowserCompositorOutputSurface::
@@ -98,7 +96,8 @@
     const base::TimeTicks timebase,
     const base::TimeDelta interval) {
   refresh_interval_ = interval;
-  update_vsync_parameters_callback_.Run(timebase, interval);
+  if (update_vsync_parameters_callback_)
+    update_vsync_parameters_callback_.Run(timebase, interval);
 }
 
 bool SoftwareBrowserCompositorOutputSurface::IsDisplayedAsOverlayPlane() const {
diff --git a/content/browser/compositor/software_browser_compositor_output_surface.h b/content/browser/compositor/software_browser_compositor_output_surface.h
index fa54dcb..012ba86 100644
--- a/content/browser/compositor/software_browser_compositor_output_surface.h
+++ b/content/browser/compositor/software_browser_compositor_output_surface.h
@@ -17,10 +17,8 @@
 class CONTENT_EXPORT SoftwareBrowserCompositorOutputSurface
     : public BrowserCompositorOutputSurface {
  public:
-  SoftwareBrowserCompositorOutputSurface(
-      std::unique_ptr<viz::SoftwareOutputDevice> software_device,
-      const viz::UpdateVSyncParametersCallback&
-          update_vsync_parameters_callback);
+  explicit SoftwareBrowserCompositorOutputSurface(
+      std::unique_ptr<viz::SoftwareOutputDevice> software_device);
 
   ~SoftwareBrowserCompositorOutputSurface() override;
 
diff --git a/content/browser/compositor/software_browser_compositor_output_surface_unittest.cc b/content/browser/compositor/software_browser_compositor_output_surface_unittest.cc
index 5ac8128f..c04aefd7 100644
--- a/content/browser/compositor/software_browser_compositor_output_surface_unittest.cc
+++ b/content/browser/compositor/software_browser_compositor_output_surface_unittest.cc
@@ -126,11 +126,13 @@
 std::unique_ptr<content::BrowserCompositorOutputSurface>
 SoftwareBrowserCompositorOutputSurfaceTest::CreateSurface(
     std::unique_ptr<viz::SoftwareOutputDevice> device) {
-  return std::make_unique<content::SoftwareBrowserCompositorOutputSurface>(
-      std::move(device),
-      base::Bind(
-          &SoftwareBrowserCompositorOutputSurfaceTest::UpdateVSyncParameters,
-          base::Unretained(this)));
+  auto surface =
+      std::make_unique<content::SoftwareBrowserCompositorOutputSurface>(
+          std::move(device));
+  surface->SetUpdateVSyncParametersCallback(base::BindRepeating(
+      &SoftwareBrowserCompositorOutputSurfaceTest::UpdateVSyncParameters,
+      base::Unretained(this)));
+  return surface;
 }
 
 void SoftwareBrowserCompositorOutputSurfaceTest::UpdateVSyncParameters(
diff --git a/content/browser/notifications/OWNERS b/content/browser/notifications/OWNERS
index 0d5ed95..fec618f3 100644
--- a/content/browser/notifications/OWNERS
+++ b/content/browser/notifications/OWNERS
@@ -4,6 +4,7 @@
 # //content/renderer/notifications/
 # //content/test/mock_platform_notification_service.*
 
+knollr@chromium.org
 mkwst@chromium.org
 peter@chromium.org
 
diff --git a/content/browser/notifications/platform_notification_context_impl.cc b/content/browser/notifications/platform_notification_context_impl.cc
index 37c746bc..258d423 100644
--- a/content/browser/notifications/platform_notification_context_impl.cc
+++ b/content/browser/notifications/platform_notification_context_impl.cc
@@ -67,7 +67,8 @@
     const scoped_refptr<ServiceWorkerContextWrapper>& service_worker_context)
     : path_(path),
       browser_context_(browser_context),
-      service_worker_context_(service_worker_context) {
+      service_worker_context_(service_worker_context),
+      has_shutdown_(false) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 }
 
@@ -109,6 +110,10 @@
     std::set<std::string> displayed_notifications,
     bool supports_synchronization) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  // Abort if the profile has been shut down already. This mainly happens in
+  // tests and very short lived sessions.
+  if (has_shutdown_)
+    return;
 
   // Check if there are pending notifications to display.
   base::Time next_trigger = base::Time::Max();
@@ -189,8 +194,11 @@
 
 void PlatformNotificationContextImpl::Shutdown() {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  has_shutdown_ = true;
 
-  service_proxy_.reset();
+  if (service_proxy_)
+    service_proxy_->Shutdown();
+
   services_.clear();
 
   // |service_worker_context_| may be NULL in tests.
@@ -267,7 +275,7 @@
     std::set<GURL> origins) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   // Make sure |browser_context_| is still valid before getting the controller.
-  if (!success || !service_proxy_) {
+  if (!success || !service_proxy_ || has_shutdown_) {
     std::move(callback).Run(/* success= */ false, /* deleted_count= */ 0);
     return;
   }
diff --git a/content/browser/notifications/platform_notification_context_impl.h b/content/browser/notifications/platform_notification_context_impl.h
index 11eed74..de7f9d4 100644
--- a/content/browser/notifications/platform_notification_context_impl.h
+++ b/content/browser/notifications/platform_notification_context_impl.h
@@ -272,6 +272,9 @@
 
   NotificationDatabase::UkmCallback ukm_callback_;
 
+  // Flag if the |browser_context_| has been shutdown already.
+  bool has_shutdown_;
+
   DISALLOW_COPY_AND_ASSIGN(PlatformNotificationContextImpl);
 };
 
diff --git a/content/browser/notifications/platform_notification_service_proxy.cc b/content/browser/notifications/platform_notification_service_proxy.cc
index 1e65ea4..5ad2b972 100644
--- a/content/browser/notifications/platform_notification_service_proxy.cc
+++ b/content/browser/notifications/platform_notification_service_proxy.cc
@@ -24,10 +24,21 @@
       notification_service_(
           GetContentClient()->browser()->GetPlatformNotificationService(
               browser_context)),
+      weak_ptr_factory_ui_(this),
       weak_ptr_factory_io_(this) {}
 
 PlatformNotificationServiceProxy::~PlatformNotificationServiceProxy() = default;
 
+void PlatformNotificationServiceProxy::Shutdown() {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  weak_ptr_factory_ui_.InvalidateWeakPtrs();
+}
+
+base::WeakPtr<PlatformNotificationServiceProxy>
+PlatformNotificationServiceProxy::AsWeakPtr() {
+  return weak_ptr_factory_ui_.GetWeakPtr();
+}
+
 void PlatformNotificationServiceProxy::DoDisplayNotification(
     const NotificationDatabaseData& data,
     const GURL& service_worker_scope,
diff --git a/content/browser/notifications/platform_notification_service_proxy.h b/content/browser/notifications/platform_notification_service_proxy.h
index 2989f332..c24cf42 100644
--- a/content/browser/notifications/platform_notification_service_proxy.h
+++ b/content/browser/notifications/platform_notification_service_proxy.h
@@ -28,8 +28,7 @@
 class ServiceWorkerContextWrapper;
 class ServiceWorkerRegistration;
 
-class CONTENT_EXPORT PlatformNotificationServiceProxy
-    : public base::SupportsWeakPtr<PlatformNotificationServiceProxy> {
+class CONTENT_EXPORT PlatformNotificationServiceProxy {
  public:
   using DisplayResultCallback =
       base::OnceCallback<void(bool /* success */,
@@ -41,6 +40,13 @@
 
   ~PlatformNotificationServiceProxy();
 
+  // To be called when the |browser_context_| has been shutdown. This
+  // invalidates all weak pointers. Must be called on the UI thread.
+  void Shutdown();
+
+  // Gets a weak pointer to be used on the UI thread.
+  base::WeakPtr<PlatformNotificationServiceProxy> AsWeakPtr();
+
   // Displays a notification with |data| and calls |callback| with the result.
   // This will verify against the given |service_worker_context_| if available.
   void DisplayNotification(const NotificationDatabaseData& data,
@@ -84,6 +90,7 @@
 
   scoped_refptr<ServiceWorkerContextWrapper> service_worker_context_;
   PlatformNotificationService* notification_service_;
+  base::WeakPtrFactory<PlatformNotificationServiceProxy> weak_ptr_factory_ui_;
   base::WeakPtrFactory<PlatformNotificationServiceProxy> weak_ptr_factory_io_;
 
   DISALLOW_COPY_AND_ASSIGN(PlatformNotificationServiceProxy);
diff --git a/content/browser/payments/payment_app_info_fetcher.cc b/content/browser/payments/payment_app_info_fetcher.cc
index 0a10a01..2ccd84c6 100644
--- a/content/browser/payments/payment_app_info_fetcher.cc
+++ b/content/browser/payments/payment_app_info_fetcher.cc
@@ -10,6 +10,7 @@
 #include "base/bind.h"
 #include "base/bind_helpers.h"
 #include "base/task/post_task.h"
+#include "components/payments/content/icon/icon_size.h"
 #include "content/browser/frame_host/render_frame_host_impl.h"
 #include "content/browser/service_worker/service_worker_context_wrapper.h"
 #include "content/browser/web_contents/web_contents_impl.h"
@@ -238,13 +239,6 @@
                       &(fetched_payment_app_info_->name));
   }
 
-  // TODO(gogerald): Choose appropriate icon size dynamically on different
-  // platforms.
-  // Here we choose a large ideal icon size to be big enough for all platforms.
-  // Note that we only scale down for this icon size but not scale up.
-  const int kPaymentAppIdealIconSize = 0xFFFF;
-  const int kPaymentAppMinimumIconSize = 0;
-
   if (manifest.icons.empty()) {
     WarnIfPossible(
         "Unable to download the payment handler's icon, because the web app "
@@ -256,8 +250,22 @@
     return;
   }
 
+  WebContents* web_contents = web_contents_helper_->web_contents();
+  if (!web_contents) {
+    LOG(WARNING) << "Unable to download the payment handler's icon because no "
+                    "renderer was found, possibly because the page was closed "
+                    "or navigated away during installation. User may not "
+                    "recognize this payment handler in UI, because it will be "
+                    "labeled only by its name and origin.";
+    RunCallbackAndDestroy();
+    return;
+  }
+  gfx::NativeView native_view = web_contents->GetNativeView();
+
   icon_url_ = blink::ManifestIconSelector::FindBestMatchingIcon(
-      manifest.icons, kPaymentAppIdealIconSize, kPaymentAppMinimumIconSize,
+      manifest.icons,
+      payments::IconSizeCalculator::IdealIconHeight(native_view),
+      payments::IconSizeCalculator::MinimumIconHeight(),
       ManifestIconDownloader::kMaxWidthToHeightRatio,
       blink::Manifest::ImageResource::Purpose::ANY);
   if (!icon_url_.is_valid()) {
@@ -271,19 +279,10 @@
     return;
   }
 
-  if (!web_contents_helper_->web_contents()) {
-    LOG(WARNING) << "Unable to download the payment handler's icon because no "
-                    "renderer was found, possibly because the page was closed "
-                    "or navigated away during installation. User may not "
-                    "recognize this payment handler in UI, because it will be "
-                    "labeled only by its name and origin.";
-    RunCallbackAndDestroy();
-    return;
-  }
-
   bool can_download = ManifestIconDownloader::Download(
-      web_contents_helper_->web_contents(), icon_url_, kPaymentAppIdealIconSize,
-      kPaymentAppMinimumIconSize,
+      web_contents, icon_url_,
+      payments::IconSizeCalculator::IdealIconHeight(native_view),
+      payments::IconSizeCalculator::MinimumIconHeight(),
       base::BindOnce(&PaymentAppInfoFetcher::SelfDeleteFetcher::OnIconFetched,
                      base::Unretained(this)),
       false /* square_only */);
diff --git a/content/browser/payments/payment_instrument_icon_fetcher.cc b/content/browser/payments/payment_instrument_icon_fetcher.cc
index c1e109b7..d0d47d2d 100644
--- a/content/browser/payments/payment_instrument_icon_fetcher.cc
+++ b/content/browser/payments/payment_instrument_icon_fetcher.cc
@@ -10,6 +10,7 @@
 #include "base/bind.h"
 #include "base/bind_helpers.h"
 #include "base/task/post_task.h"
+#include "components/payments/content/icon/icon_size.h"
 #include "content/browser/frame_host/render_frame_host_impl.h"
 #include "content/browser/storage_partition_impl.h"
 #include "content/browser/web_contents/web_contents_impl.h"
@@ -23,13 +24,6 @@
 namespace content {
 namespace {
 
-// TODO(zino): Choose appropriate icon size dynamically on different platforms.
-// Here we choose a large ideal icon size to be big enough for all platforms.
-// Note that we only scale down for this icon size but not scale up.
-// Please see: https://crbug.com/763886
-const int kPaymentAppIdealIconSize = 0xFFFF;
-const int kPaymentAppMinimumIconSize = 0;
-
 void DownloadBestMatchingIcon(
     WebContents* web_contents,
     const std::vector<blink::Manifest::ImageResource>& icons,
@@ -75,11 +69,20 @@
         callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
+  if (web_contents == nullptr) {
+    base::PostTaskWithTraits(
+        FROM_HERE, {BrowserThread::IO},
+        base::BindOnce(std::move(callback), std::string()));
+    return;
+  }
+
+  gfx::NativeView native_view = web_contents->GetNativeView();
   GURL icon_url = blink::ManifestIconSelector::FindBestMatchingIcon(
-      icons, kPaymentAppIdealIconSize, kPaymentAppMinimumIconSize,
+      icons, payments::IconSizeCalculator::IdealIconHeight(native_view),
+      payments::IconSizeCalculator::MinimumIconHeight(),
       ManifestIconDownloader::kMaxWidthToHeightRatio,
       blink::Manifest::ImageResource::Purpose::ANY);
-  if (web_contents == nullptr || !icon_url.is_valid()) {
+  if (!icon_url.is_valid()) {
     // If the icon url is invalid, it's better to give the information to
     // developers in advance unlike when fetching or decoding fails. We already
     // checked whether they are valid in renderer side. So, if the icon url is
@@ -98,8 +101,9 @@
   }
 
   bool can_download_icon = ManifestIconDownloader::Download(
-      web_contents, icon_url, kPaymentAppIdealIconSize,
-      kPaymentAppMinimumIconSize,
+      web_contents, icon_url,
+      payments::IconSizeCalculator::IdealIconHeight(native_view),
+      payments::IconSizeCalculator::MinimumIconHeight(),
       base::BindOnce(&OnIconFetched, web_contents, copy_icons,
                      std::move(callback)),
       false /* square_only */);
diff --git a/content/browser/renderer_host/compositor_impl_android.cc b/content/browser/renderer_host/compositor_impl_android.cc
index 992eedf..4420a91a 100644
--- a/content/browser/renderer_host/compositor_impl_android.cc
+++ b/content/browser/renderer_host/compositor_impl_android.cc
@@ -478,6 +478,9 @@
 
   unsigned UpdateGpuFence() override { return 0; }
 
+  void SetUpdateVSyncParametersCallback(
+      viz::UpdateVSyncParametersCallback callback) override {}
+
  private:
   gpu::CommandBufferProxyImpl* GetCommandBufferProxy() {
     ws::ContextProviderCommandBuffer* provider_command_buffer =
diff --git a/content/browser/renderer_host/dwrite_font_lookup_table_builder_win.cc b/content/browser/renderer_host/dwrite_font_lookup_table_builder_win.cc
index dd757b8..38d33e7 100644
--- a/content/browser/renderer_host/dwrite_font_lookup_table_builder_win.cc
+++ b/content/browser/renderer_host/dwrite_font_lookup_table_builder_win.cc
@@ -170,6 +170,11 @@
   // running an older version of DirectWrite (earlier than Win8.1).
   factory.As<IDWriteFactory2>(&factory2_);
 
+  // QueryInterface for IDwriteFactory3, needed for MatchUniqueFont on Windows
+  // 10. May fail on older versions, in which case, unique font matching must be
+  // done through indexing system fonts using DWriteFontLookupTableBuilder.
+  factory.As<IDWriteFactory3>(&factory3_);
+
   HRESULT hr = factory->GetSystemFontCollection(&collection_);
   DCHECK(SUCCEEDED(hr));
 
@@ -228,6 +233,16 @@
   caching_enabled_ = caching_enabled;
 }
 
+bool DWriteFontLookupTableBuilder::HasDWriteUniqueFontLookups() {
+  InitializeDirectWrite();
+  return factory3_;
+}
+
+void DWriteFontLookupTableBuilder::OverrideDWriteVersionChecksForTesting() {
+  InitializeDirectWrite();
+  factory3_.Reset();
+}
+
 base::FilePath DWriteFontLookupTableBuilder::TableCacheFilePath() {
   if (!EnsureCacheDirectory(cache_directory_))
     return base::FilePath();
@@ -303,6 +318,7 @@
   TRACE_EVENT0("dwrite,fonts",
                "DWriteFontLookupTableBuilder::EnsureFontUniqueNameTable");
   DCHECK(base::FeatureList::IsEnabled(features::kFontSrcLocalMatching));
+  DCHECK(!HasDWriteUniqueFontLookups());
   base::ScopedAllowBaseSyncPrimitives allow_base_sync_primitives;
   font_table_built_.Wait();
   return IsFontUniqueNameTableValid();
@@ -312,9 +328,20 @@
   return font_table_built_.IsSignaled() && IsFontUniqueNameTableValid();
 }
 
-void DWriteFontLookupTableBuilder::SchedulePrepareFontUniqueNameTable() {
+void DWriteFontLookupTableBuilder::
+    SchedulePrepareFontUniqueNameTableIfNeeded() {
   DCHECK(base::FeatureList::IsEnabled(features::kFontSrcLocalMatching));
 
+  {
+    base::ScopedBlockingCall scoped_blocking_call(
+        FROM_HERE, base::BlockingType::MAY_BLOCK);
+    InitializeDirectWrite();
+  }
+
+  // Nothing to do if we have API to directly lookup local fonts by unique name.
+  if (HasDWriteUniqueFontLookups())
+    return;
+
   start_time_table_ready_ = base::TimeTicks::Now();
 
   // TODO(https://crbug.com/931366): Downgrade the priority of this startup
@@ -335,6 +362,7 @@
 void DWriteFontLookupTableBuilder::PrepareFontUniqueNameTable() {
   TRACE_EVENT0("dwrite,fonts",
                "DWriteFontLookupTableBuilder::BuildFontUniqueNameTable");
+  DCHECK(!HasDWriteUniqueFontLookups());
   // The table must only be built once.
   DCHECK(!font_table_built_.IsSignaled());
 
@@ -359,12 +387,6 @@
     }
   }
 
-  {
-    base::ScopedBlockingCall scoped_blocking_call(
-        FROM_HERE, base::BlockingType::MAY_BLOCK);
-    InitializeDirectWrite();
-  }
-
   start_time_table_build_ = base::TimeTicks::Now();
   font_unique_name_table_ = std::make_unique<blink::FontUniqueNameTable>();
 
diff --git a/content/browser/renderer_host/dwrite_font_lookup_table_builder_win.h b/content/browser/renderer_host/dwrite_font_lookup_table_builder_win.h
index bbd066fb..8262f59 100644
--- a/content/browser/renderer_host/dwrite_font_lookup_table_builder_win.h
+++ b/content/browser/renderer_host/dwrite_font_lookup_table_builder_win.h
@@ -7,6 +7,7 @@
 
 #include <dwrite.h>
 #include <dwrite_2.h>
+#include <dwrite_3.h>
 #include <wrl.h>
 #include <memory>
 #include <string>
@@ -56,11 +57,12 @@
   // immediately ready without any sync operations.
   bool FontUniqueNameTableReady();
 
-  // Posts a task to load from cache or build (if cache not available) the
-  // unique name table index, should only be called once at browser startup,
-  // after that, use EnsureFontUniqueNameTable() and
-  // DuplicatedMemoryRegion() to retrieve the lookup structure buffer.
-  void SchedulePrepareFontUniqueNameTable();
+  // If needed, i.e. if we're on pre-Windows 10, posts a task to load from cache
+  // or build (if cache not available) the unique name table index, should only
+  // be called once at browser startup, after that, use
+  // EnsureFontUniqueNameTable() and DuplicatedMemoryRegion() to retrieve the
+  // lookup structure buffer.
+  void SchedulePrepareFontUniqueNameTableIfNeeded();
 
   enum class SlowDownMode { kDelayEachTask, kHangOneTask, kNoSlowdown };
 
@@ -91,6 +93,10 @@
   // repeated rebuilding of the font table lookup structure.
   void SetCachingEnabledForTesting(bool caching_enabled);
 
+  // Disables DCHECKs that ensure DWriteFontLookupTableBuilder is only run pre
+  // Windows 10, used for testing only to allow running the tests on Windows 10.
+  void OverrideDWriteVersionChecksForTesting();
+
  private:
   friend class base::NoDestructor<DWriteFontLookupTableBuilder>;
 
@@ -149,6 +155,11 @@
 
   base::FilePath TableCacheFilePath();
 
+  // Returns true if IDWriteFactory3 is available, which means that we can
+  // access IDWriteFontSet API which provides direct lookup by PostScript name
+  // and full font name, in which case we do not need to build this table.
+  bool HasDWriteUniqueFontLookups();
+
   DWriteFontLookupTableBuilder();
   ~DWriteFontLookupTableBuilder();
 
@@ -161,6 +172,7 @@
   bool direct_write_initialized_ = false;
   Microsoft::WRL::ComPtr<IDWriteFontCollection> collection_;
   Microsoft::WRL::ComPtr<IDWriteFactory2> factory2_;
+  Microsoft::WRL::ComPtr<IDWriteFactory3> factory3_;
   SlowDownMode slow_down_mode_for_testing_ = SlowDownMode::kNoSlowdown;
   uint32_t outstanding_family_results_ = 0;
   base::TimeTicks start_time_table_ready_;
diff --git a/content/browser/renderer_host/dwrite_font_lookup_table_builder_win_unittest.cc b/content/browser/renderer_host/dwrite_font_lookup_table_builder_win_unittest.cc
index 2e9ebfdd..2e8e8fc 100644
--- a/content/browser/renderer_host/dwrite_font_lookup_table_builder_win_unittest.cc
+++ b/content/browser/renderer_host/dwrite_font_lookup_table_builder_win_unittest.cc
@@ -42,6 +42,7 @@
 
   void SetUp() override {
     font_lookup_table_builder_ = DWriteFontLookupTableBuilder::GetInstance();
+    font_lookup_table_builder_->OverrideDWriteVersionChecksForTesting();
     font_lookup_table_builder_->ResetLookupTableForTesting();
     bool temp_dir_created = scoped_temp_dir_.CreateUniqueTempDir();
     ASSERT_TRUE(temp_dir_created);
@@ -88,14 +89,14 @@
 // without going through Mojo and running it on the DWRiteFontLookupTableBuilder
 // class directly.
 TEST_F(DWriteFontLookupTableBuilderTest, TestFindUniqueFontDirect) {
-  font_lookup_table_builder_->SchedulePrepareFontUniqueNameTable();
+  font_lookup_table_builder_->SchedulePrepareFontUniqueNameTableIfNeeded();
   font_lookup_table_builder_->EnsureFontUniqueNameTable();
   TestMatchFonts();
 }
 
 TEST_P(DWriteFontLookupTableBuilderTimeoutTest, TestTimeout) {
   font_lookup_table_builder_->SetSlowDownIndexingForTesting(GetParam());
-  font_lookup_table_builder_->SchedulePrepareFontUniqueNameTable();
+  font_lookup_table_builder_->SchedulePrepareFontUniqueNameTableIfNeeded();
   font_lookup_table_builder_->EnsureFontUniqueNameTable();
   base::ReadOnlySharedMemoryRegion font_table_memory =
       font_lookup_table_builder_->DuplicateMemoryRegion();
@@ -120,7 +121,7 @@
 TEST_F(DWriteFontLookupTableBuilderTest, TestReadyEarly) {
   font_lookup_table_builder_->SetSlowDownIndexingForTesting(
       DWriteFontLookupTableBuilder::SlowDownMode::kHangOneTask);
-  font_lookup_table_builder_->SchedulePrepareFontUniqueNameTable();
+  font_lookup_table_builder_->SchedulePrepareFontUniqueNameTableIfNeeded();
   ASSERT_FALSE(font_lookup_table_builder_->FontUniqueNameTableReady());
   font_lookup_table_builder_->ResumeFromHangForTesting();
   font_lookup_table_builder_->EnsureFontUniqueNameTable();
@@ -131,7 +132,7 @@
   for (unsigned i = 0; i < 3; ++i) {
     font_lookup_table_builder_->ResetLookupTableForTesting();
     font_lookup_table_builder_->SetCachingEnabledForTesting(false);
-    font_lookup_table_builder_->SchedulePrepareFontUniqueNameTable();
+    font_lookup_table_builder_->SchedulePrepareFontUniqueNameTableIfNeeded();
     font_lookup_table_builder_->EnsureFontUniqueNameTable();
   }
 }
@@ -143,7 +144,7 @@
 TEST_F(DWriteFontLookupTableBuilderTest, HandleCorruptCacheFile) {
   // Cycle once to build cache file.
   font_lookup_table_builder_->ResetLookupTableForTesting();
-  font_lookup_table_builder_->SchedulePrepareFontUniqueNameTable();
+  font_lookup_table_builder_->SchedulePrepareFontUniqueNameTableIfNeeded();
   font_lookup_table_builder_->EnsureFontUniqueNameTable();
   // Truncate table for testing
   base::FilePath cache_file_path = scoped_temp_dir_.GetPath().Append(
@@ -164,7 +165,7 @@
 
   // Reload the cache file.
   font_lookup_table_builder_->ResetLookupTableForTesting();
-  font_lookup_table_builder_->SchedulePrepareFontUniqueNameTable();
+  font_lookup_table_builder_->SchedulePrepareFontUniqueNameTableIfNeeded();
   ASSERT_TRUE(font_lookup_table_builder_->EnsureFontUniqueNameTable());
 
   TestMatchFonts();
diff --git a/content/browser/renderer_host/dwrite_font_proxy_impl_win_unittest.cc b/content/browser/renderer_host/dwrite_font_proxy_impl_win_unittest.cc
index aa6f45f..c64885b 100644
--- a/content/browser/renderer_host/dwrite_font_proxy_impl_win_unittest.cc
+++ b/content/browser/renderer_host/dwrite_font_proxy_impl_win_unittest.cc
@@ -42,6 +42,16 @@
                                                   {u8"NSimSun", 1},
                                                   {u8"calibri-bolditalic", 0}};
 
+// DirectWrite on Windows supports IDWriteFontSet API which allows for querying
+// by PostScript name and full font name directly. In the implementation of
+// DWriteFontProxy we check whether this API is available by checking for
+// whether IDWriteFactory3 is available. In order to validate in a unit test
+// whether this check works, compare it against the dwrite.dll major version -
+// versions starting from 10 have the required functionality.
+constexpr int kDWriteMajorVersionSupportingSingleLookups = 10;
+
+// Base test class that sets up the Mojo connection to DWriteFontProxy so that
+// tests can call its Mojo methods.
 class DWriteFontProxyImplUnitTest : public testing::Test {
  public:
   DWriteFontProxyImplUnitTest()
@@ -51,34 +61,50 @@
     return *dwrite_font_proxy_;
   }
 
-  base::test::ScopedTaskEnvironment scoped_task_environment_;
-  blink::mojom::DWriteFontProxyPtr dwrite_font_proxy_;
-  DWriteFontProxyImpl impl_;
-  mojo::Binding<blink::mojom::DWriteFontProxy> binding_;
-};
-
-class DWriteFontProxyUniqueNameMatchingTest
-    : public DWriteFontProxyImplUnitTest {
- public:
-  DWriteFontProxyUniqueNameMatchingTest() {
-    feature_list_.InitAndEnableFeature(features::kFontSrcLocalMatching);
-    DWriteFontLookupTableBuilder* table_builder_instance =
-        DWriteFontLookupTableBuilder::GetInstance();
-    DCHECK(scoped_temp_dir_.CreateUniqueTempDir());
-    table_builder_instance->SetCacheDirectoryForTesting(
-        scoped_temp_dir_.GetPath());
-    table_builder_instance->ResetLookupTableForTesting();
-    table_builder_instance->SchedulePrepareFontUniqueNameTable();
-  }
-
   bool SupportsSingleLookups() {
     blink::mojom::UniqueFontLookupMode lookup_mode;
     dwrite_font_proxy().GetUniqueFontLookupMode(&lookup_mode);
     return lookup_mode == blink::mojom::UniqueFontLookupMode::kSingleLookups;
   }
 
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
+  blink::mojom::DWriteFontProxyPtr dwrite_font_proxy_;
+  DWriteFontProxyImpl impl_;
+  mojo::Binding<blink::mojom::DWriteFontProxy> binding_;
+};
+
+// Derived class for tests that exercise font unique local matching mojo methods
+// of DWriteFontProxy. Needs a ScopedFeatureList to activate the feature as it
+// is currently behind a flag.
+class DWriteFontProxyLocalMatchingTest : public DWriteFontProxyImplUnitTest {
+ public:
+  DWriteFontProxyLocalMatchingTest() {
+    feature_list_.InitAndEnableFeature(features::kFontSrcLocalMatching);
+  }
+
  private:
   base::test::ScopedFeatureList feature_list_;
+};
+
+// Derived class for tests that exercise the parts of the DWriteFontProxy Mojo
+// interface that deal with accessing the font lookup table created by
+// DWriteFontLookupTableBuilder. Initializes the DWriteFontLookupTableBuilder
+// and has a ScopedTempDir for testing persisting the lookup table to disk.
+class DWriteFontProxyTableMatchingTest
+    : public DWriteFontProxyLocalMatchingTest {
+ public:
+  DWriteFontProxyTableMatchingTest() {
+    DWriteFontLookupTableBuilder* table_builder_instance =
+        DWriteFontLookupTableBuilder::GetInstance();
+    DCHECK(scoped_temp_dir_.CreateUniqueTempDir());
+    table_builder_instance->OverrideDWriteVersionChecksForTesting();
+    table_builder_instance->SetCacheDirectoryForTesting(
+        scoped_temp_dir_.GetPath());
+    table_builder_instance->ResetLookupTableForTesting();
+    table_builder_instance->SchedulePrepareFontUniqueNameTableIfNeeded();
+  }
+
+ private:
   base::ScopedTempDir scoped_temp_dir_;
 };
 
@@ -247,7 +273,7 @@
 }
 }  // namespace
 
-TEST_F(DWriteFontProxyUniqueNameMatchingTest, TestFindUniqueFont) {
+TEST_F(DWriteFontProxyTableMatchingTest, TestFindUniqueFont) {
   bool lookup_table_results_were_tested = false;
   dwrite_font_proxy().GetUniqueNameLookupTable(base::BindOnce(
       &TestWhenLookupTableReady, &lookup_table_results_were_tested));
@@ -255,7 +281,7 @@
   ASSERT_TRUE(lookup_table_results_were_tested);
 }
 
-TEST_F(DWriteFontProxyUniqueNameMatchingTest, TestSingleLookup) {
+TEST_F(DWriteFontProxyLocalMatchingTest, TestSingleLookup) {
   // Do not run this test on unsupported Windows versions.
   if (!SupportsSingleLookups())
     return;
@@ -274,7 +300,7 @@
   }
 }
 
-TEST_F(DWriteFontProxyUniqueNameMatchingTest, TestSingleLookupUnavailable) {
+TEST_F(DWriteFontProxyLocalMatchingTest, TestSingleLookupUnavailable) {
   // Do not run this test on unsupported Windows versions.
   if (!SupportsSingleLookups())
     return;
@@ -288,7 +314,7 @@
   ASSERT_EQ(ttc_index, 0u);
 }
 
-TEST_F(DWriteFontProxyUniqueNameMatchingTest, TestLookupMode) {
+TEST_F(DWriteFontProxyLocalMatchingTest, TestLookupMode) {
   std::unique_ptr<FileVersionInfo> dwrite_version_info =
       FileVersionInfo::CreateFileVersionInfo(
           base::FilePath(FILE_PATH_LITERAL("DWrite.dll")));
@@ -300,7 +326,8 @@
       std::stoi(dwrite_version.substr(0, dwrite_version.find(".")));
 
   blink::mojom::UniqueFontLookupMode expected_lookup_mode;
-  if (dwrite_major_version_number >= 10) {
+  if (dwrite_major_version_number >=
+      kDWriteMajorVersionSupportingSingleLookups) {
     expected_lookup_mode = blink::mojom::UniqueFontLookupMode::kSingleLookups;
   } else {
     expected_lookup_mode = blink::mojom::UniqueFontLookupMode::kRetrieveTable;
diff --git a/content/browser/scheduler/browser_task_executor.cc b/content/browser/scheduler/browser_task_executor.cc
index 81131371..18d61e1 100644
--- a/content/browser/scheduler/browser_task_executor.cc
+++ b/content/browser/scheduler/browser_task_executor.cc
@@ -372,7 +372,7 @@
 }
 
 // static
-void BrowserTaskExecutor::NotifyBrowserStartupCompleted() {
+void BrowserTaskExecutor::EnableBestEffortQueues() {
   DCHECK(g_browser_task_executor);
   g_browser_task_executor->browser_ui_thread_scheduler_
       ->EnableBestEffortQueues();
diff --git a/content/browser/scheduler/browser_task_executor.h b/content/browser/scheduler/browser_task_executor.h
index 65a084e..9c00991 100644
--- a/content/browser/scheduler/browser_task_executor.h
+++ b/content/browser/scheduler/browser_task_executor.h
@@ -42,7 +42,7 @@
   // called.
   static void Shutdown();
 
-  static void NotifyBrowserStartupCompleted();
+  static void EnableBestEffortQueues();
 
   // Unregister and delete the TaskExecutor after a test.
   static void ResetForTesting();
diff --git a/content/browser/scheduler/browser_task_executor_unittest.cc b/content/browser/scheduler/browser_task_executor_unittest.cc
index 995755c..0d47c88 100644
--- a/content/browser/scheduler/browser_task_executor_unittest.cc
+++ b/content/browser/scheduler/browser_task_executor_unittest.cc
@@ -230,7 +230,7 @@
                            best_effort.Get());
   scoped_task_environment_.RunUntilIdle();
 
-  BrowserTaskExecutor::NotifyBrowserStartupCompleted();
+  BrowserTaskExecutor::EnableBestEffortQueues();
   EXPECT_CALL(best_effort, Run).Times(4);
   scoped_task_environment_.FastForwardBy(
       base::TimeDelta::FromMilliseconds(100));
diff --git a/content/browser/scheduler/browser_ui_thread_scheduler.cc b/content/browser/scheduler/browser_ui_thread_scheduler.cc
index 54cae85..16399f7 100644
--- a/content/browser/scheduler/browser_ui_thread_scheduler.cc
+++ b/content/browser/scheduler/browser_ui_thread_scheduler.cc
@@ -123,7 +123,6 @@
 }
 
 void BrowserUIThreadScheduler::EnableBestEffortQueues() {
-  DCHECK(!best_effort_voter_->IsVotingToEnable());
   best_effort_voter_->SetVoteToEnable(true);
 }
 
diff --git a/content/browser/scheduler/browser_ui_thread_scheduler.h b/content/browser/scheduler/browser_ui_thread_scheduler.h
index 17f7c4f..5db6d42 100644
--- a/content/browser/scheduler/browser_ui_thread_scheduler.h
+++ b/content/browser/scheduler/browser_ui_thread_scheduler.h
@@ -42,7 +42,6 @@
   // however no tasks will be executed after this point.
   void Shutdown();
 
-  // Attention: Can only be called once
   void EnableBestEffortQueues();
 
   using QueueType = BrowserUIThreadTaskQueue::QueueType;
diff --git a/content/browser/tracing/background_tracing_active_scenario.cc b/content/browser/tracing/background_tracing_active_scenario.cc
index 7fad9c7..558bc51 100644
--- a/content/browser/tracing/background_tracing_active_scenario.cc
+++ b/content/browser/tracing/background_tracing_active_scenario.cc
@@ -147,12 +147,7 @@
   // Perfetto-related deadlocks are resolved.
   if (!TracingControllerImpl::GetInstance()->IsTracing() &&
       tracing::TracingUsesPerfettoBackend()) {
-    // TODO(oysteine): This should pass in |requires_anonymized_data_| instead
-    // of false only when using consumer API with proto output. But, for JSON
-    // output we still need this to be false since filtering happens in the JSON
-    // exporter.
-    tracing::TraceEventDataSource::GetInstance()->SetupStartupTracing(
-        /*privacy_filtering_enabled=*/false);
+    tracing::TraceEventDataSource::GetInstance()->SetupStartupTracing();
   }
 #endif
 
diff --git a/content/browser/tracing/perfetto_file_tracer.cc b/content/browser/tracing/perfetto_file_tracer.cc
index d1f93a13..b977c4d 100644
--- a/content/browser/tracing/perfetto_file_tracer.cc
+++ b/content/browser/tracing/perfetto_file_tracer.cc
@@ -9,7 +9,6 @@
 #include "base/command_line.h"
 #include "base/files/file.h"
 #include "base/files/file_util.h"
-#include "base/strings/string_number_conversions.h"
 #include "base/task/post_task.h"
 #include "base/threading/scoped_blocking_call.h"
 #include "components/tracing/common/trace_startup_config.h"
@@ -17,7 +16,6 @@
 #include "content/public/common/service_manager_connection.h"
 #include "mojo/public/cpp/system/data_pipe_drainer.h"
 #include "services/service_manager/public/cpp/connector.h"
-#include "services/tracing/public/cpp/perfetto/perfetto_config.h"
 #include "services/tracing/public/mojom/constants.mojom.h"
 #include "third_party/perfetto/include/perfetto/tracing/core/trace_config.h"
 #include "third_party/perfetto/protos/perfetto/config/trace_config.pb.h"
@@ -91,27 +89,36 @@
   ServiceManagerConnection::GetForProcess()->GetConnector()->BindInterface(
       tracing::mojom::kServiceName, &consumer_host_);
 
-  const auto& chrome_config =
-      tracing::TraceStartupConfig::GetInstance()->GetTraceConfig();
-  perfetto::TraceConfig trace_config =
-      tracing::GetDefaultPerfettoConfig(chrome_config);
-
-  // TODO(ssid): This should be moved to GetDefaultPerfettoConfig(). But,
-  // currently we only require this for proto output since JSON exporter still
-  // needs sensitive fields in trace, which will later be stripped.
-  for (auto& source : *trace_config.mutable_data_sources()) {
-    source.mutable_config()
-        ->mutable_chrome_config()
-        ->set_privacy_filtering_enabled(
-            chrome_config.IsArgumentFilterEnabled());
-  }
-
+  perfetto::TraceConfig trace_config;
   int duration_in_seconds =
       tracing::TraceStartupConfig::GetInstance()->GetStartupDuration();
   trace_config.set_duration_ms(duration_in_seconds * 1000);
 
   // We just need a single global trace buffer, for our data.
-  trace_config.mutable_buffers()->front().set_size_kb(32 * 1024);
+  trace_config.add_buffers()->set_size_kb(32 * 1024);
+
+  // We need data from two different sources to get the complete trace
+  // we're interested in both, written into the single buffer we
+  // configure above.
+
+  // This source is the actual trace events themselves coming
+  // from the base::TraceLog
+  auto* trace_event_config = trace_config.add_data_sources()->mutable_config();
+  trace_event_config->set_name(tracing::mojom::kTraceEventDataSourceName);
+  trace_event_config->set_target_buffer(0);
+  auto* chrome_config = trace_event_config->mutable_chrome_config();
+
+  // The Chrome config string is passed straight through to base::TraceLog
+  // and defines which tracing categories should be enabled.
+  auto chrome_raw_config =
+      tracing::TraceStartupConfig::GetInstance()->GetTraceConfig().ToString();
+  chrome_config->set_trace_config(chrome_raw_config);
+
+  // The second data source we're interested in is the global metadata.
+  auto* trace_metadata_config =
+      trace_config.add_data_sources()->mutable_config();
+  trace_metadata_config->set_name(tracing::mojom::kMetaDataSourceName);
+  trace_metadata_config->set_target_buffer(0);
 
   tracing::mojom::TracingSessionPtr tracing_session;
   binding_.Bind(mojo::MakeRequest(&tracing_session));
diff --git a/content/browser/tracing/perfetto_file_tracer.h b/content/browser/tracing/perfetto_file_tracer.h
index df24c5f..e6ca8ac77 100644
--- a/content/browser/tracing/perfetto_file_tracer.h
+++ b/content/browser/tracing/perfetto_file_tracer.h
@@ -33,8 +33,6 @@
   // tracing::mojom::TracingSession implementation:
   void OnTracingEnabled() override;
 
-  bool is_finished_for_testing() const { return !background_drainer_; }
-
  private:
   void OnNoMorePackets(bool queued_after_disable);
   void ReadBuffers();
diff --git a/content/browser/tracing/startup_tracing_browsertest.cc b/content/browser/tracing/startup_tracing_browsertest.cc
index cce8cad9..02cc57c9 100644
--- a/content/browser/tracing/startup_tracing_browsertest.cc
+++ b/content/browser/tracing/startup_tracing_browsertest.cc
@@ -11,11 +11,9 @@
 #include "build/build_config.h"
 #include "components/tracing/common/trace_startup_config.h"
 #include "components/tracing/common/tracing_switches.h"
-#include "content/browser/tracing/perfetto_file_tracer.h"
 #include "content/browser/tracing/tracing_controller_impl.h"
 #include "content/public/test/content_browser_test.h"
 #include "content/public/test/content_browser_test_utils.h"
-#include "services/tracing/perfetto/privacy_filtering_check.h"
 #include "services/tracing/public/cpp/perfetto/trace_event_data_source.h"
 #include "services/tracing/public/cpp/trace_startup.h"
 #include "services/tracing/public/cpp/tracing_features.h"
@@ -27,7 +25,7 @@
 // Wait until |condition| returns true.
 void WaitForCondition(base::RepeatingCallback<bool()> condition,
                       const std::string& description) {
-  const base::TimeDelta kTimeout = base::TimeDelta::FromSeconds(15);
+  const base::TimeDelta kTimeout = base::TimeDelta::FromSeconds(30);
   const base::TimeTicks start_time = base::TimeTicks::Now();
   while (!condition.Run() && (base::TimeTicks::Now() - start_time < kTimeout)) {
     base::RunLoop run_loop;
@@ -41,10 +39,7 @@
 
 }  // namespace
 
-class CommandlineStartupTracingTest : public ContentBrowserTest {
- public:
-  CommandlineStartupTracingTest() = default;
-
+class StartupTracingControllerTest : public ContentBrowserTest {
   void SetUpCommandLine(base::CommandLine* command_line) override {
     base::CreateTemporaryFile(&temp_file_path_);
     command_line->AppendSwitch(switches::kTraceStartup);
@@ -63,13 +58,10 @@
 
  protected:
   base::FilePath temp_file_path_;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(CommandlineStartupTracingTest);
 };
 
-IN_PROC_BROWSER_TEST_F(CommandlineStartupTracingTest, TestStartupTracing) {
-  NavigateToURL(shell(), GetTestUrl("", "title1.html"));
+IN_PROC_BROWSER_TEST_F(StartupTracingControllerTest, TestStartupTracing) {
+  NavigateToURL(shell(), GetTestUrl("", "title.html"));
   WaitForCondition(base::BindRepeating([]() {
                      return !TracingController::GetInstance()->IsTracing();
                    }),
@@ -124,8 +116,7 @@
 // deadlocks.
 IN_PROC_BROWSER_TEST_F(StartupTracingInProcessTest,
                        DISABLED_TestFilledStartupBuffer) {
-  tracing::TraceEventDataSource::GetInstance()->SetupStartupTracing(
-      /*privacy_filtering_enabled=*/false);
+  tracing::TraceEventDataSource::GetInstance()->SetupStartupTracing();
 
   auto config = tracing::TraceStartupConfig::GetInstance()
                     ->GetDefaultBrowserStartupConfig();
@@ -147,56 +138,4 @@
   NavigateToURL(shell(), GetTestUrl("", "title1.html"));
 }
 
-class BackgroundStartupTracingTest : public ContentBrowserTest {
- public:
-  BackgroundStartupTracingTest() = default;
-
-  void SetUpCommandLine(base::CommandLine* command_line) override {
-    base::CreateTemporaryFile(&temp_file_path_);
-    auto* startup_config = tracing::TraceStartupConfig::GetInstance();
-    startup_config->is_enabled_from_background_tracing_ = true;
-    startup_config->EnableFromBackgroundTracing();
-    startup_config->startup_duration_ = 3;
-    tracing::EnableStartupTracingIfNeeded();
-    command_line->AppendSwitchASCII(switches::kPerfettoOutputFile,
-                                    temp_file_path_.AsUTF8Unsafe());
-  }
-
- protected:
-  base::FilePath temp_file_path_;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(BackgroundStartupTracingTest);
-};
-
-#if defined(IS_CHROMECAST) && defined(OS_LINUX)
-#define MAYBE_TestStartupTracing DISABLED_TestStartupTracing
-#else
-#define MAYBE_TestStartupTracing TestStartupTracing
-#endif
-IN_PROC_BROWSER_TEST_F(BackgroundStartupTracingTest, MAYBE_TestStartupTracing) {
-  NavigateToURL(shell(), GetTestUrl("", "title1.html"));
-
-  EXPECT_FALSE(tracing::TraceStartupConfig::GetInstance()->IsEnabled());
-  EXPECT_FALSE(TracingController::GetInstance()->IsTracing());
-  WaitForCondition(base::BindRepeating([]() {
-                     return TracingControllerImpl::GetInstance()
-                         ->perfetto_file_tracer_for_testing()
-                         ->is_finished_for_testing();
-                   }),
-                   "finish file write");
-
-  std::string trace;
-  base::ScopedAllowBlockingForTesting allow_blocking;
-  ASSERT_TRUE(base::ReadFileToString(temp_file_path_, &trace));
-  tracing::PrivacyFilteringCheck checker;
-  checker.CheckProtoForUnexpectedFields(trace);
-  EXPECT_GT(checker.counts().track_event, 0u);
-  EXPECT_EQ(checker.counts().process_desc, 0u);
-  EXPECT_GT(checker.counts().thread_desc, 0u);
-  EXPECT_GT(checker.counts().interned_name, 0u);
-  EXPECT_GT(checker.counts().interned_category, 0u);
-  EXPECT_GT(checker.counts().interned_source_location, 0u);
-}
-
 }  // namespace content
diff --git a/content/browser/tracing/tracing_controller_impl.h b/content/browser/tracing/tracing_controller_impl.h
index c91a3b7..58242048f 100644
--- a/content/browser/tracing/tracing_controller_impl.h
+++ b/content/browser/tracing/tracing_controller_impl.h
@@ -84,10 +84,6 @@
   // exists.
   void FinalizeStartupTracingIfNeeded();
 
-  const PerfettoFileTracer* perfetto_file_tracer_for_testing() const {
-    return perfetto_file_tracer_.get();
-  }
-
  private:
   friend std::default_delete<TracingControllerImpl>;
 
diff --git a/content/browser/webrtc/webrtc_image_capture_browsertest.cc b/content/browser/webrtc/webrtc_image_capture_browsertest.cc
index 7137b79..f627aac 100644
--- a/content/browser/webrtc/webrtc_image_capture_browsertest.cc
+++ b/content/browser/webrtc/webrtc_image_capture_browsertest.cc
@@ -24,24 +24,12 @@
 namespace content {
 
 #if defined(OS_ANDROID)
-// TODO(chfremer): Re-enable test on Android as soon as the cause for
-// https://crbug.com/793859 is understood and fixed.
-#define MAYBE_GetPhotoCapabilities GetPhotoCapabilities
-#define MAYBE_GetPhotoSettings GetPhotoSettings
-#define MAYBE_TakePhoto TakePhoto
-#define MAYBE_GrabFrame GrabFrame
-#define MAYBE_GetTrackCapabilities GetTrackCapabilities
-#define MAYBE_GetTrackSettings GetTrackSettings
+// TODO(crbug.com/793859): Re-enable test on Android as soon as the cause for
+// the bug is understood and fixed.
 #define MAYBE_ManipulateZoom DISABLED_ManipulateZoom
 #define MAYBE_ManipulateExposureTime DISABLED_ManipulateExposureTime
 #define MAYBE_ManipulateFocusDistance DISABLED_ManipulateFocusDistance
 #else
-#define MAYBE_GetPhotoCapabilities GetPhotoCapabilities
-#define MAYBE_GetPhotoSettings GetPhotoSettings
-#define MAYBE_TakePhoto TakePhoto
-#define MAYBE_GrabFrame GrabFrame
-#define MAYBE_GetTrackCapabilities GetTrackCapabilities
-#define MAYBE_GetTrackSettings GetTrackSettings
 #define MAYBE_ManipulateZoom ManipulateZoom
 #define MAYBE_ManipulateExposureTime ManipulateExposureTime
 #define MAYBE_ManipulateFocusDistance ManipulateFocusDistance
@@ -190,37 +178,37 @@
 };
 
 IN_PROC_BROWSER_TEST_P(WebRtcImageCaptureSucceedsBrowserTest,
-                       MAYBE_GetPhotoCapabilities) {
+                       GetPhotoCapabilities) {
   embedded_test_server()->StartAcceptingConnections();
   ASSERT_TRUE(
       RunImageCaptureTestCase("testCreateAndGetPhotoCapabilitiesSucceeds()"));
 }
 
 IN_PROC_BROWSER_TEST_P(WebRtcImageCaptureSucceedsBrowserTest,
-                       MAYBE_GetPhotoSettings) {
+                       GetPhotoSettings) {
   embedded_test_server()->StartAcceptingConnections();
   ASSERT_TRUE(
       RunImageCaptureTestCase("testCreateAndGetPhotoSettingsSucceeds()"));
 }
 
-IN_PROC_BROWSER_TEST_P(WebRtcImageCaptureSucceedsBrowserTest, MAYBE_TakePhoto) {
+IN_PROC_BROWSER_TEST_P(WebRtcImageCaptureSucceedsBrowserTest, TakePhoto) {
   embedded_test_server()->StartAcceptingConnections();
   ASSERT_TRUE(RunImageCaptureTestCase("testCreateAndTakePhotoSucceeds()"));
 }
 
-IN_PROC_BROWSER_TEST_P(WebRtcImageCaptureSucceedsBrowserTest, MAYBE_GrabFrame) {
+IN_PROC_BROWSER_TEST_P(WebRtcImageCaptureSucceedsBrowserTest, GrabFrame) {
   embedded_test_server()->StartAcceptingConnections();
   ASSERT_TRUE(RunImageCaptureTestCase("testCreateAndGrabFrameSucceeds()"));
 }
 
 IN_PROC_BROWSER_TEST_P(WebRtcImageCaptureSucceedsBrowserTest,
-                       MAYBE_GetTrackCapabilities) {
+                       GetTrackCapabilities) {
   embedded_test_server()->StartAcceptingConnections();
   ASSERT_TRUE(RunImageCaptureTestCase("testCreateAndGetTrackCapabilities()"));
 }
 
 IN_PROC_BROWSER_TEST_P(WebRtcImageCaptureSucceedsBrowserTest,
-                       MAYBE_GetTrackSettings) {
+                       GetTrackSettings) {
   embedded_test_server()->StartAcceptingConnections();
   ASSERT_TRUE(RunImageCaptureTestCase("testCreateAndGetTrackSettings()"));
 }
diff --git a/content/common/throttling_url_loader.cc b/content/common/throttling_url_loader.cc
index aca8fa0..9943363a 100644
--- a/content/common/throttling_url_loader.cc
+++ b/content/common/throttling_url_loader.cc
@@ -10,6 +10,7 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "net/http/http_status_code.h"
 #include "net/http/http_util.h"
+#include "net/url_request/redirect_util.h"
 #include "services/network/public/cpp/features.h"
 
 namespace content {
@@ -325,17 +326,13 @@
         DCHECK(throttle_will_start_redirect_url_.is_empty())
             << "ThrottlingURLLoader doesn't support multiple throttles "
                "changing the URL.";
-        // Only do this sanity check if the schemes are both http[s], as this
-        // generated-redirect functionality is also used by
-        // registerProtocolHandler to map non-web to web schemes and that is
-        // safe.
         if (original_url.SchemeIsHTTPOrHTTPS() &&
-            url_request->url.SchemeIsHTTPOrHTTPS()) {
-          CHECK_EQ(original_url.GetOrigin(), url_request->url.GetOrigin())
-              << "ThrottlingURLLoader doesn't support a throttle making a "
-              << "cross-origin redirect.";
+            !url_request->url.SchemeIsHTTPOrHTTPS()) {
+          NOTREACHED() << "A URLLoaderThrottle can't redirect from http(s) to "
+                       << "a non http(s) scheme.";
+        } else {
+          throttle_will_start_redirect_url_ = url_request->url;
         }
-        throttle_will_start_redirect_url_ = url_request->url;
         // Restore the original URL so that all throttles see the same original
         // URL.
         url_request->url = original_url;
@@ -343,11 +340,6 @@
       if (!HandleThrottleResult(throttle, throttle_deferred, &deferred))
         return;
     }
-
-    // If a throttle had changed the URL, set it in the ResourceRequest struct
-    // so that it is the URL that's requested.
-    if (!throttle_will_start_redirect_url_.is_empty())
-      url_request->url = throttle_will_start_redirect_url_;
   }
 
   start_info_ =
@@ -362,13 +354,35 @@
 void ThrottlingURLLoader::StartNow() {
   DCHECK(start_info_);
   if (!throttle_will_start_redirect_url_.is_empty()) {
-    net::RedirectInfo redirect_info;
-    redirect_info.status_code = net::HTTP_TEMPORARY_REDIRECT;
-    redirect_info.new_method = start_info_->url_request.method;
-    redirect_info.new_url = throttle_will_start_redirect_url_;
-    redirect_info.new_site_for_cookies = throttle_will_start_redirect_url_;
-    redirect_info.new_top_frame_origin =
-        url::Origin::Create(throttle_will_start_redirect_url_);
+    auto first_party_url_policy =
+        start_info_->url_request.update_first_party_url_on_redirect
+            ? net::URLRequest::FirstPartyURLPolicy::
+                  UPDATE_FIRST_PARTY_URL_ON_REDIRECT
+            : net::URLRequest::FirstPartyURLPolicy::
+                  NEVER_CHANGE_FIRST_PARTY_URL;
+
+    net::RedirectInfo redirect_info = net::RedirectInfo::ComputeRedirectInfo(
+        start_info_->url_request.method, start_info_->url_request.url,
+        start_info_->url_request.site_for_cookies,
+        start_info_->url_request.top_frame_origin, first_party_url_policy,
+        start_info_->url_request.referrer_policy,
+        start_info_->url_request.referrer.spec(),
+        // Use status code 307 to preserve the method, so POST requests work.
+        net::HTTP_TEMPORARY_REDIRECT, throttle_will_start_redirect_url_,
+        base::nullopt, false, false, false);
+
+    bool should_clear_upload = false;
+    net::RedirectUtil::UpdateHttpRequest(
+        start_info_->url_request.url, start_info_->url_request.method,
+        redirect_info, base::nullopt, base::nullopt,
+        &start_info_->url_request.headers, &should_clear_upload);
+
+    if (should_clear_upload)
+      start_info_->url_request.request_body = nullptr;
+
+    // Set the new URL in the ResourceRequest struct so that it is the URL
+    // that's requested.
+    start_info_->url_request.url = throttle_will_start_redirect_url_;
 
     network::ResourceResponseHead response_head;
     std::string header_string = base::StringPrintf(
@@ -530,17 +544,6 @@
         DCHECK(throttle_will_redirect_redirect_url_.is_empty())
             << "ThrottlingURLLoader doesn't support multiple throttles "
                "changing the URL.";
-        // Only do this sanity check if the schemes are both http[s], as this
-        // generated-redirect functionality is also used by
-        // registerProtocolHandler to map non-web to web schemes and that is
-        // safe.
-        if (redirect_info_copy.new_url.SchemeIsHTTPOrHTTPS() &&
-            redirect_info.new_url.SchemeIsHTTPOrHTTPS()) {
-          CHECK_EQ(redirect_info_copy.new_url.GetOrigin(),
-                   redirect_info.new_url.GetOrigin())
-              << "ThrottlingURLLoader doesn't support a throttle making a "
-              << "cross-origin redirect.";
-        }
         throttle_will_redirect_redirect_url_ = redirect_info_copy.new_url;
       } else {
         CHECK_EQ(redirect_info_copy.new_url, redirect_info.new_url)
diff --git a/content/ppapi_plugin/ppapi_thread.cc b/content/ppapi_plugin/ppapi_thread.cc
index 404c240..adf8faa9 100644
--- a/content/ppapi_plugin/ppapi_thread.cc
+++ b/content/ppapi_plugin/ppapi_thread.cc
@@ -430,17 +430,6 @@
       return;
     }
   } else {
-#if defined(OS_MACOSX)
-    // TODO(kerrnel): Delete this once the V2 sandbox is default.
-    const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
-    if (!cmdline->HasSwitch(sandbox::switches::kSeatbeltClientName)) {
-      // We need to do this after getting |PPP_GetInterface()| (or presumably
-      // doing something nontrivial with the library), else the sandbox
-      // intercedes.
-      CHECK(InitializeSandbox());
-    }
-#endif
-
     int32_t init_error = plugin_entry_points_.initialize_module(
         local_pp_module_, &ppapi::proxy::PluginDispatcher::GetBrowserInterface);
     if (init_error != PP_OK) {
diff --git a/content/renderer/android/synchronous_layer_tree_frame_sink.cc b/content/renderer/android/synchronous_layer_tree_frame_sink.cc
index e40f9a3..2c514e0 100644
--- a/content/renderer/android/synchronous_layer_tree_frame_sink.cc
+++ b/content/renderer/android/synchronous_layer_tree_frame_sink.cc
@@ -105,6 +105,8 @@
   bool HasExternalStencilTest() const override { return false; }
   void ApplyExternalStencil() override {}
   unsigned UpdateGpuFence() override { return 0; }
+  void SetUpdateVSyncParametersCallback(
+      viz::UpdateVSyncParametersCallback callback) override {}
 };
 
 base::TimeDelta SynchronousLayerTreeFrameSink::StubDisplayClient::
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index 8553e46c..e3b818a 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -1038,7 +1038,6 @@
     "//services/network:test_support",
     "//services/service_manager/public/cpp",
     "//services/test/echo/public/mojom",
-    "//services/tracing:privacy_check",
     "//services/video_capture/public/cpp",
     "//services/video_capture/public/cpp:mocks",
     "//services/video_capture/public/mojom:constants",
diff --git a/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt
index 66f44be..3235591 100644
--- a/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt
@@ -171,11 +171,3 @@
 
 # Mark all webview tests as RetryOnFailure due to Nexus 5x driver bug.
 crbug.com/950932 [ android-webview-instrumentation qualcomm-adreno-(tm)-418 ] * [ RetryOnFailure ]
-
-# Need to rebaseline for accelerated canvases due to small anti-aliasing differences
-Pixel_CanvasLowLatency2D [ Failure ]
-Pixel_CSSFilterEffects [ Failure ]
-Pixel_CSSFilterEffects_NoOverlays [ Failure ]
-# Previously these tests were not using accelerated canvases at all due to size
-# heuristic. Rebaseline them here
-Pixel_CanvasDisplayLinearRGBAccelerated2D [ Failure ]
diff --git a/gpu/BUILD.gn b/gpu/BUILD.gn
index 3796bef..307e1cb 100644
--- a/gpu/BUILD.gn
+++ b/gpu/BUILD.gn
@@ -153,6 +153,12 @@
     "command_buffer/client/client_test_helper.h",
     "command_buffer/client/gles2_interface_stub.cc",
     "command_buffer/client/gles2_interface_stub.h",
+    "command_buffer/client/gles2_interface_stub_autogen.h",
+    "command_buffer/client/gles2_interface_stub_impl_autogen.h",
+    "command_buffer/client/webgpu_interface_stub.cc",
+    "command_buffer/client/webgpu_interface_stub.h",
+    "command_buffer/client/webgpu_interface_stub_autogen.h",
+    "command_buffer/client/webgpu_interface_stub_impl_autogen.h",
     "command_buffer/service/copy_texture_chromium_mock.cc",
     "command_buffer/service/copy_texture_chromium_mock.h",
     "command_buffer/service/error_state_mock.cc",
@@ -171,6 +177,7 @@
     ":gpu",
     ":webgpu",
     "//gpu/command_buffer/client:gles2_interface",
+    "//gpu/command_buffer/client:webgpu_interface",
     "//gpu/ipc:gpu_thread_holder",
   ]
   deps = [
diff --git a/gpu/command_buffer/build_cmd_buffer_lib.py b/gpu/command_buffer/build_cmd_buffer_lib.py
index 65915b7b..9d66d12 100644
--- a/gpu/command_buffer/build_cmd_buffer_lib.py
+++ b/gpu/command_buffer/build_cmd_buffer_lib.py
@@ -1485,8 +1485,8 @@
     args = func.GetOriginalArgs()
     arg_string = ", ".join(
         ["%s /* %s */" % (arg.type, arg.name) for arg in args])
-    f.write("%s GLES2InterfaceStub::%s(%s) {\n" %
-               (func.return_type, func.original_name, arg_string))
+    f.write("%s %sInterfaceStub::%s(%s) {\n" %
+               (func.return_type, _prefix, func.original_name, arg_string))
     if func.return_type != "void":
       f.write("  return 0;\n")
     f.write("}\n")
diff --git a/gpu/command_buffer/build_webgpu_cmd_buffer.py b/gpu/command_buffer/build_webgpu_cmd_buffer.py
index 66df1e9..e8f179a 100755
--- a/gpu/command_buffer/build_webgpu_cmd_buffer.py
+++ b/gpu/command_buffer/build_webgpu_cmd_buffer.py
@@ -96,6 +96,10 @@
     "gpu/command_buffer/client/webgpu_interface_autogen.h")
   gen.WriteGLES2ImplementationHeader(
     "gpu/command_buffer/client/webgpu_implementation_autogen.h")
+  gen.WriteGLES2InterfaceStub(
+    "gpu/command_buffer/client/webgpu_interface_stub_autogen.h")
+  gen.WriteGLES2InterfaceStubImpl(
+      "gpu/command_buffer/client/webgpu_interface_stub_impl_autogen.h")
   gen.WriteGLES2Implementation(
     "gpu/command_buffer/client/webgpu_implementation_impl_autogen.h")
   gen.WriteGLES2ImplementationUnitTests(
diff --git a/gpu/command_buffer/client/webgpu_interface.h b/gpu/command_buffer/client/webgpu_interface.h
index 917042a..5cdf6b52 100644
--- a/gpu/command_buffer/client/webgpu_interface.h
+++ b/gpu/command_buffer/client/webgpu_interface.h
@@ -9,9 +9,6 @@
 
 #include "gpu/command_buffer/client/interface_base.h"
 
-extern "C" typedef struct _ClientBuffer* ClientBuffer;
-extern "C" typedef struct _GLColorSpace* GLColorSpace;
-
 namespace gpu {
 namespace webgpu {
 
diff --git a/gpu/command_buffer/client/webgpu_interface_stub.cc b/gpu/command_buffer/client/webgpu_interface_stub.cc
new file mode 100644
index 0000000..c0163e3
--- /dev/null
+++ b/gpu/command_buffer/client/webgpu_interface_stub.cc
@@ -0,0 +1,39 @@
+// Copyright (c) 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 "gpu/command_buffer/client/webgpu_interface_stub.h"
+
+namespace gpu {
+namespace webgpu {
+
+WebGPUInterfaceStub::WebGPUInterfaceStub() = default;
+
+WebGPUInterfaceStub::~WebGPUInterfaceStub() = default;
+
+// InterfaceBase implementation.
+void WebGPUInterfaceStub::GenSyncTokenCHROMIUM(GLbyte* sync_token) {}
+void WebGPUInterfaceStub::GenUnverifiedSyncTokenCHROMIUM(GLbyte* sync_token) {}
+void WebGPUInterfaceStub::VerifySyncTokensCHROMIUM(GLbyte** sync_tokens,
+                                                   GLsizei count) {}
+void WebGPUInterfaceStub::WaitSyncTokenCHROMIUM(const GLbyte* sync_token) {}
+
+// WebGPUInterface implementation
+const DawnProcTable& WebGPUInterfaceStub::GetProcs() const {
+  return null_procs_;
+}
+void WebGPUInterfaceStub::FlushCommands() {}
+DawnDevice WebGPUInterfaceStub::GetDefaultDevice() {
+  return nullptr;
+}
+ReservedTexture WebGPUInterfaceStub::ReserveTexture(DawnDevice device) {
+  return {nullptr, 0, 0};
+}
+
+// Include the auto-generated part of this class. We split this because
+// it means we can easily edit the non-auto generated parts right here in
+// this file instead of having to edit some template or the code generator.
+#include "gpu/command_buffer/client/webgpu_interface_stub_impl_autogen.h"
+
+}  // namespace webgpu
+}  // namespace gpu
diff --git a/gpu/command_buffer/client/webgpu_interface_stub.h b/gpu/command_buffer/client/webgpu_interface_stub.h
new file mode 100644
index 0000000..e16a57e
--- /dev/null
+++ b/gpu/command_buffer/client/webgpu_interface_stub.h
@@ -0,0 +1,43 @@
+// Copyright (c) 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 GPU_COMMAND_BUFFER_CLIENT_WEBGPU_INTERFACE_STUB_H_
+#define GPU_COMMAND_BUFFER_CLIENT_WEBGPU_INTERFACE_STUB_H_
+
+#include "gpu/command_buffer/client/webgpu_interface.h"
+
+namespace gpu {
+namespace webgpu {
+
+// This class a stub to help with mocks for the WebGPUInterface class.
+class WebGPUInterfaceStub : public WebGPUInterface {
+ public:
+  WebGPUInterfaceStub();
+  ~WebGPUInterfaceStub() override;
+
+  // InterfaceBase implementation.
+  void GenSyncTokenCHROMIUM(GLbyte* sync_token) override;
+  void GenUnverifiedSyncTokenCHROMIUM(GLbyte* sync_token) override;
+  void VerifySyncTokensCHROMIUM(GLbyte** sync_tokens, GLsizei count) override;
+  void WaitSyncTokenCHROMIUM(const GLbyte* sync_token) override;
+
+  // WebGPUInterface implementation
+  const DawnProcTable& GetProcs() const override;
+  void FlushCommands() override;
+  DawnDevice GetDefaultDevice() override;
+  ReservedTexture ReserveTexture(DawnDevice device) override;
+
+// Include the auto-generated part of this class. We split this because
+// it means we can easily edit the non-auto generated parts right here in
+// this file instead of having to edit some template or the code generator.
+#include "gpu/command_buffer/client/webgpu_interface_stub_autogen.h"
+
+ private:
+  DawnProcTable null_procs_;
+};
+
+}  // namespace webgpu
+}  // namespace gpu
+
+#endif  // GPU_COMMAND_BUFFER_CLIENT_WEBGPU_INTERFACE_STUB_H_
diff --git a/gpu/command_buffer/client/webgpu_interface_stub_autogen.h b/gpu/command_buffer/client/webgpu_interface_stub_autogen.h
new file mode 100644
index 0000000..6d23a68
--- /dev/null
+++ b/gpu/command_buffer/client/webgpu_interface_stub_autogen.h
@@ -0,0 +1,22 @@
+// 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.
+
+// This file is auto-generated from
+// gpu/command_buffer/build_webgpu_cmd_buffer.py
+// It's formatted by clang-format using chromium coding style:
+//    clang-format -i -style=chromium filename
+// DO NOT EDIT!
+
+// This file is included by gles2_interface_stub.h.
+#ifndef GPU_COMMAND_BUFFER_CLIENT_WEBGPU_INTERFACE_STUB_AUTOGEN_H_
+#define GPU_COMMAND_BUFFER_CLIENT_WEBGPU_INTERFACE_STUB_AUTOGEN_H_
+
+void AssociateMailbox(GLuint device_id,
+                      GLuint device_generation,
+                      GLuint id,
+                      GLuint generation,
+                      GLuint usage,
+                      const GLbyte* mailbox) override;
+void DissociateMailbox(GLuint texture_id, GLuint texture_generation) override;
+#endif  // GPU_COMMAND_BUFFER_CLIENT_WEBGPU_INTERFACE_STUB_AUTOGEN_H_
diff --git a/gpu/command_buffer/client/webgpu_interface_stub_impl_autogen.h b/gpu/command_buffer/client/webgpu_interface_stub_impl_autogen.h
new file mode 100644
index 0000000..0d89b68
--- /dev/null
+++ b/gpu/command_buffer/client/webgpu_interface_stub_impl_autogen.h
@@ -0,0 +1,23 @@
+// 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.
+
+// This file is auto-generated from
+// gpu/command_buffer/build_webgpu_cmd_buffer.py
+// It's formatted by clang-format using chromium coding style:
+//    clang-format -i -style=chromium filename
+// DO NOT EDIT!
+
+// This file is included by gles2_interface_stub.cc.
+#ifndef GPU_COMMAND_BUFFER_CLIENT_WEBGPU_INTERFACE_STUB_IMPL_AUTOGEN_H_
+#define GPU_COMMAND_BUFFER_CLIENT_WEBGPU_INTERFACE_STUB_IMPL_AUTOGEN_H_
+
+void WebGPUInterfaceStub::AssociateMailbox(GLuint /* device_id */,
+                                           GLuint /* device_generation */,
+                                           GLuint /* id */,
+                                           GLuint /* generation */,
+                                           GLuint /* usage */,
+                                           const GLbyte* /* mailbox */) {}
+void WebGPUInterfaceStub::DissociateMailbox(GLuint /* texture_id */,
+                                            GLuint /* texture_generation */) {}
+#endif  // GPU_COMMAND_BUFFER_CLIENT_WEBGPU_INTERFACE_STUB_IMPL_AUTOGEN_H_
diff --git a/ios/build/bots/chromium.fyi/ios-simulator-cronet.json b/ios/build/bots/chromium.fyi/ios-simulator-cronet.json
index 23c987a..c59f12f2 100644
--- a/ios/build/bots/chromium.fyi/ios-simulator-cronet.json
+++ b/ios/build/bots/chromium.fyi/ios-simulator-cronet.json
@@ -27,50 +27,106 @@
       "app": "cronet_unittests_ios",
       "device type": "iPhone 5s",
       "os": "9.3",
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome"
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      }
     },
     {
       "app": "cronet_test",
       "device type": "iPhone 5s",
       "os": "9.3",
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome"
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      }
     },
     {
       "app": "cronet_test",
       "device type": "iPad Retina",
       "os": "9.3",
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome"
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      }
     },
     {
       "app": "cronet_test",
       "device type": "iPhone 5s",
       "os": "11.4",
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome"
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      }
     },
     {
       "app": "cronet_test",
       "device type": "iPad Air 2",
       "os": "11.4",
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome"
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      }
     },
     {
       "app": "cronet_test",
       "device type": "iPhone X",
       "os": "11.4",
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome"
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      }
     },
     {
       "app": "cronet_test",
       "device type": "iPhone X",
       "os": "12.1",
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome"
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      }
     }
   ]
 }
diff --git a/ios/build/bots/chromium.fyi/ios-simulator.json b/ios/build/bots/chromium.fyi/ios-simulator.json
index e4dea649..0691392 100644
--- a/ios/build/bots/chromium.fyi/ios-simulator.json
+++ b/ios/build/bots/chromium.fyi/ios-simulator.json
@@ -21,8 +21,16 @@
       ],
       "device type": "iPhone 6s",
       "os": "12.1",
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome"
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      }
     },
     {
       "app": "ios_chrome_integration_egtests",
@@ -32,8 +40,16 @@
       "device type": "iPhone 6s",
       "os": "12.1",
       "xctest": true,
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome"
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      }
     },
     {
       "app": "ios_chrome_integration_egtests",
@@ -43,8 +59,16 @@
       "device type": "iPad Air 2",
       "os": "12.1",
       "xctest": true,
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome"
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      }
     },
     {
       "app": "ios_chrome_integration_egtests",
@@ -54,8 +78,16 @@
       "device type": "iPhone 6s",
       "os": "12.1",
       "xctest": true,
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome"
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      }
     },
     {
       "app": "ios_chrome_smoke_egtests",
@@ -65,8 +97,16 @@
       "device type": "iPhone 6s",
       "os": "12.1",
       "xctest": true,
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome"
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      }
     },
     {
       "app": "ios_chrome_web_egtests",
@@ -76,8 +116,16 @@
       "device type": "iPhone 6s",
       "os": "12.1",
       "xctest": true,
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome"
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+              "host os": "Mac-10.13.6"
+          }],
+          "60": [{
+              "host os": "Mac-10.14.3"
+          }]
+      }
     },
     {
       "app": "ios_web_shell_egtests",
@@ -87,8 +135,16 @@
       "device type": "iPhone 6s",
       "os": "12.1",
       "xctest": true,
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome"
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      }
     },
     {
       "app": "ios_chrome_ui_egtests",
@@ -98,8 +154,16 @@
       "device type": "iPhone 6s",
       "os": "12.1",
       "xctest": true,
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome"
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      }
     }
   ]
 }
diff --git a/ios/build/bots/chromium.fyi/ios12-beta-simulator.json b/ios/build/bots/chromium.fyi/ios12-beta-simulator.json
index 096ac82..efcaf67fc 100644
--- a/ios/build/bots/chromium.fyi/ios12-beta-simulator.json
+++ b/ios/build/bots/chromium.fyi/ios12-beta-simulator.json
@@ -19,104 +19,208 @@
       "device type": "iPhone X",
       "os": "12.1",
       "xcode build version": "10o45e",
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome"
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      }
     },
     {
       "include": "eg_cq_tests.json",
       "device type": "iPhone X",
       "os": "12.1",
       "xcode build version": "10o45e",
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome"
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      }
     },
     {
       "include": "eg_cq_tests.json",
       "device type": "iPhone 6 Plus",
       "os": "12.1",
       "xcode build version": "10o45e",
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome"
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      }
     },
     {
       "include": "eg_cq_tests.json",
       "device type": "iPhone 5s",
       "os": "12.1",
       "xcode build version": "10o45e",
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome"
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      }
     },
     {
       "include": "eg_cq_tests.json",
       "device type": "iPad Pro",
       "os": "12.1",
       "xcode build version": "10o45e",
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome"
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      }
     },
     {
       "include": "eg_tests.json",
       "device type": "iPhone X",
       "os": "12.1",
       "xcode build version": "10o45e",
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome"
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      }
     },
     {
       "include": "eg_tests.json",
       "device type": "iPhone 6 Plus",
       "os": "12.1",
       "xcode build version": "10o45e",
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome"
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      }
     },
     {
       "include": "eg_tests.json",
       "device type": "iPad Air",
       "os": "12.1",
       "xcode build version": "10o45e",
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome"
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      }
     },
     {
       "include": "eg_tests.json",
       "device type": "iPhone 5s",
       "os": "12.1",
       "xcode build version": "10o45e",
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome"
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      }
     },
     {
       "include": "screen_size_dependent_tests.json",
       "device type": "iPhone 6s Plus",
       "os": "12.1",
       "xcode build version": "10o45e",
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome"
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      }
     },
     {
       "include": "screen_size_dependent_tests.json",
       "device type": "iPhone X",
       "os": "12.1",
       "xcode build version": "10o45e",
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome"
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      }
     },
     {
       "include": "screen_size_dependent_tests.json",
       "device type": "iPhone 5s",
       "os": "12.1",
       "xcode build version": "10o45e",
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome"
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      }
     },
     {
       "include": "screen_size_dependent_tests.json",
       "device type": "iPad Air 2",
       "os": "12.1",
       "xcode build version": "10o45e",
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome"
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      }
     }
   ]
 }
diff --git a/ios/build/bots/chromium.fyi/ios12-sdk-simulator.json b/ios/build/bots/chromium.fyi/ios12-sdk-simulator.json
index 78c9dba6..f93493f 100644
--- a/ios/build/bots/chromium.fyi/ios12-sdk-simulator.json
+++ b/ios/build/bots/chromium.fyi/ios12-sdk-simulator.json
@@ -28,8 +28,16 @@
       "include": "eg_cq_tests_ios12_simulator_fyi_experiment.json",
       "device type": "iPhone X",
       "os": "12.1",
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome",
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      },
       "shards": 4,
       "priority": 35
     },
@@ -38,8 +46,16 @@
       "include": "eg_cq_tests.json",
       "device type": "iPhone X",
       "os": "12.1",
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome",
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      },
       "priority": 35
     },
     {
@@ -47,8 +63,16 @@
       "include": "eg_cq_tests_ios12_simulator_fyi_experiment.json",
       "device type": "iPad Air 2",
       "os": "12.1",
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome",
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      },
       "shards": 4,
       "priority": 35
     },
@@ -57,8 +81,16 @@
       "include": "eg_cq_tests.json",
       "device type": "iPad Air 2",
       "os": "12.1",
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome",
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      },
       "priority": 35
     },
     {
@@ -66,8 +98,16 @@
       "include": "eg_cq_tests_ios12_simulator_fyi_experiment.json",
       "device type": "iPhone 7",
       "os": "12.1",
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome",
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      },
       "shards": 4,
       "priority": 35
     },
@@ -76,8 +116,16 @@
       "include": "eg_cq_tests.json",
       "device type": "iPhone 7",
       "os": "12.1",
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome",
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      },
       "priority": 35
     },
     {
@@ -85,8 +133,16 @@
       "include": "eg_cq_tests_ios12_simulator_fyi_experiment.json",
       "device type": "iPhone 7",
       "os": "11.4",
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome",
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      },
       "shards": 4,
       "priority": 35
     },
@@ -95,8 +151,16 @@
       "include": "eg_cq_tests.json",
       "device type": "iPhone 7",
       "os": "11.4",
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome",
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      },
       "priority": 35
     },
     {
@@ -104,8 +168,16 @@
       "include": "eg_cq_tests_ios12_simulator_fyi_experiment.json",
       "device type": "iPad Air 2",
       "os": "11.4",
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome",
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      },
       "shards": 4,
       "priority": 35
     },
@@ -114,8 +186,16 @@
       "include": "eg_cq_tests.json",
       "device type": "iPad Air 2",
       "os": "11.4",
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome",
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      },
       "priority": 35
     },
     {
@@ -123,8 +203,16 @@
       "include": "eg_cq_tests_ios12_simulator_fyi_experiment.json",
       "device type": "iPhone X",
       "os": "11.4",
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome",
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      },
       "shards": 4,
       "priority": 35
     },
@@ -133,8 +221,16 @@
       "include": "eg_cq_tests.json",
       "device type": "iPhone X",
       "os": "11.4",
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome",
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      },
       "priority": 35
     }
   ]
diff --git a/ios/build/bots/chromium.mac/ios-simulator-full-configs.json b/ios/build/bots/chromium.mac/ios-simulator-full-configs.json
index 4e1768d..f441b09 100644
--- a/ios/build/bots/chromium.mac/ios-simulator-full-configs.json
+++ b/ios/build/bots/chromium.mac/ios-simulator-full-configs.json
@@ -22,80 +22,160 @@
       "include": "eg_tests.json",
       "device type": "iPhone 7",
       "os": "11.4",
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome",
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      },
       "priority": 30
     },
     {
       "include": "eg_tests.json",
       "device type": "iPhone 7",
       "os": "12.1",
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome",
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      },
       "priority": 30
     },
     {
       "include": "eg_tests.json",
       "device type": "iPad Air 2",
       "os": "11.4",
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome",
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      },
       "priority": 30
     },
     {
       "include": "eg_tests.json",
       "device type": "iPad Air 2",
       "os": "12.1",
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome",
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      },
       "priority": 30
     },
     {
       "include": "eg_tests.json",
       "device type": "iPhone X",
       "os": "11.4",
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome",
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      },
       "priority": 30
     },
     {
       "include": "eg_tests.json",
       "device type": "iPhone X",
       "os": "12.1",
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome",
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      },
       "priority": 30
     },
     {
       "include": "eg_cq_tests.json",
       "device type": "iPhone X",
       "os": "11.4",
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome",
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      },
       "priority": 30
     },
     {
       "include": "eg_cq_tests.json",
       "device type": "iPhone X",
       "os": "12.1",
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome",
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      },
       "priority": 30
     },
     {
       "include": "eg_cq_tests.json",
       "device type": "iPad Air 2",
       "os": "11.4",
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome",
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      },
       "priority": 30
     },
     {
       "include": "eg_cq_tests.json",
       "device type": "iPad Air 2",
       "os": "12.1",
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome",
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      },
       "priority": 30
     }
   ]
diff --git a/ios/build/bots/chromium.mac/ios-simulator.json b/ios/build/bots/chromium.mac/ios-simulator.json
index 19687a4..ef943ce 100644
--- a/ios/build/bots/chromium.mac/ios-simulator.json
+++ b/ios/build/bots/chromium.mac/ios-simulator.json
@@ -1,7 +1,7 @@
 {
   "comments": [
-    "Runs tests on 64-bit iOS 10.3 and 11.4 and 12.1 tests",
-    "on iPad, iPhone, @3x, and @2x on main and CQ ios-simulator.",
+    "Runs tests on 64-bit iOS 11.4 and 12.1 on iPad, iPhone, @3x, and @2x on",
+    "main and CQ ios-simulator.",
     "Note: Xcode 10 requires Mac OS 10.13.4 or higher, hence 'host os'."
   ],
   "xcode build version": "10b61",
@@ -23,72 +23,144 @@
       "include": "screen_size_dependent_tests.json",
       "device type": "iPhone 6s Plus",
       "os": "12.1",
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome",
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      },
       "priority": 30
     },
     {
       "include": "screen_size_dependent_tests.json",
       "device type": "iPhone 6s",
       "os": "12.1",
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome",
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      },
       "priority": 30
     },
     {
       "include": "common_tests.json",
       "device type": "iPhone 6s",
       "os": "12.1",
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome",
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      },
       "priority": 30
     },
     {
       "include": "screen_size_dependent_tests.json",
       "device type": "iPad Air 2",
       "os": "12.1",
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome",
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      },
       "priority": 30
     },
     {
       "include": "screen_size_dependent_tests.json",
       "device type": "iPhone 6s Plus",
       "os": "11.4",
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome",
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      },
       "priority": 30
     },
     {
       "include": "screen_size_dependent_tests.json",
       "device type": "iPhone 6s",
       "os": "11.4",
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome",
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      },
       "priority": 30
     },
     {
       "include": "common_tests.json",
       "device type": "iPhone 6s",
       "os": "11.4",
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome",
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      },
       "priority": 30
     },
     {
       "include": "screen_size_dependent_tests.json",
       "device type": "iPad Air 2",
       "os": "11.4",
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome",
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      },
       "priority": 30
     },
     {
       "include": "eg_cq_tests.json",
       "device type": "iPhone 6s",
       "os": "11.4",
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome",
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      },
       "priority": 30
     }
   ]
diff --git a/ios/build/bots/chromium.mac/ios-slimnav.json b/ios/build/bots/chromium.mac/ios-slimnav.json
index 99ab81b..dd0f4ee2 100644
--- a/ios/build/bots/chromium.mac/ios-slimnav.json
+++ b/ios/build/bots/chromium.mac/ios-slimnav.json
@@ -21,8 +21,16 @@
       ],
       "device type": "iPhone 6s Plus",
       "os": "12.1",
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome",
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      },
       "priority": 30
     },
     {
@@ -32,8 +40,16 @@
       ],
       "device type": "iPhone 6s",
       "os": "12.1",
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome",
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      },
       "priority": 30
     },
     {
@@ -43,8 +59,16 @@
       ],
       "device type": "iPhone 6s",
       "os": "12.1",
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome",
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      },
       "priority": 30
     },
     {
@@ -54,8 +78,16 @@
       ],
       "device type": "iPad Air 2",
       "os": "12.1",
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome",
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      },
       "priority": 30
     },
     {
@@ -65,8 +97,16 @@
       ],
       "device type": "iPhone 6s Plus",
       "os": "11.4",
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome",
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      },
       "priority": 30
     },
     {
@@ -76,8 +116,16 @@
       ],
       "device type": "iPhone 6s",
       "os": "11.4",
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome",
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      },
       "priority": 30
     },
     {
@@ -87,8 +135,16 @@
       ],
       "device type": "iPhone 6s",
       "os": "11.4",
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome",
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      },
       "priority": 30
     },
     {
@@ -98,8 +154,16 @@
       ],
       "device type": "iPad Air 2",
       "os": "11.4",
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome",
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      },
       "priority": 30
     },
     {
@@ -109,8 +173,16 @@
       ],
       "device type": "iPhone 6s",
       "os": "11.4",
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome",
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      },
       "priority": 30
     },
     {
@@ -123,8 +195,16 @@
       "shards": 3,
       "xcode parallelization": true,
       "xctest": true,
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome",
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      },
       "priority": 30
     },
     {
@@ -135,8 +215,16 @@
       "device type": "iPad Air 2",
       "os": "11.4",
       "xctest": true,
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome",
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      },
       "priority": 30
     },
     {
@@ -147,8 +235,16 @@
       "device type": "iPad Air 2",
       "os": "11.4",
       "xctest": true,
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome",
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      },
       "priority": 30
     },
     {
@@ -159,8 +255,16 @@
       "device type": "iPad Air 2",
       "os": "11.4",
       "xctest": true,
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome",
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      },
       "priority": 30
     },
     {
@@ -171,8 +275,16 @@
       "device type": "iPad Air 2",
       "os": "11.4",
       "xctest": true,
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome",
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      },
       "priority": 30
     },
     {
@@ -183,8 +295,16 @@
       "device type": "iPad Air 2",
       "os": "11.4",
       "xctest": true,
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome",
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      },
       "priority": 30
     },
     {
@@ -195,8 +315,16 @@
       "device type": "iPad Air 2",
       "os": "11.4",
       "xctest": true,
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome",
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      },
       "priority": 30
     },
     {
@@ -209,8 +337,16 @@
       "device type": "iPad Air 2",
       "os": "12.1",
       "xctest": true,
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome",
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      },
       "priority": 30
     },
     {
@@ -221,8 +357,16 @@
       "xctest": true,
       "device type": "iPad Air 2",
       "os": "12.1",
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome",
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      },
       "priority": 30
     },
     {
@@ -233,8 +377,16 @@
       "device type": "iPad Air 2",
       "os": "12.1",
       "xctest": true,
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome",
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      },
       "priority": 30
     },
     {
@@ -245,8 +397,16 @@
       "device type": "iPad Air 2",
       "os": "12.1",
       "xctest": true,
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome",
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      },
       "priority": 30
     },
       {
@@ -257,8 +417,16 @@
       "device type": "iPad Air 2",
       "os": "12.1",
       "xctest": true,
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome",
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      },
       "priority": 30
     },
     {
@@ -269,8 +437,16 @@
       "device type": "iPad Air 2",
       "os": "12.1",
       "xctest": true,
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome",
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      },
       "priority": 30
     },
     {
@@ -281,8 +457,16 @@
       "device type": "iPad Air 2",
       "os": "12.1",
       "xctest": true,
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome",
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      },
       "priority": 30
     },
     {
@@ -295,8 +479,16 @@
       "shards": 3,
       "xcode parallelization": true,
       "xctest": true,
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome",
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      },
       "priority": 30
     },
     {
@@ -307,8 +499,16 @@
       "xctest": true,
       "device type": "iPhone X",
       "os": "11.4",
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome",
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      },
       "priority": 30
     },
     {
@@ -319,8 +519,16 @@
       "device type": "iPhone X",
       "os": "11.4",
       "xctest": true,
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome",
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      },
       "priority": 30
     },
     {
@@ -331,8 +539,16 @@
       "device type": "iPhone X",
       "os": "11.4",
       "xctest": true,
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome",
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      },
       "priority": 30
     },
     {
@@ -343,8 +559,16 @@
       "device type": "iPhone X",
       "os": "11.4",
       "xctest": true,
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome",
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      },
       "priority": 30
     },
     {
@@ -355,8 +579,16 @@
       "device type": "iPhone X",
       "os": "11.4",
       "xctest": true,
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome",
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      },
       "priority": 30
     },
     {
@@ -367,8 +599,16 @@
       "device type": "iPhone X",
       "os": "11.4",
       "xctest": true,
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome",
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      },
       "priority": 30
     },
     {
@@ -381,8 +621,16 @@
       "device type": "iPhone X",
       "os": "12.1",
       "xctest": true,
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome",
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      },
       "priority": 30
     },
     {
@@ -393,8 +641,16 @@
       "device type": "iPhone X",
       "os": "12.1",
       "xctest": true,
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome",
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      },
       "priority": 30
     },
     {
@@ -405,8 +661,16 @@
       "device type": "iPhone X",
       "os": "12.1",
       "xctest": true,
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome",
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      },
       "priority": 30
     },
     {
@@ -417,8 +681,16 @@
       "device type": "iPhone X",
       "os": "12.1",
       "xctest": true,
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome",
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      },
       "priority": 30
     },
     {
@@ -429,8 +701,16 @@
       "device type": "iPhone X",
       "os": "12.1",
       "xctest": true,
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome",
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      },
       "priority": 30
     },
     {
@@ -441,8 +721,16 @@
       "device type": "iPhone X",
       "os": "12.1",
       "xctest": true,
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome",
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      },
       "priority": 30
     },
     {
@@ -453,8 +741,16 @@
       "device type": "iPhone X",
       "os": "12.1",
       "xctest": true,
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome",
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      },
       "priority": 30
     },
     {
@@ -464,8 +760,16 @@
       ],
       "device type": "iPhone X",
       "os": "11.4",
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome",
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      },
       "priority": 30
     },
     {
@@ -475,8 +779,16 @@
       ],
       "device type": "iPhone X",
       "os": "12.1",
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome",
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      },
       "priority": 30
     }
   ]
diff --git a/ios/build/bots/chromium.mac/ios12-sdk-simulator.json b/ios/build/bots/chromium.mac/ios12-sdk-simulator.json
index c149c52..eb23bc0 100644
--- a/ios/build/bots/chromium.mac/ios12-sdk-simulator.json
+++ b/ios/build/bots/chromium.mac/ios12-sdk-simulator.json
@@ -23,106 +23,226 @@
       "include": "eg_cq_tests.json",
       "device type": "iPhone X",
       "os": "12.1",
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome"
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      }
     },
     {
       "include": "eg_tests.json",
       "device type": "iPhone X",
       "os": "12.1",
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome"
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      }
     },
     {
       "include": "eg_tests.json",
       "device type": "iPad Air 2",
       "os": "12.1",
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome"
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      }
     },
     {
       "include": "eg_tests.json",
       "device type": "iPhone 7",
       "os": "12.1",
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome"
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      }
     },
     {
       "include": "eg_tests.json",
       "device type": "iPhone 7",
       "os": "11.4",
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome"
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      }
     },
     {
       "include": "eg_tests.json",
       "device type": "iPad Air 2",
       "os": "11.4",
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome"
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      }
     },
     {
       "include": "eg_tests.json",
       "device type": "iPhone X",
       "os": "11.4",
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome"
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      }
     },
     {
       "include": "eg_cq_tests.json",
       "device type": "iPhone X",
       "os": "11.4",
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome"
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      }
     },
     {
       "include": "screen_size_dependent_tests.json",
       "device type": "iPhone 6s Plus",
       "os": "12.1",
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome"
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      }
     },
     {
       "include": "screen_size_dependent_tests.json",
       "device type": "iPhone 6s",
       "os": "12.1",
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome"
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      }
     },
     {
       "include": "screen_size_dependent_tests.json",
       "device type": "iPad Air 2",
       "os": "12.1",
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome"
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      }
     },
     {
       "include": "screen_size_dependent_tests.json",
       "device type": "iPad Air 2",
       "os": "11.4",
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome"
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      }
     },
     {
       "include": "common_tests.json",
       "device type": "iPhone 6s",
       "os": "11.4",
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome"
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      }
     },
     {
       "include": "eg_cq_tests.json",
       "device type": "iPhone 6s",
       "os": "12.1",
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome"
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      }
     },
     {
       "include": "eg_cq_tests.json",
       "device type": "iPhone 6s",
       "os": "11.4",
-      "host os": "Mac-10.13.6",
-      "pool": "Chrome"
+      "host os": "Mac-10.14.4",
+      "pool":"Chrome",
+      "optional_dimensions": {
+          "60": [{
+            "host os": "Mac-10.13.6"
+          }],
+          "120": [{
+            "host os": "Mac-10.14.3"
+          }]
+      }
     }
   ]
 }
diff --git a/ios/chrome/app/strings/ios_strings.grd b/ios/chrome/app/strings/ios_strings.grd
index 355e07b1..ea96037 100644
--- a/ios/chrome/app/strings/ios_strings.grd
+++ b/ios/chrome/app/strings/ios_strings.grd
@@ -1677,11 +1677,14 @@
         Open Settings
       </message>
       <message name="IDS_IOS_SYNC_SETTINGS_NOT_CONFIRMED_DESCRIPTION" desc="The error message to display when sign-in was interrupted and the user didn't review the sync settings.">
-        Initial sync setup was not finished. To start Sync, enabled the Sync toggle.
+        To start sync, turn on "Sync your Chrome data".
       </message>
       <message name="IDS_IOS_SYNC_SETUP_IN_PROGRESS" desc="The message to display when the sync setup is in progress and sync isn't available yet. [Length: 60em] [iOS only]">
         Setup in progress…
       </message>
+      <message name="IDS_IOS_SYNC_SETUP_NOT_CONFIRMED_TITLE" desc="Title of the error message shown for sync errors. [Length: 60em] [iOS only]">
+        Initial Sync Setup Not Finished
+      </message>
       <message name="IDS_IOS_SYNC_SIGN_IN_AGAIN" desc="Title displayed when the signed in user needs sign in again. [Length: 20em] [iOS only]">
         Sign in again
       </message>
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_SYNC_SETTINGS_NOT_CONFIRMED_DESCRIPTION.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_SYNC_SETTINGS_NOT_CONFIRMED_DESCRIPTION.png.sha1
new file mode 100644
index 0000000..358c157
--- /dev/null
+++ b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_SYNC_SETTINGS_NOT_CONFIRMED_DESCRIPTION.png.sha1
@@ -0,0 +1 @@
+76654a8b7dc6b17ab2e9e405e1f278caea8d2202
\ No newline at end of file
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_SYNC_SETUP_NOT_CONFIRMED_TITLE.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_SYNC_SETUP_NOT_CONFIRMED_TITLE.png.sha1
new file mode 100644
index 0000000..e52cb8e
--- /dev/null
+++ b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_SYNC_SETUP_NOT_CONFIRMED_TITLE.png.sha1
@@ -0,0 +1 @@
+0267b5bf502598d47acef8619e9eb601eda3e5c1
\ No newline at end of file
diff --git a/ios/chrome/browser/ui/alert_view_controller/alert_view_controller.h b/ios/chrome/browser/ui/alert_view_controller/alert_view_controller.h
index ced29fa3..3cf3813e 100644
--- a/ios/chrome/browser/ui/alert_view_controller/alert_view_controller.h
+++ b/ios/chrome/browser/ui/alert_view_controller/alert_view_controller.h
@@ -23,6 +23,8 @@
                           style:(UIAlertActionStyle)style
                         handler:(void (^)(AlertAction* action))handler;
 
+- (instancetype)init NS_UNAVAILABLE;
+
 @end
 
 // This class is a replacement for UIAlertController that supports custom
diff --git a/ios/chrome/browser/ui/alert_view_controller/alert_view_controller.mm b/ios/chrome/browser/ui/alert_view_controller/alert_view_controller.mm
index 3231933..10f6360 100644
--- a/ios/chrome/browser/ui/alert_view_controller/alert_view_controller.mm
+++ b/ios/chrome/browser/ui/alert_view_controller/alert_view_controller.mm
@@ -25,7 +25,7 @@
 
 // Properties of the alert view.
 constexpr CGFloat kCornerRadius = 14;
-constexpr CGFloat kMinimumWidth = 30;
+constexpr CGFloat kAlertWidth = 270;
 constexpr CGFloat kMinimumHeight = 30;
 constexpr CGFloat kMinimumMargin = 4;
 
@@ -64,16 +64,24 @@
 
 @implementation AlertAction
 
+- (instancetype)initWithTitle:(NSString*)title
+                        style:(UIAlertActionStyle)style
+                      handler:(void (^)(AlertAction* action))handler {
+  self = [super init];
+  if (self) {
+    static NSInteger actionIdentifier = 0;
+    _uniqueIdentifier = ++actionIdentifier;
+    _title = title;
+    _handler = handler;
+    _style = style;
+  }
+  return self;
+}
+
 + (instancetype)actionWithTitle:(NSString*)title
                           style:(UIAlertActionStyle)style
                         handler:(void (^)(AlertAction* action))handler {
-  AlertAction* action = [[AlertAction alloc] init];
-  static NSInteger actionIdentifier = 0;
-  action.uniqueIdentifier = ++actionIdentifier;
-  action.title = title;
-  action.handler = handler;
-  action.style = style;
-  return action;
+  return [[AlertAction alloc] initWithTitle:title style:style handler:handler];
 }
 
 @end
@@ -131,9 +139,10 @@
     [self.contentView.centerYAnchor
         constraintEqualToAnchor:self.view.centerYAnchor],
 
+    // Width
+    [self.contentView.widthAnchor constraintEqualToConstant:kAlertWidth],
+
     // Minimum Size
-    [self.contentView.widthAnchor
-        constraintGreaterThanOrEqualToConstant:kMinimumWidth],
     [self.contentView.heightAnchor
         constraintGreaterThanOrEqualToConstant:kMinimumHeight],
 
diff --git a/ios/chrome/browser/ui/settings/clear_browsing_data/clear_browsing_data_table_view_controller.mm b/ios/chrome/browser/ui/settings/clear_browsing_data/clear_browsing_data_table_view_controller.mm
index 5a8dbba..3a9df5e 100644
--- a/ios/chrome/browser/ui/settings/clear_browsing_data/clear_browsing_data_table_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/clear_browsing_data/clear_browsing_data_table_view_controller.mm
@@ -192,6 +192,19 @@
   }
 }
 
+- (void)viewWillDisappear:(BOOL)animated {
+  [super viewWillDisappear:animated];
+
+  // Write the browsing data selection states back to the browser state.
+  NSArray* dataTypeItems = [self.tableViewModel
+      itemsInSectionWithIdentifier:SectionIdentifierDataTypes];
+  for (TableViewClearBrowsingDataItem* dataTypeItem in dataTypeItems) {
+    DCHECK([dataTypeItem isKindOfClass:[TableViewClearBrowsingDataItem class]]);
+    self.browserState->GetPrefs()->SetBoolean(dataTypeItem.prefName,
+                                              dataTypeItem.checked);
+  }
+}
+
 - (void)loadModel {
   [super loadModel];
   [self.dataManager loadModel:self.tableViewModel];
@@ -511,8 +524,6 @@
   TableViewClearBrowsingDataItem* clearBrowsingDataItem =
       base::mac::ObjCCastStrict<TableViewClearBrowsingDataItem>(item);
   clearBrowsingDataItem.checked = flag;
-  self.browserState->GetPrefs()->SetBoolean(clearBrowsingDataItem.prefName,
-                                            clearBrowsingDataItem.checked);
   [self reconfigureCellsForItems:@[ clearBrowsingDataItem ]];
 }
 
diff --git a/ios/chrome/browser/ui/settings/google_services/google_services_settings_mediator.mm b/ios/chrome/browser/ui/settings/google_services/google_services_settings_mediator.mm
index db8d5c98..8ddc74d 100644
--- a/ios/chrome/browser/ui/settings/google_services/google_services_settings_mediator.mm
+++ b/ios/chrome/browser/ui/settings/google_services/google_services_settings_mediator.mm
@@ -583,14 +583,16 @@
   SettingsImageDetailTextItem* syncErrorItem =
       [[SettingsImageDetailTextItem alloc] initWithType:itemType];
   syncErrorItem.text = GetNSString(IDS_IOS_SYNC_ERROR_TITLE);
-  if (itemType == ShowPassphraseDialogErrorItemType) {
+  syncErrorItem.detailText =
+      GetSyncErrorDescriptionForSyncSetupService(self.syncSetupService);
+  if (itemType == SyncSettingsNotCofirmedErrorItemType) {
+    // Special case for the sync error title.
+    syncErrorItem.text = GetNSString(IDS_IOS_SYNC_SETUP_NOT_CONFIRMED_TITLE);
+  } else if (itemType == ShowPassphraseDialogErrorItemType) {
     // Special case only for the sync passphrase error message. The regular
     // error message should be still be displayed in the first settings screen.
     syncErrorItem.detailText = GetNSString(
         IDS_IOS_GOOGLE_SERVICES_SETTINGS_ENTER_PASSPHRASE_TO_START_SYNC);
-  } else {
-    syncErrorItem.detailText =
-        GetSyncErrorDescriptionForSyncSetupService(self.syncSetupService);
   }
   syncErrorItem.image = [UIImage imageNamed:kGoogleServicesSyncErrorImage];
   return syncErrorItem;
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 df6b806e..18e48eb 100644
--- a/ios/chrome/browser/ui/settings/privacy_table_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/privacy_table_view_controller.mm
@@ -327,7 +327,9 @@
   UIView* footerView =
       [super tableView:tableView viewForFooterInSection:section];
   if (SectionIdentifierWebServices ==
-      [self.tableViewModel sectionIdentifierForSection:section]) {
+          [self.tableViewModel sectionIdentifierForSection:section] &&
+      !unified_consent::IsUnifiedConsentFeatureEnabled()) {
+    // The footer view is only shown when Unified consent flag is off.
     TableViewLinkHeaderFooterView* footer =
         base::mac::ObjCCastStrict<TableViewLinkHeaderFooterView>(footerView);
     footer.delegate = self;
diff --git a/ios/web/navigation/BUILD.gn b/ios/web/navigation/BUILD.gn
index 76d1e39a..5f573dc 100644
--- a/ios/web/navigation/BUILD.gn
+++ b/ios/web/navigation/BUILD.gn
@@ -22,6 +22,8 @@
   sources = [
     "crw_navigation_item_holder.h",
     "crw_navigation_item_holder.mm",
+    "crw_pending_navigation_info.h",
+    "crw_pending_navigation_info.mm",
     "crw_session_controller+private_constructors.h",
     "crw_session_controller.h",
     "crw_session_controller.mm",
diff --git a/ios/web/navigation/crw_pending_navigation_info.h b/ios/web/navigation/crw_pending_navigation_info.h
new file mode 100644
index 0000000..86da1d4
--- /dev/null
+++ b/ios/web/navigation/crw_pending_navigation_info.h
@@ -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.
+
+#ifndef IOS_WEB_NAVIGATION_CRW_PENDING_NAVIGATION_INFO_H_
+#define IOS_WEB_NAVIGATION_CRW_PENDING_NAVIGATION_INFO_H_
+
+#import <UIKit/UIKit.h>
+#import <WebKit/WebKit.h>
+
+// A container object for any navigation information that is only available
+// during pre-commit delegate callbacks, and thus must be held until the
+// navigation commits and the information can be used.
+@interface CRWPendingNavigationInfo : NSObject {
+}
+// The referrer for the page.
+@property(nonatomic, copy) NSString* referrer;
+// The MIME type for the page.
+@property(nonatomic, copy) NSString* MIMEType;
+// The navigation type for the load.
+@property(nonatomic, assign) WKNavigationType navigationType;
+// HTTP request method for the load.
+@property(nonatomic, copy) NSString* HTTPMethod;
+// Whether the pending navigation has been directly cancelled before the
+// navigation is committed.
+// Cancelled navigations should be simply discarded without handling any
+// specific error.
+@property(nonatomic, assign) BOOL cancelled;
+// Whether the navigation was initiated by a user gesture.
+@property(nonatomic, assign) BOOL hasUserGesture;
+
+@end
+
+#endif  // IOS_WEB_NAVIGATION_CRW_PENDING_NAVIGATION_INFO_H_
diff --git a/ios/web/navigation/crw_pending_navigation_info.mm b/ios/web/navigation/crw_pending_navigation_info.mm
new file mode 100644
index 0000000..0981d027
--- /dev/null
+++ b/ios/web/navigation/crw_pending_navigation_info.mm
@@ -0,0 +1,20 @@
+// 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 "ios/web/navigation/crw_pending_navigation_info.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+@implementation CRWPendingNavigationInfo
+
+- (instancetype)init {
+  if ((self = [super init])) {
+    _navigationType = WKNavigationTypeOther;
+  }
+  return self;
+}
+
+@end
diff --git a/ios/web/web_state/ui/crw_web_controller.mm b/ios/web/web_state/ui/crw_web_controller.mm
index 9502ba97..3bdad4390 100644
--- a/ios/web/web_state/ui/crw_web_controller.mm
+++ b/ios/web/web_state/ui/crw_web_controller.mm
@@ -52,6 +52,7 @@
 #include "ios/web/history_state_util.h"
 #import "ios/web/interstitials/web_interstitial_impl.h"
 #import "ios/web/navigation/crw_navigation_item_holder.h"
+#import "ios/web/navigation/crw_pending_navigation_info.h"
 #include "ios/web/navigation/error_retry_state_machine.h"
 #import "ios/web/navigation/navigation_context_impl.h"
 #import "ios/web/navigation/navigation_item_impl.h"
@@ -226,42 +227,6 @@
 
 }  // namespace
 
-#pragma mark -
-
-// A container object for any navigation information that is only available
-// during pre-commit delegate callbacks, and thus must be held until the
-// navigation commits and the informatino can be used.
-@interface CRWWebControllerPendingNavigationInfo : NSObject {
-}
-// The referrer for the page.
-@property(nonatomic, copy) NSString* referrer;
-// The MIME type for the page.
-@property(nonatomic, copy) NSString* MIMEType;
-// The navigation type for the load.
-@property(nonatomic, assign) WKNavigationType navigationType;
-// HTTP request method for the load.
-@property(nonatomic, copy) NSString* HTTPMethod;
-// Whether the pending navigation has been directly cancelled before the
-// navigation is committed.
-// Cancelled navigations should be simply discarded without handling any
-// specific error.
-@property(nonatomic, assign) BOOL cancelled;
-// Whether the navigation was initiated by a user gesture.
-@property(nonatomic, assign) BOOL hasUserGesture;
-
-@end
-
-@implementation CRWWebControllerPendingNavigationInfo
-
-- (instancetype)init {
-  if ((self = [super init])) {
-    _navigationType = WKNavigationTypeOther;
-  }
-  return self;
-}
-
-@end
-
 @interface CRWWebController () <BrowsingDataRemoverObserver,
                                 CRWContextMenuDelegate,
                                 CRWNativeContentDelegate,
@@ -347,7 +312,7 @@
   // this object starts at |decidePolicyForNavigationAction| where the info is
   // extracted from the request, and ends at either |didCommitNavigation| or
   // |didFailProvisionalNavigation|.
-  CRWWebControllerPendingNavigationInfo* _pendingNavigationInfo;
+  CRWPendingNavigationInfo* _pendingNavigationInfo;
 
   // Holds all WKNavigation objects and their states which are currently in
   // flight.
@@ -5440,8 +5405,7 @@
 - (void)updatePendingNavigationInfoFromNavigationAction:
     (WKNavigationAction*)action {
   if (action.targetFrame.mainFrame) {
-    _pendingNavigationInfo =
-        [[CRWWebControllerPendingNavigationInfo alloc] init];
+    _pendingNavigationInfo = [[CRWPendingNavigationInfo alloc] init];
     [_pendingNavigationInfo
         setReferrer:[self referrerFromNavigationAction:action]];
     [_pendingNavigationInfo setNavigationType:action.navigationType];
@@ -5460,8 +5424,7 @@
     (WKNavigationResponse*)response {
   if (response.isForMainFrame) {
     if (!_pendingNavigationInfo) {
-      _pendingNavigationInfo =
-          [[CRWWebControllerPendingNavigationInfo alloc] init];
+      _pendingNavigationInfo = [[CRWPendingNavigationInfo alloc] init];
     }
     [_pendingNavigationInfo setMIMEType:response.response.MIMEType];
   }
diff --git a/media/audio/win/audio_manager_win.cc b/media/audio/win/audio_manager_win.cc
index f7efeff6..f65aa6c0 100644
--- a/media/audio/win/audio_manager_win.cc
+++ b/media/audio/win/audio_manager_win.cc
@@ -149,9 +149,9 @@
                                  AudioLogFactory* audio_log_factory)
     : AudioManagerBase(std::move(audio_thread), audio_log_factory) {
   // |CoreAudioUtil::IsSupported()| uses static variables to avoid doing
-  // multiple initializations.  This is however not thread safe.
-  // So, here we call it explicitly before we kick off the audio thread
-  // or do any other work.
+  // multiple initializations. This is thread safe but call it here explicitly
+  // to ensure initialization is done on the main thread and before we do any
+  // other work.
   CoreAudioUtil::IsSupported();
 
   SetMaxOutputStreamsAllowed(kMaxOutputStreams);
diff --git a/media/audio/win/core_audio_util_win.cc b/media/audio/win/core_audio_util_win.cc
index 9e4144c..6b5278db 100644
--- a/media/audio/win/core_audio_util_win.cc
+++ b/media/audio/win/core_audio_util_win.cc
@@ -413,13 +413,13 @@
   return channel_layout;
 }
 
-bool IsSupportedInternal() {
+IMMDeviceEnumerator* IsSupportedInternal() {
   // It is possible to force usage of WaveXxx APIs by using a command line
   // flag.
   const base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
   if (cmd_line->HasSwitch(switches::kForceWaveAudio)) {
     DVLOG(1) << "Forcing usage of Windows WaveXxx APIs";
-    return false;
+    return nullptr;
   }
 
   // Verify that it is possible to a create the IMMDeviceEnumerator interface.
@@ -430,10 +430,10 @@
     LOG(ERROR)
         << "Failed to create Core Audio device enumerator on thread with ID "
         << GetCurrentThreadId();
-    return false;
+    return nullptr;
   }
 
-  return true;
+  return device_enumerator.Detach();
 }
 
 // Retrieve an audio device specified by |device_id| or a default device
@@ -647,8 +647,10 @@
 }
 
 bool CoreAudioUtil::IsSupported() {
-  static bool g_is_supported = IsSupportedInternal();
-  return g_is_supported;
+  // Hold on to the device enumerator throughout, to avoid multiple
+  // de-/initializations of CoreAudio. See: crbug.com/955434
+  static IMMDeviceEnumerator* g_enumerator = IsSupportedInternal();
+  return g_enumerator != nullptr;
 }
 
 // CoreAudioUtil implementation.
diff --git a/media/capture/video/chromeos/video_capture_device_chromeos_halv3.cc b/media/capture/video/chromeos/video_capture_device_chromeos_halv3.cc
index 5d14a38..2d54f08 100644
--- a/media/capture/video/chromeos/video_capture_device_chromeos_halv3.cc
+++ b/media/capture/video/chromeos/video_capture_device_chromeos_halv3.cc
@@ -86,7 +86,7 @@
   if (!camera_device_delegate_) {
     return;
   }
-  CloseDevice(base::OnceClosure());
+  CloseDevice(base::UnguessableToken());
   camera_device_ipc_thread_.Stop();
   camera_device_delegate_.reset();
   device_context_.reset();
@@ -123,13 +123,12 @@
 
 void VideoCaptureDeviceChromeOSHalv3::SuspendImminent(
     power_manager::SuspendImminent::Reason reason) {
+  auto token = base::UnguessableToken::Create();
+  chromeos::PowerManagerClient::Get()->BlockSuspend(
+      token, "VideoCaptureDeviceChromeOSHalv3");
   capture_task_runner_->PostTask(
-      FROM_HERE,
-      base::BindOnce(
-          &VideoCaptureDeviceChromeOSHalv3::CloseDevice,
-          weak_ptr_factory_.GetWeakPtr(),
-          BindToCurrentLoop(chromeos::PowerManagerClient::Get()
-                                ->GetSuspendReadinessCallback(FROM_HERE))));
+      FROM_HERE, base::BindOnce(&VideoCaptureDeviceChromeOSHalv3::CloseDevice,
+                                weak_ptr_factory_.GetWeakPtr(), token));
 }
 
 void VideoCaptureDeviceChromeOSHalv3::SuspendDone(
@@ -159,7 +158,8 @@
                      camera_device_delegate_->GetWeakPtr(), rotation_));
 }
 
-void VideoCaptureDeviceChromeOSHalv3::CloseDevice(base::OnceClosure callback) {
+void VideoCaptureDeviceChromeOSHalv3::CloseDevice(
+    base::UnguessableToken unblock_suspend_token) {
   DCHECK(capture_task_runner_->BelongsToCurrentThread());
 
   if (!camera_device_delegate_) {
@@ -182,8 +182,8 @@
                                     base::Unretained(&device_closed))));
   base::TimeDelta kWaitTimeoutSecs = base::TimeDelta::FromSeconds(3);
   device_closed.TimedWait(kWaitTimeoutSecs);
-  if (callback) {
-    std::move(callback).Run();
+  if (!unblock_suspend_token.is_empty()) {
+    chromeos::PowerManagerClient::Get()->UnblockSuspend(unblock_suspend_token);
   }
 }
 
diff --git a/media/capture/video/chromeos/video_capture_device_chromeos_halv3.h b/media/capture/video/chromeos/video_capture_device_chromeos_halv3.h
index 559907a..ddd776a 100644
--- a/media/capture/video/chromeos/video_capture_device_chromeos_halv3.h
+++ b/media/capture/video/chromeos/video_capture_device_chromeos_halv3.h
@@ -61,7 +61,7 @@
 
  private:
   void OpenDevice();
-  void CloseDevice(base::OnceClosure callback);
+  void CloseDevice(base::UnguessableToken unblock_suspend_token);
 
   // DisplayRotationDelegate implementation.
   void SetDisplayRotation(const display::Display& display) final;
diff --git a/net/BUILD.gn b/net/BUILD.gn
index 8fb236f8..2c595be 100644
--- a/net/BUILD.gn
+++ b/net/BUILD.gn
@@ -3076,8 +3076,20 @@
 if (is_linux) {
   static_library("epoll_server") {
     sources = [
-      "tools/epoll_server/epoll_server.cc",
-      "tools/epoll_server/epoll_server.h",
+      "third_party/quiche/src/epoll_server/platform/api/epoll_bug.h",
+      "third_party/quiche/src/epoll_server/platform/api/epoll_export.h",
+      "third_party/quiche/src/epoll_server/platform/api/epoll_logging.h",
+      "third_party/quiche/src/epoll_server/platform/api/epoll_ptr_util.h",
+      "third_party/quiche/src/epoll_server/platform/api/epoll_thread.h",
+      "third_party/quiche/src/epoll_server/platform/api/epoll_time.h",
+      "third_party/quiche/src/epoll_server/simple_epoll_server.cc",
+      "third_party/quiche/src/epoll_server/simple_epoll_server.h",
+      "tools/epoll_server/platform/impl/epoll_bug_impl.h",
+      "tools/epoll_server/platform/impl/epoll_export_impl.h",
+      "tools/epoll_server/platform/impl/epoll_logging_impl.h",
+      "tools/epoll_server/platform/impl/epoll_ptr_util_impl.h",
+      "tools/epoll_server/platform/impl/epoll_thread_impl.h",
+      "tools/epoll_server/platform/impl/epoll_time_impl.h",
     ]
     deps = [
       ":net",
@@ -3199,6 +3211,25 @@
   }
 }
 
+source_set("epoll_server_test_tools") {
+  testonly = true
+  sources = [
+    "third_party/quiche/src/epoll_server/platform/api/epoll_address_test_utils.h",
+    "third_party/quiche/src/epoll_server/platform/api/epoll_expect_bug.h",
+    "third_party/quiche/src/epoll_server/platform/api/epoll_test.h",
+    "tools/epoll_server/platform/impl/epoll_address_test_utils_impl.h",
+    "tools/epoll_server/platform/impl/epoll_expect_bug_impl.h",
+    "tools/epoll_server/platform/impl/epoll_test_impl.h",
+  ]
+  deps = [
+    ":net",
+    ":test_support",
+    "//base",
+    "//testing/gmock",
+    "//testing/gtest",
+  ]
+}
+
 source_set("spdy_test_tools") {
   testonly = true
   sources = [
@@ -3369,6 +3400,8 @@
 
   if (is_linux) {
     sources += [
+      "third_party/quiche/src/epoll_server/fake_simple_epoll_server.cc",
+      "third_party/quiche/src/epoll_server/fake_simple_epoll_server.h",
       "third_party/quiche/src/quic/test_tools/bad_packet_writer.cc",
       "third_party/quiche/src/quic/test_tools/bad_packet_writer.h",
       "third_party/quiche/src/quic/test_tools/limited_mtu_test_writer.cc",
@@ -3387,8 +3420,6 @@
       "third_party/quiche/src/quic/test_tools/quic_test_server.h",
       "third_party/quiche/src/quic/test_tools/server_thread.cc",
       "third_party/quiche/src/quic/test_tools/server_thread.h",
-      "tools/epoll_server/fake_epoll_server.cc",
-      "tools/epoll_server/fake_epoll_server.h",
     ]
     deps += [
       ":epoll_quic_tools",
@@ -5557,6 +5588,7 @@
       "quic/platform/impl/quic_flags_test.cc",
       "quic/platform/impl/quic_linux_socket_utils_test.cc",
       "quic/platform/impl/quic_socket_utils_test.cc",
+      "third_party/quiche/src/epoll_server/simple_epoll_server_test.cc",
       "third_party/quiche/src/quic/core/chlo_extractor_test.cc",
       "third_party/quiche/src/quic/core/http/end_to_end_test.cc",
       "third_party/quiche/src/quic/core/http/quic_spdy_client_session_test.cc",
@@ -5579,6 +5611,7 @@
     deps += [
       ":epoll_quic_tools",
       ":epoll_server",
+      ":epoll_server_test_tools",
     ]
   }
 
diff --git a/net/extras/sqlite/sqlite_persistent_reporting_and_nel_store_unittest.cc b/net/extras/sqlite/sqlite_persistent_reporting_and_nel_store_unittest.cc
index c61007d9..bab431d 100644
--- a/net/extras/sqlite/sqlite_persistent_reporting_and_nel_store_unittest.cc
+++ b/net/extras/sqlite/sqlite_persistent_reporting_and_nel_store_unittest.cc
@@ -15,8 +15,11 @@
 #include "base/synchronization/waitable_event.h"
 #include "base/task/post_task.h"
 #include "base/test/bind_test_util.h"
+#include "base/test/simple_test_clock.h"
 #include "base/threading/thread_restrictions.h"
 #include "base/threading/thread_task_runner_handle.h"
+#include "net/network_error_logging/network_error_logging_service.h"
+#include "net/reporting/reporting_test_util.h"
 #include "net/test/test_with_scoped_task_environment.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -27,8 +30,13 @@
 const base::FilePath::CharType kReportingAndNELStoreFilename[] =
     FILE_PATH_LITERAL("ReportingAndNEL");
 
-const url::Origin kOrigin1 = url::Origin::Create(GURL("https://www.foo.test"));
-const url::Origin kOrigin2 = url::Origin::Create(GURL("https://www.bar.test"));
+const GURL kUrl1 = GURL("https://www.foo.test");
+const GURL kUrl2 = GURL("https://www.bar.test");
+const url::Origin kOrigin1 = url::Origin::Create(kUrl1);
+const url::Origin kOrigin2 = url::Origin::Create(kUrl2);
+const IPAddress kServerIP = IPAddress(192, 168, 0, 1);
+const std::string kHeader = "{\"report_to\":\"group\",\"max_age\":86400}";
+const std::string kHeaderMaxAge0 = "{\"report_to\":\"group\",\"max_age\":0}";
 
 enum class Op { kAdd, kDelete, kUpdate };
 
@@ -350,4 +358,216 @@
   RunUntilIdle();
 }
 
+// These tests test that a SQLitePersistentReportingAndNELStore
+// can be used by a NetworkErrorLoggingService to persist NEL policies.
+class SQLitePersistNELTest : public SQLitePersistentReportingAndNELStoreTest {
+ public:
+  SQLitePersistNELTest() {}
+
+  void SetUp() override {
+    SQLitePersistentReportingAndNELStoreTest::SetUp();
+    SetUpNetworkErrorLoggingService();
+  }
+
+  void TearDown() override {
+    service_->OnShutdown();
+    service_.reset();
+    reporting_service_.reset();
+    SQLitePersistentReportingAndNELStoreTest::TearDown();
+  }
+
+  void SetUpNetworkErrorLoggingService() {
+    CreateStore();
+    service_ = NetworkErrorLoggingService::Create(store_.get());
+    reporting_service_ = std::make_unique<TestReportingService>();
+    service_->SetReportingService(reporting_service_.get());
+    service_->SetClockForTesting(&clock_);
+  }
+
+  void SimulateRestart() {
+    TearDown();
+    SetUpNetworkErrorLoggingService();
+  }
+
+  NetworkErrorLoggingService::RequestDetails MakeRequestDetails(
+      GURL url,
+      Error error_type) {
+    NetworkErrorLoggingService::RequestDetails details;
+
+    details.uri = url;
+    details.referrer = GURL("https://referrer.com/");
+    details.user_agent = "Mozilla/1.0";
+    details.server_ip = kServerIP;
+    details.method = "GET";
+    details.status_code = 0;
+    details.elapsed_time = base::TimeDelta::FromSeconds(1);
+    details.type = error_type;
+    details.reporting_upload_depth = 0;
+
+    return details;
+  }
+
+ protected:
+  base::SimpleTestClock clock_;
+  std::unique_ptr<NetworkErrorLoggingService> service_;
+  std::unique_ptr<TestReportingService> reporting_service_;
+};
+
+TEST_F(SQLitePersistNELTest, AddAndRetrieveNELPolicy) {
+  service_->OnHeader(kOrigin1, kServerIP, kHeader);
+  RunUntilIdle();
+
+  EXPECT_EQ(1u, service_->GetPolicyOriginsForTesting().count(kOrigin1));
+  SimulateRestart();
+
+  service_->OnRequest(MakeRequestDetails(kUrl1, ERR_INVALID_RESPONSE));
+  RunUntilIdle();
+
+  EXPECT_EQ(1u, service_->GetPolicyOriginsForTesting().count(kOrigin1));
+
+  EXPECT_THAT(reporting_service_->reports(),
+              testing::ElementsAre(ReportUrlIs(kUrl1)));
+}
+
+TEST_F(SQLitePersistNELTest, AddAndDeleteNELPolicy) {
+  service_->OnHeader(kOrigin1, kServerIP, kHeader);
+  RunUntilIdle();
+
+  EXPECT_EQ(1u, service_->GetPolicyOriginsForTesting().count(kOrigin1));
+  SimulateRestart();
+
+  // Deletes the stored policy.
+  service_->OnHeader(kOrigin1, kServerIP, kHeaderMaxAge0);
+  RunUntilIdle();
+
+  EXPECT_EQ(0u, service_->GetPolicyOriginsForTesting().count(kOrigin1));
+  SimulateRestart();
+
+  service_->OnRequest(MakeRequestDetails(kUrl1, ERR_INVALID_RESPONSE));
+  RunUntilIdle();
+
+  EXPECT_EQ(0u, service_->GetPolicyOriginsForTesting().count(kOrigin1));
+  EXPECT_EQ(0u, reporting_service_->reports().size());
+}
+
+TEST_F(SQLitePersistNELTest, ExpirationTimeIsPersisted) {
+  service_->OnHeader(kOrigin1, kServerIP, kHeader);
+  RunUntilIdle();
+
+  // Makes the policy we just added expired.
+  clock_.Advance(base::TimeDelta::FromSeconds(86401));
+
+  SimulateRestart();
+
+  service_->OnRequest(MakeRequestDetails(kUrl1, ERR_INVALID_RESPONSE));
+  RunUntilIdle();
+
+  EXPECT_EQ(0u, reporting_service_->reports().size());
+
+  // Add the policy again so that it is not expired.
+  service_->OnHeader(kOrigin1, kServerIP, kHeader);
+
+  SimulateRestart();
+
+  service_->OnRequest(MakeRequestDetails(kUrl1, ERR_INVALID_RESPONSE));
+  RunUntilIdle();
+
+  EXPECT_THAT(reporting_service_->reports(),
+              testing::ElementsAre(ReportUrlIs(kUrl1)));
+}
+
+TEST_F(SQLitePersistNELTest, OnRequestUpdatesAccessTime) {
+  service_->OnHeader(kOrigin1, kServerIP, kHeader);
+  RunUntilIdle();
+
+  SimulateRestart();
+
+  // Update the access time by sending a request.
+  clock_.Advance(base::TimeDelta::FromSeconds(100));
+  service_->OnRequest(MakeRequestDetails(kUrl1, ERR_INVALID_RESPONSE));
+  RunUntilIdle();
+
+  EXPECT_THAT(reporting_service_->reports(),
+              testing::ElementsAre(ReportUrlIs(kUrl1)));
+
+  SimulateRestart();
+  // Check that the policy's access time has been updated.
+  base::Time now = clock_.Now();
+  NetworkErrorLoggingService::NELPolicy policy = MakeNELPolicy(kOrigin1, now);
+  std::vector<NetworkErrorLoggingService::NELPolicy> policies;
+  LoadNELPolicies(&policies);
+  ASSERT_EQ(1u, policies.size());
+  EXPECT_EQ(policy.origin, policies[0].origin);
+  EXPECT_TRUE(WithinOneMicrosecond(policy.last_used, policies[0].last_used));
+}
+
+TEST_F(SQLitePersistNELTest, RemoveSomeBrowsingData) {
+  service_->OnHeader(kOrigin1, kServerIP, kHeader);
+  service_->OnHeader(kOrigin2, kServerIP, kHeader);
+  RunUntilIdle();
+
+  SimulateRestart();
+
+  service_->OnRequest(MakeRequestDetails(kUrl1, ERR_INVALID_RESPONSE));
+  RunUntilIdle();
+
+  ASSERT_EQ(1u, service_->GetPolicyOriginsForTesting().count(kOrigin1));
+  ASSERT_EQ(1u, service_->GetPolicyOriginsForTesting().count(kOrigin2));
+  EXPECT_THAT(reporting_service_->reports(),
+              testing::ElementsAre(ReportUrlIs(kUrl1)));
+
+  SimulateRestart();
+
+  service_->RemoveBrowsingData(
+      base::BindRepeating([](const GURL& origin) -> bool {
+        return origin.host() == kOrigin1.host();
+      }));
+  RunUntilIdle();
+
+  EXPECT_EQ(0u, service_->GetPolicyOriginsForTesting().count(kOrigin1));
+  EXPECT_EQ(1u, service_->GetPolicyOriginsForTesting().count(kOrigin2));
+
+  SimulateRestart();
+
+  service_->OnRequest(MakeRequestDetails(kUrl1, ERR_INVALID_RESPONSE));
+  RunUntilIdle();
+  EXPECT_EQ(0u, service_->GetPolicyOriginsForTesting().count(kOrigin1));
+  EXPECT_EQ(1u, service_->GetPolicyOriginsForTesting().count(kOrigin2));
+  EXPECT_EQ(0u, reporting_service_->reports().size());
+}
+
+TEST_F(SQLitePersistNELTest, RemoveAllBrowsingData) {
+  service_->OnHeader(kOrigin1, kServerIP, kHeader);
+  service_->OnHeader(kOrigin2, kServerIP, kHeader);
+  RunUntilIdle();
+
+  SimulateRestart();
+
+  service_->OnRequest(MakeRequestDetails(kUrl1, ERR_INVALID_RESPONSE));
+  service_->OnRequest(MakeRequestDetails(kUrl2, ERR_INVALID_RESPONSE));
+  RunUntilIdle();
+
+  ASSERT_EQ(1u, service_->GetPolicyOriginsForTesting().count(kOrigin1));
+  ASSERT_EQ(1u, service_->GetPolicyOriginsForTesting().count(kOrigin2));
+  EXPECT_THAT(reporting_service_->reports(),
+              testing::ElementsAre(ReportUrlIs(kUrl1), ReportUrlIs(kUrl2)));
+
+  SimulateRestart();
+
+  service_->RemoveAllBrowsingData();
+  RunUntilIdle();
+
+  EXPECT_EQ(0u, service_->GetPolicyOriginsForTesting().count(kOrigin1));
+  EXPECT_EQ(0u, service_->GetPolicyOriginsForTesting().count(kOrigin2));
+
+  SimulateRestart();
+
+  service_->OnRequest(MakeRequestDetails(kUrl1, ERR_INVALID_RESPONSE));
+  service_->OnRequest(MakeRequestDetails(kUrl2, ERR_INVALID_RESPONSE));
+  RunUntilIdle();
+  EXPECT_EQ(0u, service_->GetPolicyOriginsForTesting().count(kOrigin1));
+  EXPECT_EQ(0u, service_->GetPolicyOriginsForTesting().count(kOrigin2));
+  EXPECT_EQ(0u, reporting_service_->reports().size());
+}
+
 }  // namespace net
diff --git a/net/network_error_logging/network_error_logging_service.cc b/net/network_error_logging/network_error_logging_service.cc
index aa875fb..f3c8af42 100644
--- a/net/network_error_logging/network_error_logging_service.cc
+++ b/net/network_error_logging/network_error_logging_service.cc
@@ -9,6 +9,7 @@
 #include <utility>
 #include <vector>
 
+#include "base/bind.h"
 #include "base/json/json_reader.h"
 #include "base/logging.h"
 #include "base/metrics/histogram_macros.h"
@@ -181,10 +182,16 @@
 class NetworkErrorLoggingServiceImpl : public NetworkErrorLoggingService {
  public:
   explicit NetworkErrorLoggingServiceImpl(PersistentNELStore* store)
-      : store_(store) {}
+      : store_(store),
+        started_loading_policies_(false),
+        initialized_(false),
+        weak_factory_(this) {
+    if (!PoliciesArePersisted())
+      initialized_ = true;
+  }
 
   ~NetworkErrorLoggingServiceImpl() override {
-    if (store_)
+    if (PoliciesArePersisted() && initialized_)
       store_->Flush();
   }
 
@@ -193,9 +200,6 @@
   void OnHeader(const url::Origin& origin,
                 const IPAddress& received_ip_address,
                 const std::string& value) override {
-    if (shut_down_)
-      return;
-
     // NEL is only available to secure origins, so don't permit insecure origins
     // to set policies.
     if (!origin.GetURL().SchemeIsCryptographic()) {
@@ -203,10 +207,184 @@
       return;
     }
 
+    base::Time header_received_time = clock_->Now();
+    // base::Unretained is safe because the callback gets stored in
+    // task_backlog_, so the callback will not outlive |*this|.
+    DoOrBacklogTask(base::BindOnce(
+        &NetworkErrorLoggingServiceImpl::DoOnHeader, base::Unretained(this),
+        origin, received_ip_address, value, header_received_time));
+  }
+
+  void OnRequest(RequestDetails details) override {
+    // This method is only called on secure requests.
+    DCHECK(details.uri.SchemeIsCryptographic());
+
+    if (!reporting_service_) {
+      RecordRequestOutcome(RequestOutcome::kDiscardedNoReportingService);
+      return;
+    }
+
+    base::Time request_received_time = clock_->Now();
+    // base::Unretained is safe because the callback gets stored in
+    // task_backlog_, so the callback will not outlive |*this|.
+    DoOrBacklogTask(base::BindOnce(&NetworkErrorLoggingServiceImpl::DoOnRequest,
+                                   base::Unretained(this), std::move(details),
+                                   request_received_time));
+  }
+
+  void QueueSignedExchangeReport(SignedExchangeReportDetails details) override {
+    if (!reporting_service_) {
+      RecordSignedExchangeRequestOutcome(
+          RequestOutcome::kDiscardedNoReportingService);
+      return;
+    }
+    if (!details.outer_url.SchemeIsCryptographic()) {
+      RecordSignedExchangeRequestOutcome(
+          RequestOutcome::kDiscardedInsecureOrigin);
+      return;
+    }
+
+    base::Time request_received_time = clock_->Now();
+    // base::Unretained is safe because the callback gets stored in
+    // task_backlog_, so the callback will not outlive |*this|.
+    DoOrBacklogTask(base::BindOnce(
+        &NetworkErrorLoggingServiceImpl::DoQueueSignedExchangeReport,
+        base::Unretained(this), std::move(details), request_received_time));
+  }
+
+  void RemoveBrowsingData(const base::RepeatingCallback<bool(const GURL&)>&
+                              origin_filter) override {
+    // base::Unretained is safe because the callback gets stored in
+    // task_backlog_, so the callback will not outlive |*this|.
+    DoOrBacklogTask(
+        base::BindOnce(&NetworkErrorLoggingServiceImpl::DoRemoveBrowsingData,
+                       base::Unretained(this), origin_filter));
+  }
+
+  void RemoveAllBrowsingData() override {
+    // base::Unretained is safe because the callback gets stored in
+    // task_backlog_, so the callback will not outlive |*this|.
+    DoOrBacklogTask(
+        base::BindOnce(&NetworkErrorLoggingServiceImpl::DoRemoveAllBrowsingData,
+                       base::Unretained(this)));
+  }
+
+  base::Value StatusAsValue() const override {
+    base::Value dict(base::Value::Type::DICTIONARY);
+    std::vector<base::Value> policy_list;
+    // We wanted sorted (or at least reproducible) output; luckily, policies_ is
+    // a std::map, and therefore already sorted.
+    for (const auto& origin_and_policy : policies_) {
+      const auto& origin = origin_and_policy.first;
+      const auto& policy = origin_and_policy.second;
+      base::Value policy_dict(base::Value::Type::DICTIONARY);
+      policy_dict.SetKey("origin", base::Value(origin.Serialize()));
+      policy_dict.SetKey("includeSubdomains",
+                         base::Value(policy.include_subdomains));
+      policy_dict.SetKey("reportTo", base::Value(policy.report_to));
+      policy_dict.SetKey("expires",
+                         base::Value(NetLog::TimeToString(policy.expires)));
+      policy_dict.SetKey("successFraction",
+                         base::Value(policy.success_fraction));
+      policy_dict.SetKey("failureFraction",
+                         base::Value(policy.failure_fraction));
+      policy_list.push_back(std::move(policy_dict));
+    }
+    dict.SetKey("originPolicies", base::Value(std::move(policy_list)));
+    return dict;
+  }
+
+  std::set<url::Origin> GetPolicyOriginsForTesting() override {
+    std::set<url::Origin> origins;
+    for (const auto& entry : policies_) {
+      origins.insert(entry.first);
+    }
+    return origins;
+  }
+
+ private:
+  // Map from origin to origin's (owned) policy.
+  // Would be unordered_map, but url::Origin has no hash.
+  using PolicyMap = std::map<url::Origin, NELPolicy>;
+
+  // Wildcard policies are policies for which the include_subdomains flag is
+  // set.
+  //
+  // Wildcard policies are accessed by domain name, not full origin, so there
+  // can be multiple wildcard policies per domain name.
+  //
+  // This is a map from domain name to the set of pointers to wildcard policies
+  // in that domain.
+  //
+  // Policies in the map are unowned; they are pointers to the original in the
+  // PolicyMap.
+  using WildcardPolicyMap = std::map<std::string, std::set<const NELPolicy*>>;
+
+  PolicyMap policies_;
+  WildcardPolicyMap wildcard_policies_;
+
+  // The persistent store in which NEL policies will be stored to disk, if not
+  // null. If |store_| is null, then NEL policies will be in-memory only.
+  // The store is owned by the URLRequestContext because Reporting also needs
+  // access to it.
+  PersistentNELStore* store_;
+
+  // Set to true when we have told the store to load NEL policies. This is to
+  // make sure we don't try to load policies multiple times.
+  bool started_loading_policies_;
+
+  // Set to true when the NEL service has been initialized. Before
+  // initialization is complete, commands to the NEL service (i.e. public
+  // method calls) are stashed away in |task_backlog_|, to be executed once
+  // initialization is complete. Initialization is complete automatically if
+  // there is no PersistentNELStore. If there is a store, then initialization is
+  // complete when the NEL policies have finished being loaded from the store
+  // (either successfully or unsuccessfully).
+  bool initialized_;
+
+  // Backlog of tasks waiting on initialization.
+  std::vector<base::OnceClosure> task_backlog_;
+
+  base::WeakPtrFactory<NetworkErrorLoggingServiceImpl> weak_factory_;
+
+  bool PoliciesArePersisted() const { return store_ != nullptr; }
+
+  void DoOrBacklogTask(base::OnceClosure task) {
+    if (shut_down_)
+      return;
+
+    FetchAllPoliciesFromStoreIfNecessary();
+
+    if (!initialized_) {
+      task_backlog_.push_back(std::move(task));
+      return;
+    }
+
+    std::move(task).Run();
+  }
+
+  void ExecuteBacklog() {
+    DCHECK(initialized_);
+
+    if (shut_down_)
+      return;
+
+    for (base::OnceClosure& task : task_backlog_) {
+      std::move(task).Run();
+    }
+    task_backlog_.clear();
+  }
+
+  void DoOnHeader(const url::Origin& origin,
+                  const IPAddress& received_ip_address,
+                  const std::string& value,
+                  base::Time header_received_time) {
+    DCHECK(initialized_);
+
     NELPolicy policy;
     policy.origin = origin;
     policy.received_ip_address = received_ip_address;
-    policy.last_used = clock_->Now();
+    policy.last_used = header_received_time;
     HeaderOutcome outcome = ParseHeader(value, clock_->Now(), &policy);
     RecordHeaderOutcome(outcome);
     if (outcome != HeaderOutcome::SET && outcome != HeaderOutcome::REMOVED)
@@ -224,9 +402,7 @@
       return;
 
     DVLOG(1) << "Received NEL policy for " << origin;
-    auto inserted = policies_.insert(std::make_pair(origin, policy));
-    DCHECK(inserted.second);
-    MaybeAddWildcardPolicy(origin, &inserted.first->second);
+    AddPolicy(std::move(policy));
 
     // Evict policies if the policy limit is exceeded.
     if (policies_.size() > kMaxPolicies) {
@@ -237,17 +413,9 @@
     }
   }
 
-  void OnRequest(RequestDetails details) override {
-    if (shut_down_)
-      return;
-
-    if (!reporting_service_) {
-      RecordRequestOutcome(RequestOutcome::kDiscardedNoReportingService);
-      return;
-    }
-
-    // This method is only called on secure requests.
-    DCHECK(details.uri.SchemeIsCryptographic());
+  void DoOnRequest(RequestDetails details, base::Time request_received_time) {
+    DCHECK(reporting_service_);
+    DCHECK(initialized_);
 
     auto report_origin = url::Origin::Create(details.uri);
     const NELPolicy* policy = FindPolicyForOrigin(report_origin);
@@ -256,8 +424,7 @@
       return;
     }
 
-    // Mark the policy used.
-    policy->last_used = clock_->Now();
+    MarkPolicyUsed(policy, request_received_time);
 
     Error type = details.type;
     // It is expected for Reporting uploads to terminate with ERR_ABORTED, since
@@ -329,21 +496,10 @@
     RecordRequestOutcome(RequestOutcome::kQueued);
   }
 
-  void QueueSignedExchangeReport(
-      const SignedExchangeReportDetails& details) override {
-    if (shut_down_)
-      return;
+  void DoQueueSignedExchangeReport(SignedExchangeReportDetails details,
+                                   base::Time request_received_time) {
+    DCHECK(reporting_service_);
 
-    if (!reporting_service_) {
-      RecordSignedExchangeRequestOutcome(
-          RequestOutcome::kDiscardedNoReportingService);
-      return;
-    }
-    if (!details.outer_url.SchemeIsCryptographic()) {
-      RecordSignedExchangeRequestOutcome(
-          RequestOutcome::kDiscardedInsecureOrigin);
-      return;
-    }
     const auto report_origin = url::Origin::Create(details.outer_url);
     const NELPolicy* policy = FindPolicyForOrigin(report_origin);
     if (!policy) {
@@ -352,8 +508,7 @@
       return;
     }
 
-    // Mark the policy used.
-    policy->last_used = clock_->Now();
+    MarkPolicyUsed(policy, request_received_time);
 
     if (IsMismatchingSubdomainReport(*policy, report_origin)) {
       RecordSignedExchangeRequestOutcome(
@@ -386,8 +541,9 @@
     RecordSignedExchangeRequestOutcome(RequestOutcome::kQueued);
   }
 
-  void RemoveBrowsingData(const base::RepeatingCallback<bool(const GURL&)>&
-                              origin_filter) override {
+  void DoRemoveBrowsingData(
+      const base::RepeatingCallback<bool(const GURL&)>& origin_filter) {
+    DCHECK(initialized_);
     for (auto it = policies_.begin(); it != policies_.end();) {
       const url::Origin& origin = it->first;
       // Remove policies matching the filter.
@@ -397,74 +553,24 @@
         ++it;
       }
     }
+    if (PoliciesArePersisted())
+      store_->Flush();
   }
 
-  void RemoveAllBrowsingData() override {
+  void DoRemoveAllBrowsingData() {
+    DCHECK(initialized_);
+    if (PoliciesArePersisted()) {
+      // TODO(chlily): Add a DeleteAllNELPolicies command to PersistentNELStore.
+      for (auto origin_and_policy : policies_) {
+        store_->DeleteNELPolicy(origin_and_policy.second);
+      }
+      store_->Flush();
+    }
+
     wildcard_policies_.clear();
     policies_.clear();
   }
 
-  base::Value StatusAsValue() const override {
-    base::Value dict(base::Value::Type::DICTIONARY);
-    std::vector<base::Value> policy_list;
-    // We wanted sorted (or at least reproducible) output; luckily, policies_ is
-    // a std::map, and therefore already sorted.
-    for (const auto& origin_and_policy : policies_) {
-      const auto& origin = origin_and_policy.first;
-      const auto& policy = origin_and_policy.second;
-      base::Value policy_dict(base::Value::Type::DICTIONARY);
-      policy_dict.SetKey("origin", base::Value(origin.Serialize()));
-      policy_dict.SetKey("includeSubdomains",
-                         base::Value(policy.include_subdomains));
-      policy_dict.SetKey("reportTo", base::Value(policy.report_to));
-      policy_dict.SetKey("expires",
-                         base::Value(NetLog::TimeToString(policy.expires)));
-      policy_dict.SetKey("successFraction",
-                         base::Value(policy.success_fraction));
-      policy_dict.SetKey("failureFraction",
-                         base::Value(policy.failure_fraction));
-      policy_list.push_back(std::move(policy_dict));
-    }
-    dict.SetKey("originPolicies", base::Value(std::move(policy_list)));
-    return dict;
-  }
-
-  std::set<url::Origin> GetPolicyOriginsForTesting() override {
-    std::set<url::Origin> origins;
-    for (const auto& entry : policies_) {
-      origins.insert(entry.first);
-    }
-    return origins;
-  }
-
- private:
-  // Map from origin to origin's (owned) policy.
-  // Would be unordered_map, but url::Origin has no hash.
-  using PolicyMap = std::map<url::Origin, NELPolicy>;
-
-  // Wildcard policies are policies for which the include_subdomains flag is
-  // set.
-  //
-  // Wildcard policies are accessed by domain name, not full origin, so there
-  // can be multiple wildcard policies per domain name.
-  //
-  // This is a map from domain name to the set of pointers to wildcard policies
-  // in that domain.
-  //
-  // Policies in the map are unowned; they are pointers to the original in the
-  // PolicyMap.
-  using WildcardPolicyMap = std::map<std::string, std::set<const NELPolicy*>>;
-
-  PolicyMap policies_;
-  WildcardPolicyMap wildcard_policies_;
-
-  // The persistent store in which NEL policies will be stored to disk, if not
-  // null. If |store_| is null, then NEL policies will be in-memory only.
-  // The store is owned by the URLRequestContext because Reporting also needs
-  // access to it.
-  // TODO(chlily): Implement.
-  PersistentNELStore* store_;
-
   HeaderOutcome ParseHeader(const std::string& json_value,
                             base::Time now,
                             NELPolicy* policy_out) const {
@@ -529,6 +635,8 @@
   }
 
   const NELPolicy* FindPolicyForOrigin(const url::Origin& origin) const {
+    DCHECK(initialized_);
+
     auto it = policies_.find(origin);
     if (it != policies_.end() && clock_->Now() < it->second.expires)
       return &it->second;
@@ -568,6 +676,25 @@
     return nullptr;
   }
 
+  // There must be no pre-existing policy for |policy.origin|. Returns iterator
+  // to the inserted policy.
+  PolicyMap::iterator AddPolicy(NELPolicy policy) {
+    // If |initialized_| is false, then we are calling this from
+    // OnPoliciesLoaded(), which means we don't want to add the given policy to
+    // the store because we have just loaded it from there.
+    if (PoliciesArePersisted() && initialized_)
+      store_->AddNELPolicy(policy);
+
+    auto iter_and_result =
+        policies_.insert(std::make_pair(policy.origin, std::move(policy)));
+    DCHECK(iter_and_result.second);
+
+    const NELPolicy& inserted_policy = iter_and_result.first->second;
+    MaybeAddWildcardPolicy(inserted_policy.origin, &inserted_policy);
+
+    return iter_and_result.first;
+  }
+
   void MaybeAddWildcardPolicy(const url::Origin& origin,
                               const NELPolicy* policy) {
     DCHECK(policy);
@@ -586,6 +713,10 @@
     DCHECK(policy_it != policies_.end());
     NELPolicy* policy = &policy_it->second;
     MaybeRemoveWildcardPolicy(policy);
+
+    if (PoliciesArePersisted() && initialized_)
+      store_->DeleteNELPolicy(*policy);
+
     return policies_.erase(policy_it);
   }
 
@@ -607,6 +738,12 @@
       wildcard_policies_.erase(wildcard_it);
   }
 
+  void MarkPolicyUsed(const NELPolicy* policy, base::Time time_used) const {
+    policy->last_used = time_used;
+    if (PoliciesArePersisted() && initialized_)
+      store_->UpdateNELPolicyAccessTime(*policy);
+  }
+
   void RemoveAllExpiredPolicies() {
     for (auto it = policies_.begin(); it != policies_.end();) {
       if (it->second.expires < clock_->Now()) {
@@ -701,6 +838,41 @@
       return base::nullopt;
     return sampling_fraction;
   }
+
+  void FetchAllPoliciesFromStoreIfNecessary() {
+    if (!PoliciesArePersisted() || started_loading_policies_)
+      return;
+
+    started_loading_policies_ = true;
+    FetchAllPoliciesFromStore();
+  }
+
+  void FetchAllPoliciesFromStore() {
+    DCHECK(PoliciesArePersisted());
+    DCHECK(!initialized_);
+
+    store_->LoadNELPolicies(
+        base::BindOnce(&NetworkErrorLoggingServiceImpl::OnPoliciesLoaded,
+                       weak_factory_.GetWeakPtr()));
+  }
+
+  // This is called when loading from the store is complete, regardless of
+  // success or failure.
+  // DB initialization may have failed, in which case we will receive an empty
+  // vector from the PersistentNELStore. This is indistinguishable from a
+  // successful load that happens to not yield any policies, but in
+  // either case we still want to go through the task backlog.
+  void OnPoliciesLoaded(std::vector<NELPolicy> loaded_policies) {
+    DCHECK(PoliciesArePersisted());
+    DCHECK(!initialized_);
+
+    // TODO(chlily): Toss any expired policies we encounter.
+    for (NELPolicy& policy : loaded_policies) {
+      AddPolicy(std::move(policy));
+    }
+    initialized_ = true;
+    ExecuteBacklog();
+  }
 };
 
 }  // namespace
@@ -815,12 +987,13 @@
 
 void NetworkErrorLoggingService::SetReportingService(
     ReportingService* reporting_service) {
+  DCHECK(!reporting_service_);
   reporting_service_ = reporting_service;
 }
 
 void NetworkErrorLoggingService::OnShutdown() {
   shut_down_ = true;
-  SetReportingService(nullptr);
+  reporting_service_ = nullptr;
 }
 
 void NetworkErrorLoggingService::SetClockForTesting(const base::Clock* clock) {
diff --git a/net/network_error_logging/network_error_logging_service.h b/net/network_error_logging/network_error_logging_service.h
index 642e2fc..98e08ce1 100644
--- a/net/network_error_logging/network_error_logging_service.h
+++ b/net/network_error_logging/network_error_logging_service.h
@@ -227,7 +227,7 @@
 
   // Queues a Signed Exchange report.
   virtual void QueueSignedExchangeReport(
-      const SignedExchangeReportDetails& details) = 0;
+      SignedExchangeReportDetails details) = 0;
 
   // Removes browsing data (origin policies) associated with any origin for
   // which |origin_filter| returns true.
@@ -241,10 +241,12 @@
   // Sets the ReportingService that will be used to queue network error reports.
   // If |nullptr| is passed, reports will be queued locally or discarded.
   // |reporting_service| must outlive the NetworkErrorLoggingService.
+  // Should not be called again if previously called with a non-null pointer.
   void SetReportingService(ReportingService* reporting_service);
 
-  // Shuts down the NEL service so that no more requests or headers are
-  // processed and no more reports are queued.
+  // Shuts down the NEL service, so that no more requests or headers can be
+  // processed, no more reports are queued, and browsing data can no longer be
+  // cleared.
   void OnShutdown();
 
   // Sets a base::Clock (used to track policy expiration) for tests.
diff --git a/net/network_error_logging/network_error_logging_service_unittest.cc b/net/network_error_logging/network_error_logging_service_unittest.cc
index 8e274fd53..c2fefd5 100644
--- a/net/network_error_logging/network_error_logging_service_unittest.cc
+++ b/net/network_error_logging/network_error_logging_service_unittest.cc
@@ -18,8 +18,7 @@
 #include "net/base/net_errors.h"
 #include "net/network_error_logging/mock_persistent_nel_store.h"
 #include "net/network_error_logging/network_error_logging_service.h"
-#include "net/reporting/reporting_policy.h"
-#include "net/reporting/reporting_service.h"
+#include "net/reporting/reporting_test_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
 #include "url/origin.h"
@@ -27,97 +26,15 @@
 namespace net {
 namespace {
 
-class TestReportingService : public ReportingService {
- public:
-  struct Report {
-    Report() = default;
-
-    Report(Report&& other)
-        : url(other.url),
-          user_agent(other.user_agent),
-          group(other.group),
-          type(other.type),
-          body(std::move(other.body)),
-          depth(other.depth) {}
-
-    Report(const GURL& url,
-           const std::string& user_agent,
-           const std::string& group,
-           const std::string& type,
-           std::unique_ptr<const base::Value> body,
-           int depth)
-        : url(url),
-          user_agent(user_agent),
-          group(group),
-          type(type),
-          body(std::move(body)),
-          depth(depth) {}
-
-    ~Report() = default;
-
-    GURL url;
-    std::string user_agent;
-    std::string group;
-    std::string type;
-    std::unique_ptr<const base::Value> body;
-    int depth;
-
-   private:
-    DISALLOW_COPY(Report);
-  };
-
-  TestReportingService() = default;
-
-  const std::vector<Report>& reports() const { return reports_; }
-
-  // ReportingService implementation:
-
-  ~TestReportingService() override = default;
-
-  void QueueReport(const GURL& url,
-                   const std::string& user_agent,
-                   const std::string& group,
-                   const std::string& type,
-                   std::unique_ptr<const base::Value> body,
-                   int depth) override {
-    reports_.push_back(
-        Report(url, user_agent, group, type, std::move(body), depth));
-  }
-
-  void ProcessHeader(const GURL& url,
-                     const std::string& header_value) override {
-    NOTREACHED();
-  }
-
-  void RemoveBrowsingData(int data_type_mask,
-                          const base::RepeatingCallback<bool(const GURL&)>&
-                              origin_filter) override {
-    NOTREACHED();
-  }
-
-  void RemoveAllBrowsingData(int data_type_mask) override { NOTREACHED(); }
-
-  void OnShutdown() override {}
-
-  const ReportingPolicy& GetPolicy() const override {
-    NOTREACHED();
-    return dummy_policy_;
-  }
-
-  ReportingContext* GetContextForTesting() const override {
-    NOTREACHED();
-    return nullptr;
-  }
-
- private:
-  std::vector<Report> reports_;
-  ReportingPolicy dummy_policy_;
-
-  DISALLOW_COPY_AND_ASSIGN(TestReportingService);
-};
-
 // The tests are parametrized on a boolean value which represents whether or not
 // to use a MockPersistentNELStore.
+// If a MockPersistentNELStore is used, then calls to
+// NetworkErrorLoggingService::OnHeader(), OnRequest(),
+// QueueSignedExchangeReport(), RemoveBrowsingData(), and
+// RemoveAllBrowsingData() will block until the store finishes loading.
+// Therefore, for tests that should run synchronously (i.e. tests that don't
+// specifically test the asynchronous/deferred task behavior), FinishLoading()
+// must be called after the first call to one of the above methods.
 class NetworkErrorLoggingServiceTest : public ::testing::TestWithParam<bool> {
  protected:
   NetworkErrorLoggingServiceTest() {
@@ -137,13 +54,6 @@
     service_->SetReportingService(reporting_service_.get());
   }
 
-  void DestroyReportingService() {
-    DCHECK(reporting_service_);
-
-    service_->SetReportingService(nullptr);
-    reporting_service_.reset();
-  }
-
   NetworkErrorLoggingService::RequestDetails MakeRequestDetails(
       GURL url,
       Error error_type,
@@ -198,6 +108,18 @@
     return url::Origin::Create(url);
   }
 
+  NetworkErrorLoggingService::NELPolicy MakePolicyForOrigin(
+      url::Origin origin,
+      base::Time expires = base::Time(),
+      base::Time last_used = base::Time()) {
+    NetworkErrorLoggingService::NELPolicy policy;
+    policy.origin = std::move(origin);
+    policy.expires = expires;
+    policy.last_used = last_used;
+
+    return policy;
+  }
+
   // Returns whether the NetworkErrorLoggingService has a policy corresponding
   // to |origin|. Returns true if so, even if the policy is expired.
   bool HasPolicyForOrigin(const url::Origin& origin) {
@@ -208,6 +130,12 @@
 
   size_t PolicyCount() { return service_->GetPolicyOriginsForTesting().size(); }
 
+  // Makes the rest of the test run synchronously.
+  void FinishLoading(bool load_success) {
+    if (store())
+      store()->FinishLoading(load_success);
+  }
+
   const GURL kUrl_ = GURL("https://example.com/path");
   const GURL kUrlDifferentPort_ = GURL("https://example.com:4433/path");
   const GURL kUrlSubdomain_ = GURL("https://subdomain.example.com/path");
@@ -247,7 +175,6 @@
 
   const GURL kReferrer_ = GURL("https://referrer.com/");
 
- private:
   // |store_| needs to outlive |service_|.
   std::unique_ptr<MockPersistentNELStore> store_;
   std::unique_ptr<NetworkErrorLoggingService> service_;
@@ -268,22 +195,32 @@
 }
 
 TEST_P(NetworkErrorLoggingServiceTest, NoReportingService) {
-  DestroyReportingService();
+  service_ = NetworkErrorLoggingService::Create(store_.get());
 
   service()->OnHeader(kOrigin_, kServerIP_, kHeader_);
 
+  // Make the rest of the test run synchronously.
+  FinishLoading(true /* load_success */);
+
+  // Should not crash.
   service()->OnRequest(MakeRequestDetails(kUrl_, ERR_CONNECTION_REFUSED));
 }
 
 TEST_P(NetworkErrorLoggingServiceTest, NoPolicyForOrigin) {
   service()->OnRequest(MakeRequestDetails(kUrl_, ERR_CONNECTION_REFUSED));
 
+  // Make the rest of the test run synchronously.
+  FinishLoading(true /* load_success */);
+
   EXPECT_TRUE(reports().empty());
 }
 
 TEST_P(NetworkErrorLoggingServiceTest, JsonTooLong) {
   service()->OnHeader(kOrigin_, kServerIP_, kHeaderTooLong_);
 
+  // Make the rest of the test run synchronously.
+  FinishLoading(true /* load_success */);
+
   service()->OnRequest(MakeRequestDetails(kUrl_, ERR_CONNECTION_REFUSED));
 
   EXPECT_TRUE(reports().empty());
@@ -292,6 +229,9 @@
 TEST_P(NetworkErrorLoggingServiceTest, JsonTooDeep) {
   service()->OnHeader(kOrigin_, kServerIP_, kHeaderTooDeep_);
 
+  // Make the rest of the test run synchronously.
+  FinishLoading(true /* load_success */);
+
   service()->OnRequest(MakeRequestDetails(kUrl_, ERR_CONNECTION_REFUSED));
 
   EXPECT_TRUE(reports().empty());
@@ -300,6 +240,9 @@
 TEST_P(NetworkErrorLoggingServiceTest, SuccessReportQueued) {
   service()->OnHeader(kOrigin_, kServerIP_, kHeaderSuccessFraction1_);
 
+  // Make the rest of the test run synchronously.
+  FinishLoading(true /* load_success */);
+
   service()->OnRequest(MakeRequestDetails(kUrl_, OK));
 
   ASSERT_EQ(1u, reports().size());
@@ -337,6 +280,9 @@
       "{\"report_to\":\"group\",\"max_age\":86400,\"failure_fraction\":1.0}";
   service()->OnHeader(kOrigin_, kServerIP_, kHeaderFailureFraction1);
 
+  // Make the rest of the test run synchronously.
+  FinishLoading(true /* load_success */);
+
   service()->OnRequest(MakeRequestDetails(kUrl_, ERR_CONNECTION_REFUSED));
 
   ASSERT_EQ(1u, reports().size());
@@ -374,6 +320,9 @@
       "{\"report_to\":\"group\",\"max_age\":86400,\"failure_fraction\":1.0}";
   service()->OnHeader(kOrigin_, kServerIP_, kHeaderFailureFraction1);
 
+  // Make the rest of the test run synchronously.
+  FinishLoading(true /* load_success */);
+
   // This error code happens to not be mapped to a NEL report `type` field
   // value.
   service()->OnRequest(MakeRequestDetails(kUrl_, ERR_FILE_NO_SPACE));
@@ -392,6 +341,9 @@
       "{\"report_to\":\"group\",\"max_age\":86400,\"failure_fraction\":1.0}";
   service()->OnHeader(kOrigin_, kServerIP_, kHeaderFailureFraction1);
 
+  // Make the rest of the test run synchronously.
+  FinishLoading(true /* load_success */);
+
   // This error code happens to not be mapped to a NEL report `type` field
   // value.  Because it's a certificate error, we'll set the `phase` to be
   // `connection`.
@@ -411,6 +363,9 @@
       "{\"report_to\":\"group\",\"max_age\":86400,\"failure_fraction\":1.0}";
   service()->OnHeader(kOrigin_, kServerIP_, kHeaderFailureFraction1);
 
+  // Make the rest of the test run synchronously.
+  FinishLoading(true /* load_success */);
+
   service()->OnRequest(MakeRequestDetails(kUrl_, OK, "GET", 504));
 
   ASSERT_EQ(1u, reports().size());
@@ -446,6 +401,9 @@
 TEST_P(NetworkErrorLoggingServiceTest, SuccessReportDowngraded) {
   service()->OnHeader(kOrigin_, kServerIP_, kHeaderSuccessFraction1_);
 
+  // Make the rest of the test run synchronously.
+  FinishLoading(true /* load_success */);
+
   service()->OnRequest(
       MakeRequestDetails(kUrl_, OK, "GET", 200, kOtherServerIP_));
 
@@ -480,6 +438,9 @@
 TEST_P(NetworkErrorLoggingServiceTest, FailureReportDowngraded) {
   service()->OnHeader(kOrigin_, kServerIP_, kHeaderSuccessFraction1_);
 
+  // Make the rest of the test run synchronously.
+  FinishLoading(true /* load_success */);
+
   service()->OnRequest(MakeRequestDetails(kUrl_, ERR_CONNECTION_REFUSED, "GET",
                                           200, kOtherServerIP_));
 
@@ -514,6 +475,9 @@
 TEST_P(NetworkErrorLoggingServiceTest, HttpErrorReportDowngraded) {
   service()->OnHeader(kOrigin_, kServerIP_, kHeaderSuccessFraction1_);
 
+  // Make the rest of the test run synchronously.
+  FinishLoading(true /* load_success */);
+
   service()->OnRequest(
       MakeRequestDetails(kUrl_, OK, "GET", 504, kOtherServerIP_));
 
@@ -548,6 +512,9 @@
 TEST_P(NetworkErrorLoggingServiceTest, DNSFailureReportNotDowngraded) {
   service()->OnHeader(kOrigin_, kServerIP_, kHeaderSuccessFraction1_);
 
+  // Make the rest of the test run synchronously.
+  FinishLoading(true /* load_success */);
+
   service()->OnRequest(MakeRequestDetails(kUrl_, ERR_NAME_NOT_RESOLVED, "GET",
                                           0, kOtherServerIP_));
 
@@ -582,6 +549,9 @@
 TEST_P(NetworkErrorLoggingServiceTest, SuccessPOSTReportQueued) {
   service()->OnHeader(kOrigin_, kServerIP_, kHeaderSuccessFraction1_);
 
+  // Make the rest of the test run synchronously.
+  FinishLoading(true /* load_success */);
+
   service()->OnRequest(MakeRequestDetails(kUrl_, OK, "POST"));
 
   ASSERT_EQ(1u, reports().size());
@@ -610,6 +580,10 @@
 
 TEST_P(NetworkErrorLoggingServiceTest, MaxAge0) {
   service()->OnHeader(kOrigin_, kServerIP_, kHeader_);
+
+  // Make the rest of the test run synchronously.
+  FinishLoading(true /* load_success */);
+
   EXPECT_EQ(1u, PolicyCount());
 
   // Max_age of 0 removes the policy.
@@ -624,6 +598,9 @@
 TEST_P(NetworkErrorLoggingServiceTest, SuccessFraction0) {
   service()->OnHeader(kOrigin_, kServerIP_, kHeaderSuccessFraction0_);
 
+  // Make the rest of the test run synchronously.
+  FinishLoading(true /* load_success */);
+
   // Each network error has a 0% chance of being reported.  Fire off several and
   // verify that no reports are produced.
   constexpr size_t kReportCount = 100;
@@ -641,6 +618,9 @@
       "\"failure_fraction\":0.25}";
   service()->OnHeader(kOrigin_, kServerIP_, kHeaderSuccessFractionHalf);
 
+  // Make the rest of the test run synchronously.
+  FinishLoading(true /* load_success */);
+
   // Each network error has a 50% chance of being reported.  Fire off several
   // and verify that some requests were reported and some weren't.  (We can't
   // verify exact counts because each decision is made randomly.)
@@ -672,6 +652,9 @@
       "{\"report_to\":\"group\",\"max_age\":86400,\"failure_fraction\":0.0}";
   service()->OnHeader(kOrigin_, kServerIP_, kHeaderFailureFraction0);
 
+  // Make the rest of the test run synchronously.
+  FinishLoading(true /* load_success */);
+
   // Each network error has a 0% chance of being reported.  Fire off several and
   // verify that no reports are produced.
   constexpr size_t kReportCount = 100;
@@ -689,6 +672,9 @@
       "\"success_fraction\":0.25}";
   service()->OnHeader(kOrigin_, kServerIP_, kHeaderFailureFractionHalf);
 
+  // Make the rest of the test run synchronously.
+  FinishLoading(true /* load_success */);
+
   // Each network error has a 50% chance of being reported.  Fire off several
   // and verify that some requests were reported and some weren't.  (We can't
   // verify exact counts because each decision is made randomly.)
@@ -717,6 +703,9 @@
        ExcludeSubdomainsDoesntMatchDifferentPort) {
   service()->OnHeader(kOrigin_, kServerIP_, kHeader_);
 
+  // Make the rest of the test run synchronously.
+  FinishLoading(true /* load_success */);
+
   service()->OnRequest(
       MakeRequestDetails(kUrlDifferentPort_, ERR_CONNECTION_REFUSED));
 
@@ -726,6 +715,9 @@
 TEST_P(NetworkErrorLoggingServiceTest, ExcludeSubdomainsDoesntMatchSubdomain) {
   service()->OnHeader(kOrigin_, kServerIP_, kHeader_);
 
+  // Make the rest of the test run synchronously.
+  FinishLoading(true /* load_success */);
+
   service()->OnRequest(
       MakeRequestDetails(kUrlSubdomain_, ERR_CONNECTION_REFUSED));
 
@@ -735,6 +727,9 @@
 TEST_P(NetworkErrorLoggingServiceTest, IncludeSubdomainsMatchesDifferentPort) {
   service()->OnHeader(kOrigin_, kServerIP_, kHeaderIncludeSubdomains_);
 
+  // Make the rest of the test run synchronously.
+  FinishLoading(true /* load_success */);
+
   service()->OnRequest(
       MakeRequestDetails(kUrlDifferentPort_, ERR_NAME_NOT_RESOLVED));
 
@@ -745,6 +740,9 @@
 TEST_P(NetworkErrorLoggingServiceTest, IncludeSubdomainsMatchesSubdomain) {
   service()->OnHeader(kOrigin_, kServerIP_, kHeaderIncludeSubdomains_);
 
+  // Make the rest of the test run synchronously.
+  FinishLoading(true /* load_success */);
+
   service()->OnRequest(
       MakeRequestDetails(kUrlSubdomain_, ERR_NAME_NOT_RESOLVED));
 
@@ -755,6 +753,9 @@
        IncludeSubdomainsDoesntMatchSuperdomain) {
   service()->OnHeader(kOriginSubdomain_, kServerIP_, kHeaderIncludeSubdomains_);
 
+  // Make the rest of the test run synchronously.
+  FinishLoading(true /* load_success */);
+
   service()->OnRequest(MakeRequestDetails(kUrl_, ERR_NAME_NOT_RESOLVED));
 
   EXPECT_TRUE(reports().empty());
@@ -764,6 +765,9 @@
        IncludeSubdomainsDoesntReportConnectionError) {
   service()->OnHeader(kOrigin_, kServerIP_, kHeaderIncludeSubdomains_);
 
+  // Make the rest of the test run synchronously.
+  FinishLoading(true /* load_success */);
+
   service()->OnRequest(
       MakeRequestDetails(kUrlSubdomain_, ERR_CONNECTION_REFUSED));
 
@@ -774,6 +778,9 @@
        IncludeSubdomainsDoesntReportApplicationError) {
   service()->OnHeader(kOrigin_, kServerIP_, kHeaderIncludeSubdomains_);
 
+  // Make the rest of the test run synchronously.
+  FinishLoading(true /* load_success */);
+
   service()->OnRequest(
       MakeRequestDetails(kUrlSubdomain_, ERR_INVALID_HTTP_RESPONSE));
 
@@ -783,6 +790,9 @@
 TEST_P(NetworkErrorLoggingServiceTest, IncludeSubdomainsDoesntReportSuccess) {
   service()->OnHeader(kOrigin_, kServerIP_, kHeaderIncludeSubdomains_);
 
+  // Make the rest of the test run synchronously.
+  FinishLoading(true /* load_success */);
+
   service()->OnRequest(MakeRequestDetails(kUrlSubdomain_, OK));
 
   EXPECT_TRUE(reports().empty());
@@ -795,6 +805,9 @@
       "\"include_subdomains\":true,\"success_fraction\":1.0}";
   service()->OnHeader(kOrigin_, kServerIP_, kHeaderIncludeSubdomainsSuccess1);
 
+  // Make the rest of the test run synchronously.
+  FinishLoading(true /* load_success */);
+
   service()->OnRequest(MakeRequestDetails(kUrl_, OK));
 
   ASSERT_EQ(1u, reports().size());
@@ -803,6 +816,10 @@
 
 TEST_P(NetworkErrorLoggingServiceTest, RemoveAllBrowsingData) {
   service()->OnHeader(kOrigin_, kServerIP_, kHeader_);
+
+  // Make the rest of the test run synchronously.
+  FinishLoading(true /* load_success */);
+
   EXPECT_EQ(1u, PolicyCount());
   EXPECT_TRUE(HasPolicyForOrigin(kOrigin_));
 
@@ -817,6 +834,10 @@
 
 TEST_P(NetworkErrorLoggingServiceTest, RemoveSomeBrowsingData) {
   service()->OnHeader(kOrigin_, kServerIP_, kHeader_);
+
+  // Make the rest of the test run synchronously.
+  FinishLoading(true /* load_success */);
+
   service()->OnHeader(kOriginDifferentHost_, kServerIP_, kHeader_);
   EXPECT_EQ(2u, PolicyCount());
 
@@ -842,6 +863,9 @@
 TEST_P(NetworkErrorLoggingServiceTest, Nested) {
   service()->OnHeader(kOrigin_, kServerIP_, kHeader_);
 
+  // Make the rest of the test run synchronously.
+  FinishLoading(true /* load_success */);
+
   NetworkErrorLoggingService::RequestDetails details =
       MakeRequestDetails(kUrl_, ERR_CONNECTION_REFUSED);
   details.reporting_upload_depth =
@@ -856,6 +880,9 @@
 TEST_P(NetworkErrorLoggingServiceTest, NestedTooDeep) {
   service()->OnHeader(kOrigin_, kServerIP_, kHeader_);
 
+  // Make the rest of the test run synchronously.
+  FinishLoading(true /* load_success */);
+
   NetworkErrorLoggingService::RequestDetails details =
       MakeRequestDetails(kUrl_, ERR_CONNECTION_REFUSED);
   details.reporting_upload_depth =
@@ -880,6 +907,10 @@
   clock.Advance(delta_from_origin);
 
   service()->OnHeader(kOrigin_, kServerIP_, kHeaderSuccessFraction1_);
+
+  // Make the rest of the test run synchronously.
+  FinishLoading(true /* load_success */);
+
   service()->OnHeader(kOriginDifferentHost_, kServerIP_, kHeader_);
   service()->OnHeader(kOriginSubdomain_, kServerIP_, kHeaderIncludeSubdomains_);
   const std::string kHeaderWrongTypes =
@@ -938,9 +969,14 @@
 }
 
 TEST_P(NetworkErrorLoggingServiceTest, NoReportingService_SignedExchange) {
-  DestroyReportingService();
+  service_ = NetworkErrorLoggingService::Create(store_.get());
 
   service()->OnHeader(kOrigin_, kServerIP_, kHeader_);
+
+  // Make the rest of the test run synchronously.
+  FinishLoading(true /* load_success */);
+
+  // Should not crash
   service()->QueueSignedExchangeReport(MakeSignedExchangeReportDetails(
       false, "sxg.failed", kUrl_, kInnerUrl_, kCertUrl_, kServerIP_));
 }
@@ -948,12 +984,19 @@
 TEST_P(NetworkErrorLoggingServiceTest, NoPolicyForOrigin_SignedExchange) {
   service()->QueueSignedExchangeReport(MakeSignedExchangeReportDetails(
       false, "sxg.failed", kUrl_, kInnerUrl_, kCertUrl_, kServerIP_));
+
+  // Make the rest of the test run synchronously.
+  FinishLoading(true /* load_success */);
+
   EXPECT_TRUE(reports().empty());
 }
 
 TEST_P(NetworkErrorLoggingServiceTest, SuccessFraction0_SignedExchange) {
   service()->OnHeader(kOrigin_, kServerIP_, kHeaderSuccessFraction0_);
 
+  // Make the rest of the test run synchronously.
+  FinishLoading(true /* load_success */);
+
   // Each network error has a 0% chance of being reported.  Fire off several and
   // verify that no reports are produced.
   constexpr size_t kReportCount = 100;
@@ -967,6 +1010,10 @@
 
 TEST_P(NetworkErrorLoggingServiceTest, SuccessReportQueued_SignedExchange) {
   service()->OnHeader(kOrigin_, kServerIP_, kHeaderSuccessFraction1_);
+
+  // Make the rest of the test run synchronously.
+  FinishLoading(true /* load_success */);
+
   service()->QueueSignedExchangeReport(MakeSignedExchangeReportDetails(
       true, "ok", kUrl_, kInnerUrl_, kCertUrl_, kServerIP_));
   ASSERT_EQ(1u, reports().size());
@@ -1013,6 +1060,10 @@
 
 TEST_P(NetworkErrorLoggingServiceTest, FailureReportQueued_SignedExchange) {
   service()->OnHeader(kOrigin_, kServerIP_, kHeader_);
+
+  // Make the rest of the test run synchronously.
+  FinishLoading(true /* load_success */);
+
   service()->QueueSignedExchangeReport(MakeSignedExchangeReportDetails(
       false, "sxg.failed", kUrl_, kInnerUrl_, kCertUrl_, kServerIP_));
   ASSERT_EQ(1u, reports().size());
@@ -1059,6 +1110,10 @@
 
 TEST_P(NetworkErrorLoggingServiceTest, MismatchingSubdomain_SignedExchange) {
   service()->OnHeader(kOrigin_, kServerIP_, kHeaderIncludeSubdomains_);
+
+  // Make the rest of the test run synchronously.
+  FinishLoading(true /* load_success */);
+
   service()->QueueSignedExchangeReport(MakeSignedExchangeReportDetails(
       false, "sxg.failed", kUrlSubdomain_, kInnerUrl_, kCertUrl_, kServerIP_));
   EXPECT_TRUE(reports().empty());
@@ -1066,6 +1121,10 @@
 
 TEST_P(NetworkErrorLoggingServiceTest, MismatchingIPAddress_SignedExchange) {
   service()->OnHeader(kOrigin_, kServerIP_, kHeader_);
+
+  // Make the rest of the test run synchronously.
+  FinishLoading(true /* load_success */);
+
   service()->QueueSignedExchangeReport(MakeSignedExchangeReportDetails(
       false, "sxg.failed", kUrl_, kInnerUrl_, kCertUrl_, kOtherServerIP_));
   EXPECT_TRUE(reports().empty());
@@ -1081,6 +1140,9 @@
   for (size_t i = 0; i < 100; ++i) {
     service()->OnHeader(MakeOrigin(i), kServerIP_, kHeader_);
   }
+  // Make the rest of the test run synchronously.
+  FinishLoading(true /* load_success */);
+
   EXPECT_EQ(100u, PolicyCount());
   clock.Advance(base::TimeDelta::FromSeconds(86401));  // max_age is 86400 sec
   // Expired policies are allowed to linger before hitting the policy limit.
@@ -1106,6 +1168,8 @@
     service()->OnHeader(MakeOrigin(i), kServerIP_, kHeader_);
     clock.Advance(base::TimeDelta::FromSeconds(1));
   }
+  // Make the rest of the test run synchronously.
+  FinishLoading(true /* load_success */);
 
   EXPECT_EQ(PolicyCount(), NetworkErrorLoggingService::kMaxPolicies);
 
@@ -1152,6 +1216,315 @@
   // this test.
 }
 
+TEST_P(NetworkErrorLoggingServiceTest, SendsCommandsToStoreSynchronous) {
+  if (!store())
+    return;
+
+  MockPersistentNELStore::CommandList expected_commands;
+  NetworkErrorLoggingService::NELPolicy policy1 = MakePolicyForOrigin(kOrigin_);
+  NetworkErrorLoggingService::NELPolicy policy2 =
+      MakePolicyForOrigin(kOriginDifferentHost_);
+  std::vector<NetworkErrorLoggingService::NELPolicy> prestored_policies = {
+      policy1, policy2};
+  store()->SetPrestoredPolicies(std::move(prestored_policies));
+
+  // The first call to any of the public methods triggers a load.
+  service()->OnHeader(kOrigin_, kServerIP_, kHeader_);
+  expected_commands.emplace_back(
+      MockPersistentNELStore::Command::Type::LOAD_NEL_POLICIES);
+  EXPECT_TRUE(store()->VerifyCommands(expected_commands));
+
+  // Make the rest of the test run synchronously.
+  FinishLoading(true /* load_success */);
+  // DoOnHeader() should now execute.
+  expected_commands.emplace_back(
+      MockPersistentNELStore::Command::Type::DELETE_NEL_POLICY, policy1);
+  expected_commands.emplace_back(
+      MockPersistentNELStore::Command::Type::ADD_NEL_POLICY, policy1);
+  EXPECT_TRUE(store()->VerifyCommands(expected_commands));
+
+  service()->OnRequest(
+      MakeRequestDetails(kOrigin_.GetURL(), ERR_CONNECTION_REFUSED));
+  expected_commands.emplace_back(
+      MockPersistentNELStore::Command::Type::UPDATE_NEL_POLICY, policy1);
+  EXPECT_TRUE(store()->VerifyCommands(expected_commands));
+
+  service()->QueueSignedExchangeReport(MakeSignedExchangeReportDetails(
+      false, "sxg.failed", kUrl_, kInnerUrl_, kCertUrl_, kServerIP_));
+  expected_commands.emplace_back(
+      MockPersistentNELStore::Command::Type::UPDATE_NEL_POLICY, policy1);
+  EXPECT_TRUE(store()->VerifyCommands(expected_commands));
+
+  // Removes policy1 but not policy2.
+  EXPECT_EQ(2, store()->StoredPoliciesCount());
+  service()->RemoveBrowsingData(
+      base::BindRepeating([](const GURL& origin) -> bool {
+        return origin.host() == "example.com";
+      }));
+  expected_commands.emplace_back(
+      MockPersistentNELStore::Command::Type::DELETE_NEL_POLICY, policy1);
+  expected_commands.emplace_back(MockPersistentNELStore::Command::Type::FLUSH);
+  EXPECT_EQ(1, store()->StoredPoliciesCount());
+  EXPECT_TRUE(store()->VerifyCommands(expected_commands));
+
+  service()->RemoveAllBrowsingData();
+  expected_commands.emplace_back(
+      MockPersistentNELStore::Command::Type::DELETE_NEL_POLICY, policy2);
+  expected_commands.emplace_back(MockPersistentNELStore::Command::Type::FLUSH);
+  EXPECT_EQ(0, store()->StoredPoliciesCount());
+  EXPECT_TRUE(store()->VerifyCommands(expected_commands));
+}
+
+// Same as the above test, except that all the tasks are queued until loading
+// is complete.
+TEST_P(NetworkErrorLoggingServiceTest, SendsCommandsToStoreDeferred) {
+  if (!store())
+    return;
+
+  MockPersistentNELStore::CommandList expected_commands;
+  NetworkErrorLoggingService::NELPolicy policy1 = MakePolicyForOrigin(kOrigin_);
+  NetworkErrorLoggingService::NELPolicy policy2 =
+      MakePolicyForOrigin(kOriginDifferentHost_);
+  std::vector<NetworkErrorLoggingService::NELPolicy> prestored_policies = {
+      policy1, policy2};
+  store()->SetPrestoredPolicies(std::move(prestored_policies));
+
+  // The first call to any of the public methods triggers a load.
+  service()->OnHeader(kOrigin_, kServerIP_, kHeader_);
+  expected_commands.emplace_back(
+      MockPersistentNELStore::Command::Type::LOAD_NEL_POLICIES);
+  EXPECT_TRUE(store()->VerifyCommands(expected_commands));
+
+  service()->OnRequest(
+      MakeRequestDetails(kOrigin_.GetURL(), ERR_CONNECTION_REFUSED));
+  EXPECT_TRUE(store()->VerifyCommands(expected_commands));
+
+  service()->QueueSignedExchangeReport(MakeSignedExchangeReportDetails(
+      false, "sxg.failed", kUrl_, kInnerUrl_, kCertUrl_, kServerIP_));
+  EXPECT_TRUE(store()->VerifyCommands(expected_commands));
+
+  // Removes policy1 but not policy2.
+  service()->RemoveBrowsingData(
+      base::BindRepeating([](const GURL& origin) -> bool {
+        return origin.host() == "example.com";
+      }));
+  EXPECT_TRUE(store()->VerifyCommands(expected_commands));
+
+  service()->RemoveAllBrowsingData();
+  EXPECT_TRUE(store()->VerifyCommands(expected_commands));
+
+  // The store has not yet been told to remove the policies because the tasks
+  // to remove browsing data were queued pending initialization.
+  EXPECT_EQ(2, store()->StoredPoliciesCount());
+
+  FinishLoading(true /* load_success */);
+  // DoOnHeader()
+  expected_commands.emplace_back(
+      MockPersistentNELStore::Command::Type::DELETE_NEL_POLICY, policy1);
+  expected_commands.emplace_back(
+      MockPersistentNELStore::Command::Type::ADD_NEL_POLICY, policy1);
+  // DoOnRequest()
+  expected_commands.emplace_back(
+      MockPersistentNELStore::Command::Type::UPDATE_NEL_POLICY, policy1);
+  // DoQueueSignedExchangeReport()
+  expected_commands.emplace_back(
+      MockPersistentNELStore::Command::Type::UPDATE_NEL_POLICY, policy1);
+  // DoRemoveBrowsingData()
+  expected_commands.emplace_back(
+      MockPersistentNELStore::Command::Type::DELETE_NEL_POLICY, policy1);
+  expected_commands.emplace_back(MockPersistentNELStore::Command::Type::FLUSH);
+  // DoRemoveAllBrowsingData()
+  expected_commands.emplace_back(
+      MockPersistentNELStore::Command::Type::DELETE_NEL_POLICY, policy2);
+  expected_commands.emplace_back(MockPersistentNELStore::Command::Type::FLUSH);
+  EXPECT_TRUE(store()->VerifyCommands(expected_commands));
+}
+
+// These two tests check that if loading fails, the commands should still
+// be sent to the store; the actual store impl will just ignore them.
+TEST_P(NetworkErrorLoggingServiceTest,
+       SendsCommandsToStoreSynchronousLoadFailed) {
+  if (!store())
+    return;
+
+  MockPersistentNELStore::CommandList expected_commands;
+  NetworkErrorLoggingService::NELPolicy policy1 = MakePolicyForOrigin(kOrigin_);
+  NetworkErrorLoggingService::NELPolicy policy2 =
+      MakePolicyForOrigin(kOriginDifferentHost_);
+  std::vector<NetworkErrorLoggingService::NELPolicy> prestored_policies = {
+      policy1, policy2};
+  store()->SetPrestoredPolicies(std::move(prestored_policies));
+
+  // The first call to any of the public methods triggers a load.
+  service()->OnHeader(kOrigin_, kServerIP_, kHeader_);
+  expected_commands.emplace_back(
+      MockPersistentNELStore::Command::Type::LOAD_NEL_POLICIES);
+  EXPECT_TRUE(store()->VerifyCommands(expected_commands));
+
+  // Make the rest of the test run synchronously.
+  FinishLoading(false /* load_success */);
+  // DoOnHeader() should now execute.
+  // Because the load failed, there will be no policies in memory, so the store
+  // is not told to delete anything.
+  expected_commands.emplace_back(
+      MockPersistentNELStore::Command::Type::ADD_NEL_POLICY, policy1);
+  EXPECT_TRUE(store()->VerifyCommands(expected_commands));
+  LOG(INFO) << store()->GetDebugString();
+
+  service()->OnRequest(
+      MakeRequestDetails(kOrigin_.GetURL(), ERR_CONNECTION_REFUSED));
+  expected_commands.emplace_back(
+      MockPersistentNELStore::Command::Type::UPDATE_NEL_POLICY, policy1);
+  EXPECT_TRUE(store()->VerifyCommands(expected_commands));
+
+  service()->QueueSignedExchangeReport(MakeSignedExchangeReportDetails(
+      false, "sxg.failed", kUrl_, kInnerUrl_, kCertUrl_, kServerIP_));
+  expected_commands.emplace_back(
+      MockPersistentNELStore::Command::Type::UPDATE_NEL_POLICY, policy1);
+  EXPECT_TRUE(store()->VerifyCommands(expected_commands));
+
+  // Removes policy1 but not policy2.
+  service()->RemoveBrowsingData(
+      base::BindRepeating([](const GURL& origin) -> bool {
+        return origin.host() == "example.com";
+      }));
+  expected_commands.emplace_back(
+      MockPersistentNELStore::Command::Type::DELETE_NEL_POLICY, policy1);
+  expected_commands.emplace_back(MockPersistentNELStore::Command::Type::FLUSH);
+  EXPECT_TRUE(store()->VerifyCommands(expected_commands));
+
+  service()->RemoveAllBrowsingData();
+  // We failed to load policy2 from the store, so there is nothing to remove
+  // here.
+  expected_commands.emplace_back(MockPersistentNELStore::Command::Type::FLUSH);
+  EXPECT_TRUE(store()->VerifyCommands(expected_commands));
+}
+
+TEST_P(NetworkErrorLoggingServiceTest, SendsCommandsToStoreDeferredLoadFailed) {
+  if (!store())
+    return;
+
+  MockPersistentNELStore::CommandList expected_commands;
+  NetworkErrorLoggingService::NELPolicy policy1 = MakePolicyForOrigin(kOrigin_);
+  NetworkErrorLoggingService::NELPolicy policy2 =
+      MakePolicyForOrigin(kOriginDifferentHost_);
+  std::vector<NetworkErrorLoggingService::NELPolicy> prestored_policies = {
+      policy1, policy2};
+  store()->SetPrestoredPolicies(std::move(prestored_policies));
+
+  // The first call to any of the public methods triggers a load.
+  service()->OnHeader(kOrigin_, kServerIP_, kHeader_);
+  expected_commands.emplace_back(
+      MockPersistentNELStore::Command::Type::LOAD_NEL_POLICIES);
+  EXPECT_TRUE(store()->VerifyCommands(expected_commands));
+
+  service()->OnRequest(
+      MakeRequestDetails(kOrigin_.GetURL(), ERR_CONNECTION_REFUSED));
+  EXPECT_TRUE(store()->VerifyCommands(expected_commands));
+
+  service()->QueueSignedExchangeReport(MakeSignedExchangeReportDetails(
+      false, "sxg.failed", kUrl_, kInnerUrl_, kCertUrl_, kServerIP_));
+  EXPECT_TRUE(store()->VerifyCommands(expected_commands));
+
+  // Removes policy1 but not policy2.
+  service()->RemoveBrowsingData(
+      base::BindRepeating([](const GURL& origin) -> bool {
+        return origin.host() == "example.com";
+      }));
+  EXPECT_TRUE(store()->VerifyCommands(expected_commands));
+
+  service()->RemoveAllBrowsingData();
+  EXPECT_TRUE(store()->VerifyCommands(expected_commands));
+
+  FinishLoading(false /* load_success */);
+  // DoOnHeader()
+  // Because the load failed, there will be no policies in memory, so the store
+  // is not told to delete anything.
+  expected_commands.emplace_back(
+      MockPersistentNELStore::Command::Type::ADD_NEL_POLICY, policy1);
+  // DoOnRequest()
+  expected_commands.emplace_back(
+      MockPersistentNELStore::Command::Type::UPDATE_NEL_POLICY, policy1);
+  // DoQueueSignedExchangeReport()
+  expected_commands.emplace_back(
+      MockPersistentNELStore::Command::Type::UPDATE_NEL_POLICY, policy1);
+  // DoRemoveBrowsingData()
+  expected_commands.emplace_back(
+      MockPersistentNELStore::Command::Type::DELETE_NEL_POLICY, policy1);
+  expected_commands.emplace_back(MockPersistentNELStore::Command::Type::FLUSH);
+  // DoRemoveAllBrowsingData()
+  // We failed to load policy2 from the store, so there is nothing to remove
+  // here.
+  expected_commands.emplace_back(MockPersistentNELStore::Command::Type::FLUSH);
+  EXPECT_TRUE(store()->VerifyCommands(expected_commands));
+}
+
+TEST_P(NetworkErrorLoggingServiceTest, FlushesStoreOnDestruction) {
+  auto store = std::make_unique<MockPersistentNELStore>();
+  std::unique_ptr<NetworkErrorLoggingService> service =
+      NetworkErrorLoggingService::Create(store.get());
+
+  MockPersistentNELStore::CommandList expected_commands;
+
+  service->OnHeader(kOrigin_, kServerIP_, kHeader_);
+  expected_commands.emplace_back(
+      MockPersistentNELStore::Command::Type::LOAD_NEL_POLICIES);
+  EXPECT_TRUE(store->VerifyCommands(expected_commands));
+
+  store->FinishLoading(false /* load_success */);
+  expected_commands.emplace_back(
+      MockPersistentNELStore::Command::Type::ADD_NEL_POLICY,
+      MakePolicyForOrigin(kOrigin_));
+  EXPECT_TRUE(store->VerifyCommands(expected_commands));
+
+  // Store should be flushed on destruction of service.
+  service.reset();
+  expected_commands.emplace_back(MockPersistentNELStore::Command::Type::FLUSH);
+  EXPECT_TRUE(store->VerifyCommands(expected_commands));
+}
+
+TEST_P(NetworkErrorLoggingServiceTest,
+       DoesntFlushStoreOnDestructionBeforeLoad) {
+  auto store = std::make_unique<MockPersistentNELStore>();
+  std::unique_ptr<NetworkErrorLoggingService> service =
+      NetworkErrorLoggingService::Create(store.get());
+
+  service.reset();
+  EXPECT_EQ(0u, store->GetAllCommands().size());
+}
+
+TEST_P(NetworkErrorLoggingServiceTest, DoNothingIfShutDown) {
+  if (!store())
+    return;
+
+  MockPersistentNELStore::CommandList expected_commands;
+
+  // The first call to any of the public methods triggers a load.
+  service()->OnHeader(kOrigin_, kServerIP_, kHeader_);
+  expected_commands.emplace_back(
+      MockPersistentNELStore::Command::Type::LOAD_NEL_POLICIES);
+  EXPECT_TRUE(store()->VerifyCommands(expected_commands));
+
+  service()->OnRequest(
+      MakeRequestDetails(kOrigin_.GetURL(), ERR_CONNECTION_REFUSED));
+  service()->QueueSignedExchangeReport(MakeSignedExchangeReportDetails(
+      false, "sxg.failed", kUrl_, kInnerUrl_, kCertUrl_, kServerIP_));
+  service()->RemoveBrowsingData(
+      base::BindRepeating([](const GURL& origin) -> bool {
+        return origin.host() == "example.com";
+      }));
+  service()->RemoveAllBrowsingData();
+
+  // Finish loading after the service has been shut down.
+  service()->OnShutdown();
+  FinishLoading(true /* load_success */);
+
+  // Only the LOAD command should have been sent to the store.
+  EXPECT_EQ(1u, store()->GetAllCommands().size());
+  EXPECT_EQ(0u, PolicyCount());
+  EXPECT_EQ(0u, reports().size());
+}
+
 INSTANTIATE_TEST_SUITE_P(NetworkErrorLoggingServiceStoreTest,
                          NetworkErrorLoggingServiceTest,
                          testing::Bool());
diff --git a/net/network_error_logging/network_error_logging_test_util.cc b/net/network_error_logging/network_error_logging_test_util.cc
index 54d6376..a75a06d8 100644
--- a/net/network_error_logging/network_error_logging_test_util.cc
+++ b/net/network_error_logging/network_error_logging_test_util.cc
@@ -33,7 +33,7 @@
 }
 
 void TestNetworkErrorLoggingService::QueueSignedExchangeReport(
-    const SignedExchangeReportDetails& details) {}
+    SignedExchangeReportDetails details) {}
 
 void TestNetworkErrorLoggingService::RemoveBrowsingData(
     const base::RepeatingCallback<bool(const GURL&)>& origin_filter) {}
diff --git a/net/network_error_logging/network_error_logging_test_util.h b/net/network_error_logging/network_error_logging_test_util.h
index 3eb29c3..c6af941 100644
--- a/net/network_error_logging/network_error_logging_test_util.h
+++ b/net/network_error_logging/network_error_logging_test_util.h
@@ -48,8 +48,7 @@
                 const IPAddress& received_ip_address,
                 const std::string& value) override;
   void OnRequest(RequestDetails details) override;
-  void QueueSignedExchangeReport(
-      const SignedExchangeReportDetails& details) override;
+  void QueueSignedExchangeReport(SignedExchangeReportDetails details) override;
   void RemoveBrowsingData(
       const base::RepeatingCallback<bool(const GURL&)>& origin_filter) override;
   void RemoveAllBrowsingData() override;
diff --git a/net/quic/platform/impl/quic_epoll_clock.cc b/net/quic/platform/impl/quic_epoll_clock.cc
index dfba155..7a84a68 100644
--- a/net/quic/platform/impl/quic_epoll_clock.cc
+++ b/net/quic/platform/impl/quic_epoll_clock.cc
@@ -4,13 +4,13 @@
 
 #include "net/quic/platform/impl/quic_epoll_clock.h"
 
+#include "net/third_party/quiche/src/epoll_server/simple_epoll_server.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
-#include "net/tools/epoll_server/epoll_server.h"
 
 namespace quic {
 
-QuicEpollClock::QuicEpollClock(net::EpollServer* epoll_server)
+QuicEpollClock::QuicEpollClock(epoll_server::SimpleEpollServer* epoll_server)
     : epoll_server_(epoll_server), largest_time_(QuicTime::Zero()) {}
 
 QuicEpollClock::~QuicEpollClock() {}
diff --git a/net/quic/platform/impl/quic_epoll_clock.h b/net/quic/platform/impl/quic_epoll_clock.h
index f20df24..cece73f 100644
--- a/net/quic/platform/impl/quic_epoll_clock.h
+++ b/net/quic/platform/impl/quic_epoll_clock.h
@@ -10,17 +10,19 @@
 #include "net/third_party/quiche/src/quic/core/quic_time.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_clock.h"
 
-namespace quic {}  // namespace quic
-namespace net {
-class EpollServer;
-}  // namespace net
+namespace epoll_server {
+
+class SimpleEpollServer;
+
+}  // namespace epoll_server
+
 namespace quic {
 
 // Clock to efficiently retrieve an approximately accurate time from an
 // net::EpollServer.
 class QuicEpollClock : public QuicClock {
  public:
-  explicit QuicEpollClock(net::EpollServer* epoll_server);
+  explicit QuicEpollClock(epoll_server::SimpleEpollServer* epoll_server);
   ~QuicEpollClock() override;
 
   // Returns the approximate current time as a QuicTime object.
@@ -40,7 +42,7 @@
       const QuicWallTime& walltime) const override;
 
  protected:
-  net::EpollServer* epoll_server_;
+  epoll_server::SimpleEpollServer* epoll_server_;
   // Largest time returned from Now() so far.
   mutable QuicTime largest_time_;
 
diff --git a/net/quic/platform/impl/quic_epoll_clock_test.cc b/net/quic/platform/impl/quic_epoll_clock_test.cc
index 75caf111..d641f54 100644
--- a/net/quic/platform/impl/quic_epoll_clock_test.cc
+++ b/net/quic/platform/impl/quic_epoll_clock_test.cc
@@ -4,9 +4,9 @@
 
 #include "net/quic/platform/impl/quic_epoll_clock.h"
 
+#include "net/third_party/quiche/src/epoll_server/fake_simple_epoll_server.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
-#include "net/tools/epoll_server/fake_epoll_server.h"
 
 namespace quic {
 namespace test {
@@ -14,7 +14,7 @@
 class QuicEpollClockTest : public QuicTest {};
 
 TEST_F(QuicEpollClockTest, ApproximateNowInUsec) {
-  FakeEpollServer epoll_server;
+  epoll_server::test::FakeSimpleEpollServer epoll_server;
   QuicEpollClock clock(&epoll_server);
 
   epoll_server.set_now_in_usec(1000000);
@@ -35,7 +35,7 @@
 }
 
 TEST_F(QuicEpollClockTest, NowInUsec) {
-  FakeEpollServer epoll_server;
+  epoll_server::test::FakeSimpleEpollServer epoll_server;
   QuicEpollClock clock(&epoll_server);
 
   epoll_server.set_now_in_usec(1000000);
@@ -47,7 +47,7 @@
 
 TEST_F(QuicEpollClockTest, MonotonicityWithRealEpollClock) {
   SetQuicReloadableFlag(quic_monotonic_epoll_clock, true);
-  net::EpollServer epoll_server;
+  epoll_server::SimpleEpollServer epoll_server;
   QuicEpollClock clock(&epoll_server);
 
   quic::QuicTime last_now = clock.Now();
@@ -61,7 +61,7 @@
 }
 
 TEST_F(QuicEpollClockTest, MonotonicityWithFakeEpollClock) {
-  FakeEpollServer epoll_server;
+  epoll_server::test::FakeSimpleEpollServer epoll_server;
   QuicEpollClock clock(&epoll_server);
 
   epoll_server.set_now_in_usec(100);
diff --git a/net/quic/platform/impl/quic_epoll_impl.h b/net/quic/platform/impl/quic_epoll_impl.h
index 94a5b0b0..f53ef8b 100644
--- a/net/quic/platform/impl/quic_epoll_impl.h
+++ b/net/quic/platform/impl/quic_epoll_impl.h
@@ -10,14 +10,14 @@
 #ifndef NET_QUIC_PLATFORM_IMPL_QUIC_EPOLL_IMPL_H_
 #define NET_QUIC_PLATFORM_IMPL_QUIC_EPOLL_IMPL_H_
 
-#include "net/tools/epoll_server/epoll_server.h"
+#include "net/third_party/quiche/src/epoll_server/simple_epoll_server.h"
 
 namespace quic {
 
-using QuicEpollServerImpl = ::net::EpollServer;
-using QuicEpollEventImpl = ::net::EpollEvent;
-using QuicEpollAlarmBaseImpl = ::net::EpollAlarm;
-using QuicEpollCallbackInterfaceImpl = ::net::EpollCallbackInterface;
+using QuicEpollServerImpl = epoll_server::SimpleEpollServer;
+using QuicEpollEventImpl = epoll_server::EpollEvent;
+using QuicEpollAlarmBaseImpl = epoll_server::EpollAlarm;
+using QuicEpollCallbackInterfaceImpl = epoll_server::EpollCallbackInterface;
 
 }  // namespace quic
 
diff --git a/net/quic/platform/impl/quic_epoll_test_tools_impl.h b/net/quic/platform/impl/quic_epoll_test_tools_impl.h
index ed7443c0..1cde56a 100644
--- a/net/quic/platform/impl/quic_epoll_test_tools_impl.h
+++ b/net/quic/platform/impl/quic_epoll_test_tools_impl.h
@@ -5,8 +5,8 @@
 #ifndef NET_QUIC_PLATFORM_IMPL_QUIC_EPOLL_TEST_TOOLS_IMPL_H_
 #define NET_QUIC_PLATFORM_IMPL_QUIC_EPOLL_TEST_TOOLS_IMPL_H_
 
-#include "net/tools/epoll_server/fake_epoll_server.h"
+#include "net/third_party/quiche/src/epoll_server/fake_simple_epoll_server.h"
 
-using QuicFakeEpollServerImpl = quic::test::FakeEpollServer;
+using QuicFakeEpollServerImpl = epoll_server::test::FakeSimpleEpollServer;
 
 #endif  // NET_QUIC_PLATFORM_IMPL_QUIC_EPOLL_TEST_TOOLS_IMPL_H_
diff --git a/net/reporting/reporting_test_util.cc b/net/reporting/reporting_test_util.cc
index c78789f..c9383f9a 100644
--- a/net/reporting/reporting_test_util.cc
+++ b/net/reporting/reporting_test_util.cc
@@ -283,4 +283,70 @@
   return tick_clock()->NowTicks() + base::TimeDelta::FromDays(1);
 }
 
+TestReportingService::Report::Report() = default;
+
+TestReportingService::Report::Report(Report&& other)
+    : url(other.url),
+      user_agent(other.user_agent),
+      group(other.group),
+      type(other.type),
+      body(std::move(other.body)),
+      depth(other.depth) {}
+
+TestReportingService::Report::Report(const GURL& url,
+                                     const std::string& user_agent,
+                                     const std::string& group,
+                                     const std::string& type,
+                                     std::unique_ptr<const base::Value> body,
+                                     int depth)
+    : url(url),
+      user_agent(user_agent),
+      group(group),
+      type(type),
+      body(std::move(body)),
+      depth(depth) {}
+
+TestReportingService::Report::~Report() = default;
+
+TestReportingService::TestReportingService() = default;
+
+TestReportingService::~TestReportingService() = default;
+
+void TestReportingService::QueueReport(const GURL& url,
+                                       const std::string& user_agent,
+                                       const std::string& group,
+                                       const std::string& type,
+                                       std::unique_ptr<const base::Value> body,
+                                       int depth) {
+  reports_.push_back(
+      Report(url, user_agent, group, type, std::move(body), depth));
+}
+
+void TestReportingService::ProcessHeader(const GURL& url,
+                                         const std::string& header_value) {
+  NOTREACHED();
+}
+
+void TestReportingService::RemoveBrowsingData(
+    int data_type_mask,
+    const base::RepeatingCallback<bool(const GURL&)>& origin_filter) {
+  NOTREACHED();
+}
+
+void TestReportingService::RemoveAllBrowsingData(int data_type_mask) {
+  NOTREACHED();
+}
+
+void TestReportingService::OnShutdown() {}
+
+const ReportingPolicy& TestReportingService::GetPolicy() const {
+  NOTREACHED();
+  return dummy_policy_;
+}
+
+ReportingContext* TestReportingService::GetContextForTesting() const {
+  NOTREACHED();
+  return nullptr;
+}
+
 }  // namespace net
diff --git a/net/reporting/reporting_test_util.h b/net/reporting/reporting_test_util.h
index b6a558c..f290c3de 100644
--- a/net/reporting/reporting_test_util.h
+++ b/net/reporting/reporting_test_util.h
@@ -16,8 +16,10 @@
 #include "net/reporting/reporting_cache.h"
 #include "net/reporting/reporting_context.h"
 #include "net/reporting/reporting_delegate.h"
+#include "net/reporting/reporting_service.h"
 #include "net/reporting/reporting_uploader.h"
 #include "net/test/test_with_scoped_task_environment.h"
+#include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
 
@@ -38,6 +40,15 @@
 class ReportingGarbageCollector;
 class TestURLRequestContext;
 
+// A matcher for ReportingReports, which checks that the url of the report is
+// the given url.
+// Usage: EXPECT_THAT(report, ReportUrlIs(url));
+// EXPECT_THAT(reports(),
+//             testing::ElementsAre(ReportUrlIs(url1), ReportUrlIs(url2)));
+MATCHER_P(ReportUrlIs, url, "") {
+  return arg.url == url;
+}
+
 // A test implementation of ReportingUploader that holds uploads for tests to
 // examine and complete with a specified outcome.
 class TestReportingUploader : public ReportingUploader {
@@ -267,6 +278,69 @@
   DISALLOW_COPY_AND_ASSIGN(ReportingTestBase);
 };
 
+class TestReportingService : public ReportingService {
+ public:
+  struct Report {
+    Report();
+
+    Report(Report&& other);
+
+    Report(const GURL& url,
+           const std::string& user_agent,
+           const std::string& group,
+           const std::string& type,
+           std::unique_ptr<const base::Value> body,
+           int depth);
+
+    ~Report();
+
+    GURL url;
+    std::string user_agent;
+    std::string group;
+    std::string type;
+    std::unique_ptr<const base::Value> body;
+    int depth;
+
+   private:
+    DISALLOW_COPY(Report);
+  };
+
+  TestReportingService();
+
+  const std::vector<Report>& reports() const { return reports_; }
+
+  // ReportingService implementation:
+
+  ~TestReportingService() override;
+
+  void QueueReport(const GURL& url,
+                   const std::string& user_agent,
+                   const std::string& group,
+                   const std::string& type,
+                   std::unique_ptr<const base::Value> body,
+                   int depth) override;
+
+  void ProcessHeader(const GURL& url, const std::string& header_value) override;
+
+  void RemoveBrowsingData(
+      int data_type_mask,
+      const base::RepeatingCallback<bool(const GURL&)>& origin_filter) override;
+
+  void RemoveAllBrowsingData(int data_type_mask) override;
+
+  void OnShutdown() override;
+
+  const ReportingPolicy& GetPolicy() const override;
+
+  ReportingContext* GetContextForTesting() const override;
+
+ private:
+  std::vector<Report> reports_;
+  ReportingPolicy dummy_policy_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestReportingService);
+};
+
 }  // namespace net
 
 #endif  // NET_REPORTING_REPORTING_TEST_UTIL_H_
diff --git a/net/tools/epoll_server/epoll_server.cc b/net/tools/epoll_server/epoll_server.cc
deleted file mode 100644
index a686dd7d..0000000
--- a/net/tools/epoll_server/epoll_server.cc
+++ /dev/null
@@ -1,809 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "net/tools/epoll_server/epoll_server.h"
-
-#include <unistd.h>  // For read, pipe, close and write.
-#include <stdlib.h>  // for abort
-#include <errno.h>    // for errno and strerror_r
-#include <algorithm>
-#include <utility>
-
-#include "base/auto_reset.h"
-#include "base/logging.h"
-#include "base/stl_util.h"
-#include "base/time/time.h"
-
-// Design notes: An efficient implementation of ready list has the following
-// desirable properties:
-//
-// A. O(1) insertion into/removal from the list in any location.
-// B. Once the callback is found by hash lookup using the fd, the lookup of
-//    corresponding entry in the list is O(1).
-// C. Safe insertion into/removal from the list during list iteration. (The
-//    ready list's purpose is to enable completely event driven I/O model.
-//    Thus, all the interesting bits happen in the callback. It is critical
-//    to not place any restriction on the API during list iteration.
-//
-// The current implementation achieves these goals with the following design:
-//
-// - The ready list is constructed as a doubly linked list to enable O(1)
-//   insertion/removal (see man 3 queue).
-// - The forward and backward links are directly embedded inside the
-//   CBAndEventMask struct. This enables O(1) lookup in the list for a given
-//   callback. (Techincally, we could've used std::list of hash_set::iterator,
-//   and keep a list::iterator in CBAndEventMask to achieve the same effect.
-//   However, iterators have two problems: no way to portably invalidate them,
-//   and no way to tell whether an iterator is singular or not. The only way to
-//   overcome these issues is to keep bools in both places, but that throws off
-//   memory alignment (up to 7 wasted bytes for each bool). The extra level of
-//   indirection will also likely be less cache friendly. Direct manipulation
-//   of link pointers makes it easier to retrieve the CBAndEventMask from the
-//   list, easier to check whether an CBAndEventMask is in the list, uses less
-//   memory (save 32 bytes/fd), and does not affect cache usage (we need to
-//   read in the struct to use the callback anyway).)
-// - Embed the fd directly into CBAndEventMask and switch to using hash_set.
-//   This removes the need to store hash_map::iterator in the list just so that
-//   we can get both the fd and the callback.
-// - The ready list is "one shot": each entry is removed before OnEvent is
-//   called. This removes the mutation-while-iterating problem.
-// - Use two lists to keep track of callbacks. The ready_list_ is the one used
-//   for registration. Before iteration, the ready_list_ is swapped into the
-//   tmp_list_. Once iteration is done, tmp_list_ will be empty, and
-//   ready_list_ will have all the new ready fds.
-
-// The size we use for buffers passed to strerror_r
-static const int kErrorBufferSize = 256;
-
-namespace net {
-
-// Clears the pipe and returns.  Used for waking the epoll server up.
-class ReadPipeCallback : public EpollCallbackInterface {
- public:
-  void OnEvent(int fd, EpollEvent* event) override {
-    DCHECK(event->in_events == EPOLLIN);
-    int data;
-    int data_read = 1;
-    // Read until the pipe is empty.
-    while (data_read > 0) {
-      data_read = read(fd, &data, sizeof(data));
-    }
-  }
-  void OnShutdown(EpollServer* eps, int fd) override {}
-  void OnRegistration(EpollServer*, int, int) override {}
-  void OnModification(int, int) override {}     // COV_NF_LINE
-  void OnUnregistration(int, bool) override {}  // COV_NF_LINE
-  std::string Name() const override { return "ReadPipeCallback"; }
-};
-
-////////////////////////////////////////////////////////////////////////////////
-////////////////////////////////////////////////////////////////////////////////
-
-EpollServer::EpollServer()
-  : epoll_fd_(epoll_create(1024)),
-    timeout_in_us_(0),
-    recorded_now_in_us_(0),
-    ready_list_size_(0),
-    wake_cb_(new ReadPipeCallback),
-    read_fd_(-1),
-    write_fd_(-1),
-    in_wait_for_events_and_execute_callbacks_(false),
-    in_shutdown_(false) {
-  // ensure that the epoll_fd_ is valid.
-  CHECK_NE(epoll_fd_, -1);
-  LIST_INIT(&ready_list_);
-  LIST_INIT(&tmp_list_);
-
-  int pipe_fds[2];
-  if (pipe(pipe_fds) < 0) {
-    // Unfortunately, it is impossible to test any such initialization in
-    // a constructor (as virtual methods do not yet work).
-    // This -could- be solved by moving initialization to an outside
-    // call...
-    int saved_errno = errno;
-    char buf[kErrorBufferSize];
-    LOG(FATAL) << "Error " << saved_errno
-               << " in pipe(): " << strerror_r(saved_errno, buf, sizeof(buf));
-  }
-  read_fd_ = pipe_fds[0];
-  write_fd_ = pipe_fds[1];
-  RegisterFD(read_fd_, wake_cb_.get(), EPOLLIN);
-}
-
-void EpollServer::CleanupFDToCBMap() {
-  auto cb_iter = cb_map_.begin();
-  while (cb_iter != cb_map_.end()) {
-    int fd = cb_iter->fd;
-    CB* cb = cb_iter->cb;
-
-    cb_iter->in_use = true;
-    if (cb) {
-      cb->OnShutdown(this, fd);
-    }
-
-    cb_map_.erase(cb_iter);
-    cb_iter = cb_map_.begin();
-  }
-}
-
-void EpollServer::CleanupTimeToAlarmCBMap() {
-  TimeToAlarmCBMap::iterator erase_it;
-
-  // Call OnShutdown() on alarms. Note that the structure of the loop
-  // is similar to the structure of loop in the function HandleAlarms()
-  for (auto i = alarm_map_.begin(); i != alarm_map_.end();) {
-    // Note that OnShutdown() can call UnregisterAlarm() on
-    // other iterators. OnShutdown() should not call UnregisterAlarm()
-    // on self because by definition the iterator is not valid any more.
-    i->second->OnShutdown(this);
-    erase_it = i;
-    ++i;
-    alarm_map_.erase(erase_it);
-  }
-}
-
-EpollServer::~EpollServer() {
-  DCHECK_EQ(in_shutdown_, false);
-  in_shutdown_ = true;
-#ifdef EPOLL_SERVER_EVENT_TRACING
-  LOG(INFO) << "\n" << event_recorder_;
-#endif
-  VLOG(2) << "Shutting down epoll server ";
-  CleanupFDToCBMap();
-
-  LIST_INIT(&ready_list_);
-  LIST_INIT(&tmp_list_);
-
-  CleanupTimeToAlarmCBMap();
-
-  close(read_fd_);
-  close(write_fd_);
-  close(epoll_fd_);
-}
-
-// Whether a CBAandEventMask is on the ready list is determined by a non-NULL
-// le_prev pointer (le_next being NULL indicates end of list).
-inline void EpollServer::AddToReadyList(CBAndEventMask* cb_and_mask) {
-  if (cb_and_mask->entry.le_prev == NULL) {
-    LIST_INSERT_HEAD(&ready_list_, cb_and_mask, entry);
-    ++ready_list_size_;
-  }
-}
-
-inline void EpollServer::RemoveFromReadyList(
-    const CBAndEventMask& cb_and_mask) {
-  if (cb_and_mask.entry.le_prev != NULL) {
-    LIST_REMOVE(&cb_and_mask, entry);
-    // Clean up all the ready list states. Don't bother with the other fields
-    // as they are initialized when the CBAandEventMask is added to the ready
-    // list. This saves a few cycles in the inner loop.
-    cb_and_mask.entry.le_prev = NULL;
-    --ready_list_size_;
-    if (ready_list_size_ == 0) {
-      DCHECK(ready_list_.lh_first == NULL);
-      DCHECK(tmp_list_.lh_first == NULL);
-    }
-  }
-}
-
-void EpollServer::RegisterFD(int fd, CB* cb, int event_mask) {
-  CHECK(cb);
-  VLOG(3) << "RegisterFD fd=" << fd << " event_mask=" << event_mask;
-  auto fd_i = cb_map_.find(CBAndEventMask(NULL, 0, fd));
-  if (cb_map_.end() != fd_i) {
-    // do we just abort, or do we just unregister the other callback?
-    // for now, lets just unregister the other callback.
-
-    // unregister any callback that may already be registered for this FD.
-    CB* other_cb = fd_i->cb;
-    if (other_cb) {
-      // Must remove from the ready list before erasing.
-      RemoveFromReadyList(*fd_i);
-      other_cb->OnUnregistration(fd, true);
-      ModFD(fd, event_mask);
-    } else {
-      // already unregistered, so just recycle the node.
-      AddFD(fd, event_mask);
-    }
-    fd_i->cb = cb;
-    fd_i->event_mask = event_mask;
-    fd_i->events_to_fake = 0;
-  } else {
-    AddFD(fd, event_mask);
-    cb_map_.insert(CBAndEventMask(cb, event_mask, fd));
-  }
-
-
-  // set the FD to be non-blocking.
-  SetNonblocking(fd);
-
-  cb->OnRegistration(this, fd, event_mask);
-}
-
-void EpollServer::SetNonblocking(int fd) {
-  int flags = fcntl(fd, F_GETFL, 0);
-  if (flags == -1) {
-    int saved_errno = errno;
-    char buf[kErrorBufferSize];
-    LOG(FATAL) << "Error " << saved_errno
-               << " doing fcntl(" << fd << ", F_GETFL, 0): "
-               << strerror_r(saved_errno, buf, sizeof(buf));
-  }
-  if (!(flags & O_NONBLOCK)) {
-    int saved_flags = flags;
-    flags = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
-    if (flags == -1) {
-      // bad.
-      int saved_errno = errno;
-      char buf[kErrorBufferSize];
-      LOG(FATAL) << "Error " << saved_errno
-        << " doing fcntl(" << fd << ", F_SETFL, " << saved_flags << "): "
-        << strerror_r(saved_errno, buf, sizeof(buf));
-    }
-  }
-}
-
-int EpollServer::epoll_wait_impl(int epfd,
-                                 struct epoll_event* events,
-                                 int max_events,
-                                 int timeout_in_ms) {
-  return epoll_wait(epfd, events, max_events, timeout_in_ms);
-}
-
-void EpollServer::RegisterFDForWrite(int fd, CB* cb) {
-  RegisterFD(fd, cb, EPOLLOUT);
-}
-
-void EpollServer::RegisterFDForReadWrite(int fd, CB* cb) {
-  RegisterFD(fd, cb, EPOLLIN | EPOLLOUT);
-}
-
-void EpollServer::RegisterFDForRead(int fd, CB* cb) {
-  RegisterFD(fd, cb, EPOLLIN);
-}
-
-void EpollServer::UnregisterFD(int fd) {
-  auto fd_i = cb_map_.find(CBAndEventMask(NULL, 0, fd));
-  if (cb_map_.end() == fd_i || fd_i->cb == NULL) {
-    // Doesn't exist in server, or has gone through UnregisterFD once and still
-    // inside the callchain of OnEvent.
-    return;
-  }
-#ifdef EPOLL_SERVER_EVENT_TRACING
-  event_recorder_.RecordUnregistration(fd);
-#endif
-  CB* cb = fd_i->cb;
-  // Since the links are embedded within the struct, we must remove it from the
-  // list before erasing it from the hash_set.
-  RemoveFromReadyList(*fd_i);
-  DelFD(fd);
-  cb->OnUnregistration(fd, false);
-  // fd_i->cb is NULL if that fd is unregistered inside the callchain of
-  // OnEvent. Since the EpollServer needs a valid CBAndEventMask after OnEvent
-  // returns in order to add it to the ready list, we cannot have UnregisterFD
-  // erase the entry if it is in use. Thus, a NULL fd_i->cb is used as a
-  // condition that tells the EpollServer that this entry is unused at a later
-  // point.
-  if (!fd_i->in_use) {
-    cb_map_.erase(fd_i);
-  } else {
-    // Remove all trace of the registration, and just keep the node alive long
-    // enough so the code that calls OnEvent doesn't have to worry about
-    // figuring out whether the CBAndEventMask is valid or not.
-    fd_i->cb = NULL;
-    fd_i->event_mask = 0;
-    fd_i->events_to_fake = 0;
-  }
-}
-
-void EpollServer::ModifyCallback(int fd, int event_mask) {
-  ModifyFD(fd, ~0, event_mask);
-}
-
-void EpollServer::StopRead(int fd) {
-  ModifyFD(fd, EPOLLIN, 0);
-}
-
-void EpollServer::StartRead(int fd) {
-  ModifyFD(fd, 0, EPOLLIN);
-}
-
-void EpollServer::StopWrite(int fd) {
-  ModifyFD(fd, EPOLLOUT, 0);
-}
-
-void EpollServer::StartWrite(int fd) {
-  ModifyFD(fd, 0, EPOLLOUT);
-}
-
-void EpollServer::HandleEvent(int fd, int event_mask) {
-#ifdef EPOLL_SERVER_EVENT_TRACING
-  event_recorder_.RecordEpollEvent(fd, event_mask);
-#endif
-  auto fd_i = cb_map_.find(CBAndEventMask(NULL, 0, fd));
-  if (fd_i == cb_map_.end() || fd_i->cb == NULL) {
-    // Ignore the event.
-    // This could occur if epoll() returns a set of events, and
-    // while processing event A (earlier) we removed the callback
-    // for event B (and are now processing event B).
-    return;
-  }
-  fd_i->events_asserted = event_mask;
-  CBAndEventMask* cb_and_mask = const_cast<CBAndEventMask*>(&*fd_i);
-  AddToReadyList(cb_and_mask);
-}
-
-void EpollServer::WaitForEventsAndExecuteCallbacks() {
-  if (in_wait_for_events_and_execute_callbacks_) {
-    LOG(DFATAL) <<
-      "Attempting to call WaitForEventsAndExecuteCallbacks"
-      " when an ancestor to the current function is already"
-      " WaitForEventsAndExecuteCallbacks!";
-    // The line below is actually tested, but in coverage mode,
-    // we never see it.
-    return;  // COV_NF_LINE
-  }
-  base::AutoReset<bool> recursion_guard(
-      &in_wait_for_events_and_execute_callbacks_, true);
-  if (alarm_map_.empty()) {
-    // no alarms, this is business as usual.
-    WaitForEventsAndCallHandleEvents(timeout_in_us_,
-                                     events_,
-                                     events_size_);
-    recorded_now_in_us_ = 0;
-    return;
-  }
-
-  // store the 'now'. If we recomputed 'now' every iteration
-  // down below, then we might never exit that loop-- any
-  // long-running alarms might install other long-running
-  // alarms, etc. By storing it here now, we ensure that
-  // a more reasonable amount of work is done here.
-  int64_t now_in_us = NowInUsec();
-
-  // Get the first timeout from the alarm_map where it is
-  // stored in absolute time.
-  int64_t next_alarm_time_in_us = alarm_map_.begin()->first;
-  VLOG(4) << "next_alarm_time = " << next_alarm_time_in_us
-          << " now             = " << now_in_us
-          << " timeout_in_us = " << timeout_in_us_;
-
-  int64_t wait_time_in_us;
-  int64_t alarm_timeout_in_us = next_alarm_time_in_us - now_in_us;
-
-  // If the next alarm is sooner than the default timeout, or if there is no
-  // timeout (timeout_in_us_ == -1), wake up when the alarm should fire.
-  // Otherwise use the default timeout.
-  if (alarm_timeout_in_us < timeout_in_us_ || timeout_in_us_ < 0) {
-    wait_time_in_us = std::max(alarm_timeout_in_us, static_cast<int64_t>(0));
-  } else {
-    wait_time_in_us = timeout_in_us_;
-  }
-
-  VLOG(4) << "wait_time_in_us = " << wait_time_in_us;
-
-  // wait for events.
-
-  WaitForEventsAndCallHandleEvents(wait_time_in_us,
-                                   events_,
-                                   events_size_);
-  CallAndReregisterAlarmEvents();
-  recorded_now_in_us_ = 0;
-}
-
-void EpollServer::SetFDReady(int fd, int events_to_fake) {
-  auto fd_i = cb_map_.find(CBAndEventMask(NULL, 0, fd));
-  if (cb_map_.end() != fd_i && fd_i->cb != NULL) {
-    // This const_cast is necessary for LIST_HEAD_INSERT to work. Declaring
-    // entry mutable is insufficient because LIST_HEAD_INSERT assigns the
-    // forward pointer of the list head to the current cb_and_mask, and the
-    // compiler complains that it can't assign a const T* to a T*.
-    CBAndEventMask* cb_and_mask = const_cast<CBAndEventMask*>(&*fd_i);
-    // Note that there is no clearly correct behavior here when
-    // cb_and_mask->events_to_fake != 0 and this function is called.
-    // Of the two operations:
-    //      cb_and_mask->events_to_fake = events_to_fake
-    //      cb_and_mask->events_to_fake |= events_to_fake
-    // the first was picked because it discourages users from calling
-    // SetFDReady repeatedly to build up the correct event set as it is more
-    // efficient to call SetFDReady once with the correct, final mask.
-    cb_and_mask->events_to_fake = events_to_fake;
-    AddToReadyList(cb_and_mask);
-  }
-}
-
-void EpollServer::SetFDNotReady(int fd) {
-  auto fd_i = cb_map_.find(CBAndEventMask(NULL, 0, fd));
-  if (cb_map_.end() != fd_i) {
-    RemoveFromReadyList(*fd_i);
-  }
-}
-
-bool EpollServer::IsFDReady(int fd) const {
-  auto fd_i = cb_map_.find(CBAndEventMask(NULL, 0, fd));
-  return (cb_map_.end() != fd_i &&
-          fd_i->cb != NULL &&
-          fd_i->entry.le_prev != NULL);
-}
-
-void EpollServer::VerifyReadyList() const {
-  int count = 0;
-  CBAndEventMask* cur = ready_list_.lh_first;
-  for (; cur; cur = cur->entry.le_next) {
-    ++count;
-  }
-  for (cur = tmp_list_.lh_first; cur; cur = cur->entry.le_next) {
-    ++count;
-  }
-  CHECK_EQ(ready_list_size_, count) << "Ready list size does not match count";
-}
-
-void EpollServer::RegisterAlarm(int64_t timeout_time_in_us, AlarmCB* ac) {
-  CHECK(ac);
-  if (base::ContainsKey(all_alarms_, ac)) {
-    LOG(FATAL) << "Alarm already exists " << ac;
-  }
-  VLOG(4) << "RegisteringAlarm at : " << timeout_time_in_us;
-
-  auto alarm_iter = alarm_map_.insert(std::make_pair(timeout_time_in_us, ac));
-
-  all_alarms_.insert(ac);
-  // Pass the iterator to the EpollAlarmCallbackInterface.
-  ac->OnRegistration(alarm_iter, this);
-}
-
-// Unregister a specific alarm callback: iterator_token must be a
-//  valid iterator. The caller must ensure the validity of the iterator.
-void EpollServer::UnregisterAlarm(const AlarmRegToken& iterator_token) {
-  AlarmCB* cb = iterator_token->second;
-  alarm_map_.erase(iterator_token);
-  all_alarms_.erase(cb);
-  cb->OnUnregistration();
-}
-
-EpollServer::AlarmRegToken EpollServer::ReregisterAlarm(
-    EpollServer::AlarmRegToken iterator_token,
-    int64_t timeout_time_in_us) {
-  AlarmCB* cb = iterator_token->second;
-  alarm_map_.erase(iterator_token);
-  return alarm_map_.emplace(timeout_time_in_us, cb);
-}
-
-int EpollServer::NumFDsRegistered() const {
-  DCHECK_GE(cb_map_.size(), 1u);
-  // Omit the internal FD (read_fd_)
-  return cb_map_.size() - 1;
-}
-
-void EpollServer::Wake() {
-  char data = 'd';  // 'd' is for data.  It's good enough for me.
-  int rv = write(write_fd_, &data, 1);
-  DCHECK_EQ(rv, 1);
-}
-
-int64_t EpollServer::NowInUsec() const {
-  return (base::Time::Now() - base::Time::UnixEpoch()).InMicroseconds();
-}
-
-int64_t EpollServer::ApproximateNowInUsec() const {
-  if (recorded_now_in_us_ != 0) {
-    return recorded_now_in_us_;
-  }
-  return this->NowInUsec();
-}
-
-std::string EpollServer::EventMaskToString(int event_mask) {
-  std::string s;
-  if (event_mask & EPOLLIN) s += "EPOLLIN ";
-  if (event_mask & EPOLLPRI) s += "EPOLLPRI ";
-  if (event_mask & EPOLLOUT) s += "EPOLLOUT ";
-  if (event_mask & EPOLLRDNORM) s += "EPOLLRDNORM ";
-  if (event_mask & EPOLLRDBAND) s += "EPOLLRDBAND ";
-  if (event_mask & EPOLLWRNORM) s += "EPOLLWRNORM ";
-  if (event_mask & EPOLLWRBAND) s += "EPOLLWRBAND ";
-  if (event_mask & EPOLLMSG) s += "EPOLLMSG ";
-  if (event_mask & EPOLLERR) s += "EPOLLERR ";
-  if (event_mask & EPOLLHUP) s += "EPOLLHUP ";
-  if (event_mask & EPOLLONESHOT) s += "EPOLLONESHOT ";
-  if (event_mask & EPOLLET) s += "EPOLLET ";
-  return s;
-}
-
-void EpollServer::LogStateOnCrash() {
-  LOG(ERROR) << "----------------------Epoll Server---------------------------";
-  LOG(ERROR) << "Epoll server " << this << " polling on fd " << epoll_fd_;
-  LOG(ERROR) << "timeout_in_us_: " << timeout_in_us_;
-
-  // Log sessions with alarms.
-  LOG(ERROR) << alarm_map_.size() << " alarms registered.";
-  for (auto it = alarm_map_.begin(); it != alarm_map_.end(); ++it) {
-    const bool skipped =
-        alarms_reregistered_and_should_be_skipped_.find(it->second)
-        != alarms_reregistered_and_should_be_skipped_.end();
-    LOG(ERROR) << "Alarm " << it->second << " registered at time " << it->first
-               << " and should be skipped = " << skipped;
-  }
-
-  LOG(ERROR) << cb_map_.size() << " fd callbacks registered.";
-  for (auto it = cb_map_.begin(); it != cb_map_.end(); ++it) {
-    LOG(ERROR) << "fd: " << it->fd << " with mask " << it->event_mask
-               << " registered with cb: " << it->cb;
-  }
-  LOG(ERROR) << "----------------------/Epoll Server--------------------------";
-}
-
-
-
-////////////////////////////////////////////////////////////////////////////////
-////////////////////////////////////////////////////////////////////////////////
-
-void EpollServer::DelFD(int fd) const {
-  struct epoll_event ee;
-  memset(&ee, 0, sizeof(ee));
-#ifdef EPOLL_SERVER_EVENT_TRACING
-  event_recorder_.RecordFDMaskEvent(fd, 0, "DelFD");
-#endif
-  if (epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, fd, &ee)) {
-    int saved_errno = errno;
-    char buf[kErrorBufferSize];
-    LOG(FATAL) << "Epoll set removal error for fd " << fd << ": "
-               << strerror_r(saved_errno, buf, sizeof(buf));
-  }
-}
-
-////////////////////////////////////////
-
-void EpollServer::AddFD(int fd, int event_mask) const {
-  struct epoll_event ee;
-  memset(&ee, 0, sizeof(ee));
-  ee.events = event_mask | EPOLLERR | EPOLLHUP;
-  ee.data.fd = fd;
-#ifdef EPOLL_SERVER_EVENT_TRACING
-  event_recorder_.RecordFDMaskEvent(fd, ee.events, "AddFD");
-#endif
-  if (epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, fd, &ee)) {
-    int saved_errno = errno;
-    char buf[kErrorBufferSize];
-    LOG(FATAL) << "Epoll set insertion error for fd " << fd << ": "
-               << strerror_r(saved_errno, buf, sizeof(buf));
-  }
-}
-
-////////////////////////////////////////
-
-void EpollServer::ModFD(int fd, int event_mask) const {
-  struct epoll_event ee;
-  memset(&ee, 0, sizeof(ee));
-  ee.events = event_mask | EPOLLERR | EPOLLHUP;
-  ee.data.fd = fd;
-#ifdef EPOLL_SERVER_EVENT_TRACING
-  event_recorder_.RecordFDMaskEvent(fd, ee.events, "ModFD");
-#endif
-  VLOG(3) <<  "modifying fd= " << fd << " "
-          << EventMaskToString(ee.events);
-  if (epoll_ctl(epoll_fd_, EPOLL_CTL_MOD, fd, &ee)) {
-    int saved_errno = errno;
-    char buf[kErrorBufferSize];
-    LOG(FATAL) << "Epoll set modification error for fd " << fd << ": "
-               << strerror_r(saved_errno, buf, sizeof(buf));
-  }
-}
-
-////////////////////////////////////////
-
-void EpollServer::ModifyFD(int fd, int remove_event, int add_event) {
-  auto fd_i = cb_map_.find(CBAndEventMask(NULL, 0, fd));
-  if (cb_map_.end() == fd_i) {
-    VLOG(2) << "Didn't find the fd " << fd << "in internal structures";
-    return;
-  }
-
-  if (fd_i->cb != NULL) {
-    int & event_mask = fd_i->event_mask;
-    VLOG(3) << "fd= " << fd
-            << " event_mask before: " << EventMaskToString(event_mask);
-    event_mask &= ~remove_event;
-    event_mask |= add_event;
-
-    VLOG(3) << " event_mask after: " << EventMaskToString(event_mask);
-
-    ModFD(fd, event_mask);
-
-    fd_i->cb->OnModification(fd, event_mask);
-  }
-}
-
-void EpollServer::WaitForEventsAndCallHandleEvents(int64_t timeout_in_us,
-                                                   struct epoll_event events[],
-                                                   int events_size) {
-  if (timeout_in_us == 0 || ready_list_.lh_first != NULL) {
-    // If ready list is not empty, then don't sleep at all.
-    timeout_in_us = 0;
-  } else if (timeout_in_us < 0) {
-    LOG(INFO) << "Negative epoll timeout: " << timeout_in_us
-              << "us; epoll will wait forever for events.";
-    // If timeout_in_us is < 0 we are supposed to Wait forever.  This means we
-    // should set timeout_in_us to -1000 so we will
-    // Wait(-1000/1000) == Wait(-1) == Wait forever.
-    timeout_in_us = -1000;
-  } else {
-    // If timeout is specified, and the ready list is empty.
-    if (timeout_in_us < 1000) {
-      timeout_in_us = 1000;
-    }
-  }
-  const int timeout_in_ms = timeout_in_us / 1000;
-  int nfds = epoll_wait_impl(epoll_fd_,
-                             events,
-                             events_size,
-                             timeout_in_ms);
-  VLOG(3) << "nfds=" << nfds;
-
-#ifdef EPOLL_SERVER_EVENT_TRACING
-  event_recorder_.RecordEpollWaitEvent(timeout_in_ms, nfds);
-#endif
-
-  // If you're wondering why the NowInUsec() is recorded here, the answer is
-  // simple: If we did it before the epoll_wait_impl, then the max error for
-  // the ApproximateNowInUs() call would be as large as the maximum length of
-  // epoll_wait, which can be arbitrarily long. Since this would make
-  // ApproximateNowInUs() worthless, we instead record the time -after- we've
-  // done epoll_wait, which guarantees that the maximum error is the amount of
-  // time it takes to process all the events generated by epoll_wait.
-  recorded_now_in_us_ = NowInUsec();
-  if (nfds > 0) {
-    for (int i = 0; i < nfds; ++i) {
-      int event_mask = events[i].events;
-      int fd = events[i].data.fd;
-      HandleEvent(fd, event_mask);
-    }
-  } else if (nfds < 0) {
-    // Catch interrupted syscall and just ignore it and move on.
-    if (errno != EINTR && errno != 0) {
-      int saved_errno = errno;
-      char buf[kErrorBufferSize];
-      LOG(FATAL) << "Error " << saved_errno << " in epoll_wait: "
-                 << strerror_r(saved_errno, buf, sizeof(buf));
-    }
-  }
-
-  // Now run through the ready list.
-  if (ready_list_.lh_first) {
-    CallReadyListCallbacks();
-  }
-}
-
-void EpollServer::CallReadyListCallbacks() {
-  // Check pre-conditions.
-  DCHECK(tmp_list_.lh_first == NULL);
-  // Swap out the ready_list_ into the tmp_list_ before traversing the list to
-  // enable SetFDReady() to just push new items into the ready_list_.
-  std::swap(ready_list_.lh_first, tmp_list_.lh_first);
-  if (tmp_list_.lh_first) {
-    tmp_list_.lh_first->entry.le_prev = &tmp_list_.lh_first;
-    EpollEvent event(0);
-    while (tmp_list_.lh_first != NULL) {
-      DCHECK_GT(ready_list_size_, 0);
-      CBAndEventMask* cb_and_mask = tmp_list_.lh_first;
-      RemoveFromReadyList(*cb_and_mask);
-
-      event.out_ready_mask = 0;
-      event.in_events =
-        cb_and_mask->events_asserted | cb_and_mask->events_to_fake;
-      // TODO(fenix): get rid of the two separate fields in cb_and_mask.
-      cb_and_mask->events_asserted = 0;
-      cb_and_mask->events_to_fake = 0;
-      {
-        // OnEvent() may call UnRegister, so we set in_use, here. Any
-        // UnRegister call will now simply set the cb to NULL instead of
-        // invalidating the cb_and_mask object (by deleting the object in the
-        // map to which cb_and_mask refers)
-        base::AutoReset<bool> in_use_guard(&(cb_and_mask->in_use), true);
-        cb_and_mask->cb->OnEvent(cb_and_mask->fd, &event);
-      }
-
-      // Since OnEvent may have called UnregisterFD, we must check here that
-      // the callback is still valid. If it isn't, then UnregisterFD *was*
-      // called, and we should now get rid of the object.
-      if (cb_and_mask->cb == NULL) {
-        cb_map_.erase(*cb_and_mask);
-      } else if (event.out_ready_mask != 0) {
-        cb_and_mask->events_to_fake = event.out_ready_mask;
-        AddToReadyList(cb_and_mask);
-      }
-    }
-  }
-  DCHECK(tmp_list_.lh_first == NULL);
-}
-
-void EpollServer::CallAndReregisterAlarmEvents() {
-  int64_t now_in_us = recorded_now_in_us_;
-  DCHECK_NE(0, recorded_now_in_us_);
-
-  TimeToAlarmCBMap::iterator erase_it;
-
-  // execute alarms.
-  for (auto i = alarm_map_.begin(); i != alarm_map_.end();) {
-    if (i->first > now_in_us) {
-      break;
-    }
-    AlarmCB* cb = i->second;
-    // Execute the OnAlarm() only if we did not register
-    // it in this loop itself.
-    const bool added_in_this_round =
-        alarms_reregistered_and_should_be_skipped_.find(cb)
-        != alarms_reregistered_and_should_be_skipped_.end();
-    if (added_in_this_round) {
-      ++i;
-      continue;
-    }
-    all_alarms_.erase(cb);
-    const int64_t new_timeout_time_in_us = cb->OnAlarm();
-
-    erase_it = i;
-    ++i;
-    alarm_map_.erase(erase_it);
-
-    if (new_timeout_time_in_us > 0) {
-      // We add to hash_set only if the new timeout is <= now_in_us.
-      // if timeout is > now_in_us then we have no fear that this alarm
-      // can be reexecuted in this loop, and hence we do not need to
-      // worry about a recursive loop.
-      DVLOG(3) << "Reregistering alarm "
-               << " " << cb
-               << " " << new_timeout_time_in_us
-               << " " << now_in_us;
-      if (new_timeout_time_in_us <= now_in_us) {
-        alarms_reregistered_and_should_be_skipped_.insert(cb);
-      }
-      RegisterAlarm(new_timeout_time_in_us, cb);
-    }
-  }
-  alarms_reregistered_and_should_be_skipped_.clear();
-}
-
-EpollAlarm::EpollAlarm() : eps_(NULL), registered_(false) {
-}
-
-EpollAlarm::~EpollAlarm() {
-  UnregisterIfRegistered();
-}
-
-int64_t EpollAlarm::OnAlarm() {
-  registered_ = false;
-  return 0;
-}
-
-void EpollAlarm::OnRegistration(const EpollServer::AlarmRegToken& token,
-                                EpollServer* eps) {
-  DCHECK_EQ(false, registered_);
-
-  token_ = token;
-  eps_ = eps;
-  registered_ = true;
-}
-
-void EpollAlarm::OnUnregistration() {
-  registered_ = false;
-}
-
-void EpollAlarm::OnShutdown(EpollServer* eps) {
-  registered_ = false;
-  eps_ = NULL;
-}
-
-// If the alarm was registered, unregister it.
-void EpollAlarm::UnregisterIfRegistered() {
-  if (!registered_) {
-    return;
-  }
-  eps_->UnregisterAlarm(token_);
-}
-
-void EpollAlarm::ReregisterAlarm(int64_t timeout_time_in_us) {
-  DCHECK(registered_);
-  token_ = eps_->ReregisterAlarm(token_, timeout_time_in_us);
-}
-
-}  // namespace net
diff --git a/net/tools/epoll_server/epoll_server.h b/net/tools/epoll_server/epoll_server.h
deleted file mode 100644
index 507c82158..0000000
--- a/net/tools/epoll_server/epoll_server.h
+++ /dev/null
@@ -1,1058 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef NET_TOOLS_EPOLL_SERVER_EPOLL_SERVER_H_
-#define NET_TOOLS_EPOLL_SERVER_EPOLL_SERVER_H_
-
-#include <fcntl.h>
-#include <stddef.h>
-#include <stdint.h>
-#include <sys/queue.h>
-
-#include <map>
-#include <memory>
-#include <string>
-#include <unordered_map>
-#include <unordered_set>
-#include <vector>
-
-// #define EPOLL_SERVER_EVENT_TRACING 1
-//
-// Defining EPOLL_SERVER_EVENT_TRACING
-// causes code to exist which didn't before.
-// This code tracks each event generated by the epollserver,
-// as well as providing a per-fd-registered summary of
-// events. Note that enabling this code vastly slows
-// down operations, and uses substantially more
-// memory. For these reasons, it should only be enabled by developers doing
-// development at their workstations.
-//
-// A structure called 'EventRecorder' will exist when
-// the macro is defined. See the EventRecorder class interface
-// within the EpollServer class for more details.
-#ifdef EPOLL_SERVER_EVENT_TRACING
-#include <ostream>
-#include "base/logging.h"
-#endif
-
-#include "base/compiler_specific.h"
-#include "base/macros.h"
-#include <sys/epoll.h>
-
-namespace net {
-
-class EpollServer;
-class EpollAlarmCallbackInterface;
-class ReadPipeCallback;
-
-struct EpollEvent {
-  EpollEvent(int events)
-      : in_events(events),
-        out_ready_mask(0) {
-  }
-
-  int in_events;            // incoming events
-  int out_ready_mask;       // the new event mask for ready list (0 means don't
-                            // get on the ready list). This field is always
-                            // initialized to 0 when the event is passed to
-                            // OnEvent.
-};
-
-// Callbacks which go into EpollServers are expected to derive from this class.
-class EpollCallbackInterface {
- public:
-  // Summary:
-  //   Called when the callback is registered into a EpollServer.
-  // Args:
-  //   eps - the poll server into which this callback was registered
-  //   fd - the file descriptor which was registered
-  //   event_mask - the event mask (composed of EPOLLIN, EPOLLOUT, etc)
-  //                which was registered (and will initially be used
-  //                in the epoll() calls)
-  virtual void OnRegistration(EpollServer* eps, int fd, int event_mask) = 0;
-
-  // Summary:
-  //   Called when the event_mask is modified (for a file-descriptor)
-  // Args:
-  //   fd - the file descriptor which was registered
-  //   event_mask - the event mask (composed of EPOLLIN, EPOLLOUT, etc)
-  //                which was is now curren (and will be used
-  //                in subsequent epoll() calls)
-  virtual void OnModification(int fd, int event_mask) = 0;
-
-  // Summary:
-  //   Called whenever an event occurs on the file-descriptor.
-  //   This is where the bulk of processing is expected to occur.
-  // Args:
-  //   fd - the file descriptor which was registered
-  //   event - a struct that contains the event mask (composed of EPOLLIN,
-  //           EPOLLOUT, etc), a flag that indicates whether this is a true
-  //           epoll_wait event vs one from the ready list, and an output
-  //           parameter for OnEvent to inform the EpollServer whether to put
-  //           this fd on the ready list.
-  virtual void OnEvent(int fd, EpollEvent* event) = 0;
-
-  // Summary:
-  //   Called when the file-descriptor is unregistered from the poll-server.
-  // Args:
-  //   fd - the file descriptor which was registered, and of this call, is now
-  //        unregistered.
-  //   replaced - If true, this callback is being replaced by another, otherwise
-  //              it is simply being removed.
-  virtual void OnUnregistration(int fd, bool replaced) = 0;
-
-  // Summary:
-  //   Called when the epoll server is shutting down.  This is different from
-  //   OnUnregistration because the subclass may want to clean up memory.
-  //   This is called in leiu of OnUnregistration.
-  // Args:
-  //  fd - the file descriptor which was registered.
-  virtual void OnShutdown(EpollServer* eps, int fd) = 0;
-
-  // Summary:
-  //   Returns a name describing the class for use in debug/error reporting.
-  virtual std::string Name() const = 0;
-
-  virtual ~EpollCallbackInterface() {}
-
- protected:
-  EpollCallbackInterface() {}
-};
-
-////////////////////////////////////////////////////////////////////////////////
-////////////////////////////////////////////////////////////////////////////////
-
-class EpollServer {
- public:
-  typedef EpollAlarmCallbackInterface AlarmCB;
-  typedef EpollCallbackInterface CB;
-
-  typedef std::multimap<int64_t, AlarmCB*> TimeToAlarmCBMap;
-  typedef TimeToAlarmCBMap::iterator AlarmRegToken;
-
-  // Summary:
-  //   Constructor:
-  //    By default, we don't wait any amount of time for events, and
-  //    we suggest to the epoll-system that we're going to use on-the-order
-  //    of 1024 FDs.
-  EpollServer();
-
-  ////////////////////////////////////////
-
-  // Destructor
-  virtual ~EpollServer();
-
-  ////////////////////////////////////////
-
-  // Summary
-  //   Register a callback to be called whenever an event contained
-  //   in the set of events included in event_mask occurs on the
-  //   file-descriptor 'fd'
-  //
-  //   Note that only one callback is allowed to be registered for
-  //   any specific file-decriptor.
-  //
-  //   If a callback is registered for a file-descriptor which has already
-  //   been registered, then the previous callback is unregistered with
-  //   the 'replaced' flag set to true. I.e. the previous callback's
-  //   OnUnregistration() function is called like so:
-  //      OnUnregistration(fd, true);
-  //
-  //  The epoll server does NOT take on ownership of the callback: the callback
-  //  creator is responsible for managing that memory.
-  //
-  // Args:
-  //   fd - a valid file-descriptor
-  //   cb - an instance of a subclass of EpollCallbackInterface
-  //   event_mask - a combination of (EPOLLOUT, EPOLLIN.. etc) indicating
-  //                the events for which the callback would like to be
-  //                called.
-  virtual void RegisterFD(int fd, CB* cb, int event_mask);
-
-  ////////////////////////////////////////
-
-  // Summary:
-  //   A shortcut for RegisterFD which sets things up such that the
-  //   callback is called when 'fd' is available for writing.
-  // Args:
-  //   fd - a valid file-descriptor
-  //   cb - an instance of a subclass of EpollCallbackInterface
-  virtual void RegisterFDForWrite(int fd, CB* cb);
-
-  ////////////////////////////////////////
-
-  // Summary:
-  //   A shortcut for RegisterFD which sets things up such that the
-  //   callback is called when 'fd' is available for reading or writing.
-  // Args:
-  //   fd - a valid file-descriptor
-  //   cb - an instance of a subclass of EpollCallbackInterface
-  virtual void RegisterFDForReadWrite(int fd, CB* cb);
-
-  ////////////////////////////////////////
-
-  // Summary:
-  //   A shortcut for RegisterFD which sets things up such that the
-  //   callback is called when 'fd' is available for reading.
-  // Args:
-  //   fd - a valid file-descriptor
-  //   cb - an instance of a subclass of EpollCallbackInterface
-  virtual void RegisterFDForRead(int fd, CB* cb);
-
-  ////////////////////////////////////////
-
-  // Summary:
-  //   Removes the FD and the associated callback from the pollserver.
-  //   If the callback is registered with other FDs, they will continue
-  //   to be processed using the callback without modification.
-  //   If the file-descriptor specified is not registered in the
-  //   epoll_server, then nothing happens as a result of this call.
-  // Args:
-  //   fd - the file-descriptor which should no-longer be monitored.
-  virtual void UnregisterFD(int fd);
-
-  ////////////////////////////////////////
-
-  // Summary:
-  //   Modifies the event mask for the file-descriptor, replacing
-  //   the old event_mask with the new one specified here.
-  //   If the file-descriptor specified is not registered in the
-  //   epoll_server, then nothing happens as a result of this call.
-  // Args:
-  //   fd - the fd whose event mask should be modified.
-  //   event_mask - the new event mask.
-  virtual void ModifyCallback(int fd, int event_mask);
-
-  ////////////////////////////////////////
-
-  // Summary:
-  //   Modifies the event mask for the file-descriptor such that we
-  //   no longer request events when 'fd' is readable.
-  //   If the file-descriptor specified is not registered in the
-  //   epoll_server, then nothing happens as a result of this call.
-  // Args:
-  //   fd - the fd whose event mask should be modified.
-  virtual void StopRead(int fd);
-
-  ////////////////////////////////////////
-
-  // Summary:
-  //   Modifies the event mask for the file-descriptor such that we
-  //   request events when 'fd' is readable.
-  //   If the file-descriptor specified is not registered in the
-  //   epoll_server, then nothing happens as a result of this call.
-  // Args:
-  //   fd - the fd whose event mask should be modified.
-  virtual void StartRead(int fd);
-
-  ////////////////////////////////////////
-
-  // Summary:
-  //   Modifies the event mask for the file-descriptor such that we
-  //   no longer request events when 'fd' is writable.
-  //   If the file-descriptor specified is not registered in the
-  //   epoll_server, then nothing happens as a result of this call.
-  // Args:
-  //   fd - the fd whose event mask should be modified.
-  virtual void StopWrite(int fd);
-
-  ////////////////////////////////////////
-
-  // Summary:
-  //   Modifies the event mask for the file-descriptor such that we
-  //   request events when 'fd' is writable.
-  //   If the file-descriptor specified is not registered in the
-  //   epoll_server, then nothing happens as a result of this call.
-  // Args:
-  //   fd - the fd whose event mask should be modified.
-  virtual void StartWrite(int fd);
-
-  ////////////////////////////////////////
-
-  // Summary:
-  //   Looks up the callback associated with the file-desriptor 'fd'.
-  //   If a callback is associated with this file-descriptor, then
-  //   it's OnEvent() method is called with the file-descriptor 'fd',
-  //   and event_mask 'event_mask'
-  //
-  //   If no callback is registered for this file-descriptor, nothing
-  //   will happen as a result of this call.
-  //
-  //   This function is used internally by the EpollServer, but is
-  //   available publically so that events might be 'faked'. Calling
-  //   this function with an fd and event_mask is equivalent (as far
-  //   as the callback is concerned) to having a real event generated
-  //   by epoll (except, of course, that read(), etc won't necessarily
-  //   be able to read anything)
-  // Args:
-  //   fd - the file-descriptor on which an event has occured.
-  //   event_mask - a bitmask representing the events which have occured
-  //                on/for this fd. This bitmask is composed of
-  //                POLLIN, POLLOUT, etc.
-  //
-  void HandleEvent(int fd, int event_mask);
-
-  // Summary:
-  //   Call this when you want the pollserver to
-  //   wait for events and execute the callbacks associated with
-  //   the file-descriptors on which those events have occured.
-  //   Depending on the value of timeout_in_us_, this may or may
-  //   not return immediately. Please reference the set_timeout()
-  //   function for the specific behaviour.
-  virtual void WaitForEventsAndExecuteCallbacks();
-
-  // Summary:
-  //   When an fd is registered to use edge trigger notification, the ready
-  //   list can be used to simulate level trigger semantics. Edge trigger
-  //   registration doesn't send an initial event, and only rising edge (going
-  //   from blocked to unblocked) events are sent. A callback can put itself on
-  //   the ready list by calling SetFDReady() after calling RegisterFD(). The
-  //   OnEvent method of all callbacks associated with the fds on the ready
-  //   list will be called immediately after processing the events returned by
-  //   epoll_wait(). The fd is removed from the ready list before the
-  //   callback's OnEvent() method is invoked. To stay on the ready list, the
-  //   OnEvent() (or some function in that call chain) must call SetFDReady
-  //   again. When a fd is unregistered using UnregisterFD(), the fd is
-  //   automatically removed from the ready list.
-  //
-  //   When the callback for a edge triggered fd hits the falling edge (about
-  //   to block, either because of it got an EAGAIN, or had a short read/write
-  //   operation), it should remove itself from the ready list using
-  //   SetFDNotReady() (since OnEvent cannot distinguish between invocation
-  //   from the ready list vs from a normal epoll event). All four ready list
-  //   methods are safe to be called  within the context of the callbacks.
-  //
-  //   Since the ready list invokes EpollCallbackInterface::OnEvent, only fds
-  //   that are registered with the EpollServer will be put on the ready list.
-  //   SetFDReady() and SetFDNotReady() will do nothing if the EpollServer
-  //   doesn't know about the fd passed in.
-  //
-  //   Since the ready list cannot reliably determine proper set of events
-  //   which should be sent to the callback, SetFDReady() requests the caller
-  //   to provide the ready list with the event mask, which will be used later
-  //   when OnEvent() is invoked by the ready list. Hence, the event_mask
-  //   passedto SetFDReady() does not affect the actual epoll registration of
-  //   the fd with the kernel. If a fd is already put on the ready list, and
-  //   SetFDReady() is called again for that fd with a different event_mask,
-  //   the event_mask will be updated.
-  virtual void SetFDReady(int fd, int events_to_fake);
-
-  virtual void SetFDNotReady(int fd);
-
-  // Summary:
-  //   IsFDReady(), ReadyListSize(), and VerifyReadyList are intended as
-  //   debugging tools and for writing unit tests.
-  //   ISFDReady() returns whether a fd is in the ready list.
-  //   ReadyListSize() returns the number of fds on the ready list.
-  //   VerifyReadyList() checks the consistency of internal data structure. It
-  //   will CHECK if it finds an error.
-  virtual bool IsFDReady(int fd) const;
-
-  size_t ReadyListSize() const { return ready_list_size_; }
-
-  void VerifyReadyList() const;
-
-  ////////////////////////////////////////
-
-  // Summary:
-  //   Registers an alarm 'ac' to go off at time 'timeout_time_in_us'.
-  //   If the callback returns a positive number from its OnAlarm() function,
-  //   then the callback will be re-registered at that time, else the alarm
-  //   owner is responsible for freeing up memory.
-  //
-  //   Important: A give AlarmCB* can not be registered again if it is already
-  //    registered. If a user wants to register a callback again it should first
-  //    unregister the previous callback before calling RegisterAlarm again.
-  // Args:
-  //   timeout_time_in_us - the absolute time at which the alarm should go off
-  //   ac - the alarm which will be called.
-  virtual void RegisterAlarm(int64_t timeout_time_in_us, AlarmCB* ac);
-
-  // Summary:
-  //   Registers an alarm 'ac' to go off at time: (ApproximateNowInUs() +
-  //   delta_in_us). While this is somewhat less accurate (see the description
-  //   for ApproximateNowInUs() to see how 'approximate'), the error is never
-  //   worse than the amount of time it takes to process all events in one
-  //   WaitForEvents.  As with 'RegisterAlarm()', if the callback returns a
-  //   positive number from its OnAlarm() function, then the callback will be
-  //   re-registered at that time, else the alarm owner is responsible for
-  //   freeing up memory.
-  //   Note that this function is purely a convienence. The
-  //   same thing may be accomplished by using RegisterAlarm with
-  //   ApproximateNowInUs() directly.
-  //
-  //   Important: A give AlarmCB* can not be registered again if it is already
-  //    registered. If a user wants to register a callback again it should first
-  //    unregister the previous callback before calling RegisterAlarm again.
-  // Args:
-  //   delta_in_us - the delta in microseconds from the ApproximateTimeInUs() at
-  //                 which point the alarm should go off.
-  //   ac - the alarm which will be called.
-  void RegisterAlarmApproximateDelta(int64_t delta_in_us, AlarmCB* ac) {
-    RegisterAlarm(ApproximateNowInUsec() + delta_in_us, ac);
-  }
-
-  ////////////////////////////////////////
-
-  // Summary:
-  //   Unregister  the alarm referred to by iterator_token; Callers should
-  //   be warned that a token may have become already invalid when OnAlarm()
-  //   is called, was unregistered, or OnShutdown was called on that alarm.
-  // Args:
-  //    iterator_token - iterator to the alarm callback to unregister.
-  virtual void UnregisterAlarm(
-      const EpollServer::AlarmRegToken& iterator_token);
-
-  virtual EpollServer::AlarmRegToken ReregisterAlarm(
-      EpollServer::AlarmRegToken iterator_token,
-      int64_t timeout_time_in_us);
-
-  ////////////////////////////////////////
-
-  // Summary:
-  //   returns the number of file-descriptors registered in this EpollServer.
-  // Returns:
-  //   number of FDs registered (discounting the internal pipe used for Wake)
-  virtual int NumFDsRegistered() const;
-
-  // Summary:
-  //   Force the epoll server to wake up (by writing to an internal pipe).
-  virtual void Wake();
-
-  // Summary:
-  //   Wrapper around WallTimer's NowInUsec.  We do this so that we can test
-  //   EpollServer without using the system clock (and can avoid the flakiness
-  //   that would ensue)
-  // Returns:
-  //   the current time as number of microseconds since the Unix epoch.
-  virtual int64_t NowInUsec() const;
-
-  // Summary:
-  //   Since calling NowInUsec() many thousands of times per
-  //   WaitForEventsAndExecuteCallbacks function call is, to say the least,
-  //   inefficient, we allow users to use an approximate time instead. The
-  //   time returned from this function is as accurate as NowInUsec() when
-  //   WaitForEventsAndExecuteCallbacks is not an ancestor of the caller's
-  //   callstack.
-  //   However, when WaitForEventsAndExecuteCallbacks -is- an ancestor, then
-  //   this function returns the time at which the
-  //   WaitForEventsAndExecuteCallbacks function started to process events or
-  //   alarms.
-  //
-  //   Essentially, this function makes available a fast and mostly accurate
-  //   mechanism for getting the time for any function handling an event or
-  //   alarm. When functions which are not handling callbacks or alarms call
-  //   this function, they get the slow and "absolutely" accurate time.
-  //
-  //   Users should be encouraged to use this function.
-  // Returns:
-  //   the "approximate" current time as number of microseconds since the Unix
-  //   epoch.
-  virtual int64_t ApproximateNowInUsec() const;
-
-  static std::string EventMaskToString(int event_mask);
-
-  // Summary:
-  //   Logs the state of the epoll server with LOG(ERROR).
-  void LogStateOnCrash();
-
-  // Summary:
-  //   Set the timeout to the value specified.
-  //   If the timeout is set to a negative number,
-  //      WaitForEventsAndExecuteCallbacks() will only return when an event has
-  //      occured
-  //   If the timeout is set to zero,
-  //      WaitForEventsAndExecuteCallbacks() will return immediately
-  //   If the timeout is set to a positive number,
-  //      WaitForEventsAndExecuteCallbacks() will return when an event has
-  //      occured, or when timeout_in_us microseconds has elapsed, whichever
-  //      is first.
-  //  Args:
-  //    timeout_in_us - value specified depending on behaviour desired.
-  //                    See above.
-  void set_timeout_in_us(int64_t timeout_in_us) {
-    timeout_in_us_ = timeout_in_us;
-  }
-
-  ////////////////////////////////////////
-
-  // Summary:
-  //   Accessor for the current value of timeout_in_us.
-  int timeout_in_us_for_test() const { return timeout_in_us_; }
-
-  // Summary:
-  // Returns true when the EpollServer() is being destroyed.
-  bool in_shutdown() const { return in_shutdown_; }
-
-  // Compatibility stub.
-  void Shutdown() {}
-
- protected:
-  virtual void SetNonblocking(int fd);
-
-  // This exists here so that we can override this function in unittests
-  // in order to make effective mock EpollServer objects.
-  virtual int epoll_wait_impl(int epfd,
-                              struct epoll_event* events,
-                              int max_events,
-                              int timeout_in_ms);
-
-  // this struct is used internally, and is never used by anything external
-  // to this class. Some of its members are declared mutable to get around the
-  // restriction imposed by hash_set. Since hash_set knows nothing about the
-  // objects it stores, it has to assume that every bit of the object is used
-  // in the hash function and equal_to comparison. Thus hash_set::iterator is a
-  // const iterator. In this case, the only thing that must stay constant is
-  // fd. Everything else are just along for the ride and changing them doesn't
-  // compromise the hash_set integrity.
-  struct CBAndEventMask {
-    CBAndEventMask()
-        : cb(NULL),
-          fd(-1),
-          event_mask(0),
-          events_asserted(0),
-          events_to_fake(0),
-          in_use(false) {
-      entry.le_next = NULL;
-      entry.le_prev = NULL;
-    }
-
-    CBAndEventMask(EpollCallbackInterface* cb,
-                   int event_mask,
-                   int fd)
-        : cb(cb), fd(fd), event_mask(event_mask), events_asserted(0),
-          events_to_fake(0), in_use(false) {
-      entry.le_next = NULL;
-      entry.le_prev = NULL;
-    }
-
-    // Required operator for hash_set. Normally operator== should be a free
-    // standing function. However, since CBAndEventMask is a protected type and
-    // it will never be a base class, it makes no difference.
-    bool operator==(const CBAndEventMask& cb_and_mask) const {
-      return fd == cb_and_mask.fd;
-    }
-    // A callback. If the fd is unregistered inside the callchain of OnEvent,
-    // the cb will be set to NULL.
-    mutable EpollCallbackInterface* cb;
-
-    mutable LIST_ENTRY(CBAndEventMask) entry;
-    // file descriptor registered with the epoll server.
-    int fd;
-    // the current event_mask registered for this callback.
-    mutable int event_mask;
-    // the event_mask that was returned by epoll
-    mutable int events_asserted;
-    // the event_mask for the ready list to use to call OnEvent.
-    mutable int events_to_fake;
-    // toggle around calls to OnEvent to tell UnregisterFD to not erase the
-    // iterator because HandleEvent is using it.
-    mutable bool in_use;
-  };
-
-  // Custom hash function to be used by hash_set.
-  struct CBAndEventMaskHash {
-    size_t operator()(const CBAndEventMask& cb_and_eventmask) const {
-      return static_cast<size_t>(cb_and_eventmask.fd);
-    }
-  };
-
-  using FDToCBMap = std::unordered_set<CBAndEventMask, CBAndEventMaskHash>;
-
-  // the following four functions are OS-specific, and are likely
-  // to be changed in a subclass if the poll/select method is changed
-  // from epoll.
-
-  // Summary:
-  //   Deletes a file-descriptor from the set of FDs that should be
-  //   monitored with epoll.
-  //   Note that this only deals with modifying data relating -directly-
-  //   with the epoll call-- it does not modify any data within the
-  //   epoll_server.
-  // Args:
-  //   fd - the file descriptor to-be-removed from the monitoring set
-  virtual void DelFD(int fd) const;
-
-  ////////////////////////////////////////
-
-  // Summary:
-  //   Adds a file-descriptor to the set of FDs that should be
-  //   monitored with epoll.
-  //   Note that this only deals with modifying data relating -directly-
-  //   with the epoll call.
-  // Args:
-  //   fd - the file descriptor to-be-added to the monitoring set
-  //   event_mask - the event mask (consisting of EPOLLIN, EPOLLOUT, etc
-  //                 OR'd together) which will be associated with this
-  //                 FD initially.
-  virtual void AddFD(int fd, int event_mask) const;
-
-  ////////////////////////////////////////
-
-  // Summary:
-  //   Modifies a file-descriptor in the set of FDs that should be
-  //   monitored with epoll.
-  //   Note that this only deals with modifying data relating -directly-
-  //   with the epoll call.
-  // Args:
-  //   fd - the file descriptor to-be-added to the monitoring set
-  //   event_mask - the event mask (consisting of EPOLLIN, EPOLLOUT, etc
-  //                 OR'd together) which will be associated with this
-  //                 FD after this call.
-  virtual void ModFD(int fd, int event_mask) const;
-
-  ////////////////////////////////////////
-
-  // Summary:
-  //   Modified the event mask associated with an FD in the set of
-  //   data needed by epoll.
-  //   Events are removed before they are added, thus, if ~0 is put
-  //   in 'remove_event', whatever is put in 'add_event' will be
-  //   the new event mask.
-  //   If the file-descriptor specified is not registered in the
-  //   epoll_server, then nothing happens as a result of this call.
-  // Args:
-  //   fd - the file descriptor whose event mask is to be modified
-  //   remove_event - the events which are to be removed from the current
-  //                  event_mask
-  //   add_event - the events which are to be added to the current event_mask
-  //
-  //
-  virtual void ModifyFD(int fd, int remove_event, int add_event);
-
-  ////////////////////////////////////////
-
-  // Summary:
-  //   Waits for events, and calls HandleEvents() for each
-  //   fd, event pair discovered to possibly have an event.
-  //   Note that a callback (B) may get a spurious event if
-  //   another callback (A) has closed a file-descriptor N, and
-  //   the callback (B) has a newly opened file-descriptor, which
-  //   also happens to be N.
-  virtual void WaitForEventsAndCallHandleEvents(int64_t timeout_in_us,
-                                                struct epoll_event events[],
-                                                int events_size);
-
-  // Summary:
-  //   A function for implementing the ready list. It invokes OnEvent for each
-  //   of the fd in the ready list, and takes care of adding them back to the
-  //   ready list if the callback requests it (by checking that out_ready_mask
-  //   is non-zero).
-  void CallReadyListCallbacks();
-
-  // Summary:
-  //   An internal function for implementing the ready list. It adds a fd's
-  //   CBAndEventMask to the ready list. If the fd is already on the ready
-  //   list, it is a no-op.
-  void AddToReadyList(CBAndEventMask* cb_and_mask);
-
-  // Summary:
-  //   An internal function for implementing the ready list. It remove a fd's
-  //   CBAndEventMask from the ready list. If the fd is not on the ready list,
-  //   it is a no-op.
-  void RemoveFromReadyList(const CBAndEventMask& cb_and_mask);
-
-  // Summary:
-  // Calls any pending alarms that should go off and reregisters them if they
-  // were recurring.
-  virtual void CallAndReregisterAlarmEvents();
-
-  // The file-descriptor created for epolling
-  int epoll_fd_;
-
-  // The mapping of file-descriptor to CBAndEventMasks
-  FDToCBMap cb_map_;
-
-  // Custom hash function to be used by hash_set.
-  struct AlarmCBHash {
-    size_t operator()(AlarmCB*const& p) const {
-      return reinterpret_cast<size_t>(p);
-    }
-  };
-
-
-  // TODO(sushantj): Having this hash_set is avoidable. We currently have it
-  // only so that we can enforce stringent checks that a caller can not register
-  // the same alarm twice. One option is to have an implementation in which
-  // this hash_set is used only in the debug mode.
-  using AlarmCBMap = std::unordered_set<AlarmCB*, AlarmCBHash>;
-  AlarmCBMap all_alarms_;
-
-  TimeToAlarmCBMap alarm_map_;
-
-  // The amount of time in microseconds that we'll wait before returning
-  // from the WaitForEventsAndExecuteCallbacks() function.
-  // If this is positive, wait that many microseconds.
-  // If this is negative, wait forever, or for the first event that occurs
-  // If this is zero, never wait for an event.
-  int64_t timeout_in_us_;
-
-  // This is nonzero only after the invocation of epoll_wait_impl within
-  // WaitForEventsAndCallHandleEvents and before the function
-  // WaitForEventsAndExecuteCallbacks returns.  At all other times, this is
-  // zero. This enables us to have relatively accurate time returned from the
-  // ApproximateNowInUs() function. See that function for more details.
-  int64_t recorded_now_in_us_;
-
-  // This is used to implement CallAndReregisterAlarmEvents. This stores
-  // all alarms that were reregistered because OnAlarm() returned a
-  // value > 0 and the time at which they should be executed is less that
-  // the current time.  By storing such alarms in this map we ensure
-  // that while calling CallAndReregisterAlarmEvents we do not call
-  // OnAlarm on any alarm in this set. This ensures that we do not
-  // go in an infinite loop.
-  AlarmCBMap alarms_reregistered_and_should_be_skipped_;
-
-  LIST_HEAD(ReadyList, CBAndEventMask) ready_list_;
-  LIST_HEAD(TmpList, CBAndEventMask) tmp_list_;
-  int ready_list_size_;
-  // TODO(alyssar): make this into something that scales up.
-  static const int events_size_ = 256;
-  struct epoll_event events_[256];
-
-#ifdef EPOLL_SERVER_EVENT_TRACING
-  struct EventRecorder {
-   public:
-    EventRecorder() : num_records_(0), record_threshold_(10000) {}
-
-    ~EventRecorder() {
-      Clear();
-    }
-
-    // When a number of events equals the record threshold,
-    // the collected data summary for all FDs will be written
-    // to LOG(INFO). Note that this does not include the
-    // individual events (if you'reinterested in those, you'll
-    // have to get at them programmatically).
-    // After any such flushing to LOG(INFO) all events will
-    // be cleared.
-    // Note that the definition of an 'event' is a bit 'hazy',
-    // as it includes the 'Unregistration' event, and perhaps
-    // others.
-    void set_record_threshold(int64_t new_threshold) {
-      record_threshold_ = new_threshold;
-    }
-
-    void Clear() {
-      for (int i = 0; i < debug_events_.size(); ++i) {
-        delete debug_events_[i];
-      }
-      debug_events_.clear();
-      unregistered_fds_.clear();
-      event_counts_.clear();
-    }
-
-    void MaybeRecordAndClear() {
-      ++num_records_;
-      if ((num_records_ > record_threshold_) &&
-          (record_threshold_ > 0)) {
-        LOG(INFO) << "\n" << *this;
-        num_records_ = 0;
-        Clear();
-      }
-    }
-
-    void RecordFDMaskEvent(int fd, int mask, const char* function) {
-      FDMaskOutput* fdmo = new FDMaskOutput(fd, mask, function);
-      debug_events_.push_back(fdmo);
-      MaybeRecordAndClear();
-    }
-
-    void RecordEpollWaitEvent(int timeout_in_ms,
-                              int num_events_generated) {
-      EpollWaitOutput* ewo = new EpollWaitOutput(timeout_in_ms,
-                                                  num_events_generated);
-      debug_events_.push_back(ewo);
-      MaybeRecordAndClear();
-    }
-
-    void RecordEpollEvent(int fd, int event_mask) {
-      Events& events_for_fd = event_counts_[fd];
-      events_for_fd.AssignFromMask(event_mask);
-      MaybeRecordAndClear();
-    }
-
-    friend ostream& operator<<(ostream& os, const EventRecorder& er) {
-      for (int i = 0; i < er.unregistered_fds_.size(); ++i) {
-        os << "fd: " << er.unregistered_fds_[i] << "\n";
-        os << er.unregistered_fds_[i];
-      }
-      for (EventCountsMap::const_iterator i = er.event_counts_.begin();
-           i != er.event_counts_.end();
-           ++i) {
-        os << "fd: " << i->first << "\n";
-        os << i->second;
-      }
-      for (int i = 0; i < er.debug_events_.size(); ++i) {
-        os << *(er.debug_events_[i]) << "\n";
-      }
-      return os;
-    }
-
-    void RecordUnregistration(int fd) {
-      EventCountsMap::iterator i = event_counts_.find(fd);
-      if (i != event_counts_.end()) {
-        unregistered_fds_.push_back(i->second);
-        event_counts_.erase(i);
-      }
-      MaybeRecordAndClear();
-    }
-
-   protected:
-    class DebugOutput {
-     public:
-      friend ostream& operator<<(ostream& os, const DebugOutput& debug_output) {
-        debug_output.OutputToStream(os);
-        return os;
-      }
-      virtual void OutputToStream(ostream* os) const = 0;
-      virtual ~DebugOutput() {}
-    };
-
-    class FDMaskOutput : public DebugOutput {
-     public:
-      FDMaskOutput(int fd, int mask, const char* function) :
-          fd_(fd), mask_(mask), function_(function) {}
-      virtual void OutputToStream(ostream* os) const {
-        (*os) << "func: " << function_
-              << "\tfd: " << fd_;
-        if (mask_ != 0) {
-           (*os) << "\tmask: " << EventMaskToString(mask_);
-        }
-      }
-      int fd_;
-      int mask_;
-      const char* function_;
-    };
-
-    class EpollWaitOutput : public DebugOutput {
-     public:
-      EpollWaitOutput(int timeout_in_ms,
-                      int num_events_generated) :
-          timeout_in_ms_(timeout_in_ms),
-          num_events_generated_(num_events_generated) {}
-      virtual void OutputToStream(ostream* os) const {
-        (*os) << "timeout_in_ms: " << timeout_in_ms_
-              << "\tnum_events_generated: " << num_events_generated_;
-      }
-     protected:
-      int timeout_in_ms_;
-      int num_events_generated_;
-    };
-
-    struct Events {
-      Events() :
-          epoll_in(0),
-          epoll_pri(0),
-          epoll_out(0),
-          epoll_rdnorm(0),
-          epoll_rdband(0),
-          epoll_wrnorm(0),
-          epoll_wrband(0),
-          epoll_msg(0),
-          epoll_err(0),
-          epoll_hup(0),
-          epoll_oneshot(0),
-          epoll_et(0) {}
-
-      void AssignFromMask(int event_mask) {
-        if (event_mask & EPOLLIN) ++epoll_in;
-        if (event_mask & EPOLLPRI) ++epoll_pri;
-        if (event_mask & EPOLLOUT) ++epoll_out;
-        if (event_mask & EPOLLRDNORM) ++epoll_rdnorm;
-        if (event_mask & EPOLLRDBAND) ++epoll_rdband;
-        if (event_mask & EPOLLWRNORM) ++epoll_wrnorm;
-        if (event_mask & EPOLLWRBAND) ++epoll_wrband;
-        if (event_mask & EPOLLMSG) ++epoll_msg;
-        if (event_mask & EPOLLERR) ++epoll_err;
-        if (event_mask & EPOLLHUP) ++epoll_hup;
-        if (event_mask & EPOLLONESHOT) ++epoll_oneshot;
-        if (event_mask & EPOLLET) ++epoll_et;
-      };
-
-      friend ostream& operator<<(ostream& os, const Events& ev) {
-        if (ev.epoll_in) {
-          os << "\t      EPOLLIN: " << ev.epoll_in << "\n";
-        }
-        if (ev.epoll_pri) {
-          os << "\t     EPOLLPRI: " << ev.epoll_pri << "\n";
-        }
-        if (ev.epoll_out) {
-          os << "\t     EPOLLOUT: " << ev.epoll_out << "\n";
-        }
-        if (ev.epoll_rdnorm) {
-          os << "\t  EPOLLRDNORM: " << ev.epoll_rdnorm << "\n";
-        }
-        if (ev.epoll_rdband) {
-          os << "\t  EPOLLRDBAND: " << ev.epoll_rdband << "\n";
-        }
-        if (ev.epoll_wrnorm) {
-          os << "\t  EPOLLWRNORM: " << ev.epoll_wrnorm << "\n";
-        }
-        if (ev.epoll_wrband) {
-          os << "\t  EPOLLWRBAND: " << ev.epoll_wrband << "\n";
-        }
-        if (ev.epoll_msg) {
-          os << "\t     EPOLLMSG: " << ev.epoll_msg << "\n";
-        }
-        if (ev.epoll_err) {
-          os << "\t     EPOLLERR: " << ev.epoll_err << "\n";
-        }
-        if (ev.epoll_hup) {
-          os << "\t     EPOLLHUP: " << ev.epoll_hup << "\n";
-        }
-        if (ev.epoll_oneshot) {
-          os << "\t EPOLLONESHOT: " << ev.epoll_oneshot << "\n";
-        }
-        if (ev.epoll_et) {
-          os << "\t      EPOLLET: " << ev.epoll_et << "\n";
-        }
-        return os;
-      }
-
-      unsigned int epoll_in;
-      unsigned int epoll_pri;
-      unsigned int epoll_out;
-      unsigned int epoll_rdnorm;
-      unsigned int epoll_rdband;
-      unsigned int epoll_wrnorm;
-      unsigned int epoll_wrband;
-      unsigned int epoll_msg;
-      unsigned int epoll_err;
-      unsigned int epoll_hup;
-      unsigned int epoll_oneshot;
-      unsigned int epoll_et;
-    };
-
-    std::vector<DebugOutput*> debug_events_;
-    std::vector<Events> unregistered_fds_;
-    using EventCountsMap = std::unordered_map<int, Events>;
-    EventCountsMap event_counts_;
-    int64_t num_records_;
-    int64_t record_threshold_;
-  };
-
-  void ClearEventRecords() {
-    event_recorder_.Clear();
-  }
-  void WriteEventRecords(ostream* os) const {
-    (*os) << event_recorder_;
-  }
-
-  mutable EventRecorder event_recorder_;
-
-#endif
-
- private:
-  // Helper functions used in the destructor.
-  void CleanupFDToCBMap();
-  void CleanupTimeToAlarmCBMap();
-
-  // The callback registered to the fds below.  As the purpose of their
-  // registration is to wake the epoll server it just clears the pipe and
-  // returns.
-  std::unique_ptr<ReadPipeCallback> wake_cb_;
-
-  // A pipe owned by the epoll server.  The server will be registered to listen
-  // on read_fd_ and can be woken by Wake() which writes to write_fd_.
-  int read_fd_;
-  int write_fd_;
-
-  // This boolean is checked to see if it is false at the top of the
-  // WaitForEventsAndExecuteCallbacks function. If not, then it either returns
-  // without doing work, and logs to ERROR, or aborts the program (in
-  // DEBUG mode). If so, then it sets the bool to true, does work, and
-  // sets it back to false when done. This catches unwanted recursion.
-  bool in_wait_for_events_and_execute_callbacks_;
-
-  // Returns true when the EpollServer() is being destroyed.
-  bool in_shutdown_;
-
-  DISALLOW_COPY_AND_ASSIGN(EpollServer);
-};
-
-class EpollAlarmCallbackInterface {
- public:
-  // Summary:
-  //   Called when an alarm times out. Invalidates an AlarmRegToken.
-  //   WARNING: If a token was saved to refer to an alarm callback, OnAlarm must
-  //   delete it, as the reference is no longer valid.
-  // Returns:
-  //   the unix time (in microseconds) at which this alarm should be signaled
-  //   again, or 0 if the alarm should be removed.
-  virtual int64_t OnAlarm() = 0;
-
-  // Summary:
-  //   Called when the an alarm is registered. Invalidates an AlarmRegToken.
-  // Args:
-  //   token: the iterator to the the alarm registered in the alarm map.
-  //   WARNING: this token becomes invalid when the alarm fires, is
-  //   unregistered, or OnShutdown is called on that alarm.
-  //   eps: the epoll server the alarm is registered with.
-  virtual void OnRegistration(const EpollServer::AlarmRegToken& token,
-                              EpollServer* eps) = 0;
-
-  // Summary:
-  //   Called when the an alarm is unregistered.
-  //   WARNING: It is not valid to unregister a callback and then use the token
-  //   that was saved to refer to the callback.
-  virtual void OnUnregistration() = 0;
-
-  // Summary:
-  //   Called when the epoll server is shutting down.
-  //   Invalidates the AlarmRegToken that was given when this alarm was
-  //   registered.
-  virtual void OnShutdown(EpollServer* eps) = 0;
-
-  virtual ~EpollAlarmCallbackInterface() {}
-
- protected:
-  EpollAlarmCallbackInterface() {}
-};
-
-// A simple alarm which unregisters itself on destruction.
-//
-// PLEASE NOTE:
-// Any classes overriding these functions must either call the implementation
-// of the parent class, or is must otherwise make sure that the 'registered_'
-// boolean and the token, 'token_', are updated appropriately.
-class EpollAlarm : public EpollAlarmCallbackInterface {
- public:
-  EpollAlarm();
-
-  ~EpollAlarm() override;
-
-  // Marks the alarm as unregistered and returns 0.  The return value may be
-  // safely ignored by subclasses.
-  int64_t OnAlarm() override;
-
-  // Marks the alarm as registered, and stores the token.
-  void OnRegistration(const EpollServer::AlarmRegToken& token,
-                      EpollServer* eps) override;
-
-  // Marks the alarm as unregistered.
-  void OnUnregistration() override;
-
-  // Marks the alarm as unregistered.
-  void OnShutdown(EpollServer* eps) override;
-
-  // If the alarm was registered, unregister it.
-  void UnregisterIfRegistered();
-
-  // Reregisters the alarm at specified time.
-  void ReregisterAlarm(int64_t timeout_time_in_us);
-
-  bool registered() const { return registered_; }
-
-  const EpollServer* eps() const { return eps_; }
-
- private:
-  EpollServer::AlarmRegToken token_;
-  EpollServer* eps_;
-  bool registered_;
-};
-
-}  // namespace net
-
-#endif  // NET_TOOLS_EPOLL_SERVER_EPOLL_SERVER_H_
diff --git a/net/tools/epoll_server/fake_epoll_server.cc b/net/tools/epoll_server/fake_epoll_server.cc
deleted file mode 100644
index e5ab48c..0000000
--- a/net/tools/epoll_server/fake_epoll_server.cc
+++ /dev/null
@@ -1,60 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "net/tools/epoll_server/fake_epoll_server.h"
-
-namespace quic {
-namespace test {
-
-FakeTimeEpollServer::FakeTimeEpollServer() : now_in_usec_(0) {}
-
-FakeTimeEpollServer::~FakeTimeEpollServer() = default;
-
-int64_t FakeTimeEpollServer::NowInUsec() const {
-  return now_in_usec_;
-}
-
-FakeEpollServer::FakeEpollServer() : until_in_usec_(-1) {}
-
-FakeEpollServer::~FakeEpollServer() = default;
-
-int FakeEpollServer::epoll_wait_impl(int epfd,
-                                     struct epoll_event* events,
-                                     int max_events,
-                                     int timeout_in_ms) {
-  int num_events = 0;
-  while (!event_queue_.empty() && num_events < max_events &&
-         event_queue_.begin()->first <= NowInUsec() &&
-         ((until_in_usec_ == -1) ||
-          (event_queue_.begin()->first < until_in_usec_))) {
-    int64_t event_time_in_usec = event_queue_.begin()->first;
-    events[num_events] = event_queue_.begin()->second;
-    if (event_time_in_usec > NowInUsec()) {
-      set_now_in_usec(event_time_in_usec);
-    }
-    event_queue_.erase(event_queue_.begin());
-    ++num_events;
-  }
-  if (num_events == 0) {       // then we'd have waited 'till the timeout.
-    if (until_in_usec_ < 0) {  // then we don't care what the final time is.
-      if (timeout_in_ms > 0) {
-        AdvanceBy(timeout_in_ms * 1000);
-      }
-    } else {  // except we assume that we don't wait for the timeout
-      // period if until_in_usec_ is a positive number.
-      set_now_in_usec(until_in_usec_);
-      // And reset until_in_usec_ to signal no waiting (as
-      // the AdvanceByExactly* stuff is meant to be one-shot,
-      // as are all similar net::EpollServer functions)
-      until_in_usec_ = -1;
-    }
-  }
-  if (until_in_usec_ >= 0) {
-    CHECK(until_in_usec_ >= NowInUsec());
-  }
-  return num_events;
-}
-
-}  // namespace test
-}  // namespace quic
diff --git a/net/tools/epoll_server/fake_epoll_server.h b/net/tools/epoll_server/fake_epoll_server.h
deleted file mode 100644
index 8605c43..0000000
--- a/net/tools/epoll_server/fake_epoll_server.h
+++ /dev/null
@@ -1,115 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef NET_TOOLS_EPOLL_SERVER_FAKE_EPOLL_SERVER_H_
-#define NET_TOOLS_EPOLL_SERVER_FAKE_EPOLL_SERVER_H_
-
-#include <stddef.h>
-#include <stdint.h>
-
-#include <unordered_map>
-#include <unordered_set>
-
-#include "base/logging.h"
-#include "base/macros.h"
-#include "net/tools/epoll_server/epoll_server.h"
-#include "testing/gmock/include/gmock/gmock.h"
-
-namespace quic {
-namespace test {
-
-// Unlike the full FakeEpollServer, this only lies about the time but lets
-// fd events operate normally.  Usefully when interacting with real backends
-// but wanting to skip forward in time to trigger timeouts.
-class FakeTimeEpollServer : public net::EpollServer {
- public:
-  FakeTimeEpollServer();
-  ~FakeTimeEpollServer() override;
-
-  // Replaces the net::EpollServer NowInUsec.
-  int64_t NowInUsec() const override;
-
-  void set_now_in_usec(int64_t nius) { now_in_usec_ = nius; }
-
-  // Advances the virtual 'now' by advancement_usec.
-  void AdvanceBy(int64_t advancement_usec) {
-    set_now_in_usec(NowInUsec() + advancement_usec);
-  }
-
-  // Advances the virtual 'now' by advancement_usec, and
-  // calls WaitForEventAndExecteCallbacks.
-  // Note that the WaitForEventsAndExecuteCallbacks invocation
-  // may cause NowInUs to advance beyond what was specified here.
-  // If that is not desired, use the AdvanceByExactly calls.
-  void AdvanceByAndWaitForEventsAndExecuteCallbacks(int64_t advancement_usec) {
-    AdvanceBy(advancement_usec);
-    WaitForEventsAndExecuteCallbacks();
-  }
-
- private:
-  int64_t now_in_usec_;
-
-  DISALLOW_COPY_AND_ASSIGN(FakeTimeEpollServer);
-};
-
-class FakeEpollServer : public FakeTimeEpollServer {
- public:  // type definitions
-  using EventQueue = std::unordered_multimap<int64_t, struct epoll_event>;
-
-  FakeEpollServer();
-  ~FakeEpollServer() override;
-
-  // time_in_usec is the time at which the event specified
-  // by 'ee' will be delivered. Note that it -is- possible
-  // to add an event for a time which has already been passed..
-  // .. upon the next time that the callbacks are invoked,
-  // all events which are in the 'past' will be delivered.
-  void AddEvent(int64_t time_in_usec, const struct epoll_event& ee) {
-    event_queue_.insert(std::make_pair(time_in_usec, ee));
-  }
-
-  // Advances the virtual 'now' by advancement_usec,
-  // and ensure that the next invocation of
-  // WaitForEventsAndExecuteCallbacks goes no farther than
-  // advancement_usec from the current time.
-  void AdvanceByExactly(int64_t advancement_usec) {
-    until_in_usec_ = NowInUsec() + advancement_usec;
-    set_now_in_usec(NowInUsec() + advancement_usec);
-  }
-
-  // As above, except calls WaitForEventsAndExecuteCallbacks.
-  void AdvanceByExactlyAndCallCallbacks(int64_t advancement_usec) {
-    AdvanceByExactly(advancement_usec);
-    WaitForEventsAndExecuteCallbacks();
-  }
-
-  std::unordered_set<AlarmCB*>::size_type NumberOfAlarms() const {
-    return all_alarms_.size();
-  }
-
- protected:  // functions
-  // These functions do nothing here, as we're not actually
-  // using the epoll_* syscalls.
-  void DelFD(int fd) const override {}
-  void AddFD(int fd, int event_mask) const override {}
-  void ModFD(int fd, int event_mask) const override {}
-
-  // Replaces the epoll_server's epoll_wait_impl.
-  int epoll_wait_impl(int epfd,
-                      struct epoll_event* events,
-                      int max_events,
-                      int timeout_in_ms) override;
-  void SetNonblocking(int fd) override {}
-
- private:  // members
-  EventQueue event_queue_;
-  int64_t until_in_usec_;
-
-  DISALLOW_COPY_AND_ASSIGN(FakeEpollServer);
-};
-
-}  // namespace test
-}  // namespace quic
-
-#endif  // NET_TOOLS_EPOLL_SERVER_FAKE_EPOLL_SERVER_H_
diff --git a/net/tools/epoll_server/platform/impl/epoll_address_test_utils_impl.h b/net/tools/epoll_server/platform/impl/epoll_address_test_utils_impl.h
new file mode 100644
index 0000000..378acd4
--- /dev/null
+++ b/net/tools/epoll_server/platform/impl/epoll_address_test_utils_impl.h
@@ -0,0 +1,18 @@
+// 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 NET_TOOLS_EPOLL_SERVER_PLATFORM_IMPL_EPOLL_ADDRESS_TEST_UTILS_IMPL_H_
+#define NET_TOOLS_EPOLL_SERVER_PLATFORM_IMPL_EPOLL_ADDRESS_TEST_UTILS_IMPL_H_
+
+#include <netinet/in.h>
+
+namespace epoll_server {
+
+int AddressFamilyUnderTestImpl() {
+  return AF_INET;
+}
+
+}  // namespace epoll_server
+
+#endif  // NET_TOOLS_EPOLL_SERVER_PLATFORM_IMPL_EPOLL_ADDRESS_TEST_UTILS_IMPL_H_
diff --git a/net/tools/epoll_server/platform/impl/epoll_bug_impl.h b/net/tools/epoll_server/platform/impl/epoll_bug_impl.h
new file mode 100644
index 0000000..696a960
--- /dev/null
+++ b/net/tools/epoll_server/platform/impl/epoll_bug_impl.h
@@ -0,0 +1,12 @@
+// 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 NET_TOOLS_EPOLL_SERVER_PLATFORM_IMPL_EPOLL_BUG_IMPL_H_
+#define NET_TOOLS_EPOLL_SERVER_PLATFORM_IMPL_EPOLL_BUG_IMPL_H_
+
+#include "net/tools/epoll_server/platform/impl/epoll_logging_impl.h"
+
+#define EPOLL_BUG_IMPL EPOLL_LOG_IMPL(DFATAL)
+
+#endif  // NET_TOOLS_EPOLL_SERVER_PLATFORM_IMPL_EPOLL_BUG_IMPL_H_
diff --git a/net/tools/epoll_server/platform/impl/epoll_expect_bug_impl.h b/net/tools/epoll_server/platform/impl/epoll_expect_bug_impl.h
new file mode 100644
index 0000000..44ba4c6
--- /dev/null
+++ b/net/tools/epoll_server/platform/impl/epoll_expect_bug_impl.h
@@ -0,0 +1,12 @@
+// 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 NET_TOOLS_EPOLL_SERVER_PLATFORM_IMPL_EPOLL_EXPECT_BUG_IMPL_H_
+#define NET_TOOLS_EPOLL_SERVER_PLATFORM_IMPL_EPOLL_EXPECT_BUG_IMPL_H_
+
+#include "net/test/gtest_util.h"
+
+#define EXPECT_EPOLL_BUG_IMPL EXPECT_DFATAL
+
+#endif  // NET_TOOLS_EPOLL_SERVER_PLATFORM_IMPL_EPOLL_EXPECT_BUG_IMPL_H_
diff --git a/net/tools/epoll_server/platform/impl/epoll_export_impl.h b/net/tools/epoll_server/platform/impl/epoll_export_impl.h
new file mode 100644
index 0000000..5a46662
--- /dev/null
+++ b/net/tools/epoll_server/platform/impl/epoll_export_impl.h
@@ -0,0 +1,13 @@
+// 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.
+
+#include "net/base/net_export.h"
+
+#ifndef NET_TOOLS_EPOLL_SERVER_PLATFORM_IMPL_EPOLL_EXPORT_IMPL_H_
+#define NET_TOOLS_EPOLL_SERVER_PLATFORM_IMPL_EPOLL_EXPORT_IMPL_H_
+
+#define EPOLL_EXPORT NET_EXPORT
+#define EPOLL_EXPORT_PRIVATE NET_EXPORT_PRIVATE
+
+#endif  // NET_TOOLS_EPOLL_SERVER_PLATFORM_IMPL_EPOLL_EXPORT_IMPL_H_
diff --git a/net/tools/epoll_server/platform/impl/epoll_logging_impl.h b/net/tools/epoll_server/platform/impl/epoll_logging_impl.h
new file mode 100644
index 0000000..9f9b8ab
--- /dev/null
+++ b/net/tools/epoll_server/platform/impl/epoll_logging_impl.h
@@ -0,0 +1,21 @@
+// 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 NET_TOOLS_EPOLL_SERVER_PLATFORM_IMPL_EPOLL_LOGGING_IMPL_H_
+#define NET_TOOLS_EPOLL_SERVER_PLATFORM_IMPL_EPOLL_LOGGING_IMPL_H_
+
+#include "base/logging.h"
+
+#define EPOLL_CHROMIUM_LOG_INFO VLOG(1)
+#define EPOLL_CHROMIUM_LOG_WARNING DLOG(WARNING)
+#define EPOLL_CHROMIUM_LOG_ERROR DLOG(ERROR)
+#define EPOLL_CHROMIUM_LOG_FATAL LOG(FATAL)
+#define EPOLL_CHROMIUM_LOG_DFATAL LOG(DFATAL)
+
+#define EPOLL_LOG_IMPL(severity) EPOLL_CHROMIUM_LOG_##severity
+#define EPOLL_VLOG_IMPL(verbose_level) VLOG(verbose_level)
+#define EPOLL_DVLOG_IMPL(verbose_level) DVLOG(verbose_level)
+#define EPOLL_PLOG_IMPL(severity) DVLOG(1)
+
+#endif  // NET_TOOLS_EPOLL_SERVER_PLATFORM_IMPL_EPOLL_LOGGING_IMPL_H_
diff --git a/net/tools/epoll_server/platform/impl/epoll_ptr_util_impl.h b/net/tools/epoll_server/platform/impl/epoll_ptr_util_impl.h
new file mode 100644
index 0000000..35bbea0
--- /dev/null
+++ b/net/tools/epoll_server/platform/impl/epoll_ptr_util_impl.h
@@ -0,0 +1,19 @@
+// 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.
+
+#include <memory>
+
+#ifndef NET_TOOLS_EPOLL_SERVER_PLATFORM_IMPL_EPOLL_PTR_UTIL_IMPL_H_
+#define NET_TOOLS_EPOLL_SERVER_PLATFORM_IMPL_EPOLL_PTR_UTIL_IMPL_H_
+
+namespace epoll_server {
+
+template <typename T, typename... Args>
+std::unique_ptr<T> EpollMakeUniqueImpl(Args&&... args) {
+  return std::make_unique<T>(std::forward<Args>(args)...);
+}
+
+}  // namespace epoll_server
+
+#endif  // NET_TOOLS_EPOLL_SERVER_PLATFORM_IMPL_EPOLL_PTR_UTIL_IMPL_H_
diff --git a/net/tools/epoll_server/platform/impl/epoll_test_impl.h b/net/tools/epoll_server/platform/impl/epoll_test_impl.h
new file mode 100644
index 0000000..a4f24b98
--- /dev/null
+++ b/net/tools/epoll_server/platform/impl/epoll_test_impl.h
@@ -0,0 +1,12 @@
+// 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 NET_TOOLS_EPOLL_SERVER_PLATFORM_IMPL_EPOLL_TEST_IMPL_H_
+#define NET_TOOLS_EPOLL_SERVER_PLATFORM_IMPL_EPOLL_TEST_IMPL_H_
+
+#include "testing/gtest/include/gtest/gtest.h"  // IWYU pragma: export
+
+using EpollTestImpl = ::testing::Test;
+
+#endif  // NET_TOOLS_EPOLL_SERVER_PLATFORM_IMPL_EPOLL_TEST_IMPL_H_
diff --git a/net/tools/epoll_server/platform/impl/epoll_thread_impl.h b/net/tools/epoll_server/platform/impl/epoll_thread_impl.h
new file mode 100644
index 0000000..d726d13
--- /dev/null
+++ b/net/tools/epoll_server/platform/impl/epoll_thread_impl.h
@@ -0,0 +1,22 @@
+// 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 NET_TOOLS_EPOLL_SERVER_PLATFORM_IMPL_EPOLL_THREAD_IMPL_H_
+#define NET_TOOLS_EPOLL_SERVER_PLATFORM_IMPL_EPOLL_THREAD_IMPL_H_
+
+#include "base/threading/simple_thread.h"
+
+namespace epoll_server {
+
+// A class representing a thread of execution in epoll_server.
+class EpollThreadImpl : public base::SimpleThread {
+ public:
+  EpollThreadImpl(const std::string& string) : base::SimpleThread(string) {}
+  EpollThreadImpl(const EpollThreadImpl&) = delete;
+  EpollThreadImpl& operator=(const EpollThreadImpl&) = delete;
+};
+
+}  // namespace epoll_server
+
+#endif  // NET_TOOLS_EPOLL_SERVER_PLATFORM_IMPL_EPOLL_THREAD_IMPL_H_
diff --git a/net/tools/epoll_server/platform/impl/epoll_time_impl.h b/net/tools/epoll_server/platform/impl/epoll_time_impl.h
new file mode 100644
index 0000000..b1c3d219
--- /dev/null
+++ b/net/tools/epoll_server/platform/impl/epoll_time_impl.h
@@ -0,0 +1,18 @@
+// 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 NET_TOOLS_EPOLL_SERVER_PLATFORM_IMPL_EPOLL_TIME_IMPL_H_
+#define NET_TOOLS_EPOLL_SERVER_PLATFORM_IMPL_EPOLL_TIME_IMPL_H_
+
+#include "base/time/time.h"
+
+namespace epoll_server {
+
+inline int64_t WallTimeNowInUsecImpl() {
+  return (base::Time::Now() - base::Time::UnixEpoch()).InMicroseconds();
+}
+
+}  // namespace epoll_server
+
+#endif  // NET_TOOLS_EPOLL_SERVER_PLATFORM_IMPL_EPOLL_TIME_IMPL_H_
diff --git a/remoting/base/chromium_url_request.cc b/remoting/base/chromium_url_request.cc
index 010f16f2..2ae424c 100644
--- a/remoting/base/chromium_url_request.cc
+++ b/remoting/base/chromium_url_request.cc
@@ -5,9 +5,9 @@
 #include "remoting/base/chromium_url_request.h"
 
 #include <memory>
+#include <utility>
 
 #include "base/bind.h"
-#include "base/callback_helpers.h"
 #include "net/base/load_flags.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 #include "services/network/public/cpp/simple_url_loader.h"
@@ -83,7 +83,7 @@
   }
 
   DCHECK(!on_result_callback_.is_null());
-  base::ResetAndReturn(&on_result_callback_).Run(result);
+  std::move(on_result_callback_).Run(result);
 }
 
 ChromiumUrlRequestFactory::ChromiumUrlRequestFactory(
diff --git a/remoting/base/gaia_oauth_client.cc b/remoting/base/gaia_oauth_client.cc
index 1274347..c85ff737 100644
--- a/remoting/base/gaia_oauth_client.cc
+++ b/remoting/base/gaia_oauth_client.cc
@@ -4,7 +4,8 @@
 
 #include "remoting/base/gaia_oauth_client.h"
 
-#include "base/callback_helpers.h"
+#include <utility>
+
 #include "base/logging.h"
 
 namespace {
@@ -57,7 +58,7 @@
 
 void GaiaOAuthClient::SendResponse(const std::string& user_email,
                                    const std::string& refresh_token) {
-  base::ResetAndReturn(&on_done_).Run(user_email, refresh_token);
+  std::move(on_done_).Run(user_email, refresh_token);
 
   // Process the next request in the queue.
   if (pending_requests_.size()) {
diff --git a/remoting/client/plugin/chromoting_instance.cc b/remoting/client/plugin/chromoting_instance.cc
index 256e235..a8182b7 100644
--- a/remoting/client/plugin/chromoting_instance.cc
+++ b/remoting/client/plugin/chromoting_instance.cc
@@ -14,7 +14,6 @@
 
 #include "base/bind.h"
 #include "base/callback.h"
-#include "base/callback_helpers.h"
 #include "base/json/json_reader.h"
 #include "base/json/json_writer.h"
 #include "base/lazy_instance.h"
@@ -906,7 +905,7 @@
     return;
   }
   if (!secret_fetched_callback_.is_null()) {
-    base::ResetAndReturn(&secret_fetched_callback_).Run(pin);
+    std::move(secret_fetched_callback_).Run(pin);
   } else {
     LOG(WARNING) << "Ignored OnPinFetched received without a pending fetch.";
   }
@@ -922,8 +921,7 @@
     return;
   }
   if (!third_party_token_fetched_callback_.is_null()) {
-    base::ResetAndReturn(&third_party_token_fetched_callback_)
-        .Run(token, shared_secret);
+    std::move(third_party_token_fetched_callback_).Run(token, shared_secret);
   } else {
     LOG(WARNING) << "Ignored OnThirdPartyTokenFetched without a pending fetch.";
   }
diff --git a/remoting/client/plugin/pepper_url_request.cc b/remoting/client/plugin/pepper_url_request.cc
index 1555f5a..97fdb89 100644
--- a/remoting/client/plugin/pepper_url_request.cc
+++ b/remoting/client/plugin/pepper_url_request.cc
@@ -5,8 +5,8 @@
 #include "remoting/client/plugin/pepper_url_request.h"
 
 #include <memory>
+#include <utility>
 
-#include "base/callback_helpers.h"
 #include "base/logging.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
 #include "ppapi/cpp/url_response_info.h"
@@ -66,7 +66,7 @@
 
   if (result < 0) {
     LOG(WARNING) << "pp::URLLoader for " << url_ << " failed: " << result;
-    base::ResetAndReturn(&on_result_callback_).Run(Result::Failed());
+    std::move(on_result_callback_).Run(Result::Failed());
     return;
   }
 
@@ -89,7 +89,7 @@
   if (result < 0) {
     LOG(WARNING) << "Failed to read HTTP response body when fetching "
                  << url_ << ", error: " << result;
-    base::ResetAndReturn(&on_result_callback_).Run(Result::Failed());
+    std::move(on_result_callback_).Run(Result::Failed());
     return;
   }
 
@@ -104,7 +104,7 @@
     return;
   }
 
-  base::ResetAndReturn(&on_result_callback_)
+  std::move(on_result_callback_)
       .Run(Result(url_loader_.GetResponseInfo().GetStatusCode(), response_));
 }
 
diff --git a/remoting/host/chromeos/message_box.cc b/remoting/host/chromeos/message_box.cc
index c3dc703..1378dff 100644
--- a/remoting/host/chromeos/message_box.cc
+++ b/remoting/host/chromeos/message_box.cc
@@ -4,6 +4,8 @@
 
 #include "remoting/host/chromeos/message_box.h"
 
+#include <utility>
+
 #include "base/macros.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/views/controls/message_box_view.h"
@@ -99,14 +101,14 @@
 
 bool MessageBox::Core::Accept() {
   if (!result_callback_.is_null()) {
-    base::ResetAndReturn(&result_callback_).Run(OK);
+    std::move(result_callback_).Run(OK);
   }
   return true /* close the window*/;
 }
 
 bool MessageBox::Core::Cancel() {
   if (!result_callback_.is_null()) {
-    base::ResetAndReturn(&result_callback_).Run(CANCEL);
+    std::move(result_callback_).Run(CANCEL);
   }
   return true /* close the window*/;
 }
diff --git a/remoting/host/daemon_process.cc b/remoting/host/daemon_process.cc
index 5cba93f..21b6743 100644
--- a/remoting/host/daemon_process.cc
+++ b/remoting/host/daemon_process.cc
@@ -6,10 +6,10 @@
 
 #include <algorithm>
 #include <string>
+#include <utility>
 
 #include "base/bind.h"
 #include "base/bind_helpers.h"
-#include "base/callback_helpers.h"
 #include "base/command_line.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
@@ -287,7 +287,7 @@
   OnWorkerProcessStopped();
 
   if (!stopped_callback_.is_null()) {
-    base::ResetAndReturn(&stopped_callback_).Run();
+    std::move(stopped_callback_).Run();
   }
 }
 
diff --git a/remoting/host/dns_blackhole_checker.cc b/remoting/host/dns_blackhole_checker.cc
index 8758ebf8..264ec5b 100644
--- a/remoting/host/dns_blackhole_checker.cc
+++ b/remoting/host/dns_blackhole_checker.cc
@@ -4,8 +4,9 @@
 
 #include "remoting/host/dns_blackhole_checker.h"
 
+#include <utility>
+
 #include "base/bind.h"
-#include "base/callback_helpers.h"
 #include "remoting/base/logging.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 #include "services/network/public/cpp/simple_url_loader.h"
@@ -45,7 +46,7 @@
              << ")";
   }
   url_loader_.reset();
-  base::ResetAndReturn(&callback_).Run(allow);
+  std::move(callback_).Run(allow);
 }
 
 void DnsBlackholeChecker::CheckForDnsBlackhole(
diff --git a/remoting/host/gcd_rest_client.cc b/remoting/host/gcd_rest_client.cc
index cbd3a42..0c31bc3 100644
--- a/remoting/host/gcd_rest_client.cc
+++ b/remoting/host/gcd_rest_client.cc
@@ -9,7 +9,6 @@
 #include <utility>
 
 #include "base/bind.h"
-#include "base/callback_helpers.h"
 #include "base/json/json_writer.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/default_clock.h"
@@ -146,7 +145,7 @@
   DCHECK(HasPendingRequest());
   resource_request_.reset();
   url_loader_.reset();
-  base::ResetAndReturn(&callback_).Run(result);
+  std::move(callback_).Run(result);
 }
 
 void GcdRestClient::OnURLLoadComplete(
diff --git a/remoting/host/gcd_state_updater.cc b/remoting/host/gcd_state_updater.cc
index 5714c14..3fea9009 100644
--- a/remoting/host/gcd_state_updater.cc
+++ b/remoting/host/gcd_state_updater.cc
@@ -9,7 +9,6 @@
 #include <utility>
 
 #include "base/bind.h"
-#include "base/callback_helpers.h"
 #include "base/strings/stringize_macros.h"
 #include "base/time/time.h"
 #include "base/values.h"
@@ -91,12 +90,12 @@
   if (result == GcdRestClient::SUCCESS) {
     if (!on_update_successful_callback_.is_null()) {
       on_unknown_host_id_error_.Reset();
-      base::ResetAndReturn(&on_update_successful_callback_).Run();
+      std::move(on_update_successful_callback_).Run();
     }
   } else if (result == GcdRestClient::NO_SUCH_HOST) {
     if (!on_unknown_host_id_error_.is_null()) {
       on_update_successful_callback_.Reset();
-      base::ResetAndReturn(&on_unknown_host_id_error_).Run();
+      std::move(on_unknown_host_id_error_).Run();
     }
   } else {
     // For any other error, do nothing since there's no way to handle
diff --git a/remoting/host/it2me/it2me_confirmation_dialog_chromeos.cc b/remoting/host/it2me/it2me_confirmation_dialog_chromeos.cc
index 0c040b0..4e1b290 100644
--- a/remoting/host/it2me/it2me_confirmation_dialog_chromeos.cc
+++ b/remoting/host/it2me/it2me_confirmation_dialog_chromeos.cc
@@ -4,10 +4,10 @@
 
 #include <memory>
 #include <string>
+#include <utility>
 
 #include "base/bind.h"
 #include "base/callback.h"
-#include "base/callback_helpers.h"
 #include "base/i18n/message_formatter.h"
 #include "base/macros.h"
 #include "base/strings/utf_string_conversions.h"
@@ -60,8 +60,8 @@
 
 void It2MeConfirmationDialogChromeOS::OnMessageBoxResult(
     MessageBox::Result result) {
-  base::ResetAndReturn(&callback_).Run(result == MessageBox::OK ?
-                                       Result::OK : Result::CANCEL);
+  std::move(callback_).Run(result == MessageBox::OK ? Result::OK
+                                                    : Result::CANCEL);
 }
 
 std::unique_ptr<It2MeConfirmationDialog>
diff --git a/remoting/host/it2me/it2me_confirmation_dialog_linux.cc b/remoting/host/it2me/it2me_confirmation_dialog_linux.cc
index 4d586c4..69c2b9c0 100644
--- a/remoting/host/it2me/it2me_confirmation_dialog_linux.cc
+++ b/remoting/host/it2me/it2me_confirmation_dialog_linux.cc
@@ -6,10 +6,10 @@
 
 #include <memory>
 #include <string>
+#include <utility>
 
 #include "base/bind.h"
 #include "base/callback.h"
-#include "base/callback_helpers.h"
 #include "base/i18n/message_formatter.h"
 #include "base/location.h"
 #include "base/logging.h"
@@ -148,8 +148,8 @@
   DCHECK(result_callback_);
 
   Hide();
-  base::ResetAndReturn(&result_callback_).Run(
-      (response_id == GTK_RESPONSE_OK) ? Result::OK : Result::CANCEL);
+  std::move(result_callback_)
+      .Run((response_id == GTK_RESPONSE_OK) ? Result::OK : Result::CANCEL);
 }
 
 }  // namespace
diff --git a/remoting/host/it2me/it2me_confirmation_dialog_mac.mm b/remoting/host/it2me/it2me_confirmation_dialog_mac.mm
index 5c697b3..6234c39 100644
--- a/remoting/host/it2me/it2me_confirmation_dialog_mac.mm
+++ b/remoting/host/it2me/it2me_confirmation_dialog_mac.mm
@@ -8,9 +8,10 @@
 
 #import <Cocoa/Cocoa.h>
 
+#include <utility>
+
 #include "base/bind.h"
 #include "base/callback.h"
-#include "base/callback_helpers.h"
 #include "base/i18n/message_formatter.h"
 #include "base/location.h"
 #include "base/mac/scoped_nsautorelease_pool.h"
@@ -108,7 +109,7 @@
   }
 
   if (result_callback_) {
-    base::ResetAndReturn(&result_callback_).Run(result);
+    std::move(result_callback_).Run(result);
   }
 }
 
@@ -176,7 +177,7 @@
 - (void)onCancel:(id)sender {
   [self hide];
   if (dialog_action_callback_) {
-    base::ResetAndReturn(&dialog_action_callback_)
+    std::move(dialog_action_callback_)
         .Run(remoting::It2MeConfirmationDialog::Result::CANCEL);
   }
 }
@@ -184,7 +185,7 @@
 - (void)onAccept:(id)sender {
   [self hide];
   if (dialog_action_callback_) {
-    base::ResetAndReturn(&dialog_action_callback_)
+    std::move(dialog_action_callback_)
         .Run(remoting::It2MeConfirmationDialog::Result::OK);
   }
 }
diff --git a/remoting/host/it2me/it2me_confirmation_dialog_proxy.cc b/remoting/host/it2me/it2me_confirmation_dialog_proxy.cc
index fa858891..0e4cfc1 100644
--- a/remoting/host/it2me/it2me_confirmation_dialog_proxy.cc
+++ b/remoting/host/it2me/it2me_confirmation_dialog_proxy.cc
@@ -7,7 +7,6 @@
 #include <utility>
 
 #include "base/bind.h"
-#include "base/callback_helpers.h"
 #include "base/location.h"
 #include "base/macros.h"
 #include "base/threading/thread_task_runner_handle.h"
@@ -105,7 +104,7 @@
 void It2MeConfirmationDialogProxy::ReportResult(
     It2MeConfirmationDialog::Result result) {
   DCHECK(core_->caller_task_runner()->BelongsToCurrentThread());
-  base::ResetAndReturn(&callback_).Run(result);
+  std::move(callback_).Run(result);
 }
 
 }  // namespace remoting
diff --git a/remoting/host/it2me/it2me_host_unittest.cc b/remoting/host/it2me/it2me_host_unittest.cc
index 3ffb131..d375e90f 100644
--- a/remoting/host/it2me/it2me_host_unittest.cc
+++ b/remoting/host/it2me/it2me_host_unittest.cc
@@ -10,7 +10,6 @@
 
 #include "base/bind.h"
 #include "base/callback.h"
-#include "base/callback_helpers.h"
 #include "base/location.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
@@ -340,7 +339,7 @@
 
   if (state_change_callback_) {
     base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE, base::ResetAndReturn(&state_change_callback_));
+        FROM_HERE, std::move(state_change_callback_));
   }
 }
 
diff --git a/remoting/host/it2me/it2me_native_messaging_host.cc b/remoting/host/it2me/it2me_native_messaging_host.cc
index 286a63bd..0eed0e92 100644
--- a/remoting/host/it2me/it2me_native_messaging_host.cc
+++ b/remoting/host/it2me/it2me_native_messaging_host.cc
@@ -10,7 +10,6 @@
 
 #include "base/bind.h"
 #include "base/callback.h"
-#include "base/callback_helpers.h"
 #include "base/json/json_reader.h"
 #include "base/json/json_writer.h"
 #include "base/single_thread_task_runner.h"
@@ -488,7 +487,7 @@
     // may not be ideal, but is still functional.
     needs_elevation_ = needs_elevation_ && allow_elevated_host;
     if (pending_connect_) {
-      base::ResetAndReturn(&pending_connect_).Run();
+      std::move(pending_connect_).Run();
     }
   }
 
@@ -515,7 +514,7 @@
     // is one, but otherwise do nothing. The policy error will be sent when a
     // connection is made; doing so beforehand would break assumptions made by
     // the Chrome app.
-    base::ResetAndReturn(&pending_connect_).Run();
+    std::move(pending_connect_).Run();
   }
 }
 
diff --git a/remoting/host/register_support_host_request.cc b/remoting/host/register_support_host_request.cc
index 4aed339c..543567c 100644
--- a/remoting/host/register_support_host_request.cc
+++ b/remoting/host/register_support_host_request.cc
@@ -6,8 +6,9 @@
 
 #include <stdint.h>
 
+#include <utility>
+
 #include "base/bind.h"
-#include "base/callback_helpers.h"
 #include "base/logging.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/stringize_macros.h"
@@ -242,7 +243,7 @@
   signal_strategy_->RemoveListener(this);
   signal_strategy_ = nullptr;
 
-  base::ResetAndReturn(&callback_).Run(support_id, lifetime, error_code);
+  std::move(callback_).Run(support_id, lifetime, error_code);
 }
 
 }  // namespace remoting
diff --git a/remoting/host/security_key/security_key_ipc_client.cc b/remoting/host/security_key/security_key_ipc_client.cc
index 5a91879..eb4f5230 100644
--- a/remoting/host/security_key/security_key_ipc_client.cc
+++ b/remoting/host/security_key/security_key_ipc_client.cc
@@ -5,10 +5,10 @@
 #include "remoting/host/security_key/security_key_ipc_client.h"
 
 #include <string>
+#include <utility>
 
 #include "base/bind.h"
 #include "base/callback.h"
-#include "base/callback_helpers.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "ipc/ipc_channel.h"
 #include "ipc/ipc_listener.h"
@@ -115,7 +115,7 @@
   DWORD peer_session_id;
   if (!ProcessIdToSessionId(peer_pid, &peer_session_id)) {
     PLOG(ERROR) << "ProcessIdToSessionId failed";
-    base::ResetAndReturn(&connection_error_callback_).Run();
+    std::move(connection_error_callback_).Run();
     return;
   }
 
@@ -123,7 +123,7 @@
     LOG(ERROR)
         << "Cannot establish connection with IPC server running in session: "
         << peer_session_id;
-    base::ResetAndReturn(&connection_error_callback_).Run();
+    std::move(connection_error_callback_).Run();
     return;
   }
 #endif  // defined(OS_WIN)
@@ -133,7 +133,7 @@
   DCHECK(thread_checker_.CalledOnValidThread());
 
   if (connection_error_callback_) {
-    base::ResetAndReturn(&connection_error_callback_).Run();
+    std::move(connection_error_callback_).Run();
   }
 }
 
@@ -142,11 +142,11 @@
   DCHECK(thread_checker_.CalledOnValidThread());
 
   if (!response_data.empty()) {
-    base::ResetAndReturn(&response_callback_).Run(response_data);
+    std::move(response_callback_).Run(response_data);
   } else {
     LOG(ERROR) << "Invalid response received";
     if (connection_error_callback_) {
-      base::ResetAndReturn(&connection_error_callback_).Run();
+      std::move(connection_error_callback_).Run();
     }
   }
 }
@@ -157,12 +157,12 @@
   if (!connected_callback_) {
     LOG(ERROR) << "Unexpected ConnectionReady message received.";
     if (connection_error_callback_) {
-      base::ResetAndReturn(&connection_error_callback_).Run();
+      std::move(connection_error_callback_).Run();
     }
     return;
   }
 
-  base::ResetAndReturn(&connected_callback_).Run(/*connection_usable=*/true);
+  std::move(connected_callback_).Run(/*connection_usable=*/true);
 }
 
 void SecurityKeyIpcClient::OnInvalidSession() {
@@ -171,12 +171,12 @@
   if (!connected_callback_) {
     LOG(ERROR) << "Unexpected InvalidSession message received.";
     if (connection_error_callback_) {
-      base::ResetAndReturn(&connection_error_callback_).Run();
+      std::move(connection_error_callback_).Run();
     }
     return;
   }
 
-  base::ResetAndReturn(&connected_callback_).Run(/*connection_usable=*/false);
+  std::move(connected_callback_).Run(/*connection_usable=*/false);
 }
 
 void SecurityKeyIpcClient::ConnectToIpcChannel() {
@@ -187,7 +187,7 @@
 
   if (!channel_handle_.is_valid() && !CheckForSecurityKeyIpcServerChannel()) {
     if (connection_error_callback_) {
-      base::ResetAndReturn(&connection_error_callback_).Run();
+      std::move(connection_error_callback_).Run();
     }
     return;
   }
@@ -201,7 +201,7 @@
   ipc_channel_.reset();
 
   if (connection_error_callback_) {
-    base::ResetAndReturn(&connection_error_callback_).Run();
+    std::move(connection_error_callback_).Run();
   }
 }
 
diff --git a/remoting/host/security_key/security_key_ipc_server_impl.cc b/remoting/host/security_key/security_key_ipc_server_impl.cc
index 90cdcc37..5a02d9ac 100644
--- a/remoting/host/security_key/security_key_ipc_server_impl.cc
+++ b/remoting/host/security_key/security_key_ipc_server_impl.cc
@@ -7,10 +7,10 @@
 #include <cstdint>
 #include <memory>
 #include <string>
+#include <utility>
 
 #include "base/bind.h"
 #include "base/callback.h"
-#include "base/callback_helpers.h"
 #include "base/location.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/threading/thread_checker.h"
@@ -147,7 +147,7 @@
   DCHECK(thread_checker_.CalledOnValidThread());
 
   if (!connect_callback_.is_null()) {
-    base::ResetAndReturn(&connect_callback_).Run();
+    std::move(connect_callback_).Run();
   }
 
 #if defined(OS_WIN)
@@ -186,11 +186,11 @@
   CloseChannel();
 
   if (!connect_callback_.is_null()) {
-    base::ResetAndReturn(&connect_callback_).Run();
+    std::move(connect_callback_).Run();
   }
   if (!done_callback_.is_null()) {
     // Note: This callback may result in this object being torn down.
-    base::ResetAndReturn(&done_callback_).Run();
+    std::move(done_callback_).Run();
   }
 }
 
diff --git a/remoting/host/security_key/security_key_message_handler.cc b/remoting/host/security_key/security_key_message_handler.cc
index 85f725a..3b9b907d 100644
--- a/remoting/host/security_key/security_key_message_handler.cc
+++ b/remoting/host/security_key/security_key_message_handler.cc
@@ -10,7 +10,6 @@
 
 #include "base/bind.h"
 #include "base/callback.h"
-#include "base/callback_helpers.h"
 #include "remoting/host/security_key/security_key_ipc_client.h"
 #include "remoting/host/security_key/security_key_ipc_constants.h"
 #include "remoting/host/security_key/security_key_message_reader_impl.h"
@@ -185,7 +184,7 @@
   reader_.reset();
 
   if (!error_callback_.is_null()) {
-    base::ResetAndReturn(&error_callback_).Run();
+    std::move(error_callback_).Run();
   }
 }
 
diff --git a/remoting/host/security_key/security_key_socket.cc b/remoting/host/security_key/security_key_socket.cc
index 2d51dfd..f30e72b 100644
--- a/remoting/host/security_key/security_key_socket.cc
+++ b/remoting/host/security_key/security_key_socket.cc
@@ -7,7 +7,6 @@
 #include <utility>
 
 #include "base/bind.h"
-#include "base/callback_helpers.h"
 #include "base/stl_util.h"
 #include "base/timer/timer.h"
 #include "net/base/io_buffer.h"
@@ -152,7 +151,7 @@
       socket_read_error_ = true;
     }
     waiting_for_request_ = false;
-    base::ResetAndReturn(&request_received_callback_).Run();
+    std::move(request_received_callback_).Run();
     return;
   }
 
@@ -165,7 +164,7 @@
                        read_buffer_->data() + result);
   if (IsRequestComplete()) {
     waiting_for_request_ = false;
-    base::ResetAndReturn(&request_received_callback_).Run();
+    std::move(request_received_callback_).Run();
     return;
   }
 
diff --git a/remoting/host/setup/host_starter.cc b/remoting/host/setup/host_starter.cc
index da166d52..7f566c5 100644
--- a/remoting/host/setup/host_starter.cc
+++ b/remoting/host/setup/host_starter.cc
@@ -8,7 +8,6 @@
 #include <utility>
 
 #include "base/bind.h"
-#include "base/callback_helpers.h"
 #include "base/guid.h"
 #include "base/location.h"
 #include "base/memory/ptr_util.h"
@@ -188,7 +187,7 @@
     service_client_->UnregisterHost(host_id_, access_token_, this);
     return;
   }
-  base::ResetAndReturn(&on_done_).Run(START_COMPLETE);
+  std::move(on_done_).Run(START_COMPLETE);
 }
 
 void HostStarter::OnOAuthError() {
@@ -201,8 +200,7 @@
     LOG(ERROR) << "OAuth error occurred when unregistering host.";
   }
 
-  base::ResetAndReturn(&on_done_)
-      .Run(unregistering_host_ ? START_ERROR : OAUTH_ERROR);
+  std::move(on_done_).Run(unregistering_host_ ? START_ERROR : OAUTH_ERROR);
 }
 
 void HostStarter::OnNetworkError(int response_code) {
@@ -216,8 +214,7 @@
     LOG(ERROR) << "Network error occurred when unregistering host.";
   }
 
-  base::ResetAndReturn(&on_done_)
-      .Run(unregistering_host_ ? START_ERROR : NETWORK_ERROR);
+  std::move(on_done_).Run(unregistering_host_ ? START_ERROR : NETWORK_ERROR);
 }
 
 void HostStarter::OnHostUnregistered() {
@@ -226,7 +223,7 @@
         FROM_HERE, base::BindOnce(&HostStarter::OnHostUnregistered, weak_ptr_));
     return;
   }
-  base::ResetAndReturn(&on_done_).Run(START_ERROR);
+  std::move(on_done_).Run(START_ERROR);
 }
 
 }  // namespace remoting
diff --git a/remoting/protocol/fake_authenticator.cc b/remoting/protocol/fake_authenticator.cc
index abac8a7..5c844c9 100644
--- a/remoting/protocol/fake_authenticator.cc
+++ b/remoting/protocol/fake_authenticator.cc
@@ -9,7 +9,6 @@
 
 #include "base/base64.h"
 #include "base/bind.h"
-#include "base/callback_helpers.h"
 #include "base/rand_util.h"
 #include "base/strings/string_number_conversions.h"
 #include "net/base/io_buffer.h"
@@ -90,7 +89,7 @@
 void FakeChannelAuthenticator::CallDoneCallback() {
   if (result_ != net::OK)
     socket_.reset();
-  base::ResetAndReturn(&done_callback_).Run(result_, std::move(socket_));
+  std::move(done_callback_).Run(result_, std::move(socket_));
 }
 
 FakeAuthenticator::Config::Config() = default;
@@ -120,7 +119,7 @@
 }
 
 void FakeAuthenticator::Resume() {
-  base::ResetAndReturn(&resume_closure_).Run();
+  std::move(resume_closure_).Run();
 }
 
 Authenticator::State FakeAuthenticator::state() const {
diff --git a/remoting/protocol/fake_datagram_socket.cc b/remoting/protocol/fake_datagram_socket.cc
index b81619c..a7bd322 100644
--- a/remoting/protocol/fake_datagram_socket.cc
+++ b/remoting/protocol/fake_datagram_socket.cc
@@ -7,7 +7,6 @@
 #include <utility>
 
 #include "base/bind.h"
-#include "base/callback_helpers.h"
 #include "base/location.h"
 #include "base/single_thread_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
@@ -39,7 +38,7 @@
     int result = CopyReadData(read_buffer_.get(), read_buffer_size_);
     read_buffer_ = nullptr;
 
-    base::ResetAndReturn(&read_callback_).Run(result);
+    std::move(read_callback_).Run(result);
   }
 }
 
diff --git a/remoting/protocol/http_ice_config_request.cc b/remoting/protocol/http_ice_config_request.cc
index 241b9055..4e5c076a 100644
--- a/remoting/protocol/http_ice_config_request.cc
+++ b/remoting/protocol/http_ice_config_request.cc
@@ -4,8 +4,9 @@
 
 #include "remoting/protocol/http_ice_config_request.h"
 
+#include <utility>
+
 #include "base/bind.h"
-#include "base/callback_helpers.h"
 #include "base/values.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
 #include "remoting/protocol/ice_config.h"
@@ -87,7 +88,7 @@
                                         const std::string& access_token) {
   if (status != OAuthTokenGetter::SUCCESS) {
     LOG(ERROR) << "Failed to get OAuth token for IceConfig request.";
-    base::ResetAndReturn(&on_ice_config_callback_).Run(IceConfig());
+    std::move(on_ice_config_callback_).Run(IceConfig());
     return;
   }
 
@@ -106,13 +107,13 @@
   if (result.status != -1 && result.status != 200) {
     LOG(ERROR) << "Received status code " << result.status << " from " << url_
                << ": " << result.response_body;
-    base::ResetAndReturn(&on_ice_config_callback_).Run(IceConfig());
+    std::move(on_ice_config_callback_).Run(IceConfig());
     return;
   }
 
   if (!result.success) {
     LOG(ERROR) << "Failed to fetch " << url_;
-    base::ResetAndReturn(&on_ice_config_callback_).Run(IceConfig());
+    std::move(on_ice_config_callback_).Run(IceConfig());
     return;
   }
 
@@ -122,7 +123,7 @@
                << result.response_body;
   }
 
-  base::ResetAndReturn(&on_ice_config_callback_).Run(ice_config);
+  std::move(on_ice_config_callback_).Run(ice_config);
 }
 
 }  // namespace protocol
diff --git a/remoting/protocol/ice_transport_channel.cc b/remoting/protocol/ice_transport_channel.cc
index 2870699..48efe98f7 100644
--- a/remoting/protocol/ice_transport_channel.cc
+++ b/remoting/protocol/ice_transport_channel.cc
@@ -9,7 +9,6 @@
 
 #include "base/bind.h"
 #include "base/callback.h"
-#include "base/callback_helpers.h"
 #include "base/single_thread_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "jingle/glue/utils.h"
@@ -142,7 +141,7 @@
       new TransportChannelSocketAdapter(channel_.get()));
   socket->SetOnDestroyedCallback(base::Bind(
       &IceTransportChannel::OnChannelDestroyed, base::Unretained(this)));
-  base::ResetAndReturn(&callback_).Run(std::move(socket));
+  std::move(callback_).Run(std::move(socket));
 }
 
 void IceTransportChannel::SetRemoteCredentials(const std::string& ufrag,
diff --git a/remoting/protocol/stream_message_pipe_adapter.cc b/remoting/protocol/stream_message_pipe_adapter.cc
index 54dc5f1..7bbc79f 100644
--- a/remoting/protocol/stream_message_pipe_adapter.cc
+++ b/remoting/protocol/stream_message_pipe_adapter.cc
@@ -8,7 +8,6 @@
 #include <utility>
 
 #include "base/bind.h"
-#include "base/callback_helpers.h"
 #include "net/base/net_errors.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
 #include "remoting/base/buffered_socket_writer.h"
@@ -90,7 +89,7 @@
   if (error == 0) {
     event_handler_->OnMessagePipeClosed();
   } else if (error_callback_) {
-    base::ResetAndReturn(&error_callback_).Run(error);
+    std::move(error_callback_).Run(error);
   }
 }
 
diff --git a/remoting/protocol/third_party_authenticator_unittest.cc b/remoting/protocol/third_party_authenticator_unittest.cc
index 38f81273..c4775f4 100644
--- a/remoting/protocol/third_party_authenticator_unittest.cc
+++ b/remoting/protocol/third_party_authenticator_unittest.cc
@@ -5,7 +5,6 @@
 #include <utility>
 
 #include "base/bind.h"
-#include "base/callback_helpers.h"
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
 #include "base/run_loop.h"
@@ -60,7 +59,7 @@
     void OnTokenFetched(const std::string& token,
                         const std::string& shared_secret) {
       ASSERT_FALSE(on_token_fetched_.is_null());
-      base::ResetAndReturn(&on_token_fetched_).Run(token, shared_secret);
+      std::move(on_token_fetched_).Run(token, shared_secret);
     }
 
    private:
@@ -84,7 +83,7 @@
 
     void OnTokenValidated(const std::string& shared_secret) {
       ASSERT_FALSE(on_token_validated_.is_null());
-      base::ResetAndReturn(&on_token_validated_).Run(shared_secret);
+      std::move(on_token_validated_).Run(shared_secret);
     }
 
     const GURL& token_url() const override { return token_url_; }
diff --git a/remoting/protocol/webrtc_transport.cc b/remoting/protocol/webrtc_transport.cc
index 48f2e5b..16ad0bce 100644
--- a/remoting/protocol/webrtc_transport.cc
+++ b/remoting/protocol/webrtc_transport.cc
@@ -10,7 +10,6 @@
 
 #include "base/base64.h"
 #include "base/bind.h"
-#include "base/callback_helpers.h"
 #include "base/command_line.h"
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
@@ -155,11 +154,10 @@
         result_callback);
   }
   void OnSuccess(webrtc::SessionDescriptionInterface* desc) override {
-    base::ResetAndReturn(&result_callback_)
-        .Run(base::WrapUnique(desc), std::string());
+    std::move(result_callback_).Run(base::WrapUnique(desc), std::string());
   }
   void OnFailure(const std::string& error) override {
-    base::ResetAndReturn(&result_callback_).Run(nullptr, error);
+    std::move(result_callback_).Run(nullptr, error);
   }
 
  protected:
@@ -189,11 +187,11 @@
   }
 
   void OnSuccess() override {
-    base::ResetAndReturn(&result_callback_).Run(true, std::string());
+    std::move(result_callback_).Run(true, std::string());
   }
 
   void OnFailure(const std::string& error) override {
-    base::ResetAndReturn(&result_callback_).Run(false, error);
+    std::move(result_callback_).Run(false, error);
   }
 
  protected:
@@ -221,7 +219,7 @@
 
   void OnStatsDelivered(
       const rtc::scoped_refptr<const webrtc::RTCStatsReport>& report) override {
-    base::ResetAndReturn(&result_callback_).Run(report);
+    std::move(result_callback_).Run(report);
   }
 
  protected:
diff --git a/remoting/signaling/iq_sender.cc b/remoting/signaling/iq_sender.cc
index 37c2ee6..94ffa39 100644
--- a/remoting/signaling/iq_sender.cc
+++ b/remoting/signaling/iq_sender.cc
@@ -8,7 +8,6 @@
 #include <utility>
 
 #include "base/bind.h"
-#include "base/callback_helpers.h"
 #include "base/location.h"
 #include "base/logging.h"
 #include "base/single_thread_task_runner.h"
@@ -151,7 +150,7 @@
 
 void IqRequest::CallCallback(const jingle_xmpp::XmlElement* stanza) {
   if (!callback_.is_null())
-    base::ResetAndReturn(&callback_).Run(this, stanza);
+    std::move(callback_).Run(this, stanza);
 }
 
 void IqRequest::OnTimeout() {
diff --git a/remoting/test/access_token_fetcher.cc b/remoting/test/access_token_fetcher.cc
index c8c214bc..02d028da 100644
--- a/remoting/test/access_token_fetcher.cc
+++ b/remoting/test/access_token_fetcher.cc
@@ -5,10 +5,10 @@
 #include "remoting/test/access_token_fetcher.h"
 
 #include <string>
+#include <utility>
 #include <vector>
 
 #include "base/bind.h"
-#include "base/callback_helpers.h"
 #include "base/logging.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "google_apis/gaia/gaia_constants.h"
@@ -164,8 +164,7 @@
     VLOG(1) << "Access Token has been validated";
   }
 
-  base::ResetAndReturn(&access_token_callback_)
-      .Run(access_token_, refresh_token_);
+  std::move(access_token_callback_).Run(access_token_, refresh_token_);
 }
 
 void AccessTokenFetcher::OnOAuthError() {
@@ -174,8 +173,7 @@
   access_token_.clear();
   refresh_token_.clear();
 
-  base::ResetAndReturn(&access_token_callback_)
-      .Run(access_token_, refresh_token_);
+  std::move(access_token_callback_).Run(access_token_, refresh_token_);
 }
 
 void AccessTokenFetcher::OnNetworkError(int response_code) {
@@ -185,8 +183,7 @@
   access_token_.clear();
   refresh_token_.clear();
 
-  base::ResetAndReturn(&access_token_callback_)
-      .Run(access_token_, refresh_token_);
+  std::move(access_token_callback_).Run(access_token_, refresh_token_);
 }
 
 void AccessTokenFetcher::ValidateAccessToken() {
diff --git a/remoting/test/host_list_fetcher.cc b/remoting/test/host_list_fetcher.cc
index 1fa28706..08f893a 100644
--- a/remoting/test/host_list_fetcher.cc
+++ b/remoting/test/host_list_fetcher.cc
@@ -4,8 +4,9 @@
 
 #include "remoting/test/host_list_fetcher.h"
 
+#include <utility>
+
 #include "base/bind.h"
-#include "base/callback_helpers.h"
 #include "base/json/json_reader.h"
 #include "base/logging.h"
 #include "base/threading/thread_task_runner_handle.h"
@@ -104,7 +105,7 @@
   if (!ProcessResponse(&hostlist)) {
     hostlist.clear();
   }
-  base::ResetAndReturn(&hostlist_callback_).Run(hostlist);
+  std::move(hostlist_callback_).Run(hostlist);
 }
 
 }  // namespace test
diff --git a/remoting/test/test_video_renderer.cc b/remoting/test/test_video_renderer.cc
index 1734ba8..0ed0de4 100644
--- a/remoting/test/test_video_renderer.cc
+++ b/remoting/test/test_video_renderer.cc
@@ -5,10 +5,10 @@
 #include "remoting/test/test_video_renderer.h"
 
 #include <stdint.h>
+
 #include <utility>
 
 #include "base/bind.h"
-#include "base/callback_helpers.h"
 #include "base/logging.h"
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
@@ -214,8 +214,8 @@
   // on the current frame.
   RGBValue accumulating_avg_value = CalculateAverageColorValue(expected_rect_);
   if (ExpectedAverageColorIsMatched(accumulating_avg_value)) {
-    main_task_runner_->PostTask(
-        FROM_HERE, base::ResetAndReturn(&image_pattern_matched_callback_));
+    main_task_runner_->PostTask(FROM_HERE,
+                                std::move(image_pattern_matched_callback_));
   }
 }
 
diff --git a/sandbox/linux/seccomp-bpf-helpers/baseline_policy_unittest.cc b/sandbox/linux/seccomp-bpf-helpers/baseline_policy_unittest.cc
index 40fcebf..cdeb210 100644
--- a/sandbox/linux/seccomp-bpf-helpers/baseline_policy_unittest.cc
+++ b/sandbox/linux/seccomp-bpf-helpers/baseline_policy_unittest.cc
@@ -10,9 +10,7 @@
 #include <sched.h>
 #include <signal.h>
 #include <stddef.h>
-#include <stdlib.h>
 #include <string.h>
-#include <sys/mman.h>
 #include <sys/prctl.h>
 #include <sys/resource.h>
 #include <sys/socket.h>
@@ -132,33 +130,6 @@
   BPF_ASSERT_EQ(EPERM, fork_errno);
 }
 
-BPF_TEST_C(BaselinePolicy, SystemEperm, BaselinePolicy) {
-  errno = 0;
-  int ret_val = system("echo SHOULD NEVER RUN");
-  BPF_ASSERT_EQ(-1, ret_val);
-  BPF_ASSERT_EQ(EPERM, errno);
-}
-
-BPF_TEST_C(BaselinePolicy, CloneVforkEperm, BaselinePolicy) {
-  errno = 0;
-  // Allocate a couple pages for the child's stack even though the child should
-  // never start.
-  constexpr size_t kStackSize = 4096 * 4;
-  void* child_stack = mmap(nullptr, kStackSize, PROT_READ | PROT_WRITE,
-                           MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0);
-  BPF_ASSERT_NE(child_stack, nullptr);
-  pid_t pid = syscall(__NR_clone, CLONE_VM | CLONE_VFORK | SIGCHLD,
-                      static_cast<char*>(child_stack) + kStackSize, nullptr,
-                      nullptr, nullptr);
-  const int clone_errno = errno;
-  TestUtils::HandlePostForkReturn(pid);
-
-  munmap(child_stack, kStackSize);
-
-  BPF_ASSERT_EQ(-1, pid);
-  BPF_ASSERT_EQ(EPERM, clone_errno);
-}
-
 BPF_TEST_C(BaselinePolicy, CreateThread, BaselinePolicy) {
   base::Thread thread("sandbox_tests");
   BPF_ASSERT(thread.Start());
diff --git a/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc b/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc
index 348ab6e..100afe5 100644
--- a/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc
+++ b/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc
@@ -135,8 +135,7 @@
 #if !defined(OS_NACL_NONSFI)
 // Allow Glibc's and Android pthread creation flags, crash on any other
 // thread creation attempts and EPERM attempts to use neither
-// CLONE_VM nor CLONE_THREAD (all fork implementations), unless CLONE_VFORK is
-// present (as in newer versions of posix_spawn).
+// CLONE_VM, nor CLONE_THREAD, which includes all fork() implementations.
 ResultExpr RestrictCloneToThreadsAndEPERMFork() {
   const Arg<unsigned long> flags(0);
 
@@ -155,16 +154,8 @@
       AnyOf(flags == kAndroidCloneMask, flags == kObsoleteAndroidCloneMask,
             flags == kGlibcPthreadFlags);
 
-  // The following two flags are the two important flags in any vfork-emulating
-  // clone call. EPERM any clone call that contains both of them.
-  const uint64_t kImportantCloneVforkFlags = CLONE_VFORK | CLONE_VM;
-
-  const BoolExpr is_fork_or_clone_vfork =
-      AnyOf((flags & (CLONE_VM | CLONE_THREAD)) == 0,
-            (flags & kImportantCloneVforkFlags) == kImportantCloneVforkFlags);
-
   return If(IsAndroid() ? android_test : glibc_test, Allow())
-      .ElseIf(is_fork_or_clone_vfork, Error(EPERM))
+      .ElseIf((flags & (CLONE_VM | CLONE_THREAD)) == 0, Error(EPERM))
       .Else(CrashSIGSYSClone());
 }
 
diff --git a/services/network/network_context.cc b/services/network/network_context.cc
index 545324be..ba7bc6c7 100644
--- a/services/network/network_context.cc
+++ b/services/network/network_context.cc
@@ -932,7 +932,7 @@
   details.status_code = report->status_code;
   details.elapsed_time = report->elapsed_time;
   details.user_agent = std::move(user_agent);
-  logging_service->QueueSignedExchangeReport(details);
+  logging_service->QueueSignedExchangeReport(std::move(details));
 }
 
 #else   // BUILDFLAG(ENABLE_REPORTING)
diff --git a/services/network/url_loader.cc b/services/network/url_loader.cc
index 19b9c31c..841dac9 100644
--- a/services/network/url_loader.cc
+++ b/services/network/url_loader.cc
@@ -638,14 +638,6 @@
     return;
   }
 
-  if (new_url &&
-      (new_url->GetOrigin() != deferred_redirect_url_->GetOrigin())) {
-    NOTREACHED() << "Can only change the URL within the same origin.";
-    NotifyCompleted(net::ERR_UNEXPECTED);
-    // |this| may have been deleted.
-    return;
-  }
-
   deferred_redirect_url_.reset();
   new_redirect_url_ = new_url;
 
diff --git a/services/tracing/BUILD.gn b/services/tracing/BUILD.gn
index f6bf49a..f03baab 100644
--- a/services/tracing/BUILD.gn
+++ b/services/tracing/BUILD.gn
@@ -58,22 +58,6 @@
   ]
 }
 
-source_set("privacy_check") {
-  testonly = true
-
-  sources = [
-    "perfetto/privacy_filtering_check.cc",
-    "perfetto/privacy_filtering_check.h",
-  ]
-
-  deps = [
-    "//base",
-    "//third_party/perfetto/include/perfetto/protozero:protozero",
-    "//third_party/perfetto/protos/perfetto/common:lite",
-    "//third_party/perfetto/protos/perfetto/trace:lite",
-  ]
-}
-
 source_set("tests") {
   testonly = true
 
diff --git a/services/tracing/perfetto/consumer_host.cc b/services/tracing/perfetto/consumer_host.cc
index 7e23fb3..eb1e28e 100644
--- a/services/tracing/perfetto/consumer_host.cc
+++ b/services/tracing/perfetto/consumer_host.cc
@@ -139,14 +139,6 @@
       privacy_filtering_enabled_ = true;
     }
   }
-#if DCHECK_IS_ON()
-  if (privacy_filtering_enabled_) {
-    // If enabled, filtering must be enabled for all data sources.
-    for (const auto& data_source : trace_config.data_sources()) {
-      DCHECK(data_source.config().chrome_config().privacy_filtering_enabled());
-    }
-  }
-#endif
   perfetto::TraceConfig trace_config_copy = AdjustTraceConfig(trace_config);
 
   filtered_pids_.clear();
diff --git a/services/tracing/perfetto/privacy_filtering_check.cc b/services/tracing/perfetto/privacy_filtering_check.cc
deleted file mode 100644
index eccc4d6..0000000
--- a/services/tracing/perfetto/privacy_filtering_check.cc
+++ /dev/null
@@ -1,64 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "services/tracing/perfetto/privacy_filtering_check.h"
-
-#include "base/logging.h"
-#include "third_party/perfetto/protos/perfetto/trace/trace.pb.h"
-#include "third_party/perfetto/protos/perfetto/trace/trace_packet.pb.h"
-
-namespace tracing {
-namespace {
-using perfetto::protos::TracePacket;
-}
-
-PrivacyFilteringCheck::PrivacyFilteringCheck() = default;
-PrivacyFilteringCheck::~PrivacyFilteringCheck() = default;
-
-// static
-void PrivacyFilteringCheck::CheckProtoForUnexpectedFields(
-    const std::string& serialized_trace_proto) {
-  auto proto = std::make_unique<perfetto::protos::Trace>();
-  if (!proto->ParseFromArray(serialized_trace_proto.data(),
-                             serialized_trace_proto.size())) {
-    NOTREACHED();
-    return;
-  }
-
-  for (auto& packet : *proto->mutable_packet()) {
-    DCHECK(packet.unknown_fields().empty());
-    // TODO(ssid): For each of these messages verify that only expected fields
-    // are present.
-    if (packet.has_track_event()) {
-      ++counts_.track_event;
-    } else if (packet.has_process_descriptor()) {
-      ++counts_.process_desc;
-    } else if (packet.has_thread_descriptor()) {
-      ++counts_.thread_desc;
-    } else if (packet.has_synchronization_marker() ||
-               packet.has_trace_stats() || packet.has_trace_config() ||
-               packet.has_system_info()) {
-      // TODO(ssid): These should not be present in the traces or must be
-      // whitelisted.
-    } else {
-      DCHECK_EQ(TracePacket::DataCase::DATA_NOT_SET, packet.data_case());
-    }
-    if (packet.has_interned_data()) {
-      counts_.interned_name += packet.interned_data().legacy_event_names_size();
-      packet.mutable_interned_data()->clear_legacy_event_names();
-      counts_.interned_category +=
-          packet.interned_data().event_categories_size();
-      packet.mutable_interned_data()->clear_event_categories();
-      counts_.interned_source_location +=
-          packet.interned_data().source_locations_size();
-      packet.mutable_interned_data()->clear_source_locations();
-
-      std::string serialized;
-      packet.interned_data().SerializeToString(&serialized);
-      DCHECK_EQ(serialized.size(), 0u);
-    }
-  }
-}
-
-}  // namespace tracing
diff --git a/services/tracing/perfetto/privacy_filtering_check.h b/services/tracing/perfetto/privacy_filtering_check.h
deleted file mode 100644
index 7928266..0000000
--- a/services/tracing/perfetto/privacy_filtering_check.h
+++ /dev/null
@@ -1,41 +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.
-
-#ifndef SERVICES_TRACING_PERFETTO_PRIVACY_FILTERING_CHECK_H_
-#define SERVICES_TRACING_PERFETTO_PRIVACY_FILTERING_CHECK_H_
-
-#include <string>
-
-#include "base/macros.h"
-
-namespace tracing {
-
-class PrivacyFilteringCheck {
- public:
-  struct TypeCounts {
-    size_t track_event = 0;
-    size_t process_desc = 0;
-    size_t thread_desc = 0;
-
-    size_t interned_name = 0;
-    size_t interned_category = 0;
-    size_t interned_source_location = 0;
-  };
-
-  PrivacyFilteringCheck();
-  ~PrivacyFilteringCheck();
-
-  void CheckProtoForUnexpectedFields(const std::string& serialized_trace_proto);
-
-  const TypeCounts& counts() const { return counts_; }
-
- private:
-  TypeCounts counts_;
-
-  DISALLOW_COPY_AND_ASSIGN(PrivacyFilteringCheck);
-};
-
-}  // namespace tracing
-
-#endif  // SERVICES_TRACING_PERFETTO_PRIVACY_FILTERING_CHECK_H_
diff --git a/services/tracing/public/cpp/perfetto/trace_event_data_source.cc b/services/tracing/public/cpp/perfetto/trace_event_data_source.cc
index aadb0b4..f0217ae 100644
--- a/services/tracing/public/cpp/perfetto/trace_event_data_source.cc
+++ b/services/tracing/public/cpp/perfetto/trace_event_data_source.cc
@@ -83,9 +83,6 @@
 
 void TraceEventMetadataSource::GenerateMetadata(
     std::unique_ptr<perfetto::TraceWriter> trace_writer) {
-  if (privacy_filtering_enabled_) {
-    return;
-  }
   DCHECK(origin_task_runner_->RunsTasksInCurrentSequence());
 
   auto trace_packet = trace_writer->NewTracePacket();
@@ -210,17 +207,15 @@
   TraceLog::GetInstance()->SetAddTraceEventOverrides(nullptr, nullptr, nullptr);
 }
 
-void TraceEventDataSource::SetupStartupTracing(bool privacy_filtering_enabled) {
+void TraceEventDataSource::SetupStartupTracing() {
   {
     base::AutoLock lock(lock_);
     // No need to do anything if startup tracing has already been set,
     // or we know Perfetto has already been setup.
     if (startup_writer_registry_ || producer_client_) {
-      DCHECK(!privacy_filtering_enabled || privacy_filtering_enabled_);
       return;
     }
 
-    privacy_filtering_enabled_ = privacy_filtering_enabled;
     startup_writer_registry_ =
         std::make_unique<perfetto::StartupTraceWriterRegistry>();
   }
@@ -230,19 +225,13 @@
 void TraceEventDataSource::StartTracing(
     PerfettoProducer* producer,
     const perfetto::DataSourceConfig& data_source_config) {
+  privacy_filtering_enabled_ =
+      data_source_config.chrome_config().privacy_filtering_enabled();
+
   std::unique_ptr<perfetto::StartupTraceWriterRegistry> unbound_writer_registry;
   {
     base::AutoLock lock(lock_);
 
-    bool should_enable_filtering =
-        data_source_config.chrome_config().privacy_filtering_enabled();
-    if (should_enable_filtering) {
-      CHECK(!startup_writer_registry_ || privacy_filtering_enabled_)
-          << "Unexpected StartTracing received when startup tracing is "
-             "running.";
-    }
-    privacy_filtering_enabled_ = should_enable_filtering;
-
     DCHECK(!producer_client_);
     producer_client_ = producer;
     target_buffer_ = data_source_config.target_buffer();
@@ -253,6 +242,9 @@
   session_id_.fetch_add(1u, std::memory_order_relaxed);
 
   if (unbound_writer_registry) {
+    // TODO(ssid): Startup tracing should know about filtering output.
+    CHECK(!privacy_filtering_enabled_);
+
     // TODO(oysteine): Investigate why trace events emitted by something in
     // BindStartupTraceWriterRegistry() causes deadlocks.
     AutoThreadLocalBoolean thread_is_in_trace_event(
diff --git a/services/tracing/public/cpp/perfetto/trace_event_data_source.h b/services/tracing/public/cpp/perfetto/trace_event_data_source.h
index b350726f9..6b949321 100644
--- a/services/tracing/public/cpp/perfetto/trace_event_data_source.h
+++ b/services/tracing/public/cpp/perfetto/trace_event_data_source.h
@@ -99,7 +99,7 @@
   // is locally buffered until connection to the perfetto service is
   // established. Expects a later call to StartTracing() to bind to the perfetto
   // service. Should only be called once.
-  void SetupStartupTracing(bool privacy_filtering_enabled);
+  void SetupStartupTracing();
 
   // The PerfettoProducer is responsible for calling StopTracing
   // which will clear the stored pointer to it, before it
@@ -150,6 +150,7 @@
   void LogHistogram(base::HistogramBase* histogram);
 
   bool disable_interning_ = false;
+  bool privacy_filtering_enabled_ = false;
   base::OnceClosure stop_complete_callback_;
 
   // Incremented and accessed atomically but without memory order guarantees.
@@ -167,7 +168,6 @@
   std::unique_ptr<perfetto::StartupTraceWriterRegistry>
       startup_writer_registry_;
   std::vector<std::string> histograms_;
-  bool privacy_filtering_enabled_ = false;
 
   DISALLOW_COPY_AND_ASSIGN(TraceEventDataSource);
 };
diff --git a/services/tracing/public/cpp/perfetto/track_event_thread_local_event_sink.h b/services/tracing/public/cpp/perfetto/track_event_thread_local_event_sink.h
index 80d1227..54d4f04 100644
--- a/services/tracing/public/cpp/perfetto/track_event_thread_local_event_sink.h
+++ b/services/tracing/public/cpp/perfetto/track_event_thread_local_event_sink.h
@@ -64,7 +64,7 @@
   base::trace_event::TraceEvent complete_event_stack_[kMaxCompleteEventDepth];
   uint32_t current_stack_depth_ = 0;
 
-  const bool privacy_filtering_enabled_;
+  bool privacy_filtering_enabled_;
 };
 
 }  // namespace tracing
diff --git a/services/tracing/public/cpp/trace_startup.cc b/services/tracing/public/cpp/trace_startup.cc
index f11df22..13a382f 100644
--- a/services/tracing/public/cpp/trace_startup.cc
+++ b/services/tracing/public/cpp/trace_startup.cc
@@ -15,11 +15,6 @@
 
 namespace tracing {
 
-namespace {
-using base::trace_event::TraceConfig;
-using base::trace_event::TraceLog;
-}  // namespace
-
 void EnableStartupTracingIfNeeded() {
   const base::CommandLine& command_line =
       *base::CommandLine::ForCurrentProcess();
@@ -32,31 +27,29 @@
 
   // Ensure TraceLog is initialized first.
   // https://crbug.com/764357
-  auto* trace_log = TraceLog::GetInstance();
-  auto* startup_config = TraceStartupConfig::GetInstance();
+  base::trace_event::TraceLog::GetInstance();
 
-  if (startup_config->IsEnabled()) {
-    if (TracingUsesPerfettoBackend()) {
-      TraceEventDataSource::GetInstance()->SetupStartupTracing(
-          startup_config->GetBackgroundStartupTracingEnabled());
-    }
-
-    const TraceConfig& trace_config = startup_config->GetTraceConfig();
-    uint8_t modes = TraceLog::RECORDING_MODE;
+  if (TraceStartupConfig::GetInstance()->IsEnabled()) {
+    const base::trace_event::TraceConfig& trace_config =
+        TraceStartupConfig::GetInstance()->GetTraceConfig();
+    uint8_t modes = base::trace_event::TraceLog::RECORDING_MODE;
     if (!trace_config.event_filters().empty())
-      modes |= TraceLog::FILTERING_MODE;
-    trace_log->SetEnabled(startup_config->GetTraceConfig(), modes);
+      modes |= base::trace_event::TraceLog::FILTERING_MODE;
+    if (TracingUsesPerfettoBackend())
+      TraceEventDataSource::GetInstance()->SetupStartupTracing();
+    base::trace_event::TraceLog::GetInstance()->SetEnabled(
+        TraceStartupConfig::GetInstance()->GetTraceConfig(), modes);
   } else if (command_line.HasSwitch(switches::kTraceToConsole)) {
     // TODO(eseckler): Remove ability to trace to the console, perfetto doesn't
     // support this and noone seems to use it.
-    TraceConfig trace_config = GetConfigForTraceToConsole();
+    base::trace_event::TraceConfig trace_config = GetConfigForTraceToConsole();
     LOG(ERROR) << "Start " << switches::kTraceToConsole
                << " with CategoryFilter '"
                << trace_config.ToCategoryFilterString() << "'.";
     if (TracingUsesPerfettoBackend())
-      TraceEventDataSource::GetInstance()->SetupStartupTracing(
-          /*privacy_filtering_enabled=*/false);
-    trace_log->SetEnabled(trace_config, TraceLog::RECORDING_MODE);
+      TraceEventDataSource::GetInstance()->SetupStartupTracing();
+    base::trace_event::TraceLog::GetInstance()->SetEnabled(
+        trace_config, base::trace_event::TraceLog::RECORDING_MODE);
   }
 }
 
diff --git a/skia/ext/benchmarking_canvas.cc b/skia/ext/benchmarking_canvas.cc
index 595bc3e..c6b7974 100644
--- a/skia/ext/benchmarking_canvas.cc
+++ b/skia/ext/benchmarking_canvas.cc
@@ -145,7 +145,7 @@
   }
 
   SkScalar color_matrix[20];
-  if (filter.asColorMatrix(color_matrix)) {
+  if (filter.asAColorMatrix(color_matrix)) {
     std::unique_ptr<base::ListValue> color_matrix_val(new base::ListValue());
     for (unsigned i = 0; i < 20; ++i)
       color_matrix_val->Append(AsValue(color_matrix[i]));
diff --git a/testing/buildbot/chromium.android.json b/testing/buildbot/chromium.android.json
index 77104b0f..263e6b2 100644
--- a/testing/buildbot/chromium.android.json
+++ b/testing/buildbot/chromium.android.json
@@ -17292,7 +17292,8 @@
               "device_type": "marlin",
               "os": "Android"
             }
-          ]
+          ],
+          "shards": 2
         },
         "test": "chrome_public_test_vr_apk"
       },
@@ -17474,7 +17475,8 @@
               ],
               "name": "shard #${SHARD_INDEX} logcats"
             }
-          ]
+          ],
+          "shards": 2
         },
         "test": "chrome_public_test_vr_apk"
       },
@@ -24707,7 +24709,8 @@
               ],
               "name": "shard #${SHARD_INDEX} logcats"
             }
-          ]
+          ],
+          "shards": 2
         },
         "test": "chrome_public_test_vr_apk"
       },
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl
index 1d8ef80..d947cd7 100644
--- a/testing/buildbot/test_suites.pyl
+++ b/testing/buildbot/test_suites.pyl
@@ -38,6 +38,9 @@
           '--replace-system-package=com.google.vr.vrcore,//third_party/gvr-android-sdk/test-apks/vr_services/vr_services_current.apk',
           '--additional-apk=//third_party/gvr-android-sdk/test-apks/vr_keyboard/vr_keyboard_current.apk',
         ],
+        'swarming': {
+          'shards': 2,
+        },
         'test': 'chrome_public_test_vr_apk',
       },
       'chrome_public_test_vr_apk-ddready-dynamicsettings': {
diff --git a/third_party/blink/common/features.cc b/third_party/blink/common/features.cc
index 4e6f583..01a4ec7 100644
--- a/third_party/blink/common/features.cc
+++ b/third_party/blink/common/features.cc
@@ -48,11 +48,6 @@
 const base::Feature kFreezePurgeMemoryAllPagesFrozen{
     "FreezePurgeMemoryAllPagesFrozen", base::FEATURE_DISABLED_BY_DEFAULT};
 
-// Controls whether or not the font cache is invalidated when a critical memory
-// pressure signal is sent.
-const base::Feature kInvalidateFontCacheOnPurge{
-    "InvalidateFontCacheOnPurge", base::FEATURE_ENABLED_BY_DEFAULT};
-
 // Enables the experimental sweep-line algorithm for tracking "jank" from
 // layout objects changing their visual location between animation frames.
 const base::Feature kJankTrackingSweepLine{"JankTrackingSweepLine",
diff --git a/third_party/blink/public/common/features.h b/third_party/blink/public/common/features.h
index ef1dc1b4..4a13ca9 100644
--- a/third_party/blink/public/common/features.h
+++ b/third_party/blink/public/common/features.h
@@ -23,7 +23,6 @@
 BLINK_COMMON_EXPORT extern const base::Feature kFirstContentfulPaintPlusPlus;
 BLINK_COMMON_EXPORT extern const base::Feature kFreezePurgeMemoryAllPagesFrozen;
 BLINK_COMMON_EXPORT extern const base::Feature kImplicitRootScroller;
-BLINK_COMMON_EXPORT extern const base::Feature kInvalidateFontCacheOnPurge;
 BLINK_COMMON_EXPORT extern const base::Feature kJankTrackingSweepLine;
 BLINK_COMMON_EXPORT extern const base::Feature kBlinkGenPropertyTrees;
 BLINK_COMMON_EXPORT extern const base::Feature kLayoutNG;
diff --git a/third_party/blink/renderer/core/css/cssom/paint_worklet_deferred_image.cc b/third_party/blink/renderer/core/css/cssom/paint_worklet_deferred_image.cc
index 05e3d02..573b37e7 100644
--- a/third_party/blink/renderer/core/css/cssom/paint_worklet_deferred_image.cc
+++ b/third_party/blink/renderer/core/css/cssom/paint_worklet_deferred_image.cc
@@ -10,6 +10,7 @@
 #include "third_party/blink/renderer/platform/graphics/graphics_context.h"
 #include "third_party/blink/renderer/platform/graphics/paint/paint_canvas.h"
 #include "third_party/blink/renderer/platform/graphics/paint/paint_record.h"
+#include "third_party/blink/renderer/platform/graphics/skia/skia_utils.h"
 
 namespace blink {
 
@@ -18,12 +19,14 @@
                   const FloatRect& dest_rect,
                   const FloatRect& src_rect,
                   const PaintFlags& flags,
+                  Image::ImageClampingMode clamping_mode,
                   scoped_refptr<PaintWorkletInput> input) {
-  canvas->drawImage(PaintImageBuilder::WithDefault()
-                        .set_paint_worklet_input(std::move(input))
-                        .set_id(PaintImage::GetNextId())
-                        .TakePaintImage(),
-                    0, 0, &flags);
+  canvas->drawImageRect(PaintImageBuilder::WithDefault()
+                            .set_paint_worklet_input(std::move(input))
+                            .set_id(PaintImage::GetNextId())
+                            .TakePaintImage(),
+                        src_rect, dest_rect, &flags,
+                        WebCoreClampingModeToSkiaRectConstraint(clamping_mode));
 }
 }  // namespace
 
@@ -32,15 +35,15 @@
                                      const FloatRect& dest_rect,
                                      const FloatRect& src_rect,
                                      RespectImageOrientationEnum,
-                                     ImageClampingMode,
+                                     ImageClampingMode clamping_mode,
                                      ImageDecodingMode) {
-  DrawInternal(canvas, dest_rect, src_rect, flags, input_);
+  DrawInternal(canvas, dest_rect, src_rect, flags, clamping_mode, input_);
 }
 
 void PaintWorkletDeferredImage::DrawTile(GraphicsContext& context,
                                          const FloatRect& src_rect) {
   DrawInternal(context.Canvas(), FloatRect(), src_rect, context.FillFlags(),
-               input_);
+               kClampImageToSourceRect, input_);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/css/selector_checker.cc b/third_party/blink/renderer/core/css/selector_checker.cc
index 6573724..c5832fe 100644
--- a/third_party/blink/renderer/core/css/selector_checker.cc
+++ b/third_party/blink/renderer/core/css/selector_checker.cc
@@ -770,8 +770,7 @@
           is_empty = false;
           break;
         }
-        if (n->IsTextNode()) {
-          Text* text_node = ToText(n);
+        if (auto* text_node = DynamicTo<Text>(n)) {
           if (!text_node->data().IsEmpty()) {
             if (text_node->ContainsOnlyWhitespaceOrEmpty()) {
               has_whitespace = true;
diff --git a/third_party/blink/renderer/core/css/style_engine_test.cc b/third_party/blink/renderer/core/css/style_engine_test.cc
index 4c15a15..02ecb3e 100644
--- a/third_party/blink/renderer/core/css/style_engine_test.cc
+++ b/third_party/blink/renderer/core/css/style_engine_test.cc
@@ -1422,15 +1422,15 @@
 
   UpdateAllLifecyclePhases();
 
-  ToText(t1->firstChild())->setData("");
+  To<Text>(t1->firstChild())->setData("");
   EXPECT_TRUE(t1->NeedsStyleInvalidation());
 
-  ToText(t2->firstChild())->setData("Text");
+  To<Text>(t2->firstChild())->setData("Text");
   EXPECT_TRUE(t2->NeedsStyleInvalidation());
 
   // This is not optimal. We do not detect that we change text to/from
   // non-empty string.
-  ToText(t3->firstChild())->setData("NewText");
+  To<Text>(t3->firstChild())->setData("NewText");
   EXPECT_TRUE(t3->NeedsStyleInvalidation());
 }
 
@@ -1455,13 +1455,13 @@
 
   UpdateAllLifecyclePhases();
 
-  ToText(t1->firstChild())->setData("");
+  To<Text>(t1->firstChild())->setData("");
   EXPECT_FALSE(t1->NeedsStyleInvalidation());
 
-  ToText(t2->lastChild())->setData("Text");
+  To<Text>(t2->lastChild())->setData("Text");
   EXPECT_FALSE(t2->NeedsStyleInvalidation());
 
-  ToText(t3->firstChild())->setData("NewText");
+  To<Text>(t3->firstChild())->setData("NewText");
   EXPECT_FALSE(t3->NeedsStyleInvalidation());
 }
 
diff --git a/third_party/blink/renderer/core/dom/character_data.cc b/third_party/blink/renderer/core/dom/character_data.cc
index f0f3ada..b8bdd0a 100644
--- a/third_party/blink/renderer/core/dom/character_data.cc
+++ b/third_party/blink/renderer/core/dom/character_data.cc
@@ -209,8 +209,8 @@
   data_ = new_data;
 
   DCHECK(!GetLayoutObject() || IsTextNode());
-  if (IsTextNode())
-    ToText(this)->UpdateTextLayoutObject(offset_of_replaced_data, old_length);
+  if (auto* text_node = DynamicTo<Text>(this))
+    text_node->UpdateTextLayoutObject(offset_of_replaced_data, old_length);
 
   if (source != kUpdateFromParser) {
     if (getNodeType() == kProcessingInstructionNode)
diff --git a/third_party/blink/renderer/core/dom/container_node.cc b/third_party/blink/renderer/core/dom/container_node.cc
index 827906e..02f23c9 100644
--- a/third_party/blink/renderer/core/dom/container_node.cc
+++ b/third_party/blink/renderer/core/dom/container_node.cc
@@ -1399,8 +1399,8 @@
   for (Node* child = firstChild(); child; child = child->nextSibling()) {
     if (!change.TraverseChild(*child))
       continue;
-    if (child->IsTextNode())
-      ToText(child)->RecalcTextStyle(change);
+    if (auto* child_text_node = DynamicTo<Text>(child))
+      child_text_node->RecalcTextStyle(change);
     else if (child->IsElementNode())
       ToElement(child)->RecalcStyle(change);
   }
@@ -1409,12 +1409,11 @@
 void ContainerNode::RebuildLayoutTreeForChild(
     Node* child,
     WhitespaceAttacher& whitespace_attacher) {
-  if (child->IsTextNode()) {
-    Text* text_node = ToText(child);
+  if (auto* child_text_node = DynamicTo<Text>(child)) {
     if (child->NeedsReattachLayoutTree())
-      text_node->RebuildTextLayoutTree(whitespace_attacher);
+      child_text_node->RebuildTextLayoutTree(whitespace_attacher);
     else
-      whitespace_attacher.DidVisitText(text_node);
+      whitespace_attacher.DidVisitText(child_text_node);
     return;
   }
 
diff --git a/third_party/blink/renderer/core/dom/document_statistics_collector.cc b/third_party/blink/renderer/core/dom/document_statistics_collector.cc
index 6be49cd..80defe0 100644
--- a/third_party/blink/renderer/core/dom/document_statistics_collector.cc
+++ b/third_party/blink/renderer/core/dom/document_statistics_collector.cc
@@ -48,10 +48,11 @@
   // Given shadow DOM rarely appears in <P> elements in long-form articles, the
   // overall accuracy should not be largely affected.
   for (Node& node : NodeTraversal::InclusiveDescendantsOf(root)) {
-    if (!node.IsTextNode()) {
+    auto* text_node = DynamicTo<Text>(node);
+    if (!text_node) {
       continue;
     }
-    length += ToText(node).length();
+    length += text_node->length();
     if (length > kTextContentLengthSaturation) {
       return kTextContentLengthSaturation;
     }
diff --git a/third_party/blink/renderer/core/dom/element.cc b/third_party/blink/renderer/core/dom/element.cc
index 319ab57..2b96acf 100644
--- a/third_party/blink/renderer/core/dom/element.cc
+++ b/third_party/blink/renderer/core/dom/element.cc
@@ -3028,7 +3028,8 @@
   for (; sibling; sibling = next_func(*sibling)) {
     if (sibling->IsElementNode())
       return true;
-    if (sibling->IsTextNode() && !ToText(sibling)->data().IsEmpty())
+    auto* text_node = DynamicTo<Text>(sibling);
+    if (text_node && !text_node->data().IsEmpty())
       return true;
   }
   return false;
@@ -3770,12 +3771,21 @@
     return;
 
   parent->ReplaceChild(fragment, this, exception_state);
-  Node* node = next ? next->previousSibling() : nullptr;
-  if (!exception_state.HadException() && node && node->IsTextNode())
-    MergeWithNextTextNode(ToText(node), exception_state);
+  if (exception_state.HadException())
+    return;
 
-  if (!exception_state.HadException() && prev && prev->IsTextNode())
-    MergeWithNextTextNode(ToText(prev), exception_state);
+  Node* node = next ? next->previousSibling() : nullptr;
+  if (auto* text = DynamicTo<Text>(node)) {
+    MergeWithNextTextNode(text, exception_state);
+    if (exception_state.HadException())
+      return;
+  }
+
+  if (auto* prev_text = DynamicTo<Text>(prev)) {
+    MergeWithNextTextNode(prev_text, exception_state);
+    if (exception_state.HadException())
+      return;
+  }
 }
 
 void Element::setOuterHTML(const StringOrTrustedHTML& string_or_html,
@@ -4002,14 +4012,14 @@
   unsigned total_length = 0;
 
   for (Node* child = firstChild(); child; child = child->nextSibling()) {
-    if (!child->IsTextNode())
+    auto* child_text_node = DynamicTo<Text>(child);
+    if (!child_text_node)
       continue;
-    Text* text = ToText(child);
     if (!first_text_node)
-      first_text_node = text;
+      first_text_node = child_text_node;
     else
       found_multiple_text_nodes = true;
-    unsigned length = text->data().length();
+    unsigned length = child_text_node->data().length();
     if (length > std::numeric_limits<unsigned>::max() - total_length)
       return g_empty_string;
     total_length += length;
@@ -4026,9 +4036,10 @@
   StringBuilder content;
   content.ReserveCapacity(total_length);
   for (Node* child = first_text_node; child; child = child->nextSibling()) {
-    if (!child->IsTextNode())
+    auto* child_text_node = DynamicTo<Text>(child);
+    if (!child_text_node)
       continue;
-    content.Append(ToText(child)->data());
+    content.Append(child_text_node->data());
   }
 
   DCHECK_EQ(content.length(), total_length);
diff --git a/third_party/blink/renderer/core/dom/first_letter_pseudo_element.cc b/third_party/blink/renderer/core/dom/first_letter_pseudo_element.cc
index 9843488..38c9b9e 100644
--- a/third_party/blink/renderer/core/dom/first_letter_pseudo_element.cc
+++ b/third_party/blink/renderer/core/dom/first_letter_pseudo_element.cc
@@ -268,7 +268,7 @@
 void FirstLetterPseudoElement::DetachLayoutTree(const AttachContext& context) {
   if (remaining_text_layout_object_) {
     if (remaining_text_layout_object_->GetNode() && GetDocument().IsActive()) {
-      Text* text_node = ToText(remaining_text_layout_object_->GetNode());
+      auto* text_node = To<Text>(remaining_text_layout_object_->GetNode());
       remaining_text_layout_object_->SetTextFragment(
           text_node->DataImpl(), 0, text_node->DataImpl()->length());
     }
diff --git a/third_party/blink/renderer/core/dom/node.cc b/third_party/blink/renderer/core/dom/node.cc
index 51a2afb..5436447 100644
--- a/third_party/blink/renderer/core/dom/node.cc
+++ b/third_party/blink/renderer/core/dom/node.cc
@@ -874,7 +874,7 @@
       break;
 
     if (node->getNodeType() == kTextNode)
-      node = ToText(node)->MergeNextSiblingNodesIfPossible();
+      node = To<Text>(node)->MergeNextSiblingNodesIfPossible();
     else
       node = NodeTraversal::NextPostOrder(*node);
   }
@@ -1814,8 +1814,8 @@
   for (const Node& node : NodeTraversal::InclusiveDescendantsOf(*this)) {
     if (IsHTMLBRElement(node) && convert_brs_to_newlines) {
       content.Append('\n');
-    } else if (node.IsTextNode()) {
-      content.Append(ToText(node).data());
+    } else if (auto* text_node = DynamicTo<Text>(node)) {
+      content.Append(text_node->data());
     }
   }
   return content.ToString();
@@ -1856,7 +1856,7 @@
       // See crbug.com/352836 also.
       // No need to do anything if the text is identical.
       if (container->HasOneTextChild() &&
-          ToText(container->firstChild())->data() == text && !text.IsEmpty())
+          To<Text>(container->firstChild())->data() == text && !text.IsEmpty())
         return;
 
       ChildListMutationScope mutation(*this);
@@ -2356,8 +2356,8 @@
         GetDocument().AddListenerTypeIfNeeded(type, *this);
     }
   }
-  if (IsTextNode())
-    old_document.Markers().RemoveMarkersForNode(*ToText(this));
+  if (auto* text_node = DynamicTo<Text>(this))
+    old_document.Markers().RemoveMarkersForNode(*text_node);
   if (GetDocument().GetPage() &&
       GetDocument().GetPage() != old_document.GetPage()) {
     GetDocument().GetFrame()->GetEventHandlerRegistry().DidMoveIntoPage(*this);
diff --git a/third_party/blink/renderer/core/dom/range.cc b/third_party/blink/renderer/core/dom/range.cc
index fe24dfc..0b12bdff 100644
--- a/third_party/blink/renderer/core/dom/range.cc
+++ b/third_party/blink/renderer/core/dom/range.cc
@@ -910,7 +910,7 @@
   // splitting it with offset range’s start offset.
   if (start_is_text) {
     reference_node =
-        ToText(start_node).splitText(start_.Offset(), exception_state);
+        To<Text>(start_node).splitText(start_.Offset(), exception_state);
     if (exception_state.HadException())
       return;
   }
@@ -1703,9 +1703,10 @@
       continue;
     }
 
-    if (!node->IsTextNode())
+    auto* const text_node = DynamicTo<Text>(node);
+    if (!text_node)
       continue;
-    LayoutText* const layout_text = ToText(node)->GetLayoutObject();
+    LayoutText* const layout_text = text_node->GetLayoutObject();
     if (!layout_text)
       continue;
 
diff --git a/third_party/blink/renderer/core/dom/range_test.cc b/third_party/blink/renderer/core/dom/range_test.cc
index 8c7f52d..35ade003 100644
--- a/third_party/blink/renderer/core/dom/range_test.cc
+++ b/third_party/blink/renderer/core/dom/range_test.cc
@@ -103,7 +103,7 @@
   V8TestingScope scope;
 
   GetDocument().body()->SetInnerHTMLFromString("1234");
-  Text* old_text = ToText(GetDocument().body()->firstChild());
+  auto* old_text = To<Text>(GetDocument().body()->firstChild());
 
   Range* range04 = Range::Create(GetDocument(), old_text, 0, old_text, 4);
   Range* range02 = Range::Create(GetDocument(), old_text, 0, old_text, 2);
@@ -111,7 +111,7 @@
   Range* range24 = Range::Create(GetDocument(), old_text, 2, old_text, 4);
 
   old_text->splitText(2, ASSERT_NO_EXCEPTION);
-  Text* new_text = ToText(old_text->nextSibling());
+  auto* new_text = To<Text>(old_text->nextSibling());
 
   EXPECT_TRUE(range04->BoundaryPointsValid());
   EXPECT_EQ(old_text, range04->startContainer());
@@ -153,7 +153,7 @@
       GetDocument().getElementById(AtomicString::FromUTF8("inner-left"));
   Element* inner_right =
       GetDocument().getElementById(AtomicString::FromUTF8("inner-right"));
-  Text* old_text = ToText(outer->childNodes()->item(2));
+  auto* old_text = To<Text>(outer->childNodes()->item(2));
 
   Range* range_outer_outside = Range::Create(GetDocument(), outer, 0, outer, 5);
   Range* range_outer_inside = Range::Create(GetDocument(), outer, 1, outer, 4);
@@ -167,7 +167,7 @@
       Range::Create(GetDocument(), old_text, 6, outer, 3);
 
   old_text->splitText(3, ASSERT_NO_EXCEPTION);
-  Text* new_text = ToText(old_text->nextSibling());
+  auto* new_text = To<Text>(old_text->nextSibling());
 
   EXPECT_TRUE(range_outer_outside->BoundaryPointsValid());
   EXPECT_EQ(outer, range_outer_outside->startContainer());
@@ -233,7 +233,7 @@
   Element* div = GetDocument().QuerySelector("div");
   Element* span1 = GetDocument().getElementById("span1");
   Element* span2 = GetDocument().getElementById("span2");
-  Text* text = ToText(div->childNodes()->item(1));
+  auto* text = To<Text>(div->childNodes()->item(1));
 
   Range* range = Range::Create(GetDocument(), span2, 0, div, 3);
 
@@ -255,7 +255,7 @@
   Element* div = GetDocument().QuerySelector("div");
   Element* span1 = GetDocument().getElementById("span1");
   Element* span2 = GetDocument().getElementById("span2");
-  Text* text = ToText(div->childNodes()->item(1));
+  auto* text = To<Text>(div->childNodes()->item(1));
 
   Range* range = Range::Create(GetDocument(), span2, 0, div, 3);
 
diff --git a/third_party/blink/renderer/core/dom/slot_assignment_test.cc b/third_party/blink/renderer/core/dom/slot_assignment_test.cc
index f85597d..e5c4dea6 100644
--- a/third_party/blink/renderer/core/dom/slot_assignment_test.cc
+++ b/third_party/blink/renderer/core/dom/slot_assignment_test.cc
@@ -68,7 +68,7 @@
 void RemoveWhiteSpaceOnlyTextNode(ContainerNode& container) {
   for (Node* descendant :
        CollectFromIterable(NodeTraversal::InclusiveDescendantsOf(container))) {
-    if (Text* text = ToTextOrNull(descendant)) {
+    if (auto* text = DynamicTo<Text>(descendant)) {
       if (text->ContainsOnlyWhitespaceOrEmpty())
         text->remove();
     } else if (Element* element = ToElementOrNull(descendant)) {
diff --git a/third_party/blink/renderer/core/dom/static_range_test.cc b/third_party/blink/renderer/core/dom/static_range_test.cc
index 6f44e4cf..ced35af4c 100644
--- a/third_party/blink/renderer/core/dom/static_range_test.cc
+++ b/third_party/blink/renderer/core/dom/static_range_test.cc
@@ -46,7 +46,7 @@
 TEST_F(StaticRangeTest, SplitTextNodeRangeWithinText) {
   V8TestingScope scope;
   GetDocument().body()->SetInnerHTMLFromString("1234");
-  Text* old_text = ToText(GetDocument().body()->firstChild());
+  auto* old_text = To<Text>(GetDocument().body()->firstChild());
 
   StaticRange* static_range04 =
       StaticRange::Create(GetDocument(), old_text, 0u, old_text, 4u);
@@ -63,7 +63,7 @@
   Range* range24 = static_range24->toRange(ASSERT_NO_EXCEPTION);
 
   old_text->splitText(2, ASSERT_NO_EXCEPTION);
-  Text* new_text = ToText(old_text->nextSibling());
+  auto* new_text = To<Text>(old_text->nextSibling());
 
   // Range should mutate.
   EXPECT_TRUE(range04->BoundaryPointsValid());
@@ -126,7 +126,7 @@
       GetDocument().getElementById(AtomicString::FromUTF8("inner-left"));
   Element* inner_right =
       GetDocument().getElementById(AtomicString::FromUTF8("inner-right"));
-  Text* old_text = ToText(outer->childNodes()->item(2));
+  auto* old_text = To<Text>(outer->childNodes()->item(2));
 
   StaticRange* static_range_outer_outside =
       StaticRange::Create(GetDocument(), outer, 0u, outer, 5u);
@@ -155,7 +155,7 @@
       static_range_from_text_to_middle_of_element->toRange(ASSERT_NO_EXCEPTION);
 
   old_text->splitText(3, ASSERT_NO_EXCEPTION);
-  Text* new_text = ToText(old_text->nextSibling());
+  auto* new_text = To<Text>(old_text->nextSibling());
 
   // Range should mutate.
   EXPECT_TRUE(range_outer_outside->BoundaryPointsValid());
@@ -232,7 +232,7 @@
 TEST_F(StaticRangeTest, InvalidToRange) {
   V8TestingScope scope;
   GetDocument().body()->SetInnerHTMLFromString("1234");
-  Text* old_text = ToText(GetDocument().body()->firstChild());
+  auto* old_text = To<Text>(GetDocument().body()->firstChild());
 
   StaticRange* static_range04 =
       StaticRange::Create(GetDocument(), old_text, 0u, old_text, 4u);
diff --git a/third_party/blink/renderer/core/dom/text.cc b/third_party/blink/renderer/core/dom/text.cc
index 67f8b5c..4609368 100644
--- a/third_party/blink/renderer/core/dom/text.cc
+++ b/third_party/blink/renderer/core/dom/text.cc
@@ -69,7 +69,7 @@
     if (next_sibling->getNodeType() != kTextNode)
       break;
 
-    Text* next_text = ToText(next_sibling);
+    auto* next_text = To<Text>(next_sibling);
 
     // Remove empty text nodes.
     if (!next_text->length()) {
diff --git a/third_party/blink/renderer/core/dom/text.h b/third_party/blink/renderer/core/dom/text.h
index fcc3500..41bf23fc 100644
--- a/third_party/blink/renderer/core/dom/text.h
+++ b/third_party/blink/renderer/core/dom/text.h
@@ -27,6 +27,7 @@
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/css/style_recalc.h"
 #include "third_party/blink/renderer/core/dom/character_data.h"
+#include "third_party/blink/renderer/platform/wtf/casting.h"
 
 namespace blink {
 
@@ -87,6 +88,11 @@
 
 DEFINE_NODE_TYPE_CASTS(Text, IsTextNode());
 
+template <>
+struct DowncastTraits<Text> {
+  static bool AllowFrom(const Node& node) { return node.IsTextNode(); }
+};
+
 }  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_CORE_DOM_TEXT_H_
diff --git a/third_party/blink/renderer/core/dom/text_test.cc b/third_party/blink/renderer/core/dom/text_test.cc
index ed16d8cd5..7a4f7b19 100644
--- a/third_party/blink/renderer/core/dom/text_test.cc
+++ b/third_party/blink/renderer/core/dom/text_test.cc
@@ -19,7 +19,7 @@
       "id=sample>a<span>b</span></pre>");
 
   Node* sample = GetDocument().getElementById("sample");
-  Text* text = ToText(sample->firstChild());
+  auto* text = To<Text>(sample->firstChild());
   text->setData(" ");
   UpdateAllLifecyclePhasesForTest();
 
@@ -30,7 +30,7 @@
   SetBodyContent("<style>*::first-letter{font:icon;}</style><pre>AB\n</pre>");
 
   Element* pre = GetDocument().QuerySelector("pre");
-  Text* text = ToText(pre->firstChild());
+  auto* text = To<Text>(pre->firstChild());
 
   Range* range = Range::Create(GetDocument(), text, 0, text, 2);
   range->deleteContents(ASSERT_NO_EXCEPTION);
diff --git a/third_party/blink/renderer/core/dom/v0_insertion_point.cc b/third_party/blink/renderer/core/dom/v0_insertion_point.cc
index 63327cb4..dae1722 100644
--- a/third_party/blink/renderer/core/dom/v0_insertion_point.cc
+++ b/third_party/blink/renderer/core/dom/v0_insertion_point.cc
@@ -149,8 +149,8 @@
       continue;
     if (node->IsElementNode())
       ToElement(node)->RecalcStyle(change);
-    else if (node->IsTextNode())
-      ToText(node)->RecalcTextStyle(change);
+    else if (auto* text_node = DynamicTo<Text>(node))
+      text_node->RecalcTextStyle(change);
   }
 }
 
diff --git a/third_party/blink/renderer/core/dom/whitespace_attacher.cc b/third_party/blink/renderer/core/dom/whitespace_attacher.cc
index df1a7ed..2334e8fc 100644
--- a/third_party/blink/renderer/core/dom/whitespace_attacher.cc
+++ b/third_party/blink/renderer/core/dom/whitespace_attacher.cc
@@ -118,10 +118,10 @@
   for (Node* sibling = last_text_node_; sibling;
        sibling = LayoutTreeBuilderTraversal::NextLayoutSibling(*sibling)) {
     LayoutObject* sibling_layout_object = sibling->GetLayoutObject();
-    if (sibling->IsTextNode() &&
-        ToText(sibling)->ContainsOnlyWhitespaceOrEmpty()) {
+    auto* text_node = DynamicTo<Text>(sibling);
+    if (text_node && text_node->ContainsOnlyWhitespaceOrEmpty()) {
       bool had_layout_object = !!sibling_layout_object;
-      ToText(sibling)->ReattachLayoutTreeIfNeeded(context);
+      text_node->ReattachLayoutTreeIfNeeded(context);
       sibling_layout_object = sibling->GetLayoutObject();
       // If sibling's layout object status didn't change we don't need to
       // continue checking other siblings since their layout object status
@@ -171,12 +171,10 @@
   for (; sibling && sibling != last_text_node_;
        sibling = LayoutTreeBuilderTraversal::NextLayoutSibling(*sibling)) {
     LayoutObject* layout_object = sibling->GetLayoutObject();
-    if (sibling->IsTextNode()) {
-      Text* text = ToText(sibling);
-      if (text->ContainsOnlyWhitespaceOrEmpty()) {
-        last_text_node_ = text;
-        return;
-      }
+    auto* text = DynamicTo<Text>(sibling);
+    if (text && text->ContainsOnlyWhitespaceOrEmpty()) {
+      last_text_node_ = text;
+      return;
     }
     if (layout_object && !layout_object->IsFloatingOrOutOfFlowPositioned()) {
       last_text_node_ = nullptr;
diff --git a/third_party/blink/renderer/core/fetch/request.cc b/third_party/blink/renderer/core/fetch/request.cc
index 01550d1c..f634618 100644
--- a/third_party/blink/renderer/core/fetch/request.cc
+++ b/third_party/blink/renderer/core/fetch/request.cc
@@ -687,61 +687,7 @@
 
 String Request::destination() const {
   // "The destination attribute’s getter must return request’s destination."
-  switch (request_->Context()) {
-    case mojom::RequestContextType::UNSPECIFIED:
-    case mojom::RequestContextType::BEACON:
-    case mojom::RequestContextType::DOWNLOAD:
-    case mojom::RequestContextType::EVENT_SOURCE:
-    case mojom::RequestContextType::FETCH:
-    case mojom::RequestContextType::PING:
-    case mojom::RequestContextType::XML_HTTP_REQUEST:
-    case mojom::RequestContextType::SUBRESOURCE:
-    case mojom::RequestContextType::PREFETCH:
-      return "";
-    case mojom::RequestContextType::CSP_REPORT:
-      return "report";
-    case mojom::RequestContextType::AUDIO:
-      return "audio";
-    case mojom::RequestContextType::EMBED:
-      return "embed";
-    case mojom::RequestContextType::FONT:
-      return "font";
-    case mojom::RequestContextType::FRAME:
-    case mojom::RequestContextType::HYPERLINK:
-    case mojom::RequestContextType::IFRAME:
-    case mojom::RequestContextType::LOCATION:
-    case mojom::RequestContextType::FORM:
-      return "document";
-    case mojom::RequestContextType::IMAGE:
-    case mojom::RequestContextType::FAVICON:
-    case mojom::RequestContextType::IMAGE_SET:
-      return "image";
-    case mojom::RequestContextType::MANIFEST:
-      return "manifest";
-    case mojom::RequestContextType::OBJECT:
-      return "object";
-    case mojom::RequestContextType::SCRIPT:
-      return "script";
-    case mojom::RequestContextType::SHARED_WORKER:
-      return "sharedworker";
-    case mojom::RequestContextType::STYLE:
-      return "style";
-    case mojom::RequestContextType::TRACK:
-      return "track";
-    case mojom::RequestContextType::VIDEO:
-      return "video";
-    case mojom::RequestContextType::WORKER:
-      return "worker";
-    case mojom::RequestContextType::XSLT:
-      return "xslt";
-    case mojom::RequestContextType::IMPORT:
-    case mojom::RequestContextType::INTERNAL:
-    case mojom::RequestContextType::PLUGIN:
-    case mojom::RequestContextType::SERVICE_WORKER:
-      return "unknown";
-  }
-  NOTREACHED();
-  return "";
+  return FetchUtils::GetDestinationFromContext(request_->Context());
 }
 
 String Request::referrer() const {
diff --git a/third_party/blink/renderer/core/paint/image_paint_timing_detector.cc b/third_party/blink/renderer/core/paint/image_paint_timing_detector.cc
index fccd7475..24df817 100644
--- a/third_party/blink/renderer/core/paint/image_paint_timing_detector.cc
+++ b/third_party/blink/renderer/core/paint/image_paint_timing_detector.cc
@@ -103,7 +103,7 @@
       largest_image_record->paint_time, "data", std::move(value));
 }
 
-void ImagePaintTimingDetector::Analyze() {
+void ImagePaintTimingDetector::UpdateCandidate() {
   ImageRecord* largest_image_record =
       records_manager_.FindLargestPaintCandidate();
   // These conditions represents the following scenarios:
@@ -132,7 +132,7 @@
   frame_index_++;
   if (need_update_timing_at_frame_end_) {
     need_update_timing_at_frame_end_ = false;
-    Analyze();
+    UpdateCandidate();
   }
   if (!records_manager_.NeedMeausuringPaintTime())
     return;
@@ -191,7 +191,7 @@
   DCHECK(ThreadState::Current()->IsMainThread());
   records_manager_.AssignPaintTimeToRegisteredQueuedNodes(
       timestamp, last_queued_frame_index);
-  Analyze();
+  UpdateCandidate();
 }
 
 void ImageRecordsManager::AssignPaintTimeToRegisteredQueuedNodes(
@@ -460,7 +460,7 @@
     const Image& image) {
   // Background images attached to <body> or <html> are likely for background
   // purpose, so we rule them out.
-  if (object.IsLayoutView()) {
+  if (object.IsLayoutView() || object.IsBody() || object.IsDocumentElement()) {
     return false;
   }
   // Generated images are excluded here, as they are likely to serve for
diff --git a/third_party/blink/renderer/core/paint/image_paint_timing_detector.h b/third_party/blink/renderer/core/paint/image_paint_timing_detector.h
index 353a9d32..a1b3594d 100644
--- a/third_party/blink/renderer/core/paint/image_paint_timing_detector.h
+++ b/third_party/blink/renderer/core/paint/image_paint_timing_detector.h
@@ -208,7 +208,7 @@
   void Deactivate();
   void HandleTooManyNodes();
 
-  void Analyze();
+  void UpdateCandidate();
 
   base::RepeatingCallback<void(WebWidgetClient::ReportTimeCallback)>
       notify_swap_time_override_for_testing_;
diff --git a/third_party/blink/renderer/core/paint/image_paint_timing_detector_test.cc b/third_party/blink/renderer/core/paint/image_paint_timing_detector_test.cc
index ed03cb3..e4e84ac 100644
--- a/third_party/blink/renderer/core/paint/image_paint_timing_detector_test.cc
+++ b/third_party/blink/renderer/core/paint/image_paint_timing_detector_test.cc
@@ -108,8 +108,10 @@
         .records_manager_.size_ordered_set_.size();
   }
 
-  void Analyze() {
-    return GetPaintTimingDetector().GetImagePaintTimingDetector().Analyze();
+  void UpdateCandidate() {
+    return GetPaintTimingDetector()
+        .GetImagePaintTimingDetector()
+        .UpdateCandidate();
   }
 
   TimeTicks LargestPaintStoredResult() {
@@ -596,7 +598,7 @@
   )HTML");
   SetImageAndPaint("target", 5, 5);
   UpdateAllLifecyclePhasesForTest();
-  Analyze();
+  UpdateCandidate();
 }
 
 TEST_F(ImagePaintTimingDetectorTest, Iframe) {
diff --git a/third_party/blink/renderer/core/paint/text_paint_timing_detector.cc b/third_party/blink/renderer/core/paint/text_paint_timing_detector.cc
index 45d02cf..be3f9495 100644
--- a/third_party/blink/renderer/core/paint/text_paint_timing_detector.cc
+++ b/third_party/blink/renderer/core/paint/text_paint_timing_detector.cc
@@ -73,21 +73,28 @@
 }
 
 void TextPaintTimingDetector::TimerFired(TimerBase* time) {
-  // Wrap Analyze method in TimerFired so that we can drop |time| for Analyze
-  // in testing.
-  Analyze();
+  // Wrap |UpdateCandidate| method in TimerFired so that we can drop |time| for
+  // |UpdateCandidate| in testing.
+  UpdateCandidate();
 }
 
-void TextPaintTimingDetector::Analyze() {
+void TextPaintTimingDetector::UpdateCandidate() {
   TextRecord* candidate = records_manager_.FindLargestPaintCandidate();
-  DCHECK(!candidate || !candidate->paint_time.is_null());
-  if (candidate && candidate->paint_time != largest_text_paint_) {
+  if (!candidate) {
+    largest_text_paint_ = base::TimeTicks();
+    largest_text_paint_size_ = 0;
+    frame_view_->GetPaintTimingDetector().DidChangePerformanceTiming();
+  } else if (candidate->paint_time != largest_text_paint_) {
     OnLargestTextDetected(*candidate);
     frame_view_->GetPaintTimingDetector().DidChangePerformanceTiming();
   }
 }
 
 void TextPaintTimingDetector::OnPaintFinished() {
+  if (need_update_timing_at_frame_end_) {
+    need_update_timing_at_frame_end_ = false;
+    UpdateCandidate();
+  }
   if (records_manager_.NeedMeausuringPaintTime()) {
     // Start repeating timer only once at the first text paint.
     if (!timer_.IsActive())
@@ -106,11 +113,7 @@
   if (!records_manager_.IsKnownVisibleNode(node_id))
     return;
   records_manager_.SetNodeDetachedIfNeeded(node_id);
-  if (records_manager_.AreAllVisibleNodesDetached() &&
-      largest_text_paint_ != base::TimeTicks()) {
-    largest_text_paint_ = base::TimeTicks();
-    frame_view_->GetPaintTimingDetector().DidChangePerformanceTiming();
-  }
+  need_update_timing_at_frame_end_ = true;
 }
 
 void TextPaintTimingDetector::RegisterNotifySwapTime(
diff --git a/third_party/blink/renderer/core/paint/text_paint_timing_detector.h b/third_party/blink/renderer/core/paint/text_paint_timing_detector.h
index 855dd93..edaf481 100644
--- a/third_party/blink/renderer/core/paint/text_paint_timing_detector.h
+++ b/third_party/blink/renderer/core/paint/text_paint_timing_detector.h
@@ -130,7 +130,7 @@
                           const TextRecord& first_text_paint,
                           unsigned candidate_index) const;
   void TimerFired(TimerBase*);
-  void Analyze();
+  void UpdateCandidate();
 
   void ReportSwapTime(WebWidgetClient::SwapResult result,
                       base::TimeTicks timestamp);
@@ -145,6 +145,7 @@
   bool is_recording_ = true;
 
   bool has_records_changed_ = true;
+  bool need_update_timing_at_frame_end_ = false;
 
   base::TimeTicks largest_text_paint_;
   uint64_t largest_text_paint_size_ = 0;
diff --git a/third_party/blink/renderer/core/paint/text_paint_timing_detector_test.cc b/third_party/blink/renderer/core/paint/text_paint_timing_detector_test.cc
index 673ace70..6f36791 100644
--- a/third_party/blink/renderer/core/paint/text_paint_timing_detector_test.cc
+++ b/third_party/blink/renderer/core/paint/text_paint_timing_detector_test.cc
@@ -96,8 +96,8 @@
                         CurrentTimeTicks());
   }
 
-  void SimulateAnalyze() {
-    GetPaintTimingDetector().GetTextPaintTimingDetector().Analyze();
+  void UpdateCandidate() {
+    GetPaintTimingDetector().GetTextPaintTimingDetector().UpdateCandidate();
   }
 
   Element* AppendFontElementToBody(String content) {
@@ -204,7 +204,7 @@
     <div>small text</div>
   )HTML");
   UpdateAllLifecyclePhasesAndSimulateSwapTime();
-  SimulateAnalyze();
+  UpdateCandidate();
   TimeTicks time2 = CurrentTimeTicks();
   TimeTicks first_largest = LargestPaintStoredResult();
   EXPECT_GE(first_largest, time1);
@@ -213,7 +213,7 @@
   Text* larger_text = GetDocument().createTextNode("a long-long-long text");
   GetDocument().body()->AppendChild(larger_text);
   UpdateAllLifecyclePhasesAndSimulateSwapTime();
-  SimulateAnalyze();
+  UpdateCandidate();
   TimeTicks time3 = CurrentTimeTicks();
   TimeTicks second_largest = LargestPaintStoredResult();
   EXPECT_GE(second_largest, time2);
@@ -307,13 +307,13 @@
   )HTML");
   Element* text = AppendDivElementToBody("text to remove");
   UpdateAllLifecyclePhasesAndSimulateSwapTime();
-  SimulateAnalyze();
+  UpdateCandidate();
   EXPECT_EQ(TextRecordOfLargestTextPaint()->node_id, NodeIdOfText(text));
   EXPECT_NE(LargestPaintStoredResult(), base::TimeTicks());
 
   RemoveElement(text);
   UpdateAllLifecyclePhasesAndSimulateSwapTime();
-  SimulateAnalyze();
+  UpdateCandidate();
   EXPECT_FALSE(TextRecordOfLargestTextPaint());
   EXPECT_EQ(LargestPaintStoredResult(), base::TimeTicks());
 }
diff --git a/third_party/blink/renderer/modules/notifications/OWNERS b/third_party/blink/renderer/modules/notifications/OWNERS
index 2fd0e4b..a5683f2 100644
--- a/third_party/blink/renderer/modules/notifications/OWNERS
+++ b/third_party/blink/renderer/modules/notifications/OWNERS
@@ -1,3 +1,4 @@
+knollr@chromium.org
 peter@chromium.org
 
 # TEAM: platform-capabilities@chromium.org
diff --git a/third_party/blink/renderer/modules/wake_lock/navigator_wake_lock.cc b/third_party/blink/renderer/modules/wake_lock/navigator_wake_lock.cc
index 1f5d29d01..ab0d0e12 100644
--- a/third_party/blink/renderer/modules/wake_lock/navigator_wake_lock.cc
+++ b/third_party/blink/renderer/modules/wake_lock/navigator_wake_lock.cc
@@ -35,7 +35,7 @@
     return ScriptPromise::RejectWithDOMException(
         script_state,
         DOMException::Create(
-            DOMExceptionCode::kSecurityError,
+            DOMExceptionCode::kNotAllowedError,
             "Access to WakeLock features is disallowed by feature policy"));
   }
 
diff --git a/third_party/blink/renderer/modules/webgpu/BUILD.gn b/third_party/blink/renderer/modules/webgpu/BUILD.gn
index c030c37..022639f 100644
--- a/third_party/blink/renderer/modules/webgpu/BUILD.gn
+++ b/third_party/blink/renderer/modules/webgpu/BUILD.gn
@@ -7,8 +7,6 @@
 blink_modules_sources("webgpu") {
   sources = [
     "dawn_callback.h",
-    "dawn_control_client_holder.cc",
-    "dawn_control_client_holder.h",
     "dawn_conversions.cc",
     "dawn_conversions.h",
     "dawn_object.cc",
diff --git a/third_party/blink/renderer/modules/webgpu/DEPS b/third_party/blink/renderer/modules/webgpu/DEPS
index f3113a02..9a81c895 100644
--- a/third_party/blink/renderer/modules/webgpu/DEPS
+++ b/third_party/blink/renderer/modules/webgpu/DEPS
@@ -1,3 +1,10 @@
 include_rules = [
+    "+cc/layers/texture_layer.h",
+    "+cc/layers/texture_layer_client.h",
+    "+cc/resources/cross_thread_shared_bitmap.h",
+    "+gpu/command_buffer/common/mailbox.h",
+    "+gpu/command_buffer/common/shared_image_usage.h",
+    "+gpu/command_buffer/common/sync_token.h",
+    "+gpu/command_buffer/client/shared_image_interface.h",
     "+gpu/command_buffer/client/webgpu_interface.h",
-]
\ No newline at end of file
+]
diff --git a/third_party/blink/renderer/modules/webgpu/dawn_object.cc b/third_party/blink/renderer/modules/webgpu/dawn_object.cc
index a3bd513..0521f0d 100644
--- a/third_party/blink/renderer/modules/webgpu/dawn_object.cc
+++ b/third_party/blink/renderer/modules/webgpu/dawn_object.cc
@@ -4,7 +4,6 @@
 
 #include "third_party/blink/renderer/modules/webgpu/dawn_object.h"
 
-#include "third_party/blink/renderer/modules/webgpu/dawn_control_client_holder.h"
 #include "third_party/blink/renderer/modules/webgpu/gpu_device.h"
 
 namespace blink {
diff --git a/third_party/blink/renderer/modules/webgpu/dawn_object.h b/third_party/blink/renderer/modules/webgpu/dawn_object.h
index 8dbec14..06c66ac 100644
--- a/third_party/blink/renderer/modules/webgpu/dawn_object.h
+++ b/third_party/blink/renderer/modules/webgpu/dawn_object.h
@@ -8,8 +8,8 @@
 #include <dawn/dawn.h>
 
 #include "base/memory/scoped_refptr.h"
-#include "third_party/blink/renderer/modules/webgpu/dawn_control_client_holder.h"
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
+#include "third_party/blink/renderer/platform/graphics/gpu/dawn_control_client_holder.h"
 
 namespace gpu {
 namespace webgpu {
diff --git a/third_party/blink/renderer/modules/webgpu/gpu.cc b/third_party/blink/renderer/modules/webgpu/gpu.cc
index 6cbc2d54..d4829e4 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu.cc
+++ b/third_party/blink/renderer/modules/webgpu/gpu.cc
@@ -12,9 +12,9 @@
 #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/core/inspector/console_message.h"
-#include "third_party/blink/renderer/modules/webgpu/dawn_control_client_holder.h"
 #include "third_party/blink/renderer/modules/webgpu/gpu_adapter.h"
 #include "third_party/blink/renderer/modules/webgpu/gpu_request_adapter_options.h"
+#include "third_party/blink/renderer/platform/graphics/gpu/dawn_control_client_holder.h"
 
 namespace blink {
 
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_adapter.cc b/third_party/blink/renderer/modules/webgpu/gpu_adapter.cc
index d250d4e9..4e89c55 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu_adapter.cc
+++ b/third_party/blink/renderer/modules/webgpu/gpu_adapter.cc
@@ -5,7 +5,6 @@
 #include "third_party/blink/renderer/modules/webgpu/gpu_adapter.h"
 
 #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
-#include "third_party/blink/renderer/modules/webgpu/dawn_control_client_holder.h"
 #include "third_party/blink/renderer/modules/webgpu/gpu_device.h"
 
 namespace blink {
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_canvas_context.cc b/third_party/blink/renderer/modules/webgpu/gpu_canvas_context.cc
index c9fa73f4..5c5f063 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu_canvas_context.cc
+++ b/third_party/blink/renderer/modules/webgpu/gpu_canvas_context.cc
@@ -29,6 +29,16 @@
 
 GPUCanvasContext::~GPUCanvasContext() {}
 
+void GPUCanvasContext::Trace(blink::Visitor* visitor) {
+  visitor->Trace(swapchain_);
+  CanvasRenderingContext::Trace(visitor);
+}
+
+const IntSize& GPUCanvasContext::CanvasSize() const {
+  return Host()->Size();
+}
+
+// CanvasRenderingContext implementation
 CanvasRenderingContext::ContextType GPUCanvasContext::GetContextType() const {
   return CanvasRenderingContext::kContextGPUPresent;
 }
@@ -37,4 +47,37 @@
   result.SetGPUCanvasContext(this);
 }
 
+void GPUCanvasContext::Stop() {
+  if (swapchain_) {
+    swapchain_->Neuter();
+    swapchain_ = nullptr;
+  }
+  stopped_ = true;
+}
+
+cc::Layer* GPUCanvasContext::CcLayer() const {
+  if (swapchain_) {
+    return swapchain_->CcLayer();
+  }
+  return nullptr;
+}
+
+// gpu_canvas_context.idl
+GPUSwapChain* GPUCanvasContext::configureSwapChain(
+    const GPUSwapChainDescriptor* descriptor) {
+  // TODO(cwallez@chromium.org): This should probably throw an exception,
+  // implement the exception when the WebGPU group decided what it should be.
+  if (stopped_) {
+    return nullptr;
+  }
+
+  if (swapchain_) {
+    // Tell any previous swapchain that it will no longer be used and can
+    // destroy all its resources (and produce errors when used).
+    swapchain_->Neuter();
+  }
+  swapchain_ = GPUSwapChain::Create(this, descriptor);
+  return swapchain_;
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_canvas_context.h b/third_party/blink/renderer/modules/webgpu/gpu_canvas_context.h
index aa4b69d..0360222 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu_canvas_context.h
+++ b/third_party/blink/renderer/modules/webgpu/gpu_canvas_context.h
@@ -7,14 +7,17 @@
 
 #include "third_party/blink/renderer/core/html/canvas/canvas_rendering_context.h"
 #include "third_party/blink/renderer/core/html/canvas/canvas_rendering_context_factory.h"
+#include "third_party/blink/renderer/modules/webgpu/gpu_swap_chain.h"
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
 #include "third_party/blink/renderer/platform/graphics/static_bitmap_image.h"
 
 namespace blink {
 
+class GPUSwapChain;
+class GPUSwapChainDescriptor;
+
 // A GPUCanvasContext does little by itself and basically just binds a canvas
-// and a GPUSwapChain together and forwards calls from one to the other. The
-// logic that are in other CanvasRenderingContext is in GPUSwapChain instead.
+// and a GPUSwapChain together and forwards calls from one to the other.
 class GPUCanvasContext : public CanvasRenderingContext {
   DEFINE_WRAPPERTYPEINFO();
 
@@ -36,6 +39,9 @@
                    const CanvasContextCreationAttributesCore&);
   ~GPUCanvasContext() override;
 
+  void Trace(blink::Visitor*) override;
+  const IntSize& CanvasSize() const;
+
   // CanvasRenderingContext implementation
   ContextType GetContextType() const override;
   void SetCanvasGetContextResult(RenderingContext&) final;
@@ -51,14 +57,16 @@
   void SetFilterQuality(SkFilterQuality) final {}
   bool IsPaintable() const final { return true; }
   int ExternallyAllocatedBufferCountPerPixel() final { return 1; }
-  void Stop() override {}
-  cc::Layer* CcLayer() const final { return nullptr; }
+  void Stop() final;
+  cc::Layer* CcLayer() const final;
 
   // gpu_canvas_context.idl
-  // TODO(crbug.com/877147): implement GPUCanvasContext.
+  GPUSwapChain* configureSwapChain(const GPUSwapChainDescriptor* descriptor);
 
  private:
   DISALLOW_COPY_AND_ASSIGN(GPUCanvasContext);
+  Member<GPUSwapChain> swapchain_;
+  bool stopped_ = false;
 };
 
 DEFINE_TYPE_CASTS(GPUCanvasContext,
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_canvas_context.idl b/third_party/blink/renderer/modules/webgpu/gpu_canvas_context.idl
index 406e125..71b4faf 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu_canvas_context.idl
+++ b/third_party/blink/renderer/modules/webgpu/gpu_canvas_context.idl
@@ -7,4 +7,5 @@
 [
     RuntimeEnabled=WebGPU
 ] interface GPUCanvasContext {
+    GPUSwapChain configureSwapChain(GPUSwapChainDescriptor descriptor);
 };
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_command_encoder.cc b/third_party/blink/renderer/modules/webgpu/gpu_command_encoder.cc
index 25d194906..79a918eb 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu_command_encoder.cc
+++ b/third_party/blink/renderer/modules/webgpu/gpu_command_encoder.cc
@@ -4,7 +4,6 @@
 
 #include "third_party/blink/renderer/modules/webgpu/gpu_command_encoder.h"
 
-#include "third_party/blink/renderer/modules/webgpu/dawn_control_client_holder.h"
 #include "third_party/blink/renderer/modules/webgpu/dawn_conversions.h"
 #include "third_party/blink/renderer/modules/webgpu/gpu_buffer.h"
 #include "third_party/blink/renderer/modules/webgpu/gpu_buffer_copy_view.h"
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_device.cc b/third_party/blink/renderer/modules/webgpu/gpu_device.cc
index a996f3d..3b76bec 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu_device.cc
+++ b/third_party/blink/renderer/modules/webgpu/gpu_device.cc
@@ -8,8 +8,6 @@
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/core/inspector/console_message.h"
-#include "third_party/blink/renderer/modules/webgpu/dawn_control_client_holder.h"
-
 #include "third_party/blink/renderer/modules/webgpu/gpu_adapter.h"
 #include "third_party/blink/renderer/modules/webgpu/gpu_bind_group.h"
 #include "third_party/blink/renderer/modules/webgpu/gpu_bind_group_layout.h"
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_swap_chain.cc b/third_party/blink/renderer/modules/webgpu/gpu_swap_chain.cc
index a39dbfc..799bdb1 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu_swap_chain.cc
+++ b/third_party/blink/renderer/modules/webgpu/gpu_swap_chain.cc
@@ -4,24 +4,82 @@
 
 #include "third_party/blink/renderer/modules/webgpu/gpu_swap_chain.h"
 
+#include "third_party/blink/renderer/modules/webgpu/dawn_conversions.h"
+#include "third_party/blink/renderer/modules/webgpu/gpu_canvas_context.h"
 #include "third_party/blink/renderer/modules/webgpu/gpu_device.h"
+#include "third_party/blink/renderer/modules/webgpu/gpu_swap_chain_descriptor.h"
+#include "third_party/blink/renderer/modules/webgpu/gpu_texture.h"
 
 namespace blink {
 
 // static
-GPUSwapChain* GPUSwapChain::Create(GPUDevice* device,
-                                   DawnSwapChain swap_chain) {
-  return MakeGarbageCollected<GPUSwapChain>(device, swap_chain);
+GPUSwapChain* GPUSwapChain::Create(GPUCanvasContext* context,
+                                   const GPUSwapChainDescriptor* descriptor) {
+  return MakeGarbageCollected<GPUSwapChain>(context, descriptor);
 }
 
-GPUSwapChain::GPUSwapChain(GPUDevice* device, DawnSwapChain swap_chain)
-    : DawnObject<DawnSwapChain>(device, swap_chain) {}
+GPUSwapChain::GPUSwapChain(GPUCanvasContext* context,
+                           const GPUSwapChainDescriptor* descriptor)
+    : DawnObjectBase(descriptor->device()->GetDawnControlClient()),
+      device_(descriptor->device()),
+      context_(context),
+      usage_(AsDawnEnum<DawnTextureUsageBit>(descriptor->usage())) {
+  swap_buffers_ = base::AdoptRef(
+      new WebGPUSwapBufferProvider(this, GetDawnControlClient(), usage_));
+}
 
 GPUSwapChain::~GPUSwapChain() {
-  if (IsDawnControlClientDestroyed()) {
-    return;
+  Neuter();
+}
+
+void GPUSwapChain::Trace(blink::Visitor* visitor) {
+  visitor->Trace(device_);
+  visitor->Trace(context_);
+  visitor->Trace(texture_);
+  ScriptWrappable::Trace(visitor);
+}
+
+void GPUSwapChain::Neuter() {
+  texture_ = nullptr;
+
+  DCHECK(swap_buffers_);
+  if (!swap_buffers_) {
+    swap_buffers_->Neuter();
+    swap_buffers_ = nullptr;
   }
-  GetProcs().swapChainRelease(GetHandle());
+}
+
+cc::Layer* GPUSwapChain::CcLayer() {
+  DCHECK(swap_buffers_);
+  return swap_buffers_->CcLayer();
+}
+
+// gpu_swap_chain.idl
+GPUTexture* GPUSwapChain::getCurrentTexture() {
+  if (!swap_buffers_) {
+    // TODO(cwallez@chromium.org) return an error texture.
+    return nullptr;
+  }
+
+  // Calling getCurrentTexture returns a texture that is valid until the
+  // animation frame it gets presented. If getCurrenTexture is called multiple
+  // time, the same texture should be returned. |texture_| is set to null when
+  // presented so that we know we should create a new one.
+  if (texture_) {
+    return texture_;
+  }
+
+  DawnTexture dawn_client_texture = swap_buffers_->GetNewTexture(
+      device_->GetHandle(), context_->CanvasSize());
+  DCHECK(dawn_client_texture);
+  texture_ = MakeGarbageCollected<GPUTexture>(device_, dawn_client_texture);
+  return texture_;
+}
+
+// WebGPUSwapBufferProvider::Client implementation
+void GPUSwapChain::OnTextureTransferred() {
+  DCHECK(texture_);
+  texture_ = nullptr;
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_swap_chain.h b/third_party/blink/renderer/modules/webgpu/gpu_swap_chain.h
index 9a4c7ae..dcd998c4 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu_swap_chain.h
+++ b/third_party/blink/renderer/modules/webgpu/gpu_swap_chain.h
@@ -6,24 +6,51 @@
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_WEBGPU_GPU_SWAP_CHAIN_H_
 
 #include "third_party/blink/renderer/modules/webgpu/dawn_object.h"
+#include "third_party/blink/renderer/platform/graphics/gpu/webgpu_swap_buffer_provider.h"
+
+namespace cc {
+class Layer;
+}
 
 namespace blink {
 
+class GPUCanvasContext;
 class GPUDevice;
+class GPUSwapChainDescriptor;
+class GPUTexture;
 
-class GPUSwapChain : public DawnObject<DawnSwapChain> {
+class GPUSwapChain : public DawnObjectBase,
+                     public WebGPUSwapBufferProvider::Client {
   DEFINE_WRAPPERTYPEINFO();
 
  public:
-  static GPUSwapChain* Create(GPUDevice* device, DawnSwapChain swap_chain);
-  explicit GPUSwapChain(GPUDevice* devivce, DawnSwapChain swap_chain);
+  static GPUSwapChain* Create(GPUCanvasContext* context,
+                              const GPUSwapChainDescriptor* descriptor);
+  explicit GPUSwapChain(GPUCanvasContext* context,
+                        const GPUSwapChainDescriptor* descriptor);
   ~GPUSwapChain() override;
 
+  void Trace(blink::Visitor* visitor) override;
+
+  void Neuter();
+  cc::Layer* CcLayer();
+
   // gpu_swap_chain.idl
-  // TODO(crbug.com/877147): implement GPUSwapChain.
+  GPUTexture* getCurrentTexture();
+
+  // WebGPUSwapBufferProvider::Client implementation
+  void OnTextureTransferred() override;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(GPUSwapChain);
+
+  scoped_refptr<WebGPUSwapBufferProvider> swap_buffers_;
+
+  Member<GPUDevice> device_;
+  Member<GPUCanvasContext> context_;
+  DawnTextureUsageBit usage_;
+
+  Member<GPUTexture> texture_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_swap_chain.idl b/third_party/blink/renderer/modules/webgpu/gpu_swap_chain.idl
index 5a20e4af..b3baf97 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu_swap_chain.idl
+++ b/third_party/blink/renderer/modules/webgpu/gpu_swap_chain.idl
@@ -7,4 +7,5 @@
 [
     RuntimeEnabled=WebGPU
 ] interface GPUSwapChain {
+    GPUTexture getCurrentTexture();
 };
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_swap_chain_descriptor.idl b/third_party/blink/renderer/modules/webgpu/gpu_swap_chain_descriptor.idl
index 84b443b..365a07ef 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu_swap_chain_descriptor.idl
+++ b/third_party/blink/renderer/modules/webgpu/gpu_swap_chain_descriptor.idl
@@ -5,7 +5,7 @@
 // https://github.com/gpuweb/gpuweb/blob/master/design/sketch.webidl
 
 dictionary GPUSwapChainDescriptor {
-    required GPUCanvasContext context;
+    required GPUDevice device;
     required GPUTextureFormat format;
     GPUTextureUsageFlags usage = 16;  // GPUTextureUsage.OUTPUT_ATTACHMENT
 };
diff --git a/third_party/blink/renderer/platform/BUILD.gn b/third_party/blink/renderer/platform/BUILD.gn
index 8420081..277254a 100644
--- a/third_party/blink/renderer/platform/BUILD.gn
+++ b/third_party/blink/renderer/platform/BUILD.gn
@@ -963,6 +963,8 @@
     "graphics/filters/spot_light_source.h",
     "graphics/generated_image.cc",
     "graphics/generated_image.h",
+    "graphics/gpu/dawn_control_client_holder.cc",
+    "graphics/gpu/dawn_control_client_holder.h",
     "graphics/gpu/drawing_buffer.cc",
     "graphics/gpu/drawing_buffer.h",
     "graphics/gpu/extensions_3d_util.cc",
@@ -977,6 +979,8 @@
     "graphics/gpu/shared_gpu_context.h",
     "graphics/gpu/webgl_image_conversion.cc",
     "graphics/gpu/webgl_image_conversion.h",
+    "graphics/gpu/webgpu_swap_buffer_provider.cc",
+    "graphics/gpu/webgpu_swap_buffer_provider.h",
     "graphics/gpu/xr_frame_transport.cc",
     "graphics/gpu/xr_frame_transport.h",
     "graphics/gpu/xr_webgl_drawing_buffer.cc",
@@ -1736,6 +1740,7 @@
     "graphics/gpu/drawing_buffer_test.cc",
     "graphics/gpu/shared_gpu_context_test.cc",
     "graphics/gpu/webgl_image_conversion_test.cc",
+    "graphics/gpu/webgpu_swap_buffer_provider_test.cc",
     "graphics/graphics_context_test.cc",
     "graphics/paint/cull_rect_test.cc",
     "graphics/paint/display_item_client_test.cc",
diff --git a/third_party/blink/renderer/platform/geometry/float_size.h b/third_party/blink/renderer/platform/geometry/float_size.h
index 74f13ab..b5e26b5 100644
--- a/third_party/blink/renderer/platform/geometry/float_size.h
+++ b/third_party/blink/renderer/platform/geometry/float_size.h
@@ -81,7 +81,10 @@
            -std::numeric_limits<float>::epsilon() < height_ &&
            height_ < std::numeric_limits<float>::epsilon();
   }
-  bool IsValid() const { return !std::isnan(width_) && !std::isnan(height_); }
+  bool IsValid() const {
+    return width_ != -std::numeric_limits<float>::infinity() &&
+           height_ != -std::numeric_limits<float>::infinity();
+  }
   bool IsExpressibleAsIntSize() const;
 
   float AspectRatio() const { return width_ / height_; }
@@ -232,11 +235,14 @@
   STATIC_ONLY(DefaultHash);
   struct Hash {
     STATIC_ONLY(Hash);
+    typedef typename IntTypes<sizeof(float)>::UnsignedType Bits;
     static unsigned GetHash(const blink::FloatSize& key) {
-      return HashInts(key.Width(), key.Height());
+      return HashInts(bit_cast<Bits>(key.Width()),
+                      bit_cast<Bits>(key.Height()));
     }
     static bool Equal(const blink::FloatSize& a, const blink::FloatSize& b) {
-      return a == b;
+      return bit_cast<Bits>(a.Width()) == bit_cast<Bits>(b.Width()) &&
+             bit_cast<Bits>(a.Height()) == bit_cast<Bits>(b.Height());
     }
     static const bool safe_to_compare_to_empty_or_deleted = true;
   };
@@ -245,11 +251,14 @@
 template <>
 struct HashTraits<blink::FloatSize> : GenericHashTraits<blink::FloatSize> {
   STATIC_ONLY(HashTraits);
-  static const bool kEmptyValueIsZero = true;
-  static blink::FloatSize EmptyValue() { return blink::FloatSize(); }
+  static const bool kEmptyValueIsZero = false;
+  static blink::FloatSize EmptyValue() {
+    return blink::FloatSize(std::numeric_limits<float>::infinity(),
+                            std::numeric_limits<float>::infinity());
+  }
   static void ConstructDeletedValue(blink::FloatSize& slot, bool) {
-    float quiet_nan = std::numeric_limits<float>::quiet_NaN();
-    slot = blink::FloatSize(quiet_nan, quiet_nan);
+    slot = blink::FloatSize(-std::numeric_limits<float>::infinity(),
+                            -std::numeric_limits<float>::infinity());
   }
   static bool IsDeletedValue(const blink::FloatSize& value) {
     return !value.IsValid();
diff --git a/third_party/blink/renderer/platform/graphics/filters/fe_color_matrix.cc b/third_party/blink/renderer/platform/graphics/filters/fe_color_matrix.cc
index e87d8af..ce5709eb 100644
--- a/third_party/blink/renderer/platform/graphics/filters/fe_color_matrix.cc
+++ b/third_party/blink/renderer/platform/graphics/filters/fe_color_matrix.cc
@@ -59,7 +59,7 @@
   return true;
 }
 
-static void SaturateMatrix(float s, SkScalar matrix[kColorMatrixSize]) {
+static void SaturateMatrix(float s, float matrix[kColorMatrixSize]) {
   matrix[0] = 0.213f + 0.787f * s;
   matrix[1] = 0.715f - 0.715f * s;
   matrix[2] = 0.072f - 0.072f * s;
@@ -77,7 +77,7 @@
   matrix[19] = 0;
 }
 
-static void HueRotateMatrix(float hue, SkScalar matrix[kColorMatrixSize]) {
+static void HueRotateMatrix(float hue, float matrix[kColorMatrixSize]) {
   float cos_hue = cosf(hue * kPiFloat / 180);
   float sin_hue = sinf(hue * kPiFloat / 180);
   matrix[0] = 0.213f + cos_hue * 0.787f - sin_hue * 0.213f;
@@ -97,8 +97,8 @@
   matrix[19] = 0;
 }
 
-static void LuminanceToAlphaMatrix(SkScalar matrix[kColorMatrixSize]) {
-  memset(matrix, 0, kColorMatrixSize * sizeof(SkScalar));
+static void LuminanceToAlphaMatrix(float matrix[kColorMatrixSize]) {
+  memset(matrix, 0, kColorMatrixSize * sizeof(float));
   matrix[15] = 0.2125f;
   matrix[16] = 0.7154f;
   matrix[17] = 0.0721f;
@@ -107,8 +107,8 @@
 static sk_sp<SkColorFilter> CreateColorFilter(ColorMatrixType type,
                                               const Vector<float>& values) {
   // Use defaults if values contains too few/many values.
-  SkScalar matrix[kColorMatrixSize];
-  memset(matrix, 0, kColorMatrixSize * sizeof(SkScalar));
+  float matrix[kColorMatrixSize];
+  memset(matrix, 0, kColorMatrixSize * sizeof(float));
   matrix[0] = matrix[6] = matrix[12] = matrix[18] = 1;
 
   switch (type) {
@@ -119,10 +119,6 @@
         for (unsigned i = 0; i < kColorMatrixSize; ++i)
           matrix[i] = values[i];
       }
-      matrix[4] *= SkScalar(255);
-      matrix[9] *= SkScalar(255);
-      matrix[14] *= SkScalar(255);
-      matrix[19] *= SkScalar(255);
       break;
     case FECOLORMATRIX_TYPE_SATURATE:
       if (values.size() == 1)
@@ -136,7 +132,7 @@
       LuminanceToAlphaMatrix(matrix);
       break;
   }
-  return SkColorFilters::MatrixRowMajor255(matrix);
+  return SkColorFilters::Matrix(matrix);
 }
 
 bool FEColorMatrix::AffectsTransparentPixels() const {
diff --git a/third_party/blink/renderer/platform/graphics/filters/source_alpha.cc b/third_party/blink/renderer/platform/graphics/filters/source_alpha.cc
index b1d52cb2..a23b3fd 100644
--- a/third_party/blink/renderer/platform/graphics/filters/source_alpha.cc
+++ b/third_party/blink/renderer/platform/graphics/filters/source_alpha.cc
@@ -38,9 +38,9 @@
 sk_sp<PaintFilter> SourceAlpha::CreateImageFilter() {
   sk_sp<PaintFilter> source_graphic(paint_filter_builder::Build(
       InputEffect(0), OperatingInterpolationSpace()));
-  SkScalar matrix[20] = {0, 0, 0, 0, 0, 0, 0, 0, 0,          0,
-                         0, 0, 0, 0, 0, 0, 0, 0, SK_Scalar1, 0};
-  sk_sp<SkColorFilter> color_filter = SkColorFilters::MatrixRowMajor255(matrix);
+  float matrix[20] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                      0, 0, 0, 0, 0, 0, 0, 0, 1, 0};
+  sk_sp<SkColorFilter> color_filter = SkColorFilters::Matrix(matrix);
   return sk_make_sp<ColorFilterPaintFilter>(std::move(color_filter),
                                             std::move(source_graphic));
 }
diff --git a/third_party/blink/renderer/platform/graphics/gpu/DEPS b/third_party/blink/renderer/platform/graphics/gpu/DEPS
index 5effcc4e..b7824e6 100644
--- a/third_party/blink/renderer/platform/graphics/gpu/DEPS
+++ b/third_party/blink/renderer/platform/graphics/gpu/DEPS
@@ -6,6 +6,8 @@
     "+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/client/webgpu_interface.h",
+    "+gpu/command_buffer/client/webgpu_interface_stub.h",
     "+gpu/command_buffer/common/capabilities.h",
     "+gpu/command_buffer/common/gpu_memory_buffer_support.h",
     "+gpu/command_buffer/common/mailbox_holder.h",
diff --git a/third_party/blink/renderer/modules/webgpu/dawn_control_client_holder.cc b/third_party/blink/renderer/platform/graphics/gpu/dawn_control_client_holder.cc
similarity index 92%
rename from third_party/blink/renderer/modules/webgpu/dawn_control_client_holder.cc
rename to third_party/blink/renderer/platform/graphics/gpu/dawn_control_client_holder.cc
index 063cb30..594e4408 100644
--- a/third_party/blink/renderer/modules/webgpu/dawn_control_client_holder.cc
+++ b/third_party/blink/renderer/platform/graphics/gpu/dawn_control_client_holder.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "third_party/blink/renderer/modules/webgpu/dawn_control_client_holder.h"
+#include "third_party/blink/renderer/platform/graphics/gpu/dawn_control_client_holder.h"
 
 #include "base/logging.h"
 #include "gpu/command_buffer/client/webgpu_interface.h"
diff --git a/third_party/blink/renderer/modules/webgpu/dawn_control_client_holder.h b/third_party/blink/renderer/platform/graphics/gpu/dawn_control_client_holder.h
similarity index 73%
rename from third_party/blink/renderer/modules/webgpu/dawn_control_client_holder.h
rename to third_party/blink/renderer/platform/graphics/gpu/dawn_control_client_holder.h
index 07fd46fe..20e93d3e 100644
--- a/third_party/blink/renderer/modules/webgpu/dawn_control_client_holder.h
+++ b/third_party/blink/renderer/platform/graphics/gpu/dawn_control_client_holder.h
@@ -2,12 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_WEBGPU_DAWN_CONTROL_CLIENT_HOLDER_H_
-#define THIRD_PARTY_BLINK_RENDERER_MODULES_WEBGPU_DAWN_CONTROL_CLIENT_HOLDER_H_
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GPU_DAWN_CONTROL_CLIENT_HOLDER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GPU_DAWN_CONTROL_CLIENT_HOLDER_H_
 
 #include <dawn/dawn.h>
 
 #include "third_party/blink/public/platform/web_graphics_context_3d_provider.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
 #include "third_party/blink/renderer/platform/wtf/ref_counted.h"
 
 namespace gpu {
@@ -23,7 +24,8 @@
 // This class holds the WebGPUInterface and a |destroyed_| flag.
 // DawnControlClientHolder::MarkDestroyed() should be called if the
 // backing WebGPUInterface has been destroyed.
-class DawnControlClientHolder : public RefCounted<DawnControlClientHolder> {
+class PLATFORM_EXPORT DawnControlClientHolder
+    : public RefCounted<DawnControlClientHolder> {
  public:
   DawnControlClientHolder(
       std::unique_ptr<WebGraphicsContext3DProvider> context_provider);
@@ -46,4 +48,4 @@
 
 }  // namespace blink
 
-#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_WEBGPU_DAWN_CONTROL_CLIENT_HOLDER_H_
+#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GPU_DAWN_CONTROL_CLIENT_HOLDER_H_
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 ab95665..fd518c3 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
@@ -8,6 +8,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/client/webgpu_interface.h"
 #include "gpu/command_buffer/common/capabilities.h"
 #include "gpu/config/gpu_feature_info.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -37,12 +38,15 @@
   WebGraphicsContext3DProviderForTests(
       std::unique_ptr<gpu::gles2::GLES2Interface> gl)
       : gl_(std::move(gl)) {}
+  WebGraphicsContext3DProviderForTests(
+      std::unique_ptr<gpu::webgpu::WebGPUInterface> webgpu)
+      : webgpu_(std::move(webgpu)) {}
 
   gpu::gles2::GLES2Interface* ContextGL() override { return gl_.get(); }
-
-  // Not used by WebGL code.
   GrContext* GetGrContext() override { return nullptr; }
-  gpu::webgpu::WebGPUInterface* WebGPUInterface() override { return nullptr; }
+  gpu::webgpu::WebGPUInterface* WebGPUInterface() override {
+    return webgpu_.get();
+  }
   bool BindToCurrentThread() override { return false; }
   const gpu::Capabilities& GetCapabilities() const override {
     return capabilities_;
@@ -66,27 +70,12 @@
  private:
   cc::StubDecodeCache image_decode_cache_;
   std::unique_ptr<gpu::gles2::GLES2Interface> gl_;
+  std::unique_ptr<gpu::webgpu::WebGPUInterface> webgpu_;
   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.
-GLenum ImageCHROMIUMTextureTarget() {
-#if defined(OS_MACOSX)
-  return GC3D_TEXTURE_RECTANGLE_ARB;
-#else
-  return GL_TEXTURE_2D;
-#endif
-}
-
-// The target to use when preparing a mailbox texture.
-GLenum DrawingBufferTextureTarget() {
-  if (RuntimeEnabledFeatures::WebGLImageChromiumEnabled())
-    return ImageCHROMIUMTextureTarget();
-  return GL_TEXTURE_2D;
-}
-
 class GLES2InterfaceForTests : public gpu::gles2::GLES2InterfaceStub,
                                public DrawingBuffer::Client {
  public:
@@ -243,7 +232,7 @@
 
   MOCK_METHOD1(BindTexImage2DMock, void(GLint imageId));
   void BindTexImage2DCHROMIUM(GLenum target, GLint image_id) override {
-    if (target == ImageCHROMIUMTextureTarget()) {
+    if (target == kImageCHROMIUMTarget) {
       texture_sizes_.Set(bound_textures_[target],
                          image_sizes_.find(image_id)->value);
       image_to_texture_map_.Set(image_id, bound_textures_[target]);
@@ -253,7 +242,7 @@
 
   MOCK_METHOD1(ReleaseTexImage2DMock, void(GLint imageId));
   void ReleaseTexImage2DCHROMIUM(GLenum target, GLint image_id) override {
-    if (target == ImageCHROMIUMTextureTarget()) {
+    if (target == kImageCHROMIUMTarget) {
       image_sizes_.Set(current_image_id_, IntSize());
       image_to_texture_map_.erase(image_id);
       ReleaseTexImage2DMock(image_id);
@@ -377,6 +366,13 @@
   }
 
  private:
+  // The target to use when binding a texture to a Chromium image.
+#if defined(OS_MACOSX)
+  static constexpr GLuint kImageCHROMIUMTarget = GC3D_TEXTURE_RECTANGLE_ARB;
+#else
+  static constexpr GLuint kImageCHROMIUMTarget = GL_TEXTURE_2D;
+#endif
+
   std::map<GLenum, GLuint> bound_textures_;
 
   // State tracked to verify that it is restored correctly.
diff --git a/third_party/blink/renderer/platform/graphics/gpu/webgpu_swap_buffer_provider.cc b/third_party/blink/renderer/platform/graphics/gpu/webgpu_swap_buffer_provider.cc
new file mode 100644
index 0000000..39789d5
--- /dev/null
+++ b/third_party/blink/renderer/platform/graphics/gpu/webgpu_swap_buffer_provider.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/graphics/gpu/webgpu_swap_buffer_provider.h"
+
+#include "gpu/GLES2/gl2extchromium.h"
+#include "gpu/command_buffer/client/shared_image_interface.h"
+#include "gpu/command_buffer/client/webgpu_interface.h"
+#include "gpu/command_buffer/common/shared_image_usage.h"
+#include "third_party/blink/renderer/platform/graphics/graphics_layer.h"
+
+namespace blink {
+
+WebGPUSwapBufferProvider::WebGPUSwapBufferProvider(
+    Client* client,
+    scoped_refptr<DawnControlClientHolder> dawn_control_client,
+    DawnTextureUsageBit usage)
+    : dawn_control_client_(dawn_control_client),
+      client_(client),
+      usage_(usage) {
+  // Create a layer that will be used by the canvas and will ask for a
+  // SharedImage each frame.
+  layer_ = cc::TextureLayer::CreateForMailbox(this);
+
+  layer_->SetIsDrawable(true);
+  layer_->SetBlendBackgroundColor(false);
+  layer_->SetNearestNeighbor(true);
+  // TODO(cwallez@chromium.org): These flags aren't taken into account when the
+  // layer is promoted to an overlay. Make sure we have fallback / emulation
+  // paths to keep the rendering correct in that cases.
+  layer_->SetContentsOpaque(true);
+  layer_->SetPremultipliedAlpha(true);
+
+  GraphicsLayer::RegisterContentsLayer(layer_.get());
+}
+
+WebGPUSwapBufferProvider::~WebGPUSwapBufferProvider() {
+  Neuter();
+}
+
+cc::Layer* WebGPUSwapBufferProvider::CcLayer() {
+  DCHECK(!neutered_);
+  return layer_.get();
+}
+
+void WebGPUSwapBufferProvider::Neuter() {
+  if (neutered_) {
+    return;
+  }
+
+  if (layer_) {
+    GraphicsLayer::UnregisterContentsLayer(layer_.get());
+    layer_->ClearClient();
+    layer_ = nullptr;
+  }
+
+  if (current_swap_buffer_) {
+    // Ensure we wait for previous WebGPU commands before destroying the shared
+    // image.
+    gpu::webgpu::WebGPUInterface* webgpu = dawn_control_client_->GetInterface();
+    webgpu->GenUnverifiedSyncTokenCHROMIUM(
+        current_swap_buffer_->access_finished_token.GetData());
+    current_swap_buffer_ = nullptr;
+  }
+
+  client_ = nullptr;
+  neutered_ = true;
+}
+
+DawnTexture WebGPUSwapBufferProvider::GetNewTexture(DawnDevice device,
+                                                    const IntSize& size) {
+  DCHECK(!current_swap_buffer_);
+
+  gpu::webgpu::WebGPUInterface* webgpu = dawn_control_client_->GetInterface();
+  gpu::SharedImageInterface* sii =
+      dawn_control_client_->GetContextProvider()->SharedImageInterface();
+
+  // Create a new swap buffer.
+  // TODO(cwallez@chromium.org): have some recycling mechanism.
+  gpu::Mailbox mailbox = sii->CreateSharedImage(
+      viz::RGBA_8888, static_cast<gfx::Size>(size),
+      gfx::ColorSpace::CreateSRGB(),
+      gpu::SHARED_IMAGE_USAGE_WEBGPU | gpu::SHARED_IMAGE_USAGE_DISPLAY);
+  gpu::SyncToken creation_token = sii->GenUnverifiedSyncToken();
+
+  current_swap_buffer_ = base::AdoptRef(new SwapBuffer(
+      this, mailbox, creation_token, static_cast<gfx::Size>(size)));
+
+  // Make sure previous Dawn wire commands are sent so that for example the ID
+  // is freed before we associate the SharedImage.
+  webgpu->FlushCommands();
+
+  // Ensure the shared image is allocated service-side before working with it
+  webgpu->WaitSyncTokenCHROMIUM(
+      current_swap_buffer_->access_finished_token.GetConstData());
+
+  // Associate the mailbox to a dawn_wire client DawnTexture object
+  gpu::webgpu::ReservedTexture reservation = webgpu->ReserveTexture(device);
+  DCHECK(reservation.texture);
+  wire_texture_id_ = reservation.id;
+  wire_texture_generation_ = reservation.generation;
+
+  webgpu->AssociateMailbox(
+      0, 0, reservation.id, reservation.generation, usage_,
+      reinterpret_cast<GLbyte*>(&current_swap_buffer_->mailbox));
+
+  // When the page request a texture it means we'll need to present it on the
+  // next animation frame.
+  layer_->SetNeedsDisplay();
+
+  return reservation.texture;
+}
+
+bool WebGPUSwapBufferProvider::PrepareTransferableResource(
+    cc::SharedBitmapIdRegistrar* bitmap_registrar,
+    viz::TransferableResource* out_resource,
+    std::unique_ptr<viz::SingleReleaseCallback>* out_release_callback) {
+  DCHECK(!neutered_);
+
+  if (!current_swap_buffer_ || neutered_) {
+    return false;
+  }
+
+  DCHECK(client_);
+  client_->OnTextureTransferred();
+
+  // Make Dawn relinquish access to the texture so it can be used by the
+  // compositor. This will call dawn::Texture::Destroy so that further accesses
+  // to the texture are errors.
+  gpu::webgpu::WebGPUInterface* webgpu = dawn_control_client_->GetInterface();
+  DCHECK_NE(wire_texture_id_, 0u);
+  webgpu->DissociateMailbox(wire_texture_id_, wire_texture_generation_);
+
+  // Make the compositor wait on previous Dawn commands.
+  webgpu->GenUnverifiedSyncTokenCHROMIUM(
+      current_swap_buffer_->access_finished_token.GetData());
+
+  // Populate the output resource
+  *out_resource = viz::TransferableResource::MakeGLOverlay(
+      current_swap_buffer_->mailbox, GL_LINEAR, GL_TEXTURE_RECTANGLE_ARB,
+      current_swap_buffer_->access_finished_token, current_swap_buffer_->size,
+      false);
+  out_resource->color_space = gfx::ColorSpace::CreateSRGB();
+  out_resource->format = viz::RGBA_8888;
+
+  // This holds a ref on the SwapBuffers that will keep it alive until the
+  // mailbox is released (and while the release callback is running).
+  auto func = WTF::Bind(&WebGPUSwapBufferProvider::MailboxReleased,
+                        scoped_refptr<WebGPUSwapBufferProvider>(this),
+                        current_swap_buffer_);
+  *out_release_callback = viz::SingleReleaseCallback::Create(std::move(func));
+
+  current_swap_buffer_ = nullptr;
+  wire_texture_id_ = 0;
+  wire_texture_generation_ = 0;
+
+  return true;
+}
+
+void WebGPUSwapBufferProvider::MailboxReleased(
+    scoped_refptr<SwapBuffer> swap_buffer,
+    const gpu::SyncToken& sync_token,
+    bool lost_resource) {
+  // Update the SyncToken to ensure that we will wait for it even if we
+  // immediately destroy this buffer.
+  swap_buffer->access_finished_token = sync_token;
+}
+
+WebGPUSwapBufferProvider::SwapBuffer::SwapBuffer(
+    WebGPUSwapBufferProvider* swap_buffers,
+    gpu::Mailbox mailbox,
+    gpu::SyncToken creation_token,
+    gfx::Size size)
+    : size(size),
+      mailbox(mailbox),
+      swap_buffers(swap_buffers),
+      access_finished_token(creation_token) {}
+
+WebGPUSwapBufferProvider::SwapBuffer::~SwapBuffer() {
+  gpu::SharedImageInterface* sii =
+      swap_buffers->dawn_control_client_->GetContextProvider()
+          ->SharedImageInterface();
+  sii->DestroySharedImage(access_finished_token, mailbox);
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/graphics/gpu/webgpu_swap_buffer_provider.h b/third_party/blink/renderer/platform/graphics/gpu/webgpu_swap_buffer_provider.h
new file mode 100644
index 0000000..6729a082
--- /dev/null
+++ b/third_party/blink/renderer/platform/graphics/gpu/webgpu_swap_buffer_provider.h
@@ -0,0 +1,95 @@
+// 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_GRAPHICS_GPU_WEBGPU_SWAP_BUFFER_PROVIDER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GPU_WEBGPU_SWAP_BUFFER_PROVIDER_H_
+
+#include "cc/layers/texture_layer.h"
+#include "cc/layers/texture_layer_client.h"
+#include "gpu/command_buffer/common/mailbox.h"
+#include "gpu/command_buffer/common/sync_token.h"
+#include "third_party/blink/renderer/platform/geometry/int_size.h"
+#include "third_party/blink/renderer/platform/graphics/gpu/dawn_control_client_holder.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/functional.h"
+#include "third_party/blink/renderer/platform/wtf/ref_counted.h"
+
+namespace blink {
+
+// WebGPUSwapBufferProvider contains the cc::Layer used by the swapchain as well
+// as the resources used for the layer. It is separate from GPUSwapChain so that
+// it can be kept alive via refcounting instead of garbage collection and so it
+// can live in blink_platform and use gpu:: or viz:: types.
+class PLATFORM_EXPORT WebGPUSwapBufferProvider
+    : public cc::TextureLayerClient,
+      public RefCounted<WebGPUSwapBufferProvider> {
+ public:
+  class Client {
+   public:
+    // Called to make the WebGPU/Dawn stop accessing the texture prior to its
+    // transfer to the compositor.
+    virtual void OnTextureTransferred() = 0;
+  };
+
+  WebGPUSwapBufferProvider(
+      Client* client,
+      scoped_refptr<DawnControlClientHolder> dawn_control_client,
+      DawnTextureUsageBit usage);
+  ~WebGPUSwapBufferProvider() override;
+
+  cc::Layer* CcLayer();
+  void Neuter();
+  DawnTexture GetNewTexture(DawnDevice device, const IntSize& size);
+
+  // cc::TextureLayerClient implementation.
+  bool PrepareTransferableResource(
+      cc::SharedBitmapIdRegistrar* bitmap_registrar,
+      viz::TransferableResource* out_resource,
+      std::unique_ptr<viz::SingleReleaseCallback>* out_release_callback)
+      override;
+
+ private:
+  // Holds resources and synchronization for one of the swapchain images.
+  struct SwapBuffer : public RefCounted<SwapBuffer> {
+    SwapBuffer(WebGPUSwapBufferProvider*,
+               gpu::Mailbox mailbox,
+               gpu::SyncToken creation_token,
+               gfx::Size size);
+    ~SwapBuffer();
+
+    gfx::Size size;
+    gpu::Mailbox mailbox;
+
+    // A reference back to the swap buffers to keep it alive while this image
+    // is in flight so that the destructor can access data in the swap
+    // buffers.
+    scoped_refptr<WebGPUSwapBufferProvider> swap_buffers;
+
+    // A token signaled when the previous user of the image is finished using
+    // it. It could be WebGPU, the compositor or the shared image creation.
+    gpu::SyncToken access_finished_token;
+
+   private:
+    DISALLOW_COPY_AND_ASSIGN(SwapBuffer);
+  };
+
+  void MailboxReleased(scoped_refptr<SwapBuffer> swap_buffer,
+                       const gpu::SyncToken& sync_token,
+                       bool lost_resource);
+
+  scoped_refptr<DawnControlClientHolder> dawn_control_client_;
+  Client* client_;
+  scoped_refptr<cc::TextureLayer> layer_;
+  bool neutered_ = false;
+
+  DawnTextureUsageBit usage_;
+
+  uint32_t wire_texture_id_ = 0;
+  uint32_t wire_texture_generation_ = 0;
+  scoped_refptr<SwapBuffer> current_swap_buffer_;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GPU_WEBGPU_SWAP_BUFFER_PROVIDER_H_
diff --git a/third_party/blink/renderer/platform/graphics/gpu/webgpu_swap_buffer_provider_test.cc b/third_party/blink/renderer/platform/graphics/gpu/webgpu_swap_buffer_provider_test.cc
new file mode 100644
index 0000000..5d9bafb
--- /dev/null
+++ b/third_party/blink/renderer/platform/graphics/gpu/webgpu_swap_buffer_provider_test.cc
@@ -0,0 +1,210 @@
+// 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/graphics/gpu/webgpu_swap_buffer_provider.h"
+
+#include "gpu/command_buffer/client/webgpu_interface_stub.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/platform/web_graphics_context_3d_provider.h"
+#include "third_party/blink/renderer/platform/graphics/gpu/drawing_buffer_test_helpers.h"
+
+using testing::_;
+using testing::Return;
+
+namespace blink {
+
+namespace {
+
+class MockWebGPUInterface : public gpu::webgpu::WebGPUInterfaceStub {
+ public:
+  MOCK_METHOD1(ReserveTexture, gpu::webgpu::ReservedTexture(DawnDevice device));
+
+  // It is hard to use GMock with SyncTokens represented as GLByte*, instead we
+  // remember which were the last sync tokens generated or waited upon.
+  void GenUnverifiedSyncTokenCHROMIUM(GLbyte* sync_token) override {
+    most_recent_generated_token =
+        gpu::SyncToken(gpu::CommandBufferNamespace::GPU_IO,
+                       gpu::CommandBufferId(), ++token_id_);
+    memcpy(sync_token, &most_recent_generated_token, sizeof(gpu::SyncToken));
+  }
+  void GenSyncTokenCHROMIUM(GLbyte* sync_token) override {
+    most_recent_generated_token =
+        gpu::SyncToken(gpu::CommandBufferNamespace::GPU_IO,
+                       gpu::CommandBufferId(), ++token_id_);
+    most_recent_generated_token.SetVerifyFlush();
+    memcpy(sync_token, &most_recent_generated_token, sizeof(gpu::SyncToken));
+  }
+
+  void WaitSyncTokenCHROMIUM(const GLbyte* sync_token_data) override {
+    memcpy(&most_recent_waited_token, sync_token_data, sizeof(gpu::SyncToken));
+  }
+  gpu::SyncToken most_recent_generated_token;
+  gpu::SyncToken most_recent_waited_token;
+
+ private:
+  uint64_t token_id_ = 42;
+};
+
+class FakeProviderClient : public WebGPUSwapBufferProvider::Client {
+ public:
+  void OnTextureTransferred() override {}
+};
+
+class WebGPUSwapBufferProviderForTests : public WebGPUSwapBufferProvider {
+ public:
+  WebGPUSwapBufferProviderForTests(
+      bool* alive,
+      Client* client,
+      scoped_refptr<DawnControlClientHolder> dawn_control_client,
+      DawnTextureUsageBit usage)
+      : WebGPUSwapBufferProvider(client, dawn_control_client, usage),
+        alive_(alive) {}
+  ~WebGPUSwapBufferProviderForTests() override { *alive_ = false; }
+
+ private:
+  bool* alive_;
+};
+
+}  // anonymous namespace
+
+class WebGPUSwapBufferProviderTest : public testing::Test {
+ protected:
+  void SetUp() override {
+    auto webgpu = std::make_unique<MockWebGPUInterface>();
+    webgpu_ = webgpu.get();
+
+    auto provider = std::make_unique<WebGraphicsContext3DProviderForTests>(
+        std::move(webgpu));
+    sii_ = provider->SharedImageInterface();
+
+    dawn_control_client_ =
+        base::MakeRefCounted<DawnControlClientHolder>(std::move(provider));
+    provider_ = base::MakeRefCounted<WebGPUSwapBufferProviderForTests>(
+        &provider_alive_, &client_, dawn_control_client_,
+        DAWN_TEXTURE_USAGE_BIT_OUTPUT_ATTACHMENT);
+  }
+
+  scoped_refptr<DawnControlClientHolder> dawn_control_client_;
+  MockWebGPUInterface* webgpu_;
+  viz::TestSharedImageInterface* sii_;
+  FakeProviderClient client_;
+  scoped_refptr<WebGPUSwapBufferProviderForTests> provider_;
+  bool provider_alive_ = true;
+};
+
+TEST_F(WebGPUSwapBufferProviderTest,
+       VerifyDestructionCompleteAfterAllResourceReleased) {
+  const IntSize kSize(10, 10);
+
+  viz::TransferableResource resource1;
+  gpu::webgpu::ReservedTexture reservation1 = {
+      reinterpret_cast<DawnTexture>(&resource1), 1, 1};
+  std::unique_ptr<viz::SingleReleaseCallback> release_callback1;
+
+  viz::TransferableResource resource2;
+  gpu::webgpu::ReservedTexture reservation2 = {
+      reinterpret_cast<DawnTexture>(&resource2), 2, 2};
+  std::unique_ptr<viz::SingleReleaseCallback> release_callback2;
+
+  viz::TransferableResource resource3;
+  gpu::webgpu::ReservedTexture reservation3 = {
+      reinterpret_cast<DawnTexture>(&resource3), 3, 3};
+  std::unique_ptr<viz::SingleReleaseCallback> release_callback3;
+
+  // Produce resources.
+  EXPECT_CALL(*webgpu_, ReserveTexture(_)).WillOnce(Return(reservation1));
+  provider_->GetNewTexture(nullptr, kSize);
+  EXPECT_TRUE(provider_->PrepareTransferableResource(nullptr, &resource1,
+                                                     &release_callback1));
+
+  EXPECT_CALL(*webgpu_, ReserveTexture(_)).WillOnce(Return(reservation2));
+  provider_->GetNewTexture(nullptr, kSize);
+  EXPECT_TRUE(provider_->PrepareTransferableResource(nullptr, &resource2,
+                                                     &release_callback2));
+
+  EXPECT_CALL(*webgpu_, ReserveTexture(_)).WillOnce(Return(reservation3));
+  provider_->GetNewTexture(nullptr, kSize);
+  EXPECT_TRUE(provider_->PrepareTransferableResource(nullptr, &resource3,
+                                                     &release_callback3));
+
+  // Release resources one by one, the provider should only be freed when the
+  // last one is called.
+  provider_ = nullptr;
+  release_callback1->Run(gpu::SyncToken(), false /* lostResource */);
+  ASSERT_EQ(provider_alive_, true);
+
+  release_callback2->Run(gpu::SyncToken(), false /* lostResource */);
+  ASSERT_EQ(provider_alive_, true);
+
+  release_callback3->Run(gpu::SyncToken(), false /* lostResource */);
+  ASSERT_EQ(provider_alive_, false);
+}
+
+TEST_F(WebGPUSwapBufferProviderTest, VerifyResizingProperlyAffectsResources) {
+  const IntSize kSize(10, 10);
+  const IntSize kOtherSize(20, 20);
+
+  viz::TransferableResource resource;
+  gpu::webgpu::ReservedTexture reservation = {
+      reinterpret_cast<DawnTexture>(&resource), 1, 1};
+  std::unique_ptr<viz::SingleReleaseCallback> release_callback;
+
+  // Produce one resource of size kSize.
+  EXPECT_CALL(*webgpu_, ReserveTexture(_)).WillOnce(Return(reservation));
+  provider_->GetNewTexture(nullptr, static_cast<IntSize>(kSize));
+  EXPECT_TRUE(provider_->PrepareTransferableResource(nullptr, &resource,
+                                                     &release_callback));
+  EXPECT_EQ(static_cast<gfx::Size>(kSize), sii_->MostRecentSize());
+  release_callback->Run(gpu::SyncToken(), false /* lostResource */);
+
+  // Produce one resource of size kOtherSize.
+  EXPECT_CALL(*webgpu_, ReserveTexture(_)).WillOnce(Return(reservation));
+  provider_->GetNewTexture(nullptr, static_cast<IntSize>(kOtherSize));
+  EXPECT_TRUE(provider_->PrepareTransferableResource(nullptr, &resource,
+                                                     &release_callback));
+  EXPECT_EQ(static_cast<gfx::Size>(kOtherSize), sii_->MostRecentSize());
+  release_callback->Run(gpu::SyncToken(), false /* lostResource */);
+
+  // Produce one resource of size kSize again.
+  EXPECT_CALL(*webgpu_, ReserveTexture(_)).WillOnce(Return(reservation));
+  provider_->GetNewTexture(nullptr, static_cast<IntSize>(kSize));
+  EXPECT_TRUE(provider_->PrepareTransferableResource(nullptr, &resource,
+                                                     &release_callback));
+  EXPECT_EQ(static_cast<gfx::Size>(kSize), sii_->MostRecentSize());
+  release_callback->Run(gpu::SyncToken(), false /* lostResource */);
+}
+
+TEST_F(WebGPUSwapBufferProviderTest, VerifyInsertAndWaitSyncTokenCorrectly) {
+  const IntSize kSize(10, 10);
+
+  viz::TransferableResource resource;
+  gpu::webgpu::ReservedTexture reservation = {
+      reinterpret_cast<DawnTexture>(&resource), 1, 1};
+  std::unique_ptr<viz::SingleReleaseCallback> release_callback;
+
+  // Produce the first resource, check that WebGPU will wait for the creation of
+  // the shared image
+  EXPECT_CALL(*webgpu_, ReserveTexture(_)).WillOnce(Return(reservation));
+  provider_->GetNewTexture(nullptr, static_cast<IntSize>(kSize));
+  EXPECT_EQ(sii_->MostRecentGeneratedToken(),
+            webgpu_->most_recent_waited_token);
+
+  // WebGPU should produce a token so that the next of user of the resource can
+  // synchronize properly
+  EXPECT_TRUE(provider_->PrepareTransferableResource(nullptr, &resource,
+                                                     &release_callback));
+  EXPECT_EQ(webgpu_->most_recent_generated_token,
+            resource.mailbox_holder.sync_token);
+
+  // Check that the release token is used to synchronize the shared image
+  // destruction
+  gpu::SyncToken release_token;
+  webgpu_->GenSyncTokenCHROMIUM(release_token.GetData());
+  release_callback->Run(release_token, false /* lostResource */);
+  release_callback = nullptr;
+  EXPECT_EQ(sii_->MostRecentDestroyToken(), release_token);
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/heap/heap_compact.cc b/third_party/blink/renderer/platform/heap/heap_compact.cc
index dfb69fd..efacd56 100644
--- a/third_party/blink/renderer/platform/heap/heap_compact.cc
+++ b/third_party/blink/renderer/platform/heap/heap_compact.cc
@@ -43,13 +43,67 @@
     relocatable_pages_.insert(page);
   }
 
-  void AddInteriorFixup(MovableReference* slot) {
-    auto it = interior_fixups_.find(slot);
-    // Ephemeron fixpoint iterations may cause repeated registrations.
-    if (UNLIKELY(it != interior_fixups_.end())) {
-      DCHECK(!it->second);
+  void Add(MovableReference* slot) {
+    MovableReference reference = *slot;
+    CHECK(reference);
+
+    // All slots and references are part of Oilpan's heap.
+    CHECK(heap_->LookupPageForAddress(reinterpret_cast<Address>(slot)));
+    CHECK(heap_->LookupPageForAddress(reinterpret_cast<Address>(reference)));
+
+    BasePage* const reference_page = PageFromObject(reference);
+    // The following cases are not compacted and do not require recording:
+    // - Backings in large pages.
+    // - Inline backings that are part of a non-backing arena.
+    if (reference_page->IsLargeObjectPage() ||
+        !HeapCompact::IsCompactableArena(reference_page->Arena()->ArenaIndex()))
+      return;
+
+    // Slots may have been recorded already but must point to the same
+    // reference. Example: Ephemeron iterations may register slots multiple
+    // times.
+    auto fixup_it = fixups_.find(reference);
+    if (UNLIKELY(fixup_it != fixups_.end())) {
+      CHECK_EQ(slot, fixup_it->second);
       return;
     }
+
+    // References must always point to live objects at this point.
+    // Slots must reside in live objects
+
+    // Add regular fixup.
+    fixups_.insert({reference, slot});
+
+    BasePage* const slot_page = PageFromObject(slot);
+
+    // Slots must reside in and references must point to live objects at this
+    // point, with the exception of slots in eagerly swept arenas where objects
+    // have already been processed. |reference| usually points to a separate
+    // backing store but can also point to inlined storage which is why the
+    // dynamic header lookup is required.
+    CHECK(reference_page->Arena()->ArenaIndex() !=
+          BlinkGC::kEagerSweepArenaIndex);
+    CHECK(static_cast<NormalPage*>(reference_page)
+              ->FindHeaderFromAddress(reinterpret_cast<Address>(reference))
+              ->IsMarked());
+    CHECK(slot_page->Arena()->ArenaIndex() == BlinkGC::kEagerSweepArenaIndex ||
+          static_cast<NormalPage*>(slot_page)
+              ->FindHeaderFromAddress(reinterpret_cast<Address>(slot))
+              ->IsMarked());
+
+    // Check whether the slot itself resides on a page that is compacted.
+    if (LIKELY(!relocatable_pages_.Contains(slot_page)))
+      return;
+
+    // Interior slots are recorded as follows:
+    // - Storing it in the interior map, which maps the slot to its (eventual)
+    //   location. Initially nullptr.
+    // - Mark it as being interior pointer within the page's interior bitmap.
+    //   This bitmap is used when moving a backing store to check whether an
+    //   interior slot is to be redirected.
+    auto interior_it = interior_fixups_.find(slot);
+    // Repeated registrations have been filtered above.
+    CHECK(interior_fixups_.end() == interior_it);
     interior_fixups_.insert({slot, nullptr});
     LOG_HEAP_COMPACTION() << "Interior slot: " << slot;
     Address slot_address = reinterpret_cast<Address>(slot);
@@ -60,62 +114,6 @@
     interiors_->Add(slot_address);
   }
 
-  void Add(MovableReference* slot) {
-    DCHECK(*slot);
-    MovableReference reference = *slot;
-    BasePage* ref_page =
-        heap_->LookupPageForAddress(reinterpret_cast<Address>(reference));
-
-    // ref_page is null if *slot is pointing to an off-heap region. This may
-    // happy if *slot is pointing to an inline buffer of HeapVector with inline
-    // capacity.
-    if (!ref_page)
-      return;
-    // Nothing to compact on a large object's page.
-    if (ref_page->IsLargeObjectPage())
-      return;
-
-    if (!HeapCompact::IsCompactableArena(ref_page->Arena()->ArenaIndex()))
-      return;
-#if DCHECK_IS_ON()
-    auto it = fixups_.find(reference);
-    DCHECK(it == fixups_.end() || it->second == slot);
-#endif
-
-    // TODO: when updateHeapResidency() becomes more discriminating about
-    // leaving out arenas that aren't worth compacting, a check for
-    // isCompactingArena() would be appropriate here, leaving early if
-    // |refPage|'s arena isn't in the set.
-
-    fixups_.insert({reference, slot});
-
-    // Note: |slot| will reside outside the Oilpan heap if it is a
-    // PersistentHeapCollectionBase. Hence pageFromObject() cannot be
-    // used, as it sanity checks the |BasePage| it returns. Simply
-    // derive the raw BasePage address here and check if it is a member
-    // of the compactable and relocatable page address set.
-    Address slot_address = reinterpret_cast<Address>(slot);
-    void* slot_page_address =
-        BlinkPageAddress(slot_address) + kBlinkGuardPageSize;
-    if (LIKELY(!relocatable_pages_.Contains(slot_page_address)))
-      return;
-#if DCHECK_IS_ON()
-    BasePage* slot_page = reinterpret_cast<BasePage*>(slot_page_address);
-    DCHECK(slot_page->Contains(slot_address));
-#endif
-    // Unlikely case, the slot resides on a compacting arena's page.
-    //  => It is an 'interior slot' (interior to a movable backing store.)
-    // Record it as an interior slot, which entails:
-    //
-    //  - Storing it in the interior map, which maps the slot to
-    //    its (eventual) location. Initially nullptr.
-    //  - Mark it as being interior pointer within the page's
-    //    "interior" bitmap. This bitmap is used when moving a backing
-    //    store, quickly/ier checking if interior slots will have to
-    //    be additionally redirected.
-    AddInteriorFixup(slot);
-  }
-
   void AddFixupCallback(MovableReference* slot,
                         MovingObjectCallback callback,
                         void* callback_data) {
diff --git a/third_party/blink/renderer/platform/loader/fetch/fetch_utils.cc b/third_party/blink/renderer/platform/loader/fetch/fetch_utils.cc
index 89eee30..de359ca2 100644
--- a/third_party/blink/renderer/platform/loader/fetch/fetch_utils.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/fetch_utils.cc
@@ -68,4 +68,64 @@
   return value.StripWhiteSpace(IsHTTPWhitespace);
 }
 
+const char* FetchUtils::GetDestinationFromContext(
+    mojom::RequestContextType context) {
+  switch (context) {
+    case mojom::RequestContextType::UNSPECIFIED:
+    case mojom::RequestContextType::BEACON:
+    case mojom::RequestContextType::DOWNLOAD:
+    case mojom::RequestContextType::EVENT_SOURCE:
+    case mojom::RequestContextType::FETCH:
+    case mojom::RequestContextType::PING:
+    case mojom::RequestContextType::XML_HTTP_REQUEST:
+    case mojom::RequestContextType::SUBRESOURCE:
+    case mojom::RequestContextType::PREFETCH:
+      return "";
+    case mojom::RequestContextType::CSP_REPORT:
+      return "report";
+    case mojom::RequestContextType::AUDIO:
+      return "audio";
+    case mojom::RequestContextType::EMBED:
+      return "embed";
+    case mojom::RequestContextType::FONT:
+      return "font";
+    case mojom::RequestContextType::FRAME:
+    case mojom::RequestContextType::HYPERLINK:
+    case mojom::RequestContextType::IFRAME:
+    case mojom::RequestContextType::LOCATION:
+    case mojom::RequestContextType::FORM:
+      return "document";
+    case mojom::RequestContextType::IMAGE:
+    case mojom::RequestContextType::FAVICON:
+    case mojom::RequestContextType::IMAGE_SET:
+      return "image";
+    case mojom::RequestContextType::MANIFEST:
+      return "manifest";
+    case mojom::RequestContextType::OBJECT:
+      return "object";
+    case mojom::RequestContextType::SCRIPT:
+      return "script";
+    case mojom::RequestContextType::SERVICE_WORKER:
+      return "serviceworker";
+    case mojom::RequestContextType::SHARED_WORKER:
+      return "sharedworker";
+    case mojom::RequestContextType::STYLE:
+      return "style";
+    case mojom::RequestContextType::TRACK:
+      return "track";
+    case mojom::RequestContextType::VIDEO:
+      return "video";
+    case mojom::RequestContextType::WORKER:
+      return "worker";
+    case mojom::RequestContextType::XSLT:
+      return "xslt";
+    case mojom::RequestContextType::IMPORT:
+    case mojom::RequestContextType::INTERNAL:
+    case mojom::RequestContextType::PLUGIN:
+      return "unknown";
+  }
+  NOTREACHED();
+  return "";
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/loader/fetch/fetch_utils.h b/third_party/blink/renderer/platform/loader/fetch/fetch_utils.h
index f5ab97e..bfbcabd 100644
--- a/third_party/blink/renderer/platform/loader/fetch/fetch_utils.h
+++ b/third_party/blink/renderer/platform/loader/fetch/fetch_utils.h
@@ -20,6 +20,11 @@
   static bool IsForbiddenResponseHeaderName(const String& name);
   static AtomicString NormalizeMethod(const AtomicString& method);
   static String NormalizeHeaderValue(const String& value);
+
+  // This function maps from Blink's internal "request context" concept to
+  // Fetch's notion of a request's "destination":
+  // https://fetch.spec.whatwg.org/#concept-request-destination.
+  static const char* GetDestinationFromContext(mojom::RequestContextType);
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc
index bdd0251..b66e4fe 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc
@@ -50,6 +50,7 @@
 #include "third_party/blink/renderer/platform/loader/fetch/fetch_client_settings_object_snapshot.h"
 #include "third_party/blink/renderer/platform/loader/fetch/fetch_context.h"
 #include "third_party/blink/renderer/platform/loader/fetch/fetch_initiator_type_names.h"
+#include "third_party/blink/renderer/platform/loader/fetch/fetch_utils.h"
 #include "third_party/blink/renderer/platform/loader/fetch/memory_cache.h"
 #include "third_party/blink/renderer/platform/loader/fetch/raw_resource.h"
 #include "third_party/blink/renderer/platform/loader/fetch/resource_fetcher_properties.h"
@@ -277,68 +278,6 @@
   }
 }
 
-// This function maps from Blink's internal "request context" concept to Fetch's
-// notion of a request's "destination":
-// https://fetch.spec.whatwg.org/#concept-request-destination.
-const char* GetDestinationFromContext(mojom::RequestContextType context) {
-  switch (context) {
-    case mojom::RequestContextType::UNSPECIFIED:
-    case mojom::RequestContextType::BEACON:
-    case mojom::RequestContextType::DOWNLOAD:
-    case mojom::RequestContextType::EVENT_SOURCE:
-    case mojom::RequestContextType::FETCH:
-    case mojom::RequestContextType::PING:
-    case mojom::RequestContextType::XML_HTTP_REQUEST:
-    case mojom::RequestContextType::SUBRESOURCE:
-    case mojom::RequestContextType::PREFETCH:
-      return "";
-    case mojom::RequestContextType::CSP_REPORT:
-      return "report";
-    case mojom::RequestContextType::AUDIO:
-      return "audio";
-    case mojom::RequestContextType::EMBED:
-      return "embed";
-    case mojom::RequestContextType::FONT:
-      return "font";
-    case mojom::RequestContextType::FRAME:
-    case mojom::RequestContextType::HYPERLINK:
-    case mojom::RequestContextType::IFRAME:
-    case mojom::RequestContextType::LOCATION:
-    case mojom::RequestContextType::FORM:
-      return "document";
-    case mojom::RequestContextType::IMAGE:
-    case mojom::RequestContextType::FAVICON:
-    case mojom::RequestContextType::IMAGE_SET:
-      return "image";
-    case mojom::RequestContextType::MANIFEST:
-      return "manifest";
-    case mojom::RequestContextType::OBJECT:
-      return "object";
-    case mojom::RequestContextType::SCRIPT:
-      return "script";
-    case mojom::RequestContextType::SERVICE_WORKER:
-      return "serviceworker";
-    case mojom::RequestContextType::SHARED_WORKER:
-      return "sharedworker";
-    case mojom::RequestContextType::STYLE:
-      return "style";
-    case mojom::RequestContextType::TRACK:
-      return "track";
-    case mojom::RequestContextType::VIDEO:
-      return "video";
-    case mojom::RequestContextType::WORKER:
-      return "worker";
-    case mojom::RequestContextType::XSLT:
-      return "xslt";
-    case mojom::RequestContextType::IMPORT:
-    case mojom::RequestContextType::INTERNAL:
-    case mojom::RequestContextType::PLUGIN:
-      return "unknown";
-  }
-  NOTREACHED();
-  return "";
-}
-
 // This maps the network::mojom::FetchRequestMode to a string that can be used
 // in a `Sec-Fetch-Mode` header.
 const char* FetchRequestModeToString(network::mojom::FetchRequestMode mode) {
@@ -365,7 +304,7 @@
   if (blink::RuntimeEnabledFeatures::FetchMetadataEnabled() &&
       url_origin->IsPotentiallyTrustworthy()) {
     const char* destination_value =
-        GetDestinationFromContext(request.GetRequestContext());
+        FetchUtils::GetDestinationFromContext(request.GetRequestContext());
 
     // If the request's destination is the empty string (e.g. `fetch()`), then
     // we'll use the identifier "empty" instead.
diff --git a/third_party/blink/renderer/platform/memory_pressure_listener.cc b/third_party/blink/renderer/platform/memory_pressure_listener.cc
index ca6f0493..8872352 100644
--- a/third_party/blink/renderer/platform/memory_pressure_listener.cc
+++ b/third_party/blink/renderer/platform/memory_pressure_listener.cc
@@ -97,18 +97,12 @@
   TRACE_EVENT0("blink", "MemoryPressureListenerRegistry::onMemoryPressure");
   for (auto& client : clients_)
     client->OnMemoryPressure(level);
-  if (level == kWebMemoryPressureLevelCritical)
-    ClearMemory();
   WTF::Partitions::DecommitFreeableMemory();
 }
 
 void MemoryPressureListenerRegistry::OnPurgeMemory() {
   for (auto& client : clients_)
     client->OnPurgeMemory();
-  // Don't call clearMemory() because font cache invalidation always causes full
-  // layout. This increases tab switching cost significantly (e.g.
-  // en.wikipedia.org/wiki/Wikipedia). So we should not invalidate the font
-  // cache in purge+throttle.
   ImageDecodingStore::Instance().Clear();
   WTF::Partitions::DecommitFreeableMemory();
 
@@ -125,13 +119,6 @@
   }
 }
 
-void MemoryPressureListenerRegistry::ClearMemory() {
-  // TODO(tasak|bashi): Make FontCache a MemoryPressureListener rather than
-  // clearing caches here.
-  if (base::FeatureList::IsEnabled(features::kInvalidateFontCacheOnPurge))
-    FontGlobalContext::ClearMemory();
-}
-
 void MemoryPressureListenerRegistry::ClearThreadSpecificMemory() {
   FontGlobalContext::ClearMemory();
 }
diff --git a/third_party/blink/renderer/platform/memory_pressure_listener.h b/third_party/blink/renderer/platform/memory_pressure_listener.h
index 0730adec..2a42261 100644
--- a/third_party/blink/renderer/platform/memory_pressure_listener.h
+++ b/third_party/blink/renderer/platform/memory_pressure_listener.h
@@ -63,7 +63,6 @@
 
   static void SetIsLowEndDeviceForTesting(bool);
 
-  void ClearMemory();
   static void ClearThreadSpecificMemory();
 
   static bool is_low_end_device_;
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 64a5222..a05f4614 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -6076,6 +6076,7 @@
 crbug.com/948257 external/wpt/console/console-label-conversion.any.html [ Pass Failure ]
 crbug.com/948257 external/wpt/console/console-label-conversion.any.worker.html [ Pass Failure ]
 crbug.com/948678 external/wpt/console/idlharness.any.html [ Pass Failure ]
+crbug.com/948678 external/wpt/console/idlharness.any.worker.html [ Pass Failure ]
 
 ### external/wpt/fetch/sec-metadata/
 crbug.com/947023 external/wpt/fetch/sec-metadata/font.tentative.https.sub.html [ Pass Failure ]
@@ -6158,3 +6159,6 @@
 crbug.com/956547 fast/dom/raf-throttling-out-of-view-cross-origin-page.html [ Pass Timeout ]
 crbug.com/956547 fast/dom/timer-throttling-out-of-view-cross-origin-page.html [ Pass Timeout ]
 crbug.com/956736 [ Linux Win ] virtual/gpu-rasterization/images/imagemap-focus-ring-outline-color-not-inherited-from-map.html [ Pass Failure ]
+
+# Sheriff 2019-04-25
+crbug.com/956547 http/tests/dom/raf-throttling-out-of-view-cross-origin-page.html [ Pass Timeout ]
diff --git a/third_party/blink/web_tests/external/wpt/mediacapture-streams/OWNERS b/third_party/blink/web_tests/external/wpt/mediacapture-streams/OWNERS
index bffa2b1..31acf62 100644
--- a/third_party/blink/web_tests/external/wpt/mediacapture-streams/OWNERS
+++ b/third_party/blink/web_tests/external/wpt/mediacapture-streams/OWNERS
@@ -1,3 +1,4 @@
 # COMPONENT: Blink>MediaStream
 # WPT-NOTIFY: true
 guidou@chromium.org
+armax@chromium.org
diff --git a/third_party/blink/web_tests/external/wpt/wake-lock/wakelock-disabled-by-feature-policy.https.sub.html b/third_party/blink/web_tests/external/wpt/wake-lock/wakelock-disabled-by-feature-policy.https.sub.html
index 75064767..ad7eeb0 100644
--- a/third_party/blink/web_tests/external/wpt/wake-lock/wakelock-disabled-by-feature-policy.https.sub.html
+++ b/third_party/blink/web_tests/external/wpt/wake-lock/wakelock-disabled-by-feature-policy.https.sub.html
@@ -12,7 +12,7 @@
     "https://{{domains[www]}}:{{ports[https][0]}}" + same_origin_src;
 
   promise_test(t => {
-    return promise_rejects(t, "SecurityError", navigator.getWakeLock("screen"));
+    return promise_rejects(t, "NotAllowedError", navigator.getWakeLock("screen"));
   }, 'Feature-Policy header {"wake-lock" : []} disallows the top-level document.');
 
   async_test(t => {
diff --git a/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-biquadfilternode-interface/biquad-getFrequencyResponse.html b/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-biquadfilternode-interface/biquad-getFrequencyResponse.html
index 2389b33..23222e4 100644
--- a/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-biquadfilternode-interface/biquad-getFrequencyResponse.html
+++ b/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-biquadfilternode-interface/biquad-getFrequencyResponse.html
@@ -36,12 +36,6 @@
       let filterQ = 1;
       let filterGain = 5;  // Decibels.
 
-      // The maximum allowed error in the magnitude response.
-      let maxAllowedMagError = 1.09931e-6;
-
-      // The maximum allowed error in the phase response.
-      let maxAllowedPhaseError = 6.4724e-8;
-
       // The magnitudes and phases of the reference frequency response.
       let expectedMagnitudes;
       let expectedPhases;
@@ -148,6 +142,10 @@
         }
       }
 
+      function decibelsToLinear(x) {
+        return Math.pow(10, x/20);
+      }
+
       // Look through the array and find any NaN or infinity. Returns the index
       // of the first occurence or -1 if none.
       function findBadNumber(signal) {
@@ -171,8 +169,17 @@
       }
 
       // Compare the frequency response with our expected response.
+      //
+      //   should - The |should| method provided by audit.define
+      //   filter - The filter node used in the test
+      //   frequencies - array of frequencies provided to |getFrequencyResponse|
+      //   magResponse - mag response from |getFrequencyResponse|
+      //   phaseResponse - phase response from |getFrequencyResponse|
+      //   maxAllowedMagError - error threshold for mag response, in dB
+      //   maxAllowedPhaseError - error threshold for phase response, in rad.
       function compareResponses(
-          should, filter, frequencies, magResponse, phaseResponse) {
+          should, filter, frequencies, magResponse, phaseResponse,
+          maxAllowedMagError, maxAllowedPhaseError) {
         let expectedResponse = frequencyResponseReference(filter, frequencies);
 
         expectedMagnitudes = expectedResponse.magnitudes;
@@ -188,16 +195,19 @@
         let hasBadNumber;
 
         hasBadNumber = findBadNumber(magResponse);
-        badResponse = !should(
-                           hasBadNumber >= 0 ? 1 : 0,
-                           'Number of non-finite values in magnitude response')
-                           .beEqualTo(0);
+        badResponse =
+            !should(
+                 hasBadNumber >= 0 ? 1 : 0,
+                 filter.type +
+                     ': Number of non-finite values in magnitude response')
+                 .beEqualTo(0);
 
         hasBadNumber = findBadNumber(phaseResponse);
-        badResponse = !should(
-                           hasBadNumber >= 0 ? 1 : 0,
-                           'Number of non-finte values in phase response')
-                           .beEqualTo(0);
+        badResponse =
+            !should(
+                 hasBadNumber >= 0 ? 1 : 0,
+                 filter.type + ': Number of non-finte values in phase response')
+                 .beEqualTo(0);
 
         // These aren't testing the implementation itself.  Instead, these are
         // sanity checks on the reference.  Failure here does not imply an error
@@ -206,14 +216,16 @@
         badResponse =
             !should(
                  hasBadNumber >= 0 ? 1 : 0,
-                 'Number of non-finite values in the expected magnitude response')
+                 filter.type +
+                     ': Number of non-finite values in the expected magnitude response')
                  .beEqualTo(0);
 
         hasBadNumber = findBadNumber(expectedPhases);
         badResponse =
             !should(
                  hasBadNumber >= 0 ? 1 : 0,
-                 'Number of non-finite values in expected phase response')
+                 filter.type +
+                     ': Number of non-finite values in expected phase response')
                  .beEqualTo(0);
 
         // If we found a NaN or infinity, the following tests aren't very
@@ -221,7 +233,8 @@
         // warning message.
         should(
             !badResponse,
-            'Actual and expected results contained only finite values')
+            filter.type +
+                ': Actual and expected results contained only finite values')
             .beTrue();
 
         for (k = 0; k < n; ++k) {
@@ -236,7 +249,7 @@
 
         should(
             linearToDecibels(maxMagError),
-            'Max error (' + linearToDecibels(maxMagError) +
+            filter.type + ': Max error (' + linearToDecibels(maxMagError) +
                 ' dB) of magnitude response at frequency ' +
                 frequencies[maxMagErrorIndex] + ' Hz')
             .beLessThanOrEqualTo(linearToDecibels(maxAllowedMagError));
@@ -254,7 +267,7 @@
 
         should(
             radToDegree(maxPhaseError),
-            'Max error (' + radToDegree(maxPhaseError) +
+            filter.type + ': Max error (' + radToDegree(maxPhaseError) +
                 ' deg) in phase response at frequency ' +
                 frequencies[maxPhaseErrorIndex] + ' Hz')
             .beLessThanOrEqualTo(radToDegree(maxAllowedPhaseError));
@@ -265,32 +278,78 @@
         return rad * 180 / Math.PI;
       }
 
-      audit.define(
-          {label: 'test', description: 'Biquad frequency response'},
-          function(task, should) {
-            context = new AudioContext();
+      // Test the getFrequencyResponse for each of filter types.  Each entry in
+      // this array is a dictionary with these elements:
+      //
+      //   type:  filter type to be tested
+      //   maxErrorInMagnitude:  Allowed error in computed magnitude response
+      //   maxErrorInPhase:      Allowed error in computed magnitude phase
+      [{
+        type: 'lowpass',
+        maxErrorInMagnitude: decibelsToLinear(-73.0178),
+        maxErrorInPhase: 8.04360e-6
+      },
+       {
+         type: 'highpass',
+         maxErrorInMagnitude: decibelsToLinear(-117.5461),
+         maxErrorInPhase: 6.9691e-6
+       },
+       {
+         type: 'bandpass',
+         maxErrorInMagnitude: decibelsToLinear(-79.0139),
+         maxErrorInPhase: 4.9371e-6
+       },
+       {
+         type: 'lowshelf',
+         maxErrorInMagnitude: decibelsToLinear(-120.4038),
+         maxErrorInPhase: 4.0724e-6
+       },
+       {
+         type: 'highshelf',
+         maxErrorInMagnitude: decibelsToLinear(-120, 1303),
+         maxErrorInPhase: 4.0724e-6
+       },
+       {
+         type: 'peaking',
+         maxErrorInMagnitude: decibelsToLinear(-119.1176),
+         maxErrorInPhase: 6.4724e-8
+       },
+       {
+         type: 'notch',
+         maxErrorInMagnitude: decibelsToLinear(-87.0808),
+         maxErrorInPhase: 6.6300e-6
+       },
+       {
+         type: 'allpass',
+         maxErrorInMagnitude: decibelsToLinear(-265.3517),
+         maxErrorInPhase: 1.3260e-5
+       }].forEach(test => {
+        audit.define(
+            {label: test.type, description: 'Frequency response'},
+            (task, should) => {
+              let context = new AudioContext();
 
-            filter = context.createBiquadFilter();
+              let filter = new BiquadFilterNode(context, {
+                type: test.type,
+                frequency: filterCutoff,
+                Q: filterQ,
+                gain: filterGain
+              });
 
-            // Arbitrarily test a peaking filter, but any kind of filter can be
-            // tested.
-            filter.type = 'peaking';
-            filter.frequency.value = filterCutoff;
-            filter.Q.value = filterQ;
-            filter.gain.value = filterGain;
+              let frequencies =
+                  createFrequencies(numberOfFrequencies, context.sampleRate);
+              magResponse = new Float32Array(numberOfFrequencies);
+              phaseResponse = new Float32Array(numberOfFrequencies);
 
-            let frequencies =
-                createFrequencies(numberOfFrequencies, context.sampleRate);
-            magResponse = new Float32Array(numberOfFrequencies);
-            phaseResponse = new Float32Array(numberOfFrequencies);
+              filter.getFrequencyResponse(
+                  frequencies, magResponse, phaseResponse);
+              compareResponses(
+                  should, filter, frequencies, magResponse, phaseResponse,
+                  test.maxErrorInMagnitude, test.maxErrorInPhase);
 
-            filter.getFrequencyResponse(
-                frequencies, magResponse, phaseResponse);
-            compareResponses(
-                should, filter, frequencies, magResponse, phaseResponse);
-
-            task.done();
-          });
+              task.done();
+            });
+      });
 
       audit.define(
           {
diff --git a/third_party/blink/web_tests/platform/mac/css3/filters/effect-contrast-expected.png b/third_party/blink/web_tests/platform/mac/css3/filters/effect-contrast-expected.png
index d11af81..69f0c9f 100644
--- a/third_party/blink/web_tests/platform/mac/css3/filters/effect-contrast-expected.png
+++ b/third_party/blink/web_tests/platform/mac/css3/filters/effect-contrast-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/css3/filters/effect-contrast-expected.png b/third_party/blink/web_tests/platform/win/css3/filters/effect-contrast-expected.png
index 4e164371..62db196 100644
--- a/third_party/blink/web_tests/platform/win/css3/filters/effect-contrast-expected.png
+++ b/third_party/blink/web_tests/platform/win/css3/filters/effect-contrast-expected.png
Binary files differ
diff --git a/tools/binary_size/libsupersize/archive.py b/tools/binary_size/libsupersize/archive.py
index ee54015..7180ca91 100644
--- a/tools/binary_size/libsupersize/archive.py
+++ b/tools/binary_size/libsupersize/archive.py
@@ -359,6 +359,13 @@
   for symbol in raw_symbols:
     ret.append(symbol)
     full_name = symbol.full_name
+    # '__typeid_' symbols appear in linker .map only, and not nm output.
+    if full_name.startswith('__typeid_'):
+      if object_paths_by_name.get(full_name):
+        logging.warning('Found unexpected __typeid_ symbol in nm output: %s',
+                        full_name)
+      continue
+
     # Don't skip if symbol.IsBss(). This is needed for LLD-LTO to work, since
     # .bss object_path data are unavailable for linker_map_parser, and need to
     # be extracted here. For regular LLD flow, incorrect aliased symbols can
@@ -651,12 +658,15 @@
     # Don't alias padding-only symbols (e.g. ** symbol gap)
     if s.size_without_padding == 0:
       continue
+    # Also skip artificial symbols that won't appear in nm output.
+    if s.full_name.startswith('** CFI jump table'):
+      continue
     name_list = names_by_address.get(s.address)
     if name_list:
       if s.full_name not in name_list:
         missing_names[s.full_name].append(s.address)
-        logging.warning('Name missing from aliases: %s %s', s.full_name,
-                        name_list)
+        logging.warning('Name missing from aliases: %08x %s %s', s.address,
+                        s.full_name, name_list)
         continue
       replacements.append((i, name_list))
       num_new_symbols += len(name_list) - 1
diff --git a/tools/binary_size/libsupersize/demangle.py b/tools/binary_size/libsupersize/demangle.py
index e155748..9e8b6700 100644
--- a/tools/binary_size/libsupersize/demangle.py
+++ b/tools/binary_size/libsupersize/demangle.py
@@ -50,9 +50,11 @@
     if pos > 0:
       name = name[pos:]
 
-    # Some mangled symbols end with '$' followed by 32 lower-case hex digits.
-    # These interfere with demangling by c++filt. This function is an adaptor
-    # for iterable |name| to detect and strip these hash suffixes.
+    # Some mangled symbols end with '$' followed by 32 lower-case hex digits,
+    # and possibly '.cfi'. These interfere with demangling by c++filt, and
+    # should be stripped.
+    if name.endswith('.cfi'):
+      name = name[:-4]
     if len(name) > 33 and name[-33] == '$' and _IsLowerHex(name[-32:]):
       yield name[:-33]
     else:
diff --git a/tools/binary_size/libsupersize/linker_map_parser.py b/tools/binary_size/libsupersize/linker_map_parser.py
index 14488be6..a6267d2 100755
--- a/tools/binary_size/libsupersize/linker_map_parser.py
+++ b/tools/binary_size/libsupersize/linker_map_parser.py
@@ -73,6 +73,8 @@
     return '** outlined function'
   if name.startswith('.L.str'):
     return models.STRING_LITERAL_NAME
+  if name.endswith(' (.cfi)'):
+    return name[:-7]
   return name
 
 
@@ -483,6 +485,11 @@
     thin_map = {}
 
     tokenizer = self.Tokenize(lines)
+
+    in_jump_table = False
+    jump_tables_count = 0
+    jump_entries_count = 0
+
     for (line, address, size, level, span, tok) in tokenizer:
       # Level 1 data match the "Out" column. They specify sections or
       # PROVIDE_HIDDEN lines.
@@ -504,28 +511,37 @@
         if level == 2:
           # E.g., 'path.o:(.text._name)' => ['path.o', '(.text._name)'].
           cur_obj, paren_value = tok.split(':')
-          # E.g., '(.text.unlikely._name)' -> '_name'.
-          mangled_name = paren_value[mangled_start_idx:-1]
-          cur_flags = _FlagsFromMangledName(mangled_name)
-          is_partial = True
-          # As of 2017/11 LLD does not distinguish merged strings from other
-          # merged data. Feature request is filed under:
-          # https://bugs.llvm.org/show_bug.cgi?id=35248
-          if cur_obj == '<internal>':
-            if cur_section == '.rodata' and mangled_name == '':
-              # Treat all <internal> sections within .rodata as as string
-              # literals. Some may hold numeric constants or other data, but
-              # there is currently no way to distinguish them.
-              mangled_name = '** lld merge strings'
-            else:
-              # e.g. <internal>:(.text.thunk)
-              mangled_name = '** ' + mangled_name
 
-            is_partial = False
-            cur_obj = None
-          elif cur_obj == 'lto.tmp' or 'thinlto-cache' in cur_obj:
-            thin_map[address] = os.path.basename(cur_obj)
-            cur_obj = None
+          in_jump_table = '.L.cfi.jumptable' in paren_value
+          if in_jump_table:
+            # Store each CFI jump table as a Level 2 symbol, whose Level 3
+            # details are discarded.
+            jump_tables_count += 1
+            cur_obj = ''  # Replaces 'lto.tmp' to prevent problem later.
+            mangled_name = '** CFI jump table'
+          else:
+            # E.g., '(.text.unlikely._name)' -> '_name'.
+            mangled_name = paren_value[mangled_start_idx:-1]
+            cur_flags = _FlagsFromMangledName(mangled_name)
+            is_partial = True
+            # As of 2017/11 LLD does not distinguish merged strings from other
+            # merged data. Feature request is filed under:
+            # https://bugs.llvm.org/show_bug.cgi?id=35248
+            if cur_obj == '<internal>':
+              if cur_section == '.rodata' and mangled_name == '':
+                # Treat all <internal> sections within .rodata as as string
+                # literals. Some may hold numeric constants or other data, but
+                # there is currently no way to distinguish them.
+                mangled_name = '** lld merge strings'
+              else:
+                # e.g. <internal>:(.text.thunk)
+                mangled_name = '** ' + mangled_name
+
+              is_partial = False
+              cur_obj = None
+            elif cur_obj == 'lto.tmp' or 'thinlto-cache' in cur_obj:
+              thin_map[address] = os.path.basename(cur_obj)
+              cur_obj = None
 
           # Create a symbol here since there may be no ensuing Level 3 lines.
           # But if there are, then the symbol can be modified later as sym[-1].
@@ -542,6 +558,17 @@
         # '$t.42' also appear at Level 3, but they are consumed by |tokenizer|,
         # so don't appear hear.
         elif level == 3:
+          # Handle .L.cfi.jumptable.
+          if in_jump_table:
+            # Level 3 entries in CFI jump tables are thunks with mangled names.
+            # Extracting them as symbols is not worthwhile; we only store the
+            # Level 2 symbol, and print the count for verbose output. For
+            # counting, '__typeid_' entries are excluded since they're likely
+            # just annotations.
+            if not tok.startswith('__typeid_'):
+              jump_entries_count += 1
+            continue
+
           # Ignore anything with '.L_MergedGlobals' prefix. This seems to only
           # happen for ARM (32-bit) builds.
           if tok.startswith('.L_MergedGlobals'):
@@ -578,8 +605,23 @@
               next_usable_address = address + syms[-1].size
               is_partial = False
             elif address >= next_usable_address:
-              # Prefer |size|, and only fall back to |span| if |size == 0|.
-              size_to_use = size if size > 0 else span
+              if tok.startswith('__typeid_'):
+                assert size == 1
+                if tok.endswith('_byte_array'):
+                  # CFI byte array table: |size| is inaccurate, so use |span|.
+                  size_to_use = span
+                else:
+                  # Likely '_global_addr' or '_unique_member'. These should be:
+                  # * Skipped since they're in CFI tables.
+                  # * Suppressed (via |next_usable_address|) by another Level 3
+                  #   symbol.
+                  # Anything that makes it here would be an anomaly worthy of
+                  # investigation, so print warnings.
+                  logging.warn('Unrecognized __typeid_ symbol at %08X', address)
+                  continue
+              else:
+                # Prefer |size|, and only fall back to |span| if |size == 0|.
+                size_to_use = size if size > 0 else span
               sym = models.Symbol(cur_section, size_to_use, address=address,
                                   full_name=tok, flags=cur_flags)
               syms.append(sym)
@@ -595,6 +637,9 @@
 
     if promoted_name_count:
       logging.info('Found %d promoted global names', promoted_name_count)
+    if jump_tables_count:
+      logging.info('Found %d CFI jump tables with %d total entries',
+                   jump_tables_count, jump_entries_count)
     return self._section_sizes, syms, {'thin_map': thin_map}
 
 
diff --git a/tools/binary_size/libsupersize/linker_map_parser_test.py b/tools/binary_size/libsupersize/linker_map_parser_test.py
index 31252fec..a050df9 100755
--- a/tools/binary_size/libsupersize/linker_map_parser_test.py
+++ b/tools/binary_size/libsupersize/linker_map_parser_test.py
@@ -14,6 +14,7 @@
 _SCRIPT_DIR = os.path.dirname(__file__)
 _TEST_DATA_DIR = os.path.join(_SCRIPT_DIR, 'testdata', 'linker_map_parser')
 _TEST_MAP_PATH = os.path.join(_TEST_DATA_DIR, 'test_lld-lto_v1.map')
+_TEST_CFI_MAP_PATH = os.path.join(_TEST_DATA_DIR, 'test_lld-lto_v1_cfi.map')
 
 
 def _CompareWithGolden(name=None):
@@ -45,23 +46,35 @@
   return ret
 
 
+def _RenderSectionSizesAndRawSymbols(section_sizes, raw_symbols):
+  ret = []
+  ret.append('******** section_sizes ********')
+  for k, v in sorted(section_sizes.iteritems()):
+    ret.append('%-24s %d' % (k, v))
+  ret.append('')
+  ret.append('******** raw_symbols ********')
+  for sym in raw_symbols:
+    ret.append(repr(sym))
+  return ret
+
+
 class LinkerMapParserTest(unittest.TestCase):
 
   @_CompareWithGolden()
   def test_Parser(self):
-    ret = []
     map_file = _ReadMapFile(_TEST_MAP_PATH)
     linker_name = linker_map_parser.DetectLinkerNameFromMapFile(iter(map_file))
     section_sizes, raw_symbols, _ = (
         linker_map_parser.MapFileParser().Parse(linker_name, iter(map_file)))
-    ret.append('******** section_sizes ********')
-    for k, v in sorted(section_sizes.iteritems()):
-      ret.append('%-24s %d' % (k, v))
-    ret.append('')
-    ret.append('******** raw_symbols ********')
-    for sym in raw_symbols:
-      ret.append(repr(sym))
-    return ret
+    return _RenderSectionSizesAndRawSymbols(section_sizes, raw_symbols)
+
+  @_CompareWithGolden()
+  def test_ParserCfi(self):
+    map_file = _ReadMapFile(_TEST_CFI_MAP_PATH)
+    linker_name = linker_map_parser.DetectLinkerNameFromMapFile(iter(map_file))
+    section_sizes, raw_symbols, _ = (
+        linker_map_parser.MapFileParser().Parse(linker_name, iter(map_file)))
+    return _RenderSectionSizesAndRawSymbols(section_sizes, raw_symbols)
 
   def test_ParseArmAnnotations(self):
     fun = linker_map_parser.MapFileParserLld.ParseArmAnnotations
diff --git a/tools/binary_size/libsupersize/testdata/linker_map_parser/ParserCfi.golden b/tools/binary_size/libsupersize/testdata/linker_map_parser/ParserCfi.golden
new file mode 100644
index 0000000..9368733
--- /dev/null
+++ b/tools/binary_size/libsupersize/testdata/linker_map_parser/ParserCfi.golden
@@ -0,0 +1,55 @@
+******** section_sizes ********
+.ARM.extab               24100
+.data.rel.ro             2430180
+.rodata                  6759914
+.text                    43636188
+
+******** raw_symbols ********
+.rodata@23dd00(size_without_padding=4,padding=0,full_name=v8_Default_embedded_blob_size_,object_path=obj/v8/v8_external_snapshot/embedded.o,source_path=,flags={},num_aliases=1,component=)
+.rodata@23dd08(size_without_padding=8,padding=0,full_name=CRASHPAD_NOTE_REFERENCE,object_path=obj/third_party/crashpad/crashpad/client/libclient.a(client/crashpad_info_note.o),source_path=,flags={},num_aliases=1,component=)
+.rodata@23dd10(size_without_padding=16,padding=0,full_name=pmmp,object_path=obj/third_party/ffmpeg/libffmpeg_internal.a(ffmpeg_internal/fft_neon.o),source_path=,flags={},num_aliases=1,component=)
+.rodata@23dd20(size_without_padding=16,padding=0,full_name=mppm,object_path=obj/third_party/ffmpeg/libffmpeg_internal.a(ffmpeg_internal/fft_neon.o),source_path=,flags={},num_aliases=1,component=)
+.rodata@23dd30(size_without_padding=3007385,padding=0,full_name=** lld merge strings,object_path=,source_path=,flags={},num_aliases=1,component=)
+.rodata@51c0cc(size_without_padding=4,padding=0,full_name=__emutls_t._ZL16g_current_locale,object_path=../../third_party/android_ndk/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/libandroid_support.a(locale.o),source_path=,flags={},num_aliases=1,component=)
+.rodata@51d2a0(size_without_padding=1,padding=0,full_name=__typeid__ZTSN7content19WebContentsObserverE_byte_array,object_path=,source_path=,flags={},num_aliases=1,component=)
+.rodata@52b7f9(size_without_padding=3404,padding=0,full_name=__typeid__ZTSN4base16SupportsUserData4DataE_byte_array,object_path=,source_path=,flags={},num_aliases=1,component=)
+.rodata@52c545(size_without_padding=1520,padding=0,full_name=__typeid__ZTSN5blink21ImageResourceObserverE_byte_array,object_path=,source_path=,flags={},num_aliases=1,component=)
+.rodata@52cb35(size_without_padding=1030,padding=0,full_name=__typeid__ZTS12KeyedService_byte_array,object_path=,source_path=,flags={},num_aliases=1,component=)
+.rodata@52cf3b(size_without_padding=29674,padding=0,full_name=__typeid__ZTSN3IPC6SenderE_byte_array,object_path=,source_path=,flags={},num_aliases=1,component=)
+.rodata@534325(size_without_padding=7384,padding=0,full_name=__typeid__ZTSN5blink15ScriptWrappableE_byte_array,object_path=,source_path=,flags={},num_aliases=1,component=)
+.rodata@535ffd(size_without_padding=1501,padding=0,full_name=__typeid__ZTSN4base11trace_event18MemoryDumpProviderE_byte_array,object_path=,source_path=,flags={},num_aliases=1,component=)
+.rodata@5365da(size_without_padding=4735,padding=0,full_name=__typeid__ZTSN5blink23MediaControlElementBaseE_byte_array,object_path=,source_path=,flags={},num_aliases=1,component=)
+.rodata@537859(size_without_padding=5257,padding=0,full_name=__typeid__ZTSN5blink11EventTargetE_byte_array,object_path=,source_path=,flags={},num_aliases=1,component=)
+.rodata@538ce2(size_without_padding=184,padding=0,full_name=__typeid__ZTSN5blink4NodeE_byte_array,object_path=,source_path=,flags={},num_aliases=1,component=)
+.rodata@538d9a(size_without_padding=4759,padding=0,full_name=__typeid__ZTSN5blink13ContainerNodeE_byte_array,object_path=,source_path=,flags={},num_aliases=1,component=)
+.rodata@53a031(size_without_padding=5237,padding=0,full_name=__typeid__ZTSN5blink7ElementE_byte_array,object_path=,source_path=,flags={},num_aliases=1,component=)
+.rodata@53b4a6(size_without_padding=4830,padding=0,full_name=__typeid__ZTSN5blink10SupplementINS_8DocumentEEE_byte_array,object_path=,source_path=,flags={},num_aliases=1,component=)
+.rodata@53c784(size_without_padding=588,padding=0,full_name=__typeid__ZTSN5blink13ListedElementE_byte_array,object_path=,source_path=,flags={},num_aliases=1,component=)
+.rodata@53c9d0(size_without_padding=4453,padding=0,full_name=__typeid__ZTSN5blink11HTMLElementE_byte_array,object_path=,source_path=,flags={},num_aliases=1,component=)
+.rodata@53db35(size_without_padding=1928,padding=0,full_name=__typeid__ZTSN5blink14FormAssociatedE_byte_array,object_path=,source_path=,flags={},num_aliases=1,component=)
+.rodata@53e2bd(size_without_padding=1969,padding=0,full_name=__typeid__ZTSN7content20NotificationObserverE_byte_array,object_path=,source_path=,flags={},num_aliases=1,component=)
+.rodata@53ea6e(size_without_padding=42,padding=0,full_name=__typeid__ZTSN7network5mojom16URLLoaderFactoryE_byte_array,object_path=,source_path=,flags={},num_aliases=1,component=)
+.rodata@53ea98(size_without_padding=48563,padding=0,full_name=__typeid__ZTSN7network5mojom15URLLoaderClientE_byte_array,object_path=,source_path=,flags={},num_aliases=1,component=)
+.rodata@54a84b(size_without_padding=1,padding=0,full_name=__typeid__ZTSN7content31BrowserChildProcessHostDelegateE_byte_array,object_path=,source_path=,flags={},num_aliases=1,component=)
+.rodata@54a84c(size_without_padding=65,padding=0,full_name=__typeid__ZTSN5blink10SupplementINS_16ExecutionContextEEE_byte_array,object_path=,source_path=,flags={},num_aliases=1,component=)
+.text@8b6140(size_without_padding=64,padding=0,full_name=,object_path=obj/third_party/boringssl/boringssl_asm/chacha-armv4.o,source_path=,flags={},num_aliases=1,component=)
+.text@8b6180(size_without_padding=1008,padding=0,full_name=ChaCha20_ctr32,object_path=obj/third_party/boringssl/boringssl_asm/chacha-armv4.o,source_path=,flags={},num_aliases=1,component=)
+.text@8b6580(size_without_padding=2204,padding=0,full_name=ChaCha20_neon,object_path=obj/third_party/boringssl/boringssl_asm/chacha-armv4.o,source_path=,flags={},num_aliases=1,component=)
+.text@29da300(size_without_padding=16,padding=0,full_name=** CFI jump table,object_path=,source_path=,flags={},num_aliases=1,component=)
+.text@29da310(size_without_padding=40,padding=0,full_name=** CFI jump table,object_path=,source_path=,flags={},num_aliases=1,component=)
+.text@29da340(size_without_padding=8,padding=0,full_name=** CFI jump table,object_path=,source_path=,flags={},num_aliases=1,component=)
+.text@29da350(size_without_padding=5,padding=0,full_name=main,object_path=,source_path=,flags={},num_aliases=1,component=)
+.text@2a59fb0(size_without_padding=24,padding=0,full_name=base::internal::DestructorAtExitLazyInstanceTraits<ChromeContentGpuClient>::New(void*),object_path=,source_path=,flags={},num_aliases=1,component=)
+.text@2a59fd0(size_without_padding=31,padding=0,full_name=base::LazyInstance<ChromeContentGpuClient, base::internal::DestructorAtExitLazyInstanceTraits<ChromeContentGpuClient> >::OnExit(void*),object_path=,source_path=,flags={},num_aliases=1,component=)
+.text@2a59ff0(size_without_padding=24,padding=0,full_name=base::internal::DestructorAtExitLazyInstanceTraits<ChromeContentRendererClient>::New(void*),object_path=,source_path=,flags={},num_aliases=1,component=)
+.text@2a5a010(size_without_padding=31,padding=0,full_name=base::LazyInstance<ChromeContentRendererClient, base::internal::DestructorAtExitLazyInstanceTraits<ChromeContentRendererClient> >::OnExit(void*),object_path=,source_path=,flags={},num_aliases=1,component=)
+.text@2a5a030(size_without_padding=24,padding=0,full_name=base::internal::DestructorAtExitLazyInstanceTraits<ChromeContentUtilityClient>::New(void*),object_path=,source_path=,flags={},num_aliases=1,component=)
+.data.rel.ro@3255000(size_without_padding=60,padding=0,full_name=fft_tab_vfp,object_path=obj/third_party/ffmpeg/libffmpeg_internal.a(ffmpeg_internal/fft_vfp.o),source_path=,flags={},num_aliases=1,component=)
+.data.rel.ro@325503c(size_without_padding=60,padding=0,full_name=fft_tab_neon,object_path=obj/third_party/ffmpeg/libffmpeg_internal.a(ffmpeg_internal/fft_neon.o),source_path=,flags={},num_aliases=1,component=)
+.data.rel.ro@325e5c0(size_without_padding=28,padding=0,full_name=vtable for content::BackgroundSyncContextImpl,object_path=,source_path=,flags={},num_aliases=1,component=)
+.data.rel.ro@325e5e0(size_without_padding=24,padding=0,full_name=vtable for content::BackgroundSyncContext,object_path=,source_path=,flags={},num_aliases=1,component=)
+.data.rel.ro@325e600(size_without_padding=100,padding=0,full_name=vtable for content::BackgroundSyncManager,object_path=,source_path=,flags={},num_aliases=1,component=)
+.data.rel.ro@326e3a0(size_without_padding=20,padding=0,full_name=_ZTVN12v8_inspector12_GLOBAL__N_120HeapSnapshotProgressE$5c43413d323f2f7febefe6cf2f4ba25c,object_path=,source_path=,flags={},num_aliases=1,component=)
+.data.rel.ro@923d5e0(size_without_padding=16,padding=0,full_name=.L__unnamed_1426,object_path=,source_path=,flags={},num_aliases=1,component=)
+.data.rel.ro@923d5f0(size_without_padding=120,padding=0,full_name=vtable for media::ClearKeyProperties,object_path=,source_path=,flags={},num_aliases=1,component=)
+.data.rel.ro@923d690(size_without_padding=120,padding=0,full_name=vtable for cdm::ExternalClearKeyProperties,object_path=,source_path=,flags={},num_aliases=1,component=)
diff --git a/tools/binary_size/libsupersize/testdata/linker_map_parser/test_lld-lto_v1_cfi.map b/tools/binary_size/libsupersize/testdata/linker_map_parser/test_lld-lto_v1_cfi.map
new file mode 100644
index 0000000..23c753e7
--- /dev/null
+++ b/tools/binary_size/libsupersize/testdata/linker_map_parser/test_lld-lto_v1_cfi.map
@@ -0,0 +1,138 @@
+# Test Linker map for LLD with ThinLTO, "v1" format, focusing on changes
+# introduced CFI.
+# .map files actually don't have comments and blank lines! These are added to
+# improve documentation, and are stripped by tests.
+
+# First line is needed to identify .map file type.
+     VMA      LMA     Size Align Out     In      Symbol
+# .rodata can have CFI byte arrays (start with other symbols).
+  23dd00   23dd00   6725ea   256 .rodata
+  23dd00   23dd00        4     8         obj/v8/v8_external_snapshot/embedded.o:(.rodata)
+  23dd00   23dd00        0     1                 v8_Default_embedded_blob_size_
+  23dd08   23dd08        8     8         obj/third_party/crashpad/crashpad/client/libclient.a(client/crashpad_info_note.o):(.rodata)
+  23dd08   23dd08        0     1                 CRASHPAD_NOTE_REFERENCE
+  23dd10   23dd10       20    16         obj/third_party/ffmpeg/libffmpeg_internal.a(ffmpeg_internal/fft_neon.o):(.rodata)
+  23dd10   23dd10       10     1                 pmmp
+  23dd20   23dd20       10     1                 mppm
+  23dd30   23dd30   2de399     1         <internal>:(.rodata)
+  51c0cc   51c0cc        4     4         ../../third_party/android_ndk/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/libandroid_support.a(locale.o):(.rodata.__emutls_t._ZL16g_current_locale)
+  51c0cc   51c0cc        4     1                 __emutls_t._ZL16g_current_locale
+  51c0cc   51c0cc        0     1                 $d
+
+# CFI byte arrays.
+# See: https://clang.llvm.org/docs/ControlFlowIntegrityDesign.html
+  51d2a0   51d2a0    2d5ed    16         lto.tmp:(.rodata..L__unnamed_3117)
+  51d2a0   51d2a0        1     1                 __typeid__ZTSN3IPC8ListenerE_byte_array
+  51d2a0   51d2a0        1     1                 __typeid__ZTSN4mojo15MessageReceiverE_byte_array
+  51d2a0   51d2a0        1     1                 __typeid__ZTSN5blink10NameClientE_byte_array
+  51d2a0   51d2a0        1     1                 __typeid__ZTSN5blink17DisplayItemClientE_byte_array
+  51d2a0   51d2a0        1     1                 __typeid__ZTSN5blink21GarbageCollectedMixinE_byte_array
+  51d2a0   51d2a0        1     1                 __typeid__ZTSN5blink24ContextLifecycleObserverE_byte_array
+  51d2a0   51d2a0        1     1                 __typeid__ZTSN5blink25ActiveScriptWrappableBaseE_byte_array
+  51d2a0   51d2a0        1     1                 __typeid__ZTSN7content19WebContentsObserverE_byte_array
+  52b7f9   52b7f9        1     1                 __typeid__ZTSN4base16SupportsUserData4DataE_byte_array
+  52c545   52c545        1     1                 __typeid__ZTSN5blink21ImageResourceObserverE_byte_array
+  52cb35   52cb35        1     1                 __typeid__ZTS12KeyedService_byte_array
+  52cf3b   52cf3b        1     1                 __typeid__ZTSN3IPC6SenderE_byte_array
+  534325   534325        1     1                 __typeid__ZTSN5blink15ScriptWrappableE_byte_array
+  535ffd   535ffd        1     1                 __typeid__ZTSN4base11trace_event18MemoryDumpProviderE_byte_array
+  5365da   5365da        1     1                 __typeid__ZTSN5blink23MediaControlElementBaseE_byte_array
+  537859   537859        1     1                 __typeid__ZTSN5blink11EventTargetE_byte_array
+  538ce2   538ce2        1     1                 .Lbits_use.897
+  538ce2   538ce2        1     1                 .Lbits_use.898
+  538ce2   538ce2        1     1                 .Lbits_use.899
+  538ce2   538ce2        1     1                 .Lbits_use.900
+  538ce2   538ce2        1     1                 .Lbits_use.902
+  538ce2   538ce2        1     1                 .Lbits_use.903
+  538ce2   538ce2        1     1                 .Lbits_use.904
+  538ce2   538ce2        1     1                 __typeid__ZTSN5blink4NodeE_byte_array
+  538d9a   538d9a        1     1                 __typeid__ZTSN5blink13ContainerNodeE_byte_array
+  53a031   53a031        1     1                 .Lbits_use.905
+  53a031   53a031        1     1                 __typeid__ZTSN5blink7ElementE_byte_array
+  53b4a6   53b4a6        1     1                 __typeid__ZTSN5blink10SupplementINS_8DocumentEEE_byte_array
+  53c784   53c784        1     1                 __typeid__ZTSN5blink13ListedElementE_byte_array
+  53c9d0   53c9d0        1     1                 __typeid__ZTSN5blink11HTMLElementE_byte_array
+  53db35   53db35        1     1                 __typeid__ZTSN5blink14FormAssociatedE_byte_array
+  53e2bd   53e2bd        1     1                 __typeid__ZTSN7content20NotificationObserverE_byte_array
+  53ea6e   53ea6e        1     1                 __typeid__ZTSN7network5mojom16URLLoaderFactoryE_byte_array
+  53ea98   53ea98        1     1                 __typeid__ZTSN7network5mojom15URLLoaderClientE_byte_array
+# Snip out many examples (pretend that the previous symbol was huge).
+  54a84b   54a84b        1     1                 __typeid__ZTSN5blink32PresentationAvailabilityObserverE_byte_array
+  54a84b   54a84b        1     1                 __typeid__ZTSN5blink7UIEventE_byte_array
+  54a84b   54a84b        1     1                 __typeid__ZTSN6syncer15ChangeProcessorE_byte_array
+  54a84b   54a84b        1     1                 __typeid__ZTSN7content20ServiceWorkerVersion8ObserverE_byte_array
+  54a84b   54a84b        1     1                 __typeid__ZTSN7content31BrowserChildProcessHostDelegateE_byte_array
+  54a84c   54a84c        1     1                 __typeid__ZTSN5blink10SupplementINS_16ExecutionContextEEE_byte_array
+
+# Other sections.
+  8b02ec   8b02ec     5e24     4 .ARM.extab
+  8b02ec   8b02ec        c     4         ../../third_party/android_ndk/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/libandroid_support.a(locale.o):(.ARM.extab.text.uselocale)
+  8b02ec   8b02ec        0     1                 $d
+  8b02f8   8b02f8        c     4         ../../third_party/android_ndk/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/libandroid_support.a(locale.o):(.ARM.extab.text.newlocale)
+  8b02f8   8b02f8        0     1                 $d
+
+# .text can contain L.cfi.jumptable (start with other symbols).
+  8b6140   8b6140  299d5dc    64 .text
+  8b6140   8b6140      cdc    32         obj/third_party/boringssl/boringssl_asm/chacha-armv4.o:(.text)
+  8b6144   8b6144        0     1                 $d.0
+  8b6180   8b6180        0     1                 $a.1
+  8b6180   8b6180      3f0     1                 ChaCha20_ctr32
+  8b6580   8b6580      89c     1                 ChaCha20_neon
+
+# Sample L.cfi.jumptable.
+ 29da300  29da300       10    16         lto.tmp:(.text..L.cfi.jumptable.2091)
+ 29da300  29da300        1     1                 __typeid__ZTSF15vpx_codec_err_tP18vpx_codec_alg_privPK9vpx_imagelmlmE_global_addr
+ 29da300  29da300       10     1                 vp8e_encode$7d077cb90f9945e3a735fb4eb6fe8afc
+ 29da308  29da308       10     1                 encoder_encode$34c1039d90d239ad455f8ef2e0009211
+ 29da310  29da310       28    16         lto.tmp:(.text..L.cfi.jumptable.2093)
+ 29da310  29da310       28     1                 base::internal::Invoker<base::internal::BindState<void (extensions::image_writer::WriteFromUrlOperation::*)(base::FilePath), base::internal::UnretainedWrapper<extensions::image_writer::WriteFromUrlOperation> >, void (base::FilePath)>::RunOnce(base::internal::BindStateBase*, base::FilePath&&)
+ 29da310  29da310        1     1                 __typeid__ZTSFvPN4base8internal13BindStateBaseEONS_8FilePathEE_global_addr
+ 29da318  29da318       28     1                 base::internal::Invoker<base::internal::BindState<void (extensions::ExtensionDownloader::*)(base::FilePath), base::internal::UnretainedWrapper<extensions::ExtensionDownloader> >, void (base::FilePath)>::RunOnce(base::internal::BindStateBase*, base::FilePath&&)
+ 29da320  29da320       28     1                 base::internal::Invoker<base::internal::BindState<void (cloud_print::PrivetURLLoader::*)(base::FilePath), base::WeakPtr<cloud_print::PrivetURLLoader> >, void (base::FilePath)>::RunOnce(base::internal::BindStateBase*, base::FilePath&&)
+ 29da328  29da328       28     1                 base::internal::Invoker<base::internal::BindState<void (FileDownloader::*)(base::FilePath), base::internal::UnretainedWrapper<FileDownloader> >, void (base::FilePath)>::RunOnce(base::internal::BindStateBase*, base::FilePath&&)
+ 29da330  29da330       28     1                 _ZN4base8internal7InvokerINS0_9BindStateIZN13update_client18NetworkFetcherImpl14DownloadToFileERK4GURLRKNS_8FilePathENS_12OnceCallbackIFvS7_ilEEENS_17RepeatingCallbackIFvlEEENSB_IFvS8_ilEEEE3$_1JPN7network15SimpleURLLoaderESI_EEEFvS8_EE7RunOnceEPNS0_13BindStateBaseEOS8_$465b209000b54f7cf2bdcf6c53188e38
+ 29da340  29da340        8    16         lto.tmp:(.text..L.cfi.jumptable.2094)
+ 29da340  29da340        1     1                 __typeid__ZTSFlPvS_miE_global_addr
+ 29da340  29da340        8     1                 fseek_file_func$bb5c8d26f6b2986183d5943acbc61f67
+ 29da350  29da350        5    16         thinlto-cache/Thin-82ee80.tmp.o:(.text.main)
+ 29da350  29da350        5     1                 main
+
+# Demangled symbols that end with ' (.cfi)'.
+ 2a59fb0  2a59fb0       18    16         thinlto-cache/Thin-7c598e.tmp.o:(.text._ZN4base8internal34DestructorAtExitLazyInstanceTraitsI22ChromeContentGpuClientE3NewEPv.cfi)
+ 2a59fb0  2a59fb0       18     1                 base::internal::DestructorAtExitLazyInstanceTraits<ChromeContentGpuClient>::New(void*) (.cfi)
+ 2a59fd0  2a59fd0       1f    16         thinlto-cache/Thin-7c598e.tmp.o:(.text._ZN4base12LazyInstanceI22ChromeContentGpuClientNS_8internal34DestructorAtExitLazyInstanceTraitsIS1_EEE6OnExitEPv.cfi)
+ 2a59fd0  2a59fd0       1f     1                 base::LazyInstance<ChromeContentGpuClient, base::internal::DestructorAtExitLazyInstanceTraits<ChromeContentGpuClient> >::OnExit(void*) (.cfi)
+ 2a59ff0  2a59ff0       18    16         thinlto-cache/Thin-7c598e.tmp.o:(.text._ZN4base8internal34DestructorAtExitLazyInstanceTraitsI27ChromeContentRendererClientE3NewEPv.cfi)
+ 2a59ff0  2a59ff0       18     1                 base::internal::DestructorAtExitLazyInstanceTraits<ChromeContentRendererClient>::New(void*) (.cfi)
+ 2a5a010  2a5a010       1f    16         thinlto-cache/Thin-7c598e.tmp.o:(.text._ZN4base12LazyInstanceI27ChromeContentRendererClientNS_8internal34DestructorAtExitLazyInstanceTraitsIS1_EEE6OnExitEPv.cfi)
+ 2a5a010  2a5a010       1f     1                 base::LazyInstance<ChromeContentRendererClient, base::internal::DestructorAtExitLazyInstanceTraits<ChromeContentRendererClient> >::OnExit(void*) (.cfi)
+ 2a5a030  2a5a030       18    16         thinlto-cache/Thin-7c598e.tmp.o:(.text._ZN4base8internal34DestructorAtExitLazyInstanceTraitsI26ChromeContentUtilityClientE3NewEPv.cfi)
+ 2a5a030  2a5a030       18     1                 base::internal::DestructorAtExitLazyInstanceTraits<ChromeContentUtilityClient>::New(void*) (.cfi)
+
+# .data.re.ro can have __typeid_ usages that are unrelated to CFI (start with other symbols).
+ 3255000  3255000   2514e4    16 .data.rel.ro
+ 3255000  3255000       3c     4         obj/third_party/ffmpeg/libffmpeg_internal.a(ffmpeg_internal/fft_vfp.o):(.data.rel.ro)
+ 3255000  3255000       3c     1                 fft_tab_vfp
+ 325503c  325503c       3c     4         obj/third_party/ffmpeg/libffmpeg_internal.a(ffmpeg_internal/fft_neon.o):(.data.rel.ro)
+ 325503c  325503c       3c     1                 fft_tab_neon
+
+# __typeid_ usages that are unrelated to CFI.
+ 325e5c0  325e5c0       38    16         lto.tmp:(.data.rel.ro..L__unnamed_306)
+ 325e5c0  325e5c0       1c     1                 vtable for content::BackgroundSyncContextImpl
+ 325e5c8  325e5c8        1     1                 __typeid__ZTSN7content21BackgroundSyncContextE_global_addr
+ 325e5c8  325e5c8        1     1                 __typeid__ZTSN7content25BackgroundSyncContextImplE_global_addr
+ 325e5e0  325e5e0       18     1                 vtable for content::BackgroundSyncContext
+ 325e600  325e600       64    16         lto.tmp:(.data.rel.ro..L__unnamed_307)
+ 325e600  325e600       64     1                 vtable for content::BackgroundSyncManager
+ 325e608  325e608        1     1                 __typeid__ZTSN7content21BackgroundSyncManagerE_global_addr
+
+ 326e3a0  326e3a0       14    16         lto.tmp:(.data.rel.ro..L__unnamed_769)
+ 326e3a0  326e3a0       14     1                 _ZTVN12v8_inspector12_GLOBAL__N_120HeapSnapshotProgressE$5c43413d323f2f7febefe6cf2f4ba25c
+ 326e3a8  326e3a8        1     1                 __typeid_1$5c43413d323f2f7febefe6cf2f4ba25c_global_addr
+ 326e3a8  326e3a8        1     1                 __typeid__ZTSN2v815ActivityControlE_global_addr
+
+ 923d5e0  923d5e0      128    16         lto.tmp:(.data.rel.ro..L__unnamed_1426)
+ 923d5f0  923d5f0       78     1                 vtable for media::ClearKeyProperties
+ 923d600  923d600        1     1                 __typeid__ZTSN5media19KeySystemPropertiesE_96_unique_member
+ 923d600  923d600        1     1                 __typeid__ZTSN5media19KeySystemPropertiesE_global_addr
+ 923d690  923d690       78     1                 vtable for cdm::ExternalClearKeyProperties
diff --git a/tools/perf/cli_tools/soundwave/studies/health_study.py b/tools/perf/cli_tools/soundwave/studies/health_study.py
index eb5711a4..ba2d8385 100644
--- a/tools/perf/cli_tools/soundwave/studies/health_study.py
+++ b/tools/perf/cli_tools/soundwave/studies/health_study.py
@@ -2,7 +2,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-CLOUD_PATH = 'gs://chome-health-tvdata/datasets/health_study.csv'
+CLOUD_PATH = 'gs://chrome-health-tvdata/datasets/health_study.csv'
 
 OVERALL_PSS = ('memory:{browser}:all_processes:reported_by_os:system_memory'
                ':proportional_resident_size_avg')
@@ -28,7 +28,7 @@
 
 def IterSystemHealthBots():
   yield 'ChromiumPerf/android-go-perf'
-  yield 'ChromiumPerfFyi/android-go_webview-perf'
+  yield 'ChromiumPerf/android-go_webview-perf'
 
 
 def GetBrowserFromBot(bot):
diff --git a/tools/perf/cli_tools/soundwave/studies/v8_study.py b/tools/perf/cli_tools/soundwave/studies/v8_study.py
index 5a09749..ea50583 100644
--- a/tools/perf/cli_tools/soundwave/studies/v8_study.py
+++ b/tools/perf/cli_tools/soundwave/studies/v8_study.py
@@ -6,7 +6,7 @@
 from cli_tools.soundwave.tables import timeseries
 
 
-CLOUD_PATH = 'gs://chome-health-tvdata/datasets/v8_report.csv'
+CLOUD_PATH = 'gs://chrome-health-tvdata/datasets/v8_report.csv'
 
 ANDROID_GO = 'ChromiumPerf:android-go-perf'
 V8_EFFECTIVE_SIZE = (
diff --git a/ui/android/resources/resource_manager_impl.cc b/ui/android/resources/resource_manager_impl.cc
index e79d19f..78e6e86 100644
--- a/ui/android/resources/resource_manager_impl.cc
+++ b/ui/android/resources/resource_manager_impl.cc
@@ -159,13 +159,12 @@
   // Build a color filter to use on the base resource. This filter multiplies
   // the RGB components by the components of the new color but retains the
   // alpha of the original image.
-  SkScalar color_matrix[20] = {
-      0, 0, 0, 0, SkColorGetR(tint_color),
-      0, 0, 0, 0, SkColorGetG(tint_color),
-      0, 0, 0, 0, SkColorGetB(tint_color),
-      0, 0, 0, 1, 0};
+  float color_matrix[20] = {0, 0, 0, 0, SkColorGetR(tint_color) * (1.0f / 255),
+                            0, 0, 0, 0, SkColorGetG(tint_color) * (1.0f / 255),
+                            0, 0, 0, 0, SkColorGetB(tint_color) * (1.0f / 255),
+                            0, 0, 0, 1, 0};
   SkPaint color_filter;
-  color_filter.setColorFilter(SkColorFilters::MatrixRowMajor255(color_matrix));
+  color_filter.setColorFilter(SkColorFilters::Matrix(color_matrix));
 
   // Draw the resource and make it immutable.
   base_image->ui_resource()
diff --git a/ui/aura/BUILD.gn b/ui/aura/BUILD.gn
index d2169dbf..f23dded 100644
--- a/ui/aura/BUILD.gn
+++ b/ui/aura/BUILD.gn
@@ -157,6 +157,7 @@
     "window_port_for_shutdown.cc",
     "window_port_for_shutdown.h",
     "window_targeter.cc",
+    "window_tracker.cc",
     "window_tree_host.cc",
     "window_tree_host_platform.cc",
   ]
diff --git a/ui/aura/window_tracker.cc b/ui/aura/window_tracker.cc
new file mode 100644
index 0000000..8b43559
--- /dev/null
+++ b/ui/aura/window_tracker.cc
@@ -0,0 +1,60 @@
+// 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/aura/window_tracker.h"
+
+#include "ui/aura/window.h"
+
+namespace aura {
+
+WindowTracker::WindowTracker(const WindowList& windows) {
+  for (Window* window : windows)
+    Add(window);
+}
+
+WindowTracker::WindowTracker() = default;
+
+WindowTracker::~WindowTracker() {
+  RemoveAll();
+}
+
+void WindowTracker::Add(Window* window) {
+  if (base::ContainsValue(windows_, window))
+    return;
+
+  window->AddObserver(this);
+  windows_.push_back(window);
+}
+
+void WindowTracker::RemoveAll() {
+  for (Window* window : windows_)
+    window->RemoveObserver(this);
+  windows_.clear();
+}
+
+void WindowTracker::Remove(Window* window) {
+  auto iter = std::find(windows_.begin(), windows_.end(), window);
+  if (iter != windows_.end()) {
+    window->RemoveObserver(this);
+    windows_.erase(iter);
+  }
+}
+
+Window* WindowTracker::Pop() {
+  DCHECK(!windows_.empty());
+  Window* result = windows_[0];
+  Remove(result);
+  return result;
+}
+
+bool WindowTracker::Contains(Window* window) const {
+  return base::ContainsValue(windows_, window);
+}
+
+void WindowTracker::OnWindowDestroying(Window* window) {
+  DCHECK(Contains(window));
+  Remove(window);
+}
+
+}  // namespace aura
diff --git a/ui/aura/window_tracker.h b/ui/aura/window_tracker.h
index d44ec3f..591816b 100644
--- a/ui/aura/window_tracker.h
+++ b/ui/aura/window_tracker.h
@@ -7,14 +7,49 @@
 
 #include <vector>
 
-#include "base/compiler_specific.h"
 #include "base/macros.h"
+#include "ui/aura/aura_export.h"
 #include "ui/aura/window_observer.h"
-#include "ui/base/window_tracker_template.h"
 
 namespace aura {
 
-using WindowTracker = ui::WindowTrackerTemplate<Window, WindowObserver>;
+// This class is used to track an ordered list of Windows. When a Window is
+// destroyed it is removed from the list of Windows.
+class AURA_EXPORT WindowTracker : public WindowObserver {
+ public:
+  // A vector is used for tracking the windows (instead of a set) as some places
+  // care about ordering.
+  using WindowList = std::vector<Window*>;
+
+  explicit WindowTracker(const WindowList& windows);
+  WindowTracker();
+  ~WindowTracker() override;
+
+  // Returns the set of windows being observed.
+  const WindowList& windows() const { return windows_; }
+
+  // Adds |window| to the set of Windows being tracked.
+  void Add(Window* window);
+
+  void RemoveAll();
+
+  // Removes |window| from the set of windows being tracked.
+  void Remove(Window* window);
+
+  Window* Pop();
+
+  // Returns true if |window| was previously added and has not been removed or
+  // deleted.
+  bool Contains(Window* window) const;
+
+  // WindowObserver overrides:
+  void OnWindowDestroying(Window* window) override;
+
+ private:
+  WindowList windows_;
+
+  DISALLOW_COPY_AND_ASSIGN(WindowTracker);
+};
 
 }  // namespace aura
 
diff --git a/ui/base/BUILD.gn b/ui/base/BUILD.gn
index 960d583..098da250 100644
--- a/ui/base/BUILD.gn
+++ b/ui/base/BUILD.gn
@@ -484,11 +484,6 @@
     }
   }
 
-  if (use_aura) {
-    deps += [ "//ui/events" ]
-    sources += [ "window_tracker_template.h" ]
-  }
-
   if (!use_aura || !is_linux) {
     sources -= [ "resource/resource_bundle_auralinux.cc" ]
   }
diff --git a/ui/base/window_tracker_template.h b/ui/base/window_tracker_template.h
deleted file mode 100644
index e91bdac..0000000
--- a/ui/base/window_tracker_template.h
+++ /dev/null
@@ -1,89 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef UI_BASE_WINDOW_TRACKER_TEMPLATE_H_
-#define UI_BASE_WINDOW_TRACKER_TEMPLATE_H_
-
-#include <vector>
-
-#include "base/macros.h"
-#include "base/stl_util.h"
-
-namespace ui {
-
-// This class is used to track an ordered list of objects that support an
-// observer interface with the function OnWindowDestroying(). When the object
-// is destroyed it is removed from the ordered list of objects.
-// Examples of T include aura::Window and its corresponding
-// aura::WindowObserver interface.
-template <class T, class TObserver>
-class WindowTrackerTemplate : public TObserver {
- public:
-  // A vector<> is used for tracking the windows (instead of a set<>) because
-  // the user may want to know about the order of the windows that have been
-  // added.
-  using WindowList = std::vector<T*>;
-
-  explicit WindowTrackerTemplate(const WindowList& windows) {
-    for (T* window : windows)
-      Add(window);
-  }
-  WindowTrackerTemplate() {}
-  ~WindowTrackerTemplate() override { RemoveAll(); }
-
-  // Returns the set of windows being observed.
-  const WindowList& windows() const { return windows_; }
-
-  // Adds |window| to the set of Windows being tracked.
-  void Add(T* window) {
-    if (base::ContainsValue(windows_, window))
-      return;
-
-    window->AddObserver(this);
-    windows_.push_back(window);
-  }
-
-  void RemoveAll() {
-    for (T* window : windows_)
-      window->RemoveObserver(this);
-    windows_.clear();
-  }
-
-  // Removes |window| from the set of windows being tracked.
-  void Remove(T* window) {
-    auto iter = std::find(windows_.begin(), windows_.end(), window);
-    if (iter != windows_.end()) {
-      window->RemoveObserver(this);
-      windows_.erase(iter);
-    }
-  }
-
-  T* Pop() {
-    DCHECK(!windows_.empty());
-    T* result = windows_[0];
-    Remove(result);
-    return result;
-  }
-
-  // Returns true if |window| was previously added and has not been removed or
-  // deleted.
-  bool Contains(T* window) const {
-    return base::ContainsValue(windows_, window);
-  }
-
-  // Observer overrides:
-  void OnWindowDestroying(T* window) override {
-    DCHECK(Contains(window));
-    Remove(window);
-  }
-
- private:
-  WindowList windows_;
-
-  DISALLOW_COPY_AND_ASSIGN(WindowTrackerTemplate);
-};
-
-}  // namespace ui
-
-#endif  // UI_BASE_WINDOW_TRACKER_TEMPLATE_H_
diff --git a/ui/compositor/test/in_process_context_factory.cc b/ui/compositor/test/in_process_context_factory.cc
index c3eeff37..e13d0026 100644
--- a/ui/compositor/test/in_process_context_factory.cc
+++ b/ui/compositor/test/in_process_context_factory.cc
@@ -129,6 +129,8 @@
   bool HasExternalStencilTest() const override { return false; }
   void ApplyExternalStencil() override {}
   unsigned UpdateGpuFence() override { return 0; }
+  void SetUpdateVSyncParametersCallback(
+      viz::UpdateVSyncParametersCallback callback) override {}
 
  private:
   void OnSwapBuffersComplete() {