diff --git a/DEPS b/DEPS
index a2c97c74..73f2a63a 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': 'b5c685991faa79d6ac5caeacd48644eca4954426',
+  'skia_revision': '26f410d8cfc0a0416cfd24ea9600bffc6922380f',
   # 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': 'bd925e997f619432df931dfdc27e89994fff5bfa',
+  'v8_revision': '583668a38b0f491873eb3e5950297c4a1412303a',
   # 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.
@@ -141,11 +141,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': 'f39b4f0280ef8dafa7df18c6f0e93e2573e4bf9d',
+  'angle_revision': 'a131a1515471571ec058e8940c0de1bdad2a0060',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
-  'swiftshader_revision': 'f0aa9d5ab738e6007eb962d0d7fc9b5abce5c5ef',
+  'swiftshader_revision': '26357f18cb2a6d319c8c4717c98ea0dd8bedc4f4',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
@@ -1172,7 +1172,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' +  '7ed01b7a5be9a78286f5a9c82e41354f28b761c7',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' +  '0d930129cf195eff04d3fddd3f95925ead17b875',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + 'ac0d98b5cee6c024b0cffeb4f8f45b6fc5ccdb78',
@@ -1343,7 +1343,7 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + '688fbfe33779392aa210d67d4aa12cb012f112c2',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + 'f4770401dc296637adb7aec2c8c2b80b65fc94b8',
+    Var('webrtc_git') + '/src.git' + '@' + 'cb2a4ffb2bbd09958ad62b7016ac551a20d6dc62',
 
   'src/third_party/xdg-utils': {
       'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d',
@@ -1384,7 +1384,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@c516188a72115b5ea63a94ff2eb2bbb57dbd73c7',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@fe00b609c1733bf30c03930ba5eb20bf0caf3012',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/WATCHLISTS b/WATCHLISTS
index 38d2cf1..4d7f817 100644
--- a/WATCHLISTS
+++ b/WATCHLISTS
@@ -2558,7 +2558,8 @@
     'video_gpu': ['acourbot+watch@chromium.org'],
     'virtual_keyboard': ['blakeo+virtualkb@chromium.org',
                          'dfaden+virtualkb@google.com',
-                         'yhanada+watchvk@chromium.org'],
+                         'yhanada+watchvk@chromium.org',
+                         'shend+watch@chromium.org'],
     'virtual_reality': ['feature-vr-reviews@chromium.org'],
     'vulkan': ['cblume+vulkan@chromium.org'],
     'wake_lock': ['mattreynolds+watch@chromium.org'],
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index 5d7a1063..7580936 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -1339,6 +1339,7 @@
 
     # TODO(stevenjb): Investigate whether this is OK. https://crbug.com/644336.
     "//chromeos/dbus/audio",
+    "//chromeos/dbus/hammerd",
     "//chromeos/dbus/power",
     "//chromeos/dbus/power:power_manager_proto",
     "//chromeos/dbus/services:services",
@@ -1940,6 +1941,7 @@
     # TODO(stevenjb): Investigate whether this is OK. https://crbug.com/644336.
     "//chromeos/audio",
     "//chromeos/dbus:test_support",
+    "//chromeos/dbus/hammerd",
     "//chromeos/dbus/power",
     "//chromeos/dbus/power:power_manager_proto",
 
diff --git a/ash/login/ui/lock_contents_view.cc b/ash/login/ui/lock_contents_view.cc
index 2a6235a..7b4c4ef 100644
--- a/ash/login/ui/lock_contents_view.cc
+++ b/ash/login/ui/lock_contents_view.cc
@@ -56,7 +56,7 @@
 #include "ui/gfx/geometry/size.h"
 #include "ui/gfx/geometry/vector2d.h"
 #include "ui/gfx/text_constants.h"
-#include "ui/views/accessibility/ax_aura_obj_cache.h"
+#include "ui/views/accessibility/view_accessibility.h"
 #include "ui/views/background.h"
 #include "ui/views/controls/button/button.h"
 #include "ui/views/controls/label.h"
@@ -589,13 +589,9 @@
 void LockContentsView::GetAccessibleNodeData(ui::AXNodeData* node_data) {
   Shelf* shelf = Shelf::ForWindow(GetWidget()->GetNativeWindow());
   ShelfWidget* shelf_widget = shelf->shelf_widget();
-  int next_id = views::AXAuraObjCache::GetInstance()->GetID(shelf_widget);
-  node_data->AddIntAttribute(ax::mojom::IntAttribute::kNextFocusId, next_id);
+  GetViewAccessibility().OverrideNextFocus(shelf_widget);
 
-  int previous_id =
-      views::AXAuraObjCache::GetInstance()->GetID(shelf->GetStatusAreaWidget());
-  node_data->AddIntAttribute(ax::mojom::IntAttribute::kPreviousFocusId,
-                             previous_id);
+  GetViewAccessibility().OverridePreviousFocus(shelf->GetStatusAreaWidget());
   node_data->SetNameExplicitlyEmpty();
 }
 
diff --git a/ash/public/cpp/vector_icons/BUILD.gn b/ash/public/cpp/vector_icons/BUILD.gn
index 350c537f..f4c69a9d 100644
--- a/ash/public/cpp/vector_icons/BUILD.gn
+++ b/ash/public/cpp/vector_icons/BUILD.gn
@@ -11,7 +11,6 @@
   icons = [
     "assistant.icon",
     "assistant_mic.icon",
-    "auto_launch_managed_guest_session.icon",
     "notification_assistant.icon",
     "notification_captive_portal.icon",
     "notification_cellular_alert.icon",
diff --git a/ash/public/cpp/vector_icons/auto_launch_managed_guest_session.icon b/ash/public/cpp/vector_icons/auto_launch_managed_guest_session.icon
deleted file mode 100644
index 83328fd..0000000
--- a/ash/public/cpp/vector_icons/auto_launch_managed_guest_session.icon
+++ /dev/null
@@ -1,40 +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.
-
-CANVAS_DIMENSIONS, 96,
-MOVE_TO, 48, 8,
-CUBIC_TO, 70.08f, 8, 88, 25.92f, 88, 48,
-CUBIC_TO, 88, 70.08f, 70.08f, 88, 48, 88,
-CUBIC_TO, 25.92f, 88, 8, 70.08f, 8, 48,
-CUBIC_TO, 8, 25.92f, 25.92f, 8, 48, 8,
-CLOSE,
-MOVE_TO, 48, 16,
-CUBIC_TO, 42.48f, 16, 38, 20.48f, 38, 26,
-CUBIC_TO, 38, 31.52f, 42.48f, 36, 48, 36,
-CUBIC_TO, 53.52f, 36, 58, 31.52f, 58, 26,
-CUBIC_TO, 58, 20.48f, 53.52f, 16, 48, 16,
-CLOSE,
-MOVE_TO, 68, 68.88f,
-LINE_TO, 68, 51.12f,
-CUBIC_TO, 68, 44.8f, 57.48f, 40, 48, 40,
-CUBIC_TO, 38.52f, 40, 28, 44.8f, 28, 51.12f,
-LINE_TO, 28, 68.88f,
-CUBIC_TO, 28, 72.48f, 31.36f, 75.56f, 36, 77.56f,
-CUBIC_TO, 39.56f, 79.08f, 43.88f, 80, 48, 80,
-CUBIC_TO, 49.32f, 80, 50.68f, 79.88f, 52, 79.68f,
-LINE_TO, 52, 71.68f,
-CUBIC_TO, 50.68f, 71.88f, 49.32f, 72, 48, 72,
-CUBIC_TO, 43.96f, 72, 39.72f, 71.12f, 36.2f, 69.6f,
-CUBIC_TO, 37.2f, 66.36f, 42.84f, 64, 48, 64,
-CUBIC_TO, 49.32f, 64, 50.68f, 64.16f, 52, 64.48f,
-CUBIC_TO, 56.28f, 65.4f, 60, 67.76f, 60, 70.68f,
-LINE_TO, 60, 77.56f,
-CUBIC_TO, 64.64f, 75.52f, 68, 72.44f, 68, 68.88f,
-CLOSE,
-MOVE_TO, 49.25f, 60.5f,
-CUBIC_TO, 45.8f, 60.5f, 43, 57.7f, 43, 54.25f,
-CUBIC_TO, 43, 50.8f, 45.8f, 48, 49.25f, 48,
-CUBIC_TO, 52.7f, 48, 55.5f, 50.8f, 55.5f, 54.25f,
-CUBIC_TO, 55.5f, 57.7f, 52.7f, 60.5f, 49.25f, 60.5f,
-CLOSE
diff --git a/ash/shelf/login_shelf_view.cc b/ash/shelf/login_shelf_view.cc
index 3f6df540..f36f517 100644
--- a/ash/shelf/login_shelf_view.cc
+++ b/ash/shelf/login_shelf_view.cc
@@ -48,7 +48,7 @@
 #include "ui/gfx/image/image_skia_operations.h"
 #include "ui/gfx/paint_vector_icon.h"
 #include "ui/gfx/vector_icon_types.h"
-#include "ui/views/accessibility/ax_aura_obj_cache.h"
+#include "ui/views/accessibility/view_accessibility.h"
 #include "ui/views/animation/ink_drop_impl.h"
 #include "ui/views/animation/ink_drop_mask.h"
 #include "ui/views/controls/button/label_button.h"
@@ -456,16 +456,12 @@
 
 void LoginShelfView::GetAccessibleNodeData(ui::AXNodeData* node_data) {
   if (LockScreen::HasInstance()) {
-    int previous_id = views::AXAuraObjCache::GetInstance()->GetID(
-        LockScreen::Get()->widget());
-    node_data->AddIntAttribute(ax::mojom::IntAttribute::kPreviousFocusId,
-                               previous_id);
+    GetViewAccessibility().OverridePreviousFocus(LockScreen::Get()->widget());
   }
 
   Shelf* shelf = Shelf::ForWindow(GetWidget()->GetNativeWindow());
-  int next_id =
-      views::AXAuraObjCache::GetInstance()->GetID(shelf->GetStatusAreaWidget());
-  node_data->AddIntAttribute(ax::mojom::IntAttribute::kNextFocusId, next_id);
+
+  GetViewAccessibility().OverrideNextFocus(shelf->GetStatusAreaWidget());
   node_data->role = ax::mojom::Role::kToolbar;
   node_data->SetName(l10n_util::GetStringUTF8(IDS_ASH_SHELF_ACCESSIBLE_NAME));
 }
diff --git a/ash/shelf/shelf_constants.h b/ash/shelf/shelf_constants.h
index f5ddf3fb..7bda858 100644
--- a/ash/shelf/shelf_constants.h
+++ b/ash/shelf/shelf_constants.h
@@ -67,7 +67,7 @@
 
 // The alpha value for the shelf background.
 ASH_EXPORT constexpr int kShelfTranslucentOverAppList = 51;            // 20%
-ASH_EXPORT constexpr int kShelfTranslucentAlpha = 153;                 // 60%
+ASH_EXPORT constexpr int kShelfTranslucentAlpha = 189;                 // 74%
 // Using 0xFF causes clipping on the overlay candidate content, which prevent
 // HW overlay, probably due to a bug in compositor. Fix it and use 0xFF.
 // crbug.com/901538
diff --git a/ash/system/message_center/message_center_controller.cc b/ash/system/message_center/message_center_controller.cc
index da282560..c380a89 100644
--- a/ash/system/message_center/message_center_controller.cc
+++ b/ash/system/message_center/message_center_controller.cc
@@ -20,6 +20,7 @@
 #include "components/account_id/account_id.h"
 #include "components/pref_registry/pref_registry_syncable.h"
 #include "components/prefs/pref_registry_simple.h"
+#include "components/vector_icons/vector_icons.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/message_center/message_center.h"
 #include "ui/message_center/public/cpp/notification.h"
@@ -126,8 +127,8 @@
         std::make_unique<PopupNotificationBlocker>(MessageCenter::Get());
   }
 
-  message_center::RegisterVectorIcons({&kNotificationAssistantIcon,
-                                       &kAutoLaunchManagedGuestSessionIcon,
+  message_center::RegisterVectorIcons({&vector_icons::kBusinessIcon,
+                                       &kNotificationAssistantIcon,
                                        &kNotificationCaptivePortalIcon,
                                        &kNotificationCellularAlertIcon,
                                        &kNotificationDownloadIcon,
diff --git a/ash/system/tray/tray_background_view.cc b/ash/system/tray/tray_background_view.cc
index 081039f..ae121e9 100644
--- a/ash/system/tray/tray_background_view.cc
+++ b/ash/system/tray/tray_background_view.cc
@@ -35,7 +35,7 @@
 #include "ui/gfx/geometry/size.h"
 #include "ui/gfx/scoped_canvas.h"
 #include "ui/gfx/transform.h"
-#include "ui/views/accessibility/ax_aura_obj_cache.h"
+#include "ui/views/accessibility/view_accessibility.h"
 #include "ui/views/animation/flood_fill_ink_drop_ripple.h"
 #include "ui/views/animation/ink_drop_highlight.h"
 #include "ui/views/animation/ink_drop_mask.h"
@@ -308,16 +308,12 @@
   node_data->SetName(GetAccessibleNameForTray());
 
   if (LockScreen::HasInstance()) {
-    int next_id = views::AXAuraObjCache::GetInstance()->GetID(
-        LockScreen::Get()->widget());
-    node_data->AddIntAttribute(ax::mojom::IntAttribute::kNextFocusId, next_id);
+    GetViewAccessibility().OverrideNextFocus(LockScreen::Get()->widget());
   }
 
   Shelf* shelf = Shelf::ForWindow(GetWidget()->GetNativeWindow());
   ShelfWidget* shelf_widget = shelf->shelf_widget();
-  int previous_id = views::AXAuraObjCache::GetInstance()->GetID(shelf_widget);
-  node_data->AddIntAttribute(ax::mojom::IntAttribute::kPreviousFocusId,
-                             previous_id);
+  GetViewAccessibility().OverridePreviousFocus(shelf_widget);
 }
 
 void TrayBackgroundView::ChildPreferredSizeChanged(views::View* child) {
diff --git a/ash/ws/ax_ash_window_utils_unittest.cc b/ash/ws/ax_ash_window_utils_unittest.cc
index 35297dc..57bcfc1d 100644
--- a/ash/ws/ax_ash_window_utils_unittest.cc
+++ b/ash/ws/ax_ash_window_utils_unittest.cc
@@ -152,11 +152,11 @@
 // serialization code.
 TEST_F(AXAshWindowUtilsTest, SerializeNodeTree) {
   // Build a desktop tree serializer similar to AutomationManagerAura.
-  AXAuraObjCache* cache = AXAuraObjCache::GetInstance();
-  cache->OnRootWindowObjCreated(Shell::GetPrimaryRootWindow());
-  AXRootObjWrapper root_wrapper(nullptr /* delegate */);
+  AXAuraObjCache cache;
+  cache.OnRootWindowObjCreated(Shell::GetPrimaryRootWindow());
+  AXRootObjWrapper root_wrapper(nullptr /* delegate */, &cache);
   AXTreeSourceViews ax_tree_source(&root_wrapper,
-                                   ui::AXTreeID::CreateNewAXTreeID());
+                                   ui::AXTreeID::CreateNewAXTreeID(), &cache);
   AuraAXTreeSerializer ax_serializer(&ax_tree_source);
 
   // Initial tree is valid.
@@ -177,8 +177,7 @@
 
   // Serialize walking up from the textfield.
   ui::AXTreeUpdate label_update;
-  AXAuraObjWrapper* wrapper =
-      AXAuraObjCache::GetInstance()->GetOrCreate(textfield_);
+  AXAuraObjWrapper* wrapper = cache.GetOrCreate(textfield_);
   ASSERT_TRUE(ax_serializer.SerializeChanges(wrapper, &label_update));
   ASSERT_TRUE(ax_tree.Unserialize(label_update));
 
@@ -192,8 +191,8 @@
   // AXAuraObjCache can reach into a client Widget to find a focused view.
   textfield_->RequestFocus();
   EXPECT_TRUE(textfield_->HasFocus());
-  AXAuraObjWrapper* textfield_wrapper = cache->GetOrCreate(textfield_);
-  EXPECT_EQ(textfield_wrapper, cache->GetFocus());
+  AXAuraObjWrapper* textfield_wrapper = cache.GetOrCreate(textfield_);
+  EXPECT_EQ(textfield_wrapper, cache.GetFocus());
 }
 
 TEST_F(AXAshWindowUtilsTest, IsRootWindow) {
diff --git a/base/BUILD.gn b/base/BUILD.gn
index 215417e..fadacd7 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -804,6 +804,7 @@
     "task/task_features.cc",
     "task/task_features.h",
     "task/task_observer.h",
+    "task/task_scheduler/can_schedule_sequence_observer.h",
     "task/task_scheduler/delayed_task_manager.cc",
     "task/task_scheduler/delayed_task_manager.h",
     "task/task_scheduler/environment_config.cc",
@@ -2580,7 +2581,6 @@
     "task/sequence_manager/work_deduplicator_unittest.cc",
     "task/sequence_manager/work_queue_sets_unittest.cc",
     "task/sequence_manager/work_queue_unittest.cc",
-    "task/task_scheduler/can_run_policy_test.h",
     "task/task_scheduler/delayed_task_manager_unittest.cc",
     "task/task_scheduler/priority_queue_unittest.cc",
     "task/task_scheduler/scheduler_lock_unittest.cc",
diff --git a/base/task/task_scheduler/can_run_policy_test.h b/base/task/task_scheduler/can_run_policy_test.h
deleted file mode 100644
index 1a7f508..0000000
--- a/base/task/task_scheduler/can_run_policy_test.h
+++ /dev/null
@@ -1,189 +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 BASE_TASK_TASK_SCHEDULER_CAN_RUN_POLICY_TEST_H_
-#define BASE_TASK_TASK_SCHEDULER_CAN_RUN_POLICY_TEST_H_
-
-#include "base/synchronization/atomic_flag.h"
-#include "base/synchronization/waitable_event.h"
-#include "base/task/task_scheduler/task_tracker.h"
-#include "base/task/task_scheduler/test_utils.h"
-#include "base/task_runner.h"
-#include "base/test/bind_test_util.h"
-#include "base/test/test_timeouts.h"
-#include "base/threading/platform_thread.h"
-
-namespace base {
-namespace internal {
-namespace test {
-
-// Verify that tasks only run when allowed by the CanRunPolicy. |target| is the
-// object on which DidUpdateCanRunPolicy() must be called after updating the
-// CanRunPolicy in |task_tracker|. |create_task_runner| is a function that
-// receives a TaskPriority and returns a TaskRunner. |task_tracker| is the
-// TaskTracker.
-template <typename Target, typename CreateTaskRunner>
-void TestCanRunPolicyBasic(Target* target,
-                           CreateTaskRunner create_task_runner,
-                           TaskTracker* task_tracker) {
-  AtomicFlag foreground_can_run;
-  WaitableEvent foreground_did_run;
-  AtomicFlag best_effort_can_run;
-  WaitableEvent best_effort_did_run;
-
-  task_tracker->SetCanRunPolicy(CanRunPolicy::kNone);
-  target->DidUpdateCanRunPolicy();
-
-  const auto user_visible_task_runner =
-      create_task_runner(TaskPriority::USER_VISIBLE);
-  user_visible_task_runner->PostTask(FROM_HERE, BindLambdaForTesting([&]() {
-                                       EXPECT_TRUE(foreground_can_run.IsSet());
-                                       foreground_did_run.Signal();
-                                     }));
-  const auto best_effort_task_runner =
-      create_task_runner(TaskPriority::BEST_EFFORT);
-  best_effort_task_runner->PostTask(FROM_HERE, BindLambdaForTesting([&]() {
-                                      EXPECT_TRUE(best_effort_can_run.IsSet());
-                                      best_effort_did_run.Signal();
-                                    }));
-
-  PlatformThread::Sleep(TestTimeouts::tiny_timeout());
-
-  foreground_can_run.Set();
-  task_tracker->SetCanRunPolicy(CanRunPolicy::kForegroundOnly);
-  target->DidUpdateCanRunPolicy();
-  foreground_did_run.Wait();
-
-  PlatformThread::Sleep(TestTimeouts::tiny_timeout());
-
-  best_effort_can_run.Set();
-  task_tracker->SetCanRunPolicy(CanRunPolicy::kAll);
-  target->DidUpdateCanRunPolicy();
-  best_effort_did_run.Wait();
-}
-
-// Verify that if a task was allowed to run by the CanRunPolicy when it was
-// posted, but the CanRunPolicy is updated to disallow it from running before it
-// starts running, it doesn't run. |target| is the object on which
-// DidUpdateCanRunPolicy() must be called after updating the CanRunPolicy in
-// |task_tracker|. |create_task_runner| is a function that receives a
-// TaskPriority and returns a *Sequenced*TaskRunner. |task_tracker| is the
-// TaskTracker.
-template <typename Target, typename CreateTaskRunner>
-void TestCanRunPolicyChangedBeforeRun(Target* target,
-                                      CreateTaskRunner create_task_runner,
-                                      TaskTracker* task_tracker) {
-  constexpr struct {
-    // Descriptor for the test case.
-    const char* descriptor;
-    // Task priority being tested.
-    TaskPriority priority;
-    // Policy that disallows running tasks with |priority|.
-    CanRunPolicy disallow_policy;
-    // Policy that allows running tasks with |priority|.
-    CanRunPolicy allow_policy;
-  } kTestCases[] = {
-      {"BestEffort/kNone/kAll", TaskPriority::BEST_EFFORT, CanRunPolicy::kNone,
-       CanRunPolicy::kAll},
-      {"BestEffort/kForegroundOnly/kAll", TaskPriority::BEST_EFFORT,
-       CanRunPolicy::kForegroundOnly, CanRunPolicy::kAll},
-      {"UserVisible/kNone/kForegroundOnly", TaskPriority::USER_VISIBLE,
-       CanRunPolicy::kNone, CanRunPolicy::kForegroundOnly},
-      {"UserVisible/kNone/kAll", TaskPriority::USER_VISIBLE,
-       CanRunPolicy::kNone, CanRunPolicy::kAll}};
-
-  for (auto& test_case : kTestCases) {
-    SCOPED_TRACE(test_case.descriptor);
-
-    WaitableEvent first_task_started;
-    WaitableEvent first_task_blocked;
-    AtomicFlag second_task_can_run;
-
-    task_tracker->SetCanRunPolicy(test_case.allow_policy);
-    target->DidUpdateCanRunPolicy();
-
-    const auto task_runner = create_task_runner(test_case.priority);
-    task_runner->PostTask(
-        FROM_HERE, BindLambdaForTesting([&]() {
-          first_task_started.Signal();
-          test::WaitWithoutBlockingObserver(&first_task_blocked);
-        }));
-    task_runner->PostTask(FROM_HERE, BindLambdaForTesting([&]() {
-                            EXPECT_TRUE(second_task_can_run.IsSet());
-                          }));
-
-    first_task_started.Wait();
-    task_tracker->SetCanRunPolicy(test_case.disallow_policy);
-    target->DidUpdateCanRunPolicy();
-    first_task_blocked.Signal();
-
-    PlatformThread::Sleep(TestTimeouts::tiny_timeout());
-
-    second_task_can_run.Set();
-    task_tracker->SetCanRunPolicy(test_case.allow_policy);
-    target->DidUpdateCanRunPolicy();
-    task_tracker->FlushForTesting();
-  }
-}
-
-// Regression test for https://crbug.com/950383
-template <typename Target, typename CreateTaskRunner>
-void TestCanRunPolicyLoad(Target* target,
-                          CreateTaskRunner create_task_runner,
-                          TaskTracker* task_tracker) {
-  constexpr struct {
-    // Descriptor for the test case.
-    const char* descriptor;
-    // Task priority being tested.
-    TaskPriority priority;
-    // Policy that allows running tasks with |priority|.
-    CanRunPolicy allow_policy;
-    // Policy that disallows running tasks with |priority|.
-    CanRunPolicy disallow_policy;
-  } kTestCases[] = {
-      {"BestEffort/kAll/kNone", TaskPriority::BEST_EFFORT, CanRunPolicy::kAll,
-       CanRunPolicy::kNone},
-      {"BestEffort/kAll/kForegroundOnly", TaskPriority::BEST_EFFORT,
-       CanRunPolicy::kAll, CanRunPolicy::kForegroundOnly},
-      {"UserVisible/kForegroundOnly/kNone", TaskPriority::USER_VISIBLE,
-       CanRunPolicy::kForegroundOnly, CanRunPolicy::kNone},
-      {"UserVisible/kAll/kNone", TaskPriority::USER_VISIBLE, CanRunPolicy::kAll,
-       CanRunPolicy::kNone}};
-
-  for (auto& test_case : kTestCases) {
-    SCOPED_TRACE(test_case.descriptor);
-
-    task_tracker->SetCanRunPolicy(test_case.allow_policy);
-    target->DidUpdateCanRunPolicy();
-
-    const auto task_runner = create_task_runner(test_case.priority);
-     
-    // Post less tasks on iOS to avoid timeouts.
-    const size_t kLargeNumber =
-#if defined(OS_IOS)
-        16;
-#else
-        256;
-#endif
-    for (size_t i = 0; i < kLargeNumber; ++i)
-      task_runner->PostTask(FROM_HERE, DoNothing());
-
-    // Change the CanRunPolicy concurrently with running tasks.
-    // This should not cause crashes.
-    task_tracker->SetCanRunPolicy(test_case.disallow_policy);
-    target->DidUpdateCanRunPolicy();
-
-    PlatformThread::Sleep(TestTimeouts::tiny_timeout());
-
-    task_tracker->SetCanRunPolicy(test_case.allow_policy);
-    target->DidUpdateCanRunPolicy();
-    task_tracker->FlushForTesting();
-  }
-}
-
-}  // namespace test
-}  // namespace internal
-}  // namespace base
-
-#endif  // BASE_TASK_TASK_SCHEDULER_CAN_RUN_POLICY_TEST_H_
diff --git a/base/task/task_scheduler/can_schedule_sequence_observer.h b/base/task/task_scheduler/can_schedule_sequence_observer.h
new file mode 100644
index 0000000..50284a1
--- /dev/null
+++ b/base/task/task_scheduler/can_schedule_sequence_observer.h
@@ -0,0 +1,27 @@
+// 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 BASE_TASK_TASK_SCHEDULER_CAN_SCHEDULE_SEQUENCE_OBSERVER_H_
+#define BASE_TASK_TASK_SCHEDULER_CAN_SCHEDULE_SEQUENCE_OBSERVER_H_
+
+#include "base/task/task_scheduler/sequence.h"
+
+namespace base {
+namespace internal {
+
+class CanScheduleSequenceObserver {
+ public:
+  // Called when |sequence| can be scheduled. It is expected that
+  // TaskTracker::RunNextTask() will be called with |sequence| as argument after
+  // this is called.
+  virtual void OnCanScheduleSequence(scoped_refptr<Sequence> sequence) = 0;
+
+ protected:
+  virtual ~CanScheduleSequenceObserver() = default;
+};
+
+}  // namespace internal
+}  // namespace base
+
+#endif  // BASE_TASK_TASK_SCHEDULER_CAN_SCHEDULE_SEQUENCE_OBSERVER_H_
diff --git a/base/task/task_scheduler/platform_native_worker_pool.cc b/base/task/task_scheduler/platform_native_worker_pool.cc
index 7c95a51..d32f278b 100644
--- a/base/task/task_scheduler/platform_native_worker_pool.cc
+++ b/base/task/task_scheduler/platform_native_worker_pool.cc
@@ -75,23 +75,24 @@
 }
 
 void PlatformNativeWorkerPool::RunNextSequenceImpl() {
+  BindToCurrentThread();
+
   scoped_refptr<Sequence> sequence = GetWork();
+  DCHECK(sequence);
+
+  sequence = task_tracker_->RunAndPopNextTask(std::move(sequence), this);
 
   if (sequence) {
-    BindToCurrentThread();
-    sequence = task_tracker_->RunAndPopNextTask(std::move(sequence));
-    UnbindFromCurrentThread();
-
-    if (sequence) {
-      ScopedWorkersExecutor workers_executor(this);
-      ScopedReenqueueExecutor reenqueue_executor;
-      auto sequence_and_transaction =
-          SequenceAndTransaction::FromSequence(std::move(sequence));
-      AutoSchedulerLock auto_lock(lock_);
-      ReEnqueueSequenceLockRequired(&workers_executor, &reenqueue_executor,
-                                    std::move(sequence_and_transaction));
-    }
+    ScopedWorkersExecutor workers_executor(this);
+    ScopedReenqueueExecutor reenqueue_executor;
+    auto sequence_and_transaction =
+        SequenceAndTransaction::FromSequence(std::move(sequence));
+    AutoSchedulerLock auto_lock(lock_);
+    ReEnqueueSequenceLockRequired(&workers_executor, &reenqueue_executor,
+                                  std::move(sequence_and_transaction));
   }
+
+  UnbindFromCurrentThread();
 }
 
 scoped_refptr<Sequence> PlatformNativeWorkerPool::GetWork() {
@@ -102,11 +103,6 @@
   // PriorityQueue after RemoveSequence().
   if (priority_queue_.IsEmpty())
     return nullptr;
-
-  // Enforce the CanRunPolicy.
-  const TaskPriority priority = priority_queue_.PeekSortKey().priority();
-  if (!task_tracker_->CanRunPriority(priority))
-    return nullptr;
   return priority_queue_.PopSequence();
 }
 
@@ -129,10 +125,7 @@
     return;
   // Ensure that there is at least one pending threadpool work per Sequence in
   // the PriorityQueue.
-  const size_t desired_num_pending_threadpool_work =
-      GetNumQueuedCanRunBestEffortSequences() +
-      GetNumQueuedCanRunForegroundSequences();
-
+  const size_t desired_num_pending_threadpool_work = priority_queue_.Size();
   if (desired_num_pending_threadpool_work > num_pending_threadpool_work_) {
     static_cast<ScopedWorkersExecutor*>(executor)
         ->set_num_threadpool_work_to_submit(
@@ -156,11 +149,5 @@
   // number of worker threads created.
 }
 
-void PlatformNativeWorkerPool::DidUpdateCanRunPolicy() {
-  ScopedWorkersExecutor executor(this);
-  AutoSchedulerLock auto_lock(lock_);
-  EnsureEnoughWorkersLockRequired(&executor);
-}
-
 }  // namespace internal
 }  // namespace base
diff --git a/base/task/task_scheduler/platform_native_worker_pool.h b/base/task/task_scheduler/platform_native_worker_pool.h
index f82b132..79e680a4 100644
--- a/base/task/task_scheduler/platform_native_worker_pool.h
+++ b/base/task/task_scheduler/platform_native_worker_pool.h
@@ -26,7 +26,6 @@
   void JoinForTesting() override;
   size_t GetMaxConcurrentNonBlockedTasksDeprecated() const override;
   void ReportHeartbeatMetrics() const override;
-  void DidUpdateCanRunPolicy() override;
 
  protected:
   PlatformNativeWorkerPool(TrackedRef<TaskTracker> task_tracker,
diff --git a/base/task/task_scheduler/scheduler_single_thread_task_runner_manager.cc b/base/task/task_scheduler/scheduler_single_thread_task_runner_manager.cc
index b10a2ed..c5cb647 100644
--- a/base/task/task_scheduler/scheduler_single_thread_task_runner_manager.cc
+++ b/base/task/task_scheduler/scheduler_single_thread_task_runner_manager.cc
@@ -79,17 +79,22 @@
 class SchedulerWorkerDelegate : public SchedulerWorker::Delegate {
  public:
   SchedulerWorkerDelegate(const std::string& thread_name,
-                          SchedulerWorker::ThreadLabel thread_label,
-                          TrackedRef<TaskTracker> task_tracker)
-      : thread_name_(thread_name),
-        thread_label_(thread_label),
-        task_tracker_(std::move(task_tracker)) {}
+                          SchedulerWorker::ThreadLabel thread_label)
+      : thread_name_(thread_name), thread_label_(thread_label) {}
 
   void set_worker(SchedulerWorker* worker) {
     DCHECK(!worker_);
     worker_ = worker;
   }
 
+  // SchedulerWorker::Delegate:
+  void OnCanScheduleSequence(scoped_refptr<Sequence> sequence) override {
+    DCHECK(worker_);
+    ReEnqueueSequence(
+        SequenceAndTransaction::FromSequence(std::move(sequence)));
+    worker_->WakeUp();
+  }
+
   SchedulerWorker::ThreadLabel GetThreadLabel() const final {
     return thread_label_;
   }
@@ -101,35 +106,26 @@
 
   scoped_refptr<Sequence> GetWork(SchedulerWorker* worker) override {
     AutoSchedulerLock auto_lock(lock_);
-    auto sequence = GetWorkLockRequired(worker);
-    if (sequence == nullptr) {
-      // The worker will sleep after this returns nullptr.
-      worker_awake_ = false;
-    }
-    return sequence;
+    return priority_queue_.IsEmpty() ? nullptr : priority_queue_.PopSequence();
   }
 
   void DidRunTask(scoped_refptr<Sequence> sequence) override {
     if (sequence) {
-      EnqueueSequence(
+      ReEnqueueSequence(
           SequenceAndTransaction::FromSequence(std::move(sequence)));
     }
   }
 
-  TimeDelta GetSleepTimeout() override { return TimeDelta::Max(); }
-
-  void PostTaskNow(scoped_refptr<Sequence> sequence, Task task) {
-    auto sequence_and_transaction =
-        SequenceAndTransaction::FromSequence(std::move(sequence));
-    const bool task_source_should_be_queued =
-        sequence_and_transaction.transaction.PushTask(std::move(task));
-    if (task_source_should_be_queued) {
-      bool should_wakeup = EnqueueSequence(std::move(sequence_and_transaction));
-      if (should_wakeup)
-        worker_->WakeUp();
-    }
+  void ReEnqueueSequence(SequenceAndTransaction sequence_and_transaction) {
+    const SequenceSortKey sequence_sort_key =
+        sequence_and_transaction.transaction.GetSortKey();
+    AutoSchedulerLock auto_lock(lock_);
+    priority_queue_.Push(std::move(sequence_and_transaction.sequence),
+                         sequence_sort_key);
   }
 
+  TimeDelta GetSleepTimeout() override { return TimeDelta::Max(); }
+
   bool RunsTasksInCurrentSequence() {
     // We check the thread ref instead of the sequence for the benefit of COM
     // callbacks which may execute without a sequence context.
@@ -138,69 +134,22 @@
 
   void OnMainExit(SchedulerWorker* /* worker */) override {}
 
-  void DidUpdateCanRunPolicy() {
-    bool should_wakeup = false;
-    {
-      AutoSchedulerLock auto_lock(lock_);
-      if (!worker_awake_ && CanRunNextSequence()) {
-        should_wakeup = true;
-        worker_awake_ = true;
-      }
-    }
-    if (should_wakeup)
-      worker_->WakeUp();
-  }
-
   void EnableFlushPriorityQueueSequencesOnDestroyForTesting() {
     AutoSchedulerLock auto_lock(lock_);
     priority_queue_.EnableFlushSequencesOnDestroyForTesting();
   }
 
- protected:
-  scoped_refptr<Sequence> GetWorkLockRequired(SchedulerWorker* worker)
-      EXCLUSIVE_LOCKS_REQUIRED(lock_) {
-    if (!CanRunNextSequence()) {
-      return nullptr;
-    }
-    return priority_queue_.PopSequence();
-  }
-
-  const TrackedRef<TaskTracker>& task_tracker() { return task_tracker_; }
-
-  SchedulerLock lock_;
-  bool worker_awake_ GUARDED_BY(lock_) = false;
-
  private:
-  // Enqueues a sequence in this single-threaded worker's priority queue.
-  // Returns true iff the worker must wakeup, i.e. sequence is allowed to run
-  // and the worker was not awake.
-  bool EnqueueSequence(SequenceAndTransaction sequence_and_transaction) {
-    AutoSchedulerLock auto_lock(lock_);
-    priority_queue_.Push(std::move(sequence_and_transaction.sequence),
-                         sequence_and_transaction.transaction.GetSortKey());
-    if (!worker_awake_ && CanRunNextSequence()) {
-      worker_awake_ = true;
-      return true;
-    }
-    return false;
-  }
-
-  bool CanRunNextSequence() EXCLUSIVE_LOCKS_REQUIRED(lock_) {
-    return !priority_queue_.IsEmpty() &&
-           task_tracker_->CanRunPriority(
-               priority_queue_.PeekSortKey().priority());
-  }
-
   const std::string thread_name_;
   const SchedulerWorker::ThreadLabel thread_label_;
 
   // The SchedulerWorker that has |this| as a delegate. Must be set before
   // starting or posting a task to the SchedulerWorker, because it's used in
-  // OnMainEntry() and PostTaskNow().
+  // OnMainEntry() and OnCanScheduleSequence() (called when a sequence held up
+  // by WillScheduleSequence() in PostTaskNow() can be scheduled).
   SchedulerWorker* worker_ = nullptr;
 
-  const TrackedRef<TaskTracker> task_tracker_;
-
+  SchedulerLock lock_;
   PriorityQueue priority_queue_ GUARDED_BY(lock_);
 
   AtomicThreadRefChecker thread_ref_checker_;
@@ -215,9 +164,8 @@
   SchedulerWorkerCOMDelegate(const std::string& thread_name,
                              SchedulerWorker::ThreadLabel thread_label,
                              TrackedRef<TaskTracker> task_tracker)
-      : SchedulerWorkerDelegate(thread_name,
-                                thread_label,
-                                std::move(task_tracker)) {}
+      : SchedulerWorkerDelegate(thread_name, thread_label),
+        task_tracker_(std::move(task_tracker)) {}
 
   ~SchedulerWorkerCOMDelegate() override { DCHECK(!scoped_com_initializer_); }
 
@@ -237,16 +185,14 @@
     // * Both SchedulerWorkerDelegate::GetWork() and the Windows Message Queue
     //   have work:
     //   Process sequences from each source round-robin style.
-    AutoSchedulerLock auto_lock(lock_);
     scoped_refptr<Sequence> sequence;
     if (get_work_first_) {
-      sequence = SchedulerWorkerDelegate::GetWorkLockRequired(worker);
+      sequence = SchedulerWorkerDelegate::GetWork(worker);
       if (sequence)
         get_work_first_ = false;
     }
 
     if (!sequence) {
-      AutoSchedulerUnlock auto_unlock(lock_);
       sequence = GetWorkFromWindowsMessageQueue();
       if (sequence)
         get_work_first_ = true;
@@ -257,11 +203,7 @@
       // and found there was no work. We don't want to return null immediately
       // as that could cause the thread to go to sleep while work is waiting via
       // SchedulerWorkerDelegate::GetWork().
-      sequence = SchedulerWorkerDelegate::GetWorkLockRequired(worker);
-    }
-    if (sequence == nullptr) {
-      // The worker will sleep after this returns nullptr.
-      worker_awake_ = false;
+      sequence = SchedulerWorkerDelegate::GetWork(worker);
     }
     return sequence;
   }
@@ -276,12 +218,8 @@
     const DWORD milliseconds_wait = checked_cast<DWORD>(
         sleep_time.is_max() ? INFINITE : sleep_time.InMilliseconds());
     const HANDLE wake_up_event_handle = wake_up_event->handle();
-    DWORD reason = MsgWaitForMultipleObjectsEx(
-        1, &wake_up_event_handle, milliseconds_wait, QS_ALLINPUT, 0);
-    if (reason != WAIT_OBJECT_0) {
-      AutoSchedulerLock auto_lock(lock_);
-      worker_awake_ = true;
-    }
+    MsgWaitForMultipleObjectsEx(1, &wake_up_event_handle, milliseconds_wait,
+                                QS_ALLINPUT, 0);
   }
 
  private:
@@ -296,8 +234,8 @@
                                  },
                                  std::move(msg)),
                              TimeDelta());
-      if (task_tracker()->WillPostTask(
-              &pump_message_task, TaskShutdownBehavior::SKIP_ON_SHUTDOWN)) {
+      if (task_tracker_->WillPostTask(&pump_message_task,
+                                      TaskShutdownBehavior::SKIP_ON_SHUTDOWN)) {
         bool was_empty = message_pump_sequence_->BeginTransaction().PushTask(
             std::move(pump_message_task));
         DCHECK(was_empty) << "GetWorkFromWindowsMessageQueue() does not expect "
@@ -313,6 +251,7 @@
       MakeRefCounted<Sequence>(TaskTraits(MayBlock()),
                                nullptr,
                                TaskSourceExecutionMode::kParallel);
+  const TrackedRef<TaskTracker> task_tracker_;
   std::unique_ptr<win::ScopedCOMInitializer> scoped_com_initializer_;
 
   DISALLOW_COPY_AND_ASSIGN(SchedulerWorkerCOMDelegate);
@@ -358,15 +297,11 @@
     }
 
     if (task.delayed_run_time.is_null()) {
-      GetDelegate()->PostTaskNow(sequence_, std::move(task));
+      PostTaskNow(std::move(task));
     } else {
-      // Unretained(GetDelegate()) is safe because this TaskRunner and its
-      // worker are kept alive as long as there are pending Tasks.
       outer_->delayed_task_manager_->AddDelayedTask(
           std::move(task),
-          BindOnce(&SchedulerWorkerDelegate::PostTaskNow,
-                   Unretained(GetDelegate()), sequence_),
-          this);
+          BindOnce(&SchedulerSingleThreadTaskRunner::PostTaskNow, this), this);
     }
     return true;
   }
@@ -410,6 +345,20 @@
     }
   }
 
+  void PostTaskNow(Task task) {
+    auto sequence_and_transaction =
+        SequenceAndTransaction::FromSequence(sequence_);
+    const bool task_source_should_be_queued =
+        sequence_and_transaction.transaction.PushTask(std::move(task));
+    if (task_source_should_be_queued) {
+      if (outer_->task_tracker_->WillScheduleSequence(
+              sequence_and_transaction.transaction, GetDelegate())) {
+        GetDelegate()->ReEnqueueSequence(std::move(sequence_and_transaction));
+        worker_->WakeUp();
+      }
+    }
+  }
+
   SchedulerWorkerDelegate* GetDelegate() const {
     return static_cast<SchedulerWorkerDelegate*>(worker_->delegate());
   }
@@ -469,27 +418,8 @@
   // SchedulerSingleThreadTaskRunner::PostTaskNow(). As a result, it's
   // unnecessary to call WakeUp() for each worker (in fact, an extraneous
   // WakeUp() would be racy and wrong - see https://crbug.com/862582).
-  for (scoped_refptr<SchedulerWorker> worker : workers_to_start) {
+  for (scoped_refptr<SchedulerWorker> worker : workers_to_start)
     worker->Start(scheduler_worker_observer_);
-  }
-}
-
-void SchedulerSingleThreadTaskRunnerManager::DidUpdateCanRunPolicy() {
-  decltype(workers_) workers_to_update;
-
-  {
-    AutoSchedulerLock auto_lock(lock_);
-    if (!started_)
-      return;
-    workers_to_update = workers_;
-  }
-  // Any worker created after the lock is released will see the latest
-  // CanRunPolicy if tasks are posted to it and thus doesn't need a
-  // DidUpdateCanRunPolicy() notification.
-  for (auto& worker : workers_to_update) {
-    static_cast<SchedulerWorkerDelegate*>(worker->delegate())
-        ->DidUpdateCanRunPolicy();
-  }
 }
 
 scoped_refptr<SingleThreadTaskRunner>
@@ -605,8 +535,7 @@
       StringPrintf("TaskSchedulerSingleThread%s%d", name.c_str(), id),
       thread_mode == SingleThreadTaskRunnerThreadMode::DEDICATED
           ? SchedulerWorker::ThreadLabel::DEDICATED
-          : SchedulerWorker::ThreadLabel::SHARED,
-      task_tracker_);
+          : SchedulerWorker::ThreadLabel::SHARED);
 }
 
 #if defined(OS_WIN)
diff --git a/base/task/task_scheduler/scheduler_single_thread_task_runner_manager.h b/base/task/task_scheduler/scheduler_single_thread_task_runner_manager.h
index 9d33d3d..220dde2ce 100644
--- a/base/task/task_scheduler/scheduler_single_thread_task_runner_manager.h
+++ b/base/task/task_scheduler/scheduler_single_thread_task_runner_manager.h
@@ -63,10 +63,6 @@
   // JoinForTesting() has returned (must never be destroyed in production).
   void Start(SchedulerWorkerObserver* scheduler_worker_observer = nullptr);
 
-  // Wakes up workers as appropriate for the new CanRunPolicy policy. Must be
-  // called after an update to CanRunPolicy in TaskTracker.
-  void DidUpdateCanRunPolicy();
-
   // Creates a SingleThreadTaskRunner which runs tasks with |traits| on a thread
   // named "TaskSchedulerSingleThread[Shared]" +
   // kEnvironmentParams[GetEnvironmentIndexForTraits(traits)].name_suffix +
diff --git a/base/task/task_scheduler/scheduler_single_thread_task_runner_manager_unittest.cc b/base/task/task_scheduler/scheduler_single_thread_task_runner_manager_unittest.cc
index 4b77df6..f33be2fb 100644
--- a/base/task/task_scheduler/scheduler_single_thread_task_runner_manager_unittest.cc
+++ b/base/task/task_scheduler/scheduler_single_thread_task_runner_manager_unittest.cc
@@ -11,14 +11,11 @@
 #include "base/synchronization/lock.h"
 #include "base/synchronization/waitable_event.h"
 #include "base/task/post_task.h"
-#include "base/task/task_scheduler/can_run_policy_test.h"
 #include "base/task/task_scheduler/delayed_task_manager.h"
 #include "base/task/task_scheduler/environment_config.h"
 #include "base/task/task_scheduler/scheduler_worker_pool_params.h"
 #include "base/task/task_scheduler/task_tracker.h"
-#include "base/task/task_scheduler/test_utils.h"
 #include "base/task/task_traits.h"
-#include "base/test/bind_test_util.h"
 #include "base/test/gtest_util.h"
 #include "base/test/test_timeouts.h"
 #include "base/threading/platform_thread.h"
@@ -41,7 +38,7 @@
 namespace {
 
 class TaskSchedulerSingleThreadTaskRunnerManagerTest : public testing::Test {
- protected:
+ public:
   TaskSchedulerSingleThreadTaskRunnerManagerTest()
       : service_thread_("TaskSchedulerServiceThread") {}
 
@@ -60,6 +57,7 @@
     service_thread_.Stop();
   }
 
+ protected:
   virtual void StartSingleThreadTaskRunnerManagerFromSetUp() {
     single_thread_task_runner_manager_->Start();
   }
@@ -118,7 +116,7 @@
   task_runner_2->PostTask(FROM_HERE,
                           BindOnce(&CaptureThreadRef, &thread_ref_2));
 
-  test::ShutdownTaskTracker(&task_tracker_);
+  task_tracker_.Shutdown();
 
   ASSERT_FALSE(thread_ref_1.is_null());
   ASSERT_FALSE(thread_ref_2.is_null());
@@ -144,7 +142,7 @@
   task_runner_2->PostTask(FROM_HERE,
                           BindOnce(&CaptureThreadRef, &thread_ref_2));
 
-  test::ShutdownTaskTracker(&task_tracker_);
+  task_tracker_.Shutdown();
 
   ASSERT_FALSE(thread_ref_1.is_null());
   ASSERT_FALSE(thread_ref_2.is_null());
@@ -187,7 +185,7 @@
           },
           task_runner_1, task_runner_2));
 
-  test::ShutdownTaskTracker(&task_tracker_);
+  task_tracker_.Shutdown();
 }
 
 TEST_F(TaskSchedulerSingleThreadTaskRunnerManagerTest,
@@ -232,7 +230,7 @@
       ->PostTask(FROM_HERE, DoNothing());
 
   // Shutdown should not hang even though the first task hasn't finished.
-  test::ShutdownTaskTracker(&task_tracker_);
+  task_tracker_.Shutdown();
 
   // Let the first task finish.
   task_can_continue.Signal();
@@ -250,12 +248,6 @@
  public:
   TaskSchedulerSingleThreadTaskRunnerManagerCommonTest() = default;
 
-  scoped_refptr<SingleThreadTaskRunner> CreateTaskRunner(
-      TaskTraits traits = TaskTraits()) {
-    return single_thread_task_runner_manager_
-        ->CreateSingleThreadTaskRunnerWithTraits(traits, GetParam());
-  }
-
  private:
   DISALLOW_COPY_AND_ASSIGN(
       TaskSchedulerSingleThreadTaskRunnerManagerCommonTest);
@@ -269,9 +261,13 @@
   // Shutting down can cause priorities to get raised. This means we have to use
   // events to determine when a task is run.
   scoped_refptr<SingleThreadTaskRunner> task_runner_background =
-      CreateTaskRunner({TaskPriority::BEST_EFFORT});
+      single_thread_task_runner_manager_
+          ->CreateSingleThreadTaskRunnerWithTraits({TaskPriority::BEST_EFFORT},
+                                                   GetParam());
   scoped_refptr<SingleThreadTaskRunner> task_runner_normal =
-      CreateTaskRunner({TaskPriority::USER_VISIBLE});
+      single_thread_task_runner_manager_
+          ->CreateSingleThreadTaskRunnerWithTraits({TaskPriority::USER_VISIBLE},
+                                                   GetParam());
 
   ThreadPriority thread_priority_background;
   task_runner_background->PostTask(
@@ -303,7 +299,8 @@
   constexpr TaskTraits foo_traits = {TaskPriority::BEST_EFFORT,
                                      TaskShutdownBehavior::BLOCK_SHUTDOWN};
   scoped_refptr<SingleThreadTaskRunner> foo_task_runner =
-      CreateTaskRunner(foo_traits);
+      single_thread_task_runner_manager_
+          ->CreateSingleThreadTaskRunnerWithTraits(foo_traits, GetParam());
   std::string foo_captured_name;
   foo_task_runner->PostTask(FROM_HERE,
                             BindOnce(&CaptureThreadName, &foo_captured_name));
@@ -320,7 +317,7 @@
   user_blocking_task_runner->PostTask(
       FROM_HERE, BindOnce(&CaptureThreadName, &user_blocking_captured_name));
 
-  test::ShutdownTaskTracker(&task_tracker_);
+  task_tracker_.Shutdown();
 
   EXPECT_NE(std::string::npos,
             foo_captured_name.find(
@@ -343,8 +340,10 @@
 
 TEST_P(TaskSchedulerSingleThreadTaskRunnerManagerCommonTest,
        PostTaskAfterShutdown) {
-  auto task_runner = CreateTaskRunner();
-  test::ShutdownTaskTracker(&task_tracker_);
+  auto task_runner =
+      single_thread_task_runner_manager_
+          ->CreateSingleThreadTaskRunnerWithTraits(TaskTraits(), GetParam());
+  task_tracker_.Shutdown();
   EXPECT_FALSE(task_runner->PostTask(FROM_HERE, BindOnce(&ShouldNotRun)));
 }
 
@@ -354,7 +353,9 @@
 
   WaitableEvent task_ran(WaitableEvent::ResetPolicy::AUTOMATIC,
                          WaitableEvent::InitialState::NOT_SIGNALED);
-  auto task_runner = CreateTaskRunner();
+  auto task_runner =
+      single_thread_task_runner_manager_
+          ->CreateSingleThreadTaskRunnerWithTraits(TaskTraits(), GetParam());
 
   // Wait until the task runner is up and running to make sure the test below is
   // solely timing the delayed task, not bringing up a physical thread.
@@ -383,37 +384,15 @@
 // but doesn't crash.
 TEST_P(TaskSchedulerSingleThreadTaskRunnerManagerCommonTest,
        PostTaskAfterDestroy) {
-  auto task_runner = CreateTaskRunner();
+  auto task_runner =
+      single_thread_task_runner_manager_
+          ->CreateSingleThreadTaskRunnerWithTraits(TaskTraits(), GetParam());
   EXPECT_TRUE(task_runner->PostTask(FROM_HERE, DoNothing()));
-  test::ShutdownTaskTracker(&task_tracker_);
+  task_tracker_.Shutdown();
   TearDownSingleThreadTaskRunnerManager();
   EXPECT_FALSE(task_runner->PostTask(FROM_HERE, BindOnce(&ShouldNotRun)));
 }
 
-// Verify that tasks only run when allowed by the CanRunPolicy.
-TEST_P(TaskSchedulerSingleThreadTaskRunnerManagerCommonTest,
-       CanRunPolicyBasic) {
-  test::TestCanRunPolicyBasic(
-      single_thread_task_runner_manager_.get(),
-      [this](TaskPriority priority) { return CreateTaskRunner({priority}); },
-      &task_tracker_);
-}
-
-TEST_P(TaskSchedulerSingleThreadTaskRunnerManagerCommonTest,
-       CanRunPolicyUpdatedBeforeRun) {
-  test::TestCanRunPolicyChangedBeforeRun(
-      single_thread_task_runner_manager_.get(),
-      [this](TaskPriority priority) { return CreateTaskRunner({priority}); },
-      &task_tracker_);
-}
-
-TEST_P(TaskSchedulerSingleThreadTaskRunnerManagerCommonTest, CanRunPolicyLoad) {
-  test::TestCanRunPolicyLoad(
-      single_thread_task_runner_manager_.get(),
-      [this](TaskPriority priority) { return CreateTaskRunner({priority}); },
-      &task_tracker_);
-}
-
 INSTANTIATE_TEST_SUITE_P(
     AllModes,
     TaskSchedulerSingleThreadTaskRunnerManagerCommonTest,
@@ -530,7 +509,7 @@
   com_task_runner->PostTask(FROM_HERE, BindOnce(&win::AssertComApartmentType,
                                                 win::ComApartmentType::STA));
 
-  test::ShutdownTaskTracker(&task_tracker_);
+  task_tracker_.Shutdown();
 }
 
 TEST_F(TaskSchedulerSingleThreadTaskRunnerManagerTest, COMSTASameThreadUsed) {
@@ -550,7 +529,7 @@
   task_runner_2->PostTask(FROM_HERE,
                           BindOnce(&CaptureThreadRef, &thread_ref_2));
 
-  test::ShutdownTaskTracker(&task_tracker_);
+  task_tracker_.Shutdown();
 
   ASSERT_FALSE(thread_ref_1.is_null());
   ASSERT_FALSE(thread_ref_2.is_null());
@@ -628,7 +607,7 @@
   com_task_runner->PostTask(
       FROM_HERE, BindOnce([](HWND hwnd) { ::DestroyWindow(hwnd); }, hwnd));
 
-  test::ShutdownTaskTracker(&task_tracker_);
+  task_tracker_.Shutdown();
 }
 
 #endif  // defined(OS_WIN)
diff --git a/base/task/task_scheduler/scheduler_worker.cc b/base/task/task_scheduler/scheduler_worker.cc
index deb098b8..c8d951c3 100644
--- a/base/task/task_scheduler/scheduler_worker.cc
+++ b/base/task/task_scheduler/scheduler_worker.cc
@@ -337,7 +337,8 @@
       continue;
     }
 
-    sequence = task_tracker_->RunAndPopNextTask(std::move(sequence));
+    sequence =
+        task_tracker_->RunAndPopNextTask(std::move(sequence), delegate_.get());
 
     delegate_->DidRunTask(std::move(sequence));
 
diff --git a/base/task/task_scheduler/scheduler_worker.h b/base/task/task_scheduler/scheduler_worker.h
index 37f4232d..88d1608 100644
--- a/base/task/task_scheduler/scheduler_worker.h
+++ b/base/task/task_scheduler/scheduler_worker.h
@@ -12,6 +12,7 @@
 #include "base/memory/ref_counted.h"
 #include "base/synchronization/atomic_flag.h"
 #include "base/synchronization/waitable_event.h"
+#include "base/task/task_scheduler/can_schedule_sequence_observer.h"
 #include "base/task/task_scheduler/scheduler_lock.h"
 #include "base/task/task_scheduler/scheduler_worker_params.h"
 #include "base/task/task_scheduler/sequence.h"
@@ -60,11 +61,12 @@
 #endif  // defined(OS_WIN)
   };
 
-  // Delegate interface for SchedulerWorker. All methods are called from the
-  // thread managed by the SchedulerWorker instance.
-  class BASE_EXPORT Delegate {
+  // Delegate interface for SchedulerWorker. All methods except
+  // OnCanScheduleSequence() (inherited from CanScheduleSequenceObserver) are
+  // called from the thread managed by the SchedulerWorker instance.
+  class BASE_EXPORT Delegate : public CanScheduleSequenceObserver {
    public:
-    virtual ~Delegate() = default;
+    ~Delegate() override = default;
 
     // Returns the ThreadLabel the Delegate wants its SchedulerWorkers' stacks
     // to be labeled with.
diff --git a/base/task/task_scheduler/scheduler_worker_pool.cc b/base/task/task_scheduler/scheduler_worker_pool.cc
index 0adb6d1..14e5e36 100644
--- a/base/task/task_scheduler/scheduler_worker_pool.cc
+++ b/base/task/task_scheduler/scheduler_worker_pool.cc
@@ -73,6 +73,17 @@
   return GetCurrentWorkerPool() == this;
 }
 
+void SchedulerWorkerPool::OnCanScheduleSequence(
+    scoped_refptr<Sequence> sequence) {
+  if (replacement_pool_) {
+    replacement_pool_->OnCanScheduleSequence(std::move(sequence));
+    return;
+  }
+
+  PushSequenceAndWakeUpWorkers(
+      SequenceAndTransaction::FromSequence(std::move(sequence)));
+}
+
 void SchedulerWorkerPool::PostTaskWithSequenceNow(
     Task task,
     SequenceAndTransaction sequence_and_transaction) {
@@ -85,31 +96,14 @@
   const bool task_source_should_be_queued =
       sequence_and_transaction.transaction.PushTask(std::move(task));
   if (task_source_should_be_queued) {
-    PushSequenceAndWakeUpWorkers(std::move(sequence_and_transaction));
+    // Try to schedule the Sequence locked by |sequence_transaction|.
+    if (task_tracker_->WillScheduleSequence(
+            sequence_and_transaction.transaction, this)) {
+      PushSequenceAndWakeUpWorkers(std::move(sequence_and_transaction));
+    }
   }
 }
 
-size_t SchedulerWorkerPool::GetNumQueuedCanRunBestEffortSequences() const {
-  const size_t num_queued =
-      priority_queue_.GetNumSequencesWithPriority(TaskPriority::BEST_EFFORT);
-  if (num_queued == 0 ||
-      !task_tracker_->CanRunPriority(TaskPriority::BEST_EFFORT)) {
-    return 0U;
-  }
-  return num_queued;
-}
-
-size_t SchedulerWorkerPool::GetNumQueuedCanRunForegroundSequences() const {
-  const size_t num_queued =
-      priority_queue_.GetNumSequencesWithPriority(TaskPriority::USER_VISIBLE) +
-      priority_queue_.GetNumSequencesWithPriority(TaskPriority::USER_BLOCKING);
-  if (num_queued == 0 ||
-      !task_tracker_->CanRunPriority(TaskPriority::HIGHEST)) {
-    return 0U;
-  }
-  return num_queued;
-}
-
 bool SchedulerWorkerPool::RemoveSequence(scoped_refptr<Sequence> sequence) {
   AutoSchedulerLock auto_lock(lock_);
   return priority_queue_.RemoveSequence(std::move(sequence));
diff --git a/base/task/task_scheduler/scheduler_worker_pool.h b/base/task/task_scheduler/scheduler_worker_pool.h
index 414e289..9c14fe3 100644
--- a/base/task/task_scheduler/scheduler_worker_pool.h
+++ b/base/task/task_scheduler/scheduler_worker_pool.h
@@ -7,6 +7,7 @@
 
 #include "base/base_export.h"
 #include "base/memory/ref_counted.h"
+#include "base/task/task_scheduler/can_schedule_sequence_observer.h"
 #include "base/task/task_scheduler/priority_queue.h"
 #include "base/task/task_scheduler/scheduler_lock.h"
 #include "base/task/task_scheduler/sequence.h"
@@ -19,8 +20,8 @@
 
 class TaskTracker;
 
-// Interface and base implementation for a worker pool.
-class BASE_EXPORT SchedulerWorkerPool {
+// Interface for a worker pool.
+class BASE_EXPORT SchedulerWorkerPool : public CanScheduleSequenceObserver {
  public:
   // Delegate interface for SchedulerWorkerPool.
   class BASE_EXPORT Delegate {
@@ -43,7 +44,10 @@
 #endif  // defined(OS_WIN)
   };
 
-  virtual ~SchedulerWorkerPool();
+  ~SchedulerWorkerPool() override;
+
+  // CanScheduleSequenceObserver:
+  void OnCanScheduleSequence(scoped_refptr<Sequence> sequence) final;
 
   // Posts |task| to be executed by this SchedulerWorkerPool as part of
   // the Sequence in |sequence_and_transaction|. This must only be called after
@@ -108,10 +112,6 @@
   // Reports relevant metrics per implementation.
   virtual void ReportHeartbeatMetrics() const = 0;
 
-  // Wakes up workers as appropriate for the new CanRunPolicy policy. Must be
-  // called after an update to CanRunPolicy in TaskTracker.
-  virtual void DidUpdateCanRunPolicy() = 0;
-
  protected:
   // Derived classes must implement a ScopedWorkersExecutor that derives from
   // this to perform operations on workers at the end of a scope, when all locks
@@ -156,16 +156,6 @@
   const TrackedRef<TaskTracker> task_tracker_;
   const TrackedRef<Delegate> delegate_;
 
-  // Returns the number of queued BEST_EFFORT sequences allowed to run by the
-  // current CanRunPolicy.
-  size_t GetNumQueuedCanRunBestEffortSequences() const
-      EXCLUSIVE_LOCKS_REQUIRED(lock_);
-
-  // Returns the number of queued USER_VISIBLE/USER_BLOCKING sequences allowed
-  // to run by the current CanRunPolicy.
-  size_t GetNumQueuedCanRunForegroundSequences() const
-      EXCLUSIVE_LOCKS_REQUIRED(lock_);
-
   // Ensures that there are enough workers to run queued sequences. |executor|
   // is forwarded from the one received in PushSequenceAndWakeUpWorkersImpl()
   virtual void EnsureEnoughWorkersLockRequired(
diff --git a/base/task/task_scheduler/scheduler_worker_pool_impl.cc b/base/task/task_scheduler/scheduler_worker_pool_impl.cc
index 286753c..1effafe 100644
--- a/base/task/task_scheduler/scheduler_worker_pool_impl.cc
+++ b/base/task/task_scheduler/scheduler_worker_pool_impl.cc
@@ -210,6 +210,7 @@
   ~SchedulerWorkerDelegateImpl() override;
 
   // SchedulerWorker::Delegate:
+  void OnCanScheduleSequence(scoped_refptr<Sequence> sequence) override;
   SchedulerWorker::ThreadLabel GetThreadLabel() const override;
   void OnMainEntry(const SchedulerWorker* worker) override;
   scoped_refptr<Sequence> GetWork(SchedulerWorker* worker) override;
@@ -563,6 +564,11 @@
 SchedulerWorkerPoolImpl::SchedulerWorkerDelegateImpl::
     ~SchedulerWorkerDelegateImpl() = default;
 
+void SchedulerWorkerPoolImpl::SchedulerWorkerDelegateImpl::
+    OnCanScheduleSequence(scoped_refptr<Sequence> sequence) {
+  outer_->OnCanScheduleSequence(std::move(sequence));
+}
+
 SchedulerWorker::ThreadLabel
 SchedulerWorkerPoolImpl::SchedulerWorkerDelegateImpl::GetThreadLabel() const {
   return SchedulerWorker::ThreadLabel::POOLED;
@@ -628,14 +634,13 @@
     return nullptr;
   }
 
-  // Enforce the CanRunPolicy and that no more than |max_best_effort_tasks_|
-  // BEST_EFFORT tasks run concurrently.
-  const TaskPriority priority =
-      outer_->priority_queue_.PeekSortKey().priority();
-  if (!outer_->task_tracker_->CanRunPriority(priority) ||
-      (priority == TaskPriority::BEST_EFFORT &&
-       outer_->num_running_best_effort_tasks_ >=
-           outer_->max_best_effort_tasks_)) {
+  // Enforce that no more than |max_best_effort_tasks_| BEST_EFFORT tasks run
+  // concurrently.
+  const bool next_sequence_is_best_effort =
+      outer_->priority_queue_.PeekSortKey().priority() ==
+      TaskPriority::BEST_EFFORT;
+  if (next_sequence_is_best_effort && outer_->num_running_best_effort_tasks_ >=
+                                          outer_->max_best_effort_tasks_) {
     OnWorkerBecomesIdleLockRequired(worker);
     return nullptr;
   }
@@ -646,7 +651,7 @@
   DCHECK(!outer_->idle_workers_stack_.Contains(worker));
 
   // Running BEST_EFFORT task bookkeeping.
-  if (priority == TaskPriority::BEST_EFFORT) {
+  if (next_sequence_is_best_effort) {
     write_worker().is_running_best_effort_task = true;
     ++outer_->num_running_best_effort_tasks_;
   }
@@ -1034,25 +1039,15 @@
 }
 
 size_t SchedulerWorkerPoolImpl::GetDesiredNumAwakeWorkersLockRequired() const {
-  if (!task_tracker_->CanRunPriority(TaskPriority::HIGHEST))
-    return 0U;
-
-  // Number of BEST_EFFORT sequences that are running or queued and allowed to
-  // run by the CanRunPolicy.
-  const size_t num_running_or_queued_can_run_best_effort_sequences =
-      num_running_best_effort_tasks_ + GetNumQueuedCanRunBestEffortSequences();
-
-  // Number of USER_{VISIBLE|BLOCKING} sequences that are running or queued.
+  const size_t num_running_or_queued_best_effort_sequences =
+      num_running_best_effort_tasks_ +
+      priority_queue_.GetNumSequencesWithPriority(TaskPriority::BEST_EFFORT);
   const size_t num_running_or_queued_foreground_sequences =
-      (num_running_tasks_ - num_running_best_effort_tasks_) +
-      priority_queue_.GetNumSequencesWithPriority(TaskPriority::USER_VISIBLE) +
-      priority_queue_.GetNumSequencesWithPriority(TaskPriority::USER_BLOCKING);
+      num_running_tasks_ + priority_queue_.Size() -
+      num_running_or_queued_best_effort_sequences;
 
-  const size_t workers_for_best_effort_sequences =
-      std::max(std::min(num_running_or_queued_can_run_best_effort_sequences,
-                        max_best_effort_tasks_),
-               num_running_best_effort_tasks_);
-
+  const size_t workers_for_best_effort_sequences = std::min(
+      num_running_or_queued_best_effort_sequences, max_best_effort_tasks_);
   const size_t workers_for_foreground_sequences =
       num_running_or_queued_foreground_sequences;
 
@@ -1061,12 +1056,6 @@
        max_tasks_, kMaxNumberOfWorkers});
 }
 
-void SchedulerWorkerPoolImpl::DidUpdateCanRunPolicy() {
-  ScopedWorkersExecutor executor(this);
-  AutoSchedulerLock auto_lock(lock_);
-  EnsureEnoughWorkersLockRequired(&executor);
-}
-
 void SchedulerWorkerPoolImpl::EnsureEnoughWorkersLockRequired(
     BaseScopedWorkersExecutor* base_executor) {
   // Don't do anything if the pool isn't started.
diff --git a/base/task/task_scheduler/scheduler_worker_pool_impl.h b/base/task/task_scheduler/scheduler_worker_pool_impl.h
index 3526d8e..6949e51 100644
--- a/base/task/task_scheduler/scheduler_worker_pool_impl.h
+++ b/base/task/task_scheduler/scheduler_worker_pool_impl.h
@@ -91,7 +91,6 @@
   void JoinForTesting() override;
   size_t GetMaxConcurrentNonBlockedTasksDeprecated() const override;
   void ReportHeartbeatMetrics() const override;
-  void DidUpdateCanRunPolicy() override;
 
   const HistogramBase* num_tasks_before_detach_histogram() const {
     return num_tasks_before_detach_histogram_;
diff --git a/base/task/task_scheduler/scheduler_worker_pool_impl_unittest.cc b/base/task/task_scheduler/scheduler_worker_pool_impl_unittest.cc
index 93afc77f..d3afa74 100644
--- a/base/task/task_scheduler/scheduler_worker_pool_impl_unittest.cc
+++ b/base/task/task_scheduler/scheduler_worker_pool_impl_unittest.cc
@@ -1044,9 +1044,9 @@
   DISALLOW_COPY_AND_ASSIGN(TaskSchedulerWorkerPoolBlockingTest);
 };
 
-// Verify that SaturateWithBlockingTasks() causes max tasks to increase and
-// creates a worker if needed. Also verify that UnblockBlockingTasks() decreases
-// max tasks after an increase.
+// Verify that BlockingScopeEntered() causes max tasks to increase and creates a
+// worker if needed. Also verify that BlockingScopeExited() decreases max tasks
+// after an increase.
 TEST_P(TaskSchedulerWorkerPoolBlockingTest, ThreadBlockedUnblocked) {
   CreateAndStartWorkerPool();
 
@@ -1066,69 +1066,6 @@
   EXPECT_EQ(worker_pool_->GetMaxTasksForTesting(), kMaxTasks);
 }
 
-// Verify that flooding the pool with more BEST_EFFORT tasks than
-// kMaxBestEffortTasks doesn't prevent USER_VISIBLE tasks from running.
-TEST_P(TaskSchedulerWorkerPoolBlockingTest, TooManyBestEffortTasks) {
-  constexpr size_t kMaxBestEffortTasks = kMaxTasks / 2;
-
-  CreateAndStartWorkerPool(TimeDelta::Max(), kMaxTasks, kMaxBestEffortTasks);
-
-  WaitableEvent threads_continue;
-  {
-    WaitableEvent entered_blocking_scope;
-    RepeatingClosure entered_blocking_scope_barrier = BarrierClosure(
-        kMaxBestEffortTasks + 1,
-        BindOnce(&WaitableEvent::Signal, Unretained(&entered_blocking_scope)));
-    WaitableEvent exit_blocking_scope;
-
-    WaitableEvent threads_running;
-    RepeatingClosure threads_running_barrier = BarrierClosure(
-        kMaxBestEffortTasks + 1,
-        BindOnce(&WaitableEvent::Signal, Unretained(&threads_running)));
-
-    const auto best_effort_task_runner = test::CreateTaskRunnerWithTraits(
-        {TaskPriority::BEST_EFFORT, MayBlock()},
-        &mock_scheduler_task_runner_delegate_);
-    for (size_t i = 0; i < kMaxBestEffortTasks + 1; ++i) {
-      best_effort_task_runner->PostTask(
-          FROM_HERE, BindLambdaForTesting([&]() {
-            {
-              NestedScopedBlockingCall scoped_blocking_call(GetParam());
-              entered_blocking_scope_barrier.Run();
-              test::WaitWithoutBlockingObserver(&exit_blocking_scope);
-            }
-            threads_running_barrier.Run();
-            test::WaitWithoutBlockingObserver(&threads_continue);
-          }));
-    }
-    entered_blocking_scope.Wait();
-    exit_blocking_scope.Signal();
-    threads_running.Wait();
-  }
-
-  // At this point, kMaxBestEffortTasks + 1 threads are running (plus
-  // potentially the idle thread), but max_task and max_best_effort_task are
-  // back to normal.
-  EXPECT_GE(worker_pool_->NumberOfWorkersForTesting(), kMaxBestEffortTasks + 1);
-  EXPECT_LE(worker_pool_->NumberOfWorkersForTesting(), kMaxBestEffortTasks + 2);
-  EXPECT_EQ(worker_pool_->GetMaxTasksForTesting(), kMaxTasks);
-
-  WaitableEvent threads_running;
-  task_runner_->PostTask(FROM_HERE, BindLambdaForTesting([&]() {
-                           threads_running.Signal();
-                           test::WaitWithoutBlockingObserver(&threads_continue);
-                         }));
-
-  // This should not block forever.
-  threads_running.Wait();
-
-  EXPECT_GE(worker_pool_->NumberOfWorkersForTesting(), kMaxBestEffortTasks + 2);
-  EXPECT_LE(worker_pool_->NumberOfWorkersForTesting(), kMaxBestEffortTasks + 3);
-  threads_continue.Signal();
-
-  task_tracker_.FlushForTesting();
-}
-
 // Verify that tasks posted in a saturated pool before a ScopedBlockingCall will
 // execute after ScopedBlockingCall is instantiated.
 TEST_P(TaskSchedulerWorkerPoolBlockingTest, PostBeforeBlocking) {
diff --git a/base/task/task_scheduler/scheduler_worker_pool_unittest.cc b/base/task/task_scheduler/scheduler_worker_pool_unittest.cc
index 81c1916a..c3b236c 100644
--- a/base/task/task_scheduler/scheduler_worker_pool_unittest.cc
+++ b/base/task/task_scheduler/scheduler_worker_pool_unittest.cc
@@ -11,7 +11,6 @@
 #include "base/bind_helpers.h"
 #include "base/location.h"
 #include "base/memory/ref_counted.h"
-#include "base/task/task_scheduler/can_run_policy_test.h"
 #include "base/task/task_scheduler/delayed_task_manager.h"
 #include "base/task/task_scheduler/scheduler_sequenced_task_runner.h"
 #include "base/task/task_scheduler/scheduler_worker_pool_impl.h"
@@ -167,13 +166,6 @@
     }
   }
 
-  scoped_refptr<TaskRunner> CreateTaskRunner(
-      const TaskTraits& traits = TaskTraits()) {
-    return test::CreateTaskRunnerWithExecutionMode(
-        GetParam().execution_mode, &mock_scheduler_task_runner_delegate_,
-        traits);
-  }
-
   Thread service_thread_;
   TaskTracker task_tracker_ = {"Test"};
   DelayedTaskManager delayed_task_manager_;
@@ -248,8 +240,9 @@
 // Verify that a Task can't be posted after shutdown.
 TEST_P(TaskSchedulerWorkerPoolTest, PostTaskAfterShutdown) {
   StartWorkerPool();
-  auto task_runner = CreateTaskRunner();
-  test::ShutdownTaskTracker(&task_tracker_);
+  auto task_runner = test::CreateTaskRunnerWithExecutionMode(
+      GetParam().execution_mode, &mock_scheduler_task_runner_delegate_);
+  task_tracker_.Shutdown();
   EXPECT_FALSE(task_runner->PostTask(FROM_HERE, BindOnce(&ShouldNotRun)));
 }
 
@@ -257,9 +250,10 @@
 // crash.
 TEST_P(TaskSchedulerWorkerPoolTest, PostAfterDestroy) {
   StartWorkerPool();
-  auto task_runner = CreateTaskRunner();
+  auto task_runner = test::CreateTaskRunnerWithExecutionMode(
+      GetParam().execution_mode, &mock_scheduler_task_runner_delegate_);
   EXPECT_TRUE(task_runner->PostTask(FROM_HERE, DoNothing()));
-  test::ShutdownTaskTracker(&task_tracker_);
+  task_tracker_.Shutdown();
   worker_pool_->JoinForTesting();
   worker_pool_.reset();
   EXPECT_FALSE(task_runner->PostTask(FROM_HERE, BindOnce(&ShouldNotRun)));
@@ -271,7 +265,9 @@
 
   WaitableEvent task_ran(WaitableEvent::ResetPolicy::AUTOMATIC,
                          WaitableEvent::InitialState::NOT_SIGNALED);
-  auto task_runner = CreateTaskRunner();
+
+  auto task_runner = test::CreateTaskRunnerWithExecutionMode(
+      GetParam().execution_mode, &mock_scheduler_task_runner_delegate_);
 
   // Wait until the task runner is up and running to make sure the test below is
   // solely timing the delayed task, not bringing up a physical thread.
@@ -304,7 +300,8 @@
 // complements it to get full coverage of that method.
 TEST_P(TaskSchedulerWorkerPoolTest, SequencedRunsTasksInCurrentSequence) {
   StartWorkerPool();
-  auto task_runner = CreateTaskRunner();
+  auto task_runner = test::CreateTaskRunnerWithExecutionMode(
+      GetParam().execution_mode, &mock_scheduler_task_runner_delegate_);
   auto sequenced_task_runner = test::CreateSequencedTaskRunnerWithTraits(
       TaskTraits(), &mock_scheduler_task_runner_delegate_);
 
@@ -326,7 +323,9 @@
   WaitableEvent task_1_running;
   WaitableEvent task_2_running;
 
-  auto task_runner = CreateTaskRunner();
+  scoped_refptr<TaskRunner> task_runner = test::CreateTaskRunnerWithTraits(
+      {WithBaseSyncPrimitives()}, &mock_scheduler_task_runner_delegate_);
+
   task_runner->PostTask(
       FROM_HERE, BindOnce(&WaitableEvent::Signal, Unretained(&task_1_running)));
   task_runner->PostTask(
@@ -347,35 +346,6 @@
   task_tracker_.FlushForTesting();
 }
 
-// Verify that tasks only run when allowed by the CanRunPolicy.
-TEST_P(TaskSchedulerWorkerPoolTest, CanRunPolicyBasic) {
-  StartWorkerPool();
-  test::TestCanRunPolicyBasic(
-      worker_pool_.get(),
-      [this](TaskPriority priority) { return CreateTaskRunner({priority}); },
-      &task_tracker_);
-}
-
-TEST_P(TaskSchedulerWorkerPoolTest, CanRunPolicyUpdatedBeforeRun) {
-  StartWorkerPool();
-  // This test only works with SequencedTaskRunner become it assumes
-  // ordered execution of 2 posted tasks.
-  if (GetParam().execution_mode != test::ExecutionMode::SEQUENCED)
-    return;
-  test::TestCanRunPolicyChangedBeforeRun(
-      worker_pool_.get(),
-      [this](TaskPriority priority) { return CreateTaskRunner({priority}); },
-      &task_tracker_);
-}
-
-TEST_P(TaskSchedulerWorkerPoolTest, CanRunPolicyLoad) {
-  StartWorkerPool();
-  test::TestCanRunPolicyLoad(
-      worker_pool_.get(),
-      [this](TaskPriority priority) { return CreateTaskRunner({priority}); },
-      &task_tracker_);
-}
-
 // Verify that the maximum number of BEST_EFFORT tasks that can run concurrently
 // in a pool does not affect Sequences with a priority that was increased from
 // BEST_EFFORT to USER_BLOCKING.
diff --git a/base/task/task_scheduler/scheduler_worker_stack_unittest.cc b/base/task/task_scheduler/scheduler_worker_stack_unittest.cc
index f94d2a7..f78243b 100644
--- a/base/task/task_scheduler/scheduler_worker_stack_unittest.cc
+++ b/base/task/task_scheduler/scheduler_worker_stack_unittest.cc
@@ -21,6 +21,9 @@
 
 class MockSchedulerWorkerDelegate : public SchedulerWorker::Delegate {
  public:
+  void OnCanScheduleSequence(scoped_refptr<Sequence> sequence) override {
+    ADD_FAILURE() << "Unexpected call to OnCanScheduleSequence().";
+  }
   SchedulerWorker::ThreadLabel GetThreadLabel() const override {
     return SchedulerWorker::ThreadLabel::DEDICATED;
   }
diff --git a/base/task/task_scheduler/scheduler_worker_unittest.cc b/base/task/task_scheduler/scheduler_worker_unittest.cc
index 9803f81..4e9bac7 100644
--- a/base/task/task_scheduler/scheduler_worker_unittest.cc
+++ b/base/task/task_scheduler/scheduler_worker_unittest.cc
@@ -53,6 +53,9 @@
   SchedulerWorkerDefaultDelegate() = default;
 
   // SchedulerWorker::Delegate:
+  void OnCanScheduleSequence(scoped_refptr<Sequence> sequence) override {
+    ADD_FAILURE() << "Unexpected call to OnCanScheduleSequence().";
+  }
   SchedulerWorker::ThreadLabel GetThreadLabel() const override {
     return SchedulerWorker::ThreadLabel::DEDICATED;
   }
@@ -195,6 +198,8 @@
         outer_->created_sequences_.push_back(sequence);
       }
 
+      EXPECT_TRUE(outer_->task_tracker_.WillScheduleSequence(
+          sequence_transaction, nullptr));
       return sequence;
     }
 
@@ -457,7 +462,10 @@
         TimeDelta());
     EXPECT_TRUE(
         task_tracker_->WillPostTask(&task, sequence->shutdown_behavior()));
-    sequence->BeginTransaction().PushTask(std::move(task));
+    Sequence::Transaction sequence_transaction(sequence->BeginTransaction());
+    sequence_transaction.PushTask(std::move(task));
+    EXPECT_TRUE(
+        task_tracker_->WillScheduleSequence(sequence_transaction, nullptr));
     return sequence;
   }
 
@@ -593,7 +601,7 @@
   worker->WakeUp();
 
   controls->WaitForWorkToRun();
-  test::ShutdownTaskTracker(&task_tracker);
+  task_tracker.Shutdown();
   worker->Cleanup();
   worker = nullptr;
   controls->UnblockWork();
@@ -737,11 +745,6 @@
 
   TaskTracker task_tracker("Test");
 
-  // Block shutdown to ensure that the worker doesn't exit when StartShutdown()
-  // is called.
-  Task task(FROM_HERE, DoNothing(), TimeDelta());
-  task_tracker.WillPostTask(&task, TaskShutdownBehavior::BLOCK_SHUTDOWN);
-
   std::unique_ptr<ExpectThreadPriorityDelegate> delegate(
       new ExpectThreadPriorityDelegate);
   ExpectThreadPriorityDelegate* delegate_raw = delegate.get();
@@ -758,7 +761,7 @@
 
   // Verify that the thread priority is bumped to NORMAL during shutdown.
   delegate_raw->SetExpectedThreadPriority(ThreadPriority::NORMAL);
-  task_tracker.StartShutdown();
+  task_tracker.SetHasShutdownStartedForTesting();
   worker->WakeUp();
   delegate_raw->WaitForPriorityVerifiedInGetWork();
 
diff --git a/base/task/task_scheduler/task_scheduler.cc b/base/task/task_scheduler/task_scheduler.cc
index 6075029a7..02c9297 100644
--- a/base/task/task_scheduler/task_scheduler.cc
+++ b/base/task/task_scheduler/task_scheduler.cc
@@ -35,12 +35,12 @@
 
 TaskScheduler::ScopedExecutionFence::ScopedExecutionFence() {
   DCHECK(g_task_scheduler);
-  g_task_scheduler->SetCanRun(false);
+  g_task_scheduler->SetExecutionFenceEnabled(true);
 }
 
 TaskScheduler::ScopedExecutionFence::~ScopedExecutionFence() {
   DCHECK(g_task_scheduler);
-  g_task_scheduler->SetCanRun(true);
+  g_task_scheduler->SetExecutionFenceEnabled(false);
 }
 
 #if !defined(OS_NACL)
diff --git a/base/task/task_scheduler/task_scheduler.h b/base/task/task_scheduler/task_scheduler.h
index 02bad08..185e8536 100644
--- a/base/task/task_scheduler/task_scheduler.h
+++ b/base/task/task_scheduler/task_scheduler.h
@@ -209,9 +209,8 @@
   virtual int GetMaxConcurrentNonBlockedTasksWithTraitsDeprecated(
       const TaskTraits& traits) const = 0;
 
-  // Sets whether tasks of any / BEST_EFFORT priority are allowed to run.
-  virtual void SetCanRun(bool can_run) = 0;
-  virtual void SetCanRunBestEffort(bool can_run) = 0;
+  // Enables/disables an execution fence that prevents tasks from running.
+  virtual void SetExecutionFenceEnabled(bool execution_fence_enabled) = 0;
 };
 
 }  // namespace base
diff --git a/base/task/task_scheduler/task_scheduler_impl.cc b/base/task/task_scheduler/task_scheduler_impl.cc
index 8b71a26..7acd6f68 100644
--- a/base/task/task_scheduler/task_scheduler_impl.cc
+++ b/base/task/task_scheduler/task_scheduler_impl.cc
@@ -8,10 +8,8 @@
 #include <string>
 #include <utility>
 
-#include "base/base_switches.h"
 #include "base/bind.h"
 #include "base/bind_helpers.h"
-#include "base/command_line.h"
 #include "base/compiler_specific.h"
 #include "base/feature_list.h"
 #include "base/message_loop/message_loop.h"
@@ -40,15 +38,6 @@
 constexpr EnvironmentParams kBackgroundPoolEnvironmentParams{
     "Background", base::ThreadPriority::BACKGROUND};
 
-// Indicates whether BEST_EFFORT tasks are disabled by a command line switch.
-bool HasDisableBestEffortTasksSwitch() {
-  // The CommandLine might not be initialized if TaskScheduler is initialized
-  // in a dynamic library which doesn't have access to argc/argv.
-  return CommandLine::InitializedForCurrentProcess() &&
-         CommandLine::ForCurrentProcess()->HasSwitch(
-             switches::kDisableBestEffortTasks);
-}
-
 }  // namespace
 
 TaskSchedulerImpl::TaskSchedulerImpl(StringPiece histogram_label)
@@ -65,7 +54,6 @@
                         Unretained(this)))),
       single_thread_task_runner_manager_(task_tracker_->GetTrackedRef(),
                                          &delayed_task_manager_),
-      can_run_best_effort_(!HasDisableBestEffortTasksSwitch()),
       tracked_ref_factory_(this) {
   DCHECK(!histogram_label.empty());
 
@@ -103,9 +91,6 @@
 void TaskSchedulerImpl::Start(
     const TaskScheduler::InitParams& init_params,
     SchedulerWorkerObserver* scheduler_worker_observer) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(!started_);
-
   internal::InitializeThreadPrioritiesFeature();
 
   // This is set in Start() and not in the constructor because variation params
@@ -187,8 +172,6 @@
         service_thread_task_runner, scheduler_worker_observer,
         worker_environment);
   }
-
-  started_ = true;
 }
 
 bool TaskSchedulerImpl::PostDelayedTaskWithTraits(const Location& from_here,
@@ -252,18 +235,7 @@
 }
 
 void TaskSchedulerImpl::Shutdown() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  task_tracker_->StartShutdown();
-
-  // Allow all tasks to run. Done after initiating shutdown to ensure that non-
-  // BLOCK_SHUTDOWN tasks don't get a chance to run and that BLOCK_SHUTDOWN
-  // tasks run with a normal thread priority.
-  can_run_ = true;
-  can_run_best_effort_ = true;
-  UpdateCanRunPolicy();
-
-  task_tracker_->CompleteShutdown();
+  task_tracker_->Shutdown();
 }
 
 void TaskSchedulerImpl::FlushForTesting() {
@@ -292,18 +264,8 @@
 #endif
 }
 
-void TaskSchedulerImpl::SetCanRun(bool can_run) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK_NE(can_run_, can_run);
-  can_run_ = can_run;
-  UpdateCanRunPolicy();
-}
-
-void TaskSchedulerImpl::SetCanRunBestEffort(bool can_run_best_effort) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK_NE(can_run_best_effort_, can_run_best_effort);
-  can_run_best_effort_ = can_run_best_effort;
-  UpdateCanRunPolicy();
+void TaskSchedulerImpl::SetExecutionFenceEnabled(bool execution_fence_enabled) {
+  task_tracker_->SetExecutionFenceEnabled(execution_fence_enabled);
 }
 
 bool TaskSchedulerImpl::PostTaskWithSequence(Task task,
@@ -406,20 +368,6 @@
   return &foreground_pool_.value();
 }
 
-void TaskSchedulerImpl::UpdateCanRunPolicy() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  const CanRunPolicy can_run_policy =
-      can_run_ ? (can_run_best_effort_ ? CanRunPolicy::kAll
-                                       : CanRunPolicy::kForegroundOnly)
-               : CanRunPolicy::kNone;
-  task_tracker_->SetCanRunPolicy(can_run_policy);
-  GetForegroundWorkerPool()->DidUpdateCanRunPolicy();
-  if (background_pool_)
-    background_pool_->DidUpdateCanRunPolicy();
-  single_thread_task_runner_manager_.DidUpdateCanRunPolicy();
-}
-
 TaskTraits TaskSchedulerImpl::SetUserBlockingPriorityIfNeeded(
     TaskTraits traits) const {
   if (all_tasks_user_blocking_.IsSet())
diff --git a/base/task/task_scheduler/task_scheduler_impl.h b/base/task/task_scheduler/task_scheduler_impl.h
index ab70eb5..adad3a4 100644
--- a/base/task/task_scheduler/task_scheduler_impl.h
+++ b/base/task/task_scheduler/task_scheduler_impl.h
@@ -14,7 +14,6 @@
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
 #include "base/memory/ref_counted.h"
-#include "base/sequence_checker.h"
 #include "base/strings/string_piece.h"
 #include "base/synchronization/atomic_flag.h"
 #include "base/task/single_thread_task_runner_thread_mode.h"
@@ -79,8 +78,7 @@
   void FlushForTesting() override;
   void FlushAsyncForTesting(OnceClosure flush_callback) override;
   void JoinForTesting() override;
-  void SetCanRun(bool can_run) override;
-  void SetCanRunBestEffort(bool can_run_best_effort) override;
+  void SetExecutionFenceEnabled(bool execution_fence_enabled) override;
 
   // TaskExecutor:
   bool PostDelayedTaskWithTraits(const Location& from_here,
@@ -104,10 +102,6 @@
       const TaskTraits& traits);
 
  private:
-  // Invoked after |can_run_| or |can_run_best_effort_| is updated. Sets the
-  // CanRunPolicy in TaskTracker and wakes up workers as appropriate.
-  void UpdateCanRunPolicy();
-
   // Returns |traits|, with priority set to TaskPriority::USER_BLOCKING if
   // |all_tasks_user_blocking_| is set.
   TaskTraits SetUserBlockingPriorityIfNeeded(TaskTraits traits) const;
@@ -148,15 +142,6 @@
   Optional<SchedulerWorkerPoolImpl> foreground_pool_;
   Optional<SchedulerWorkerPoolImpl> background_pool_;
 
-  // Whether this TaskScheduler was started. Access controlled by
-  // |sequence_checker_|.
-  bool started_ = false;
-
-  // Whether starting to run a Task with any/BEST_EFFORT priority is currently
-  // allowed. Access controlled by |sequence_checker_|.
-  bool can_run_ = true;
-  bool can_run_best_effort_;
-
 #if defined(OS_WIN)
   Optional<PlatformNativeWorkerPoolWin> native_foreground_pool_;
 #elif defined(OS_MACOSX)
@@ -173,9 +158,6 @@
   base::win::ComInitCheckHook com_init_check_hook_;
 #endif
 
-  // Asserts that operations occur in sequence with Start().
-  SEQUENCE_CHECKER(sequence_checker_);
-
   TrackedRefFactory<SchedulerWorkerPool::Delegate> tracked_ref_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(TaskSchedulerImpl);
diff --git a/base/task/task_scheduler/task_scheduler_impl_unittest.cc b/base/task/task_scheduler/task_scheduler_impl_unittest.cc
index 7187c196..f5c710ef 100644
--- a/base/task/task_scheduler/task_scheduler_impl_unittest.cc
+++ b/base/task/task_scheduler/task_scheduler_impl_unittest.cc
@@ -495,75 +495,6 @@
   flush_event.Wait();
 }
 
-// Verifies that tasks only run when allowed by SetCanRun().
-TEST_P(TaskSchedulerImplTest, SetCanRun) {
-  StartTaskScheduler();
-
-  AtomicFlag can_run;
-  WaitableEvent did_run;
-  scheduler_.SetCanRun(false);
-
-  CreateTaskRunnerWithTraitsAndExecutionMode(&scheduler_, GetParam().traits,
-                                             GetParam().execution_mode)
-      ->PostTask(FROM_HERE, BindLambdaForTesting([&]() {
-                   EXPECT_TRUE(can_run.IsSet());
-                   did_run.Signal();
-                 }));
-
-  PlatformThread::Sleep(TestTimeouts::tiny_timeout());
-
-  can_run.Set();
-  scheduler_.SetCanRun(true);
-  did_run.Wait();
-}
-
-// Verifies that a call to SetCanRun(false) before Start() is honored.
-TEST_P(TaskSchedulerImplTest, SetCanRunBeforeStart) {
-  scheduler_.SetCanRun(false);
-  StartTaskScheduler();
-
-  AtomicFlag can_run;
-  WaitableEvent did_run;
-
-  CreateTaskRunnerWithTraitsAndExecutionMode(&scheduler_, GetParam().traits,
-                                             GetParam().execution_mode)
-      ->PostTask(FROM_HERE, BindLambdaForTesting([&]() {
-                   EXPECT_TRUE(can_run.IsSet());
-                   did_run.Signal();
-                 }));
-
-  PlatformThread::Sleep(TestTimeouts::tiny_timeout());
-
-  can_run.Set();
-  scheduler_.SetCanRun(true);
-  did_run.Wait();
-}
-
-// Verifies that BEST_EFFORT tasks only run when allowed by
-// SetCanRunBestEffort().
-TEST_P(TaskSchedulerImplTest, SetCanRunBestEffort) {
-  StartTaskScheduler();
-
-  AtomicFlag can_run;
-  WaitableEvent did_run;
-  scheduler_.SetCanRunBestEffort(false);
-
-  CreateTaskRunnerWithTraitsAndExecutionMode(&scheduler_, GetParam().traits,
-                                             GetParam().execution_mode)
-      ->PostTask(
-          FROM_HERE, BindLambdaForTesting([&]() {
-            if (GetParam().traits.priority() == TaskPriority::BEST_EFFORT)
-              EXPECT_TRUE(can_run.IsSet());
-            did_run.Signal();
-          }));
-
-  PlatformThread::Sleep(TestTimeouts::tiny_timeout());
-
-  can_run.Set();
-  scheduler_.SetCanRunBestEffort(true);
-  did_run.Wait();
-}
-
 INSTANTIATE_TEST_SUITE_P(OneTaskSchedulerImplTestParams,
                          TaskSchedulerImplTest,
                          ::testing::ValuesIn(GetTaskSchedulerImplTestParams()));
diff --git a/base/task/task_scheduler/task_tracker.cc b/base/task/task_scheduler/task_tracker.cc
index 211f1261..6a218ab3 100644
--- a/base/task/task_scheduler/task_tracker.cc
+++ b/base/task/task_scheduler/task_tracker.cc
@@ -8,7 +8,9 @@
 #include <string>
 #include <vector>
 
+#include "base/base_switches.h"
 #include "base/callback.h"
+#include "base/command_line.h"
 #include "base/compiler_specific.h"
 #include "base/debug/alias.h"
 #include "base/json/json_writer.h"
@@ -128,6 +130,19 @@
                         : 0];
 }
 
+// Returns the maximum number of TaskPriority::BEST_EFFORT sequences that can be
+// scheduled concurrently based on command line flags.
+int GetMaxNumScheduledBestEffortSequences() {
+  // The CommandLine might not be initialized if TaskScheduler is initialized
+  // in a dynamic library which doesn't have access to argc/argv.
+  if (CommandLine::InitializedForCurrentProcess() &&
+      CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kDisableBestEffortTasks)) {
+    return 0;
+  }
+  return std::numeric_limits<int>::max();
+}
+
 // Returns shutdown behavior based on |traits|; returns SKIP_ON_SHUTDOWN if
 // shutdown behavior is BLOCK_SHUTDOWN and |is_delayed|, because delayed tasks
 // are not allowed to block shutdown.
@@ -229,10 +244,47 @@
   DISALLOW_COPY_AND_ASSIGN(State);
 };
 
-// TODO(jessemckenna): Write a helper function to avoid code duplication below.
+struct TaskTracker::PreemptedSequence {
+  PreemptedSequence() = default;
+  PreemptedSequence(scoped_refptr<Sequence> sequence_in,
+                    TimeTicks next_task_sequenced_time_in,
+                    CanScheduleSequenceObserver* observer_in)
+      : sequence(std::move(sequence_in)),
+        next_task_sequenced_time(next_task_sequenced_time_in),
+        observer(observer_in) {}
+  PreemptedSequence(PreemptedSequence&& other) = default;
+  ~PreemptedSequence() = default;
+  PreemptedSequence& operator=(PreemptedSequence&& other) = default;
+  bool operator<(const PreemptedSequence& other) const {
+    return next_task_sequenced_time < other.next_task_sequenced_time;
+  }
+  bool operator>(const PreemptedSequence& other) const {
+    return next_task_sequenced_time > other.next_task_sequenced_time;
+  }
+
+  // A sequence waiting to be scheduled.
+  scoped_refptr<Sequence> sequence;
+
+  // The sequenced time of the next task in |sequence|.
+  TimeTicks next_task_sequenced_time;
+
+  // An observer to notify when |sequence| can be scheduled.
+  CanScheduleSequenceObserver* observer = nullptr;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(PreemptedSequence);
+};
+
+TaskTracker::PreemptionState::PreemptionState() = default;
+TaskTracker::PreemptionState::~PreemptionState() = default;
+
 TaskTracker::TaskTracker(StringPiece histogram_label)
+    : TaskTracker(histogram_label, GetMaxNumScheduledBestEffortSequences()) {}
+
+// TODO(jessemckenna): Write a helper function to avoid code duplication below.
+TaskTracker::TaskTracker(StringPiece histogram_label,
+                         int max_num_scheduled_best_effort_sequences)
     : state_(new State),
-      can_run_policy_(CanRunPolicy::kAll),
       flush_cv_(flush_lock_.CreateConditionVariable()),
       shutdown_lock_(&flush_lock_),
       task_latency_histograms_{
@@ -297,44 +349,50 @@
   DCHECK(*(&task_latency_histograms_[static_cast<int>(TaskPriority::HIGHEST) +
                                      1][0] -
            1));
+  preemption_state_[static_cast<int>(TaskPriority::BEST_EFFORT)]
+      .max_scheduled_sequences = max_num_scheduled_best_effort_sequences;
+  DETACH_FROM_SEQUENCE(sequence_checker_);
 }
 
 TaskTracker::~TaskTracker() = default;
 
-void TaskTracker::StartShutdown() {
-  AutoSchedulerLock auto_lock(shutdown_lock_);
+void TaskTracker::SetExecutionFenceEnabled(bool execution_fence_enabled) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-  // This method can only be called once.
-  DCHECK(!shutdown_event_);
-  DCHECK(!state_->HasShutdownStarted());
+#if DCHECK_IS_ON()
+  // It is invalid to have two fences at the same time.
+  DCHECK_NE(execution_fence_enabled_, execution_fence_enabled);
+  execution_fence_enabled_ = execution_fence_enabled;
+#endif
 
-  shutdown_event_ = std::make_unique<WaitableEvent>();
+  for (int priority_index = static_cast<int>(TaskPriority::HIGHEST);
+       priority_index >= static_cast<int>(TaskPriority::LOWEST);
+       --priority_index) {
+    int max_scheduled_sequences;
+    if (execution_fence_enabled) {
+      preemption_state_[priority_index].max_scheduled_sequences_before_fence =
+          preemption_state_[priority_index].max_scheduled_sequences;
+      max_scheduled_sequences = 0;
+    } else {
+      max_scheduled_sequences = preemption_state_[priority_index]
+                                    .max_scheduled_sequences_before_fence;
+    }
 
-  const bool tasks_are_blocking_shutdown = state_->StartShutdown();
-
-  // From now, if a thread causes the number of tasks blocking shutdown to
-  // become zero, it will call OnBlockingShutdownTasksComplete().
-
-  if (!tasks_are_blocking_shutdown) {
-    // If another thread posts a BLOCK_SHUTDOWN task at this moment, it will
-    // block until this method releases |shutdown_lock_|. Then, it will fail
-    // DCHECK(!shutdown_event_->IsSignaled()). This is the desired behavior
-    // because posting a BLOCK_SHUTDOWN task after StartShutdown() when no
-    // tasks are blocking shutdown isn't allowed.
-    shutdown_event_->Signal();
-    return;
+    SetMaxNumScheduledSequences(max_scheduled_sequences,
+                                static_cast<TaskPriority>(priority_index));
   }
 }
 
-void TaskTracker::CompleteShutdown() {
-  DCHECK(shutdown_event_);
-  // It is safe to access |shutdown_event_| without holding |lock_| because the
-  // pointer never changes after being set by StartShutdown(), which must be
-  // called before this.
-  {
-    base::ScopedAllowBaseSyncPrimitives allow_wait;
-    shutdown_event_->Wait();
-  }
+size_t TaskTracker::GetPreemptedSequenceCountForTesting(
+    TaskPriority task_priority) {
+  int priority_index = static_cast<int>(task_priority);
+  AutoSchedulerLock auto_lock(preemption_state_[priority_index].lock);
+  return preemption_state_[priority_index].preempted_sequences.size();
+}
+
+void TaskTracker::Shutdown() {
+  PerformShutdown();
+  DCHECK(IsShutdownComplete());
 
   // Unblock FlushForTesting() and perform the FlushAsyncForTesting callback
   // when shutdown completes.
@@ -368,10 +426,6 @@
   }
 }
 
-void TaskTracker::SetCanRunPolicy(CanRunPolicy can_run_policy) {
-  can_run_policy_.store(can_run_policy);
-}
-
 bool TaskTracker::WillPostTask(Task* task,
                                TaskShutdownBehavior shutdown_behavior) {
   DCHECK(task);
@@ -390,22 +444,33 @@
   return true;
 }
 
-bool TaskTracker::CanRunPriority(TaskPriority priority) const {
-  auto can_run_policy = can_run_policy_.load();
+bool TaskTracker::WillScheduleSequence(
+    const Sequence::Transaction& sequence_transaction,
+    CanScheduleSequenceObserver* observer) {
+  const SequenceSortKey sort_key = sequence_transaction.GetSortKey();
+  const int priority_index = static_cast<int>(sort_key.priority());
 
-  if (can_run_policy == CanRunPolicy::kAll)
-    return true;
+  AutoSchedulerLock auto_lock(preemption_state_[priority_index].lock);
 
-  if (can_run_policy == CanRunPolicy::kForegroundOnly &&
-      priority >= TaskPriority::USER_VISIBLE) {
+  if (preemption_state_[priority_index].current_scheduled_sequences <
+      preemption_state_[priority_index].max_scheduled_sequences) {
+    ++preemption_state_[priority_index].current_scheduled_sequences;
     return true;
   }
 
+  // It is convenient not to have to specify an observer when scheduling
+  // foreground sequences in tests.
+  DCHECK(observer);
+
+  preemption_state_[priority_index].preempted_sequences.emplace(
+      WrapRefCounted(sequence_transaction.sequence()),
+      sort_key.next_task_sequenced_time(), observer);
   return false;
 }
 
 scoped_refptr<Sequence> TaskTracker::RunAndPopNextTask(
-    scoped_refptr<Sequence> sequence) {
+    scoped_refptr<Sequence> sequence,
+    CanScheduleSequenceObserver* observer) {
   DCHECK(sequence);
 
   // Run the next task in |sequence|.
@@ -438,10 +503,15 @@
   const bool sequence_must_be_queued =
       sequence->BeginTransaction().DidRunTask();
 
-  // The sequence should be reenqueued iff requested by DidRunTask().
+  // Never reschedule a Sequence empty after DidRunTask(). The contract is such
+  // that next poster to make it non-empty is responsible to schedule it.
   if (!sequence_must_be_queued)
-    return nullptr;
-  return sequence;
+    sequence = nullptr;
+
+  // Allow |sequence| to be rescheduled only if its next task is set to run
+  // earlier than the earliest currently preempted sequence
+  return ManageSequencesAfterRunningTask(std::move(sequence), observer,
+                                         traits.priority());
 }
 
 bool TaskTracker::HasShutdownStarted() const {
@@ -453,6 +523,16 @@
   return shutdown_event_ && shutdown_event_->IsSignaled();
 }
 
+void TaskTracker::SetHasShutdownStartedForTesting() {
+  AutoSchedulerLock auto_lock(shutdown_lock_);
+
+  // Create a dummy |shutdown_event_| to satisfy TaskTracker's expectation of
+  // its existence during shutdown (e.g. in OnBlockingShutdownTasksComplete()).
+  shutdown_event_ = std::make_unique<WaitableEvent>();
+
+  state_->StartShutdown();
+}
+
 void TaskTracker::RecordLatencyHistogram(
     LatencyHistogramType latency_histogram_type,
     TaskTraits task_traits,
@@ -575,6 +655,105 @@
   ThreadRestrictions::SetSingletonAllowed(previous_singleton_allowed);
 }
 
+void TaskTracker::PerformShutdown() {
+  {
+    AutoSchedulerLock auto_lock(shutdown_lock_);
+
+    // This method can only be called once.
+    DCHECK(!shutdown_event_);
+    DCHECK(!state_->HasShutdownStarted());
+
+    shutdown_event_ = std::make_unique<WaitableEvent>();
+
+    const bool tasks_are_blocking_shutdown = state_->StartShutdown();
+
+    // From now, if a thread causes the number of tasks blocking shutdown to
+    // become zero, it will call OnBlockingShutdownTasksComplete().
+
+    if (!tasks_are_blocking_shutdown) {
+      // If another thread posts a BLOCK_SHUTDOWN task at this moment, it will
+      // block until this method releases |shutdown_lock_|. Then, it will fail
+      // DCHECK(!shutdown_event_->IsSignaled()). This is the desired behavior
+      // because posting a BLOCK_SHUTDOWN task when TaskTracker::Shutdown() has
+      // started and no tasks are blocking shutdown isn't allowed.
+      shutdown_event_->Signal();
+      return;
+    }
+  }
+
+  // Remove the cap on the maximum number of sequences that can be scheduled
+  // concurrently. Done after starting shutdown to ensure that non-
+  // BLOCK_SHUTDOWN sequences don't get a chance to run and that BLOCK_SHUTDOWN
+  // sequences run on threads running with a normal priority.
+  for (int priority_index = static_cast<int>(TaskPriority::HIGHEST);
+       priority_index >= static_cast<int>(TaskPriority::LOWEST);
+       --priority_index) {
+    SetMaxNumScheduledSequences(std::numeric_limits<int>::max(),
+                                static_cast<TaskPriority>(priority_index));
+  }
+
+  // It is safe to access |shutdown_event_| without holding |lock_| because the
+  // pointer never changes after being set above.
+  {
+    base::ScopedAllowBaseSyncPrimitives allow_wait;
+    shutdown_event_->Wait();
+  }
+}
+
+void TaskTracker::SetMaxNumScheduledSequences(int max_scheduled_sequences,
+                                              TaskPriority task_priority) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  std::vector<PreemptedSequence> sequences_to_schedule;
+  int priority_index = static_cast<int>(task_priority);
+
+  {
+    AutoSchedulerLock auto_lock(preemption_state_[priority_index].lock);
+    preemption_state_[priority_index].max_scheduled_sequences =
+        max_scheduled_sequences;
+
+    while (preemption_state_[priority_index].current_scheduled_sequences <
+               max_scheduled_sequences &&
+           !preemption_state_[priority_index].preempted_sequences.empty()) {
+      sequences_to_schedule.push_back(
+          GetPreemptedSequenceToScheduleLockRequired(task_priority));
+    }
+  }
+
+  for (auto& sequence_to_schedule : sequences_to_schedule)
+    SchedulePreemptedSequence(std::move(sequence_to_schedule));
+}
+
+TaskTracker::PreemptedSequence
+TaskTracker::GetPreemptedSequenceToScheduleLockRequired(
+    TaskPriority task_priority) {
+  int priority_index = static_cast<int>(task_priority);
+
+  preemption_state_[priority_index].lock.AssertAcquired();
+  DCHECK(!preemption_state_[priority_index].preempted_sequences.empty());
+
+  ++preemption_state_[priority_index].current_scheduled_sequences;
+  DCHECK_LE(preemption_state_[priority_index].current_scheduled_sequences,
+            preemption_state_[priority_index].max_scheduled_sequences);
+
+  // The const_cast on top is okay since the PreemptedSequence is
+  // transactionnaly being popped from
+  // |preemption_state_[priority_index].preempted_sequences| right after and the
+  // move doesn't alter the sort order (a requirement for the Windows STL's
+  // consistency debug-checks for std::priority_queue::top()).
+  PreemptedSequence popped_sequence = std::move(const_cast<PreemptedSequence&>(
+      preemption_state_[priority_index].preempted_sequences.top()));
+  preemption_state_[priority_index].preempted_sequences.pop();
+  return popped_sequence;
+}
+
+void TaskTracker::SchedulePreemptedSequence(
+    PreemptedSequence sequence_to_schedule) {
+  DCHECK(sequence_to_schedule.observer);
+  sequence_to_schedule.observer->OnCanScheduleSequence(
+      std::move(sequence_to_schedule.sequence));
+}
+
 bool TaskTracker::HasIncompleteUndelayedTasksForTesting() const {
   return subtle::Acquire_Load(&num_incomplete_undelayed_tasks_) != 0;
 }
@@ -690,6 +869,56 @@
   }
 }
 
+scoped_refptr<Sequence> TaskTracker::ManageSequencesAfterRunningTask(
+    scoped_refptr<Sequence> just_ran_sequence,
+    CanScheduleSequenceObserver* observer,
+    TaskPriority task_priority) {
+  const TimeTicks next_task_sequenced_time =
+      just_ran_sequence ? just_ran_sequence->BeginTransaction()
+                              .GetSortKey()
+                              .next_task_sequenced_time()
+                        : TimeTicks();
+  PreemptedSequence sequence_to_schedule;
+  int priority_index = static_cast<int>(task_priority);
+
+  {
+    AutoSchedulerLock auto_lock(preemption_state_[priority_index].lock);
+
+    --preemption_state_[priority_index].current_scheduled_sequences;
+
+    const bool can_schedule_sequence =
+        preemption_state_[priority_index].current_scheduled_sequences <
+        preemption_state_[priority_index].max_scheduled_sequences;
+
+    if (just_ran_sequence) {
+      if (can_schedule_sequence &&
+          (preemption_state_[priority_index].preempted_sequences.empty() ||
+           preemption_state_[priority_index]
+                   .preempted_sequences.top()
+                   .next_task_sequenced_time > next_task_sequenced_time)) {
+        ++preemption_state_[priority_index].current_scheduled_sequences;
+        return just_ran_sequence;
+      }
+
+      preemption_state_[priority_index].preempted_sequences.emplace(
+          std::move(just_ran_sequence), next_task_sequenced_time, observer);
+    }
+
+    if (can_schedule_sequence &&
+        !preemption_state_[priority_index].preempted_sequences.empty()) {
+      sequence_to_schedule =
+          GetPreemptedSequenceToScheduleLockRequired(task_priority);
+    }
+  }
+
+  // |sequence_to_schedule.sequence| may be null if there was no preempted
+  // sequence.
+  if (sequence_to_schedule.sequence)
+    SchedulePreemptedSequence(std::move(sequence_to_schedule));
+
+  return nullptr;
+}
+
 void TaskTracker::CallFlushCallbackForTesting() {
   OnceClosure flush_callback;
   {
diff --git a/base/task/task_scheduler/task_tracker.h b/base/task/task_scheduler/task_tracker.h
index 2ce7f95..8dd9fe4 100644
--- a/base/task/task_scheduler/task_tracker.h
+++ b/base/task/task_scheduler/task_tracker.h
@@ -21,6 +21,7 @@
 #include "base/strings/string_piece.h"
 #include "base/synchronization/waitable_event.h"
 #include "base/task/common/task_annotator.h"
+#include "base/task/task_scheduler/can_schedule_sequence_observer.h"
 #include "base/task/task_scheduler/scheduler_lock.h"
 #include "base/task/task_scheduler/sequence.h"
 #include "base/task/task_scheduler/task.h"
@@ -34,42 +35,85 @@
 
 namespace internal {
 
-// Determines which tasks are allowed to run.
-enum class CanRunPolicy {
-  // All tasks are allowed to run.
-  kAll,
-  // Only USER_VISIBLE and USER_BLOCKING tasks are allowed to run.
-  kForegroundOnly,
-  // No tasks can run.
-  kNone,
-};
-
 // TaskTracker enforces policies that determines whether:
 // - A task can be added to a sequence (WillPostTask).
-// - Tasks for a given priority can run (CanRunPriority).
+// - A sequence can be scheduled (WillScheduleSequence).
 // - The next task in a scheduled sequence can run (RunAndPopNextTask).
 // TaskTracker also sets up the environment to run a task (RunAndPopNextTask)
 // and records metrics and trace events. This class is thread-safe.
+//
+// Life of a sequence:
+// (possible states: IDLE, PREEMPTED, SCHEDULED, RUNNING)
+//
+//                            Create a sequence
+//                                   |
+//  ------------------------> Sequence is IDLE
+//  |                                |
+//  |                     Add a task to the sequence
+//  |            (allowed by TaskTracker::WillPostTask)
+//  |                                |
+//  |               TaskTracker:WillScheduleSequence
+//  |           _____________________|_____________________
+//  |           |                                          |
+//  |    Returns true                                Returns false
+//  |           |                                          |
+//  |           |                                Sequence is PREEMPTED <----
+//  |           |                                          |               |
+//  |           |                            Eventually,                   |
+//  |           |                            CanScheduleSequenceObserver   |
+//  |           |                            is notified that the          |
+//  |           |                            sequence can be scheduled.    |
+//  |           |__________________________________________|               |
+//  |                               |                                      |
+//  |                   (*) Sequence is SCHEDULED                          |
+//  |                               |                                      |
+//  |                A thread is ready to run the next                     |
+//  |                      task in the sequence                            |
+//  |                               |                                      |
+//  |                TaskTracker::RunAndPopNextTask                        |
+//  |                A task from the sequence is run                       |
+//  |                      Sequence is RUNNING                             |
+//  |                               |                                      |
+//  |         ______________________|____                                  |
+//  |         |                          |                                 |
+//  |   Sequence is empty      Sequence has more tasks                     |
+//  |_________|             _____________|_______________                  |
+//                          |                            |                 |
+//                   Sequence can be            Sequence cannot be         |
+//                   scheduled                  scheduled at this          |
+//                          |                   moment                     |
+//                   Go back to (*)                      |_________________|
+//
+//
+// Note: A best-effort task is a task posted with TaskPriority::BEST_EFFORT. A
+// foreground task is a task posted with TaskPriority::USER_VISIBLE or
+// TaskPriority::USER_BLOCKING.
+//
+// TODO(fdoray): We want to allow disabling TaskPriority::BEST_EFFORT tasks in a
+// scope (e.g. during startup or page load), but we don't need a dynamic maximum
+// number of best-effort tasks. The code could probably be simplified if it
+// didn't support that. https://crbug.com/831835
 class BASE_EXPORT TaskTracker {
  public:
   // |histogram_label| is used as a suffix for histograms, it must not be empty.
+  // The first constructor sets the maximum number of TaskPriority::BEST_EFFORT
+  // sequences that can be scheduled concurrently to 0 if the
+  // --disable-best-effort-tasks flag is specified, max() otherwise. The second
+  // constructor sets it to |max_num_scheduled_best_effort_sequences|.
   TaskTracker(StringPiece histogram_label);
+  TaskTracker(StringPiece histogram_label,
+              int max_num_scheduled_best_effort_sequences);
 
   virtual ~TaskTracker();
 
-  // Initiates shutdown. Once this is called, only BLOCK_SHUTDOWN tasks will
-  // start running (doesn't affect tasks that are already running). This can
-  // only be called once.
-  void StartShutdown();
-
-  // Synchronously completes shutdown. StartShutdown() must be called first.
-  // Returns when:
+  // Synchronously shuts down the scheduler. Once this is called, only tasks
+  // posted with the BLOCK_SHUTDOWN behavior will be run. Returns when:
   // - All SKIP_ON_SHUTDOWN tasks that were already running have completed their
   //   execution.
   // - All posted BLOCK_SHUTDOWN tasks have completed their execution.
   // CONTINUE_ON_SHUTDOWN tasks still may be running after Shutdown returns.
   // This can only be called once.
-  void CompleteShutdown();
+  void Shutdown();
 
   // Waits until there are no incomplete undelayed tasks. May be called in tests
   // to validate that a condition is met after all undelayed tasks have run.
@@ -85,34 +129,45 @@
   // FlushAsyncForTesting() may be pending at any given time.
   void FlushAsyncForTesting(OnceClosure flush_callback);
 
-  // Sets the new CanRunPolicy policy, possibly affecting result of
-  // CanRunPriority(). The caller must wake up worker as appropriate so that
-  // tasks that are allowed to run by the new policy can be scheduled.
-  void SetCanRunPolicy(CanRunPolicy can_run_policy);
-
   // Informs this TaskTracker that |task| from a |shutdown_behavior| sequence
   // is about to be posted. Returns true if this operation is allowed (|task|
   // should be posted if-and-only-if it is). This method may also modify
   // metadata on |task| if desired.
   bool WillPostTask(Task* task, TaskShutdownBehavior shutdown_behavior);
 
-  // Returns true if a task with |priority| can run under to the current policy.
-  bool CanRunPriority(TaskPriority priority) const;
+  // Informs this TaskTracker that the Sequence locked by |sequence_transaction|
+  // is about to be scheduled. If this returns true, it is expected that
+  // RunAndPopNextTask() will soon be called with the Sequence as argument.
+  // Otherwise, RunAndPopNextTask() must not be called with the Sequence as
+  // argument until |observer| is notified that the Sequence can be scheduled
+  // (the caller doesn't need to keep a pointer to the Sequence; it will be
+  // included in the notification to |observer|). WillPostTask() must have
+  // allowed the task in front of the Sequence to be posted before this is
+  // called. |observer| is only required if the priority of the Sequence is
+  // TaskPriority::BEST_EFFORT.
+  bool WillScheduleSequence(const Sequence::Transaction& sequence_transaction,
+                            CanScheduleSequenceObserver* observer);
 
   // Runs the next task in |sequence| unless the current shutdown state prevents
   // that. Then, pops the task from |sequence| (even if it didn't run). Returns
-  // |sequence| if non-empty after popping a task from it (which indicates that
-  // it should be reenqueued). WillPostTask() must have allowed the task in
-  // front of |sequence| to be posted before this is called.
-  scoped_refptr<Sequence> RunAndPopNextTask(scoped_refptr<Sequence> sequence);
+  // |sequence| if it can be rescheduled immediately. If |sequence| is non-empty
+  // after popping a task from it but it can't be rescheduled immediately, it
+  // will be handed back to |observer| when it can be rescheduled.
+  // WillPostTask() must have allowed the task in front of |sequence| to be
+  // posted before this is called. Also, WillScheduleSequence(),
+  // RunAndPopNextTask() or CanScheduleSequenceObserver::OnCanScheduleSequence()
+  // must have allowed |sequence| to be (re)scheduled.
+  scoped_refptr<Sequence> RunAndPopNextTask(
+      scoped_refptr<Sequence> sequence,
+      CanScheduleSequenceObserver* observer);
 
-  // Returns true once shutdown has started (StartShutdown() was called).
-  // Note: sequential consistency with the thread calling StartShutdown() isn't
-  // guaranteed by this call.
+  // Returns true once shutdown has started (Shutdown() has been called but
+  // might not have returned). Note: sequential consistency with the thread
+  // calling Shutdown() (or SetHasShutdownStartedForTesting()) isn't guaranteed
+  // by this call.
   bool HasShutdownStarted() const;
 
-  // Returns true if shutdown has completed (StartShutdown() was called and
-  // no tasks are blocking shutdown).
+  // Returns true if shutdown has completed (Shutdown() has returned).
   bool IsShutdownComplete() const;
 
   enum class LatencyHistogramType {
@@ -125,6 +180,11 @@
     HEARTBEAT_LATENCY,
   };
 
+  // Causes HasShutdownStarted() to return true. Unlike when Shutdown() returns,
+  // IsShutdownComplete() won't return true after this returns. Shutdown()
+  // cannot be called after this.
+  void SetHasShutdownStartedForTesting();
+
   // Records two histograms
   // 1. TaskScheduler.[label].HeartbeatLatencyMicroseconds.[suffix]:
   //    Now() - posted_time
@@ -145,6 +205,13 @@
     return tracked_ref_factory_.GetTrackedRef();
   }
 
+  // Enables/disables an execution fence. When the fence is released,
+  // reschedules the sequences that were preempted by the fence.
+  void SetExecutionFenceEnabled(bool execution_fence_enabled);
+
+  // Returns the number of preempted sequences of a given priority.
+  size_t GetPreemptedSequenceCountForTesting(TaskPriority priority);
+
  protected:
   // Runs and deletes |task| if |can_run_task| is true. Otherwise, just deletes
   // |task|. |task| is always deleted in the environment where it runs or would
@@ -164,9 +231,62 @@
 
  private:
   class State;
+  struct PreemptedSequence;
+
+  struct PreemptionState {
+    PreemptionState();
+    ~PreemptionState();
+
+    // A priority queue of sequences that are waiting to be scheduled. Use
+    // std::greater so that the sequence which contains the task that has been
+    // posted the earliest is on top of the priority queue.
+    std::priority_queue<PreemptedSequence,
+                        std::vector<PreemptedSequence>,
+                        std::greater<PreemptedSequence>>
+        preempted_sequences;
+
+    // Maximum number of sequences that can that be scheduled concurrently.
+    int max_scheduled_sequences = std::numeric_limits<int>::max();
+
+    // Caches the |max_scheduled_sequences| before enabling the execution fence.
+    int max_scheduled_sequences_before_fence = 0;
+
+    // Number of currently scheduled sequences.
+    int current_scheduled_sequences = 0;
+
+    // Synchronizes accesses to other members.
+    // |max_scheduled_sequences| and |max_scheduled_sequences_before_fence| are
+    // only written from the main sequence within the scope of |lock|. Reads can
+    // happen on the main sequence without holding |lock|, or on any other
+    // sequence while holding |lock|.
+    SchedulerLock lock;
+
+   private:
+    DISALLOW_COPY_AND_ASSIGN(PreemptionState);
+  };
 
   void PerformShutdown();
 
+  // Sets the maximum number of sequences of priority |priority| that can be
+  // scheduled concurrently to |max_scheduled_sequences|.
+  void SetMaxNumScheduledSequences(int max_scheduled_sequences,
+                                   TaskPriority priority);
+
+  // Pops the next sequence in |preemption_state_[priority].preempted_sequences|
+  // and increments |preemption_state_[priority].current_scheduled_sequences|.
+  // Must only be called in the scope of |preemption_state_[priority].lock|,
+  // with |preemption_state_[priority].preempted_sequences| non-empty. The
+  // caller must forward the returned sequence to the associated
+  // CanScheduleSequenceObserver as soon as |preemption_state_[priority].lock|
+  // is released.
+  PreemptedSequence GetPreemptedSequenceToScheduleLockRequired(
+      TaskPriority priority);
+
+  // Schedules |sequence_to_schedule.sequence| using
+  // |sequence_to_schedule.observer|. Does not verify that the sequence is
+  // allowed to be scheduled.
+  void SchedulePreemptedSequence(PreemptedSequence sequence_to_schedule);
+
   // Called before WillPostTask() informs the tracing system that a task has
   // been posted. Updates |num_tasks_blocking_shutdown_| if necessary and
   // returns true if the current shutdown state allows the task to be posted.
@@ -190,6 +310,24 @@
   // if it reaches zero.
   void DecrementNumIncompleteUndelayedTasks();
 
+  // To be called after running a task from |just_ran_sequence|. Performs the
+  // following actions:
+  //  - If |just_ran_sequence| is non-null:
+  //    - returns it if it should be rescheduled by the caller of
+  //      RunAndPopNextTask(), i.e. its next task is set to run earlier than the
+  //      earliest currently preempted sequence.
+  //    - Otherwise |just_ran_sequence| is preempted and the next preempted
+  //      sequence is scheduled (|observer| will be notified when
+  //      |just_ran_sequence| should be scheduled again).
+  //  - If |just_ran_sequence| is null (RunAndPopNextTask() just popped the last
+  //    task from it):
+  //    - the next preempeted sequence (if any) is scheduled.
+  //  - In all cases: adjusts the number of scheduled sequences accordingly.
+  scoped_refptr<Sequence> ManageSequencesAfterRunningTask(
+      scoped_refptr<Sequence> just_ran_sequence,
+      CanScheduleSequenceObserver* observer,
+      TaskPriority task_priority);
+
   // Calls |flush_callback_for_testing_| if one is available in a lock-safe
   // manner.
   void CallFlushCallbackForTesting();
@@ -222,9 +360,6 @@
   // returns.
   subtle::Atomic32 num_incomplete_undelayed_tasks_ = 0;
 
-  // Global policy the determines result of CanRunPriority().
-  std::atomic<CanRunPolicy> can_run_policy_;
-
   // Lock associated with |flush_cv_|. Partially synchronizes access to
   // |num_incomplete_undelayed_tasks_|. Full synchronization isn't needed
   // because it's atomic, but synchronization is needed to coordinate waking and
@@ -265,6 +400,19 @@
   HistogramBase* const
       num_tasks_run_while_queuing_histograms_[kNumTaskPriorities][2];
 
+  PreemptionState preemption_state_[kNumTaskPriorities];
+
+#if DCHECK_IS_ON()
+  // Indicates whether to prevent tasks running.
+  bool execution_fence_enabled_ = false;
+#endif
+
+  // Enforces that |max_scheduled_sequences| and
+  // |max_scheduled_sequences_before_fence| in PreemptedState are only written
+  // on the main sequence (determined by the first call to
+  // SetMaxNumScheduledSequences or SetExecutionFenceEnabled).
+  SEQUENCE_CHECKER(sequence_checker_);
+
   // Ensures all state (e.g. dangling cleaned up workers) is coalesced before
   // destroying the TaskTracker (e.g. in test environments).
   // Ref. https://crbug.com/827615.
diff --git a/base/task/task_scheduler/task_tracker_posix_unittest.cc b/base/task/task_scheduler/task_tracker_posix_unittest.cc
index f0a49dd..8d35b08 100644
--- a/base/task/task_scheduler/task_tracker_posix_unittest.cc
+++ b/base/task/task_scheduler/task_tracker_posix_unittest.cc
@@ -61,9 +61,11 @@
   EXPECT_TRUE(tracker_.WillPostTask(&task, default_traits.shutdown_behavior()));
 
   auto sequence = test::CreateSequenceWithTask(std::move(task), default_traits);
+  EXPECT_TRUE(
+      tracker_.WillScheduleSequence(sequence->BeginTransaction(), nullptr));
   // Expect RunAndPopNextTask to return nullptr since |sequence| is empty after
   // popping a task from it.
-  EXPECT_FALSE(tracker_.RunAndPopNextTask(sequence));
+  EXPECT_FALSE(tracker_.RunAndPopNextTask(sequence, nullptr));
 
   EXPECT_TRUE(did_run);
 }
@@ -85,10 +87,12 @@
   auto sequence = test::CreateSequenceWithTask(
       std::move(task), default_traits, MakeRefCounted<NullTaskRunner>(),
       TaskSourceExecutionMode::kSequenced);
+  EXPECT_TRUE(
+      tracker_.WillScheduleSequence(sequence->BeginTransaction(), nullptr));
 
   // Expect RunAndPopNextTask to return nullptr since |sequence| is empty after
   // popping a task from it.
-  EXPECT_FALSE(tracker_.RunAndPopNextTask(sequence));
+  EXPECT_FALSE(tracker_.RunAndPopNextTask(sequence, nullptr));
 
   // Join the service thread to make sure that the read watch is registered and
   // unregistered before file descriptors are closed.
diff --git a/base/task/task_scheduler/task_tracker_unittest.cc b/base/task/task_scheduler/task_tracker_unittest.cc
index a5ef426..798dd823 100644
--- a/base/task/task_scheduler/task_tracker_unittest.cc
+++ b/base/task/task_scheduler/task_tracker_unittest.cc
@@ -48,6 +48,15 @@
 
 constexpr size_t kLoadTestNumIterations = 75;
 
+class MockCanScheduleSequenceObserver : public CanScheduleSequenceObserver {
+ public:
+  void OnCanScheduleSequence(scoped_refptr<Sequence> sequence) override {
+    MockOnCanScheduleSequence(sequence.get());
+  }
+
+  MOCK_METHOD1(MockOnCanScheduleSequence, void(Sequence*));
+};
+
 // Invokes a closure asynchronously.
 class CallbackThread : public SimpleThread {
  public:
@@ -123,10 +132,16 @@
         (action_ == Action::RUN || action_ == Action::WILL_POST_AND_RUN)) {
       EXPECT_TRUE(owned_task_.task);
 
+      testing::StrictMock<MockCanScheduleSequenceObserver>
+          never_notified_observer;
+      auto sequence =
+          test::CreateSequenceWithTask(std::move(owned_task_), traits_);
+      ASSERT_TRUE(tracker_->WillScheduleSequence(sequence->BeginTransaction(),
+                                                 &never_notified_observer));
       // Expect RunAndPopNextTask to return nullptr since |sequence| is empty
       // after popping a task from it.
-      EXPECT_FALSE(tracker_->RunAndPopNextTask(
-          test::CreateSequenceWithTask(std::move(owned_task_), traits_)));
+      EXPECT_FALSE(tracker_->RunAndPopNextTask(std::move(sequence),
+                                               &never_notified_observer));
     }
   }
 
@@ -167,19 +182,22 @@
   }
 
   void DispatchAndRunTaskWithTracker(Task task, const TaskTraits& traits) {
-    tracker_.RunAndPopNextTask(
-        test::CreateSequenceWithTask(std::move(task), traits));
+    auto sequence = test::CreateSequenceWithTask(std::move(task), traits);
+    ASSERT_TRUE(tracker_.WillScheduleSequence(sequence->BeginTransaction(),
+                                              &never_notified_observer_));
+    tracker_.RunAndPopNextTask(std::move(sequence), &never_notified_observer_);
   }
 
-  // Calls tracker_->CompleteShutdown() on a new thread and expects it to block.
-  void ExpectAsyncCompleteShutdownBlocks() {
+  // Calls tracker_->Shutdown() on a new thread. When this returns, Shutdown()
+  // method has been entered on the new thread, but it hasn't necessarily
+  // returned.
+  void CallShutdownAsync() {
     ASSERT_FALSE(thread_calling_shutdown_);
-    ASSERT_TRUE(tracker_.HasShutdownStarted());
-    thread_calling_shutdown_ = std::make_unique<CallbackThread>(
-        Bind(&TaskTracker::CompleteShutdown, Unretained(&tracker_)));
+    thread_calling_shutdown_.reset(new CallbackThread(
+        Bind(&TaskTracker::Shutdown, Unretained(&tracker_))));
     thread_calling_shutdown_->Start();
-    PlatformThread::Sleep(TestTimeouts::tiny_timeout());
-    VerifyAsyncShutdownInProgress();
+    while (!tracker_.HasShutdownStarted())
+      PlatformThread::YieldCurrentThread();
   }
 
   void WaitForAsyncIsShutdownComplete() {
@@ -221,6 +239,7 @@
   }
 
   TaskTracker tracker_ = {"Test"};
+  testing::StrictMock<MockCanScheduleSequenceObserver> never_notified_observer_;
 
  private:
   void RunTaskCallback() {
@@ -278,7 +297,7 @@
   EXPECT_EQ(1U, NumTasksExecuted());
 
   // Shutdown() shouldn't block.
-  test::ShutdownTaskTracker(&tracker_);
+  tracker_.Shutdown();
 }
 
 TEST_P(TaskSchedulerTaskTrackerTest, WillPostAndRunLongTaskBeforeShutdown) {
@@ -310,14 +329,14 @@
   task_running.Wait();
 
   // Initiate shutdown after the task has started to run.
-  tracker_.StartShutdown();
+  CallShutdownAsync();
 
   if (GetParam() == TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN) {
     // Shutdown should complete even with a CONTINUE_ON_SHUTDOWN in progress.
-    tracker_.CompleteShutdown();
+    WAIT_FOR_ASYNC_SHUTDOWN_COMPLETED();
   } else {
     // Shutdown should block with any non CONTINUE_ON_SHUTDOWN task in progress.
-    ExpectAsyncCompleteShutdownBlocks();
+    VERIFY_ASYNC_SHUTDOWN_IN_PROGRESS();
   }
 
   // Unblock the task.
@@ -340,9 +359,9 @@
   EXPECT_TRUE(tracker_.WillPostTask(&block_shutdown_task,
                                     TaskShutdownBehavior::BLOCK_SHUTDOWN));
 
-  // Start shutdown and try to complete it asynchronously.
-  tracker_.StartShutdown();
-  ExpectAsyncCompleteShutdownBlocks();
+  // Call Shutdown() asynchronously.
+  CallShutdownAsync();
+  VERIFY_ASYNC_SHUTDOWN_IN_PROGRESS();
 
   // Try to run |task|. It should only run it it's BLOCK_SHUTDOWN. Otherwise it
   // should be discarded.
@@ -365,13 +384,12 @@
   Task task(CreateTask());
   EXPECT_TRUE(tracker_.WillPostTask(&task, GetParam()));
 
-  // Start shutdown.
-  tracker_.StartShutdown();
+  // Call Shutdown() asynchronously.
+  CallShutdownAsync();
   EXPECT_EQ(0U, NumTasksExecuted());
 
   if (GetParam() == TaskShutdownBehavior::BLOCK_SHUTDOWN) {
-    // Verify that CompleteShutdown() blocks.
-    ExpectAsyncCompleteShutdownBlocks();
+    VERIFY_ASYNC_SHUTDOWN_IN_PROGRESS();
 
     // Run the task to unblock shutdown.
     DispatchAndRunTaskWithTracker(std::move(task), GetParam());
@@ -382,7 +400,7 @@
     // shutdown after shutdown because Shutdown() won't return if there are
     // pending BLOCK_SHUTDOWN tasks.
   } else {
-    tracker_.CompleteShutdown();
+    WAIT_FOR_ASYNC_SHUTDOWN_COMPLETED();
 
     // The task shouldn't be allowed to run after shutdown.
     DispatchAndRunTaskWithTracker(std::move(task), GetParam());
@@ -397,8 +415,9 @@
   EXPECT_TRUE(tracker_.WillPostTask(&block_shutdown_task,
                                     TaskShutdownBehavior::BLOCK_SHUTDOWN));
 
-  // Start shutdown.
-  tracker_.StartShutdown();
+  // Call Shutdown() asynchronously.
+  CallShutdownAsync();
+  VERIFY_ASYNC_SHUTDOWN_IN_PROGRESS();
 
   if (GetParam() == TaskShutdownBehavior::BLOCK_SHUTDOWN) {
     // Inform |task_tracker_| that a BLOCK_SHUTDOWN task will be posted.
@@ -417,10 +436,8 @@
     // Don't try to run the task, because it wasn't allowed to be posted.
   }
 
-  // Verify that CompleteShutdown() blocks.
-  ExpectAsyncCompleteShutdownBlocks();
-
   // Unblock shutdown by running |block_shutdown_task|.
+  VERIFY_ASYNC_SHUTDOWN_IN_PROGRESS();
   DispatchAndRunTaskWithTracker(std::move(block_shutdown_task),
                                 TaskShutdownBehavior::BLOCK_SHUTDOWN);
   EXPECT_EQ(GetParam() == TaskShutdownBehavior::BLOCK_SHUTDOWN ? 2U : 1U,
@@ -429,7 +446,7 @@
 }
 
 TEST_P(TaskSchedulerTaskTrackerTest, WillPostAfterShutdown) {
-  test::ShutdownTaskTracker(&tracker_);
+  tracker_.Shutdown();
 
   Task task(CreateTask());
 
@@ -509,10 +526,13 @@
   EXPECT_FALSE(ThreadTaskRunnerHandle::IsSet());
   EXPECT_FALSE(SequencedTaskRunnerHandle::IsSet());
 
+  testing::StrictMock<MockCanScheduleSequenceObserver> never_notified_observer;
   auto sequence = test::CreateSequenceWithTask(
       std::move(verify_task), traits, std::move(task_runner), execution_mode);
 
-  tracker->RunAndPopNextTask(std::move(sequence));
+  ASSERT_TRUE(tracker->WillScheduleSequence(sequence->BeginTransaction(),
+                                            &never_notified_observer));
+  tracker->RunAndPopNextTask(std::move(sequence), &never_notified_observer);
 
   // TaskRunnerHandle state is reset outside of task's scope.
   EXPECT_FALSE(ThreadTaskRunnerHandle::IsSet());
@@ -752,7 +772,7 @@
 
   // Shutdown() should return immediately since there are no pending
   // BLOCK_SHUTDOWN tasks.
-  test::ShutdownTaskTracker(&tracker_);
+  tracker_.Shutdown();
 
   // FlushForTesting() should return immediately after shutdown, even if an
   // undelayed task hasn't run.
@@ -769,7 +789,7 @@
 
   // Shutdown() should return immediately since there are no pending
   // BLOCK_SHUTDOWN tasks.
-  test::ShutdownTaskTracker(&tracker_);
+  tracker_.Shutdown();
 
   // FlushForTesting() should callback immediately after shutdown, even if an
   // undelayed task hasn't run.
@@ -796,7 +816,7 @@
 
   // Shutdown() should return immediately since there are no pending
   // BLOCK_SHUTDOWN tasks.
-  test::ShutdownTaskTracker(&tracker_);
+  tracker_.Shutdown();
 
   // FlushForTesting() should now return, even if an undelayed task hasn't run.
   WAIT_FOR_ASYNC_FLUSH_RETURNED();
@@ -820,7 +840,7 @@
 
   // Shutdown() should return immediately since there are no pending
   // BLOCK_SHUTDOWN tasks.
-  test::ShutdownTaskTracker(&tracker_);
+  tracker_.Shutdown();
 
   // FlushAsyncForTesting() should now callback, even if an undelayed task
   // hasn't run.
@@ -849,7 +869,7 @@
 
   // Since the delayed task doesn't block shutdown, a call to Shutdown() should
   // not hang.
-  test::ShutdownTaskTracker(&tracker_);
+  tracker_.Shutdown();
 }
 
 INSTANTIATE_TEST_SUITE_P(
@@ -888,9 +908,11 @@
     sequence_transaction.PushTask(std::move(task));
 
     EXPECT_FALSE(SequenceToken::GetForCurrentThread().IsValid());
+    ASSERT_TRUE(tracker_.WillScheduleSequence(sequence_transaction,
+                                              &never_notified_observer_));
   }
 
-  tracker_.RunAndPopNextTask(std::move(sequence));
+  tracker_.RunAndPopNextTask(std::move(sequence), &never_notified_observer_);
   EXPECT_FALSE(SequenceToken::GetForCurrentThread().IsValid());
 }
 
@@ -925,7 +947,7 @@
   EXPECT_EQ(kLoadTestNumIterations * 3, NumTasksExecuted());
 
   // Should return immediately because no tasks are blocking shutdown.
-  test::ShutdownTaskTracker(&tracker_);
+  tracker_.Shutdown();
 }
 
 TEST_F(TaskSchedulerTaskTrackerTest,
@@ -968,9 +990,8 @@
   for (const auto& thread : post_threads)
     thread->Join();
 
-  // Start shutdown and try to complete shutdown asynchronously.
-  tracker_.StartShutdown();
-  ExpectAsyncCompleteShutdownBlocks();
+  // Call Shutdown() asynchronously.
+  CallShutdownAsync();
 
   // Run tasks asynchronously.
   std::vector<std::unique_ptr<ThreadPostingAndRunningTask>> run_threads;
@@ -1009,9 +1030,8 @@
   EXPECT_TRUE(tracker_.WillPostTask(&block_shutdown_task,
                                     TaskShutdownBehavior::BLOCK_SHUTDOWN));
 
-  // Start shutdown and try to complete it asynchronously.
-  tracker_.StartShutdown();
-  ExpectAsyncCompleteShutdownBlocks();
+  // Call Shutdown() asynchronously.
+  CallShutdownAsync();
 
   // Post and run tasks asynchronously.
   std::vector<std::unique_ptr<ThreadPostingAndRunningTask>> threads;
@@ -1067,8 +1087,335 @@
 
   scoped_refptr<Sequence> sequence =
       test::CreateSequenceWithTask(std::move(task_1), default_traits);
-  sequence->BeginTransaction().PushTask(std::move(task_2));
-  EXPECT_EQ(sequence, tracker_.RunAndPopNextTask(sequence));
+  {
+    Sequence::Transaction sequence_transaction(sequence->BeginTransaction());
+    sequence_transaction.PushTask(std::move(task_2));
+    EXPECT_TRUE(tracker_.WillScheduleSequence(sequence_transaction, nullptr));
+  }
+
+  EXPECT_EQ(sequence, tracker_.RunAndPopNextTask(sequence, nullptr));
+}
+
+namespace {
+
+void TestWillScheduleBestEffortSequenceWithMaxBestEffortSequences(
+    int max_num_scheduled_best_effort_sequences,
+    TaskTracker& tracker) {
+  // Simulate posting |max_num_scheduled_best_effort_sequences| best-effort
+  // tasks and scheduling the associated sequences. This should succeed.
+  std::vector<scoped_refptr<Sequence>> scheduled_sequences;
+  testing::StrictMock<MockCanScheduleSequenceObserver> never_notified_observer;
+  TaskTraits best_effort_traits = TaskTraits(TaskPriority::BEST_EFFORT);
+  for (int i = 0; i < max_num_scheduled_best_effort_sequences; ++i) {
+    Task task(FROM_HERE, DoNothing(), TimeDelta());
+    EXPECT_TRUE(
+        tracker.WillPostTask(&task, best_effort_traits.shutdown_behavior()));
+    scoped_refptr<Sequence> sequence =
+        test::CreateSequenceWithTask(std::move(task), best_effort_traits);
+    ASSERT_TRUE(tracker.WillScheduleSequence(sequence->BeginTransaction(),
+                                             &never_notified_observer));
+    scheduled_sequences.push_back(std::move(sequence));
+  }
+
+  // Simulate posting extra best-effort tasks and scheduling the associated
+  // sequences. This should fail because the maximum number of best-effort
+  // sequences that can be scheduled concurrently is already reached.
+  std::vector<std::unique_ptr<bool>> extra_tasks_did_run;
+  std::vector<
+      std::unique_ptr<testing::StrictMock<MockCanScheduleSequenceObserver>>>
+      extra_observers;
+  std::vector<scoped_refptr<Sequence>> extra_sequences;
+  for (int i = 0; i < max_num_scheduled_best_effort_sequences; ++i) {
+    extra_tasks_did_run.push_back(std::make_unique<bool>());
+    Task extra_task(
+        FROM_HERE,
+        BindOnce([](bool* extra_task_did_run) { *extra_task_did_run = true; },
+                 Unretained(extra_tasks_did_run.back().get())),
+        TimeDelta());
+    EXPECT_TRUE(tracker.WillPostTask(&extra_task,
+                                     best_effort_traits.shutdown_behavior()));
+    extra_sequences.push_back(test::CreateSequenceWithTask(
+        std::move(extra_task), best_effort_traits));
+    extra_observers.push_back(
+        std::make_unique<
+            testing::StrictMock<MockCanScheduleSequenceObserver>>());
+    EXPECT_FALSE(
+        tracker.WillScheduleSequence(extra_sequences.back()->BeginTransaction(),
+                                     extra_observers.back().get()));
+  }
+
+  // Run the sequences scheduled at the beginning of the test. Expect an
+  // observer from |extra_observer| to be notified every time a task finishes to
+  // run.
+  for (int i = 0; i < max_num_scheduled_best_effort_sequences; ++i) {
+    EXPECT_CALL(*extra_observers[i].get(),
+                MockOnCanScheduleSequence(extra_sequences[i].get()));
+    EXPECT_FALSE(tracker.RunAndPopNextTask(scheduled_sequences[i],
+                                           &never_notified_observer));
+    testing::Mock::VerifyAndClear(extra_observers[i].get());
+  }
+
+  // Run the extra sequences.
+  for (int i = 0; i < max_num_scheduled_best_effort_sequences; ++i) {
+    EXPECT_FALSE(*extra_tasks_did_run[i]);
+    EXPECT_FALSE(tracker.RunAndPopNextTask(extra_sequences[i],
+                                           &never_notified_observer));
+    EXPECT_TRUE(*extra_tasks_did_run[i]);
+  }
+}
+
+}  // namespace
+
+// Verify that WillScheduleSequence() returns nullptr when it receives a
+// best-effort sequence and the maximum number of best-effort sequences that can
+// be scheduled concurrently is reached. Verify that an observer is notified
+// when a best-effort sequence can be scheduled (i.e. when one of the previously
+// scheduled best-effort sequences has run).
+TEST_F(TaskSchedulerTaskTrackerTest,
+       WillScheduleBestEffortSequenceWithMaxBestEffortSequences) {
+  constexpr int kMaxNumScheduledBestEffortSequences = 2;
+  TaskTracker tracker("Test", kMaxNumScheduledBestEffortSequences);
+  TestWillScheduleBestEffortSequenceWithMaxBestEffortSequences(
+      kMaxNumScheduledBestEffortSequences, tracker);
+}
+
+// Verify that providing a cap for the number of BEST_EFFORT tasks to the
+// constructor of TaskTracker is compatible with using an execution fence.
+TEST_F(TaskSchedulerTaskTrackerTest,
+       WillScheduleBestEffortSequenceWithMaxBestEffortSequencesAndFence) {
+  constexpr int kMaxNumScheduledBestEffortSequences = 2;
+  TaskTracker tracker("Test", kMaxNumScheduledBestEffortSequences);
+  tracker.SetExecutionFenceEnabled(true);
+  tracker.SetExecutionFenceEnabled(false);
+  TestWillScheduleBestEffortSequenceWithMaxBestEffortSequences(
+      kMaxNumScheduledBestEffortSequences, tracker);
+}
+
+namespace {
+
+void SetBool(bool* arg) {
+  ASSERT_TRUE(arg);
+  EXPECT_FALSE(*arg);
+  *arg = true;
+}
+
+}  // namespace
+
+// Verify that enabling the ScopedExecutionFence will prevent
+// WillScheduleSequence() returning sequence.
+TEST_F(TaskSchedulerTaskTrackerTest,
+       WillScheduleSequenceWithScopedExecutionFence) {
+  TaskTraits default_traits = {};
+  Task task_a(FROM_HERE, DoNothing(), TimeDelta());
+  EXPECT_TRUE(
+      tracker_.WillPostTask(&task_a, default_traits.shutdown_behavior()));
+  scoped_refptr<Sequence> sequence_a =
+      test::CreateSequenceWithTask(std::move(task_a), default_traits);
+  testing::StrictMock<MockCanScheduleSequenceObserver> never_notified_observer;
+  EXPECT_TRUE(tracker_.WillScheduleSequence(sequence_a->BeginTransaction(),
+                                            &never_notified_observer));
+
+  // Verify that WillScheduleSequence() returns nullptr for foreground sequence
+  // when the ScopedExecutionFence is enabled.
+  tracker_.SetExecutionFenceEnabled(true);
+  bool task_b_1_did_run = false;
+  Task task_b_1(FROM_HERE, BindOnce(&SetBool, Unretained(&task_b_1_did_run)),
+                TimeDelta());
+  EXPECT_TRUE(
+      tracker_.WillPostTask(&task_b_1, default_traits.shutdown_behavior()));
+  scoped_refptr<Sequence> sequence_b =
+      test::CreateSequenceWithTask(std::move(task_b_1), default_traits);
+  testing::StrictMock<MockCanScheduleSequenceObserver> observer_b_1;
+  EXPECT_EQ(0u, tracker_.GetPreemptedSequenceCountForTesting(
+                    TaskPriority::BEST_EFFORT));
+  EXPECT_FALSE(tracker_.WillScheduleSequence(sequence_b->BeginTransaction(),
+                                             &observer_b_1));
+  EXPECT_EQ(1u, tracker_.GetPreemptedSequenceCountForTesting(
+                    TaskPriority::USER_VISIBLE));
+
+  bool task_b_2_did_run = false;
+  Task task_b_2(FROM_HERE, BindOnce(&SetBool, Unretained(&task_b_2_did_run)),
+                TimeDelta());
+  TaskTraits best_effort_traits = TaskTraits(TaskPriority::BEST_EFFORT);
+  EXPECT_TRUE(
+      tracker_.WillPostTask(&task_b_2, best_effort_traits.shutdown_behavior()));
+  sequence_b->BeginTransaction().PushTask(std::move(task_b_2));
+  testing::StrictMock<MockCanScheduleSequenceObserver> observer_b_2;
+  EXPECT_FALSE(tracker_.WillScheduleSequence(sequence_b->BeginTransaction(),
+                                             &observer_b_2));
+  // The TaskPriority of the Sequence is unchanged by posting new tasks to it.
+  EXPECT_EQ(2u, tracker_.GetPreemptedSequenceCountForTesting(
+                    TaskPriority::USER_VISIBLE));
+
+  // Verify that WillScheduleSequence() returns nullptr for best-effort sequence
+  // when the ScopedExecutionFence is enabled.
+  bool task_c_did_run = false;
+  Task task_c(FROM_HERE, BindOnce(&SetBool, Unretained(&task_c_did_run)),
+              TimeDelta());
+  EXPECT_TRUE(
+      tracker_.WillPostTask(&task_c, best_effort_traits.shutdown_behavior()));
+  scoped_refptr<Sequence> sequence_c =
+      test::CreateSequenceWithTask(std::move(task_c), best_effort_traits);
+  testing::StrictMock<MockCanScheduleSequenceObserver> observer_c;
+  EXPECT_FALSE(tracker_.WillScheduleSequence(sequence_c->BeginTransaction(),
+                                             &observer_c));
+  EXPECT_EQ(1u, tracker_.GetPreemptedSequenceCountForTesting(
+                    TaskPriority::BEST_EFFORT));
+
+  // Verifies that the sequences preempted when the fence is on are rescheduled
+  // right after the fence is released.
+  EXPECT_CALL(observer_b_1, MockOnCanScheduleSequence(sequence_b.get()));
+  EXPECT_CALL(observer_b_2, MockOnCanScheduleSequence(sequence_b.get()));
+  EXPECT_CALL(observer_c, MockOnCanScheduleSequence(sequence_c.get()));
+  tracker_.SetExecutionFenceEnabled(false);
+  testing::Mock::VerifyAndClear(&observer_b_1);
+  testing::Mock::VerifyAndClear(&observer_b_2);
+  testing::Mock::VerifyAndClear(&observer_c);
+  EXPECT_EQ(0u, tracker_.GetPreemptedSequenceCountForTesting(
+                    TaskPriority::USER_VISIBLE));
+  EXPECT_EQ(0u, tracker_.GetPreemptedSequenceCountForTesting(
+                    TaskPriority::BEST_EFFORT));
+
+  // Runs the sequences and verifies the tasks are done.
+  EXPECT_FALSE(
+      tracker_.RunAndPopNextTask(sequence_a, &never_notified_observer));
+
+  EXPECT_FALSE(task_b_1_did_run);
+  EXPECT_EQ(sequence_b, tracker_.RunAndPopNextTask(sequence_b, &observer_b_1));
+  EXPECT_TRUE(task_b_1_did_run);
+
+  EXPECT_FALSE(task_b_2_did_run);
+  EXPECT_FALSE(tracker_.RunAndPopNextTask(sequence_b, &observer_b_2));
+  EXPECT_TRUE(task_b_2_did_run);
+
+  EXPECT_FALSE(task_c_did_run);
+  EXPECT_FALSE(tracker_.RunAndPopNextTask(sequence_c, &observer_c));
+  EXPECT_TRUE(task_c_did_run);
+
+  // Verify that WillScheduleSequence() returns the sequence when the
+  // ScopedExecutionFence isn't enabled.
+  Task task_d(FROM_HERE, DoNothing(), TimeDelta());
+  EXPECT_TRUE(
+      tracker_.WillPostTask(&task_d, default_traits.shutdown_behavior()));
+  scoped_refptr<Sequence> sequence_d =
+      test::CreateSequenceWithTask(std::move(task_d), default_traits);
+  EXPECT_TRUE(tracker_.WillScheduleSequence(sequence_d->BeginTransaction(),
+                                            &never_notified_observer));
+}
+
+// Verify that RunAndPopNextTask() doesn't reschedule the best-effort sequence
+// it was assigned if there is a preempted best-effort sequence with an earlier
+// sequence time (compared to the next task in the sequence assigned to
+// RunAndPopNextTask()).
+TEST_F(TaskSchedulerTaskTrackerTest,
+       RunNextBestEffortTaskWithEarlierPendingBestEffortTask) {
+  constexpr int kMaxNumScheduledBestEffortSequences = 1;
+  TaskTracker tracker("Test", kMaxNumScheduledBestEffortSequences);
+  testing::StrictMock<MockCanScheduleSequenceObserver> never_notified_observer;
+  TaskTraits best_effort_traits = TaskTraits(TaskPriority::BEST_EFFORT);
+
+  // Simulate posting a best-effort task and scheduling the associated sequence.
+  // This should succeed.
+  bool task_a_1_did_run = false;
+  Task task_a_1(FROM_HERE, BindOnce(&SetBool, Unretained(&task_a_1_did_run)),
+                TimeDelta());
+  EXPECT_TRUE(
+      tracker.WillPostTask(&task_a_1, best_effort_traits.shutdown_behavior()));
+  scoped_refptr<Sequence> sequence_a =
+      test::CreateSequenceWithTask(std::move(task_a_1), best_effort_traits);
+  EXPECT_TRUE(tracker.WillScheduleSequence(sequence_a->BeginTransaction(),
+                                           &never_notified_observer));
+
+  // Simulate posting an extra best-effort task and scheduling the associated
+  // sequence. This should fail because the maximum number of best-effort
+  // sequences that can be scheduled concurrently is already reached.
+  bool task_b_1_did_run = false;
+  Task task_b_1(FROM_HERE, BindOnce(&SetBool, Unretained(&task_b_1_did_run)),
+                TimeDelta());
+  EXPECT_TRUE(
+      tracker.WillPostTask(&task_b_1, best_effort_traits.shutdown_behavior()));
+  scoped_refptr<Sequence> sequence_b =
+      test::CreateSequenceWithTask(std::move(task_b_1), best_effort_traits);
+  testing::StrictMock<MockCanScheduleSequenceObserver> task_b_1_observer;
+  EXPECT_FALSE(tracker.WillScheduleSequence(sequence_b->BeginTransaction(),
+                                            &task_b_1_observer));
+
+  // Wait to be sure that the sequence time of |task_a_2| is after the sequenced
+  // time of |task_b_1|.
+  PlatformThread::Sleep(TestTimeouts::tiny_timeout());
+
+  // Post an extra best-effort task in |sequence_a|.
+  bool task_a_2_did_run = false;
+  Task task_a_2(FROM_HERE, BindOnce(&SetBool, Unretained(&task_a_2_did_run)),
+                TimeDelta());
+  EXPECT_TRUE(
+      tracker.WillPostTask(&task_a_2, best_effort_traits.shutdown_behavior()));
+  sequence_a->BeginTransaction().PushTask(std::move(task_a_2));
+
+  // Run the first task in |sequence_a|. RunAndPopNextTask() should return
+  // nullptr since |sequence_a| can't be rescheduled immediately.
+  // |task_b_1_observer| should be notified that |sequence_b| can be scheduled.
+  testing::StrictMock<MockCanScheduleSequenceObserver> task_a_2_observer;
+  EXPECT_CALL(task_b_1_observer, MockOnCanScheduleSequence(sequence_b.get()));
+  EXPECT_FALSE(tracker.RunAndPopNextTask(sequence_a, &task_a_2_observer));
+  testing::Mock::VerifyAndClear(&task_b_1_observer);
+  EXPECT_TRUE(task_a_1_did_run);
+
+  // Run the first task in |sequence_b|. RunAndPopNextTask() should return
+  // nullptr since |sequence_b| is empty after popping a task from it.
+  // |task_a_2_observer| should be notified that |sequence_a| can be
+  // scheduled.
+  EXPECT_CALL(task_a_2_observer, MockOnCanScheduleSequence(sequence_a.get()));
+  EXPECT_FALSE(tracker.RunAndPopNextTask(sequence_b, &never_notified_observer));
+  testing::Mock::VerifyAndClear(&task_a_2_observer);
+  EXPECT_TRUE(task_b_1_did_run);
+
+  // Run the first task in |sequence_a|. RunAndPopNextTask() should return
+  // nullptr since |sequence_b| is empty after popping a task from it. No
+  // observer should be notified.
+  EXPECT_FALSE(tracker.RunAndPopNextTask(sequence_a, &never_notified_observer));
+  EXPECT_TRUE(task_a_2_did_run);
+}
+
+// Verify that preempted best-effort sequences are scheduled when shutdown
+// starts.
+TEST_F(TaskSchedulerTaskTrackerTest,
+       SchedulePreemptedBestEffortSequencesOnShutdown) {
+  constexpr int kMaxNumScheduledBestEffortSequences = 0;
+  TaskTracker tracker("Test", kMaxNumScheduledBestEffortSequences);
+  testing::StrictMock<MockCanScheduleSequenceObserver> observer;
+
+  // Simulate scheduling sequences. TaskTracker should prevent this.
+  std::vector<scoped_refptr<Sequence>> preempted_sequences;
+  for (int i = 0; i < 3; ++i) {
+    Task task(FROM_HERE, DoNothing(),
+              TimeDelta());
+    EXPECT_TRUE(
+        tracker.WillPostTask(&task, TaskShutdownBehavior::BLOCK_SHUTDOWN));
+    scoped_refptr<Sequence> sequence = test::CreateSequenceWithTask(
+        std::move(task), TaskTraits(TaskPriority::BEST_EFFORT,
+                                    TaskShutdownBehavior::BLOCK_SHUTDOWN));
+    EXPECT_FALSE(
+        tracker.WillScheduleSequence(sequence->BeginTransaction(), &observer));
+    preempted_sequences.push_back(std::move(sequence));
+
+    // Wait to be sure that tasks have different |sequenced_time|.
+    PlatformThread::Sleep(TestTimeouts::tiny_timeout());
+  }
+
+  // Perform shutdown. Expect |preempted_sequences| to be scheduled in posting
+  // order.
+  {
+    testing::InSequence in_sequence;
+    for (auto& preempted_sequence : preempted_sequences) {
+      EXPECT_CALL(observer, MockOnCanScheduleSequence(preempted_sequence.get()))
+          .WillOnce(testing::Invoke([&tracker](Sequence* sequence) {
+            // Run the task to unblock shutdown.
+            tracker.RunAndPopNextTask(sequence, nullptr);
+          }));
+    }
+    tracker.Shutdown();
+  }
 }
 
 namespace {
@@ -1092,10 +1439,15 @@
     TaskTraits default_traits = {};
     EXPECT_TRUE(task_tracker->WillPostTask(&task_without_sync_primitives,
                                            default_traits.shutdown_behavior()));
+    testing::StrictMock<MockCanScheduleSequenceObserver>
+        never_notified_observer;
     auto sequence_without_sync_primitives = test::CreateSequenceWithTask(
         std::move(task_without_sync_primitives), default_traits);
-    task_tracker->RunAndPopNextTask(
-        std::move(sequence_without_sync_primitives));
+    ASSERT_TRUE(task_tracker->WillScheduleSequence(
+        sequence_without_sync_primitives->BeginTransaction(),
+        &never_notified_observer));
+    task_tracker->RunAndPopNextTask(std::move(sequence_without_sync_primitives),
+                                    &never_notified_observer);
 
     // Disallow waiting. Expect TaskTracker to allow it before running a task
     // with the WithBaseSyncPrimitives() trait.
@@ -1113,7 +1465,11 @@
         traits_with_sync_primitives.shutdown_behavior()));
     auto sequence_with_sync_primitives = test::CreateSequenceWithTask(
         std::move(task_with_sync_primitives), traits_with_sync_primitives);
-    task_tracker->RunAndPopNextTask(std::move(sequence_with_sync_primitives));
+    ASSERT_TRUE(task_tracker->WillScheduleSequence(
+        sequence_with_sync_primitives->BeginTransaction(),
+        &never_notified_observer));
+    task_tracker->RunAndPopNextTask(std::move(sequence_with_sync_primitives),
+                                    &never_notified_observer);
 
     ScopedAllowBaseSyncPrimitivesForTesting
         allow_wait_in_task_tracker_destructor;
@@ -1143,6 +1499,7 @@
   auto statistics_recorder = StatisticsRecorder::CreateTemporaryForTesting();
 
   TaskTracker tracker("Test");
+  testing::StrictMock<MockCanScheduleSequenceObserver> never_notified_observer;
 
   struct {
     const TaskTraits traits;
@@ -1182,8 +1539,10 @@
 
     HistogramTester tester;
 
-    tracker.RunAndPopNextTask(
-        test::CreateSequenceWithTask(std::move(task), test.traits));
+    auto sequence = test::CreateSequenceWithTask(std::move(task), test.traits);
+    ASSERT_TRUE(tracker.WillScheduleSequence(sequence->BeginTransaction(),
+                                             &never_notified_observer));
+    tracker.RunAndPopNextTask(std::move(sequence), &never_notified_observer);
     tester.ExpectTotalCount(test.expected_histogram, 1);
   }
 }
diff --git a/base/task/task_scheduler/test_utils.cc b/base/task/task_scheduler/test_utils.cc
index f7f04bd..c9e2e57 100644
--- a/base/task/task_scheduler/test_utils.cc
+++ b/base/task/task_scheduler/test_utils.cc
@@ -58,8 +58,9 @@
 
 scoped_refptr<TaskRunner> CreateTaskRunnerWithExecutionMode(
     test::ExecutionMode execution_mode,
-    MockSchedulerTaskRunnerDelegate* mock_scheduler_task_runner_delegate,
-    const TaskTraits& traits) {
+    MockSchedulerTaskRunnerDelegate* mock_scheduler_task_runner_delegate) {
+  // Allow tasks posted to the returned TaskRunner to wait on a WaitableEvent.
+  const TaskTraits traits = {WithBaseSyncPrimitives()};
   switch (execution_mode) {
     case test::ExecutionMode::PARALLEL:
       return CreateTaskRunnerWithTraits(traits,
@@ -162,11 +163,6 @@
   worker_pool_ = worker_pool;
 }
 
-void ShutdownTaskTracker(TaskTracker* task_tracker) {
-  task_tracker->StartShutdown();
-  task_tracker->CompleteShutdown();
-}
-
 }  // namespace test
 }  // namespace internal
 }  // namespace base
diff --git a/base/task/task_scheduler/test_utils.h b/base/task/task_scheduler/test_utils.h
index 97b80c3..bd82a22 100644
--- a/base/task/task_scheduler/test_utils.h
+++ b/base/task/task_scheduler/test_utils.h
@@ -98,8 +98,7 @@
 // Caveat: this does not support ExecutionMode::SINGLE_THREADED.
 scoped_refptr<TaskRunner> CreateTaskRunnerWithExecutionMode(
     test::ExecutionMode execution_mode,
-    MockSchedulerTaskRunnerDelegate* mock_scheduler_task_runner_delegate,
-    const TaskTraits& traits = TaskTraits());
+    MockSchedulerTaskRunnerDelegate* mock_scheduler_task_runner_delegate);
 
 scoped_refptr<TaskRunner> CreateTaskRunnerWithTraits(
     const TaskTraits& traits,
@@ -111,9 +110,6 @@
 
 void WaitWithoutBlockingObserver(WaitableEvent* event);
 
-// Calls StartShutdown() and CompleteShutdown() on |task_tracker|.
-void ShutdownTaskTracker(TaskTracker* task_tracker);
-
 }  // namespace test
 }  // namespace internal
 }  // namespace base
diff --git a/base/test/task_scheduler_test_helpers_android.cc b/base/test/task_scheduler_test_helpers_android.cc
index 2e935a0..99471fda25 100644
--- a/base/test/task_scheduler_test_helpers_android.cc
+++ b/base/test/task_scheduler_test_helpers_android.cc
@@ -19,7 +19,8 @@
 // static
 void TaskSchedulerTestHelpers::SetTaskSchedulerExecutionFenceEnabledForTesting(
     bool execution_fence_enabled) {
-  TaskScheduler::GetInstance()->SetCanRun(!execution_fence_enabled);
+  TaskScheduler::GetInstance()->SetExecutionFenceEnabled(
+      execution_fence_enabled);
 }
 
 }  // namespace base
diff --git a/build/android/docs/android_app_bundles.md b/build/android/docs/android_app_bundles.md
index 81d38be..89344771 100644
--- a/build/android/docs/android_app_bundles.md
+++ b/build/android/docs/android_app_bundles.md
@@ -11,9 +11,7 @@
 Android applications on the Google Play Store, that allows reducing the size
 of binaries sent for installation to individual devices that run on Android L
 and beyond. For more information about them, see the official Android
-documentation at:
-
-  https://developer.android.com/guide/app-bundle/
+[documentation](https://developer.android.com/guide/app-bundle/).
 
 For the context of this document, the most important points are:
 
@@ -26,9 +24,10 @@
   - The splitting can be based on various criteria: e.g. language or screen
     density for resources, or cpu ABI for native code.
 
-  - The bundle also uses the notion of modules to separate several application
-    features. Each module has its own code, assets and resources, and can be
-    installed separately from the rest of the application if needed.
+  - The bundle also uses the notion of dynamic features modules (DFMs) to
+    separate several application features. Each module has its own code, assets
+    and resources, and can be installed separately from the rest of the
+    application if needed.
 
   - The main application itself is stored in the '`base`' module (this name
     cannot be changed).
@@ -36,8 +35,8 @@
 
 # Declaring app bundles with GN templates
 
-Here's an example that shows how to declare a simple bundle that contains
-a single base module, which enables language-based splits:
+Here's an example that shows how to declare a simple bundle that contains a
+single base module, which enables language-based splits:
 
 ```gn
 
@@ -83,9 +82,14 @@
     This works like an APK wrapper script (e.g. `foo_apk`). Use `--help`
     to see all possible commands supported by the script.
 
-If you need more modules besides the base one, you will need to list all the
-extra ones using the extra_modules variable which takes a list of GN scopes,
-as in:
+
+# Declaring dynamic feature modules with GN templates
+
+Please see
+[Dynamic Feature Modules](../../../docs/android_dynamic_feature_modules.md) for
+more details. In short, if you need more modules besides the base one, you
+will need to list all the extra ones using the extra_modules variable which
+takes a list of GN scopes, as in:
 
 ```gn
 
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index d58423f5..04b27d9 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-8916603075599745008
\ No newline at end of file
+8916571834170930272
\ No newline at end of file
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index 22152b3..12ceb24 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-8916654067773471280
\ No newline at end of file
+8916578545239015472
\ No newline at end of file
diff --git a/build/util/android_chrome_version.py b/build/util/android_chrome_version.py
index 5628f1a..f183441 100644
--- a/build/util/android_chrome_version.py
+++ b/build/util/android_chrome_version.py
@@ -113,11 +113,11 @@
 """
 ARCH64_APK_VARIANTS = {
     '64_32': {
-        'PACKAGES': frozenset(['MONOCHROME', 'TRICHROME']),
+        'PACKAGES': frozenset(['MONOCHROME']),
         'MODIFIER': 10
     },
     '64': {
-        'PACKAGES': frozenset(['MONOCHROME', 'TRICHROME']),
+        'PACKAGES': frozenset(['MONOCHROME']),
         'MODIFIER': 20
     }
 }
diff --git a/build/util/android_chrome_version_test.py b/build/util/android_chrome_version_test.py
index 5e743d3..ad3c5ca1 100644
--- a/build/util/android_chrome_version_test.py
+++ b/build/util/android_chrome_version_test.py
@@ -172,13 +172,9 @@
         self.EXAMPLE_VERSION_VALUES, arch='arm64', is_next_build=False)
     arch_monochrome_64_32_version_code = output['MONOCHROME_64_32_VERSION_CODE']
     arch_monochrome_64_version_code = output['MONOCHROME_64_VERSION_CODE']
-    arch_trichrome_64_32_version_code = output['TRICHROME_64_32_VERSION_CODE']
-    arch_trichrome_64_version_code = output['TRICHROME_64_VERSION_CODE']
 
     self.assertEqual(arch_monochrome_64_32_version_code, '372000042')
     self.assertEqual(arch_monochrome_64_version_code, '372000052')
-    self.assertEqual(arch_trichrome_64_32_version_code, '372000043')
-    self.assertEqual(arch_trichrome_64_version_code, '372000053')
 
   def testGenerateVersionCodesAndroidArchX64(self):
     """Assert it handles different architectures correctly.
@@ -204,13 +200,9 @@
         self.EXAMPLE_VERSION_VALUES, arch='x64', is_next_build=False)
     arch_monochrome_64_32_version_code = output['MONOCHROME_64_32_VERSION_CODE']
     arch_monochrome_64_version_code = output['MONOCHROME_64_VERSION_CODE']
-    arch_trichrome_64_32_version_code = output['TRICHROME_64_32_VERSION_CODE']
-    arch_trichrome_64_version_code = output['TRICHROME_64_VERSION_CODE']
 
     self.assertEqual(arch_monochrome_64_32_version_code, '372000072')
     self.assertEqual(arch_monochrome_64_version_code, '372000082')
-    self.assertEqual(arch_trichrome_64_32_version_code, '372000073')
-    self.assertEqual(arch_trichrome_64_version_code, '372000083')
 
   def testGenerateVersionCodesAndroidArchOrderArm(self):
     """Assert it handles different architectures correctly.
diff --git a/build/util/version.gni b/build/util/version.gni
index a28f2e46..0bcae72 100644
--- a/build/util/version.gni
+++ b/build/util/version.gni
@@ -51,9 +51,7 @@
   if (target_cpu == "arm64" || target_cpu == "x64") {
     _version_dictionary_template +=
         "monochrome_64_32_version_code = \"@MONOCHROME_64_32_VERSION_CODE@\" " +
-        "monochrome_64_version_code = \"@MONOCHROME_64_VERSION_CODE@\" " +
-        "trichrome_64_32_version_code = \"@TRICHROME_64_32_VERSION_CODE@\" " +
-        "trichrome_64_version_code = \"@TRICHROME_64_VERSION_CODE@\" "
+        "monochrome_64_version_code = \"@MONOCHROME_64_VERSION_CODE@\" "
   }
 
   _script_arguments += [
@@ -98,18 +96,16 @@
 } else if (target_os == "android") {
   forward_variables_from(_result,
                          [
-                           "chrome_modern_version_code",
                            "chrome_version_code",
-                           "monochrome_64_32_version_code",
-                           "monochrome_64_version_code",
+                           "chrome_modern_version_code",
                            "monochrome_version_code",
-                           "notouch_chrome_version_code",
-                           "trichrome_64_32_version_code",
-                           "trichrome_64_version_code",
                            "trichrome_version_code",
+                           "notouch_chrome_version_code",
+                           "webview_stable_version_code",
                            "webview_beta_version_code",
                            "webview_dev_version_code",
-                           "webview_stable_version_code",
+                           "monochrome_64_32_version_code",
+                           "monochrome_64_version_code",
                          ])
 
   chrome_version_name = chrome_version_full
@@ -130,8 +126,6 @@
     lines_to_write += [
       "Monochrome_64_32=$monochrome_64_32_version_code",
       "Monochrome_64=$monochrome_64_version_code",
-      "TrichromeChrome_64_32=$trichrome_64_32_version_code",
-      "TrichromeChrome_64=$trichrome_64_version_code",
     ]
   }
 
diff --git a/build/util/version_test.py b/build/util/version_test.py
index 2a65ddc7..290d026 100644
--- a/build/util/version_test.py
+++ b/build/util/version_test.py
@@ -120,9 +120,7 @@
     new_template = (
         self._EXAMPLE_ANDROID_TEMPLATE +
         "monochrome_64_32_version_code = \"@MONOCHROME_64_32_VERSION_CODE@\" "
-        "monochrome_64_version_code = \"@MONOCHROME_64_VERSION_CODE@\" "
-        "trichrome_64_32_version_code = \"@TRICHROME_64_32_VERSION_CODE@\" "
-        "trichrome_64_version_code = \"@TRICHROME_64_VERSION_CODE@\" ")
+        "monochrome_64_version_code = \"@MONOCHROME_64_VERSION_CODE@\" ")
     args_with_template = _ReplaceArgs(self._EXAMPLE_ANDROID_ARGS,
                                       ['-t', new_template])
     new_args = _ReplaceArgs(args_with_template, ['-a', 'arm64'])
@@ -133,19 +131,13 @@
                              r'\bmonochrome_64_32_version_code = "\d+"\s')
     self.assertRegexpMatches(contents,
                              r'\bmonochrome_64_version_code = "\d+"\s')
-    self.assertRegexpMatches(contents,
-                             r'\btrichrome_64_32_version_code = "\d+"\s')
-    self.assertRegexpMatches(contents,
-                             r'\btrichrome_64_version_code = "\d+"\s')
 
   def testBuildOutputAndroidArchVariantsX64(self):
     """Assert 64-bit-specific version codes"""
     new_template = (
         self._EXAMPLE_ANDROID_TEMPLATE +
         "monochrome_64_32_version_code = \"@MONOCHROME_64_32_VERSION_CODE@\" "
-        "monochrome_64_version_code = \"@MONOCHROME_64_VERSION_CODE@\" "
-        "trichrome_64_32_version_code = \"@TRICHROME_64_32_VERSION_CODE@\" "
-        "trichrome_64_version_code = \"@TRICHROME_64_VERSION_CODE@\" ")
+        "monochrome_64_version_code = \"@MONOCHROME_64_VERSION_CODE@\" ")
     args_with_template = _ReplaceArgs(self._EXAMPLE_ANDROID_ARGS,
                                       ['-t', new_template])
     new_args = _ReplaceArgs(args_with_template, ['-a', 'x64'])
@@ -156,10 +148,6 @@
                              r'\bmonochrome_64_32_version_code = "\d+"\s')
     self.assertRegexpMatches(contents,
                              r'\bmonochrome_64_version_code = "\d+"\s')
-    self.assertRegexpMatches(contents,
-                             r'\btrichrome_64_32_version_code = "\d+"\s')
-    self.assertRegexpMatches(contents,
-                             r'\btrichrome_64_version_code = "\d+"\s')
 
   def testBuildOutputAndroidChromeArchInput(self):
     """Assert it raises an exception when using an invalid architecture input"""
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index f260097..b243d59 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -2054,7 +2054,7 @@
   }
 }
 
-template("monochrome_or_trichrome_public_bundle_tmpl") {
+template("monochrome_public_bundle_tmpl") {
   _base_module_target_name = "${invoker.target_name}__base_bundle_module"
   _is_trichrome =
       defined(invoker.use_trichrome_library) && invoker.use_trichrome_library
@@ -2067,25 +2067,15 @@
   }
 
   if (_is_trichrome) {
-    if (defined(invoker.is_64_bit_browser) && invoker.is_64_bit_browser) {
-      if (invoker.build_apk_secondary_abi && invoker.include_32_bit_webview) {
-        _version_code = trichrome_64_32_version_code
-      } else {
-        _version_code = trichrome_64_version_code
-      }
+    _version_code = trichrome_version_code
+  } else if (defined(invoker.is_64_bit_browser) && invoker.is_64_bit_browser) {
+    if (invoker.build_apk_secondary_abi && invoker.include_32_bit_webview) {
+      _version_code = monochrome_64_32_version_code
     } else {
-      _version_code = trichrome_version_code
+      _version_code = monochrome_64_version_code
     }
   } else {
-    if (defined(invoker.is_64_bit_browser) && invoker.is_64_bit_browser) {
-      if (invoker.build_apk_secondary_abi && invoker.include_32_bit_webview) {
-        _version_code = monochrome_64_32_version_code
-      } else {
-        _version_code = monochrome_64_version_code
-      }
-    } else {
-      _version_code = monochrome_version_code
-    }
+    _version_code = monochrome_version_code
   }
   _version_name = chrome_version_name
 
@@ -2183,7 +2173,7 @@
   }
 }
 
-monochrome_or_trichrome_public_bundle_tmpl("monochrome_public_bundle") {
+monochrome_public_bundle_tmpl("monochrome_public_bundle") {
   bundle_suffix = ""
 }
 
@@ -2197,45 +2187,27 @@
   }
 }
 
-monochrome_or_trichrome_public_bundle_tmpl("trichrome_chrome_bundle") {
-  bundle_suffix = ""
-  use_trichrome_library = true
+if (android_64bit_target_cpu) {
+  monochrome_public_bundle_tmpl("monochrome_64_public_bundle") {
+    bundle_suffix = "64"
+    is_64_bit_browser = true
+    if (build_apk_secondary_abi) {
+      include_32_bit_webview = false
+    }
+  }
+
+  monochrome_public_bundle_tmpl("monochrome_64_32_public_bundle") {
+    bundle_suffix = "6432"
+    is_64_bit_browser = true
+    if (build_apk_secondary_abi) {
+      include_32_bit_webview = true
+    }
+  }
 }
 
-if (android_64bit_target_cpu) {
-  monochrome_or_trichrome_public_bundle_tmpl("monochrome_64_public_bundle") {
-    bundle_suffix = "64"
-    is_64_bit_browser = true
-    if (build_apk_secondary_abi) {
-      include_32_bit_webview = false
-    }
-  }
-
-  monochrome_or_trichrome_public_bundle_tmpl("monochrome_64_32_public_bundle") {
-    bundle_suffix = "6432"
-    is_64_bit_browser = true
-    if (build_apk_secondary_abi) {
-      include_32_bit_webview = true
-    }
-  }
-
-  monochrome_or_trichrome_public_bundle_tmpl("trichrome_64_chrome_bundle") {
-    bundle_suffix = "64"
-    is_64_bit_browser = true
-    use_trichrome_library = true
-    if (build_apk_secondary_abi) {
-      include_32_bit_webview = false
-    }
-  }
-
-  monochrome_or_trichrome_public_bundle_tmpl("trichrome_64_32_chrome_bundle") {
-    bundle_suffix = "6432"
-    is_64_bit_browser = true
-    use_trichrome_library = true
-    if (build_apk_secondary_abi) {
-      include_32_bit_webview = true
-    }
-  }
+monochrome_public_bundle_tmpl("trichrome_chrome_bundle") {
+  bundle_suffix = ""
+  use_trichrome_library = true
 }
 
 generate_jni("chrome_jni_headers") {
diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni
index 365d587..3d4acb0 100644
--- a/chrome/android/chrome_java_sources.gni
+++ b/chrome/android/chrome_java_sources.gni
@@ -898,6 +898,7 @@
   "java/src/org/chromium/chrome/browser/locale/LocaleManager.java",
   "java/src/org/chromium/chrome/browser/locale/LocaleTemplateUrlLoader.java",
   "java/src/org/chromium/chrome/browser/locale/SogouPromoDialog.java",
+  "java/src/org/chromium/chrome/browser/locationcustomizations/LocationCustomizations.java",
   "java/src/org/chromium/chrome/browser/login/ChromeHttpAuthHandler.java",
   "java/src/org/chromium/chrome/browser/login/LoginPrompt.java",
   "java/src/org/chromium/chrome/browser/media/MediaCaptureNotificationService.java",
diff --git a/chrome/android/chrome_public_apk_tmpl.gni b/chrome/android/chrome_public_apk_tmpl.gni
index f8fa973..a244711c 100644
--- a/chrome/android/chrome_public_apk_tmpl.gni
+++ b/chrome/android/chrome_public_apk_tmpl.gni
@@ -366,9 +366,7 @@
       # depend on their respective versions of the shared library APK even
       # though they're functionally the same.
       native_lib_placeholders = [ "libdummy.so" ]
-      if (android_64bit_target_cpu && build_apk_secondary_abi &&
-          (!defined(invoker.is_64_bit_browser) || !invoker.is_64_bit_browser ||
-           invoker.include_32_bit_webview)) {
+      if (android_64bit_target_cpu && build_apk_secondary_abi) {
         secondary_native_lib_placeholders = [ "libdummy.so" ]
       }
       _pak_prefix = "trichrome_chrome"
diff --git a/chrome/android/features/autofill_assistant/java/res/drawable/autofill_assistant_chip_assistive_bg.xml b/chrome/android/features/autofill_assistant/java/res/drawable/autofill_assistant_chip_assistive_bg.xml
deleted file mode 100644
index 905094f..0000000
--- a/chrome/android/features/autofill_assistant/java/res/drawable/autofill_assistant_chip_assistive_bg.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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. -->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-  <item
-      android:state_pressed="true"
-      android:drawable="@drawable/autofill_assistant_chip_assistive_bg_pressed" />
-  <item
-      android:drawable="@drawable/autofill_assistant_chip_assistive_bg_normal" />
-</selector>
diff --git a/chrome/android/features/autofill_assistant/java/res/drawable/autofill_assistant_chip_assistive_bg_normal.xml b/chrome/android/features/autofill_assistant/java/res/drawable/autofill_assistant_chip_assistive_bg_normal.xml
deleted file mode 100644
index 3b52275..0000000
--- a/chrome/android/features/autofill_assistant/java/res/drawable/autofill_assistant_chip_assistive_bg_normal.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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. -->
-<shape
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:shape="rectangle">
-  <corners
-      android:radius="20dp" />
-  <solid
-      android:color="@color/white_mode_tint" />
-  <stroke
-      android:width="1dp"
-      android:color="@color/hairline_stroke_color" />
-</shape>
diff --git a/chrome/android/features/autofill_assistant/java/res/drawable/autofill_assistant_chip_assistive_bg_pressed.xml b/chrome/android/features/autofill_assistant/java/res/drawable/autofill_assistant_chip_assistive_bg_pressed.xml
deleted file mode 100644
index 1bdf15ea..0000000
--- a/chrome/android/features/autofill_assistant/java/res/drawable/autofill_assistant_chip_assistive_bg_pressed.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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. -->
-<shape
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:shape="rectangle">
-  <corners
-      android:radius="20dp" />
-  <solid
-      android:color="@color/modern_grey_300" />
-</shape>
diff --git a/chrome/android/features/autofill_assistant/java/res/layout/autofill_assistant_chip_assistive.xml b/chrome/android/features/autofill_assistant/java/res/layout/autofill_assistant_chip_assistive.xml
deleted file mode 100644
index 1e6f69c..0000000
--- a/chrome/android/features/autofill_assistant/java/res/layout/autofill_assistant_chip_assistive.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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. -->
-<TextView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="wrap_content"
-    android:layout_height="wrap_content"
-    android:paddingStart="16dp"
-    android:paddingEnd="16dp"
-    android:textAppearance="@style/TextAppearance.AssistantBlackTitle"
-    android:gravity="center_vertical|center_horizontal"
-    android:minHeight="32dp"
-    android:singleLine="true"
-    android:background="@drawable/autofill_assistant_chip_assistive_bg" />
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/carousel/AssistantChipViewHolder.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/carousel/AssistantChipViewHolder.java
index ab8e781c..77b3d7c7 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/carousel/AssistantChipViewHolder.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/carousel/AssistantChipViewHolder.java
@@ -6,47 +6,59 @@
 
 import android.support.v7.widget.RecyclerView.ViewHolder;
 import android.view.LayoutInflater;
+import android.view.View;
 import android.view.ViewGroup;
 import android.widget.TextView;
 
 import org.chromium.chrome.autofill_assistant.R;
+import org.chromium.ui.widget.ChipView;
 
 /**
  * The {@link ViewHolder} responsible for reflecting an {@link AssistantChip} to a {@link
  * TextView}.
  */
 class AssistantChipViewHolder extends ViewHolder {
+    private final View mView;
     private final TextView mText;
 
-    private AssistantChipViewHolder(TextView itemView) {
-        super(itemView);
+    private AssistantChipViewHolder(View view, TextView itemView) {
+        super(view);
+        mView = view;
         mText = itemView;
     }
 
     static AssistantChipViewHolder create(ViewGroup parent, int viewType) {
         LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
-        int resId = -1;
+        View view = null;
+        TextView textView = null;
         switch (viewType % AssistantChip.Type.NUM_ENTRIES) {
             // TODO: inflate normal chrome buttons instead.
             case AssistantChip.Type.CHIP_ASSISTIVE:
-                resId = R.layout.autofill_assistant_chip_assistive;
+                ChipView chipView = new ChipView(parent.getContext(), R.style.AssistiveChip);
+                chipView.setLayoutParams(new ViewGroup.LayoutParams(
+                        ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
+                view = chipView;
+                textView = chipView.getPrimaryTextView();
                 break;
             case AssistantChip.Type.BUTTON_FILLED_BLUE:
-                resId = R.layout.autofill_assistant_button_filled;
+                view = layoutInflater.inflate(
+                        R.layout.autofill_assistant_button_filled, /* root= */ null);
+                textView = (TextView) view;
                 break;
             case AssistantChip.Type.BUTTON_HAIRLINE:
-                resId = R.layout.autofill_assistant_button_hairline;
+                view = layoutInflater.inflate(
+                        R.layout.autofill_assistant_button_hairline, /* root= */ null);
+                textView = (TextView) view;
                 break;
             default:
                 assert false : "Unsupported view type " + viewType;
         }
 
-        TextView view = (TextView) layoutInflater.inflate(resId, /* root= */ null);
         if (viewType >= AssistantChip.Type.NUM_ENTRIES) {
             view.setEnabled(false);
         }
 
-        return new AssistantChipViewHolder(view);
+        return new AssistantChipViewHolder(view, textView);
     }
 
     static int getViewType(AssistantChip chip) {
@@ -62,6 +74,6 @@
 
     public void bind(AssistantChip chip) {
         mText.setText(chip.getText());
-        mText.setOnClickListener(ignoredView -> chip.getSelectedListener().run());
+        mView.setOnClickListener(ignoredView -> chip.getSelectedListener().run());
     }
 }
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestCoordinator.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestCoordinator.java
index 17577d7..c6e22d2 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestCoordinator.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestCoordinator.java
@@ -104,7 +104,7 @@
         buildPaymentRequestUI(webContents, options);
         mPaymentRequestUI.show(UrlFormatter.formatUrlForSecurityDisplayOmitScheme(
                                        webContents.getLastCommittedUrl()),
-                options.mRequestShipping,
+                options.mRequestShipping, options.mRequestPaymentMethod,
                 options.mRequestPayerName || options.mRequestPayerPhone
                         || options.mRequestPayerEmail,
                 new ShippingStrings(PaymentShippingType.SHIPPING));
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestModel.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestModel.java
index d912840..c3838b6b 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestModel.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestModel.java
@@ -30,13 +30,13 @@
     }
 
     @CalledByNative
-    private void setOptions(String defaultEmail, boolean requestShipping, boolean requestPayerName,
-            boolean requestPayerPhone, boolean requestPayerEmail,
-            String[] supportedBasicCardNetworks) {
+    private void setOptions(String defaultEmail, boolean requestShipping,
+            boolean requestPaymentMethod, boolean requestPayerName, boolean requestPayerPhone,
+            boolean requestPayerEmail, String[] supportedBasicCardNetworks) {
         set(OPTIONS,
                 new AssistantPaymentRequestOptions(requestPayerName, requestPayerEmail,
-                        requestPayerPhone, requestShipping, supportedBasicCardNetworks,
-                        defaultEmail));
+                        requestPayerPhone, requestShipping, requestPaymentMethod,
+                        supportedBasicCardNetworks, defaultEmail));
     }
 
     @CalledByNative
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestOptions.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestOptions.java
index 5cccb6bf..bdfeddb0 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestOptions.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestOptions.java
@@ -14,17 +14,19 @@
     final boolean mRequestPayerEmail;
     final boolean mRequestPayerPhone;
     final boolean mRequestShipping;
+    final boolean mRequestPaymentMethod;
     final String[] mSupportedBasicCardNetworks;
     @Nullable
     final String mDefaultEmail;
 
     AssistantPaymentRequestOptions(boolean requestPayerName, boolean requestPayerEmail,
-            boolean requestPayerPhone, boolean requestShipping, String[] supportedBasicCardNetworks,
-            @Nullable String defaultEmail) {
+            boolean requestPayerPhone, boolean requestShipping, boolean requestPaymentMethod,
+            String[] supportedBasicCardNetworks, @Nullable String defaultEmail) {
         this.mRequestPayerName = requestPayerName;
         this.mRequestPayerEmail = requestPayerEmail;
         this.mRequestPayerPhone = requestPayerPhone;
         this.mRequestShipping = requestShipping;
+        this.mRequestPaymentMethod = requestPaymentMethod;
         this.mSupportedBasicCardNetworks = supportedBasicCardNetworks;
         this.mDefaultEmail = defaultEmail;
     }
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestUI.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestUI.java
index 39966aca..3bcf8da6 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestUI.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestUI.java
@@ -121,6 +121,7 @@
     private LinearLayout mPaymentContainerLayout;
 
     private boolean mRequestShipping;
+    private boolean mRequestPaymentMethod;
     private boolean mRequestContactDetails;
     private ShippingStrings mShippingStrings;
     private OptionSection mShippingAddressSection;
@@ -149,10 +150,6 @@
                 new LinearLayout.LayoutParams(/* width= */ ViewGroup.LayoutParams.MATCH_PARENT,
                         /* height= */ 0, /* weight= */ 1));
 
-        // Add separators at the top and bottom of the list.
-        mRequestViewContainer.setEdgeVisibility(
-                FadingEdgeScrollView.EdgeType.HARD, FadingEdgeScrollView.EdgeType.FADING);
-
         mEditorDialog = new EditorDialog(mActivity, null,
                 /*deleteRunnable =*/null);
         mCardEditorDialog = new EditorDialog(mActivity, null,
@@ -177,14 +174,16 @@
      *
      * @param origin          The origin of the information will be send to.
      * @param requestShipping Whether the UI should show the shipping address selection.
+     * @param requestPaymentMethod Whether the UI should show the payment method selection.
      * @param requestContact  Whether the UI should show the payer name, email address and phone
      *                        number selection.
      * @param shippingStrings The string resource identifiers to use in the shipping sections.
      *
      */
-    public void show(String origin, boolean requestShipping, boolean requestContact,
-            ShippingStrings shippingStrings) {
+    public void show(String origin, boolean requestShipping, boolean requestPaymentMethod,
+            boolean requestContact, ShippingStrings shippingStrings) {
         mRequestShipping = requestShipping;
+        mRequestPaymentMethod = requestPaymentMethod;
         mRequestContactDetails = requestContact;
         mShippingStrings = shippingStrings;
 
@@ -261,11 +260,13 @@
                             LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
         }
 
-        if (mRequestContactDetails || mRequestShipping)
-            mSectionSeparators.add(new SectionSeparator(mPaymentContainerLayout));
-        mPaymentContainerLayout.addView(mPaymentMethodSection,
-                new LinearLayout.LayoutParams(
-                        LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
+        if (mRequestPaymentMethod) {
+            if (mRequestContactDetails || mRequestShipping)
+                mSectionSeparators.add(new SectionSeparator(mPaymentContainerLayout));
+            mPaymentContainerLayout.addView(mPaymentMethodSection,
+                    new LinearLayout.LayoutParams(
+                            LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
+        }
 
         // Always expand separators to make them align with the rest of the UI.
         for (int i = 0; i < mSectionSeparators.size(); i++) {
@@ -280,8 +281,10 @@
             updateSection(DataType.SHIPPING_ADDRESSES,
                     mClient.getSectionInformation(DataType.SHIPPING_ADDRESSES));
         }
-        updateSection(
-                DataType.PAYMENT_METHODS, mClient.getSectionInformation(DataType.PAYMENT_METHODS));
+        if (mRequestPaymentMethod) {
+            updateSection(DataType.PAYMENT_METHODS,
+                    mClient.getSectionInformation(DataType.PAYMENT_METHODS));
+        }
 
         // Force the initial appearance of edit chevrons next to all sections.
         updateSectionVisibility();
@@ -303,8 +306,6 @@
         mContactDetailsSection = null;
         mPaymentMethodSection = null;
         mShippingAddressSectionInformation = null;
-        mContactDetailsSection = null;
-        mPaymentMethodSection = null;
         mSectionSeparators = null;
     }
 
diff --git a/chrome/android/java/res/values/dimens.xml b/chrome/android/java/res/values/dimens.xml
index a1ba5c0..b5db1203 100644
--- a/chrome/android/java/res/values/dimens.xml
+++ b/chrome/android/java/res/values/dimens.xml
@@ -425,7 +425,9 @@
     <dimen name="default_favicon_size">16dp</dimen>
     <dimen name="default_favicon_corner_radius">3dp</dimen>
     <dimen name="default_favicon_icon_text_size">10dp</dimen>
-    <!-- TODO(crbug.com/900912): Fix and remove lint ignore -->
+    <!-- We intentionally use 16px since most websites have a favicon that's 16px. If we use 16dp
+         and scale then we'll start showing fewer favicons in some UIs on higher density devices.
+         -->
     <dimen tools:ignore="PxUsage" name="default_favicon_min_size">16px</dimen>
     <dimen name="circular_monogram_size">20dp</dimen>
     <dimen name="circular_monogram_text_size">14dp</dimen>
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
index 7688b8a..b45a5c50 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
@@ -99,6 +99,7 @@
 import org.chromium.chrome.browser.infobar.DataReductionPromoInfoBar;
 import org.chromium.chrome.browser.language.LanguageAskPrompt;
 import org.chromium.chrome.browser.locale.LocaleManager;
+import org.chromium.chrome.browser.locationcustomizations.LocationCustomizations;
 import org.chromium.chrome.browser.metrics.ActivityStopMetrics;
 import org.chromium.chrome.browser.metrics.LaunchMetrics;
 import org.chromium.chrome.browser.metrics.MainIntentBehaviorMetrics;
@@ -788,6 +789,7 @@
 
         mLocaleManager.setSnackbarManager(getSnackbarManager());
         mLocaleManager.startObservingPhoneChanges();
+        LocationCustomizations.initialize();
 
         if (!ChromeFeatureList.isEnabled(ChromeFeatureList.INTEREST_FEED_CONTENT_SUGGESTIONS)) {
             if (isWarmOnResume()) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/locationcustomizations/LocationCustomizations.java b/chrome/android/java/src/org/chromium/chrome/browser/locationcustomizations/LocationCustomizations.java
new file mode 100644
index 0000000..86d61bc
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/locationcustomizations/LocationCustomizations.java
@@ -0,0 +1,23 @@
+// 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.locationcustomizations;
+
+import org.chromium.chrome.browser.locale.LocaleManager;
+import org.chromium.chrome.browser.preferences.Pref;
+import org.chromium.chrome.browser.preferences.PrefServiceBridge;
+
+/**
+ * Does customizations specified by users locations.
+ */
+public class LocationCustomizations {
+    /**
+     * Initialize location customizations.
+     */
+    public static void initialize() {
+        if (LocaleManager.getInstance().isSpecialUser()) {
+            PrefServiceBridge.getInstance().setBoolean(Pref.NTP_ARTICLES_SECTION_ENABLED, false);
+        }
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/SingleCategoryPreferences.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/SingleCategoryPreferences.java
index b227089a1..86256cb3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/SingleCategoryPreferences.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/SingleCategoryPreferences.java
@@ -195,6 +195,8 @@
     private void updateAllowedHeader(int numAllowed, boolean toggleValue) {
         ExpandablePreferenceGroup allowedGroup =
                 (ExpandablePreferenceGroup) getPreferenceScreen().findPreference(ALLOWED_GROUP);
+        if (allowedGroup == null) return;
+
         if (numAllowed == 0) {
             if (allowedGroup != null) getPreferenceScreen().removePreference(allowedGroup);
             return;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/signin/AccountSigninView.java b/chrome/android/java/src/org/chromium/chrome/browser/signin/AccountSigninView.java
index c43b700..82e69d2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/signin/AccountSigninView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/signin/AccountSigninView.java
@@ -30,6 +30,7 @@
 import org.chromium.chrome.browser.externalauth.UserRecoverableErrorHandler;
 import org.chromium.chrome.browser.preferences.PrefServiceBridge;
 import org.chromium.chrome.browser.signin.ConfirmImportSyncDataDialog.ImportSyncType;
+import org.chromium.chrome.browser.sync.SyncUserDataWiper;
 import org.chromium.components.signin.AccountIdProvider;
 import org.chromium.components.signin.AccountManagerDelegateException;
 import org.chromium.components.signin.AccountManagerFacade;
@@ -681,7 +682,7 @@
                     @Override
                     public void onConfirm(boolean wipeData) {
                         mConfirmSyncDataStateMachine = null;
-                        SigninManager.wipeSyncUserDataIfRequired(wipeData).then(
+                        SyncUserDataWiper.wipeSyncUserDataIfRequired(wipeData).then(
                                 (Void v) -> showConfirmationPage());
                     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninFragmentBase.java b/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninFragmentBase.java
index 633e4f2..8982e353 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninFragmentBase.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninFragmentBase.java
@@ -34,6 +34,7 @@
 import org.chromium.chrome.browser.consent_auditor.ConsentAuditorFeature;
 import org.chromium.chrome.browser.externalauth.UserRecoverableErrorHandler;
 import org.chromium.chrome.browser.preferences.PrefServiceBridge;
+import org.chromium.chrome.browser.sync.SyncUserDataWiper;
 import org.chromium.components.signin.AccountIdProvider;
 import org.chromium.components.signin.AccountManagerDelegateException;
 import org.chromium.components.signin.AccountManagerFacade;
@@ -452,7 +453,7 @@
 
                         // Don't start sign-in if this fragment has been destroyed.
                         if (mDestroyed) return;
-                        SigninManager.wipeSyncUserDataIfRequired(wipeData).then((Void v) -> {
+                        SyncUserDataWiper.wipeSyncUserDataIfRequired(wipeData).then((Void v) -> {
                             onSigninAccepted(mSelectedAccountName, mIsDefaultAccountSelected,
                                     settingsClicked, () -> mIsSigninInProgress = false);
                         });
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninManager.java b/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninManager.java
index aef01ce1..02705b5a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninManager.java
@@ -29,7 +29,6 @@
 import org.chromium.base.task.PostTask;
 import org.chromium.chrome.browser.externalauth.ExternalAuthUtils;
 import org.chromium.chrome.browser.externalauth.UserRecoverableErrorHandler;
-import org.chromium.chrome.browser.sync.SyncUserDataWiper;
 import org.chromium.components.signin.AccountIdProvider;
 import org.chromium.components.signin.AccountManagerFacade;
 import org.chromium.components.signin.AccountTrackerService;
@@ -705,18 +704,6 @@
         SigninManagerJni.get().wipeGoogleServiceWorkerCaches(this, mNativeSigninManagerAndroid);
     }
 
-    /**
-     * Convenience method to return a Promise to be fulfilled when the user's sync data has been
-     * wiped if the parameter is true, or an already fulfilled Promise if the parameter is false.
-     */
-    public static Promise<Void> wipeSyncUserDataIfRequired(boolean required) {
-        if (required) {
-            return SyncUserDataWiper.wipeSyncUserData();
-        } else {
-            return Promise.fulfilled(null);
-        }
-    }
-
     @CalledByNative
     @VisibleForTesting
     protected void onProfileDataWiped() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/sync/SyncAccountSwitcher.java b/chrome/android/java/src/org/chromium/chrome/browser/sync/SyncAccountSwitcher.java
index 33ecf9dd..5baedcb8 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/sync/SyncAccountSwitcher.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/sync/SyncAccountSwitcher.java
@@ -78,7 +78,7 @@
                 .then((Void argument) -> {
                     // Once signed out, clear the last signed in user and wipe data if needed.
                     SigninManager.get().clearLastSignedInUser();
-                    return SigninManager.wipeSyncUserDataIfRequired(wipeData);
+                    return SyncUserDataWiper.wipeSyncUserDataIfRequired(wipeData);
                 })
                 .then((Void argument) -> {
                     // Once the data has been wiped (if needed), sign in to the next account.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/sync/SyncUserDataWiper.java b/chrome/android/java/src/org/chromium/chrome/browser/sync/SyncUserDataWiper.java
index eb46ea6..f8fd0e3b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/sync/SyncUserDataWiper.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/sync/SyncUserDataWiper.java
@@ -49,5 +49,18 @@
 
         return promise;
     }
-}
 
+    /**
+     * Wipes the user's bookmarks and sync data if required.
+     * @param required Whether the promise the user's bookmarks and sync data should be wiped.
+     * @return A promise which will be fulfilled once the data is wiped if required is true, or
+     *         immediately otherwise.
+     */
+    public static Promise<Void> wipeSyncUserDataIfRequired(boolean required) {
+        if (required) {
+            return SyncUserDataWiper.wipeSyncUserData();
+        } else {
+            return Promise.fulfilled(null);
+        }
+    }
+}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/notifications/NotificationPlatformBridgeIntentTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/notifications/NotificationPlatformBridgeIntentTest.java
index 66e81ec8..c70fc353 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/notifications/NotificationPlatformBridgeIntentTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/notifications/NotificationPlatformBridgeIntentTest.java
@@ -17,6 +17,7 @@
 
 import org.chromium.base.library_loader.LibraryLoader;
 import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.base.test.util.DisabledTest;
 import org.chromium.base.test.util.Feature;
 import org.chromium.base.test.util.RetryOnFailure;
 import org.chromium.chrome.browser.ChromeSwitches;
@@ -133,6 +134,7 @@
      */
     @Test
     @MediumTest
+    @DisabledTest(message = "https://crbug.com/950635")
     @Feature({"Browser", "Notifications"})
     public void testLaunchProcessForNotificationActivation() throws Exception {
         Assert.assertFalse("The native library should not be loaded yet",
diff --git a/chrome/app/chromeos_strings.grdp b/chrome/app/chromeos_strings.grdp
index c683d7e..7f4fd3f5 100644
--- a/chrome/app/chromeos_strings.grdp
+++ b/chrome/app/chromeos_strings.grdp
@@ -1725,12 +1725,15 @@
     Learn more.
   </message>
 
-  <!--Auto launch managed guest sessions privacy notification -->
+  <!-- Auto launch managed guest sessions privacy notification -->
   <message name="IDS_AUTO_LAUNCH_NOTIFICATION_TITLE" desc="Title of the notification which is shown when the managed guest session auto launched.">
-    This Chromebook can be monitored
+    Managed guest session
   </message>
   <message name="IDS_AUTO_LAUNCH_NOTIFICATION_MESSAGE" desc="Message of the notification which is shown when the managed guest session auto launched.">
-    <ph name="ENROLLMENT_DOMAIN">$1<ex>example.com</ex></ph> manages this device and has access to all user activity, including webpages visited, passwords, and email. See details.
+    The administrator of this device has access to all activity, including passwords and communications.
+  </message>
+  <message name="IDS_AUTO_LAUNCH_NOTIFICATION_BUTTON" desc="Text of the button.">
+    View details
   </message>
 
 
diff --git a/chrome/app/settings_strings.grdp b/chrome/app/settings_strings.grdp
index 18bd480..01d056a 100644
--- a/chrome/app/settings_strings.grdp
+++ b/chrome/app/settings_strings.grdp
@@ -3525,6 +3525,12 @@
     <message name="IDS_SETTINGS_ACCOUNT_MANAGER_REMOVE_ACCOUNT_LABEL" desc="Label of the Remove account button in Account Manager.">
       Remove account from this device
     </message>
+    <message name="IDS_SETTINGS_ACCOUNT_MANAGER_MANAGEMENT_STATUS_MANAGED_ACCOUNT" desc="Management status label for managed accounts. This will be e.g. 'Managed by google.com' for Enterprise accounts and 'Managed by Family Link' for child accounts.">
+      Managed by <ph name="DOMAIN">$1<ex>google.com</ex></ph>
+    </message>
+    <message name="IDS_SETTINGS_ACCOUNT_MANAGER_MANAGEMENT_STATUS_UNMANAGED_ACCOUNT" desc="Management status label for unmanaged accounts.">
+      Primary account
+    </message>
     <message name="IDS_SETTINGS_ADD_FINGERPRINT_DIALOG_TITLE" desc="Title of the add fingerprint dialog popup.">
       Set up your fingerprint
     </message>
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_ACCOUNT_MANAGER_MANAGEMENT_STATUS_MANAGED_ACCOUNT.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_ACCOUNT_MANAGER_MANAGEMENT_STATUS_MANAGED_ACCOUNT.png.sha1
new file mode 100644
index 0000000..40733490
--- /dev/null
+++ b/chrome/app/settings_strings_grdp/IDS_SETTINGS_ACCOUNT_MANAGER_MANAGEMENT_STATUS_MANAGED_ACCOUNT.png.sha1
@@ -0,0 +1 @@
+405ae38539f535622d3f63180e5356cae2418762
\ No newline at end of file
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_ACCOUNT_MANAGER_MANAGEMENT_STATUS_UNMANAGED_ACCOUNT.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_ACCOUNT_MANAGER_MANAGEMENT_STATUS_UNMANAGED_ACCOUNT.png.sha1
new file mode 100644
index 0000000..694007d
--- /dev/null
+++ b/chrome/app/settings_strings_grdp/IDS_SETTINGS_ACCOUNT_MANAGER_MANAGEMENT_STATUS_UNMANAGED_ACCOUNT.png.sha1
@@ -0,0 +1 @@
+23596a407dd85bfbc9ecb0dc2b8e9ab31079fc43
\ No newline at end of file
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index ff6b2c2..6b13b0a 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -1969,8 +1969,7 @@
     "//components/undo",
     "//components/unified_consent",
     "//components/update_client",
-    "//components/update_client:patch_impl",
-    "//components/update_client:unzip_impl",
+    "//components/update_client:common_impl",
     "//components/upload_list",
     "//components/url_formatter",
     "//components/url_formatter/top_domains:top500_domains",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index e104157..e162b732 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -2617,12 +2617,6 @@
      flag_descriptions::kForceColorProfileDescription, kOsAll,
      MULTI_VALUE_TYPE(kForceColorProfileChoices)},
 
-#if defined(OS_CHROMEOS)
-    {"use-monitor-color-space", flag_descriptions::kUseMonitorColorSpaceName,
-     flag_descriptions::kUseMonitorColorSpaceDescription, kOsCrOS,
-     FEATURE_VALUE_TYPE(display::features::kUseMonitorColorSpace)},
-#endif  // OS_CHROMEOS
-
 #if defined(OS_ANDROID)
     {"enable-webnfc", flag_descriptions::kEnableWebNfcName,
      flag_descriptions::kEnableWebNfcDescription, kOsAndroid,
diff --git a/chrome/browser/android/autofill_assistant/ui_controller_android.cc b/chrome/browser/android/autofill_assistant/ui_controller_android.cc
index 033992fcb..27c69b2 100644
--- a/chrome/browser/android/autofill_assistant/ui_controller_android.cc
+++ b/chrome/browser/android/autofill_assistant/ui_controller_android.cc
@@ -594,8 +594,9 @@
       env, jmodel,
       base::android::ConvertUTF8ToJavaString(env,
                                              client_->GetAccountEmailAddress()),
-      payment_options->request_shipping, payment_options->request_payer_name,
-      payment_options->request_payer_phone,
+      payment_options->request_shipping,
+      payment_options->request_payment_method,
+      payment_options->request_payer_name, payment_options->request_payer_phone,
       payment_options->request_payer_email,
       base::android::ToJavaArrayOfStrings(
           env, payment_options->supported_basic_card_networks));
diff --git a/chrome/browser/apps/app_service/app_icon_factory.cc b/chrome/browser/apps/app_service/app_icon_factory.cc
index 15d7f05..031d84cb8 100644
--- a/chrome/browser/apps/app_service/app_icon_factory.cc
+++ b/chrome/browser/apps/app_service/app_icon_factory.cc
@@ -23,6 +23,7 @@
 #include "extensions/browser/image_loader.h"
 #include "extensions/common/manifest.h"
 #include "extensions/common/manifest_handlers/icons_handler.h"
+#include "extensions/grit/extensions_browser_resources.h"
 #include "services/data_decoder/public/cpp/decode_image.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/gfx/image/image.h"
@@ -72,28 +73,40 @@
 
 // Runs |callback| passing an IconValuePtr with a compressed image: a
 // std::vector<uint8_t>.
+//
+// It will fall back to the |default_icon_resource| if the data is empty.
 void RunCallbackWithCompressedData(
     int size_hint_in_dip,
+    int default_icon_resource,
     bool is_placeholder_icon,
     apps::IconEffects icon_effects,
     apps::mojom::Publisher::LoadIconCallback callback,
     std::vector<uint8_t> data) {
-  apps::mojom::IconValuePtr iv = apps::mojom::IconValue::New();
-  iv->icon_compression = data.empty()
-                             ? apps::mojom::IconCompression::kUnknown
-                             : apps::mojom::IconCompression::kCompressed;
-  iv->compressed = std::move(data);
-  iv->is_placeholder_icon = is_placeholder_icon;
-  if (icon_effects) {
-    // TODO(crbug.com/826982): decompress the image, apply the icon_effects,
-    // and recompress the post-processed image.
-    //
-    // Even if there are no icon_effects, we might also want to do this if the
-    // size_hint_in_dip doesn't match the compressed image's pixel size. This
-    // isn't trivial, though, as determining the compressed image's pixel size
-    // might involve a sandboxed decoder process.
+  // TODO(crbug.com/826982): if icon_effects is non-zero, we should arguably
+  // decompress the image, apply the icon_effects, and recompress the
+  // post-processed image.
+  //
+  // Even if there are no icon_effects, we might also want to do this if the
+  // size_hint_in_dip doesn't match the compressed image's pixel size. This
+  // isn't trivial, though, as determining the compressed image's pixel size
+  // might involve a sandboxed decoder process.
+
+  if (!data.empty()) {
+    apps::mojom::IconValuePtr iv = apps::mojom::IconValue::New();
+    iv->icon_compression = apps::mojom::IconCompression::kCompressed;
+    iv->compressed = std::move(data);
+    iv->is_placeholder_icon = is_placeholder_icon;
+    std::move(callback).Run(std::move(iv));
+    return;
   }
-  std::move(callback).Run(std::move(iv));
+  if (default_icon_resource) {
+    LoadIconFromResource(apps::mojom::IconCompression::kCompressed,
+                         size_hint_in_dip, default_icon_resource,
+                         is_placeholder_icon, icon_effects,
+                         std::move(callback));
+    return;
+  }
+  std::move(callback).Run(apps::mojom::IconValue::New());
 }
 
 // Like RunCallbackWithCompressedData, but calls "fallback(callback)" if the
@@ -109,9 +122,10 @@
     std::move(fallback).Run(std::move(callback));
     return;
   }
-  RunCallbackWithCompressedData(size_hint_in_dip, is_placeholder_icon,
-                                icon_effects, std::move(callback),
-                                std::move(data));
+  constexpr int default_icon_resource = 0;
+  RunCallbackWithCompressedData(size_hint_in_dip, default_icon_resource,
+                                is_placeholder_icon, icon_effects,
+                                std::move(callback), std::move(data));
 }
 
 // Runs |callback| passing an IconValuePtr with an uncompressed image: a
@@ -156,33 +170,48 @@
 
 // Runs |callback| passing an IconValuePtr with an uncompressed image: an
 // ImageSkia.
+//
+// It will fall back to the |default_icon_resource| if the image is null.
 void RunCallbackWithUncompressedImageSkia(
     int size_hint_in_dip,
+    int default_icon_resource,
     bool is_placeholder_icon,
     apps::IconEffects icon_effects,
     apps::mojom::Publisher::LoadIconCallback callback,
     const gfx::ImageSkia image) {
-  apps::mojom::IconValuePtr iv = apps::mojom::IconValue::New();
-  iv->icon_compression = apps::mojom::IconCompression::kUncompressed;
-  iv->uncompressed = image;
-  iv->is_placeholder_icon = is_placeholder_icon;
-  if (icon_effects && !iv->uncompressed.isNull()) {
-    ApplyIconEffects(icon_effects, size_hint_in_dip, &iv->uncompressed);
+  if (!image.isNull()) {
+    apps::mojom::IconValuePtr iv = apps::mojom::IconValue::New();
+    iv->icon_compression = apps::mojom::IconCompression::kUncompressed;
+    iv->uncompressed = image;
+    iv->is_placeholder_icon = is_placeholder_icon;
+    if (icon_effects && !iv->uncompressed.isNull()) {
+      ApplyIconEffects(icon_effects, size_hint_in_dip, &iv->uncompressed);
+    }
+    std::move(callback).Run(std::move(iv));
+    return;
   }
-  std::move(callback).Run(std::move(iv));
+  if (default_icon_resource) {
+    LoadIconFromResource(apps::mojom::IconCompression::kUncompressed,
+                         size_hint_in_dip, default_icon_resource,
+                         is_placeholder_icon, icon_effects,
+                         std::move(callback));
+    return;
+  }
+  std::move(callback).Run(apps::mojom::IconValue::New());
 }
 
 // Runs |callback| passing an IconValuePtr with an uncompressed image: an
 // Image.
 void RunCallbackWithUncompressedImage(
     int size_hint_in_dip,
+    int default_icon_resource,
     bool is_placeholder_icon,
     apps::IconEffects icon_effects,
     apps::mojom::Publisher::LoadIconCallback callback,
     const gfx::Image& image) {
-  RunCallbackWithUncompressedImageSkia(size_hint_in_dip, is_placeholder_icon,
-                                       icon_effects, std::move(callback),
-                                       image.AsImageSkia());
+  RunCallbackWithUncompressedImageSkia(
+      size_hint_in_dip, default_icon_resource, is_placeholder_icon,
+      icon_effects, std::move(callback), image.AsImageSkia());
 }
 
 }  // namespace
@@ -198,6 +227,10 @@
   constexpr bool is_placeholder_icon = false;
   int size_hint_in_px = apps_util::ConvertDipToPx(size_hint_in_dip);
 
+  // This is the default icon for AppType::kExtension. Other app types might
+  // use a different default icon, such as IDR_LOGO_CROSTINI_DEFAULT_192.
+  constexpr int default_icon_resource = IDR_APP_DEFAULT_ICON;
+
   const extensions::Extension* extension =
       extensions::ExtensionSystem::Get(context)
           ->extension_service()
@@ -216,8 +249,8 @@
             extension, std::move(ext_resource),
             gfx::Size(size_hint_in_px, size_hint_in_px),
             base::BindOnce(&RunCallbackWithUncompressedImage, size_hint_in_dip,
-                           is_placeholder_icon, icon_effects,
-                           std::move(callback)));
+                           default_icon_resource, is_placeholder_icon,
+                           icon_effects, std::move(callback)));
         return;
       }
 
@@ -244,8 +277,8 @@
                 ui::ResourceBundle::GetSharedInstance().GetRawDataResource(
                     resource_info.resource_id);
             RunCallbackWithCompressedData(
-                size_hint_in_dip, is_placeholder_icon, icon_effects,
-                std::move(callback),
+                size_hint_in_dip, default_icon_resource, is_placeholder_icon,
+                icon_effects, std::move(callback),
                 std::vector<uint8_t>(data.begin(), data.end()));
             return;
           }
@@ -259,15 +292,18 @@
               base::BindOnce(&CompressedDataFromResource,
                              std::move(ext_resource)),
               base::BindOnce(&RunCallbackWithCompressedData, size_hint_in_dip,
-                             is_placeholder_icon, icon_effects,
-                             std::move(callback)));
+                             default_icon_resource, is_placeholder_icon,
+                             icon_effects, std::move(callback)));
           return;
         }
       }
     }
   }
 
-  std::move(callback).Run(apps::mojom::IconValue::New());
+  // Fall back to the default_icon_resource.
+  LoadIconFromResource(icon_compression, size_hint_in_dip,
+                       default_icon_resource, is_placeholder_icon, icon_effects,
+                       std::move(callback));
 }
 
 void LoadIconFromFileWithFallback(
@@ -314,6 +350,11 @@
                           bool is_placeholder_icon,
                           IconEffects icon_effects,
                           apps::mojom::Publisher::LoadIconCallback callback) {
+  // This must be zero, to avoid a potential infinite loop if the
+  // RunCallbackWithXxx functions could otherwise call back into
+  // LoadIconFromResource.
+  constexpr int default_icon_resource = 0;
+
   if (resource_id != 0) {
     switch (icon_compression) {
       case apps::mojom::IconCompression::kUnknown:
@@ -324,8 +365,8 @@
             ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
                 resource_id);
         RunCallbackWithUncompressedImageSkia(
-            size_hint_in_dip, is_placeholder_icon, icon_effects,
-            std::move(callback),
+            size_hint_in_dip, default_icon_resource, is_placeholder_icon,
+            icon_effects, std::move(callback),
             gfx::ImageSkiaOperations::CreateResizedImage(
                 *unscaled, skia::ImageOperations::RESIZE_BEST,
                 gfx::Size(size_hint_in_dip, size_hint_in_dip)));
@@ -337,8 +378,8 @@
             ui::ResourceBundle::GetSharedInstance().GetRawDataResource(
                 resource_id);
         RunCallbackWithCompressedData(
-            size_hint_in_dip, is_placeholder_icon, icon_effects,
-            std::move(callback),
+            size_hint_in_dip, default_icon_resource, is_placeholder_icon,
+            icon_effects, std::move(callback),
             std::vector<uint8_t>(data.begin(), data.end()));
         return;
       }
diff --git a/chrome/browser/apps/intent_helper/apps_navigation_throttle.cc b/chrome/browser/apps/intent_helper/apps_navigation_throttle.cc
index 50f6d14..4530be32 100644
--- a/chrome/browser/apps/intent_helper/apps_navigation_throttle.cc
+++ b/chrome/browser/apps/intent_helper/apps_navigation_throttle.cc
@@ -26,12 +26,11 @@
 #include "content/public/browser/site_instance.h"
 #include "content/public/browser/web_contents.h"
 #include "extensions/browser/extension_registry.h"
+#include "extensions/common/constants.h"
 #include "url/origin.h"
 
 namespace {
 
-constexpr char kGoogleCom[] = "google.com";
-
 // Compares the host name of the referrer and target URL to decide whether
 // the navigation needs to be overridden.
 bool ShouldOverrideUrlLoading(const GURL& previous_url,
@@ -51,28 +50,10 @@
   // Check the scheme for both |previous_url| and |current_url| since an
   // extension could have referred us (e.g. Google Docs).
   if (!current_url.SchemeIsHTTPOrHTTPS() ||
-      !previous_url.SchemeIsHTTPOrHTTPS()) {
+      previous_url.SchemeIs(extensions::kExtensionScheme)) {
     return false;
   }
 
-  // TODO(dominickn): this was added as a special case for ARC. Reconsider if
-  // it's necessary for all app platforms.
-  if (net::registry_controlled_domains::SameDomainOrHost(
-          current_url, previous_url,
-          net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES)) {
-    if (net::registry_controlled_domains::GetDomainAndRegistry(
-            current_url,
-            net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES) ==
-        kGoogleCom) {
-      // Navigation within the google.com domain are good candidates for this
-      // throttle (and consecuently the picker UI) only if they have different
-      // hosts, this is because multiple services are hosted within the same
-      // domain e.g. play.google.com, mail.google.com and so on.
-      return current_url.host_piece() != previous_url.host_piece();
-    }
-
-    return false;
-  }
   return true;
 }
 
@@ -214,7 +195,8 @@
       ui_displayed_(false),
       ui_auto_display_service_(
           IntentPickerAutoDisplayService::Get(Profile::FromBrowserContext(
-              navigation_handle->GetWebContents()->GetBrowserContext()))) {
+              navigation_handle->GetWebContents()->GetBrowserContext()))),
+      navigate_from_link_(false) {
   // |ui_auto_display_service_| can be null iff the call is coming from
   // IntentPickerView. Since the pointer to our service is never modified
   // (in case it is successfully created here) this check covers all the
@@ -389,6 +371,10 @@
   return false;
 }
 
+bool AppsNavigationThrottle::navigate_from_link() {
+  return navigate_from_link_;
+}
+
 // static
 AppsNavigationThrottle::PickerAction AppsNavigationThrottle::GetPickerAction(
     apps::mojom::AppType app_type,
@@ -429,6 +415,8 @@
   content::NavigationHandle* handle = navigation_handle();
   DCHECK(!ui_displayed_);
 
+  navigate_from_link_ = false;
+
   // Always handle http(s) <form> submissions in Chrome for two reasons: 1) we
   // don't have a way to send POST data to ARC, and 2) intercepting http(s) form
   // submissions is not very important because such submissions are usually
@@ -439,23 +427,12 @@
   // Ignore navigations with the CLIENT_REDIRECT qualifier on.
   constexpr bool kAllowClientRedirect = false;
 
-  // We must never handle navigations started within a context menu.
-  if (handle->WasStartedFromContextMenu())
-    return content::NavigationThrottle::PROCEED;
-
   ui::PageTransition page_transition = handle->GetPageTransition();
   content::WebContents* web_contents = handle->GetWebContents();
   const GURL& url = handle->GetURL();
-  if (ShouldIgnoreNavigation(page_transition, kAllowFormSubmit,
-                             kAllowClientRedirect)) {
-    if ((page_transition & ui::PAGE_TRANSITION_FORWARD_BACK) ||
-        (page_transition & ui::PAGE_TRANSITION_FROM_ADDRESS_BAR)) {
-      // This enforces that whether we ignore the navigation or not, we make
-      // sure that the user cannot copy/paste or type a url to reuse
-      // ArcWebContentsData.
-      MaybeRemoveComingFromArcFlag(web_contents, starting_url_, url);
-    }
-    return content::NavigationThrottle::PROCEED;
+  if (!ShouldIgnoreNavigation(page_transition, kAllowFormSubmit,
+                              kAllowClientRedirect)) {
+    navigate_from_link_ = true;
   }
 
   MaybeRemoveComingFromArcFlag(web_contents, starting_url_, url);
diff --git a/chrome/browser/apps/intent_helper/apps_navigation_throttle.h b/chrome/browser/apps/intent_helper/apps_navigation_throttle.h
index 1262e01..9b3afd4 100644
--- a/chrome/browser/apps/intent_helper/apps_navigation_throttle.h
+++ b/chrome/browser/apps/intent_helper/apps_navigation_throttle.h
@@ -190,6 +190,8 @@
 
   virtual bool ShouldShowRememberSelection();
 
+  bool navigate_from_link();
+
   // Keeps track of whether we already shown the UI or preferred app. Since
   // AppsNavigationThrottle cannot wait for the user (due to the non-blocking
   // nature of the feature) the best we can do is check if we launched a
@@ -219,6 +221,11 @@
   // A reference to the starting GURL.
   GURL starting_url_;
 
+  // Keeps track of whether the navigation is coming from a link or not. If the
+  // navigation is not from a link, we will not show the pop up for the intent
+  // picker bubble.
+  bool navigate_from_link_;
+
   DISALLOW_COPY_AND_ASSIGN(AppsNavigationThrottle);
 };
 
diff --git a/chrome/browser/apps/intent_helper/apps_navigation_throttle_unittest.cc b/chrome/browser/apps/intent_helper/apps_navigation_throttle_unittest.cc
index 6f1235b..150a9698 100644
--- a/chrome/browser/apps/intent_helper/apps_navigation_throttle_unittest.cc
+++ b/chrome/browser/apps/intent_helper/apps_navigation_throttle_unittest.cc
@@ -10,52 +10,6 @@
 namespace apps {
 
 TEST(AppsNavigationThrottleTest, TestShouldOverrideUrlLoading) {
-  // A navigation within the same domain shouldn't be overridden except if the
-  // domain is google.com.
-  EXPECT_FALSE(AppsNavigationThrottle::ShouldOverrideUrlLoadingForTesting(
-      GURL("http://google.com"), GURL("http://google.com/")));
-  EXPECT_TRUE(AppsNavigationThrottle::ShouldOverrideUrlLoadingForTesting(
-      GURL("http://google.com"), GURL("http://a.google.com/")));
-  EXPECT_TRUE(AppsNavigationThrottle::ShouldOverrideUrlLoadingForTesting(
-      GURL("http://a.google.com"), GURL("http://google.com/")));
-  EXPECT_TRUE(AppsNavigationThrottle::ShouldOverrideUrlLoadingForTesting(
-      GURL("http://a.google.com"), GURL("http://b.google.com/")));
-  EXPECT_TRUE(AppsNavigationThrottle::ShouldOverrideUrlLoadingForTesting(
-      GURL("http://a.google.com"), GURL("http://b.c.google.com/")));
-  EXPECT_TRUE(AppsNavigationThrottle::ShouldOverrideUrlLoadingForTesting(
-      GURL("http://a.b.google.com"), GURL("http://c.google.com/")));
-  EXPECT_TRUE(AppsNavigationThrottle::ShouldOverrideUrlLoadingForTesting(
-      GURL("http://a.b.google.com"), GURL("http://b.google.com")));
-  EXPECT_TRUE(AppsNavigationThrottle::ShouldOverrideUrlLoadingForTesting(
-      GURL("http://b.google.com"), GURL("http://a.b.google.com")));
-  EXPECT_FALSE(AppsNavigationThrottle::ShouldOverrideUrlLoadingForTesting(
-      GURL("http://notg.com"), GURL("http://notg.com")));
-  EXPECT_FALSE(AppsNavigationThrottle::ShouldOverrideUrlLoadingForTesting(
-      GURL("http://a.notg.com"), GURL("http://notg.com")));
-  EXPECT_FALSE(AppsNavigationThrottle::ShouldOverrideUrlLoadingForTesting(
-      GURL("http://notg.com"), GURL("http://a.notg.com")));
-  EXPECT_FALSE(AppsNavigationThrottle::ShouldOverrideUrlLoadingForTesting(
-      GURL("http://a.notg.com"), GURL("http://b.notg.com")));
-  EXPECT_FALSE(AppsNavigationThrottle::ShouldOverrideUrlLoadingForTesting(
-      GURL("http://a.notg.com"), GURL("http://a.b.notg.com")));
-  EXPECT_FALSE(AppsNavigationThrottle::ShouldOverrideUrlLoadingForTesting(
-      GURL("http://a.b.notg.com"), GURL("http://c.notg.com")));
-
-  // Same as last tests, except for "play.google.com".
-  EXPECT_TRUE(AppsNavigationThrottle::ShouldOverrideUrlLoadingForTesting(
-      GURL("http://google.com"), GURL("http://play.google.com/fake_app")));
-  EXPECT_TRUE(AppsNavigationThrottle::ShouldOverrideUrlLoadingForTesting(
-      GURL("https://www.google.com.mx"),
-      GURL("https://play.google.com/fake_app")));
-  EXPECT_TRUE(AppsNavigationThrottle::ShouldOverrideUrlLoadingForTesting(
-      GURL("https://mail.google.com"),
-      GURL("https://play.google.com/fake_app")));
-  EXPECT_FALSE(AppsNavigationThrottle::ShouldOverrideUrlLoadingForTesting(
-      GURL("https://play.google.com/search"),
-      GURL("https://play.google.com/fake_app")));
-  EXPECT_TRUE(AppsNavigationThrottle::ShouldOverrideUrlLoadingForTesting(
-      GURL("http://not_google.com"), GURL("http://play.google.com/fake_app")));
-
   // If either of two paramters is empty, the function should return false.
   EXPECT_FALSE(AppsNavigationThrottle::ShouldOverrideUrlLoadingForTesting(
       GURL(), GURL("http://a.google.com/")));
@@ -64,23 +18,48 @@
   EXPECT_FALSE(AppsNavigationThrottle::ShouldOverrideUrlLoadingForTesting(
       GURL(), GURL()));
 
-  // A navigation not within the same domain can be overridden.
+  // A navigation to an a url that is neither an http nor https scheme cannot be
+  // override.
+  EXPECT_FALSE(AppsNavigationThrottle::ShouldOverrideUrlLoadingForTesting(
+      GURL("http://www.a.com"), GURL("chrome-extension://fake_document")));
+  EXPECT_FALSE(AppsNavigationThrottle::ShouldOverrideUrlLoadingForTesting(
+      GURL("https://www.a.com"), GURL("chrome-extension://fake_document")));
+  EXPECT_FALSE(AppsNavigationThrottle::ShouldOverrideUrlLoadingForTesting(
+      GURL("http://www.a.com"), GURL("chrome://fake_document")));
+  EXPECT_FALSE(AppsNavigationThrottle::ShouldOverrideUrlLoadingForTesting(
+      GURL("http://www.a.com"), GURL("file://fake_document")));
+  EXPECT_FALSE(AppsNavigationThrottle::ShouldOverrideUrlLoadingForTesting(
+      GURL("https://www.a.com"), GURL("chrome://fake_document")));
+  EXPECT_FALSE(AppsNavigationThrottle::ShouldOverrideUrlLoadingForTesting(
+      GURL("https://www.a.com"), GURL("file://fake_document")));
+
+  // A navigation from chrome-extension scheme cannot be overriden.
+  EXPECT_FALSE(AppsNavigationThrottle::ShouldOverrideUrlLoadingForTesting(
+      GURL("chrome-extension://fake_document"), GURL("http://www.a.com")));
+  EXPECT_FALSE(AppsNavigationThrottle::ShouldOverrideUrlLoadingForTesting(
+      GURL("chrome-extension://fake_document"), GURL("https://www.a.com")));
+  EXPECT_FALSE(AppsNavigationThrottle::ShouldOverrideUrlLoadingForTesting(
+      GURL("chrome-extension://fake_a"), GURL("chrome-extension://fake_b")));
+
+  // Other navigations can be overridden.
   EXPECT_TRUE(AppsNavigationThrottle::ShouldOverrideUrlLoadingForTesting(
       GURL("http://www.google.com"), GURL("http://www.not-google.com/")));
   EXPECT_TRUE(AppsNavigationThrottle::ShouldOverrideUrlLoadingForTesting(
       GURL("http://www.not-google.com"), GURL("http://www.google.com/")));
-
-  // A navigation with neither an http nor https scheme cannot be overriden.
-  EXPECT_FALSE(AppsNavigationThrottle::ShouldOverrideUrlLoadingForTesting(
-      GURL("chrome-extension://fake_document"), GURL("http://www.a.com")));
-  EXPECT_FALSE(AppsNavigationThrottle::ShouldOverrideUrlLoadingForTesting(
-      GURL("http://www.a.com"), GURL("chrome-extension://fake_document")));
-  EXPECT_FALSE(AppsNavigationThrottle::ShouldOverrideUrlLoadingForTesting(
-      GURL("chrome-extension://fake_document"), GURL("https://www.a.com")));
-  EXPECT_FALSE(AppsNavigationThrottle::ShouldOverrideUrlLoadingForTesting(
-      GURL("https://www.a.com"), GURL("chrome-extension://fake_document")));
-  EXPECT_FALSE(AppsNavigationThrottle::ShouldOverrideUrlLoadingForTesting(
-      GURL("chrome-extension://fake_a"), GURL("chrome-extension://fake_b")));
+  EXPECT_TRUE(AppsNavigationThrottle::ShouldOverrideUrlLoadingForTesting(
+      GURL("http://www.google.com"), GURL("http://www.google.com/")));
+  EXPECT_TRUE(AppsNavigationThrottle::ShouldOverrideUrlLoadingForTesting(
+      GURL("http://a.google.com"), GURL("http://b.google.com/")));
+  EXPECT_TRUE(AppsNavigationThrottle::ShouldOverrideUrlLoadingForTesting(
+      GURL("http://a.not-google.com"), GURL("http://b.not-google.com")));
+  EXPECT_TRUE(AppsNavigationThrottle::ShouldOverrideUrlLoadingForTesting(
+      GURL("chrome://fake_document"), GURL("http://www.a.com")));
+  EXPECT_TRUE(AppsNavigationThrottle::ShouldOverrideUrlLoadingForTesting(
+      GURL("file://fake_document"), GURL("http://www.a.com")));
+  EXPECT_TRUE(AppsNavigationThrottle::ShouldOverrideUrlLoadingForTesting(
+      GURL("chrome://fake_document"), GURL("https://www.a.com")));
+  EXPECT_TRUE(AppsNavigationThrottle::ShouldOverrideUrlLoadingForTesting(
+      GURL("file://fake_document"), GURL("https://www.a.com")));
 }
 
 TEST(AppsNavigationThrottleTest, TestGetPickerAction) {
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index a6f9d4d..8985978 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -186,6 +186,7 @@
     "//components/translate/core/browser:browser",
     "//components/ukm/content",
     "//components/user_manager",
+    "//components/vector_icons",
 
     # This depends directly on the variations target, rather than just
     # transitively via the common target because the proto sources need to
@@ -2294,8 +2295,8 @@
     "diagnosticsd/diagnosticsd_manager_unittest.cc",
     "diagnosticsd/diagnosticsd_messaging_unittest.cc",
     "diagnosticsd/diagnosticsd_web_request_service_unittest.cc",
-    "diagnosticsd/testing_diagnosticsd_bridge.cc",
-    "diagnosticsd/testing_diagnosticsd_bridge.h",
+    "diagnosticsd/testing_diagnosticsd_bridge_wrapper.cc",
+    "diagnosticsd/testing_diagnosticsd_bridge_wrapper.h",
     "drive/download_handler_unittest.cc",
     "drive/drive_file_stream_reader_unittest.cc",
     "drive/drive_integration_service_unittest.cc",
diff --git a/chrome/browser/chromeos/apps/intent_helper/chromeos_apps_navigation_throttle.cc b/chrome/browser/chromeos/apps/intent_helper/chromeos_apps_navigation_throttle.cc
index 66a097bf..f1c376a8 100644
--- a/chrome/browser/chromeos/apps/intent_helper/chromeos_apps_navigation_throttle.cc
+++ b/chrome/browser/chromeos/apps/intent_helper/chromeos_apps_navigation_throttle.cc
@@ -246,7 +246,8 @@
     const std::vector<apps::IntentPickerAppInfo>& apps_for_picker,
     content::WebContents* web_contents,
     const GURL& url) {
-  return ShouldAutoDisplayUi(apps_for_picker, web_contents, url)
+  return ShouldAutoDisplayUi(apps_for_picker, web_contents, url) &&
+                 navigate_from_link()
              ? PickerShowState::kPopOut
              : PickerShowState::kOmnibox;
 }
diff --git a/chrome/browser/chromeos/dbus/dbus_helper.cc b/chrome/browser/chromeos/dbus/dbus_helper.cc
index c21988bd..c12fba2 100644
--- a/chrome/browser/chromeos/dbus/dbus_helper.cc
+++ b/chrome/browser/chromeos/dbus/dbus_helper.cc
@@ -14,7 +14,6 @@
 #include "chromeos/dbus/auth_policy/auth_policy_client.h"
 #include "chromeos/dbus/biod/biod_client.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
-#include "chromeos/dbus/hammerd/hammerd_client.h"
 #include "chromeos/dbus/kerberos/kerberos_client.h"
 #include "chromeos/dbus/machine_learning/machine_learning_client.h"
 #include "chromeos/dbus/media_analytics/media_analytics_client.h"
diff --git a/chrome/browser/chromeos/diagnosticsd/OWNERS b/chrome/browser/chromeos/diagnosticsd/OWNERS
index f301e83..b363b3e 100644
--- a/chrome/browser/chromeos/diagnosticsd/OWNERS
+++ b/chrome/browser/chromeos/diagnosticsd/OWNERS
@@ -1,3 +1,6 @@
+pbond@chromium.org
+
+# Backup reviewers:
 emaxx@chromium.org
 pmarko@chromium.org
 
diff --git a/chrome/browser/chromeos/diagnosticsd/diagnosticsd_bridge.cc b/chrome/browser/chromeos/diagnosticsd/diagnosticsd_bridge.cc
index edffab2..bc6b7dc3 100644
--- a/chrome/browser/chromeos/diagnosticsd/diagnosticsd_bridge.cc
+++ b/chrome/browser/chromeos/diagnosticsd/diagnosticsd_bridge.cc
@@ -122,6 +122,14 @@
   g_diagnosticsd_bridge_instance = nullptr;
 }
 
+void DiagnosticsdBridge::SetConfigurationData(const std::string* data) {
+  configuration_data_ = data;
+}
+
+const std::string& DiagnosticsdBridge::GetConfigurationDataForTesting() {
+  return configuration_data_ ? *configuration_data_ : base::EmptyString();
+}
+
 void DiagnosticsdBridge::WaitForDBusService() {
   if (connection_attempt_ >= kMaxConnectionAttemptCount) {
     DLOG(WARNING) << "Stopping attempts to connect to diagnosticsd - too many "
@@ -282,6 +290,12 @@
       std::move(request_body_content), std::move(callback));
 }
 
+void DiagnosticsdBridge::GetConfigurationData(
+    GetConfigurationDataCallback callback) {
+  std::move(callback).Run(configuration_data_ ? *configuration_data_
+                                              : std::string());
+}
+
 void DiagnosticsdBridge::SendDiagnosticsProcessorMessageToUi(
     mojo::ScopedHandle json_message,
     SendDiagnosticsProcessorMessageToUiCallback callback) {
diff --git a/chrome/browser/chromeos/diagnosticsd/diagnosticsd_bridge.h b/chrome/browser/chromeos/diagnosticsd/diagnosticsd_bridge.h
index 642e837..33ff8c6 100644
--- a/chrome/browser/chromeos/diagnosticsd/diagnosticsd_bridge.h
+++ b/chrome/browser/chromeos/diagnosticsd/diagnosticsd_bridge.h
@@ -62,6 +62,12 @@
 
   ~DiagnosticsdBridge() override;
 
+  // Sets the Wilco DTC configuration data, passed and owned by the
+  // |DiagnosticsdManager| from the device policy.
+  // The nullptr should be passed to clear it.
+  void SetConfigurationData(const std::string* data);
+  const std::string& GetConfigurationDataForTesting();
+
   // Mojo proxy to the DiagnosticsdService implementation in the diagnosticsd
   // daemon. Returns null when bootstrapping of Mojo connection hasn't started
   // yet. Note that, however, non-null is already returned before the
@@ -101,6 +107,7 @@
   void SendDiagnosticsProcessorMessageToUi(
       mojo::ScopedHandle json_message,
       SendDiagnosticsProcessorMessageToUiCallback callback) override;
+  void GetConfigurationData(GetConfigurationDataCallback callback) override;
 
   std::unique_ptr<Delegate> delegate_;
 
@@ -120,6 +127,11 @@
   // The service to perform diagnostics_processor's web requests.
   DiagnosticsdWebRequestService web_request_service_;
 
+  // The Wilco DTC configuration data blob, passed from the device policy, is
+  // stored and owned by |DiagnosticsdManager|.
+  // nullptr if there is no available configuration data for the Wilco DTC.
+  const std::string* configuration_data_ = nullptr;
+
   // These weak pointer factories must be the last members:
 
   // Used for cancelling previously posted tasks that wait for the D-Bus service
diff --git a/chrome/browser/chromeos/diagnosticsd/diagnosticsd_bridge_unittest.cc b/chrome/browser/chromeos/diagnosticsd/diagnosticsd_bridge_unittest.cc
index e6f7264..88c8368 100644
--- a/chrome/browser/chromeos/diagnosticsd/diagnosticsd_bridge_unittest.cc
+++ b/chrome/browser/chromeos/diagnosticsd/diagnosticsd_bridge_unittest.cc
@@ -34,6 +34,7 @@
   MOCK_METHOD2(SendUiMessageToDiagnosticsProcessor,
                void(mojo::ScopedHandle,
                     SendUiMessageToDiagnosticsProcessorCallback));
+  MOCK_METHOD0(NotifyConfigurationDataChanged, void());
 };
 
 // Fake implementation of the DiagnosticsdServiceFactory Mojo interface that
diff --git a/chrome/browser/chromeos/diagnosticsd/diagnosticsd_manager.cc b/chrome/browser/chromeos/diagnosticsd/diagnosticsd_manager.cc
index cf57dc08..755a11c 100644
--- a/chrome/browser/chromeos/diagnosticsd/diagnosticsd_manager.cc
+++ b/chrome/browser/chromeos/diagnosticsd/diagnosticsd_manager.cc
@@ -23,6 +23,32 @@
 
 namespace {
 
+DiagnosticsdManager* g_diagnosticsd_manager_instance = nullptr;
+
+class DiagnosticsdManagerDelegateImpl final
+    : public DiagnosticsdManager::Delegate {
+ public:
+  DiagnosticsdManagerDelegateImpl();
+  ~DiagnosticsdManagerDelegateImpl() override;
+
+  // Delegate overrides:
+  std::unique_ptr<DiagnosticsdBridge> CreateDiagnosticsdBridge() override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(DiagnosticsdManagerDelegateImpl);
+};
+
+DiagnosticsdManagerDelegateImpl::DiagnosticsdManagerDelegateImpl() = default;
+
+DiagnosticsdManagerDelegateImpl::~DiagnosticsdManagerDelegateImpl() = default;
+
+std::unique_ptr<DiagnosticsdBridge>
+DiagnosticsdManagerDelegateImpl::CreateDiagnosticsdBridge() {
+  return std::make_unique<DiagnosticsdBridge>(
+      g_browser_process->system_network_context_manager()
+          ->GetSharedURLLoaderFactory());
+}
+
 // Returns true if only affiliated users are logged-in.
 bool AreOnlyAffiliatedUsersLoggedIn() {
   const user_manager::UserList logged_in_users =
@@ -37,8 +63,24 @@
 
 }  // namespace
 
+DiagnosticsdManager::Delegate::~Delegate() = default;
+
+// static
+DiagnosticsdManager* DiagnosticsdManager::Get() {
+  return g_diagnosticsd_manager_instance;
+}
+
 DiagnosticsdManager::DiagnosticsdManager()
-    : callback_weak_ptr_factory_(this), weak_ptr_factory_(this) {
+    : DiagnosticsdManager(std::make_unique<DiagnosticsdManagerDelegateImpl>()) {
+}
+
+DiagnosticsdManager::DiagnosticsdManager(std::unique_ptr<Delegate> delegate)
+    : delegate_(std::move(delegate)),
+      callback_weak_ptr_factory_(this),
+      weak_ptr_factory_(this) {
+  DCHECK(delegate_);
+  DCHECK(!g_diagnosticsd_manager_instance);
+  g_diagnosticsd_manager_instance = this;
   wilco_dtc_allowed_observer_ = CrosSettings::Get()->AddSettingsObserver(
       kDeviceWilcoDtcAllowed,
       base::BindRepeating(&DiagnosticsdManager::StartOrStopWilcoDtc,
@@ -50,12 +92,35 @@
 }
 
 DiagnosticsdManager::~DiagnosticsdManager() {
+  DCHECK_EQ(g_diagnosticsd_manager_instance, this);
+  g_diagnosticsd_manager_instance = nullptr;
+
   // The destruction may mean that non-affiliated user is logging out.
   StartOrStopWilcoDtc();
 
   session_manager::SessionManager::Get()->RemoveObserver(this);
 }
 
+void DiagnosticsdManager::SetConfigurationData(
+    std::unique_ptr<std::string> data) {
+  configuration_data_ = std::move(data);
+
+  if (!diagnosticsd_bridge_) {
+    VLOG(0) << "Cannot send notification - no bridge to the daemon";
+    return;
+  }
+  diagnosticsd_bridge_->SetConfigurationData(configuration_data_.get());
+
+  diagnosticsd::mojom::DiagnosticsdServiceProxy* const diagnosticsd_mojo_proxy =
+      diagnosticsd_bridge_->diagnosticsd_service_mojo_proxy();
+  if (!diagnosticsd_mojo_proxy) {
+    VLOG(0) << "Cannot send message - Mojo connection to the daemon isn't "
+               "bootstrapped yet";
+    return;
+  }
+  diagnosticsd_mojo_proxy->NotifyConfigurationDataChanged();
+}
+
 void DiagnosticsdManager::OnSessionStateChanged() {
   session_manager::SessionState session_state =
       session_manager::SessionManager::Get()->session_state();
@@ -95,11 +160,13 @@
     DLOG(ERROR) << "Failed to start the wilco DTC";
   } else {
     VLOG(1) << "Wilco DTC started";
-    if (!diagnosticsd_bridge_) {
-      diagnosticsd_bridge_ = std::make_unique<DiagnosticsdBridge>(
-          g_browser_process->system_network_context_manager()
-              ->GetSharedURLLoaderFactory());
-    }
+    if (!diagnosticsd_bridge_)
+      diagnosticsd_bridge_ = delegate_->CreateDiagnosticsdBridge();
+    DCHECK(diagnosticsd_bridge_);
+
+    // Once the bridge is created, notify it about an available configuration
+    // data blob.
+    diagnosticsd_bridge_->SetConfigurationData(configuration_data_.get());
   }
 }
 
diff --git a/chrome/browser/chromeos/diagnosticsd/diagnosticsd_manager.h b/chrome/browser/chromeos/diagnosticsd/diagnosticsd_manager.h
index ef0f1ed6..49581e93 100644
--- a/chrome/browser/chromeos/diagnosticsd/diagnosticsd_manager.h
+++ b/chrome/browser/chromeos/diagnosticsd/diagnosticsd_manager.h
@@ -6,6 +6,7 @@
 #define CHROME_BROWSER_CHROMEOS_DIAGNOSTICSD_DIAGNOSTICSD_MANAGER_H_
 
 #include <memory>
+#include <string>
 
 #include "base/callback.h"
 #include "base/macros.h"
@@ -27,9 +28,28 @@
  public:
   using WilcoDtcCallback = base::OnceCallback<void(bool)>;
 
+  // Delegate class, allowing to pass a stub diagnosticsd bridge in unit tests.
+  class Delegate {
+   public:
+    virtual ~Delegate();
+    // Returns a DiagnosticsdBridge instance.
+    virtual std::unique_ptr<DiagnosticsdBridge> CreateDiagnosticsdBridge() = 0;
+  };
+
+  // Returns the global singleton instance.
+  static DiagnosticsdManager* Get();
+
   DiagnosticsdManager();
+  // For use in tests.
+  explicit DiagnosticsdManager(std::unique_ptr<Delegate> delegate);
+
   ~DiagnosticsdManager() override;
 
+  // Sets the Wilco DTC configuration data, passed by the device policy.
+  // The nullptr should be passed to clear it.
+  // Notifies the |diagnosticsd_bridge_| if it is created.
+  void SetConfigurationData(std::unique_ptr<std::string> data);
+
  private:
   // session_manager::SessionManagerObserver override:
   void OnSessionStateChanged() override;
@@ -46,10 +66,15 @@
   void OnStartWilcoDtc(bool success);
   void OnStopWilcoDtc(bool success);
 
+  std::unique_ptr<Delegate> delegate_;
+
   // Observer to changes in the wilco DTC allowed policy.
   std::unique_ptr<CrosSettings::ObserverSubscription>
       wilco_dtc_allowed_observer_;
 
+  // The configuration data blob is stored and owned.
+  std::unique_ptr<std::string> configuration_data_;
+
   std::unique_ptr<DiagnosticsdBridge> diagnosticsd_bridge_;
 
   // |callback_weak_factory_ptr_| is used only in Stop/StartWilcoDtc to be able
diff --git a/chrome/browser/chromeos/diagnosticsd/diagnosticsd_manager_unittest.cc b/chrome/browser/chromeos/diagnosticsd/diagnosticsd_manager_unittest.cc
index b620513..2c0ab740 100644
--- a/chrome/browser/chromeos/diagnosticsd/diagnosticsd_manager_unittest.cc
+++ b/chrome/browser/chromeos/diagnosticsd/diagnosticsd_manager_unittest.cc
@@ -3,66 +3,103 @@
 // found in the LICENSE file.
 
 #include "chrome/browser/chromeos/diagnosticsd/diagnosticsd_manager.h"
+
+#include "base/barrier_closure.h"
 #include "base/memory/ptr_util.h"
 #include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "chrome/browser/chromeos/diagnosticsd/testing_diagnosticsd_bridge_wrapper.h"
 #include "chrome/browser/chromeos/login/users/fake_chrome_user_manager.h"
 #include "chrome/browser/chromeos/settings/scoped_testing_cros_settings.h"
 #include "chrome/browser/chromeos/settings/stub_cros_settings_provider.h"
+#include "chrome/services/diagnosticsd/public/mojom/diagnosticsd.mojom.h"
+#include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/upstart/fake_upstart_client.h"
 #include "components/session_manager/core/session_manager.h"
 #include "components/session_manager/session_manager_types.h"
 #include "components/user_manager/scoped_user_manager.h"
 #include "components/user_manager/user_names.h"
+#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
+#include "services/network/test/test_url_loader_factory.h"
+#include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+using testing::Invoke;
+using testing::StrictMock;
+
 namespace chromeos {
 
 namespace {
 
-// An implementation of Upstart Client that tracks StartWilcoDtcService() /
-// StopWilcoDtcService() calls.
+// An implementation of Upstart Client that fakes a start/ stop of wilco DTC
+// services on StartWilcoDtcService() / StopWilcoDtcService() calls.
 class TestUpstartClient final : public FakeUpstartClient {
  public:
   // FakeUpstartClient overrides:
   void StartWilcoDtcService(
       chromeos::VoidDBusMethodCallback callback) override {
-    is_wilco_dtc_started_ = true;
-    EXPECT_FALSE(is_callback_called_);
-    is_callback_called_ = true;
+    std::move(callback).Run(true /* success */);
   }
 
   void StopWilcoDtcService(chromeos::VoidDBusMethodCallback callback) override {
-    is_wilco_dtc_started_ = false;
-    EXPECT_FALSE(is_callback_called_);
-    is_callback_called_ = true;
+    std::move(callback).Run(true /* success */);
   }
+};
 
-  // Returns true if wilco DTC support services are started.
-  // Also resets the internal state, allowing new StartWilcoDtciService() /
-  // StopWilcoDtcService() calls.
-  bool GetAndResetWilcoDtcStarted() {
-    EXPECT_TRUE(is_callback_called_);
-    is_callback_called_ = false;
-    return is_wilco_dtc_started_;
+class MockMojoDiagnosticsdService
+    : public diagnosticsd::mojom::DiagnosticsdService {
+ public:
+  MOCK_METHOD2(SendUiMessageToDiagnosticsProcessor,
+               void(mojo::ScopedHandle,
+                    SendUiMessageToDiagnosticsProcessorCallback));
+
+  MOCK_METHOD0(NotifyConfigurationDataChanged, void());
+};
+
+// An implementation of the DiagnosticsdManager::Delegate that owns the testing
+// instance of the DiagnosticsdBridge.
+class FakeDiagnosticsdManagerDelegate final
+    : public DiagnosticsdManager::Delegate {
+ public:
+  FakeDiagnosticsdManagerDelegate(
+      MockMojoDiagnosticsdService* mojo_diagnosticsd_service)
+      : mojo_diagnosticsd_service_(mojo_diagnosticsd_service) {}
+
+  // DiagnosticsdManager::Delegate overrides:
+  std::unique_ptr<DiagnosticsdBridge> CreateDiagnosticsdBridge() override {
+    std::unique_ptr<DiagnosticsdBridge> diagnosticsd_bridge;
+    testing_diagnosticsd_bridge_wrapper_ =
+        TestingDiagnosticsdBridgeWrapper::Create(
+            mojo_diagnosticsd_service_,
+            base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
+                &test_url_loader_factory_),
+            &diagnosticsd_bridge);
+    DCHECK(diagnosticsd_bridge);
+    testing_diagnosticsd_bridge_wrapper_->EstablishFakeMojoConnection();
+    return diagnosticsd_bridge;
   }
 
  private:
-  bool is_wilco_dtc_started_ = false;
-
-  // Identifies whether StartWilcoDtcService() / StopWilcoDtcService() were
-  // called since the last GetAndResetWilcoDtcStarted() obtained the state of
-  // wilco DTC.
-  bool is_callback_called_ = false;
+  network::TestURLLoaderFactory test_url_loader_factory_;
+  std::unique_ptr<TestingDiagnosticsdBridgeWrapper>
+      testing_diagnosticsd_bridge_wrapper_;
+  MockMojoDiagnosticsdService* mojo_diagnosticsd_service_;
 };
 
 // Tests DiagnosticsdManager class instance.
 class DiagnosticsdManagerTest : public testing::Test {
  protected:
   DiagnosticsdManagerTest() {
+    DBusThreadManager::Initialize();
     upstart_client_ = std::make_unique<TestUpstartClient>();
   }
 
-  ~DiagnosticsdManagerTest() override {}
+  ~DiagnosticsdManagerTest() override { DBusThreadManager::Shutdown(); }
+
+  std::unique_ptr<DiagnosticsdManager::Delegate> CreateDelegate() {
+    return std::make_unique<FakeDiagnosticsdManagerDelegate>(
+        &mojo_diagnosticsd_service_);
+  }
 
   void SetWilcoDtcAllowedPolicy(bool wilco_dtc_allowed) {
     scoped_testing_cros_settings_.device_settings()->SetBoolean(
@@ -77,8 +114,8 @@
     session_manager_.SetSessionState(session_manager::SessionState::ACTIVE);
   }
 
-  bool is_wilco_dtc_started() {
-    return upstart_client_->GetAndResetWilcoDtcStarted();
+  MockMojoDiagnosticsdService* mojo_diagnosticsd_service() {
+    return &mojo_diagnosticsd_service_;
   }
 
  private:
@@ -89,54 +126,55 @@
   user_manager::ScopedUserManager scoped_user_manager_{
       base::WrapUnique(fake_user_manager_)};
   session_manager::SessionManager session_manager_;
+  StrictMock<MockMojoDiagnosticsdService> mojo_diagnosticsd_service_;
 };
 
 // Test that wilco DTC support services are not started on enterprise enrolled
 // devices with a certain device policy unset.
 TEST_F(DiagnosticsdManagerTest, EnterpriseiWilcoDtcBasic) {
-  DiagnosticsdManager diagnosticsd_manager;
-  EXPECT_FALSE(is_wilco_dtc_started());
+  DiagnosticsdManager diagnosticsd_manager(CreateDelegate());
+  EXPECT_FALSE(DiagnosticsdBridge::Get());
 }
 
 // Test that wilco DTC support services are not started if disabled by device
 // policy.
 TEST_F(DiagnosticsdManagerTest, EnterpriseWilcoDtcDisabled) {
-  DiagnosticsdManager diagnosticsd_manager;
-  EXPECT_FALSE(is_wilco_dtc_started());
+  DiagnosticsdManager diagnosticsd_manager(CreateDelegate());
+  EXPECT_FALSE(DiagnosticsdBridge::Get());
 
   SetWilcoDtcAllowedPolicy(false);
-  EXPECT_FALSE(is_wilco_dtc_started());
+  EXPECT_FALSE(DiagnosticsdBridge::Get());
 }
 
 // Test that wilco DTC support services are started if enabled by policy.
 TEST_F(DiagnosticsdManagerTest, EnterpriseWilcoDtcAllowed) {
   SetWilcoDtcAllowedPolicy(true);
-  DiagnosticsdManager diagosticsd_manager;
-  EXPECT_TRUE(is_wilco_dtc_started());
+  DiagnosticsdManager diagnosticsd_manager(CreateDelegate());
+  EXPECT_TRUE(DiagnosticsdBridge::Get());
 }
 
 // Test that wilco DTC support services are not started if non-affiliated user
 // is logged-in.
 TEST_F(DiagnosticsdManagerTest, EnterpriseNonAffiliatedUserLoggedIn) {
-  DiagnosticsdManager diagosticsd_manager;
-  EXPECT_FALSE(is_wilco_dtc_started());
+  DiagnosticsdManager diagnosticsd_manager(CreateDelegate());
+  EXPECT_FALSE(DiagnosticsdBridge::Get());
 
   SetWilcoDtcAllowedPolicy(true);
-  EXPECT_TRUE(is_wilco_dtc_started());
+  EXPECT_TRUE(DiagnosticsdBridge::Get());
 
   LogInUser(false);
-  EXPECT_FALSE(is_wilco_dtc_started());
+  EXPECT_FALSE(DiagnosticsdBridge::Get());
 }
 
 // Test that wilco DTC support services are started if enabled by device policy
 // and affiliated user is logged-in.
 TEST_F(DiagnosticsdManagerTest, EnterpriseAffiliatedUserLoggedIn) {
   SetWilcoDtcAllowedPolicy(true);
-  DiagnosticsdManager diagosticsd_manager;
-  EXPECT_TRUE(is_wilco_dtc_started());
+  DiagnosticsdManager diagnosticsd_manager(CreateDelegate());
+  EXPECT_TRUE(DiagnosticsdBridge::Get());
 
   LogInUser(true);
-  EXPECT_TRUE(is_wilco_dtc_started());
+  EXPECT_TRUE(DiagnosticsdBridge::Get());
 }
 
 // Test that wilco DTC support services are not started if non-affiliated user
@@ -144,9 +182,59 @@
 TEST_F(DiagnosticsdManagerTest, EnterpriseNonAffiliatedUserLoggedInBefore) {
   SetWilcoDtcAllowedPolicy(true);
   LogInUser(false);
-  DiagnosticsdManager diagnosticsd_manager;
+  DiagnosticsdManager diagnosticsd_manager(CreateDelegate());
 
-  EXPECT_FALSE(is_wilco_dtc_started());
+  EXPECT_FALSE(DiagnosticsdBridge::Get());
+}
+
+// Test that wilco DTC support services are properly notified about the changes
+// of configuration data.
+TEST_F(DiagnosticsdManagerTest, ConfigurationData) {
+  constexpr char kFakeConfigurationData[] =
+      "{\"fake-message\": \"Fake JSON configuration data\"}";
+
+  DiagnosticsdManager diagnosticsd_manager(CreateDelegate());
+  EXPECT_FALSE(DiagnosticsdBridge::Get());
+
+  SetWilcoDtcAllowedPolicy(true);
+  EXPECT_TRUE(DiagnosticsdBridge::Get());
+  // An empty configuration data by default.
+  EXPECT_TRUE(
+      DiagnosticsdBridge::Get()->GetConfigurationDataForTesting().empty());
+
+  // Set a non-empty configuration data.
+  {
+    base::RunLoop run_loop;
+    EXPECT_CALL(*mojo_diagnosticsd_service(), NotifyConfigurationDataChanged())
+        .WillOnce(Invoke([&run_loop]() { run_loop.Quit(); }));
+
+    diagnosticsd_manager.SetConfigurationData(
+        std::make_unique<std::string>(kFakeConfigurationData));
+    EXPECT_EQ(kFakeConfigurationData,
+              DiagnosticsdBridge::Get()->GetConfigurationDataForTesting());
+    run_loop.Run();
+  }
+
+  // Restart the bridge.
+  SetWilcoDtcAllowedPolicy(false);
+  EXPECT_FALSE(DiagnosticsdBridge::Get());
+  SetWilcoDtcAllowedPolicy(true);
+  EXPECT_TRUE(DiagnosticsdBridge::Get());
+
+  // The configuration data has not been changed.
+  EXPECT_EQ(kFakeConfigurationData,
+            DiagnosticsdBridge::Get()->GetConfigurationDataForTesting());
+
+  // Clear the configuration data.
+  {
+    base::RunLoop run_loop;
+    EXPECT_CALL(*mojo_diagnosticsd_service(), NotifyConfigurationDataChanged())
+        .WillOnce(Invoke([&run_loop]() { run_loop.Quit(); }));
+    diagnosticsd_manager.SetConfigurationData(nullptr);
+    EXPECT_TRUE(
+        DiagnosticsdBridge::Get()->GetConfigurationDataForTesting().empty());
+    run_loop.Run();
+  }
 }
 
 }  // namespace
diff --git a/chrome/browser/chromeos/diagnosticsd/diagnosticsd_messaging_unittest.cc b/chrome/browser/chromeos/diagnosticsd/diagnosticsd_messaging_unittest.cc
index 69ed7ce..6b98724 100644
--- a/chrome/browser/chromeos/diagnosticsd/diagnosticsd_messaging_unittest.cc
+++ b/chrome/browser/chromeos/diagnosticsd/diagnosticsd_messaging_unittest.cc
@@ -15,7 +15,7 @@
 #include "base/test/scoped_task_environment.h"
 #include "chrome/browser/chromeos/diagnosticsd/diagnosticsd_messaging.h"
 #include "chrome/browser/chromeos/diagnosticsd/mojo_utils.h"
-#include "chrome/browser/chromeos/diagnosticsd/testing_diagnosticsd_bridge.h"
+#include "chrome/browser/chromeos/diagnosticsd/testing_diagnosticsd_bridge_wrapper.h"
 #include "chrome/services/diagnosticsd/public/mojom/diagnosticsd.mojom.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "extensions/browser/api/messaging/native_message_host.h"
@@ -97,6 +97,7 @@
   MOCK_METHOD2(SendUiMessageToDiagnosticsProcessorImpl,
                void(const std::string& json_message,
                     SendUiMessageToDiagnosticsProcessorImplCallback callback));
+  MOCK_METHOD0(NotifyConfigurationDataChanged, void());
 };
 
 }  // namespace
@@ -126,10 +127,12 @@
  protected:
   DiagnosticsdMessagingOpenedByExtensionTest() {
     DBusThreadManager::Initialize();
-    diagnosticsd_bridge_ = std::make_unique<TestingDiagnosticsdBridge>(
-        &mojo_diagnosticsd_service_,
-        base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
-            &test_url_loader_factory_));
+    testing_diagnosticsd_bridge_wrapper_ =
+        TestingDiagnosticsdBridgeWrapper::Create(
+            &mojo_diagnosticsd_service_,
+            base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
+                &test_url_loader_factory_),
+            &diagnosticsd_bridge_);
   }
 
   ~DiagnosticsdMessagingOpenedByExtensionTest() override {
@@ -143,8 +146,8 @@
     return &mojo_diagnosticsd_service_;
   }
 
-  TestingDiagnosticsdBridge* diagnosticsd_bridge() {
-    return diagnosticsd_bridge_.get();
+  TestingDiagnosticsdBridgeWrapper* diagnosticsd_bridge_wrapper() {
+    return testing_diagnosticsd_bridge_wrapper_.get();
   }
 
   void RunUntilIdle() { scoped_task_environment_.RunUntilIdle(); }
@@ -153,7 +156,9 @@
   base::test::ScopedTaskEnvironment scoped_task_environment_;
   StrictMock<MockMojoDiagnosticsdService> mojo_diagnosticsd_service_;
   network::TestURLLoaderFactory test_url_loader_factory_;
-  std::unique_ptr<TestingDiagnosticsdBridge> diagnosticsd_bridge_;
+  std::unique_ptr<TestingDiagnosticsdBridgeWrapper>
+      testing_diagnosticsd_bridge_wrapper_;
+  std::unique_ptr<DiagnosticsdBridge> diagnosticsd_bridge_;
 };
 
 }  // namespace
@@ -181,7 +186,7 @@
     : public DiagnosticsdMessagingOpenedByExtensionTest {
  protected:
   DiagnosticsdMessagingOpenedByExtensionSingleHostTest() {
-    diagnosticsd_bridge()->EstablishFakeMojoConnection();
+    diagnosticsd_bridge_wrapper()->EstablishFakeMojoConnection();
     message_host_ = CreateExtensionOwnedDiagnosticsdMessageHost();
     message_host_->Start(&message_host_client_);
   }
diff --git a/chrome/browser/chromeos/diagnosticsd/testing_diagnosticsd_bridge.cc b/chrome/browser/chromeos/diagnosticsd/testing_diagnosticsd_bridge_wrapper.cc
similarity index 82%
rename from chrome/browser/chromeos/diagnosticsd/testing_diagnosticsd_bridge.cc
rename to chrome/browser/chromeos/diagnosticsd/testing_diagnosticsd_bridge_wrapper.cc
index bf4755e..745854c 100644
--- a/chrome/browser/chromeos/diagnosticsd/testing_diagnosticsd_bridge.cc
+++ b/chrome/browser/chromeos/diagnosticsd/testing_diagnosticsd_bridge_wrapper.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 "chrome/browser/chromeos/diagnosticsd/testing_diagnosticsd_bridge.h"
+#include "chrome/browser/chromeos/diagnosticsd/testing_diagnosticsd_bridge_wrapper.h"
 
 #include <unistd.h>
 #include <memory>
@@ -11,6 +11,7 @@
 #include "base/bind.h"
 #include "base/files/scoped_file.h"
 #include "base/logging.h"
+#include "base/memory/ptr_util.h"
 #include "base/posix/eintr_wrapper.h"
 #include "base/run_loop.h"
 #include "base/test/bind_test_util.h"
@@ -60,10 +61,10 @@
     DCHECK(service);
     DCHECK(client);
     // Redirect to |get_service_handler_callback_| to let
-    // TestingDiagnosticsdBridge capture |client| (which points to the
+    // TestingDiagnosticsdBridgeWrapper capture |client| (which points to the
     // production implementation in DiagnosticsdBridge) and fulfill |service|
     // (to make it point to the stub implementation of the DiagnosticsdService
-    // Mojo service that was passed to TestingDiagnosticsdBridge).
+    // Mojo service that was passed to TestingDiagnosticsdBridgeWrapper).
     get_service_handler_callback_.Run(std::move(service), std::move(client));
     std::move(callback).Run();
   }
@@ -86,10 +87,10 @@
 // Testing implementation of the DiagnosticsdBridge delegate that stubs out the
 // process of generating the Mojo invitation and tie it with
 // TestingMojoDiagnosticsdServiceFactory instead.
-class TestingDiagnosticsdBridgeDelegate final
+class TestingDiagnosticsdBridgeWrapperDelegate final
     : public DiagnosticsdBridge::Delegate {
  public:
-  explicit TestingDiagnosticsdBridgeDelegate(
+  explicit TestingDiagnosticsdBridgeWrapperDelegate(
       std::unique_ptr<TestingMojoDiagnosticsdServiceFactory>
           mojo_diagnosticsd_service_factory)
       : mojo_diagnosticsd_service_factory_(
@@ -116,7 +117,7 @@
   std::unique_ptr<TestingMojoDiagnosticsdServiceFactory>
       mojo_diagnosticsd_service_factory_;
 
-  DISALLOW_COPY_AND_ASSIGN(TestingDiagnosticsdBridgeDelegate);
+  DISALLOW_COPY_AND_ASSIGN(TestingDiagnosticsdBridgeWrapperDelegate);
 };
 
 FakeDiagnosticsdClient* GetFakeDbusDiagnosticsdClient() {
@@ -129,20 +130,19 @@
 
 }  // namespace
 
-TestingDiagnosticsdBridge::TestingDiagnosticsdBridge(
+// static
+std::unique_ptr<TestingDiagnosticsdBridgeWrapper>
+TestingDiagnosticsdBridgeWrapper::Create(
     diagnosticsd::mojom::DiagnosticsdService* mojo_diagnosticsd_service,
-    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
-    : mojo_diagnosticsd_service_binding_(mojo_diagnosticsd_service),
-      bridge_(std::make_unique<TestingDiagnosticsdBridgeDelegate>(
-                  std::make_unique<TestingMojoDiagnosticsdServiceFactory>(
-                      base::BindRepeating(
-                          &TestingDiagnosticsdBridge::HandleMojoGetService,
-                          base::Unretained(this)))),
-              url_loader_factory) {}
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+    std::unique_ptr<DiagnosticsdBridge>* bridge) {
+  return base::WrapUnique(new TestingDiagnosticsdBridgeWrapper(
+      mojo_diagnosticsd_service, std::move(url_loader_factory), bridge));
+}
 
-TestingDiagnosticsdBridge::~TestingDiagnosticsdBridge() = default;
+TestingDiagnosticsdBridgeWrapper::~TestingDiagnosticsdBridgeWrapper() = default;
 
-void TestingDiagnosticsdBridge::EstablishFakeMojoConnection() {
+void TestingDiagnosticsdBridgeWrapper::EstablishFakeMojoConnection() {
   DCHECK(!mojo_diagnosticsd_client_);
   DCHECK(!mojo_get_service_handler_);
 
@@ -179,7 +179,7 @@
       std::move(intercepted_mojo_diagnosticsd_service_request));
 }
 
-void TestingDiagnosticsdBridge::HandleMojoGetService(
+void TestingDiagnosticsdBridgeWrapper::HandleMojoGetService(
     diagnosticsd::mojom::DiagnosticsdServiceRequest
         mojo_diagnosticsd_service_request,
     diagnosticsd::mojom::DiagnosticsdClientPtr mojo_diagnosticsd_client) {
@@ -188,4 +188,18 @@
   mojo_diagnosticsd_client_ = std::move(mojo_diagnosticsd_client);
 }
 
+TestingDiagnosticsdBridgeWrapper::TestingDiagnosticsdBridgeWrapper(
+    diagnosticsd::mojom::DiagnosticsdService* mojo_diagnosticsd_service,
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+    std::unique_ptr<DiagnosticsdBridge>* bridge)
+    : mojo_diagnosticsd_service_binding_(mojo_diagnosticsd_service) {
+  *bridge = std::make_unique<DiagnosticsdBridge>(
+      std::make_unique<TestingDiagnosticsdBridgeWrapperDelegate>(
+          std::make_unique<TestingMojoDiagnosticsdServiceFactory>(
+              base::BindRepeating(
+                  &TestingDiagnosticsdBridgeWrapper::HandleMojoGetService,
+                  base::Unretained(this)))),
+      url_loader_factory);
+}
+
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/diagnosticsd/testing_diagnosticsd_bridge.h b/chrome/browser/chromeos/diagnosticsd/testing_diagnosticsd_bridge_wrapper.h
similarity index 76%
rename from chrome/browser/chromeos/diagnosticsd/testing_diagnosticsd_bridge.h
rename to chrome/browser/chromeos/diagnosticsd/testing_diagnosticsd_bridge_wrapper.h
index 66ad11d..f90a6dd 100644
--- a/chrome/browser/chromeos/diagnosticsd/testing_diagnosticsd_bridge.h
+++ b/chrome/browser/chromeos/diagnosticsd/testing_diagnosticsd_bridge_wrapper.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 CHROME_BROWSER_CHROMEOS_DIAGNOSTICSD_TESTING_DIAGNOSTICSD_BRIDGE_H_
-#define CHROME_BROWSER_CHROMEOS_DIAGNOSTICSD_TESTING_DIAGNOSTICSD_BRIDGE_H_
+#ifndef CHROME_BROWSER_CHROMEOS_DIAGNOSTICSD_TESTING_DIAGNOSTICSD_BRIDGE_WRAPPER_H_
+#define CHROME_BROWSER_CHROMEOS_DIAGNOSTICSD_TESTING_DIAGNOSTICSD_BRIDGE_WRAPPER_H_
 
 #include "base/callback.h"
 #include "base/macros.h"
@@ -18,15 +18,19 @@
 // Manages a fake instance of DiagnosticsdBridge for unit tests. Mocks out the
 // Mojo communication and provides tools for simulating and handling Mojo
 // requests.
-class TestingDiagnosticsdBridge final {
+class TestingDiagnosticsdBridgeWrapper final {
  public:
   // |mojo_diagnosticsd_service| is an unowned pointer that should be a stub
   // implementation of the DiagnosticsdService Mojo service (which in production
-  // is implemented by the diagnosticsd daemon).
-  TestingDiagnosticsdBridge(
+  // is implemented by the wilco_dtc_supportd daemon).
+  // |bridge| is an unowned bridge instance that holds the stub diagnosticsd
+  // bridge instance created by the TestingDiagnosticsdBridgeWrapper.
+  static std::unique_ptr<TestingDiagnosticsdBridgeWrapper> Create(
       diagnosticsd::mojom::DiagnosticsdService* mojo_diagnosticsd_service,
-      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory);
-  ~TestingDiagnosticsdBridge();
+      scoped_refptr<network::SharedURLLoaderFactory> url_loade_factory,
+      std::unique_ptr<DiagnosticsdBridge>* bridge);
+
+  ~TestingDiagnosticsdBridgeWrapper();
 
   // Simulates bootstrapping the Mojo communication between the diagnosticsd
   // daemon and the browser.
@@ -42,6 +46,14 @@
   }
 
  private:
+  // |mojo_diagnosticsd_service| is an unowned pointer that should be a stub
+  // implementation of the DiagnosticsdService Mojo service (which in production
+  // is implemented by the diagnosticsd daemon).
+  TestingDiagnosticsdBridgeWrapper(
+      diagnosticsd::mojom::DiagnosticsdService* mojo_diagnosticsd_service,
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+      std::unique_ptr<DiagnosticsdBridge>* bridge);
+
   // Implements the GetService Mojo method of the DiagnosticsdServiceFactory
   // interface. Called during the simulated Mojo boostrapping.
   void HandleMojoGetService(
@@ -54,8 +66,6 @@
   mojo::Binding<diagnosticsd::mojom::DiagnosticsdService>
       mojo_diagnosticsd_service_binding_;
 
-  DiagnosticsdBridge bridge_;
-
   // Mojo pointer that points to the DiagnosticsdClient implementation (owned by
   // |bridge_|).  Is initialized if the Mojo is bootstrapped by
   // EstablishFakeMojoConnection().
@@ -68,9 +78,9 @@
                               mojo_diagnosticsd_service_request)>
       mojo_get_service_handler_;
 
-  DISALLOW_COPY_AND_ASSIGN(TestingDiagnosticsdBridge);
+  DISALLOW_COPY_AND_ASSIGN(TestingDiagnosticsdBridgeWrapper);
 };
 
 }  // namespace chromeos
 
-#endif  // CHROME_BROWSER_CHROMEOS_DIAGNOSTICSD_TESTING_DIAGNOSTICSD_BRIDGE_H_
+#endif  // CHROME_BROWSER_CHROMEOS_DIAGNOSTICSD_TESTING_DIAGNOSTICSD_BRIDGE_WRAPPER_H_
diff --git a/chrome/browser/chromeos/login/existing_user_controller.cc b/chrome/browser/chromeos/login/existing_user_controller.cc
index 7253ca4b..f0b5cd7 100644
--- a/chrome/browser/chromeos/login/existing_user_controller.cc
+++ b/chrome/browser/chromeos/login/existing_user_controller.cc
@@ -96,6 +96,7 @@
 #include "components/user_manager/user_manager.h"
 #include "components/user_manager/user_names.h"
 #include "components/user_manager/user_type.h"
+#include "components/vector_icons/vector_icons.h"
 #include "components/version_info/version_info.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
@@ -1027,13 +1028,13 @@
   policy::BrowserPolicyConnectorChromeOS* connector =
       g_browser_process->platform_part()->browser_policy_connector_chromeos();
   DCHECK(connector->IsEnterpriseManaged());
-  std::string device_domain = connector->GetEnterpriseDisplayDomain();
-  if (device_domain.empty() && connector->IsActiveDirectoryManaged())
-    device_domain = connector->GetRealm();
+  message_center::RichNotificationData data;
+  data.buttons.push_back(message_center::ButtonInfo(
+      l10n_util::GetStringUTF16(IDS_AUTO_LAUNCH_NOTIFICATION_BUTTON)));
   const base::string16 title =
       l10n_util::GetStringUTF16(IDS_AUTO_LAUNCH_NOTIFICATION_TITLE);
-  const base::string16 message = l10n_util::GetStringFUTF16(
-      IDS_AUTO_LAUNCH_NOTIFICATION_MESSAGE, base::UTF8ToUTF16(device_domain));
+  const base::string16 message =
+      l10n_util::GetStringUTF16(IDS_AUTO_LAUNCH_NOTIFICATION_MESSAGE);
   auto delegate =
       base::MakeRefCounted<message_center::HandleNotificationClickDelegate>(
           base::BindRepeating([](base::Optional<int> button_index) {
@@ -1047,8 +1048,7 @@
           message_center::NotifierId(
               message_center::NotifierType::SYSTEM_COMPONENT,
               kAutoLaunchNotifierId),
-          message_center::RichNotificationData(), std::move(delegate),
-          ash::kAutoLaunchManagedGuestSessionIcon,
+          data, std::move(delegate), vector_icons::kBusinessIcon,
           message_center::SystemNotificationWarningLevel::NORMAL);
   notification->SetSystemPriority();
   notification->set_pinned(true);
diff --git a/chrome/browser/chromeos/policy/device_wilco_dtc_configuration_handler.cc b/chrome/browser/chromeos/policy/device_wilco_dtc_configuration_handler.cc
index 3b8e77b..2995687 100644
--- a/chrome/browser/chromeos/policy/device_wilco_dtc_configuration_handler.cc
+++ b/chrome/browser/chromeos/policy/device_wilco_dtc_configuration_handler.cc
@@ -4,10 +4,22 @@
 
 #include "chrome/browser/chromeos/policy/device_wilco_dtc_configuration_handler.h"
 
+#include "chrome/browser/chromeos/diagnosticsd/diagnosticsd_manager.h"
 #include "components/policy/policy_constants.h"
 
 namespace policy {
 
+namespace {
+
+chromeos::DiagnosticsdManager* GetDiagnosticsdManager() {
+  chromeos::DiagnosticsdManager* const diagnosticsd_manager =
+      chromeos::DiagnosticsdManager::Get();
+  DCHECK(diagnosticsd_manager);
+  return diagnosticsd_manager;
+}
+
+}  // namespace
+
 DeviceWilcoDtcConfigurationHandler::DeviceWilcoDtcConfigurationHandler(
     PolicyService* policy_service)
     : device_wilco_dtc_configuration_observer_(
@@ -20,14 +32,14 @@
 
 void DeviceWilcoDtcConfigurationHandler::OnDeviceExternalDataCleared(
     const std::string& policy) {
-  // TODO(b/123933434): handle a data cleared event.
+  GetDiagnosticsdManager()->SetConfigurationData(nullptr);
 }
 
 void DeviceWilcoDtcConfigurationHandler::OnDeviceExternalDataFetched(
     const std::string& policy,
     std::unique_ptr<std::string> data,
     const base::FilePath& file_path) {
-  // TODO(b/123933434): handle |data|.
+  GetDiagnosticsdManager()->SetConfigurationData(std::move(data));
 }
 
 void DeviceWilcoDtcConfigurationHandler::Shutdown() {
diff --git a/chrome/browser/data_reduction_proxy/data_reduction_proxy_browsertest.cc b/chrome/browser/data_reduction_proxy/data_reduction_proxy_browsertest.cc
index f520395..280e207 100644
--- a/chrome/browser/data_reduction_proxy/data_reduction_proxy_browsertest.cc
+++ b/chrome/browser/data_reduction_proxy/data_reduction_proxy_browsertest.cc
@@ -826,6 +826,46 @@
   net::EmbeddedTestServer core_server_;
 };
 
+IN_PROC_BROWSER_TEST_F(DataReductionProxyFallbackBrowsertest,
+                       ProxyBypassedOn502Error) {
+  base::HistogramTester histogram_tester;
+  net::EmbeddedTestServer test_server;
+  test_server.RegisterRequestHandler(
+      base::BindRepeating(&BasicResponse, kDummyBody));
+  ASSERT_TRUE(test_server.Start());
+
+  SetStatusCode(net::HTTP_BAD_GATEWAY);
+
+  ui_test_utils::NavigateToURL(browser(),
+                               GetURLWithMockHost(test_server, "/echo"));
+  EXPECT_THAT(GetBody(), kSecondaryResponse);
+  histogram_tester.ExpectUniqueSample(
+      "DataReductionProxy.BypassTypePrimary",
+      BYPASS_EVENT_TYPE_STATUS_502_HTTP_BAD_GATEWAY, 1);
+}
+
+IN_PROC_BROWSER_TEST_F(DataReductionProxyFallbackBrowsertest,
+                       ProxyShortBypassedOn502ErrorWithFeature) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeatureWithParameters(
+      features::kDataReductionProxyBlockOnBadGatewayResponse,
+      {{"block_duration_seconds", "10"}});
+  base::HistogramTester histogram_tester;
+  net::EmbeddedTestServer test_server;
+  test_server.RegisterRequestHandler(
+      base::BindRepeating(&BasicResponse, kDummyBody));
+  ASSERT_TRUE(test_server.Start());
+
+  SetStatusCode(net::HTTP_BAD_GATEWAY);
+
+  ui_test_utils::NavigateToURL(browser(),
+                               GetURLWithMockHost(test_server, "/echo"));
+  // Both the proxies should be blocked.
+  EXPECT_THAT(GetBody(), kDummyBody);
+  histogram_tester.ExpectUniqueSample("DataReductionProxy.BlockTypePrimary",
+                                      BYPASS_EVENT_TYPE_SHORT, 1);
+}
+
 IN_PROC_BROWSER_TEST_F(DataReductionProxyResourceTypeBrowsertest,
                        FirstProxyUsedForMedia) {
   ui_test_utils::NavigateToURL(
diff --git a/chrome/browser/extensions/BUILD.gn b/chrome/browser/extensions/BUILD.gn
index a086a5d..c01f2f5 100644
--- a/chrome/browser/extensions/BUILD.gn
+++ b/chrome/browser/extensions/BUILD.gn
@@ -854,8 +854,7 @@
     "//components/undo",
     "//components/unified_consent",
     "//components/update_client",
-    "//components/update_client:patch_impl",
-    "//components/update_client:unzip_impl",
+    "//components/update_client:common_impl",
     "//components/url_matcher",
     "//components/user_prefs",
     "//components/vector_icons",
diff --git a/chrome/browser/extensions/api/sessions/sessions_apitest.cc b/chrome/browser/extensions/api/sessions/sessions_apitest.cc
index c25e137..bdc65707 100644
--- a/chrome/browser/extensions/api/sessions/sessions_apitest.cc
+++ b/chrome/browser/extensions/api/sessions/sessions_apitest.cc
@@ -246,10 +246,12 @@
         time_now - base::TimeDelta::FromSeconds(index);
     header_entity_data.modification_time = header_entity_data.creation_time;
 
-    syncer::UpdateResponseData header_update;
-    header_update.entity = header_entity_data.PassToPtr();
-    header_update.response_version = 1;
-    worker.UpdateFromServer({header_update});
+    auto header_update = std::make_unique<syncer::UpdateResponseData>();
+    header_update->entity = header_entity_data.PassToPtr();
+    header_update->response_version = 1;
+    syncer::UpdateResponseDataList updates;
+    updates.push_back(std::move(header_update));
+    worker.UpdateFromServer(std::move(updates));
 
     for (size_t i = 0; i < tabs.size(); i++) {
       sync_pb::EntitySpecifics tab_entity;
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 3eb98bf5..ba9f512c 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -2607,7 +2607,7 @@
   },
   {
     "name": "out-of-blink-cors",
-    // "owners": [ "your-team" ],
+    "owners": [ "toyoshim" ],
     "expiry_milestone": 76
   },
   {
@@ -3024,11 +3024,6 @@
     "expiry_milestone": 75
   },
   {
-    "name": "use-monitor-color-space",
-    // "owners": [ "your-team" ],
-    "expiry_milestone": 76
-  },
-  {
     "name": "use-multilogin-endpoint",
     "owners": [ "droger", "msarda" ],
     "expiry_milestone": 78
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 079bc24..069292a4 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -3381,12 +3381,6 @@
     "Runs the mojo UI service (mus) and the ash window manager and system UI "
     "in a separate process.";
 
-// TODO(mcasas): remove after https://crbug.com/771345.
-const char kUseMonitorColorSpaceName[] = "Use monitor color space";
-const char kUseMonitorColorSpaceDescription[] =
-    "Enables Chrome to use the  color space information provided by the monitor"
-    " instead of the default sRGB color space.";
-
 const char kUserActivityPredictionMlServiceName[] =
     "ML Service Smart Dim model";
 const char kUserActivityPredictionMlServiceDescription[] =
diff --git a/chrome/browser/installable/installable_metrics.cc b/chrome/browser/installable/installable_metrics.cc
index 14e17539..83aa6da 100644
--- a/chrome/browser/installable/installable_metrics.cc
+++ b/chrome/browser/installable/installable_metrics.cc
@@ -237,7 +237,8 @@
          source == WebappInstallSource::INTERNAL_DEFAULT ||
          source == WebappInstallSource::EXTERNAL_DEFAULT ||
          source == WebappInstallSource::EXTERNAL_POLICY ||
-         source == WebappInstallSource::SYSTEM_DEFAULT;
+         source == WebappInstallSource::SYSTEM_DEFAULT ||
+         source == WebappInstallSource::OMNIBOX_INSTALL_ICON;
 }
 
 // static
diff --git a/chrome/browser/installable/installable_metrics.h b/chrome/browser/installable/installable_metrics.h
index 747e8a4..4d87649 100644
--- a/chrome/browser/installable/installable_metrics.h
+++ b/chrome/browser/installable/installable_metrics.h
@@ -93,6 +93,9 @@
   // A system app installed on Chrome OS.
   SYSTEM_DEFAULT = 14,
 
+  // Install icon in the Omnibox.
+  OMNIBOX_INSTALL_ICON = 15,
+
   // Add any new values above this one.
   COUNT,
 };
diff --git a/chrome/browser/password_manager/password_manager_browsertest.cc b/chrome/browser/password_manager/password_manager_browsertest.cc
index 1dff807..4cb7d61 100644
--- a/chrome/browser/password_manager/password_manager_browsertest.cc
+++ b/chrome/browser/password_manager/password_manager_browsertest.cc
@@ -3898,7 +3898,16 @@
   prompt_observer->AcceptUpdatePrompt(stored_form);
 
   WaitForPasswordStore();
-  CheckThatCredentialsStored("user", "new password");
+  // There are two credentials saved with the new password.
+  const auto& passwords_map = password_store->stored_passwords();
+  ASSERT_THAT(passwords_map, ElementsAre(testing::Key(url_A.GetOrigin()),
+                                         testing::Key(url_B.GetOrigin())));
+  for (const auto& credentials : passwords_map) {
+    ASSERT_THAT(credentials.second, testing::SizeIs(1));
+    EXPECT_EQ(base::ASCIIToUTF16("user"), credentials.second[0].username_value);
+    EXPECT_EQ(base::ASCIIToUTF16("new password"),
+              credentials.second[0].password_value);
+  }
 }
 
 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,
diff --git a/chrome/browser/password_manager/password_manager_test_base.cc b/chrome/browser/password_manager/password_manager_test_base.cc
index 18912a4..a4c7906d 100644
--- a/chrome/browser/password_manager/password_manager_test_base.cc
+++ b/chrome/browser/password_manager/password_manager_test_base.cc
@@ -683,6 +683,7 @@
 void PasswordManagerBrowserTestBase::CheckThatCredentialsStored(
     const std::string& username,
     const std::string& password) {
+  SCOPED_TRACE(::testing::Message() << username << ", " << password);
   scoped_refptr<password_manager::TestPasswordStore> password_store =
       static_cast<password_manager::TestPasswordStore*>(
           PasswordStoreFactory::GetForProfile(
diff --git a/chrome/browser/pdf/pdf_extension_test.cc b/chrome/browser/pdf/pdf_extension_test.cc
index d49789ac..56b7c69 100644
--- a/chrome/browser/pdf/pdf_extension_test.cc
+++ b/chrome/browser/pdf/pdf_extension_test.cc
@@ -796,6 +796,25 @@
   EXPECT_EQ(1, CountPDFProcesses());
 }
 
+// Tests that PDF with no filename extension can be loaded from local file.
+IN_PROC_BROWSER_TEST_F(PDFExtensionTest, ExtensionlessPDFLocalFileLoads) {
+  GURL test_pdf_url;
+  {
+    base::ScopedAllowBlockingForTesting allow_blocking;
+    base::FilePath test_data_dir;
+    ASSERT_TRUE(base::PathService::Get(chrome::DIR_TEST_DATA, &test_data_dir));
+    test_data_dir = test_data_dir.AppendASCII("pdf");
+    base::FilePath test_data_file = test_data_dir.AppendASCII("imgpdf");
+    ASSERT_TRUE(PathExists(test_data_file));
+    test_pdf_url = GURL("file://" + test_data_file.MaybeAsASCII());
+  }
+  WebContents* guest_contents = LoadPdfGetGuestContents(test_pdf_url);
+  ASSERT_TRUE(guest_contents);
+
+  // Did launch a PPAPI process.
+  EXPECT_EQ(1, CountPDFProcesses());
+}
+
 // This test ensures that link permissions are enforced properly in PDFs.
 IN_PROC_BROWSER_TEST_F(PDFExtensionTest, LinkPermissions) {
   GURL test_pdf_url(embedded_test_server()->GetURL("/pdf/test.pdf"));
diff --git a/chrome/browser/resources/app_management/permission_item.js b/chrome/browser/resources/app_management/permission_item.js
index b59a784f..20592d6 100644
--- a/chrome/browser/resources/app_management/permission_item.js
+++ b/chrome/browser/resources/app_management/permission_item.js
@@ -76,8 +76,8 @@
   onClick_: function(e) {
     e.preventDefault();
 
-    const /** @type {AppManagementPermissionToggleElement} */ toggle =
-        this.$['permission-toggle'];
+    const toggle = /** @type {AppManagementPermissionToggleElement} */
+        assert(this.$$('#permission-toggle'));
     toggle.togglePermission_();
   },
 });
diff --git a/chrome/browser/resources/chromeos/kiosk_next_home/manifest.json b/chrome/browser/resources/chromeos/kiosk_next_home/manifest.json
index bf4c22f2..bb307f4a 100644
--- a/chrome/browser/resources/chromeos/kiosk_next_home/manifest.json
+++ b/chrome/browser/resources/chromeos/kiosk_next_home/manifest.json
@@ -9,9 +9,9 @@
     "192": "static/icon192.png"
   },
   "permissions": [
-    "arcAppsPrivate",
     "https://*.googleapis.com",
-    "chrome://app-icon/"
+    "chrome://app-icon/",
+    "mojoPrivate"
   ],
   "app": {
     "background": {
diff --git a/chrome/browser/resources/management/management_ui.html b/chrome/browser/resources/management/management_ui.html
index eb66adf..b21c762d 100644
--- a/chrome/browser/resources/management/management_ui.html
+++ b/chrome/browser/resources/management/management_ui.html
@@ -205,6 +205,12 @@
             </div>
           </section>
 <if expr="chromeos">
+          <div hidden="[[!localTrustRoots_]]">
+            <section class="three-line single-column">
+              <h2>$i18n{localTrustRoots}</h2>
+              <div id="trust-roots-configuration">[[localTrustRoots_]]</div>
+            </section>
+          </div>
           <template is="dom-if"
               if="[[showDeviceReportingInfo_(deviceReportingInfo_)]]">
             <section class="single-column">
@@ -274,14 +280,6 @@
               </div>
             </section>
           </template>
-<if expr="chromeos">
-          <template is="dom-if" if="[[localTrustRoots_]]">
-            <section class="single-column">
-              <h2>$i18n{localTrustRoots}</h2>
-              <div id="trust-roots-configuration">[[localTrustRoots_]]</div>
-            </section>
-          </template>
-</if>
         </div>
       </div>
     </main>
diff --git a/chrome/browser/resources/management/management_ui.js b/chrome/browser/resources/management/management_ui.js
index b66d147..235cbd3 100644
--- a/chrome/browser/resources/management/management_ui.js
+++ b/chrome/browser/resources/management/management_ui.js
@@ -150,9 +150,9 @@
   /** @private */
   getLocalTrustRootsInfo_() {
     this.browserProxy_.getLocalTrustRootsInfo().then(trustRootsConfigured => {
-      this.localTrustRoots_ = loadTimeData.getString(
-          trustRootsConfigured ? 'managementTrustRootsConfigured' :
-                                 'managementTrustRootsNotConfigured');
+      this.localTrustRoots_ = trustRootsConfigured ?
+          loadTimeData.getString('managementTrustRootsConfigured') :
+          '';
     });
   },
 
diff --git a/chrome/browser/resources/print_preview/data/destination.js b/chrome/browser/resources/print_preview/data/destination.js
index 22854d3..d037df0 100644
--- a/chrome/browser/resources/print_preview/data/destination.js
+++ b/chrome/browser/resources/print_preview/data/destination.js
@@ -180,13 +180,25 @@
 
 // <if expr="chromeos">
 /**
- * Enumeration of duplex modes used by Chromium.
+ * Enumeration of color mode restrictions used by Chromium.
+ * This has to coincide with |printing::ColorModeRestriction| as defined in
+ * printing/backend/printing_restrictions.h
+ * @enum {number}
+ */
+print_preview.ColorModeRestriction = {
+  UNSET: 0x0,
+  MONOCHROME: 0x1,
+  COLOR: 0x2,
+};
+
+/**
+ * Enumeration of duplex mode restrictions used by Chromium.
  * This has to coincide with |printing::DuplexModeRestriction| as defined in
  * printing/backend/printing_restrictions.h
  * @enum {number}
  */
 print_preview.DuplexModeRestriction = {
-  NONE: 0x0,
+  UNSET: 0x0,
   SIMPLEX: 0x1,
   LONG_EDGE: 0x2,
   SHORT_EDGE: 0x4,
@@ -194,13 +206,13 @@
 };
 
 /**
- * Enumeration of PIN printing modes used by Chromium.
+ * Enumeration of PIN printing mode restrictions used by Chromium.
  * This has to coincide with |printing::PinModeRestriction| as defined in
  * printing/backend/printing_restrictions.h
  * @enum {number}
  */
 print_preview.PinModeRestriction = {
-  NONE: 0,
+  UNSET: 0,
   PIN: 1,
   NO_PIN: 2
 };
@@ -208,10 +220,10 @@
 /**
  * Policies affecting a destination.
  * @typedef {{
- *   allowedColorModes: ?print_preview.ColorMode,
+ *   allowedColorModes: ?print_preview.ColorModeRestriction,
  *   allowedDuplexModes: ?print_preview.DuplexModeRestriction,
  *   allowedPinMode: ?print_preview.PinModeRestriction,
- *   defaultColorMode: ?print_preview.ColorMode,
+ *   defaultColorMode: ?print_preview.ColorModeRestriction,
  *   defaultDuplexMode: ?print_preview.DuplexModeRestriction,
  *   defaultPinMode: ?print_preview.PinModeRestriction,
  * }}
@@ -789,7 +801,7 @@
 
     // <if expr="chromeos">
     /**
-     * @return {?print_preview.ColorMode} Color mode set by policy.
+     * @return {?print_preview.ColorModeRestriction} Color mode set by policy.
      */
     get colorPolicy() {
       return this.policies && this.policies.allowedColorModes ?
@@ -839,8 +851,8 @@
 
     // <if expr="chromeos">
     /**
-     * @return {?print_preview.ColorMode} Value of default color setting given
-     *     by policy.
+     * @return {?print_preview.ColorModeRestriction} Value of default color
+     *     setting given by policy.
      */
     get defaultColorPolicy() {
       return this.policies && this.policies.defaultColorMode;
diff --git a/chrome/browser/resources/print_preview/new/model.js b/chrome/browser/resources/print_preview/new/model.js
index 19c144f..54accee 100644
--- a/chrome/browser/resources/print_preview/new/model.js
+++ b/chrome/browser/resources/print_preview/new/model.js
@@ -975,7 +975,8 @@
       // |this.setSetting| does nothing if policy is present.
       // We want to set the value nevertheless so we call |this.set| directly.
       this.set(
-          'settings.color.value', colorValue == print_preview.ColorMode.COLOR);
+          'settings.color.value',
+          colorValue == print_preview.ColorModeRestriction.COLOR);
     }
     this.set('settings.color.setByPolicy', !!colorPolicy);
 
diff --git a/chrome/browser/resources/settings/people_page/account_manager.html b/chrome/browser/resources/settings/people_page/account_manager.html
index f0da46fc..755a883 100644
--- a/chrome/browser/resources/settings/people_page/account_manager.html
+++ b/chrome/browser/resources/settings/people_page/account_manager.html
@@ -55,6 +55,10 @@
         left: auto;
         right: 60%;
       }
+
+      .management-status {
+        color: var(--cr-secondary-text-color);
+      }
     </style>
 
     <div class="settings-box first">$i18n{accountManagerDescription}</div>
@@ -111,12 +115,20 @@
             </paper-button>
           </template>
 
-          <paper-icon-button-light class="icon-more-vert"
-              hidden="[[item.isDeviceAccount]]">
-            <button title="$i18n{moreActions}"
-                on-click="onAccountActionsMenuButtonTap_">
-            </button>
-          </paper-icon-button-light>
+          <!-- If this is the Device Account, display the management status -->
+          <template is="dom-if" if="[[item.isDeviceAccount]]">
+            <span class="management-status">
+              [[getManagementLabel_(item)]]
+            </span>
+          </template>
+          <!-- Else, display a hamburger menu for removing the account -->
+          <template is="dom-if" if="[[!item.isDeviceAccount]]">
+            <paper-icon-button-light class="icon-more-vert">
+              <button title="$i18n{moreActions}"
+                  on-click="onAccountActionsMenuButtonTap_">
+              </button>
+            </paper-icon-button-light>
+          </template>
         </div>
       </template>
 
diff --git a/chrome/browser/resources/settings/people_page/account_manager.js b/chrome/browser/resources/settings/people_page/account_manager.js
index cb97652..eb24b96 100644
--- a/chrome/browser/resources/settings/people_page/account_manager.js
+++ b/chrome/browser/resources/settings/people_page/account_manager.js
@@ -101,6 +101,21 @@
   },
 
   /**
+   * @param {!settings.Account} account
+   * @return {string} An appropriate management status label. e.g.
+   *    "Primary account" for unmanaged accounts, "Managed by <Domain>"
+   *    for Enterprise managed accounts etc.
+   * @private
+   */
+  getManagementLabel_: function(account) {
+    if (account.organization) {
+      return this.i18n('accountManagerManagedLabel', account.organization);
+    }
+
+    return this.i18n('accountManagerUnmanagedLabel');
+  },
+
+  /**
    * @param {!CustomEvent<!{model: !{item: !settings.Account}}>} event
    * @private
    */
diff --git a/chrome/browser/resources/settings/people_page/account_manager_browser_proxy.js b/chrome/browser/resources/settings/people_page/account_manager_browser_proxy.js
index c087831..a83b3dcb 100644
--- a/chrome/browser/resources/settings/people_page/account_manager_browser_proxy.js
+++ b/chrome/browser/resources/settings/people_page/account_manager_browser_proxy.js
@@ -19,6 +19,7 @@
  *   fullName: string,
  *   email: string,
  *   pic: string,
+ *   organization: (string|undefined),
  * }}
  */
 settings.Account;
diff --git a/chrome/browser/search/ntp_features.cc b/chrome/browser/search/ntp_features.cc
index ecf9de4..d70cdc3 100644
--- a/chrome/browser/search/ntp_features.cc
+++ b/chrome/browser/search/ntp_features.cc
@@ -11,11 +11,11 @@
 
 // If enabled, the user will see Doodles on the New Tab Page.
 const base::Feature kDoodlesOnLocalNtp{"DoodlesOnLocalNtp",
-                                       base::FEATURE_DISABLED_BY_DEFAULT};
+                                       base::FEATURE_ENABLED_BY_DEFAULT};
 
 // If enabled, the user will sometimes see promos on the NTP.
 const base::Feature kPromosOnLocalNtp{"PromosOnLocalNtp",
-                                      base::FEATURE_DISABLED_BY_DEFAULT};
+                                      base::FEATURE_ENABLED_BY_DEFAULT};
 
 // If enabled, the fakebox will not be shown on the NTP.
 const base::Feature kRemoveNtpFakebox{"RemoveNtpFakebox",
@@ -23,11 +23,11 @@
 
 // If enabled, the user will sometimes see search suggestions on the NTP.
 const base::Feature kSearchSuggestionsOnLocalNtp{
-    "SearchSuggestionsOnLocalNtp", base::FEATURE_DISABLED_BY_DEFAULT};
+    "SearchSuggestionsOnLocalNtp", base::FEATURE_ENABLED_BY_DEFAULT};
 
 // Enables using the local NTP if Google is the default search engine.
 const base::Feature kUseGoogleLocalNtp{"UseGoogleLocalNtp",
-                                       base::FEATURE_DISABLED_BY_DEFAULT};
+                                       base::FEATURE_ENABLED_BY_DEFAULT};
 
 // If enabled, show a search icon (magnifier glass) in the NTP fakebox.
 const base::Feature kFakeboxSearchIconOnNtp{"FakeboxSearchIconOnNtp",
diff --git a/chrome/browser/signin/process_dice_header_delegate_impl.cc b/chrome/browser/signin/process_dice_header_delegate_impl.cc
index df05cd8..366273d81 100644
--- a/chrome/browser/signin/process_dice_header_delegate_impl.cc
+++ b/chrome/browser/signin/process_dice_header_delegate_impl.cc
@@ -8,7 +8,7 @@
 
 #include "base/callback.h"
 #include "base/logging.h"
-#include "chrome/common/webui_url_constants.h"
+#include "chrome/common/url_constants.h"
 #include "content/public/browser/navigation_controller.h"
 #include "content/public/browser/web_contents.h"
 #include "services/identity/public/cpp/identity_manager.h"
@@ -19,7 +19,7 @@
 void RedirectToNtp(content::WebContents* contents) {
   VLOG(1) << "RedirectToNtp";
   contents->GetController().LoadURL(
-      GURL(chrome::kChromeUINewTabURL), content::Referrer(),
+      GURL(chrome::kChromeSearchLocalNtpUrl), content::Referrer(),
       ui::PAGE_TRANSITION_AUTO_TOPLEVEL, std::string());
 }
 
diff --git a/chrome/browser/signin/process_dice_header_delegate_impl_unittest.cc b/chrome/browser/signin/process_dice_header_delegate_impl_unittest.cc
index 0f92e03..84a85ec 100644
--- a/chrome/browser/signin/process_dice_header_delegate_impl_unittest.cc
+++ b/chrome/browser/signin/process_dice_header_delegate_impl_unittest.cc
@@ -10,7 +10,7 @@
 
 #include "base/bind.h"
 #include "base/bind_helpers.h"
-#include "chrome/common/webui_url_constants.h"
+#include "chrome/common/url_constants.h"
 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
 #include "components/signin/core/browser/account_consistency_method.h"
 #include "content/public/browser/web_contents.h"
@@ -161,7 +161,7 @@
   delegate->EnableSync(account_id_);
   EXPECT_EQ(GetParam().callback_called, enable_sync_called_);
   GURL expected_url =
-      GetParam().show_ntp ? GURL(chrome::kChromeUINewTabURL) : kSigninURL;
+      GetParam().show_ntp ? GURL(chrome::kChromeSearchLocalNtpUrl) : kSigninURL;
   EXPECT_EQ(expected_url, web_contents()->GetVisibleURL());
   EXPECT_FALSE(show_error_called_);
 }
@@ -206,7 +206,7 @@
   EXPECT_FALSE(enable_sync_called_);
   EXPECT_EQ(GetParam().callback_called, show_error_called_);
   GURL expected_url =
-      GetParam().show_ntp ? GURL(chrome::kChromeUINewTabURL) : kSigninURL;
+      GetParam().show_ntp ? GURL(chrome::kChromeSearchLocalNtpUrl) : kSigninURL;
   EXPECT_EQ(expected_url, web_contents()->GetVisibleURL());
 }
 
diff --git a/chrome/browser/sync/test/integration/autofill_helper.cc b/chrome/browser/sync/test/integration/autofill_helper.cc
index 0a34edb..796b95f 100644
--- a/chrome/browser/sync/test/integration/autofill_helper.cc
+++ b/chrome/browser/sync/test/integration/autofill_helper.cc
@@ -27,6 +27,7 @@
 #include "components/autofill/core/browser/webdata/autofill_table.h"
 #include "components/autofill/core/browser/webdata/autofill_webdata_service.h"
 #include "components/autofill/core/common/form_field_data.h"
+#include "components/sync/driver/profile_sync_service.h"
 #include "components/webdata/common/web_database.h"
 
 using autofill::AutofillChangeList;
@@ -210,8 +211,13 @@
 }
 
 PersonalDataManager* GetPersonalDataManager(int index) {
-  return autofill::PersonalDataManagerFactory::GetForProfile(
+  auto* pdm = autofill::PersonalDataManagerFactory::GetForProfile(
       test()->GetProfile(index));
+  // Hook the sync service up to the personal data manager.
+  // This is normally done by autofill_manager, which we don't
+  // have in our tests.
+  pdm->OnSyncServiceInitialized(test()->GetSyncService(index));
+  return pdm;
 }
 
 void AddKeys(int profile, const std::set<AutofillKey>& keys) {
diff --git a/chrome/browser/sync/test/integration/enable_disable_test.cc b/chrome/browser/sync/test/integration/enable_disable_test.cc
index 61c6929..a99489b 100644
--- a/chrome/browser/sync/test/integration/enable_disable_test.cc
+++ b/chrome/browser/sync/test/integration/enable_disable_test.cc
@@ -116,15 +116,6 @@
         .num_updates_downloaded_total;
   }
 
-  sync_pb::ClientToServerMessage TriggerGetUpdatesCycleAndWait() {
-    TriggerSyncForModelTypes(0, {syncer::BOOKMARKS});
-    EXPECT_TRUE(UpdatedProgressMarkerChecker(GetSyncService(0)).Wait());
-
-    sync_pb::ClientToServerMessage message;
-    EXPECT_TRUE(GetFakeServer()->GetLastGetUpdatesMessage(&message));
-    return message;
-  }
-
  protected:
   void SetupTest(bool all_types_enabled) {
     ASSERT_TRUE(SetupClients());
@@ -444,7 +435,24 @@
   EXPECT_EQ(cache_guid, prefs.GetCacheGuid());
 }
 
-IN_PROC_BROWSER_TEST_F(EnableDisableSingleClientTest, PRE_ResendsBagOfChips) {
+class EnableDisableSingleClientSelfNotifyTest
+    : public EnableDisableSingleClientTest {
+ public:
+  // UpdatedProgressMarkerChecker relies on the 'self-notify' feature.
+  bool TestUsesSelfNotifications() override { return true; }
+
+  sync_pb::ClientToServerMessage TriggerGetUpdatesCycleAndWait() {
+    TriggerSyncForModelTypes(0, {syncer::BOOKMARKS});
+    EXPECT_TRUE(UpdatedProgressMarkerChecker(GetSyncService(0)).Wait());
+
+    sync_pb::ClientToServerMessage message;
+    EXPECT_TRUE(GetFakeServer()->GetLastGetUpdatesMessage(&message));
+    return message;
+  }
+};
+
+IN_PROC_BROWSER_TEST_F(EnableDisableSingleClientSelfNotifyTest,
+                       PRE_ResendsBagOfChips) {
   sync_pb::ChipBag bag_of_chips;
   bag_of_chips.set_server_chips(kTestServerChips);
   ASSERT_FALSE(base::IsStringUTF8(bag_of_chips.SerializeAsString()));
@@ -460,11 +468,12 @@
   EXPECT_EQ(kTestServerChips, message.bag_of_chips().server_chips());
 }
 
-IN_PROC_BROWSER_TEST_F(EnableDisableSingleClientTest, ResendsBagOfChips) {
+IN_PROC_BROWSER_TEST_F(EnableDisableSingleClientSelfNotifyTest,
+                       ResendsBagOfChips) {
   ASSERT_TRUE(SetupClients());
   SyncPrefs prefs(GetProfile(0)->GetPrefs());
   ASSERT_NE("", prefs.GetBagOfChips());
-  ASSERT_TRUE(GetClient(0)->AwaitEngineInitialization());
+  ASSERT_TRUE(GetClient(0)->AwaitSyncSetupCompletion());
 
   sync_pb::ClientToServerMessage message = TriggerGetUpdatesCycleAndWait();
   EXPECT_TRUE(message.has_bag_of_chips());
diff --git a/chrome/browser/sync/test/integration/single_client_wallet_sync_test.cc b/chrome/browser/sync/test/integration/single_client_wallet_sync_test.cc
index 1bc7749..4e45efe 100644
--- a/chrome/browser/sync/test/integration/single_client_wallet_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_wallet_sync_test.cc
@@ -340,7 +340,6 @@
                        DownloadAccountStorage_Card) {
   ASSERT_TRUE(SetupClients());
   autofill::PersonalDataManager* pdm = GetPersonalDataManager(0);
-  pdm->OnSyncServiceInitialized(GetSyncService(0));
 
   GetFakeServer()->SetWalletData({CreateDefaultSyncWalletAddress(),
                                   CreateDefaultSyncWalletCard(),
@@ -1104,7 +1103,7 @@
 // synced down form the server.
 IN_PROC_BROWSER_TEST_P(SingleClientWalletSyncTestWithDefaultFeatures,
                        NewWalletCardRemovesExistingCardAndProfile) {
-  ASSERT_TRUE(SetupClients());
+  ASSERT_TRUE(SetupSync());
   autofill::PersonalDataManager* pdm = GetPersonalDataManager(0);
   ASSERT_NE(nullptr, pdm);
 
@@ -1143,7 +1142,13 @@
   // Add a new card from the server and sync it down.
   GetFakeServer()->SetWalletData(
       {CreateDefaultSyncWalletCard(), CreateDefaultSyncPaymentsCustomerData()});
-  ASSERT_TRUE(SetupSync());
+  // Constructing the checker captures the current progress marker. Make sure to
+  // do that before triggering the fetch.
+  WaitForNextWalletUpdateChecker checker(GetSyncService(0));
+  // Trigger a sync and wait for the new data to arrive.
+  TriggerSyncForModelTypes(0,
+                           syncer::ModelTypeSet(syncer::AUTOFILL_WALLET_DATA));
+  ASSERT_TRUE(checker.Wait());
 
   // The only card present on the client should be the one from the server.
   cards = pdm->GetCreditCards();
@@ -1166,7 +1171,7 @@
 // synced down form the server.
 IN_PROC_BROWSER_TEST_P(SingleClientWalletSyncTestWithDefaultFeatures,
                        NewWalletDataRemovesExistingData) {
-  ASSERT_TRUE(SetupClients());
+  ASSERT_TRUE(SetupSync());
   autofill::PersonalDataManager* pdm = GetPersonalDataManager(0);
   ASSERT_NE(nullptr, pdm);
 
@@ -1206,7 +1211,13 @@
   GetFakeServer()->SetWalletData(
       {CreateDefaultSyncWalletAddress(),
        CreateSyncPaymentsCustomerData(/*customer_id=*/"newid")});
-  ASSERT_TRUE(SetupSync());
+  // Constructing the checker captures the current progress marker. Make sure to
+  // do that before triggering the fetch.
+  WaitForNextWalletUpdateChecker checker(GetSyncService(0));
+  // Trigger a sync and wait for the new data to arrive.
+  TriggerSyncForModelTypes(0,
+                           syncer::ModelTypeSet(syncer::AUTOFILL_WALLET_DATA));
+  ASSERT_TRUE(checker.Wait());
 
   // The only profile present on the client should be the one from the server.
   profiles = pdm->GetServerProfiles();
@@ -1226,7 +1237,7 @@
 // be overwritten when that same card is synced again.
 IN_PROC_BROWSER_TEST_P(SingleClientWalletSyncTestWithDefaultFeatures,
                        SameWalletCard_PreservesLocalBillingAddressId) {
-  ASSERT_TRUE(SetupClients());
+  ASSERT_TRUE(SetupSync());
   autofill::PersonalDataManager* pdm = GetPersonalDataManager(0);
   ASSERT_NE(nullptr, pdm);
 
@@ -1248,7 +1259,13 @@
   // Sync the same card from the server, except with a default billing address
   // id.
   GetFakeServer()->SetWalletData({CreateDefaultSyncWalletCard()});
-  ASSERT_TRUE(SetupSync());
+  // Constructing the checker captures the current progress marker. Make sure to
+  // do that before triggering the fetch.
+  WaitForNextWalletUpdateChecker checker(GetSyncService(0));
+  // Trigger a sync and wait for the new data to arrive.
+  TriggerSyncForModelTypes(0,
+                           syncer::ModelTypeSet(syncer::AUTOFILL_WALLET_DATA));
+  ASSERT_TRUE(checker.Wait());
 
   // The billing address is should still refer to the local profile.
   cards = pdm->GetCreditCards();
@@ -1261,7 +1278,7 @@
 // overwritten when that same card is synced again.
 IN_PROC_BROWSER_TEST_P(SingleClientWalletSyncTestWithDefaultFeatures,
                        SameWalletCard_DiscardsOldServerBillingAddressId) {
-  ASSERT_TRUE(SetupClients());
+  ASSERT_TRUE(SetupSync());
   autofill::PersonalDataManager* pdm = GetPersonalDataManager(0);
   ASSERT_NE(nullptr, pdm);
 
@@ -1283,7 +1300,13 @@
   // Sync the same card from the server, except with a default billing address
   // id.
   GetFakeServer()->SetWalletData({CreateDefaultSyncWalletCard()});
-  ASSERT_TRUE(SetupSync());
+  // Constructing the checker captures the current progress marker. Make sure to
+  // do that before triggering the fetch.
+  WaitForNextWalletUpdateChecker checker(GetSyncService(0));
+  // Trigger a sync and wait for the new data to arrive.
+  TriggerSyncForModelTypes(0,
+                           syncer::ModelTypeSet(syncer::AUTOFILL_WALLET_DATA));
+  ASSERT_TRUE(checker.Wait());
 
   // The billing address should be the one from the server card.
   cards = pdm->GetCreditCards();
@@ -1333,7 +1356,6 @@
 IN_PROC_BROWSER_TEST_P(SingleClientWalletSecondaryAccountSyncTest,
                        SwitchesFromAccountToProfileStorageOnSyncOptIn) {
   ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
-  GetPersonalDataManager(0)->OnSyncServiceInitialized(GetSyncService(0));
 
   GetFakeServer()->SetWalletData(
       {CreateDefaultSyncWalletCard(), CreateDefaultSyncPaymentsCustomerData()});
@@ -1397,7 +1419,6 @@
     SingleClientWalletSecondaryAccountSyncTest,
     SwitchesFromAccountToProfileStorageOnSyncOptInWithAdvancedSetup) {
   ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
-  GetPersonalDataManager(0)->OnSyncServiceInitialized(GetSyncService(0));
 
   GetFakeServer()->SetWalletData({CreateDefaultSyncWalletCard()});
 
@@ -1491,7 +1512,6 @@
 IN_PROC_BROWSER_TEST_P(SingleClientWalletWithAccountStorageSyncTest,
                        SwitchesBetweenAccountAndProfileStorageOnTogglingSync) {
   ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
-  GetPersonalDataManager(0)->OnSyncServiceInitialized(GetSyncService(0));
 
   GetFakeServer()->SetWalletData({CreateDefaultSyncWalletCard()});
 
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 5a5bbee..7e98f3f 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
@@ -47,8 +47,6 @@
 
   ~TwoClientPasswordsSyncTest() override {}
 
-  bool TestUsesSelfNotifications() override { return false; }
-
  protected:
   // TODO(crbug.com/915219): This leads to a data race and thus all tests here
   // are disabled on TSan. It is hard to avoid as overriding g_feature_list
@@ -133,7 +131,7 @@
 #define MAYBE_MergeWithTheMostRecent MergeWithTheMostRecent
 #endif
 IN_PROC_BROWSER_TEST_P(TwoClientPasswordsSyncTest,
-                       E2E_ENABLED(MAYBE_MergeWithTheMostRecent)) {
+                       MAYBE_MergeWithTheMostRecent) {
   // Setup the test to have Form 0 and Form 1 added on both clients. Form 0 is
   // more recent on Client 0, and Form 1 is more recent on Client 1. They should
   // be merged such that recent passwords are chosen.
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 d3cf2502..4671ac5f 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
@@ -63,14 +63,6 @@
 
   bool SetupSync() override {
     test_clock_.SetNow(kArbitraryDefaultTime);
-    // Plug in SyncService into PDM so that it can check we use full sync. We
-    // need to do it before starting the sync in SetupSync(). We need to setup
-    // the clients before that so we can access their sync service.
-    if (!SetupClients()) {
-      return false;
-    }
-    GetPersonalDataManager(0)->OnSyncServiceInitialized(GetSyncService(0));
-    GetPersonalDataManager(1)->OnSyncServiceInitialized(GetSyncService(1));
 
     if (!SyncTest::SetupSync()) {
       return false;
diff --git a/chrome/browser/sync/test/integration/updated_progress_marker_checker.cc b/chrome/browser/sync/test/integration/updated_progress_marker_checker.cc
index ac20da15..3d70c20 100644
--- a/chrome/browser/sync/test/integration/updated_progress_marker_checker.cc
+++ b/chrome/browser/sync/test/integration/updated_progress_marker_checker.cc
@@ -5,12 +5,16 @@
 #include "chrome/browser/sync/test/integration/updated_progress_marker_checker.h"
 
 #include "base/bind.h"
+#include "chrome/browser/sync/test/integration/sync_datatype_helper.h"
+#include "chrome/browser/sync/test/integration/sync_test.h"
 #include "components/sync/driver/profile_sync_service.h"
 #include "components/sync/engine/cycle/sync_cycle_snapshot.h"
 
 UpdatedProgressMarkerChecker::UpdatedProgressMarkerChecker(
     syncer::ProfileSyncService* service)
     : SingleClientStatusChangeChecker(service), weak_ptr_factory_(this) {
+  DCHECK(sync_datatype_helper::test()->TestUsesSelfNotifications());
+
   // HasUnsyncedItemsForTest() posts a task to the sync thread which guarantees
   // that all tasks posted to the sync thread before this constructor have been
   // processed.
diff --git a/chrome/browser/sync/test/integration/wallet_helper.cc b/chrome/browser/sync/test/integration/wallet_helper.cc
index af23a1f..28c2546 100644
--- a/chrome/browser/sync/test/integration/wallet_helper.cc
+++ b/chrome/browser/sync/test/integration/wallet_helper.cc
@@ -20,6 +20,7 @@
 #include "components/autofill/core/browser/personal_data_manager.h"
 #include "components/autofill/core/browser/webdata/autofill_table.h"
 #include "components/keyed_service/core/service_access_type.h"
+#include "components/sync/driver/profile_sync_service.h"
 #include "components/sync/driver/sync_driver_switches.h"
 #include "components/sync/protocol/model_type_state.pb.h"
 
@@ -216,8 +217,13 @@
 const char kDefaultBillingAddressID[] = "billing address entity ID";
 
 PersonalDataManager* GetPersonalDataManager(int index) {
-  return autofill::PersonalDataManagerFactory::GetForProfile(
+  auto* pdm = autofill::PersonalDataManagerFactory::GetForProfile(
       test()->GetProfile(index));
+  // Hook the sync service up to the personal data manager.
+  // This is normally done by autofill_manager, which we don't
+  // have in our tests.
+  pdm->OnSyncServiceInitialized(test()->GetSyncService(index));
+  return pdm;
 }
 
 scoped_refptr<AutofillWebDataService> GetProfileWebDataService(int index) {
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 7640896..84259bf 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -359,6 +359,7 @@
   public_deps = [
     "//components/dom_distiller/core",
     "//components/sync",
+    "//components/sync:user_events",
     "//components/translate/content/browser",
     "//content/public/browser",
   ]
diff --git a/chrome/browser/ui/ash/accessibility/ax_tree_source_aura_unittest.cc b/chrome/browser/ui/ash/accessibility/ax_tree_source_aura_unittest.cc
index 622e3b70..6e99dd8 100644
--- a/chrome/browser/ui/ash/accessibility/ax_tree_source_aura_unittest.cc
+++ b/chrome/browser/ui/ash/accessibility/ax_tree_source_aura_unittest.cc
@@ -83,8 +83,9 @@
   Widget* widget_;
   View* content_;
   Textfield* textfield_;
+  AXAuraObjCache cache_;
   // A simulated desktop root with no delegate.
-  AXRootObjWrapper root_wrapper_{nullptr};
+  AXRootObjWrapper root_wrapper_{nullptr, &cache_};
 
  private:
   DISALLOW_COPY_AND_ASSIGN(AXTreeSourceAuraTest);
@@ -94,23 +95,22 @@
   // Focus the textfield so the cursor does not disappear.
   textfield_->RequestFocus();
 
-  AXTreeSourceViews ax_tree(&root_wrapper_, ui::AXTreeID::CreateNewAXTreeID());
+  AXTreeSourceViews ax_tree(&root_wrapper_, ui::AXTreeID::CreateNewAXTreeID(),
+                            &cache_);
   ASSERT_TRUE(ax_tree.GetRoot());
 
   // ID's should be > 0.
   ASSERT_GE(ax_tree.GetRoot()->GetUniqueId(), 1);
 
   // Grab the content view directly from cache to avoid walking down the tree.
-  AXAuraObjWrapper* content =
-      AXAuraObjCache::GetInstance()->GetOrCreate(content_);
+  AXAuraObjWrapper* content = cache_.GetOrCreate(content_);
   std::vector<AXAuraObjWrapper*> content_children;
   ax_tree.GetChildren(content, &content_children);
   ASSERT_EQ(1U, content_children.size());
 
   // Walk down to the text field and assert it is what we expect.
   AXAuraObjWrapper* textfield = content_children[0];
-  AXAuraObjWrapper* cached_textfield =
-      AXAuraObjCache::GetInstance()->GetOrCreate(textfield_);
+  AXAuraObjWrapper* cached_textfield = cache_.GetOrCreate(textfield_);
   ASSERT_EQ(cached_textfield, textfield);
   std::vector<AXAuraObjWrapper*> textfield_children;
   ax_tree.GetChildren(textfield, &textfield_children);
@@ -131,11 +131,11 @@
 }
 
 TEST_F(AXTreeSourceAuraTest, DoDefault) {
-  AXTreeSourceViews ax_tree(&root_wrapper_, ui::AXTreeID::CreateNewAXTreeID());
+  AXTreeSourceViews ax_tree(&root_wrapper_, ui::AXTreeID::CreateNewAXTreeID(),
+                            &cache_);
 
   // Grab a wrapper to |DoDefault| (click).
-  AXAuraObjWrapper* textfield_wrapper =
-      AXAuraObjCache::GetInstance()->GetOrCreate(textfield_);
+  AXAuraObjWrapper* textfield_wrapper = cache_.GetOrCreate(textfield_);
 
   // Click and verify focus.
   ASSERT_FALSE(textfield_->HasFocus());
@@ -147,11 +147,11 @@
 }
 
 TEST_F(AXTreeSourceAuraTest, Focus) {
-  AXTreeSourceViews ax_tree(&root_wrapper_, ui::AXTreeID::CreateNewAXTreeID());
+  AXTreeSourceViews ax_tree(&root_wrapper_, ui::AXTreeID::CreateNewAXTreeID(),
+                            &cache_);
 
   // Grab a wrapper to focus.
-  AXAuraObjWrapper* textfield_wrapper =
-      AXAuraObjCache::GetInstance()->GetOrCreate(textfield_);
+  AXAuraObjWrapper* textfield_wrapper = cache_.GetOrCreate(textfield_);
 
   // Focus and verify.
   ASSERT_FALSE(textfield_->HasFocus());
@@ -163,7 +163,8 @@
 }
 
 TEST_F(AXTreeSourceAuraTest, Serialize) {
-  AXTreeSourceViews ax_tree(&root_wrapper_, ui::AXTreeID::CreateNewAXTreeID());
+  AXTreeSourceViews ax_tree(&root_wrapper_, ui::AXTreeID::CreateNewAXTreeID(),
+                            &cache_);
   AuraAXTreeSerializer ax_serializer(&ax_tree);
   ui::AXTreeUpdate out_update;
 
@@ -179,8 +180,7 @@
 
   // Grab the textfield since serialization only walks up the tree (not down
   // from root).
-  AXAuraObjWrapper* textfield_wrapper =
-      AXAuraObjCache::GetInstance()->GetOrCreate(textfield_);
+  AXAuraObjWrapper* textfield_wrapper = cache_.GetOrCreate(textfield_);
 
   // Now, re-serialize.
   ui::AXTreeUpdate out_update2;
@@ -203,10 +203,10 @@
 }
 
 TEST_F(AXTreeSourceAuraTest, SerializeWindowSetsClipsChildren) {
-  AXTreeSourceViews ax_tree(&root_wrapper_, ui::AXTreeID::CreateNewAXTreeID());
+  AXTreeSourceViews ax_tree(&root_wrapper_, ui::AXTreeID::CreateNewAXTreeID(),
+                            &cache_);
   AuraAXTreeSerializer ax_serializer(&ax_tree);
-  AXAuraObjWrapper* widget_wrapper =
-      AXAuraObjCache::GetInstance()->GetOrCreate(widget_);
+  AXAuraObjWrapper* widget_wrapper = cache_.GetOrCreate(widget_);
   ui::AXNodeData node_data;
   ax_tree.SerializeNode(widget_wrapper, &node_data);
   EXPECT_EQ(ax::mojom::Role::kWindow, node_data.role);
diff --git a/chrome/browser/ui/aura/accessibility/automation_manager_aura.cc b/chrome/browser/ui/aura/accessibility/automation_manager_aura.cc
index 0c1ee2f..e302c690 100644
--- a/chrome/browser/ui/aura/accessibility/automation_manager_aura.cc
+++ b/chrome/browser/ui/aura/accessibility/automation_manager_aura.cc
@@ -53,21 +53,20 @@
   // GetTopLevelWindows() returns the correct values when automation is enabled
   // with multiple displays connected.
   for (aura::Window* root : ash::Shell::GetAllRootWindows())
-    views::AXAuraObjCache::GetInstance()->OnRootWindowObjCreated(root);
+    cache_->OnRootWindowObjCreated(root);
 #endif
 
   SendEvent(current_tree_->GetRoot(), ax::mojom::Event::kLoadComplete);
   // Intentionally not reset at shutdown since we cannot rely on the shutdown
   // ordering of two base::Singletons.
-  views::AXAuraObjCache::GetInstance()->SetDelegate(this);
+  cache_->SetDelegate(this);
 
 #if defined(OS_CHROMEOS)
   // TODO(crbug.com/756054): Support MultiProcessMash.
   if (!features::IsMultiProcessMash()) {
     aura::Window* active_window = ash::wm::GetActiveWindow();
     if (active_window) {
-      views::AXAuraObjWrapper* focus =
-          views::AXAuraObjCache::GetInstance()->GetOrCreate(active_window);
+      views::AXAuraObjWrapper* focus = cache_->GetOrCreate(active_window);
       if (focus)
         SendEvent(focus, ax::mojom::Event::kChildrenChanged);
     }
@@ -93,8 +92,7 @@
   if (!enabled_)
     return;
 
-  views::AXAuraObjWrapper* obj =
-      views::AXAuraObjCache::GetInstance()->GetOrCreate(view);
+  views::AXAuraObjWrapper* obj = cache_->GetOrCreate(view);
   if (!obj)
     return;
 
@@ -128,7 +126,7 @@
 
 void AutomationManagerAura::SendEventOnObjectById(int32_t id,
                                                   ax::mojom::Event event_type) {
-  views::AXAuraObjWrapper* obj = views::AXAuraObjCache::GetInstance()->Get(id);
+  views::AXAuraObjWrapper* obj = cache_->Get(id);
   if (obj)
     SendEvent(obj, event_type);
 }
@@ -168,7 +166,9 @@
 }
 
 AutomationManagerAura::AutomationManagerAura()
-    : enabled_(false), processing_events_(false) {
+    : enabled_(false),
+      processing_events_(false),
+      cache_(std::make_unique<views::AXAuraObjCache>()) {
   views::AXEventManager::Get()->AddObserver(this);
 }
 
@@ -177,9 +177,9 @@
 
 void AutomationManagerAura::Reset(bool reset_serializer) {
   if (!current_tree_) {
-    desktop_root_ = std::make_unique<AXRootObjWrapper>(this);
+    desktop_root_ = std::make_unique<AXRootObjWrapper>(this, cache_.get());
     current_tree_ = std::make_unique<views::AXTreeSourceViews>(
-        desktop_root_.get(), ax_tree_id());
+        desktop_root_.get(), ax_tree_id(), cache_.get());
   }
   if (reset_serializer) {
     current_tree_serializer_.reset();
@@ -194,7 +194,7 @@
     alert_window_ = std::make_unique<views::AccessibilityAlertWindow>(
         shell->GetContainer(shell->GetPrimaryRootWindow(),
                             ash::kShellWindowId_OverlayContainer),
-        views::AXAuraObjCache::GetInstance());
+        cache_.get());
 #endif  // defined(OS_CHROMEOS)
   }
 }
@@ -222,8 +222,7 @@
   tree_updates.push_back(update);
 
   // Make sure the focused node is serialized.
-  views::AXAuraObjWrapper* focus =
-      views::AXAuraObjCache::GetInstance()->GetFocus();
+  views::AXAuraObjWrapper* focus = cache_->GetFocus();
   if (focus) {
     ui::AXTreeUpdate focused_node_update;
     current_tree_serializer_->SerializeChanges(focus, &focused_node_update);
@@ -310,8 +309,7 @@
   }
 
   // Otherwise, fire the event directly on the Window.
-  views::AXAuraObjWrapper* window_wrapper =
-      views::AXAuraObjCache::GetInstance()->GetOrCreate(window);
+  views::AXAuraObjWrapper* window_wrapper = cache_->GetOrCreate(window);
   if (window_wrapper)
     SendEvent(window_wrapper, action.hit_test_event_to_fire);
 #endif
diff --git a/chrome/browser/ui/aura/accessibility/automation_manager_aura.h b/chrome/browser/ui/aura/accessibility/automation_manager_aura.h
index 77ebf6f..a65cbf2 100644
--- a/chrome/browser/ui/aura/accessibility/automation_manager_aura.h
+++ b/chrome/browser/ui/aura/accessibility/automation_manager_aura.h
@@ -73,6 +73,11 @@
     event_bundle_sink_ = sink;
   }
 
+  void set_ax_aura_obj_cache_for_testing(
+      std::unique_ptr<views::AXAuraObjCache> cache) {
+    cache_ = std::move(cache);
+  }
+
  private:
   friend class base::NoDestructor<AutomationManagerAura>;
 
@@ -122,6 +127,8 @@
 
   std::unique_ptr<views::AccessibilityAlertWindow> alert_window_;
 
+  std::unique_ptr<views::AXAuraObjCache> cache_;
+
   DISALLOW_COPY_AND_ASSIGN(AutomationManagerAura);
 };
 
diff --git a/chrome/browser/ui/aura/accessibility/automation_manager_aura_browsertest.cc b/chrome/browser/ui/aura/accessibility/automation_manager_aura_browsertest.cc
index ed9e29e63..d47e3b59 100644
--- a/chrome/browser/ui/aura/accessibility/automation_manager_aura_browsertest.cc
+++ b/chrome/browser/ui/aura/accessibility/automation_manager_aura_browsertest.cc
@@ -157,7 +157,10 @@
 
 IN_PROC_BROWSER_TEST_F(AutomationManagerAuraBrowserTest,
                        TransientFocusChangesAreSuppressed) {
+  auto cache = std::make_unique<views::AXAuraObjCache>();
+  auto* cache_ptr = cache.get();
   AutomationManagerAura* manager = AutomationManagerAura::GetInstance();
+  manager->set_ax_aura_obj_cache_for_testing(std::move(cache));
   manager->Enable();
 
   views::Widget* widget = new views::Widget;
@@ -167,24 +170,23 @@
   widget->Show();
   widget->Activate();
 
-  views::AXAuraObjCache* cache = views::AXAuraObjCache::GetInstance();
-  cache->set_focused_widget_for_testing(widget);
+  cache_ptr->set_focused_widget_for_testing(widget);
 
   views::View* view1 = new views::View();
   view1->GetViewAccessibility().OverrideName("view1");
   view1->SetFocusBehavior(views::View::FocusBehavior::ALWAYS);
   widget->GetRootView()->AddChildView(view1);
-  views::AXAuraObjWrapper* wrapper1 = cache->GetOrCreate(view1);
+  views::AXAuraObjWrapper* wrapper1 = cache_ptr->GetOrCreate(view1);
   views::View* view2 = new views::View();
   view2->SetFocusBehavior(views::View::FocusBehavior::ALWAYS);
   view2->GetViewAccessibility().OverrideName("view2");
   widget->GetRootView()->AddChildView(view2);
-  views::AXAuraObjWrapper* wrapper2 = cache->GetOrCreate(view2);
+  views::AXAuraObjWrapper* wrapper2 = cache_ptr->GetOrCreate(view2);
   views::View* view3 = new views::View();
   view3->GetViewAccessibility().OverrideName("view3");
   view3->SetFocusBehavior(views::View::FocusBehavior::ALWAYS);
   widget->GetRootView()->AddChildView(view3);
-  views::AXAuraObjWrapper* wrapper3 = cache->GetOrCreate(view3);
+  views::AXAuraObjWrapper* wrapper3 = cache_ptr->GetOrCreate(view3);
 
   AutomationEventWaiter waiter;
 
@@ -207,7 +209,7 @@
   EXPECT_FALSE(waiter.WasNodeIdFocused(wrapper2->GetUniqueId()));
   EXPECT_TRUE(waiter.WasNodeIdFocused(wrapper3->GetUniqueId()));
 
-  cache->set_focused_widget_for_testing(nullptr);
+  cache_ptr->set_focused_widget_for_testing(nullptr);
 
   AddFailureOnWidgetAccessibilityError(widget);
 }
diff --git a/chrome/browser/ui/browser_instant_controller_unittest.cc b/chrome/browser/ui/browser_instant_controller_unittest.cc
index c2e5aade..60c8139 100644
--- a/chrome/browser/ui/browser_instant_controller_unittest.cc
+++ b/chrome/browser/ui/browser_instant_controller_unittest.cc
@@ -45,7 +45,6 @@
 // Test cases for when Google is the initial, but not final provider.
 const TabReloadTestCase kTabReloadTestCasesFinalProviderNotGoogle[] = {
     {"Local NTP", chrome::kChromeSearchLocalNtpUrl, true, true, true, true},
-    {"Remote NTP", "https://www.google.com/newtab", true, true, false, false},
     {"Remote SERP", "https://www.google.com/url?bar=search+terms", false, false,
      false, false},
     {"Other NTP", "https://bar.com/newtab", false, false, false, false}};
@@ -53,7 +52,6 @@
 // Test cases for when Google is both the initial and final provider.
 const TabReloadTestCase kTabReloadTestCasesFinalProviderGoogle[] = {
     {"Local NTP", chrome::kChromeSearchLocalNtpUrl, true, true, true, true},
-    {"Remote NTP", "https://www.google.com/newtab", true, false, true, true},
     {"Remote SERP", "https://www.google.com/url?bar=search+terms", false, false,
      false, false},
     {"Other NTP", "https://bar.com/newtab", false, false, false, false}};
diff --git a/chrome/browser/ui/input_method/input_method_engine.cc b/chrome/browser/ui/input_method/input_method_engine.cc
index fa487abf..cc13e14 100644
--- a/chrome/browser/ui/input_method/input_method_engine.cc
+++ b/chrome/browser/ui/input_method/input_method_engine.cc
@@ -91,18 +91,17 @@
 
 void InputMethodEngine::CommitTextToInputContext(int context_id,
                                                  const std::string& text) {
-  // Append the text to the buffer, as it allows committing text multiple times
-  // when processing a key event.
-  text_ += text;
-
   ui::IMEInputContextHandlerInterface* input_context =
       ui::IMEBridge::Get()->GetInputContextHandler();
   // If the IME extension is handling key event, hold the text until the key
   // event is handled.
   if (input_context && !handling_key_event_) {
-    input_context->CommitText(text_);
+    input_context->CommitText(text);
     text_ = "";
   } else {
+    // Append the text to the buffer, as it allows committing text multiple
+    // times when processing a key event.
+    text_ += text;
     commit_text_changed_ = true;
   }
 }
diff --git a/chrome/browser/ui/toolbar/recent_tabs_builder_test_helper.cc b/chrome/browser/ui/toolbar/recent_tabs_builder_test_helper.cc
index 7f6b4fe..9b20d05 100644
--- a/chrome/browser/ui/toolbar/recent_tabs_builder_test_helper.cc
+++ b/chrome/browser/ui/toolbar/recent_tabs_builder_test_helper.cc
@@ -204,7 +204,7 @@
 
   sync_pb::ModelTypeState model_type_state;
   model_type_state.set_initial_sync_done(true);
-  processor->OnUpdateReceived(model_type_state, updates);
+  processor->OnUpdateReceived(model_type_state, std::move(updates));
   // ClientTagBasedModelTypeProcessor uses ModelTypeProcessorProxy during
   // activation, which involves task posting for receiving updates.
   base::RunLoop().RunUntilIdle();
@@ -301,7 +301,8 @@
   return specifics;
 }
 
-syncer::UpdateResponseData RecentTabsBuilderTestHelper::BuildUpdateResponseData(
+std::unique_ptr<syncer::UpdateResponseData>
+RecentTabsBuilderTestHelper::BuildUpdateResponseData(
     const sync_pb::SessionSpecifics& specifics,
     base::Time timestamp) {
   syncer::EntityData entity;
@@ -312,8 +313,8 @@
       syncer::SESSIONS, sync_sessions::SessionStore::GetClientTag(specifics));
   entity.id = entity.client_tag_hash;
 
-  syncer::UpdateResponseData update;
-  update.entity = entity.PassToPtr();
-  update.response_version = ++next_response_version_;
+  auto update = std::make_unique<syncer::UpdateResponseData>();
+  update->entity = entity.PassToPtr();
+  update->response_version = ++next_response_version_;
   return update;
 }
diff --git a/chrome/browser/ui/toolbar/recent_tabs_builder_test_helper.h b/chrome/browser/ui/toolbar/recent_tabs_builder_test_helper.h
index 17cacd5..b8dae09 100644
--- a/chrome/browser/ui/toolbar/recent_tabs_builder_test_helper.h
+++ b/chrome/browser/ui/toolbar/recent_tabs_builder_test_helper.h
@@ -67,7 +67,7 @@
   sync_pb::SessionSpecifics BuildTabSpecifics(int session_index,
                                               int window_index,
                                               int tab_index);
-  syncer::UpdateResponseData BuildUpdateResponseData(
+  std::unique_ptr<syncer::UpdateResponseData> BuildUpdateResponseData(
       const sync_pb::SessionSpecifics& specifics,
       base::Time timestamp);
 
diff --git a/chrome/browser/ui/views/autofill/save_card_bubble_views_browsertest.cc b/chrome/browser/ui/views/autofill/save_card_bubble_views_browsertest.cc
index b45730d..e227d7a 100644
--- a/chrome/browser/ui/views/autofill/save_card_bubble_views_browsertest.cc
+++ b/chrome/browser/ui/views/autofill/save_card_bubble_views_browsertest.cc
@@ -17,6 +17,7 @@
 #include "chrome/browser/signin/identity_manager_factory.h"
 #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/secondary_account_helper.h"
 #include "chrome/browser/sync/test/integration/sync_test.h"
 #include "chrome/browser/ui/autofill/chrome_autofill_client.h"
 #include "chrome/browser/ui/autofill/payments/payments_ui_constants.h"
@@ -49,6 +50,7 @@
 #include "components/network_session_configurator/common/network_switches.h"
 #include "components/signin/core/browser/signin_buildflags.h"
 #include "components/sync/driver/profile_sync_service.h"
+#include "components/sync/driver/sync_driver_switches.h"
 #include "components/sync/test/fake_server/fake_server.h"
 #include "components/sync/test/fake_server/fake_server_network_resources.h"
 #include "content/public/browser/web_contents_observer.h"
@@ -735,7 +737,6 @@
  private:
   std::unique_ptr<autofill::EventWaiter<DialogEvent>> event_waiter_;
   std::unique_ptr<net::FakeURLFetcherFactory> url_fetcher_factory_;
-  network::TestURLLoaderFactory test_url_loader_factory_;
   scoped_refptr<network::SharedURLLoaderFactory> test_shared_loader_factory_;
   std::unique_ptr<device::ScopedGeolocationOverrider> geolocation_overrider_;
 
@@ -1328,6 +1329,85 @@
       AutofillMetrics::SAVE_CARD_PROMPT_END_ACCEPTED, 1);
 }
 
+// On Chrome OS, the test profile starts with a primary account already set, so
+// sync-the-transport tests don't apply.
+#if !defined(OS_CHROMEOS)
+
+// Sets up Chrome with Sync-the-transport mode enabled, with the Wallet datatype
+// as enabled type.
+class SaveCardBubbleViewsSyncTransportFullFormBrowserTest
+    : public SaveCardBubbleViewsFullFormBrowserTest {
+ protected:
+  SaveCardBubbleViewsSyncTransportFullFormBrowserTest() = default;
+
+  void SetUpInProcessBrowserTestFixture() override {
+    // Set up Sync the transport mode, so that sync starts on content-area
+    // signins. Also add wallet data type to the list of enabled types.
+    scoped_feature_list_.InitWithFeatures(
+        /*enabled_features=*/{features::kAutofillUpstream,
+                              features::kAutofillEnableAccountWalletStorage,
+                              switches::kSyncSupportSecondaryAccount},
+        /*disabled_features=*/{});
+    test_signin_client_factory_ =
+        secondary_account_helper::SetUpSigninClient(test_url_loader_factory());
+
+    SaveCardBubbleViewsFullFormBrowserTest::SetUpInProcessBrowserTestFixture();
+  }
+
+ private:
+  secondary_account_helper::ScopedSigninClientFactory
+      test_signin_client_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(SaveCardBubbleViewsSyncTransportFullFormBrowserTest);
+};
+
+// Tests the upload save bubble. Ensures that clicking the [Save] button
+// successfully causes the bubble to go away and sends an UploadCardRequest RPC
+// to Google Payments.
+IN_PROC_BROWSER_TEST_F(SaveCardBubbleViewsSyncTransportFullFormBrowserTest,
+                       Upload_TransportMode_ClickingSaveClosesBubble) {
+  // Signing in (without making the account Chrome's primary one or explicitly
+  // setting up Sync) causes the Sync machinery to start up in standalone
+  // transport mode.
+  secondary_account_helper::SignInSecondaryAccount(
+      browser()->profile(), test_url_loader_factory(), "user@gmail.com");
+  ASSERT_NE(syncer::SyncService::TransportState::DISABLED,
+            harness_->service()->GetTransportState());
+
+  ASSERT_TRUE(harness_->AwaitSyncTransportActive());
+  ASSERT_EQ(syncer::SyncService::TransportState::ACTIVE,
+            harness_->service()->GetTransportState());
+  ASSERT_FALSE(harness_->service()->IsSyncFeatureEnabled());
+
+  // Set up the Payments RPC.
+  SetUploadDetailsRpcPaymentsAccepts();
+
+  // Submitting the form should show the upload save bubble and legal footer.
+  // (Must wait for response from Payments before accessing the controller.)
+  ResetEventWaiterForSequence(
+      {DialogEvent::REQUESTED_UPLOAD_SAVE,
+       DialogEvent::RECEIVED_GET_UPLOAD_DETAILS_RESPONSE});
+
+  NavigateTo(kCreditCardUploadForm);
+  FillAndSubmitForm();
+  WaitForObservedEvent();
+  EXPECT_TRUE(
+      FindViewInBubbleById(DialogViewId::MAIN_CONTENT_VIEW_UPLOAD)->visible());
+  EXPECT_TRUE(FindViewInBubbleById(DialogViewId::FOOTNOTE_VIEW)->visible());
+
+  // Clicking [Save] should accept and close it, then send an UploadCardRequest
+  // to Google Payments.
+  ResetEventWaiterForSequence({DialogEvent::SENT_UPLOAD_CARD_REQUEST});
+  base::HistogramTester histogram_tester;
+  ClickOnDialogViewWithIdAndWait(DialogViewId::OK_BUTTON);
+  // UMA should have recorded bubble acceptance.
+  histogram_tester.ExpectUniqueSample(
+      "Autofill.SaveCreditCardPrompt.Upload.FirstShow",
+      AutofillMetrics::SAVE_CARD_PROMPT_END_ACCEPTED, 1);
+}
+
+#endif  // !OS_CHROMEOS
+
 // Tests the upload save bubble. Ensures that clicking the [No thanks] button
 // successfully causes the bubble to go away.
 IN_PROC_BROWSER_TEST_F(SaveCardBubbleViewsFullFormBrowserTest,
diff --git a/chrome/browser/ui/views/page_action/pwa_install_view.cc b/chrome/browser/ui/views/page_action/pwa_install_view.cc
index 0cf1c50..eafbcfb9 100644
--- a/chrome/browser/ui/views/page_action/pwa_install_view.cc
+++ b/chrome/browser/ui/views/page_action/pwa_install_view.cc
@@ -4,9 +4,12 @@
 
 #include "chrome/browser/ui/views/page_action/pwa_install_view.h"
 
+#include "base/bind_helpers.h"
 #include "base/metrics/user_metrics.h"
 #include "chrome/app/chrome_command_ids.h"
 #include "chrome/browser/banners/app_banner_manager.h"
+#include "chrome/browser/installable/installable_metrics.h"
+#include "chrome/browser/ui/web_applications/web_app_dialog_utils.h"
 #include "chrome/browser/web_applications/components/web_app_tab_helper_base.h"
 #include "chrome/grit/generated_resources.h"
 #include "components/omnibox/browser/vector_icons.h"
@@ -14,7 +17,7 @@
 
 PwaInstallView::PwaInstallView(CommandUpdater* command_updater,
                                PageActionIconView::Delegate* delegate)
-    : PageActionIconView(command_updater, IDC_INSTALL_PWA, delegate) {
+    : PageActionIconView(nullptr, 0, delegate) {
   SetVisible(false);
   SetLabel(l10n_util::GetStringUTF16(IDS_OMNIBOX_PWA_INSTALL_ICON_LABEL));
   SetUpForInOutAnimation();
@@ -54,6 +57,9 @@
 
 void PwaInstallView::OnExecuting(PageActionIconView::ExecuteSource source) {
   base::RecordAction(base::UserMetricsAction("PWAInstallIcon"));
+  web_app::CreateWebAppFromBanner(GetWebContents(),
+                                  WebappInstallSource::OMNIBOX_INSTALL_ICON,
+                                  base::DoNothing());
 }
 
 views::BubbleDialogDelegateView* PwaInstallView::GetBubble() const {
diff --git a/chrome/browser/ui/views/profiles/profile_chooser_view.cc b/chrome/browser/ui/views/profiles/profile_chooser_view.cc
index 2b7f5550..6392194 100644
--- a/chrome/browser/ui/views/profiles/profile_chooser_view.cc
+++ b/chrome/browser/ui/views/profiles/profile_chooser_view.cc
@@ -243,6 +243,8 @@
 void ProfileChooserView::ButtonPressed(views::Button* sender,
                                        const ui::Event& event) {
   if (sender == manage_google_account_button_) {
+    base::RecordAction(
+        base::UserMetricsAction("ProfileChooser_ManageGoogleAccountClicked"));
     NavigateToGoogleAccountPage(browser()->profile());
   } else if (sender == passwords_button_) {
     base::RecordAction(
diff --git a/chrome/browser/ui/web_applications/web_app_dialog_utils.h b/chrome/browser/ui/web_applications/web_app_dialog_utils.h
index 6d41f19c..e1aa636 100644
--- a/chrome/browser/ui/web_applications/web_app_dialog_utils.h
+++ b/chrome/browser/ui/web_applications/web_app_dialog_utils.h
@@ -33,8 +33,10 @@
     base::OnceCallback<void(const AppId& app_id, InstallResultCode code)>;
 
 // Starts install of a WebApp for a given |web_contents|, initiated from
-// Application's Banner UI.
+// a promotional banner or omnibox install icon.
 // Returns false if WebApps are disabled for the profile behind |web_contents|.
+// TODO(https://crbug.com/907351): Rename this to describe its behaviour instead
+// of its callers.
 bool CreateWebAppFromBanner(content::WebContents* web_contents,
                             WebappInstallSource install_source,
                             WebAppInstalledCallback installed_callback);
diff --git a/chrome/browser/ui/webui/management_ui.cc b/chrome/browser/ui/webui/management_ui.cc
index 36dbc196..ab6ae97b 100644
--- a/chrome/browser/ui/webui/management_ui.cc
+++ b/chrome/browser/ui/webui/management_ui.cc
@@ -70,6 +70,8 @@
   static constexpr LocalizedString kLocalizedStrings[] = {
 #if defined(OS_CHROMEOS)
     {"learnMore", IDS_LEARN_MORE},
+    {"localTrustRoots", IDS_MANAGEMENT_LOCAL_TRUST_ROOTS},
+    {"managementTrustRootsConfigured", IDS_MANAGEMENT_TRUST_ROOTS_CONFIGURED},
     {"deviceConfiguration", IDS_MANAGEMENT_DEVICE_CONFIGURATION},
     {"deviceReporting", IDS_MANAGEMENT_DEVICE_REPORTING},
     {kManagementLogUploadEnabled, IDS_MANAGEMENT_LOG_UPLOAD_ENABLED},
@@ -88,9 +90,6 @@
     {"extensionReporting", IDS_MANAGEMENT_EXTENSION_REPORTING},
     {"extensionName", IDS_MANAGEMENT_EXTENSIONS_NAME},
     {"extensionPermissions", IDS_MANAGEMENT_EXTENSIONS_PERMISSIONS},
-    {"localTrustRoots", IDS_MANAGEMENT_LOCAL_TRUST_ROOTS},
-    {"managementTrustRootsNotConfigured",
-     IDS_MANAGEMENT_TRUST_ROOTS_NOT_CONFIGURED},
     {"title", IDS_MANAGEMENT_TITLE},
     {"toolbarTitle", IDS_MANAGEMENT_TOOLBAR_TITLE},
     {"searchPrompt", IDS_SETTINGS_SEARCH_PROMPT},
@@ -127,14 +126,6 @@
   source->AddString("managementAccountLearnMoreUrl",
                     chrome::kManagedUiLearnMoreUrl);
 
-#if BUILDFLAG(ENABLE_EXTENSIONS)
-
-#endif  // BUILDFLAG(ENABLE_EXTENSIONS)
-
-#if defined(OS_CHROMEOS)
-  source->AddLocalizedString("managementTrustRootsConfigured",
-                             IDS_MANAGEMENT_TRUST_ROOTS_CONFIGURED);
-#endif  // defined(OS_CHROMEOS)
   source->SetJsonPath("strings.js");
   // Add required resources.
   source->AddResourcePath("management_browser_proxy.html",
diff --git a/chrome/browser/ui/webui/settings/chromeos/account_manager_handler.cc b/chrome/browser/ui/webui/settings/chromeos/account_manager_handler.cc
index ea0e06b3..a721a6c 100644
--- a/chrome/browser/ui/webui/settings/chromeos/account_manager_handler.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/account_manager_handler.cc
@@ -12,6 +12,7 @@
 #include "base/macros.h"
 #include "base/values.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
+#include "chrome/browser/policy/profile_policy_connector_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/webui/chromeos/account_manager_welcome_dialog.h"
 #include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h"
@@ -30,6 +31,19 @@
 
 namespace {
 
+constexpr char kFamilyLink[] = "Family Link";
+
+std::string GetEnterpriseDomainFromUsername(const std::string& username) {
+  size_t email_separator_pos = username.find('@');
+  bool is_email = email_separator_pos != std::string::npos &&
+                  email_separator_pos < username.length() - 1;
+
+  if (!is_email)
+    return std::string();
+
+  return gaia::ExtractDomainName(username);
+}
+
 AccountManager::AccountKey GetAccountKeyFromJsCallback(
     const base::DictionaryValue* const dictionary) {
   const base::Value* id_value = dictionary->FindKey("id");
@@ -173,6 +187,19 @@
   // Device account must show up at the top.
   if (!device_account.empty()) {
     device_account.SetBoolean("isDeviceAccount", true);
+
+    // Check if user is managed.
+    const Profile* const profile = Profile::FromWebUI(web_ui());
+    if (profile->IsChild()) {
+      device_account.SetString("organization", kFamilyLink);
+    } else if (policy::ProfilePolicyConnectorFactory::IsProfileManaged(
+                   profile)) {
+      device_account.SetString(
+          "organization",
+          GetEnterpriseDomainFromUsername(
+              identity_manager_->GetPrimaryAccountInfo().email));
+    }
+
     accounts.GetList().insert(accounts.GetList().begin(),
                               std::move(device_account));
   }
diff --git a/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc b/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
index 50f3c74..4259c13 100644
--- a/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
@@ -1600,6 +1600,10 @@
      IDS_SETTINGS_ACCOUNT_MANAGER_SIGNED_OUT_ACCOUNT_PLACEHOLDER},
     {"accountManagerReauthenticationLabel",
      IDS_SETTINGS_ACCOUNT_MANAGER_REAUTHENTICATION_LABEL},
+    {"accountManagerManagedLabel",
+     IDS_SETTINGS_ACCOUNT_MANAGER_MANAGEMENT_STATUS_MANAGED_ACCOUNT},
+    {"accountManagerUnmanagedLabel",
+     IDS_SETTINGS_ACCOUNT_MANAGER_MANAGEMENT_STATUS_UNMANAGED_ACCOUNT},
     {"configureFingerprintTitle", IDS_SETTINGS_ADD_FINGERPRINT_DIALOG_TITLE},
     {"configureFingerprintInstructionLocateScannerStep",
      IDS_SETTINGS_ADD_FINGERPRINT_DIALOG_INSTRUCTION_LOCATE_SCANNER},
diff --git a/chrome/common/chrome_features.cc b/chrome/common/chrome_features.cc
index dc004299..15d1434b 100644
--- a/chrome/common/chrome_features.cc
+++ b/chrome/common/chrome_features.cc
@@ -413,10 +413,10 @@
 #if defined(OS_CHROMEOS)
 // Enables or disables notification which pop-ups after managed guest session
 // autolaunch
-// TODO(owner:raleksandrov): Remove this when message will be confirmed.
+// TODO(owner:raleksandrov): Remove this after successful launch.
 // https://crbug.com/927331
 const base::Feature kManagedGuestSessionNotification{
-    "ManagedGuestSessionNotification", base::FEATURE_DISABLED_BY_DEFAULT};
+    "ManagedGuestSessionNotification", base::FEATURE_ENABLED_BY_DEFAULT};
 #endif
 
 // Enables or disables modal permission prompts.
diff --git a/chrome/services/diagnosticsd/OWNERS b/chrome/services/diagnosticsd/OWNERS
index f301e83..b363b3e 100644
--- a/chrome/services/diagnosticsd/OWNERS
+++ b/chrome/services/diagnosticsd/OWNERS
@@ -1,3 +1,6 @@
+pbond@chromium.org
+
+# Backup reviewers:
 emaxx@chromium.org
 pmarko@chromium.org
 
diff --git a/chrome/services/diagnosticsd/public/mojom/diagnosticsd.mojom b/chrome/services/diagnosticsd/public/mojom/diagnosticsd.mojom
index 5ad154e..a9df4af 100644
--- a/chrome/services/diagnosticsd/public/mojom/diagnosticsd.mojom
+++ b/chrome/services/diagnosticsd/public/mojom/diagnosticsd.mojom
@@ -60,6 +60,17 @@
   // diagnostics_processor daemon).
   SendUiMessageToDiagnosticsProcessor@0(handle json_message)
       => (handle? response_json_message);
+
+  // Called when new configuration data blob is available. This happens when
+  // the device policy, passing this configuration data blob, gets updated.
+  // It is only a notification that the configuration data has been changed.
+  // The GetConfigurationData method of DiagnosticsdClient needs to be called to
+  // retrieve the actual configuration data.
+  // NOTE: This notification is only triggered for changes that occur after the
+  // current Mojo connection was established;
+  // an initial call to GetConfigurationData should be used in order to fetch
+  // the changes that happened beforehand.
+  NotifyConfigurationDataChanged@1();
 };
 
 // Interface exposed by the consumer of DiagnosticsdService. In production this
@@ -112,4 +123,8 @@
   // diagnostics_processor daemon).
   SendDiagnosticsProcessorMessageToUi@1(handle json_message)
       => (handle? response_json_message);
+
+  // Retrieves a JSON-formatted configuration data. The length of
+  // |json_configuration_data| does not exceed 20'000 bytes.
+  GetConfigurationData@2() => (string json_configuration_data);
 };
diff --git a/chrome/test/data/pdf/imgpdf b/chrome/test/data/pdf/imgpdf
new file mode 100644
index 0000000..fa9e7b1
--- /dev/null
+++ b/chrome/test/data/pdf/imgpdf
Binary files differ
diff --git a/chrome/test/data/webui/app_management/arc_permission_view_test.js b/chrome/test/data/webui/app_management/arc_permission_view_test.js
index 0734ba0f..df00817 100644
--- a/chrome/test/data/webui/app_management/arc_permission_view_test.js
+++ b/chrome/test/data/webui/app_management/arc_permission_view_test.js
@@ -17,10 +17,33 @@
     arcPermissionView.root.querySelector('#subpermission-expand-row').click();
   }
 
+  function getPermissionBoolByType(permissionType) {
+    return app_management.util.getPermissionValueBool(
+        arcPermissionView.app_, permissionType);
+  }
+
+  function getPermissionToggleByType(permissionType) {
+    return arcPermissionView.root
+        .querySelector('[permission-type=' + permissionType + ']')
+        .root.querySelector('app-management-permission-toggle')
+        .root.querySelector('cr-toggle');
+  }
+
+  async function clickPermissionToggle(permissionType) {
+    getPermissionToggleByType(permissionType).click();
+    await fakeHandler.$.flushForTesting();
+  }
+
+  async function clickPermissionItem(permissionType) {
+    getPermissionItemByPermissionType(permissionType).click();
+    await fakeHandler.$.flushForTesting();
+  }
+
   setup(async () => {
     fakeHandler = setupFakeHandler();
     replaceStore();
 
+    // Create an ARC app without microphone permissions.
     const arcOptions = {
       type: apps.mojom.AppType.kArc,
       permissions: app_management.FakePageHandler.createArcPermissions([
@@ -52,4 +75,49 @@
     assertFalse(isHidden(getPermissionItemByPermissionType('LOCATION')));
     assertFalse(isHidden(getPermissionItemByPermissionType('CAMERA')));
   });
+
+  test('Toggle works correctly', async () => {
+    const checkPermissionToggle = async (permissionType) => {
+      assertTrue(getPermissionBoolByType(permissionType));
+      assertTrue(getPermissionToggleByType(permissionType).checked);
+
+      // Toggle Off.
+      await clickPermissionToggle(permissionType);
+      assertFalse(getPermissionBoolByType(permissionType));
+      assertFalse(getPermissionToggleByType(permissionType).checked);
+
+      // Toggle On.
+      await clickPermissionToggle(permissionType);
+      assertTrue(getPermissionBoolByType(permissionType));
+      assertTrue(getPermissionToggleByType(permissionType).checked);
+    };
+
+    expandPermissions();
+    await checkPermissionToggle('LOCATION');
+    await checkPermissionToggle('CAMERA');
+    await checkPermissionToggle('NOTIFICATIONS');
+  });
+
+
+  test('OnClick handler for permission item works correctly', async () => {
+    const checkPermissionItemOnClick = async (permissionType) => {
+      assertTrue(getPermissionBoolByType(permissionType));
+      assertTrue(getPermissionToggleByType(permissionType).checked);
+
+      // Toggle Off.
+      await clickPermissionItem(permissionType);
+      assertFalse(getPermissionBoolByType(permissionType));
+      assertFalse(getPermissionToggleByType(permissionType).checked);
+
+      // Toggle On.
+      await clickPermissionItem(permissionType);
+      assertTrue(getPermissionBoolByType(permissionType));
+      assertTrue(getPermissionToggleByType(permissionType).checked);
+    };
+
+    expandPermissions();
+    await checkPermissionItemOnClick('LOCATION');
+    await checkPermissionItemOnClick('CAMERA');
+    await checkPermissionItemOnClick('NOTIFICATIONS');
+  });
 });
diff --git a/chrome/test/data/webui/app_management/pwa_permission_view_test.js b/chrome/test/data/webui/app_management/pwa_permission_view_test.js
index 325de47d..cfc030f 100644
--- a/chrome/test/data/webui/app_management/pwa_permission_view_test.js
+++ b/chrome/test/data/webui/app_management/pwa_permission_view_test.js
@@ -46,7 +46,7 @@
   });
 
   test('toggle permissions', async function() {
-    let checkToggle = async function(permissionType) {
+    const checkToggle = async (permissionType) => {
       assertTrue(getPermissionBoolByType(permissionType));
       assertTrue(getPermissionToggleByType(permissionType).checked);
 
diff --git a/chrome/test/data/webui/print_preview/destination_search_test.js b/chrome/test/data/webui/print_preview/destination_search_test.js
index f8d9947..ec5a066 100644
--- a/chrome/test/data/webui/print_preview/destination_search_test.js
+++ b/chrome/test/data/webui/print_preview/destination_search_test.js
@@ -212,7 +212,7 @@
         capabilities:
             print_preview_test_utils.getCddTemplate(destId).capabilities,
         policies: {
-          allowedColorModes: print_preview.ColorMode.GRAY,
+          allowedColorModes: print_preview.ColorModeRestriction.MONOCHROME,
           allowedDuplexModes: print_preview.DuplexModeRestriction.DUPLEX,
         },
         success: true,
@@ -227,7 +227,7 @@
         assertNotEquals(null, selectedDestination.capabilities);
         assertNotEquals(null, selectedDestination.policies);
         assertEquals(
-            print_preview.ColorMode.GRAY,
+            print_preview.ColorModeRestriction.MONOCHROME,
             selectedDestination.policies.allowedColorModes);
         assertEquals(
             print_preview.DuplexModeRestriction.DUPLEX,
diff --git a/chrome/test/data/webui/print_preview/model_settings_policy_test.js b/chrome/test/data/webui/print_preview/model_settings_policy_test.js
index f7c10061..3f3906a 100644
--- a/chrome/test/data/webui/print_preview/model_settings_policy_test.js
+++ b/chrome/test/data/webui/print_preview/model_settings_policy_test.js
@@ -46,8 +46,8 @@
       [{
         // Policy has no effect, setting unavailable
         colorCap: {option: [{type: 'STANDARD_COLOR', is_default: true}]},
-        colorPolicy: print_preview.ColorMode.COLOR,
-        colorDefault: print_preview.ColorMode.COLOR,
+        colorPolicy: print_preview.ColorModeRestriction.COLOR,
+        colorDefault: print_preview.ColorModeRestriction.COLOR,
         expectedValue: true,
         expectedAvailable: false,
         expectedManaged: false,
@@ -56,8 +56,8 @@
        {
          // Policy contradicts actual capabilities, setting unavailable.
          colorCap: {option: [{type: 'STANDARD_COLOR', is_default: true}]},
-         colorPolicy: print_preview.ColorMode.GRAY,
-         colorDefault: print_preview.ColorMode.GRAY,
+         colorPolicy: print_preview.ColorModeRestriction.MONOCHROME,
+         colorDefault: print_preview.ColorModeRestriction.MONOCHROME,
          expectedValue: true,
          expectedAvailable: false,
          expectedManaged: false,
@@ -71,9 +71,9 @@
              {type: 'STANDARD_COLOR'}
            ]
          },
-         colorPolicy: print_preview.ColorMode.COLOR,
+         colorPolicy: print_preview.ColorModeRestriction.COLOR,
          // Default mismatches restriction and is ignored.
-         colorDefault: print_preview.ColorMode.GRAY,
+         colorDefault: print_preview.ColorModeRestriction.MONOCHROME,
          expectedValue: true,
          expectedAvailable: true,
          expectedManaged: true,
@@ -87,7 +87,7 @@
              {type: 'STANDARD_COLOR'}
            ]
          },
-         colorDefault: print_preview.ColorMode.COLOR,
+         colorDefault: print_preview.ColorModeRestriction.COLOR,
          expectedValue: true,
          expectedAvailable: true,
          expectedManaged: false,
@@ -267,7 +267,7 @@
        {
          // Policy has no effect, setting is not supported.
          pinCap: {supported: false},
-         pinPolicy: print_preview.PinModeRestriction.NONE,
+         pinPolicy: print_preview.PinModeRestriction.UNSET,
          pinDefault: print_preview.PinModeRestriction.PIN,
          expectedValue: false,
          expectedAvailable: false,
@@ -286,7 +286,7 @@
        {
          // No restriction policy, setting is modifiable.
          pinCap: {supported: true},
-         pinPolicy: print_preview.PinModeRestriction.NONE,
+         pinPolicy: print_preview.PinModeRestriction.UNSET,
          pinDefault: print_preview.PinModeRestriction.NO_PIN,
          expectedValue: false,
          expectedAvailable: true,
diff --git a/chrome/test/data/webui/settings/people_page_account_manager_test.js b/chrome/test/data/webui/settings/people_page_account_manager_test.js
index 8deac9f..8626238 100644
--- a/chrome/test/data/webui/settings/people_page_account_manager_test.js
+++ b/chrome/test/data/webui/settings/people_page_account_manager_test.js
@@ -29,6 +29,7 @@
             fullName: 'Device Account',
             email: 'admin@domain.com',
             pic: 'data:image/png;base64,abc123',
+            organization: 'Family Link',
           },
           {
             id: '456',
@@ -73,6 +74,39 @@
     }
   }
 
+  /** @implements {settings.AccountManagerBrowserProxy} */
+  class TestAccountManagerBrowserProxyForUnmanagedAccounts extends
+      TestAccountManagerBrowserProxy {
+    constructor() {
+      super([
+        'getAccounts',
+        'addAccount',
+        'reauthenticateAccount',
+        'removeAccount',
+        'showWelcomeDialogIfRequired',
+      ]);
+    }
+
+    /** @override */
+    getAccounts() {
+      this.methodCalled('getAccounts');
+
+      return new Promise((resolve) => {
+        resolve([
+          {
+            id: '123',
+            accountType: 1,
+            isDeviceAccount: true,
+            isSignedIn: true,
+            fullName: 'Device Account',
+            email: 'admin@domain.com',
+            pic: 'data:image/png;base64,abc123',
+          },
+        ]);
+      });
+    }
+  }
+
   suite('AccountManagerTests', function() {
     let browserProxy = null;
     let accountManager = null;
@@ -127,8 +161,9 @@
     test('RemoveAccount', function() {
       return browserProxy.whenCalled('getAccounts').then(() => {
         Polymer.dom.flush();
-        // Click on 'More Actions' for the second account
-        accountManager.root.querySelectorAll('paper-icon-button-light')[1]
+        // Click on 'More Actions' for the second account (First one (index 0)
+        // to have the hamburger menu).
+        accountManager.root.querySelectorAll('paper-icon-button-light')[0]
             .querySelector('button')
             .click();
         // Click on 'Remove account'
@@ -151,6 +186,51 @@
       // welcome screen should be shown if required.
       assertGT(browserProxy.getCallCount('showWelcomeDialogIfRequired'), 0);
     });
+
+    test('ManagementStatusForManagedAccounts', function() {
+      return browserProxy.whenCalled('getAccounts').then(() => {
+        Polymer.dom.flush();
+
+        const managementLabel =
+            accountManager.root.querySelectorAll('.management-status')[0]
+                .innerHTML.trim();
+        assertEquals('Managed by Family Link', managementLabel);
+      });
+    });
+  });
+
+  suite('AccountManagerUnmanagedAccountTests', function() {
+    let browserProxy = null;
+    let accountManager = null;
+    let accountList = null;
+
+    setup(function() {
+      browserProxy = new TestAccountManagerBrowserProxyForUnmanagedAccounts();
+      settings.AccountManagerBrowserProxyImpl.instance_ = browserProxy;
+      PolymerTest.clearBody();
+
+      accountManager = document.createElement('settings-account-manager');
+      document.body.appendChild(accountManager);
+      accountList = accountManager.$$('#account-list');
+      assertTrue(!!accountList);
+
+      settings.navigateTo(settings.routes.ACCOUNT_MANAGER);
+    });
+
+    teardown(function() {
+      accountManager.remove();
+    });
+
+    test('ManagementStatusForUnmanagedAccounts', function() {
+      return browserProxy.whenCalled('getAccounts').then(() => {
+        Polymer.dom.flush();
+
+        const managementLabel =
+            accountManager.root.querySelectorAll('.management-status')[0]
+                .innerHTML.trim();
+        assertEquals('Primary account', managementLabel);
+      });
+    });
   });
 
   suite('AccountManagerAccountAdditionDisabledTests', function() {
diff --git a/chrome/updater/configurator.cc b/chrome/updater/configurator.cc
index d7c52de..8e372ad 100644
--- a/chrome/updater/configurator.cc
+++ b/chrome/updater/configurator.cc
@@ -17,7 +17,6 @@
 #include "components/update_client/protocol_handler.h"
 #include "components/update_client/unzipper.h"
 #include "components/version_info/version_info.h"
-#include "services/service_manager/public/cpp/connector.h"
 #include "url/gurl.h"
 
 #if defined(OS_WIN)
diff --git a/chromecast/base/cast_features.cc b/chromecast/base/cast_features.cc
index f537eb0..419abea 100644
--- a/chromecast/base/cast_features.cc
+++ b/chromecast/base/cast_features.cc
@@ -148,6 +148,9 @@
     "disable_idle_sockets_close_on_memory_pressure",
     base::FEATURE_DISABLED_BY_DEFAULT};
 
+const base::Feature kEnableGeneralAudienceBrowsing{
+    "enable_general_audience_browsing", base::FEATURE_DISABLED_BY_DEFAULT};
+
 // End Chromecast Feature definitions.
 const base::Feature* kFeatures[] = {
     &kAllowUserMediaAccess,
@@ -155,6 +158,7 @@
     &kTripleBuffer720,
     &kSingleBuffer,
     &kDisableIdleSocketsCloseOnMemoryPressure,
+    &kEnableGeneralAudienceBrowsing,
 };
 
 // An iterator for a base::DictionaryValue. Use an alias for brevity in loops.
diff --git a/chromecast/base/cast_features.h b/chromecast/base/cast_features.h
index e7b3a45..6893eb8 100644
--- a/chromecast/base/cast_features.h
+++ b/chromecast/base/cast_features.h
@@ -28,6 +28,7 @@
 extern const base::Feature kTripleBuffer720;
 extern const base::Feature kSingleBuffer;
 extern const base::Feature kDisableIdleSocketsCloseOnMemoryPressure;
+extern const base::Feature kEnableGeneralAudienceBrowsing;
 
 // Get an iterable list of all of the cast features for checking all features as
 // a collection.
diff --git a/chromecast/browser/BUILD.gn b/chromecast/browser/BUILD.gn
index dda1a65e..cf3e172 100644
--- a/chromecast/browser/BUILD.gn
+++ b/chromecast/browser/BUILD.gn
@@ -100,6 +100,10 @@
     "devtools/cast_devtools_manager_delegate.h",
     "devtools/remote_debugging_server.cc",
     "devtools/remote_debugging_server.h",
+    "general_audience_browsing_navigation_throttle.cc",
+    "general_audience_browsing_navigation_throttle.h",
+    "general_audience_browsing_service.cc",
+    "general_audience_browsing_service.h",
     "lru_renderer_cache.cc",
     "lru_renderer_cache.h",
     "media/media_caps_impl.cc",
@@ -148,6 +152,7 @@
     "//chromecast/base:cast_sys_info",
     "//chromecast/base:cast_version",
     "//chromecast/browser/bluetooth/public/mojom",
+    "//chromecast/browser/general_audience_browsing/mojom",
     "//chromecast/common",
     "//chromecast/common/media",
     "//chromecast/common/mojom",
@@ -168,8 +173,11 @@
     "//components/metrics:ui",
     "//components/network_hints/browser",
     "//components/network_session_configurator/common",
+    "//components/policy/core/browser",
     "//components/prefs",
     "//components/proxy_config",
+    "//components/safe_search_api",
+    "//components/safe_search_api:safe_search_client",
     "//components/viz/service",
     "//content/public/browser",
     "//content/public/common",
diff --git a/chromecast/browser/DEPS b/chromecast/browser/DEPS
index 5431ac4e..4b115276 100644
--- a/chromecast/browser/DEPS
+++ b/chromecast/browser/DEPS
@@ -16,9 +16,11 @@
   "+components/keyed_service",
   "+components/network_hints/browser",
   "+components/network_session_configurator/common",
+  "+components/policy/core/browser",
   "+components/prefs",
   "+components/pref_registry",
   "+components/proxy_config",
+  "+components/safe_search_api",
   "+components/services/heap_profiling/heap_profiling_service.h",
   "+components/services/heap_profiling/public/mojom",
   "+components/services/heap_profiling/public/cpp/settings.h",
diff --git a/chromecast/browser/cast_browser_main_parts.cc b/chromecast/browser/cast_browser_main_parts.cc
index 27b2d52..91bde69 100644
--- a/chromecast/browser/cast_browser_main_parts.cc
+++ b/chromecast/browser/cast_browser_main_parts.cc
@@ -593,6 +593,8 @@
   cast_browser_process_->metrics_service_client()->Initialize();
   url_request_context_factory_->InitializeNetworkDelegates();
 
+  cast_content_browser_client_->CreateGeneralAudienceBrowsingService();
+
   cast_browser_process_->cast_service()->Start();
 }
 
diff --git a/chromecast/browser/cast_content_browser_client.cc b/chromecast/browser/cast_content_browser_client.cc
index 87982208..6940a748 100644
--- a/chromecast/browser/cast_content_browser_client.cc
+++ b/chromecast/browser/cast_content_browser_client.cc
@@ -40,6 +40,8 @@
 #include "chromecast/browser/cast_session_id_map.h"
 #include "chromecast/browser/default_navigation_throttle.h"
 #include "chromecast/browser/devtools/cast_devtools_manager_delegate.h"
+#include "chromecast/browser/general_audience_browsing_navigation_throttle.h"
+#include "chromecast/browser/general_audience_browsing_service.h"
 #include "chromecast/browser/media/media_caps_impl.h"
 #include "chromecast/browser/service/cast_service_simple.h"
 #include "chromecast/browser/tts/tts_controller.h"
@@ -958,6 +960,13 @@
     }
   }
 #endif
+
+  if (chromecast::IsFeatureEnabled(kEnableGeneralAudienceBrowsing)) {
+    throttles.push_back(
+        std::make_unique<GeneralAudienceBrowsingNavigationThrottle>(
+            handle, general_audience_browsing_service_.get()));
+  }
+
   return throttles;
 }
 
@@ -1062,5 +1071,12 @@
 #endif  // !defined(OS_FUCHSIA)
 }
 
+void CastContentBrowserClient::CreateGeneralAudienceBrowsingService() {
+  DCHECK(!general_audience_browsing_service_);
+  general_audience_browsing_service_ =
+      std::make_unique<GeneralAudienceBrowsingService>(
+          cast_network_contexts_->GetSystemSharedURLLoaderFactory());
+}
+
 }  // namespace shell
 }  // namespace chromecast
diff --git a/chromecast/browser/cast_content_browser_client.h b/chromecast/browser/cast_content_browser_client.h
index de53ebe..9d0d443 100644
--- a/chromecast/browser/cast_content_browser_client.h
+++ b/chromecast/browser/cast_content_browser_client.h
@@ -50,6 +50,7 @@
 class CastService;
 class CastWindowManager;
 class CastFeatureListCreator;
+class GeneralAudienceBrowsingService;
 class MemoryPressureControllerImpl;
 
 namespace media {
@@ -227,6 +228,8 @@
     return cast_feature_list_creator_;
   }
 
+  void CreateGeneralAudienceBrowsingService();
+
 #if BUILDFLAG(USE_CHROMECAST_CDMS)
   virtual std::unique_ptr<::media::CdmFactory> CreateCdmFactory(
       service_manager::mojom::InterfaceProvider* host_interfaces);
@@ -295,6 +298,8 @@
   std::unique_ptr<CastResourceDispatcherHostDelegate>
       resource_dispatcher_host_delegate_;
   std::unique_ptr<media::CmaBackendFactory> cma_backend_factory_;
+  std::unique_ptr<GeneralAudienceBrowsingService>
+      general_audience_browsing_service_;
 
   CastFeatureListCreator* cast_feature_list_creator_;
 
diff --git a/chromecast/browser/general_audience_browsing/mojom/BUILD.gn b/chromecast/browser/general_audience_browsing/mojom/BUILD.gn
new file mode 100644
index 0000000..740525b3
--- /dev/null
+++ b/chromecast/browser/general_audience_browsing/mojom/BUILD.gn
@@ -0,0 +1,11 @@
+# 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("//mojo/public/tools/bindings/mojom.gni")
+
+mojom("mojom") {
+  sources = [
+    "general_audience_browsing.mojom",
+  ]
+}
diff --git a/chromecast/browser/general_audience_browsing/mojom/OWNERS b/chromecast/browser/general_audience_browsing/mojom/OWNERS
new file mode 100644
index 0000000..08850f4
--- /dev/null
+++ b/chromecast/browser/general_audience_browsing/mojom/OWNERS
@@ -0,0 +1,2 @@
+per-file *.mojom=set noparent
+per-file *.mojom=file://ipc/SECURITY_OWNERS
diff --git a/chromecast/browser/general_audience_browsing/mojom/general_audience_browsing.mojom b/chromecast/browser/general_audience_browsing/mojom/general_audience_browsing.mojom
new file mode 100644
index 0000000..2f28857
--- /dev/null
+++ b/chromecast/browser/general_audience_browsing/mojom/general_audience_browsing.mojom
@@ -0,0 +1,14 @@
+// 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.
+
+module chromecast.mojom;
+
+interface GeneralAudienceBrowsingAPIKeyObserver {
+  OnGeneralAudienceBrowsingAPIKeyChanged(string api_key);
+};
+
+interface GeneralAudienceBrowsingAPIKeySubject {
+  AddGeneralAudienceBrowsingAPIKeyObserver(
+      GeneralAudienceBrowsingAPIKeyObserver observer);
+};
diff --git a/chromecast/browser/general_audience_browsing_navigation_throttle.cc b/chromecast/browser/general_audience_browsing_navigation_throttle.cc
new file mode 100644
index 0000000..d291811
--- /dev/null
+++ b/chromecast/browser/general_audience_browsing_navigation_throttle.cc
@@ -0,0 +1,92 @@
+// 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 "chromecast/browser/general_audience_browsing_navigation_throttle.h"
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "chromecast/browser/general_audience_browsing_service.h"
+#include "components/policy/core/browser/url_util.h"
+#include "content/public/browser/navigation_handle.h"
+#include "url/gurl.h"
+
+namespace chromecast {
+
+GeneralAudienceBrowsingNavigationThrottle::
+    GeneralAudienceBrowsingNavigationThrottle(
+        content::NavigationHandle* navigation_handle,
+        GeneralAudienceBrowsingService* general_audience_browsing_service)
+    : NavigationThrottle(navigation_handle),
+      general_audience_browsing_service_(general_audience_browsing_service),
+      weak_ptr_factory_(this) {
+  DCHECK(general_audience_browsing_service_);
+}
+
+GeneralAudienceBrowsingNavigationThrottle::
+    ~GeneralAudienceBrowsingNavigationThrottle() = default;
+
+content::NavigationThrottle::ThrottleCheckResult
+GeneralAudienceBrowsingNavigationThrottle::CheckURL() {
+  deferred_ = false;
+  const GURL& url = navigation_handle()->GetURL();
+  DVLOG(1) << "Check URL " << url.spec();
+
+  // Only apply filters to HTTP[s] URLs.
+  if (!url.SchemeIsHTTPOrHTTPS())
+    return PROCEED;
+
+  GURL effective_url = policy::url_util::GetEmbeddedURL(url);
+  if (!effective_url.is_valid())
+    effective_url = url;
+  GURL normalized_url = policy::url_util::Normalize(effective_url);
+
+  bool synchronous = general_audience_browsing_service_->CheckURL(
+      effective_url,
+      base::BindOnce(
+          &GeneralAudienceBrowsingNavigationThrottle::CheckURLCallback,
+          weak_ptr_factory_.GetWeakPtr()));
+
+  if (!synchronous) {
+    deferred_ = true;
+    return DEFER;
+  }
+
+  if (should_cancel_) {
+    DVLOG(1) << "Unsafe URL blocked";
+    return ThrottleCheckResult(CANCEL, net::ERR_BLOCKED_BY_ADMINISTRATOR);
+  }
+  return PROCEED;
+}
+
+content::NavigationThrottle::ThrottleCheckResult
+GeneralAudienceBrowsingNavigationThrottle::WillStartRequest() {
+  return CheckURL();
+}
+
+content::NavigationThrottle::ThrottleCheckResult
+GeneralAudienceBrowsingNavigationThrottle::WillRedirectRequest() {
+  return CheckURL();
+}
+
+const char* GeneralAudienceBrowsingNavigationThrottle::GetNameForLogging() {
+  return "GeneralAudienceBrowsingNavigationThrottle";
+}
+
+void GeneralAudienceBrowsingNavigationThrottle::CheckURLCallback(bool is_safe) {
+  if (!deferred_) {
+    should_cancel_ = !is_safe;
+    return;
+  }
+
+  deferred_ = false;
+  if (is_safe) {
+    Resume();
+  } else {
+    DVLOG(1) << "Unsafe URL blocked";
+    CancelDeferredNavigation(
+        ThrottleCheckResult(CANCEL, net::ERR_BLOCKED_BY_ADMINISTRATOR));
+  }
+}
+
+}  // namespace chromecast
diff --git a/chromecast/browser/general_audience_browsing_navigation_throttle.h b/chromecast/browser/general_audience_browsing_navigation_throttle.h
new file mode 100644
index 0000000..f6c3ac8
--- /dev/null
+++ b/chromecast/browser/general_audience_browsing_navigation_throttle.h
@@ -0,0 +1,57 @@
+// 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 CHROMECAST_BROWSER_GENERAL_AUDIENCE_BROWSING_NAVIGATION_THROTTLE_H_
+#define CHROMECAST_BROWSER_GENERAL_AUDIENCE_BROWSING_NAVIGATION_THROTTLE_H_
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "content/public/browser/navigation_throttle.h"
+
+namespace content {
+class NavigationHandle;
+}  // namespace content
+
+namespace chromecast {
+
+class GeneralAudienceBrowsingService;
+
+class GeneralAudienceBrowsingNavigationThrottle
+    : public content::NavigationThrottle {
+ public:
+  GeneralAudienceBrowsingNavigationThrottle(
+      content::NavigationHandle* navigation_handle,
+      GeneralAudienceBrowsingService* general_audience_browsing_service);
+  ~GeneralAudienceBrowsingNavigationThrottle() override;
+
+  // NavigationThrottle overrides.
+  ThrottleCheckResult WillStartRequest() override;
+  ThrottleCheckResult WillRedirectRequest() override;
+
+  const char* GetNameForLogging() override;
+
+ private:
+  content::NavigationThrottle::ThrottleCheckResult CheckURL();
+
+  // Callback from GeneralAudienceBrowsingService.
+  void CheckURLCallback(bool is_safe);
+
+  GeneralAudienceBrowsingService* general_audience_browsing_service_;
+
+  // Whether the request was deferred in order to check the Safe Search API.
+  bool deferred_ = false;
+
+  // Whether the Safe Search API callback determined the in-progress navigation
+  // should be canceled.
+  bool should_cancel_ = false;
+
+  base::WeakPtrFactory<GeneralAudienceBrowsingNavigationThrottle>
+      weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(GeneralAudienceBrowsingNavigationThrottle);
+};
+
+}  // namespace chromecast
+
+#endif  // CHROMECAST_BROWSER_GENERAL_AUDIENCE_BROWSING_NAVIGATION_THROTTLE_H_
diff --git a/chromecast/browser/general_audience_browsing_service.cc b/chromecast/browser/general_audience_browsing_service.cc
new file mode 100644
index 0000000..fcbcb9f
--- /dev/null
+++ b/chromecast/browser/general_audience_browsing_service.cc
@@ -0,0 +1,113 @@
+// 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 "chromecast/browser/general_audience_browsing_service.h"
+
+#include "base/logging.h"
+#include "chromecast/common/mojom/constants.mojom.h"
+#include "components/policy/core/browser/url_util.h"
+#include "components/safe_search_api/safe_search/safe_search_url_checker_client.h"
+#include "components/safe_search_api/url_checker.h"
+#include "content/public/common/service_manager_connection.h"
+#include "net/base/net_errors.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "services/service_manager/public/cpp/connector.h"
+
+namespace chromecast {
+
+namespace {
+
+// Calls the CheckURLCallback with the result of the Safe Search API check.
+void CheckURLCallbackWrapper(
+    GeneralAudienceBrowsingService::CheckURLCallback callback,
+    const GURL& /* url */,
+    safe_search_api::Classification classification,
+    bool /* uncertain */) {
+  std::move(callback).Run(classification ==
+                          safe_search_api::Classification::SAFE);
+}
+
+net::NetworkTrafficAnnotationTag CreateNetworkTrafficAnnotationTag() {
+  return net::DefineNetworkTrafficAnnotation(
+      "cast_general_audience_browsing_throttle", R"(
+          semantics {
+            sender: "Cast Safe Search"
+            description:
+              "Checks whether a given URL (or set of URLs) is considered "
+              "safe by Google SafeSearch."
+            trigger:
+              "This is sent for every navigation."
+            data: "URL to be checked."
+            destination: GOOGLE_OWNED_SERVICE
+          }
+          policy {
+            cookies_allowed: NO
+            setting:
+              "This fearture is always enabled"
+            chrome_policy {
+              SafeSitesFilterBehavior {
+                SafeSitesFilterBehavior: 0
+              }
+            }
+          })");
+}
+
+}  // namespace
+
+GeneralAudienceBrowsingService::GeneralAudienceBrowsingService(
+    scoped_refptr<network::SharedURLLoaderFactory> shared_url_loader_factory)
+    : shared_url_loader_factory_(shared_url_loader_factory),
+      general_audience_browsing_api_key_observer_binding_(this) {
+  mojom::GeneralAudienceBrowsingAPIKeyObserverPtr observer_ptr;
+  general_audience_browsing_api_key_observer_binding_.Bind(
+      mojo::MakeRequest(&observer_ptr));
+  content::ServiceManagerConnection::GetForProcess()
+      ->GetConnector()
+      ->BindInterface(mojom::kChromecastServiceName,
+                      &general_audience_browsing_api_key_subject_ptr_);
+  general_audience_browsing_api_key_subject_ptr_
+      ->AddGeneralAudienceBrowsingAPIKeyObserver(std::move(observer_ptr));
+}
+
+GeneralAudienceBrowsingService::~GeneralAudienceBrowsingService() = default;
+
+bool GeneralAudienceBrowsingService::CheckURL(const GURL& url,
+                                              CheckURLCallback callback) {
+  if (!safe_search_url_checker_) {
+    safe_search_url_checker_ = CreateSafeSearchURLChecker();
+  }
+
+  return safe_search_url_checker_->CheckURL(
+      policy::url_util::Normalize(url),
+      base::BindOnce(&CheckURLCallbackWrapper, std::move(callback)));
+}
+
+void GeneralAudienceBrowsingService::SetSafeSearchURLCheckerForTest(
+    std::unique_ptr<safe_search_api::URLChecker> safe_search_url_checker) {
+  safe_search_url_checker_ = std::move(safe_search_url_checker);
+}
+
+void GeneralAudienceBrowsingService::OnGeneralAudienceBrowsingAPIKeyChanged(
+    const std::string& api_key) {
+  if (api_key != api_key_) {
+    api_key_ = api_key;
+    if (safe_search_url_checker_) {
+      // The URLChecker only accepts API key in constructor, no way to change it
+      // after it's been created. So we'll have to recreate one if the API key
+      // has been changed. (This should rarely happen though.)
+      safe_search_url_checker_ = CreateSafeSearchURLChecker();
+    }
+  }
+}
+
+std::unique_ptr<safe_search_api::URLChecker>
+GeneralAudienceBrowsingService::CreateSafeSearchURLChecker() {
+  return std::make_unique<safe_search_api::URLChecker>(
+      std::make_unique<safe_search_api::SafeSearchURLCheckerClient>(
+          shared_url_loader_factory_, CreateNetworkTrafficAnnotationTag(),
+          std::string(), api_key_),
+      /* cache size */ 1000);
+}
+
+}  // namespace chromecast
diff --git a/chromecast/browser/general_audience_browsing_service.h b/chromecast/browser/general_audience_browsing_service.h
new file mode 100644
index 0000000..f2f9b48
--- /dev/null
+++ b/chromecast/browser/general_audience_browsing_service.h
@@ -0,0 +1,67 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMECAST_BROWSER_GENERAL_AUDIENCE_BROWSING_SERVICE_H_
+#define CHROMECAST_BROWSER_GENERAL_AUDIENCE_BROWSING_SERVICE_H_
+
+#include <string>
+
+#include "base/macros.h"
+#include "base/memory/scoped_refptr.h"
+#include "chromecast/browser/general_audience_browsing/mojom/general_audience_browsing.mojom.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "url/gurl.h"
+
+namespace network {
+class SharedURLLoaderFactory;
+}  // namespace network
+
+namespace safe_search_api {
+class URLChecker;
+}  // namespace safe_search_api
+
+namespace chromecast {
+
+class GeneralAudienceBrowsingService
+    : public mojom::GeneralAudienceBrowsingAPIKeyObserver {
+ public:
+  using CheckURLCallback = base::OnceCallback<void(bool is_safe)>;
+
+  GeneralAudienceBrowsingService(
+      scoped_refptr<network::SharedURLLoaderFactory> shared_url_loader_factory);
+  ~GeneralAudienceBrowsingService() override;
+
+  // Starts a call to the Safe Search API for the given URL to determine whether
+  // the URL is "safe" (not porn). Returns whether |callback| was run
+  // synchronously.
+  bool CheckURL(const GURL& url, CheckURLCallback callback);
+
+  // Creates a SafeSearch URLChecker using a given URLLoaderFactory for testing.
+  void SetSafeSearchURLCheckerForTest(
+      std::unique_ptr<safe_search_api::URLChecker> safe_search_url_checker);
+
+  // mojom::GeneralAudienceBrowsingAPIKeyObserver implementation
+  void OnGeneralAudienceBrowsingAPIKeyChanged(
+      const std::string& api_key) override;
+
+ private:
+  std::unique_ptr<safe_search_api::URLChecker> CreateSafeSearchURLChecker();
+
+  std::string api_key_;
+
+  std::unique_ptr<safe_search_api::URLChecker> safe_search_url_checker_;
+
+  scoped_refptr<network::SharedURLLoaderFactory> shared_url_loader_factory_;
+
+  mojo::Binding<mojom::GeneralAudienceBrowsingAPIKeyObserver>
+      general_audience_browsing_api_key_observer_binding_;
+  mojom::GeneralAudienceBrowsingAPIKeySubjectPtr
+      general_audience_browsing_api_key_subject_ptr_;
+
+  DISALLOW_COPY_AND_ASSIGN(GeneralAudienceBrowsingService);
+};
+
+}  // namespace chromecast
+
+#endif  // CHROMECAST_BROWSER_GENERAL_AUDIENCE_BROWSING_SERVICE_H_
diff --git a/chromecast/browser/ui/aura/accessibility/automation_manager_aura.cc b/chromecast/browser/ui/aura/accessibility/automation_manager_aura.cc
index 15668f82..9da1273 100644
--- a/chromecast/browser/ui/aura/accessibility/automation_manager_aura.cc
+++ b/chromecast/browser/ui/aura/accessibility/automation_manager_aura.cc
@@ -39,7 +39,7 @@
   Reset(false);
 
   SendEvent(current_tree_->GetRoot(), ax::mojom::Event::kLoadComplete);
-  views::AXAuraObjCache::GetInstance()->SetDelegate(this);
+  cache_.SetDelegate(this);
 
   aura::Window* active_window =
       chromecast::shell::CastBrowserProcess::GetInstance()
@@ -47,8 +47,7 @@
           ->window_tree_host()
           ->window();
   if (active_window) {
-    views::AXAuraObjWrapper* focus =
-        views::AXAuraObjCache::GetInstance()->GetOrCreate(active_window);
+    views::AXAuraObjWrapper* focus = cache_.GetOrCreate(active_window);
     SendEvent(focus, ax::mojom::Event::kChildrenChanged);
   }
 
@@ -75,8 +74,7 @@
   if (!enabled_)
     return;
 
-  views::AXAuraObjWrapper* obj =
-      views::AXAuraObjCache::GetInstance()->GetOrCreate(view);
+  views::AXAuraObjWrapper* obj = cache_.GetOrCreate(view);
   if (!obj)
     return;
 
@@ -110,7 +108,7 @@
 
 void AutomationManagerAura::SendEventOnObjectById(int32_t id,
                                                   ax::mojom::Event event_type) {
-  views::AXAuraObjWrapper* obj = views::AXAuraObjCache::GetInstance()->Get(id);
+  views::AXAuraObjWrapper* obj = cache_.Get(id);
   if (obj)
     SendEvent(obj, event_type);
 }
@@ -152,9 +150,9 @@
 
 void AutomationManagerAura::Reset(bool reset_serializer) {
   if (!current_tree_) {
-    desktop_root_ = std::make_unique<AXRootObjWrapper>(this);
-    current_tree_ =
-        std::make_unique<AXTreeSourceAura>(desktop_root_.get(), ax_tree_id());
+    desktop_root_ = std::make_unique<AXRootObjWrapper>(this, &cache_);
+    current_tree_ = std::make_unique<AXTreeSourceAura>(desktop_root_.get(),
+                                                       ax_tree_id(), &cache_);
   }
   if (reset_serializer) {
     current_tree_serializer_.reset();
@@ -198,8 +196,7 @@
   tree_updates.push_back(update);
 
   // Make sure the focused node is serialized.
-  views::AXAuraObjWrapper* focus =
-      views::AXAuraObjCache::GetInstance()->GetFocus();
+  views::AXAuraObjWrapper* focus = cache_.GetFocus();
   if (focus) {
     ui::AXTreeUpdate focused_node_update;
     current_tree_serializer_->SerializeChanges(focus, &focused_node_update);
diff --git a/chromecast/browser/ui/aura/accessibility/automation_manager_aura.h b/chromecast/browser/ui/aura/accessibility/automation_manager_aura.h
index 064f3ec..f9c2cb3 100644
--- a/chromecast/browser/ui/aura/accessibility/automation_manager_aura.h
+++ b/chromecast/browser/ui/aura/accessibility/automation_manager_aura.h
@@ -117,6 +117,8 @@
 
   std::unique_ptr<views::AccessibilityAlertWindow> alert_window_;
 
+  views::AXAuraObjCache cache_;
+
   DISALLOW_COPY_AND_ASSIGN(AutomationManagerAura);
 };
 
diff --git a/chromecast/browser/ui/aura/accessibility/ax_tree_source_aura.cc b/chromecast/browser/ui/aura/accessibility/ax_tree_source_aura.cc
index 53a9713f..4df11a8 100644
--- a/chromecast/browser/ui/aura/accessibility/ax_tree_source_aura.cc
+++ b/chromecast/browser/ui/aura/accessibility/ax_tree_source_aura.cc
@@ -10,8 +10,9 @@
 #include "ui/views/accessibility/ax_aura_obj_wrapper.h"
 
 AXTreeSourceAura::AXTreeSourceAura(views::AXAuraObjWrapper* root,
-                                   const ui::AXTreeID& tree_id)
-    : AXTreeSourceViews(root, tree_id) {}
+                                   const ui::AXTreeID& tree_id,
+                                   views::AXAuraObjCache* cache)
+    : AXTreeSourceViews(root, tree_id, cache) {}
 
 AXTreeSourceAura::~AXTreeSourceAura() = default;
 
diff --git a/chromecast/browser/ui/aura/accessibility/ax_tree_source_aura.h b/chromecast/browser/ui/aura/accessibility/ax_tree_source_aura.h
index 8137889..1e78bf57 100644
--- a/chromecast/browser/ui/aura/accessibility/ax_tree_source_aura.h
+++ b/chromecast/browser/ui/aura/accessibility/ax_tree_source_aura.h
@@ -14,7 +14,9 @@
 // use with other accessibility classes.
 class AXTreeSourceAura : public views::AXTreeSourceViews {
  public:
-  AXTreeSourceAura(views::AXAuraObjWrapper* root, const ui::AXTreeID& tree_id);
+  AXTreeSourceAura(views::AXAuraObjWrapper* root,
+                   const ui::AXTreeID& tree_id,
+                   views::AXAuraObjCache* cache);
   ~AXTreeSourceAura() override;
 
   // AXTreeSource:
diff --git a/chromeos/dbus/BUILD.gn b/chromeos/dbus/BUILD.gn
index af9d255..97177fb 100644
--- a/chromeos/dbus/BUILD.gn
+++ b/chromeos/dbus/BUILD.gn
@@ -102,10 +102,6 @@
     "fake_update_engine_client.h",
     "fake_virtual_file_provider_client.cc",
     "fake_virtual_file_provider_client.h",
-    "hammerd/fake_hammerd_client.cc",
-    "hammerd/fake_hammerd_client.h",
-    "hammerd/hammerd_client.cc",
-    "hammerd/hammerd_client.h",
     "image_burner_client.cc",
     "image_burner_client.h",
     "image_loader_client.cc",
diff --git a/chromeos/dbus/OWNERS b/chromeos/dbus/OWNERS
index da7080c..99dda4f 100644
--- a/chromeos/dbus/OWNERS
+++ b/chromeos/dbus/OWNERS
@@ -2,12 +2,8 @@
 hashimoto@chromium.org
 derat@chromium.org
 
-per-file *audio*=jennyz@chromium.org
-per-file *audio*=hychao@chromium.org
-per-file *cryptohome*=dkrahn@chromium.org
 per-file *diagnosticsd*=emaxx@chromium.org
 per-file *diagnosticsd*=pmarko@chromium.org
-per-file *power*=derat@chromium.org
 per-file *smb_provider*=amistry@chromium.org
 per-file *smb_provider*=baileyberro@chromium.org
 per-file *smb_provider*=slangley@chromium.org
diff --git a/chromeos/dbus/audio/OWNERS b/chromeos/dbus/audio/OWNERS
new file mode 100644
index 0000000..e71cb32
--- /dev/null
+++ b/chromeos/dbus/audio/OWNERS
@@ -0,0 +1,2 @@
+jennyz@chromium.org
+hychao@chromium.org
diff --git a/chromeos/dbus/cryptohome/OWNERS b/chromeos/dbus/cryptohome/OWNERS
new file mode 100644
index 0000000..43256bc
--- /dev/null
+++ b/chromeos/dbus/cryptohome/OWNERS
@@ -0,0 +1,2 @@
+dkrahn@chromium.org
+hashimoto@chromium.org
diff --git a/chromeos/dbus/hammerd/BUILD.gn b/chromeos/dbus/hammerd/BUILD.gn
new file mode 100644
index 0000000..60d6249a
--- /dev/null
+++ b/chromeos/dbus/hammerd/BUILD.gn
@@ -0,0 +1,21 @@
+# Copyright 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+assert(is_chromeos, "Non-ChromeOS builds cannot depend on //chromeos")
+
+component("hammerd") {
+  defines = [ "IS_HAMMERD_IMPL" ]
+
+  deps = [
+    "//base",
+    "//dbus",
+  ]
+
+  sources = [
+    "fake_hammerd_client.cc",
+    "fake_hammerd_client.h",
+    "hammerd_client.cc",
+    "hammerd_client.h",
+  ]
+}
diff --git a/chromeos/dbus/hammerd/fake_hammerd_client.h b/chromeos/dbus/hammerd/fake_hammerd_client.h
index 32309ae..6d56217 100644
--- a/chromeos/dbus/hammerd/fake_hammerd_client.h
+++ b/chromeos/dbus/hammerd/fake_hammerd_client.h
@@ -14,7 +14,7 @@
 
 namespace chromeos {
 
-class COMPONENT_EXPORT(CHROMEOS_DBUS) FakeHammerdClient : public HammerdClient {
+class COMPONENT_EXPORT(HAMMERD) FakeHammerdClient : public HammerdClient {
  public:
   FakeHammerdClient();
   ~FakeHammerdClient() override;
diff --git a/chromeos/dbus/hammerd/hammerd_client.h b/chromeos/dbus/hammerd/hammerd_client.h
index 2ce2bb4..e1302f6c 100644
--- a/chromeos/dbus/hammerd/hammerd_client.h
+++ b/chromeos/dbus/hammerd/hammerd_client.h
@@ -12,7 +12,10 @@
 
 #include "base/component_export.h"
 #include "base/macros.h"
-#include "chromeos/dbus/dbus_client.h"
+
+namespace dbus {
+class Bus;
+}
 
 namespace chromeos {
 
@@ -23,7 +26,7 @@
 //  * the connected base pairing events.
 // The client forwards the received signals to its observers (together with any
 // data extracted from the signal object).
-class COMPONENT_EXPORT(CHROMEOS_DBUS) HammerdClient {
+class COMPONENT_EXPORT(HAMMERD) HammerdClient {
  public:
   class Observer {
    public:
diff --git a/chromeos/dbus/power/OWNERS b/chromeos/dbus/power/OWNERS
new file mode 100644
index 0000000..3c97e54
--- /dev/null
+++ b/chromeos/dbus/power/OWNERS
@@ -0,0 +1 @@
+derat@chromium.org
diff --git a/chromeos/dbus/shill/OWNERS b/chromeos/dbus/shill/OWNERS
new file mode 100644
index 0000000..137a112
--- /dev/null
+++ b/chromeos/dbus/shill/OWNERS
@@ -0,0 +1 @@
+file://chromeos/network/OWNERS
diff --git a/components/autofill/core/browser/webdata/autocomplete_sync_bridge_unittest.cc b/components/autofill/core/browser/webdata/autocomplete_sync_bridge_unittest.cc
index 98b1bbe4..27860e2 100644
--- a/components/autofill/core/browser/webdata/autocomplete_sync_bridge_unittest.cc
+++ b/components/autofill/core/browser/webdata/autocomplete_sync_bridge_unittest.cc
@@ -186,7 +186,7 @@
     for (const AutofillSpecifics& specifics : remote_data) {
       initial_updates.push_back(SpecificsToUpdateResponse(specifics));
     }
-    real_processor_->OnUpdateReceived(state, initial_updates);
+    real_processor_->OnUpdateReceived(state, std::move(initial_updates));
   }
 
   void SaveSpecificsToTable(
@@ -254,10 +254,10 @@
     return data.PassToPtr();
   }
 
-  syncer::UpdateResponseData SpecificsToUpdateResponse(
+  std::unique_ptr<syncer::UpdateResponseData> SpecificsToUpdateResponse(
       const AutofillSpecifics& specifics) {
-    syncer::UpdateResponseData data;
-    data.entity = SpecificsToEntity(specifics);
+    auto data = std::make_unique<syncer::UpdateResponseData>();
+    data->entity = SpecificsToEntity(specifics);
     return data;
   }
 
diff --git a/components/autofill/core/browser/webdata/autofill_profile_sync_bridge_unittest.cc b/components/autofill/core/browser/webdata/autofill_profile_sync_bridge_unittest.cc
index 8a29f61e..e2410d7 100644
--- a/components/autofill/core/browser/webdata/autofill_profile_sync_bridge_unittest.cc
+++ b/components/autofill/core/browser/webdata/autofill_profile_sync_bridge_unittest.cc
@@ -264,7 +264,7 @@
     for (const AutofillProfileSpecifics& specifics : remote_data) {
       initial_updates.push_back(SpecificsToUpdateResponse(specifics));
     }
-    real_processor_->OnUpdateReceived(state, initial_updates);
+    real_processor_->OnUpdateReceived(state, std::move(initial_updates));
   }
 
   void ApplySyncChanges(const EntityChangeList& changes) {
@@ -301,10 +301,10 @@
     return data.PassToPtr();
   }
 
-  syncer::UpdateResponseData SpecificsToUpdateResponse(
+  std::unique_ptr<syncer::UpdateResponseData> SpecificsToUpdateResponse(
       const AutofillProfileSpecifics& specifics) {
-    syncer::UpdateResponseData data;
-    data.entity = SpecificsToEntity(specifics);
+    auto data = std::make_unique<syncer::UpdateResponseData>();
+    data->entity = SpecificsToEntity(specifics);
     return data;
   }
 
diff --git a/components/autofill/core/browser/webdata/autofill_table_unittest.cc b/components/autofill/core/browser/webdata/autofill_table_unittest.cc
index 5ca2d3bd..dce60d15 100644
--- a/components/autofill/core/browser/webdata/autofill_table_unittest.cc
+++ b/components/autofill/core/browser/webdata/autofill_table_unittest.cc
@@ -2911,8 +2911,8 @@
   EntityMetadataMap metadata_records = metadata_batch.TakeAllMetadata();
 
   EXPECT_EQ(metadata_records.size(), 2u);
-  EXPECT_EQ(metadata_records[storage_key].sequence_number(), 1);
-  EXPECT_EQ(metadata_records[storage_key2].sequence_number(), 2);
+  EXPECT_EQ(metadata_records[storage_key]->sequence_number(), 1);
+  EXPECT_EQ(metadata_records[storage_key2]->sequence_number(), 2);
 
   // Now check that a model type state update replaces the old value
   model_type_state.set_initial_sync_done(false);
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 b99970e..237782d 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
@@ -327,7 +327,7 @@
     for (const WalletMetadataSpecifics& specifics : remote_data) {
       updates.push_back(SpecificsToUpdateResponse(specifics));
     }
-    real_processor_->OnUpdateReceived(state, updates);
+    real_processor_->OnUpdateReceived(state, std::move(updates));
   }
 
   void ReceiveTombstones(
@@ -344,7 +344,7 @@
       updates.push_back(
           SpecificsToUpdateResponse(specifics, /*is_deleted=*/true));
     }
-    real_processor_->OnUpdateReceived(state, updates);
+    real_processor_->OnUpdateReceived(state, std::move(updates));
   }
 
   EntityData SpecificsToEntity(const WalletMetadataSpecifics& specifics,
@@ -362,12 +362,12 @@
     return data;
   }
 
-  syncer::UpdateResponseData SpecificsToUpdateResponse(
+  std::unique_ptr<syncer::UpdateResponseData> SpecificsToUpdateResponse(
       const WalletMetadataSpecifics& specifics,
       bool is_deleted = false) {
-    syncer::UpdateResponseData data;
-    data.entity = SpecificsToEntity(specifics, is_deleted).PassToPtr();
-    data.response_version = response_version;
+    auto data = std::make_unique<syncer::UpdateResponseData>();
+    data->entity = SpecificsToEntity(specifics, is_deleted).PassToPtr();
+    data->response_version = response_version;
     return data;
   }
 
@@ -418,7 +418,8 @@
     AutofillTable* table = AutofillTable::FromWebDatabase(&db_);
     syncer::MetadataBatch batch;
     if (table->GetAllSyncMetadata(syncer::AUTOFILL_WALLET_METADATA, &batch)) {
-      for (std::pair<std::string, sync_pb::EntityMetadata> entry :
+      for (const std::pair<const std::string,
+                           std::unique_ptr<sync_pb::EntityMetadata>>& entry :
            batch.GetAllMetadata()) {
         storage_keys.push_back(entry.first);
       }
diff --git a/components/autofill/core/browser/webdata/autofill_wallet_sync_bridge_unittest.cc b/components/autofill/core/browser/webdata/autofill_wallet_sync_bridge_unittest.cc
index 54ed391..9950071 100644
--- a/components/autofill/core/browser/webdata/autofill_wallet_sync_bridge_unittest.cc
+++ b/components/autofill/core/browser/webdata/autofill_wallet_sync_bridge_unittest.cc
@@ -276,7 +276,7 @@
     for (const AutofillWalletSpecifics& specifics : remote_data) {
       initial_updates.push_back(SpecificsToUpdateResponse(specifics));
     }
-    real_processor_->OnUpdateReceived(state, initial_updates);
+    real_processor_->OnUpdateReceived(state, std::move(initial_updates));
   }
 
   void ExpectAddressesDiffInHistograms(int added, int removed) {
@@ -350,10 +350,10 @@
     return data;
   }
 
-  syncer::UpdateResponseData SpecificsToUpdateResponse(
+  std::unique_ptr<syncer::UpdateResponseData> SpecificsToUpdateResponse(
       const AutofillWalletSpecifics& specifics) {
-    syncer::UpdateResponseData data;
-    data.entity = SpecificsToEntity(specifics).PassToPtr();
+    auto data = std::make_unique<syncer::UpdateResponseData>();
+    data->entity = SpecificsToEntity(specifics).PassToPtr();
     return data;
   }
 
diff --git a/components/autofill/core/common/password_form.cc b/components/autofill/core/common/password_form.cc
index 9e6e5ee..fc592740 100644
--- a/components/autofill/core/common/password_form.cc
+++ b/components/autofill/core/common/password_form.cc
@@ -125,6 +125,10 @@
                           : !password_element.empty();
 }
 
+bool PasswordForm::IsFederatedCredential() const {
+  return !federation_origin.opaque();
+}
+
 bool PasswordForm::operator==(const PasswordForm& form) const {
   return scheme == form.scheme && signon_realm == form.signon_realm &&
          origin == form.origin && action == form.action &&
diff --git a/components/autofill/core/common/password_form.h b/components/autofill/core/common/password_form.h
index b863440..db531ee 100644
--- a/components/autofill/core/common/password_form.h
+++ b/components/autofill/core/common/password_form.h
@@ -337,6 +337,9 @@
   // Returns true if current password element is set.
   bool HasPasswordElement() const;
 
+  // True iff |federation_origin| isn't empty.
+  bool IsFederatedCredential() const;
+
   // Equality operators for testing.
   bool operator==(const PasswordForm& form) const;
   bool operator!=(const PasswordForm& form) const;
diff --git a/components/autofill/core/common/password_form_fill_data.cc b/components/autofill/core/common/password_form_fill_data.cc
index 42b65b4c..920903ca 100644
--- a/components/autofill/core/common/password_form_fill_data.cc
+++ b/components/autofill/core/common/password_form_fill_data.cc
@@ -12,26 +12,32 @@
 
 namespace autofill {
 
-PasswordFormFillData::PasswordFormFillData() : wait_for_username(false) {}
+namespace {
 
-PasswordFormFillData::PasswordFormFillData(const PasswordFormFillData& other) =
-    default;
+bool IsPublicSuffixMatchOrAffiliationBasedMatch(const PasswordForm& form) {
+  return form.is_public_suffix_match || form.is_affiliation_based_match;
+}
 
-PasswordFormFillData::~PasswordFormFillData() = default;
+}  // namespace
 
-void InitPasswordFormFillData(
+PasswordFormFillData::PasswordFormFillData() = default;
+
+PasswordFormFillData::PasswordFormFillData(
     const PasswordForm& form_on_page,
     const std::map<base::string16, const PasswordForm*>& matches,
-    const PasswordForm* const preferred_match,
-    bool wait_for_username_before_autofill,
-    PasswordFormFillData* result) {
+    const PasswordForm& preferred_match,
+    bool wait_for_username)
+    : form_renderer_id(form_on_page.form_data.unique_renderer_id),
+      name(form_on_page.form_data.name),
+      origin(form_on_page.origin),
+      action(form_on_page.action),
+      wait_for_username(wait_for_username),
+      has_renderer_ids(form_on_page.has_renderer_ids) {
   // Note that many of the |FormFieldData| members are not initialized for
   // |username_field| and |password_field| because they are currently not used
   // by the password autocomplete code.
-  FormFieldData username_field;
-  username_field.value = preferred_match->username_value;
-  FormFieldData password_field;
-  password_field.value = preferred_match->password_value;
+  username_field.value = preferred_match.username_value;
+  password_field.value = preferred_match.password_value;
   if (!form_on_page.only_for_fallback) {
     // Fill fields identifying information only for non-fallback case. In
     // fallback case, a fill popup is shown on clicking on each password
@@ -39,7 +45,7 @@
     username_field.name = form_on_page.username_element;
     username_field.unique_renderer_id =
         form_on_page.username_element_renderer_id;
-    result->username_may_use_prefilled_placeholder =
+    username_may_use_prefilled_placeholder =
         form_on_page.username_may_use_prefilled_placeholder;
 
     password_field.name = form_on_page.password_element;
@@ -54,33 +60,25 @@
 #endif
   }
 
-  // Fill basic form data.
-  result->form_renderer_id = form_on_page.form_data.unique_renderer_id;
-  result->name = form_on_page.form_data.name;
-  result->origin = form_on_page.origin;
-  result->action = form_on_page.action;
-  result->username_field = username_field;
-  result->password_field = password_field;
-  result->wait_for_username = wait_for_username_before_autofill;
-  result->has_renderer_ids = form_on_page.has_renderer_ids;
-
-  if (preferred_match->is_public_suffix_match ||
-      preferred_match->is_affiliation_based_match)
-    result->preferred_realm = preferred_match->signon_realm;
+  if (IsPublicSuffixMatchOrAffiliationBasedMatch(preferred_match))
+    preferred_realm = preferred_match.signon_realm;
 
   // Copy additional username/value pairs.
   for (const auto& it : matches) {
-    if (it.second != preferred_match) {
-      PasswordAndRealm value;
+    if (it.second != &preferred_match) {
+      PasswordAndRealm& value = additional_logins[it.first];
       value.password = it.second->password_value;
-      if (it.second->is_public_suffix_match ||
-          it.second->is_affiliation_based_match)
+      if (IsPublicSuffixMatchOrAffiliationBasedMatch(*it.second))
         value.realm = it.second->signon_realm;
-      result->additional_logins[it.first] = value;
     }
   }
 }
 
+PasswordFormFillData::PasswordFormFillData(const PasswordFormFillData& other) =
+    default;
+
+PasswordFormFillData::~PasswordFormFillData() = default;
+
 PasswordFormFillData MaybeClearPasswordValues(
     const PasswordFormFillData& data) {
   // In case when there is a username on a page (for example in a hidden field),
diff --git a/components/autofill/core/common/password_form_fill_data.h b/components/autofill/core/common/password_form_fill_data.h
index cbb47f89..dfe6586f 100644
--- a/components/autofill/core/common/password_form_fill_data.h
+++ b/components/autofill/core/common/password_form_fill_data.h
@@ -21,7 +21,26 @@
 // struct are only set when the password's realm differs from the realm of the
 // form that we are filling.
 struct PasswordFormFillData {
-  typedef std::map<base::string16, PasswordAndRealm> LoginCollection;
+  using LoginCollection = std::map<base::string16, PasswordAndRealm>;
+
+  PasswordFormFillData();
+
+  // Create a FillData structure in preparation for autofilling a form, from
+  // basic_data identifying which form to fill, and a collection of matching
+  // stored logins to use as username/password values. |preferred_match| should
+  // equal (address) one of matches. |wait_for_username| is true if we should
+  // not autofill anything until the user typed in a valid username and blurred
+  // the field. If |enable_possible_usernames| is true, we will populate
+  // possible_usernames.
+  PasswordFormFillData(
+      const PasswordForm& form_on_page,
+      const std::map<base::string16, const PasswordForm*>& matches,
+      const PasswordForm& preferred_match,
+      bool wait_for_username);
+
+  PasswordFormFillData(const PasswordFormFillData& other);
+
+  ~PasswordFormFillData();
 
   // If |has_renderer_ids| == true then |form_renderer_id| contains the unique
   // renderer form id. No special values for |has_renderer_ids| == false case
@@ -32,7 +51,7 @@
   // Username and Password elements renderer ids are in
   // |username_field.unique_renderer_id| and |password_field.unique_renderer_id|
   // correspondingly.
-  uint32_t form_renderer_id;
+  uint32_t form_renderer_id = FormData::kNotSetFormRendererId;
 
   // The name of the form.
   base::string16 name;
@@ -64,33 +83,15 @@
   // PasswordManager determined there is an additional risk associated with this
   // form. This can happen, for example, if action URI's of the observed form
   // and our saved representation don't match up.
-  bool wait_for_username;
+  bool wait_for_username = false;
 
   // True if renderer ids for form, username and password fields are present.
   // TODO(https://crbug.com/831123): Remove this field when old parsing is
   // removed and filling by renderer ids is by default.
   bool has_renderer_ids = false;
 
-  PasswordFormFillData();
-  PasswordFormFillData(const PasswordFormFillData& other);
-  ~PasswordFormFillData();
 };
 
-// Create a FillData structure in preparation for autofilling a form,
-// from basic_data identifying which form to fill, and a collection of
-// matching stored logins to use as username/password values.
-// |preferred_match| should equal (address) one of matches.
-// |wait_for_username_before_autofill| is true if we should not autofill
-// anything until the user typed in a valid username and blurred the field.
-// If |enable_possible_usernames| is true, we will populate possible_usernames
-// in |result|.
-void InitPasswordFormFillData(
-    const PasswordForm& form_on_page,
-    const std::map<base::string16, const PasswordForm*>& matches,
-    const PasswordForm* const preferred_match,
-    bool wait_for_username_before_autofill,
-    PasswordFormFillData* result);
-
 // If |data.wait_for_username| is set, the renderer does not need to receive
 // passwords, yet, and this function clears the password values from |data|.
 PasswordFormFillData MaybeClearPasswordValues(const PasswordFormFillData& data);
diff --git a/components/autofill/core/common/password_form_fill_data_unittest.cc b/components/autofill/core/common/password_form_fill_data_unittest.cc
index ffe37b1..03eaf74 100644
--- a/components/autofill/core/common/password_form_fill_data_unittest.cc
+++ b/components/autofill/core/common/password_form_fill_data_unittest.cc
@@ -49,36 +49,26 @@
 
   std::map<base::string16, const PasswordForm*> matches;
 
-  PasswordFormFillData result;
-  InitPasswordFormFillData(form_on_page,
-                           matches,
-                           &preferred_match,
-                           true,
-                           &result);
+  PasswordFormFillData result(form_on_page, matches, preferred_match, true);
 
-  // |wait_for_username| should reflect the |wait_for_username_before_autofill|
-  // argument of InitPasswordFormFillData which in this case is true.
+  // |wait_for_username| should reflect the |wait_for_username| argument passed
+  // to the constructor, which in this case is true.
   EXPECT_TRUE(result.wait_for_username);
   // The preferred realm should be empty since it's the same as the realm of
   // the form.
   EXPECT_EQ(std::string(), result.preferred_realm);
 
-  PasswordFormFillData result2;
-  InitPasswordFormFillData(form_on_page,
-                           matches,
-                           &preferred_match,
-                           false,
-                           &result2);
+  PasswordFormFillData result2(form_on_page, matches, preferred_match, false);
 
-  // |wait_for_username| should reflect the |wait_for_username_before_autofill|
-  // argument of InitPasswordFormFillData which in this case is false.
+  // |wait_for_username| should reflect the |wait_for_username| argument passed
+  // to the constructor, which in this case is false.
   EXPECT_FALSE(result2.wait_for_username);
 }
 
-// Tests that the InitPasswordFormFillData behaves correctly when there is a
-// preferred match that was found using public suffix matching, an additional
-// result that also used public suffix matching, and a third result that was
-// found without using public suffix matching.
+// Tests that constructing a PasswordFormFillData behaves correctly when there
+// is a preferred match that was found using public suffix matching, an
+// additional result that also used public suffix matching, and a third result
+// that was found without using public suffix matching.
 TEST(PasswordFormFillDataTest, TestPublicSuffixDomainMatching) {
   // Create the current form on the page.
   PasswordForm form_on_page;
@@ -142,12 +132,7 @@
   matches.insert(
       std::make_pair(public_suffix_match.username_value, &public_suffix_match));
 
-  PasswordFormFillData result;
-  InitPasswordFormFillData(form_on_page,
-                           matches,
-                           &preferred_match,
-                           true,
-                           &result);
+  PasswordFormFillData result(form_on_page, matches, preferred_match, true);
   EXPECT_TRUE(result.wait_for_username);
   // The preferred realm should match the signon realm from the
   // preferred match so the user can see where the result came from.
@@ -164,9 +149,9 @@
   EXPECT_EQ(iter->second.realm, public_suffix_match.signon_realm);
 }
 
-// Tests that the InitPasswordFormFillData behaves correctly when there is a
-// preferred match that was found using affiliation based matching, an
-// additional result that also used affiliation based matching, and a third
+// Tests that the constructing a PasswordFormFillData behaves correctly when
+// there is a preferred match that was found using affiliation based matching,
+// an additional result that also used affiliation based matching, and a third
 // result that was found without using affiliation based matching.
 TEST(PasswordFormFillDataTest, TestAffiliationMatch) {
   // Create the current form on the page.
@@ -222,9 +207,7 @@
   matches.insert(
       std::make_pair(affiliated_match.username_value, &affiliated_match));
 
-  PasswordFormFillData result;
-  InitPasswordFormFillData(form_on_page, matches, &preferred_match, false,
-                           &result);
+  PasswordFormFillData result(form_on_page, matches, preferred_match, false);
   EXPECT_FALSE(result.wait_for_username);
   // The preferred realm should match the signon realm from the
   // preferred match so the user can see where the result came from.
@@ -268,9 +251,7 @@
 
   std::map<base::string16, const PasswordForm*> matches;
 
-  PasswordFormFillData result;
-  InitPasswordFormFillData(form_on_page, matches, &preferred_match, true,
-                           &result);
+  PasswordFormFillData result(form_on_page, matches, preferred_match, true);
 
   EXPECT_EQ(form_data.unique_renderer_id, result.form_renderer_id);
   EXPECT_EQ(form_on_page.has_renderer_ids, result.has_renderer_ids);
diff --git a/components/autofill_assistant/browser/actions/get_payment_information_action.cc b/components/autofill_assistant/browser/actions/get_payment_information_action.cc
index dff48e8..a8b6adb 100644
--- a/components/autofill_assistant/browser/actions/get_payment_information_action.cc
+++ b/components/autofill_assistant/browser/actions/get_payment_information_action.cc
@@ -50,6 +50,9 @@
 
   payment_options->request_shipping =
       !get_payment_information.shipping_address_name().empty();
+  payment_options->request_payment_method =
+      get_payment_information.ask_for_payment();
+
   payment_options->callback =
       base::BindOnce(&GetPaymentInformationAction::OnGetPaymentInformation,
                      weak_ptr_factory_.GetWeakPtr(), delegate,
diff --git a/components/autofill_assistant/browser/client_status.cc b/components/autofill_assistant/browser/client_status.cc
index ed08ec7..af5602c 100644
--- a/components/autofill_assistant/browser/client_status.cc
+++ b/components/autofill_assistant/browser/client_status.cc
@@ -112,6 +112,10 @@
       out << "UNEXPECTED_JS_ERROR";
       break;
 
+    case ProcessedActionStatusProto::TOO_MANY_ELEMENTS:
+      out << "TOO_MANY_ELEMENTS";
+      break;
+
       // Intentionally no default case to make compilation fail if a new value
       // was added to the enum but not to this list.
   }
diff --git a/components/autofill_assistant/browser/controller.cc b/components/autofill_assistant/browser/controller.cc
index 847767c..5a978d9 100644
--- a/components/autofill_assistant/browser/controller.cc
+++ b/components/autofill_assistant/browser/controller.cc
@@ -614,13 +614,15 @@
   base::Value dict(base::Value::Type::DICTIONARY);
 
   dict.SetKey("status", base::Value(status_message_));
-  std::vector<base::Value> parameters_js;
-  for (const auto& entry : trigger_context_->script_parameters) {
-    base::Value parameter_js = base::Value(base::Value::Type::DICTIONARY);
-    parameter_js.SetKey(entry.first, base::Value(entry.second));
-    parameters_js.push_back(std::move(parameter_js));
+  if (trigger_context_) {
+    std::vector<base::Value> parameters_js;
+    for (const auto& entry : trigger_context_->script_parameters) {
+      base::Value parameter_js = base::Value(base::Value::Type::DICTIONARY);
+      parameter_js.SetKey(entry.first, base::Value(entry.second));
+      parameters_js.push_back(std::move(parameter_js));
+    }
+    dict.SetKey("parameters", base::Value(parameters_js));
   }
-  dict.SetKey("parameters", base::Value(parameters_js));
   dict.SetKey("scripts", script_tracker()->GetDebugContext());
 
   if (details_)
diff --git a/components/autofill_assistant/browser/payment_request.h b/components/autofill_assistant/browser/payment_request.h
index db9faa4ed..feed28a 100644
--- a/components/autofill_assistant/browser/payment_request.h
+++ b/components/autofill_assistant/browser/payment_request.h
@@ -51,6 +51,7 @@
   bool request_payer_email = false;
   bool request_payer_phone = false;
   bool request_shipping = false;
+  bool request_payment_method = false;
   std::vector<std::string> supported_basic_card_networks;
   std::string default_email;
 
diff --git a/components/autofill_assistant/browser/service.proto b/components/autofill_assistant/browser/service.proto
index 83d04a97..9dabbd5 100644
--- a/components/autofill_assistant/browser/service.proto
+++ b/components/autofill_assistant/browser/service.proto
@@ -478,6 +478,11 @@
   //
   // ProcessedActionProto.UnexpectedErrorInfoProto contains more details.
   UNEXPECTED_JS_ERROR = 17;
+
+  // There were more than one matching element in the DOM in a context where
+  // only one is supported. This is generally a scripting error: the selector is
+  // not specific enough.
+  TOO_MANY_ELEMENTS = 18;
 }
 
 // The pseudo type values come from
diff --git a/components/autofill_assistant/browser/web_controller.cc b/components/autofill_assistant/browser/web_controller.cc
index f20400e..93a8be3 100644
--- a/components/autofill_assistant/browser/web_controller.cc
+++ b/components/autofill_assistant/browser/web_controller.cc
@@ -145,18 +145,22 @@
 
 // Javascript code to query an elements for a selector, either the first
 // (non-strict) or a single (strict) element.
+//
+// Returns undefined if no elements are found, TOO_MANY_ELEMENTS (18) if too
+// many elements were found and strict mode is enabled.
 const char* const kQuerySelector =
     R"(function (selector, strictMode) {
       var found = this.querySelectorAll(selector);
-      if(found.length == 1)
-        return found[0];
-      if(found.length > 1 && !strictMode)
-        return found[0];
-      return undefined;
+      if(found.length == 0) return undefined;
+      if(found.length > 1 && strictMode) return 18;
+      return found[0];
     })";
 
 // Javascript code to query a visible elements for a selector, either the first
 // (non-strict) or a single (strict) visible element.q
+//
+// Returns undefined if no elements are found, TOO_MANY_ELEMENTS (18) if too
+// many elements were found and strict mode is enabled.
 const char* const kQuerySelectorWithConditions =
     R"(function (selector, strict, visible, inner_text_re) {
         var found = this.querySelectorAll(selector);
@@ -169,7 +173,7 @@
         };
         for (let i = 0; i < found.length; i++) {
           if (match(found[i])) {
-            if (found_index != -1) return undefined;
+            if (found_index != -1) return 18;
             found_index = i;
             if (!strict) break;
           }
@@ -522,6 +526,10 @@
 void WebController::ElementFinder::Start(FindElementCallback callback) {
   callback_ = std::move(callback);
 
+  if (selector_.empty()) {
+    SendResult(ClientStatus(INVALID_SELECTOR));
+    return;
+  }
   devtools_client_->GetRuntime()->Evaluate(
       std::string(kGetDocumentElement),
       base::BindOnce(&WebController::ElementFinder::OnGetDocumentElement,
@@ -596,13 +604,21 @@
 void WebController::ElementFinder::OnQuerySelectorAll(
     size_t index,
     std::unique_ptr<runtime::CallFunctionOnResult> result) {
-  if (!result || result->HasExceptionDetails()) {
+  if (!result || result->HasExceptionDetails() || !result->GetResult()) {
     DVLOG(1) << __func__ << "Failed to query selector " << index << " of "
              << selector_;
-    SendResult(ClientStatus(OTHER_ACTION_STATUS));
+    SendResult(JavaScriptErrorStatus(__FILE__, __LINE__,
+                                     result && result->HasExceptionDetails()
+                                         ? result->GetExceptionDetails()
+                                         : nullptr));
     return;
   }
-  if (!result || !result->GetResult() || !result->GetResult()->HasObjectId()) {
+  if (result->GetResult()->HasValue() &&
+      result->GetResult()->GetValue()->GetInt() == TOO_MANY_ELEMENTS) {
+    SendResult(ClientStatus(TOO_MANY_ELEMENTS));
+    return;
+  }
+  if (!result->GetResult()->HasObjectId()) {
     SendResult(ClientStatus(ELEMENT_RESOLUTION_FAILED));
     return;
   }
diff --git a/components/autofill_assistant/browser/web_controller_browsertest.cc b/components/autofill_assistant/browser/web_controller_browsertest.cc
index f52d0ab..5b46b24 100644
--- a/components/autofill_assistant/browser/web_controller_browsertest.cc
+++ b/components/autofill_assistant/browser/web_controller_browsertest.cc
@@ -721,6 +721,24 @@
       Selector({"#iframe", "#iframe", "#hidden"}).MustBeVisible());
 }
 
+IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest, FindElementErrorStatus) {
+  ClientStatus status;
+
+  FindElement(
+      Selector(ElementReferenceProto::default_instance()).MustBeVisible(),
+      &status, nullptr);
+  EXPECT_EQ(INVALID_SELECTOR, status.proto_status());
+
+  FindElement(Selector({"#doesnotexist"}).MustBeVisible(), &status, nullptr);
+  EXPECT_EQ(ELEMENT_RESOLUTION_FAILED, status.proto_status());
+
+  FindElement(Selector({"div"}), &status, nullptr);
+  EXPECT_EQ(TOO_MANY_ELEMENTS, status.proto_status());
+
+  FindElement(Selector({"div"}).MustBeVisible(), &status, nullptr);
+  EXPECT_EQ(TOO_MANY_ELEMENTS, status.proto_status());
+}
+
 IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest, FocusElement) {
   Selector selector;
   selector.selectors.emplace_back("#iframe");
diff --git a/components/browser_sync/BUILD.gn b/components/browser_sync/BUILD.gn
index db0906a9..5e5ebdc 100644
--- a/components/browser_sync/BUILD.gn
+++ b/components/browser_sync/BUILD.gn
@@ -29,6 +29,7 @@
     "//components/prefs",
     "//components/reading_list/features:flags",
     "//components/send_tab_to_self",
+    "//components/sync:user_events",
     "//components/sync_bookmarks",
     "//components/sync_sessions",
     "//components/version_info",
diff --git a/components/data_reduction_proxy/core/common/data_reduction_proxy_features.cc b/components/data_reduction_proxy/core/common/data_reduction_proxy_features.cc
index 87d7321..7c20617e 100644
--- a/components/data_reduction_proxy/core/common/data_reduction_proxy_features.cc
+++ b/components/data_reduction_proxy/core/common/data_reduction_proxy_features.cc
@@ -36,5 +36,12 @@
     "DataReductionProxyEnabledWithNetworkService",
     base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Enables block action of all proxies when 502 is received with no
+// Chrome-Proxy header. The block duration is configurable via field trial with
+// a default of one second.
+const base::Feature kDataReductionProxyBlockOnBadGatewayResponse{
+    "DataReductionProxyBlockOnBadGatewayResponse",
+    base::FEATURE_DISABLED_BY_DEFAULT};
+
 }  // namespace features
 }  // namespace data_reduction_proxy
diff --git a/components/data_reduction_proxy/core/common/data_reduction_proxy_features.h b/components/data_reduction_proxy/core/common/data_reduction_proxy_features.h
index 3212c6f2..6192e135 100644
--- a/components/data_reduction_proxy/core/common/data_reduction_proxy_features.h
+++ b/components/data_reduction_proxy/core/common/data_reduction_proxy_features.h
@@ -15,6 +15,7 @@
 extern const base::Feature kDogfood;
 extern const base::Feature kDataReductionProxyEnabledWithNetworkService;
 extern const base::Feature kDataSaverUseOnDeviceSafeBrowsing;
+extern const base::Feature kDataReductionProxyBlockOnBadGatewayResponse;
 
 }  // namespace features
 }  // namespace data_reduction_proxy
diff --git a/components/data_reduction_proxy/core/common/data_reduction_proxy_headers.cc b/components/data_reduction_proxy/core/common/data_reduction_proxy_headers.cc
index 6fa327d5..0a9b7ad 100644
--- a/components/data_reduction_proxy/core/common/data_reduction_proxy_headers.cc
+++ b/components/data_reduction_proxy/core/common/data_reduction_proxy_headers.cc
@@ -418,8 +418,22 @@
   // Fall back if a 500, 502 or 503 is returned.
   if (headers.response_code() == net::HTTP_INTERNAL_SERVER_ERROR)
     return BYPASS_EVENT_TYPE_STATUS_500_HTTP_INTERNAL_SERVER_ERROR;
-  if (headers.response_code() == net::HTTP_BAD_GATEWAY)
+  if (headers.response_code() == net::HTTP_BAD_GATEWAY) {
+    if (base::FeatureList::IsEnabled(
+            features::kDataReductionProxyBlockOnBadGatewayResponse)) {
+      // When 502 response is received with no valid directive in Chrome-Proxy
+      // header, it is likely the renderer may have been blocked in receiving
+      // the headers due to CORB, etc. In this case, block-once or a block all
+      // proxies for a small number of seconds can be an alternative.
+      data_reduction_proxy_info->bypass_all = true;
+      data_reduction_proxy_info->bypass_duration =
+          base::TimeDelta::FromSeconds(base::GetFieldTrialParamByFeatureAsInt(
+              features::kDataReductionProxyBlockOnBadGatewayResponse,
+              "block_duration_seconds", 1));
+      return BYPASS_EVENT_TYPE_SHORT;
+    }
     return BYPASS_EVENT_TYPE_STATUS_502_HTTP_BAD_GATEWAY;
+  }
   if (headers.response_code() == net::HTTP_SERVICE_UNAVAILABLE)
     return BYPASS_EVENT_TYPE_STATUS_503_HTTP_SERVICE_UNAVAILABLE;
   // TODO(kundaji): Bypass if Proxy-Authenticate header value cannot be
diff --git a/components/history/core/browser/sync/typed_url_sync_metadata_database_unittest.cc b/components/history/core/browser/sync/typed_url_sync_metadata_database_unittest.cc
index 1274f8f..5f92eac 100644
--- a/components/history/core/browser/sync/typed_url_sync_metadata_database_unittest.cc
+++ b/components/history/core/browser/sync/typed_url_sync_metadata_database_unittest.cc
@@ -95,8 +95,8 @@
   EntityMetadataMap metadata_records = metadata_batch.TakeAllMetadata();
 
   EXPECT_EQ(metadata_records.size(), 2u);
-  EXPECT_EQ(metadata_records[storage_key].sequence_number(), 1);
-  EXPECT_EQ(metadata_records[storage_key2].sequence_number(), 2);
+  EXPECT_EQ(metadata_records[storage_key]->sequence_number(), 1);
+  EXPECT_EQ(metadata_records[storage_key2]->sequence_number(), 2);
 
   // Now check that a model type state update replaces the old value
   model_type_state.set_initial_sync_done(false);
diff --git a/components/management_strings.grdp b/components/management_strings.grdp
index 6bc6afe..6e0bbea 100644
--- a/components/management_strings.grdp
+++ b/components/management_strings.grdp
@@ -106,6 +106,13 @@
   </message>
 
   <if expr="chromeos">
+    <!-- Strings related to Local trust roots section -->
+    <message name="IDS_MANAGEMENT_LOCAL_TRUST_ROOTS" desc="Title of the types of local trust roots section of the page">
+      Custom root certificates
+    </message>
+    <message name="IDS_MANAGEMENT_TRUST_ROOTS_CONFIGURED" desc="Message describing that the administrators have installed their own certificates">
+      Administrators of this device have set up security certificates that may allow them to see the content of websites you visit.
+    </message>
     <!-- Strings related to device reporting section of the management page -->
     <message name="IDS_MANAGEMENT_DEVICE_REPORTING" desc="Title of the types of device reporting section of the page">
       Device
@@ -149,13 +156,6 @@
     Permissions
   </message>
 
-  <message name="IDS_MANAGEMENT_LOCAL_TRUST_ROOTS" desc="Title of the types of local trust roots section of the page">
-    Custom root certificates
-  </message>
-  <message name="IDS_MANAGEMENT_TRUST_ROOTS_NOT_CONFIGURED" desc="Message describing that the administrators have not installed certificates">
-    The contents of websites that you visit is not seen by your administrators
-  </message>
-
   <message name="IDS_MANAGEMENT_BROWSER_REPORTING" desc="Title of the types of browser reporting section of the page">
     Browser
   </message>
@@ -186,10 +186,4 @@
   <message name="IDS_MANAGEMENT_EXTENSION_REPORT_PERF_CRASH" desc="Message explaining that an extension currently reports the user's  performance data and crash report">
     Performance data and crash reports
   </message>
-
-  <if expr="chromeos">
-    <message name="IDS_MANAGEMENT_TRUST_ROOTS_CONFIGURED" desc="Message describing that the administrators have installed their own certificates">
-      Your administrator has configured custom root certificates, which may allow the administrator to see the contents of websites that you visit.
-    </message>
-  </if>
 </grit-part>
diff --git a/components/password_manager/content/browser/content_password_manager_driver_unittest.cc b/components/password_manager/content/browser/content_password_manager_driver_unittest.cc
index 21c0629..189aa69 100644
--- a/components/password_manager/content/browser/content_password_manager_driver_unittest.cc
+++ b/components/password_manager/content/browser/content_password_manager_driver_unittest.cc
@@ -125,10 +125,7 @@
   non_preferred_match.password_value = ASCIIToUTF16("test1");
   matches[non_preferred_match.username_value] = &non_preferred_match;
 
-  PasswordFormFillData result;
-  InitPasswordFormFillData(form_on_page, matches, &preferred_match, true,
-                           &result);
-  return result;
+  return PasswordFormFillData(form_on_page, matches, preferred_match, true);
 }
 
 MATCHER(WerePasswordsCleared, "Passwords not cleared") {
diff --git a/components/password_manager/core/browser/form_saver.h b/components/password_manager/core/browser/form_saver.h
index 0214149..ae93cfd 100644
--- a/components/password_manager/core/browser/form_saver.h
+++ b/components/password_manager/core/browser/form_saver.h
@@ -27,17 +27,23 @@
   // Configures the |observed| form to become a blacklist entry and saves it.
   virtual void PermanentlyBlacklist(autofill::PasswordForm* observed) = 0;
 
-  // Saves the |pending| form and updates the stored preference on
-  // |best_matches|.
-  virtual void Save(
-      const autofill::PasswordForm& pending,
-      const std::map<base::string16, const autofill::PasswordForm*>&
-          best_matches) = 0;
+  // Saves the |pending| form.
+  // |matches| are relevant credentials for the site. After saving |pending|,
+  // the following clean up steps are performed on the credentials stored on
+  // disk that correspond to |matches|:
+  // - the |preferred| state is reset to false.
+  // - empty-username credentials with the same password are removed.
+  // - if |old_password| is provided, the old credentials with the same username
+  //   and the old password are updated to the new password.
+  virtual void Save(const autofill::PasswordForm& pending,
+                    const std::vector<const autofill::PasswordForm*>& matches,
+                    const base::string16& old_password) = 0;
 
   // Updates the |pending| form and updates the stored preference on
   // |best_matches|. If |old_primary_key| is given, uses it for saving
   // |pending|. It also updates the password store with all
   // |credentials_to_update|.
+  // TODO(crbug/831123): it's a convoluted interface, switch to the one for Save
   virtual void Update(
       const autofill::PasswordForm& pending,
       const std::map<base::string16, const autofill::PasswordForm*>&
diff --git a/components/password_manager/core/browser/form_saver_impl.cc b/components/password_manager/core/browser/form_saver_impl.cc
index 106fb07..3e7d231 100644
--- a/components/password_manager/core/browser/form_saver_impl.cc
+++ b/components/password_manager/core/browser/form_saver_impl.cc
@@ -60,10 +60,50 @@
   store_->AddLogin(*observed);
 }
 
-void FormSaverImpl::Save(
-    const PasswordForm& pending,
-    const std::map<base::string16, const PasswordForm*>& best_matches) {
-  SaveImpl(pending, true, best_matches, nullptr, nullptr);
+void FormSaverImpl::Save(const PasswordForm& pending,
+                         const std::vector<const PasswordForm*>& matches,
+                         const base::string16& old_password) {
+  DCHECK(pending.preferred);
+  DCHECK(!pending.blacklisted_by_user);
+  // TODO(crbug/936011): move all the generation stuff out of here.
+  if (presaved_)
+    return SaveImpl(pending, false, {}, nullptr, nullptr);
+
+  PasswordForm sanitized_pending(pending);
+  SanitizeFormData(&sanitized_pending.form_data);
+  store_->AddLogin(sanitized_pending);
+  // Update existing matches in the password store.
+  for (const auto* match : matches) {
+    if (match->IsFederatedCredential())
+      continue;
+    // Delete obsolete empty username credentials.
+    const bool same_password = match->password_value == pending.password_value;
+    const bool username_was_added =
+        match->username_value.empty() && !pending.username_value.empty();
+    if (same_password && username_was_added && !match->is_public_suffix_match) {
+      store_->RemoveLogin(*match);
+      continue;
+    }
+    base::Optional<PasswordForm> form_to_update;
+    const bool same_username = match->username_value == pending.username_value;
+    if (same_username) {
+      // Maybe update the password value.
+      const bool form_has_old_password = match->password_value == old_password;
+      if (form_has_old_password) {
+        form_to_update = *match;
+        form_to_update->password_value = pending.password_value;
+      }
+    } else if (match->preferred && !match->is_public_suffix_match) {
+      // No other credential on the same security origin can be preferred but
+      // the most recent one.
+      form_to_update = *match;
+      form_to_update->preferred = false;
+    }
+    if (form_to_update) {
+      SanitizeFormData(&form_to_update->form_data);
+      store_->UpdateLogin(std::move(*form_to_update));
+    }
+  }
 }
 
 void FormSaverImpl::Update(
diff --git a/components/password_manager/core/browser/form_saver_impl.h b/components/password_manager/core/browser/form_saver_impl.h
index 018d885..0050b350 100644
--- a/components/password_manager/core/browser/form_saver_impl.h
+++ b/components/password_manager/core/browser/form_saver_impl.h
@@ -26,8 +26,8 @@
   // FormSaver:
   void PermanentlyBlacklist(autofill::PasswordForm* observed) override;
   void Save(const autofill::PasswordForm& pending,
-            const std::map<base::string16, const autofill::PasswordForm*>&
-                best_matches) override;
+            const std::vector<const autofill::PasswordForm*>& matches,
+            const base::string16& old_password) override;
   void Update(const autofill::PasswordForm& pending,
               const std::map<base::string16, const autofill::PasswordForm*>&
                   best_matches,
diff --git a/components/password_manager/core/browser/form_saver_impl_unittest.cc b/components/password_manager/core/browser/form_saver_impl_unittest.cc
index e9c3dd1..0403122d 100644
--- a/components/password_manager/core/browser/form_saver_impl_unittest.cc
+++ b/components/password_manager/core/browser/form_saver_impl_unittest.cc
@@ -103,7 +103,7 @@
   EXPECT_CALL(*mock_store_, AddLogin(_)).WillOnce(SaveArg<0>(&saved));
   EXPECT_CALL(*mock_store_, UpdateLogin(_)).Times(0);
   EXPECT_CALL(*mock_store_, UpdateLoginWithPrimaryKey(_, _)).Times(0);
-  form_saver_.Save(pending, std::map<base::string16, const PasswordForm*>());
+  form_saver_.Save(pending, {} /* matches */, base::string16());
   EXPECT_EQ(ASCIIToUTF16("nameofuser"), saved.username_value);
   EXPECT_EQ(ASCIIToUTF16("wordToP4a55"), saved.password_value);
 }
@@ -182,18 +182,21 @@
   PasswordForm pending = CreatePending("nameofuser", "wordToP4a55");
   pending.preferred = true;
 
-  // |best_matches| will contain two forms: one non-PSL matched with a username
-  // different from the pending one, and one PSL-matched with a username same
-  // as the pending one, both marked as "preferred". FormSaver should ignore
-  // the pending and PSL-matched one, but should update the non-PSL matched
-  // form (with different username) to no longer be preferred.
-  std::map<base::string16, const PasswordForm*> best_matches;
+  // |best_matches| will contain 3 forms
+  // - non-PSL matched with a different username.
+  // - PSL-matched with the same username
+  // - another non-PSL that is not preferred.
+  // FormSaver should ignore the pending and PSL-matched one, but should update
+  // the non-PSL matched form (with different username) to no longer be
+  // preferred.
   PasswordForm other = pending;
   other.username_value = ASCIIToUTF16("othername");
-  best_matches[other.username_value] = &other;
+  PasswordForm other_non_preferred = CreatePending("other", "wordToP4a55");
+  other_non_preferred.preferred = false;
   PasswordForm psl_match = pending;
   psl_match.is_public_suffix_match = true;
-  best_matches[psl_match.username_value] = &psl_match;
+  const std::vector<const PasswordForm*> matches = {&other, &psl_match,
+                                                    &other_non_preferred};
 
   PasswordForm saved;
   PasswordForm updated;
@@ -201,7 +204,7 @@
   EXPECT_CALL(*mock_store_, AddLogin(_)).WillOnce(SaveArg<0>(&saved));
   EXPECT_CALL(*mock_store_, UpdateLogin(_)).WillOnce(SaveArg<0>(&updated));
   EXPECT_CALL(*mock_store_, UpdateLoginWithPrimaryKey(_, _)).Times(0);
-  form_saver_.Save(pending, best_matches);
+  form_saver_.Save(pending, matches, base::string16());
   EXPECT_EQ(ASCIIToUTF16("nameofuser"), saved.username_value);
   EXPECT_EQ(ASCIIToUTF16("wordToP4a55"), saved.password_value);
   EXPECT_TRUE(saved.preferred);
@@ -213,30 +216,26 @@
 }
 
 // Check that storing credentials with a non-empty username results in deleting
-// credentials with the same password but no username, if present in best
+// credentials with the same password but empty username, if present in best
 // matches.
 TEST_F(FormSaverImplTest, Save_AndDeleteEmptyUsernameCredentials) {
   PasswordForm pending = CreatePending("nameofuser", "wordToP4a55");
 
-  std::map<base::string16, const PasswordForm*> best_matches;
-  best_matches[pending.username_value] = &pending;
+  PasswordForm non_empty_username = pending;
+  non_empty_username.username_value = ASCIIToUTF16("othername");
+  non_empty_username.preferred = false;
+
   PasswordForm no_username = pending;
   no_username.username_value.clear();
   no_username.preferred = false;
-  best_matches[no_username.username_value] = &no_username;
+  const std::vector<const PasswordForm*> matches = {&non_empty_username,
+                                                    &no_username};
 
-  PasswordForm saved;
-  PasswordForm removed;
-
-  EXPECT_CALL(*mock_store_, AddLogin(_)).WillOnce(SaveArg<0>(&saved));
+  EXPECT_CALL(*mock_store_, AddLogin(pending));
   EXPECT_CALL(*mock_store_, UpdateLogin(_)).Times(0);
   EXPECT_CALL(*mock_store_, UpdateLoginWithPrimaryKey(_, _)).Times(0);
-  EXPECT_CALL(*mock_store_, RemoveLogin(_)).WillOnce(SaveArg<0>(&removed));
-  form_saver_.Save(pending, best_matches);
-  EXPECT_EQ(ASCIIToUTF16("nameofuser"), saved.username_value);
-  EXPECT_EQ(ASCIIToUTF16("wordToP4a55"), saved.password_value);
-  EXPECT_TRUE(removed.username_value.empty());
-  EXPECT_EQ(ASCIIToUTF16("wordToP4a55"), removed.password_value);
+  EXPECT_CALL(*mock_store_, RemoveLogin(no_username));
+  form_saver_.Save(pending, matches, base::string16());
 }
 
 // Check that storing credentials with a non-empty username does not result in
@@ -246,49 +245,16 @@
        Save_AndDoNotDeleteEmptyUsernameCredentialsWithDifferentPassword) {
   PasswordForm pending = CreatePending("nameofuser", "wordToP4a55");
 
-  std::map<base::string16, const PasswordForm*> best_matches;
-  best_matches[pending.username_value] = &pending;
   PasswordForm no_username = pending;
   no_username.username_value.clear();
   no_username.preferred = false;
   no_username.password_value = ASCIIToUTF16("abcd");
-  best_matches[no_username.username_value] = &no_username;
 
-  PasswordForm saved;
-
-  EXPECT_CALL(*mock_store_, AddLogin(_)).WillOnce(SaveArg<0>(&saved));
+  EXPECT_CALL(*mock_store_, AddLogin(pending));
   EXPECT_CALL(*mock_store_, UpdateLogin(_)).Times(0);
   EXPECT_CALL(*mock_store_, UpdateLoginWithPrimaryKey(_, _)).Times(0);
   EXPECT_CALL(*mock_store_, RemoveLogin(_)).Times(0);
-  form_saver_.Save(pending, best_matches);
-  EXPECT_EQ(ASCIIToUTF16("nameofuser"), saved.username_value);
-  EXPECT_EQ(ASCIIToUTF16("wordToP4a55"), saved.password_value);
-}
-
-// Check that if both "abc"/"pwd" and ""/"pwd" are both stored, and "abc"/"pwd"
-// is updated to "abc"/"def", then ""/"pwd" is not deleted.
-TEST_F(FormSaverImplTest,
-       Save_DoNotDeleteUsernamelessOnUpdatingPasswordWithUsername) {
-  PasswordForm pending = CreatePending("abc", "pwd");
-
-  std::map<base::string16, const PasswordForm*> best_matches;
-  best_matches[pending.username_value] = &pending;
-  PasswordForm no_username = pending;
-  no_username.username_value.clear();
-  no_username.preferred = false;
-  best_matches[no_username.username_value] = &no_username;
-
-  pending.password_value = ASCIIToUTF16("def");
-
-  PasswordForm saved;
-
-  EXPECT_CALL(*mock_store_, AddLogin(_)).WillOnce(SaveArg<0>(&saved));
-  EXPECT_CALL(*mock_store_, UpdateLogin(_)).Times(0);
-  EXPECT_CALL(*mock_store_, UpdateLoginWithPrimaryKey(_, _)).Times(0);
-  EXPECT_CALL(*mock_store_, RemoveLogin(_)).Times(0);
-  form_saver_.Save(pending, best_matches);
-  EXPECT_EQ(ASCIIToUTF16("abc"), saved.username_value);
-  EXPECT_EQ(ASCIIToUTF16("def"), saved.password_value);
+  form_saver_.Save(pending, {&no_username}, base::string16());
 }
 
 // Check that if a credential without username is saved, and another credential
@@ -297,21 +263,15 @@
 TEST_F(FormSaverImplTest, Save_EmptyUsernameWillNotCauseDeletion) {
   PasswordForm pending = CreatePending("", "wordToP4a55");
 
-  std::map<base::string16, const PasswordForm*> best_matches;
   PasswordForm with_username = pending;
   with_username.username_value = ASCIIToUTF16("nameofuser");
   with_username.preferred = false;
-  best_matches[with_username.username_value] = &with_username;
 
-  PasswordForm saved;
-
-  EXPECT_CALL(*mock_store_, AddLogin(_)).WillOnce(SaveArg<0>(&saved));
+  EXPECT_CALL(*mock_store_, AddLogin(pending));
   EXPECT_CALL(*mock_store_, UpdateLogin(_)).Times(0);
   EXPECT_CALL(*mock_store_, UpdateLoginWithPrimaryKey(_, _)).Times(0);
   EXPECT_CALL(*mock_store_, RemoveLogin(_)).Times(0);
-  form_saver_.Save(pending, best_matches);
-  EXPECT_TRUE(saved.username_value.empty());
-  EXPECT_EQ(ASCIIToUTF16("wordToP4a55"), saved.password_value);
+  form_saver_.Save(pending, {&with_username}, base::string16());
 }
 
 // Check that PSL-matched credentials in best matches are exempt from deletion,
@@ -320,22 +280,17 @@
 TEST_F(FormSaverImplTest, Save_AndDoNotDeleteEmptyUsernamePSLCredentials) {
   PasswordForm pending = CreatePending("nameofuser", "wordToP4a55");
 
-  std::map<base::string16, const PasswordForm*> best_matches;
-  best_matches[pending.username_value] = &pending;
+  PasswordForm stored = pending;
   PasswordForm no_username_psl = pending;
   no_username_psl.username_value.clear();
   no_username_psl.is_public_suffix_match = true;
-  best_matches[no_username_psl.username_value] = &no_username_psl;
+  const std::vector<const PasswordForm*> matches = {&stored, &no_username_psl};
 
-  PasswordForm saved;
-
-  EXPECT_CALL(*mock_store_, AddLogin(_)).WillOnce(SaveArg<0>(&saved));
+  EXPECT_CALL(*mock_store_, AddLogin(pending));
   EXPECT_CALL(*mock_store_, UpdateLogin(_)).Times(0);
   EXPECT_CALL(*mock_store_, UpdateLoginWithPrimaryKey(_, _)).Times(0);
   EXPECT_CALL(*mock_store_, RemoveLogin(_)).Times(0);
-  form_saver_.Save(pending, best_matches);
-  EXPECT_EQ(ASCIIToUTF16("nameofuser"), saved.username_value);
-  EXPECT_EQ(ASCIIToUTF16("wordToP4a55"), saved.password_value);
+  form_saver_.Save(pending, matches, base::string16());
 }
 
 // Check that on storing a credential, other credentials with the same password
@@ -343,22 +298,85 @@
 TEST_F(FormSaverImplTest, Save_AndDoNotDeleteNonEmptyUsernameCredentials) {
   PasswordForm pending = CreatePending("nameofuser", "wordToP4a55");
 
-  std::map<base::string16, const PasswordForm*> best_matches;
-  best_matches[pending.username_value] = &pending;
   PasswordForm other_username = pending;
   other_username.username_value = ASCIIToUTF16("other username");
   other_username.preferred = false;
-  best_matches[other_username.username_value] = &other_username;
 
-  PasswordForm saved;
-
-  EXPECT_CALL(*mock_store_, AddLogin(_)).WillOnce(SaveArg<0>(&saved));
+  EXPECT_CALL(*mock_store_, AddLogin(pending));
   EXPECT_CALL(*mock_store_, UpdateLogin(_)).Times(0);
   EXPECT_CALL(*mock_store_, UpdateLoginWithPrimaryKey(_, _)).Times(0);
   EXPECT_CALL(*mock_store_, RemoveLogin(_)).Times(0);
-  form_saver_.Save(pending, best_matches);
-  EXPECT_EQ(ASCIIToUTF16("nameofuser"), saved.username_value);
-  EXPECT_EQ(ASCIIToUTF16("wordToP4a55"), saved.password_value);
+  form_saver_.Save(pending, {&other_username}, base::string16());
+}
+
+// Stores a credential and makes sure that its duplicate is updated.
+TEST_F(FormSaverImplTest, Save_AndUpdatePasswordValuesOnExactMatch) {
+  constexpr char kOldPassword[] = "old_password";
+  constexpr char kNewPassword[] = "new_password";
+
+  PasswordForm duplicate = CreatePending("nameofuser", kOldPassword);
+  duplicate.origin = GURL("https://example.in/somePath");
+
+  PasswordForm expected_update = duplicate;
+  expected_update.password_value = ASCIIToUTF16(kNewPassword);
+  PasswordForm pending = CreatePending("nameofuser", kNewPassword);
+
+  EXPECT_CALL(*mock_store_, AddLogin(pending));
+  EXPECT_CALL(*mock_store_, UpdateLogin(expected_update));
+  EXPECT_CALL(*mock_store_, UpdateLoginWithPrimaryKey(_, _)).Times(0);
+  EXPECT_CALL(*mock_store_, RemoveLogin(_)).Times(0);
+  form_saver_.Save(pending, {&duplicate}, ASCIIToUTF16(kOldPassword));
+}
+
+// Stores a credential and makes sure that its PSL duplicate is updated.
+TEST_F(FormSaverImplTest, Save_AndUpdatePasswordValuesOnPSLMatch) {
+  constexpr char kOldPassword[] = "old_password";
+  constexpr char kNewPassword[] = "new_password";
+  PasswordForm pending = CreatePending("nameofuser", kOldPassword);
+
+  PasswordForm duplicate = pending;
+  duplicate.origin = GURL("https://www.example.in");
+  duplicate.signon_realm = duplicate.origin.spec();
+  duplicate.is_public_suffix_match = true;
+
+  PasswordForm expected_update = duplicate;
+  expected_update.password_value = ASCIIToUTF16(kNewPassword);
+  pending.password_value = ASCIIToUTF16(kNewPassword);
+
+  EXPECT_CALL(*mock_store_, AddLogin(pending));
+  EXPECT_CALL(*mock_store_, UpdateLogin(expected_update));
+  EXPECT_CALL(*mock_store_, UpdateLoginWithPrimaryKey(_, _)).Times(0);
+  EXPECT_CALL(*mock_store_, RemoveLogin(_)).Times(0);
+  form_saver_.Save(pending, {&duplicate}, ASCIIToUTF16(kOldPassword));
+}
+
+// Stores a credential and makes sure that not exact matches are not updated.
+TEST_F(FormSaverImplTest, Save_AndUpdatePasswordValues_IgnoreNonMatches) {
+  constexpr char kOldPassword[] = "old_password";
+  constexpr char kNewPassword[] = "new_password";
+  PasswordForm pending = CreatePending("nameofuser", kOldPassword);
+
+  PasswordForm different_username = pending;
+  different_username.username_value = ASCIIToUTF16("someuser");
+  different_username.preferred = false;
+
+  PasswordForm different_password = pending;
+  different_password.password_value = ASCIIToUTF16("some_password");
+  different_password.preferred = false;
+
+  PasswordForm empty_username = pending;
+  empty_username.username_value.clear();
+  empty_username.preferred = false;
+  const std::vector<const PasswordForm*> matches = {
+      &different_username, &different_password, &empty_username};
+
+  pending.password_value = ASCIIToUTF16(kNewPassword);
+
+  EXPECT_CALL(*mock_store_, AddLogin(pending));
+  EXPECT_CALL(*mock_store_, UpdateLogin(_)).Times(0);
+  EXPECT_CALL(*mock_store_, UpdateLoginWithPrimaryKey(_, _)).Times(0);
+  EXPECT_CALL(*mock_store_, RemoveLogin(_)).Times(0);
+  form_saver_.Save(pending, matches, ASCIIToUTF16(kOldPassword));
 }
 
 // Check that presaving a password for the first time results in adding it.
@@ -411,7 +429,7 @@
   EXPECT_CALL(*mock_store_, UpdateLogin(_)).Times(0);
   EXPECT_CALL(*mock_store_, UpdateLoginWithPrimaryKey(_, _))
       .WillOnce(DoAll(SaveArg<0>(&saved_new), SaveArg<1>(&saved_old)));
-  form_saver_.Save(pending, std::map<base::string16, const PasswordForm*>());
+  form_saver_.Save(pending, {}, base::string16());
   EXPECT_EQ(ASCIIToUTF16("generatedU"), saved_old.username_value);
   EXPECT_EQ(ASCIIToUTF16("generatedP"), saved_old.password_value);
   EXPECT_EQ(ASCIIToUTF16("nameofuser"), saved_new.username_value);
@@ -560,7 +578,7 @@
     if (presave)
       form_saver_.PresaveGeneratedPassword(pending);
     else
-      form_saver_.Save(pending, {});
+      form_saver_.Save(pending, {}, base::string16());
 
     ASSERT_EQ(1u, saved.form_data.fields.size());
     const FormFieldData& saved_field = saved.form_data.fields[0];
diff --git a/components/password_manager/core/browser/login_database_unittest.cc b/components/password_manager/core/browser/login_database_unittest.cc
index eae64976..42c6fde 100644
--- a/components/password_manager/core/browser/login_database_unittest.cc
+++ b/components/password_manager/core/browser/login_database_unittest.cc
@@ -1769,8 +1769,8 @@
       metadata_batch->TakeAllMetadata();
 
   EXPECT_EQ(metadata_records.size(), 2u);
-  EXPECT_EQ(metadata_records[kStorageKey1].sequence_number(), 1);
-  EXPECT_EQ(metadata_records[kStorageKey2].sequence_number(), 2);
+  EXPECT_EQ(metadata_records[kStorageKey1]->sequence_number(), 1);
+  EXPECT_EQ(metadata_records[kStorageKey2]->sequence_number(), 2);
 
   // Now check that a model type state update replaces the old value
   model_type_state.set_initial_sync_done(false);
diff --git a/components/password_manager/core/browser/new_password_form_manager.cc b/components/password_manager/core/browser/new_password_form_manager.cc
index 1bb3857e..0399d06 100644
--- a/components/password_manager/core/browser/new_password_form_manager.cc
+++ b/components/password_manager/core/browser/new_password_form_manager.cc
@@ -260,7 +260,18 @@
     pending_credentials_.date_created = base::Time::Now();
     votes_uploader_.SendVotesOnSave(observed_form_, *parsed_submitted_form_,
                                     best_matches_, &pending_credentials_);
-    form_saver_->Save(pending_credentials_, best_matches_);
+    base::string16 old_password;
+    if (password_overridden_) {
+      // |pending_credentials_| contains not only a new credential that is to be
+      // stored, but it also implies that existing credentials in the store need
+      // to get the password updated.
+      const PasswordForm* saved_form =
+          password_manager_util::GetMatchForUpdating(*parsed_submitted_form_,
+                                                     best_matches_);
+      DCHECK(saved_form);
+      old_password = saved_form->password_value;
+    }
+    form_saver_->Save(pending_credentials_, GetAllMatches(), old_password);
   } else {
     ProcessUpdate();
     std::vector<PasswordForm> credentials_to_update =
@@ -443,14 +454,8 @@
          parsed_submitted_form_->IsPossibleChangePasswordFormWithoutUsername();
 }
 
-bool NewPasswordFormManager::RetryPasswordFormPasswordUpdate() const {
-  return retry_password_form_password_update_;
-}
-
 bool NewPasswordFormManager::IsPasswordUpdate() const {
-  return (!GetBestMatches().empty() &&
-          IsPossibleChangePasswordFormWithoutUsername()) ||
-         IsPasswordOverridden() || RetryPasswordFormPasswordUpdate();
+  return IsPasswordOverridden();
 }
 
 std::vector<base::WeakPtr<PasswordManagerDriver>>
@@ -536,8 +541,6 @@
   }
   result->is_new_login_ = is_new_login_;
   result->password_overridden_ = password_overridden_;
-  result->retry_password_form_password_update_ =
-      retry_password_form_password_update_;
   result->is_submitted_ = is_submitted_;
 
   return result;
@@ -814,26 +817,24 @@
   // changed in this function to initial states.
   is_new_login_ = true;
   SetPasswordOverridden(false);
-  retry_password_form_password_update_ = false;
 
   ValueElementPair password_to_save(PasswordToSave(*parsed_submitted_form_));
 
   // Look for the actually submitted credentials in the list of previously saved
   // credentials that were available to autofilling.
-  const PasswordForm* saved_form =
-      FindBestSavedMatch(parsed_submitted_form_.get());
+  const PasswordForm* saved_form = password_manager_util::GetMatchForUpdating(
+      *parsed_submitted_form_, best_matches_);
   if (saved_form) {
-    // The user signed in with a login we autofilled.
+    // A similar credential exists in the store already.
     pending_credentials_ = *saved_form;
     SetPasswordOverridden(pending_credentials_.password_value !=
                           password_to_save.first);
+    // If the autofilled credentials were a PSL match, store a copy with the
+    // current origin and signon realm. This ensures that on the next visit, a
+    // precise match is found.
+    is_new_login_ = pending_credentials_.is_public_suffix_match;
 
-    if (pending_credentials_.is_public_suffix_match) {
-      // If the autofilled credentials were a PSL match or credentials stored
-      // from Android apps, store a copy with the current origin and signon
-      // realm. This ensures that on the next visit, a precise match is found.
-      is_new_login_ = true;
-
+    if (is_new_login_) {
       // Update credential to reflect that it has been used for submission.
       // If this isn't updated, then password generation uploads are off for
       // sites where PSL matching is required to fill the login form, as two
@@ -841,73 +842,9 @@
       password_manager_util::UpdateMetadataForUsage(&pending_credentials_);
 
       // Update |pending_credentials_| in order to be able correctly save it.
-      pending_credentials_.origin = submitted_form_.origin;
+      pending_credentials_.origin = parsed_submitted_form_->origin;
       pending_credentials_.signon_realm = parsed_submitted_form_->signon_realm;
-
-      // Normally, the copy of the PSL matched credentials, adapted for the
-      // current domain, is saved automatically without asking the user, because
-      // the copy likely represents the same account, i.e., the one for which
-      // the user already agreed to store a password.
-      //
-      // However, if the user changes the suggested password, it might indicate
-      // that the autofilled credentials and |submitted_password_form|
-      // actually correspond to two different accounts (see
-      // http://crbug.com/385619). In that case the user should be asked again
-      // before saving the password. This is ensured by setting
-      // |password_overriden_| on |pending_credentials_| to false.
-      //
-      // There is still the edge case when the autofilled credentials represent
-      // the same account as |submitted_password_form| but the stored password
-      // was out of date. In that case, the user just had to manually enter the
-      // new password, which is now in |submitted_password_form|. The best
-      // thing would be to save automatically, and also update the original
-      // credentials. However, we have no way to tell if this is the case.
-      // This will likely happen infrequently, and the inconvenience put on the
-      // user by asking them is not significant, so we are fine with asking
-      // here again.
-      if (password_overridden_) {
-        pending_credentials_.is_public_suffix_match = false;
-        SetPasswordOverridden(false);
-      }
-    } else {  // Not a PSL match but a match of an already stored credential.
-      is_new_login_ = false;
-    }
-  } else if (!best_matches_.empty() &&
-             parsed_submitted_form_->type != PasswordForm::TYPE_API &&
-             parsed_submitted_form_->username_value.empty()) {
-    // This branch deals with the case that the submitted form has no username
-    // element and needs to decide whether to offer to update any credentials.
-    // In that case, the user can select any previously stored credential as
-    // the one to update, but we still try to find the best candidate.
-
-    // Find the best candidate to select by default in the password update
-    // bubble. If no best candidate is found, any one can be offered.
-    const PasswordForm* best_update_match =
-        FindBestMatchForUpdatePassword(parsed_submitted_form_->password_value);
-
-    // A retry password form is one that consists of only an "old password"
-    // field, i.e. one that is not a "new password".
-    retry_password_form_password_update_ =
-        parsed_submitted_form_->username_value.empty() &&
-        parsed_submitted_form_->new_password_value.empty();
-
-    is_new_login_ = false;
-    if (best_update_match) {
-      // Chose |best_update_match| to be updated.
-      pending_credentials_ = *best_update_match;
-    } else if (HasGeneratedPassword()) {
-      // If a password was generated and we didn't find a match, we have to save
-      // it in a separate entry since we have to store it but we don't know
-      // where.
-      CreatePendingCredentialsForNewCredentials(*parsed_submitted_form_,
-                                                password_to_save.second);
-      is_new_login_ = true;
-    } else {
-      // We don't have a good candidate to choose as the default credential for
-      // the update bubble and the user has to pick one.
-      // We set |pending_credentials_| to the bare minimum, which is the correct
-      // origin.
-      pending_credentials_.origin = submitted_form_.origin;
+      pending_credentials_.action = parsed_submitted_form_->action;
     }
   } else {
     is_new_login_ = true;
@@ -929,10 +866,6 @@
               kCorrectedUsernameInForm);
     }
   }
-
-  if (!IsValidAndroidFacetURI(pending_credentials_.signon_realm))
-    pending_credentials_.action = submitted_form_.action;
-
   pending_credentials_.password_value = generated_password_.empty()
                                             ? password_to_save.first
                                             : generated_password_;
@@ -963,61 +896,6 @@
     pending_credentials_.type = PasswordForm::TYPE_GENERATED;
 }
 
-const PasswordForm* NewPasswordFormManager::FindBestMatchForUpdatePassword(
-    const base::string16& password) const {
-  // This function is called for forms that do not contain a username field.
-  // This means that we cannot update credentials based on a matching username
-  // and that we may need to show an update prompt.
-  if (best_matches_.size() == 1 && !HasGeneratedPassword()) {
-    // In case the submitted form contained no username but a password, and if
-    // the user has only one credential stored, return it as the one that should
-    // be updated.
-    return best_matches_.begin()->second;
-  }
-  if (password.empty())
-    return nullptr;
-
-  // Return any existing credential that has the same |password| saved already.
-  for (const auto& key_value : best_matches_) {
-    if (key_value.second->password_value == password)
-      return key_value.second;
-  }
-  return nullptr;
-}
-
-const PasswordForm* NewPasswordFormManager::FindBestSavedMatch(
-    const PasswordForm* submitted_form) const {
-  if (!submitted_form->federation_origin.opaque())
-    return nullptr;
-
-  // Return form with matching |username_value|.
-  auto it = best_matches_.find(submitted_form->username_value);
-  if (it != best_matches_.end())
-    return it->second;
-
-  // Match Credential API forms only by username. Stop here if nothing was found
-  // above.
-  if (submitted_form->type == PasswordForm::TYPE_API)
-    return nullptr;
-
-  // Verify that the submitted form has no username and no "new password"
-  // and bail out with a nullptr otherwise.
-  bool submitted_form_has_username = !submitted_form->username_value.empty();
-  bool submitted_form_has_new_password_element =
-      !submitted_form->new_password_value.empty();
-  if (submitted_form_has_username || submitted_form_has_new_password_element)
-    return nullptr;
-
-  // At this line we are certain that the submitted form contains only a
-  // password field that is not a "new password". Now we can check whether we
-  // have a match by password of an already saved credential.
-  for (const auto& stored_match : best_matches_) {
-    if (stored_match.second->password_value == submitted_form->password_value)
-      return stored_match.second;
-  }
-  return nullptr;
-}
-
 void NewPasswordFormManager::CreatePendingCredentialsForNewCredentials(
     const PasswordForm& submitted_password_form,
     const base::string16& password_element) {
@@ -1056,7 +934,7 @@
   // update the stats, or the user typed in a new password for autofilled
   // username, or the user selected one of the non-preferred matches,
   // thus requiring a swap of preferred bits.
-  DCHECK(!is_new_login_ && pending_credentials_.preferred);
+  DCHECK(pending_credentials_.preferred);
   DCHECK(!client_->IsIncognito());
   DCHECK(parsed_submitted_form_);
 
@@ -1200,4 +1078,16 @@
 #endif
 }
 
+std::vector<const PasswordForm*> NewPasswordFormManager::GetAllMatches() const {
+  std::vector<const autofill::PasswordForm*> result =
+      form_fetcher_->GetNonFederatedMatches();
+  PasswordForm::Scheme observed_form_scheme =
+      observed_http_auth_digest_ ? observed_http_auth_digest_->scheme
+                                 : PasswordForm::SCHEME_HTML;
+  base::EraseIf(result, [observed_form_scheme](const auto* form) {
+    return form->scheme != observed_form_scheme;
+  });
+  return result;
+}
+
 }  // namespace password_manager
diff --git a/components/password_manager/core/browser/new_password_form_manager.h b/components/password_manager/core/browser/new_password_form_manager.h
index 2cb42f3..c5b7ba1 100644
--- a/components/password_manager/core/browser/new_password_form_manager.h
+++ b/components/password_manager/core/browser/new_password_form_manager.h
@@ -150,7 +150,6 @@
                                   bool is_manual_generation) override;
   void SetGenerationElement(const base::string16& generation_element) override;
   bool IsPossibleChangePasswordFormWithoutUsername() const override;
-  bool RetryPasswordFormPasswordUpdate() const override;
   bool IsPasswordUpdate() const override;
   std::vector<base::WeakPtr<PasswordManagerDriver>> GetDrivers() const override;
   const autofill::PasswordForm* GetSubmittedForm() const override;
@@ -237,19 +236,6 @@
       const autofill::PasswordForm& submitted_password_form,
       const base::string16& password_element);
 
-  // If |best_matches_| contains only one entry, then return this entry.
-  // Otherwise for empty |password| return nullptr and for non-empty |password|
-  // returns the any entry in |best_matches_| with the same password, if it
-  // exists, and nullptr otherwise.
-  const autofill::PasswordForm* FindBestMatchForUpdatePassword(
-      const base::string16& password) const;
-
-  // Try to return a member of |best_matches_| which is most likely to represent
-  // the same credential as |form|. Return null if there is none. This is used
-  // to tell whether the user submitted a credential filled by Chrome.
-  const autofill::PasswordForm* FindBestSavedMatch(
-      const autofill::PasswordForm* form) const;
-
   void SetPasswordOverridden(bool password_overridden) {
     password_overridden_ = password_overridden;
     votes_uploader_.set_password_overridden(password_overridden);
@@ -267,6 +253,7 @@
   // the old password and username with |pending_credentials_| to the new
   // password of |pending_credentials_|, and returns copies of all such modified
   // credentials.
+  // TODO(crbug/831123): remove. FormSaver should do the job.
   std::vector<autofill::PasswordForm> FindOtherCredentialsToUpdate();
 
   // Helper function for calling form parsing and logging results if logging is
@@ -284,6 +271,10 @@
   void CalculateFillingAssistanceMetric(
       const autofill::FormData& submitted_form);
 
+  // Returns all the credentials for the origin (essentially, |best_matches_|
+  // and |not_best_matches_|).
+  std::vector<const autofill::PasswordForm*> GetAllMatches() const;
+
   // The client which implements embedder-specific PasswordManager operations.
   PasswordManagerClient* client_;
 
@@ -353,8 +344,10 @@
   // |submitted_form_| and |best_matches_|.
   autofill::PasswordForm pending_credentials_;
 
-  // Whether |pending_credentials_| stores a new login or is an update to an
-  // existing one.
+  // Whether |pending_credentials_| stores a credential that should be added
+  // to the password store. False means it's a pure update to the existing ones.
+  // TODO(crbug/831123): this value only makes sense internally. Remove public
+  // dependencies on it.
   bool is_new_login_ = true;
 
   // Contains a generated password, empty if no password generation happened or
@@ -367,16 +360,10 @@
   base::string16 generation_element_;
 #endif
 
-  // Whether the saved password was overridden.
+  // Whether a saved password was overridden. The flag is true when there is a
+  // credential in the store that will get a new password value.
   bool password_overridden_ = false;
 
-  // A form is considered to be "retry" password if it has only one field which
-  // is a current password field.
-  // This variable is true if the password passed through ProvisionallySave() is
-  // a password that is not part of any password form stored for this origin
-  // and it was entered on a retry password form.
-  bool retry_password_form_password_update_ = false;
-
   // If Chrome has already autofilled a few times, it is probable that autofill
   // is triggered by programmatic changes in the page. We set a maximum number
   // of times that Chrome will autofill to avoid being stuck in an infinite
diff --git a/components/password_manager/core/browser/new_password_form_manager_unittest.cc b/components/password_manager/core/browser/new_password_form_manager_unittest.cc
index 556fa88..acd6988 100644
--- a/components/password_manager/core/browser/new_password_form_manager_unittest.cc
+++ b/components/password_manager/core/browser/new_password_form_manager_unittest.cc
@@ -189,10 +189,10 @@
 
   // FormSaver:
   MOCK_METHOD1(PermanentlyBlacklist, void(autofill::PasswordForm* observed));
-  MOCK_METHOD2(
-      Save,
-      void(const autofill::PasswordForm& pending,
-           const std::map<base::string16, const PasswordForm*>& best_matches));
+  MOCK_METHOD3(Save,
+               void(const autofill::PasswordForm& pending,
+                    const std::vector<const autofill::PasswordForm*>& matches,
+                    const base::string16& old_password));
   MOCK_METHOD4(
       Update,
       void(const autofill::PasswordForm& pending,
@@ -758,9 +758,7 @@
   submitted_form.fields[0].value = ASCIIToUTF16("strongpassword");
   submitted_form.fields[1].value = ASCIIToUTF16("verystrongpassword");
 
-  PasswordForm expected;
-  expected.origin = observed_form_.origin;
-  expected.action = observed_form_.action;
+  PasswordForm expected = saved_match_;
   expected.password_value = ASCIIToUTF16("verystrongpassword");
 
   EXPECT_TRUE(form_manager_->ProvisionallySave(submitted_form, &driver_));
@@ -847,8 +845,8 @@
 
   MockFormSaver& form_saver = MockFormSaver::Get(form_manager_.get());
   PasswordForm saved_form;
-  std::map<base::string16, const PasswordForm*> best_matches;
-  EXPECT_CALL(form_saver, Save(_, _))
+  std::vector<const PasswordForm*> best_matches;
+  EXPECT_CALL(form_saver, Save(_, _, _))
       .WillOnce(DoAll(SaveArg<0>(&saved_form), SaveArg<1>(&best_matches)));
   EXPECT_CALL(client_, UpdateFormManagers());
 
@@ -865,10 +863,7 @@
             saved_form.username_element);
   EXPECT_EQ(submitted_form.fields[kPasswordFieldIndex].name,
             saved_form.password_element);
-  EXPECT_EQ(1u, best_matches.size());
-  base::string16 saved_username = saved_match_.username_value;
-  ASSERT_TRUE(best_matches.find(saved_username) != best_matches.end());
-  EXPECT_EQ(saved_match_, *best_matches[saved_username]);
+  EXPECT_EQ(std::vector<const PasswordForm*>{&saved_match_}, best_matches);
 
   // Check UKM metrics.
   form_manager_.reset();
@@ -900,8 +895,8 @@
 
   MockFormSaver& form_saver = MockFormSaver::Get(form_manager_.get());
   PasswordForm saved_form;
-  std::map<base::string16, const PasswordForm*> best_matches;
-  EXPECT_CALL(form_saver, Save(_, _))
+  std::vector<const PasswordForm*> best_matches;
+  EXPECT_CALL(form_saver, Save(_, _, _))
       .WillOnce(DoAll(SaveArg<0>(&saved_form), SaveArg<1>(&best_matches)));
 
   form_manager_->Save();
@@ -915,10 +910,7 @@
 
   EXPECT_TRUE(saved_form.preferred);
 
-  EXPECT_EQ(1u, best_matches.size());
-  base::string16 saved_username = psl_saved_match_.username_value;
-  ASSERT_TRUE(best_matches.find(saved_username) != best_matches.end());
-  EXPECT_EQ(psl_saved_match_, *best_matches[saved_username]);
+  EXPECT_EQ(std::vector<const PasswordForm*>{&psl_saved_match_}, best_matches);
 }
 
 // Tests that when credentials with already saved username but with a new
@@ -976,7 +968,7 @@
 
   EXPECT_TRUE(form_manager_->ProvisionallySave(submitted_form, &driver_));
   EXPECT_FALSE(form_manager_->IsNewLogin());
-  EXPECT_FALSE(form_manager_->IsPasswordOverridden());
+  EXPECT_TRUE(form_manager_->IsPasswordOverridden());
   EXPECT_TRUE(form_manager_->IsPasswordUpdate());
 
   MockFormSaver& form_saver = MockFormSaver::Get(form_manager_.get());
@@ -1198,7 +1190,7 @@
   // Check that the password which was chosen by the user is saved.
   MockFormSaver& form_saver = MockFormSaver::Get(form_manager_.get());
   PasswordForm saved_form;
-  EXPECT_CALL(form_saver, Save(_, _)).WillOnce(SaveArg<0>(&saved_form));
+  EXPECT_CALL(form_saver, Save(_, _, _)).WillOnce(SaveArg<0>(&saved_form));
 
   form_manager_->Save();
   CheckPendingCredentials(expected, saved_form);
@@ -1483,7 +1475,7 @@
   EXPECT_TRUE(form_manager_->HasGeneratedPassword());
 
   // Check that the generated password is saved.
-  EXPECT_CALL(form_saver, Save(_, _)).WillOnce(SaveArg<0>(&saved_form));
+  EXPECT_CALL(form_saver, Save(_, _, _)).WillOnce(SaveArg<0>(&saved_form));
   EXPECT_CALL(client_, UpdateFormManagers());
 
   EXPECT_TRUE(form_manager_->ProvisionallySave(submitted_form_, &driver_));
@@ -1876,7 +1868,7 @@
     EXPECT_TRUE(form_manager_->IsNewLogin());
 
     PasswordForm saved_form;
-    EXPECT_CALL(form_saver, Save(_, _)).WillOnce(SaveArg<0>(&saved_form));
+    EXPECT_CALL(form_saver, Save(_, _, _)).WillOnce(SaveArg<0>(&saved_form));
     form_manager_->Save();
 
     EXPECT_EQ(http_auth_form.signon_realm, saved_form.signon_realm);
diff --git a/components/password_manager/core/browser/password_form_filling.cc b/components/password_manager/core/browser/password_form_filling.cc
index d234228..68c2b5b 100644
--- a/components/password_manager/core/browser/password_form_filling.cc
+++ b/components/password_manager/core/browser/password_form_filling.cc
@@ -60,9 +60,8 @@
     logger->LogMessage(Logger::STRING_PASSWORDMANAGER_AUTOFILL);
   }
 
-  autofill::PasswordFormFillData fill_data;
-  InitPasswordFormFillData(form_for_autofill, best_matches, &preferred_match,
-                           wait_for_username, &fill_data);
+  PasswordFormFillData fill_data(form_for_autofill, best_matches,
+                                 preferred_match, wait_for_username);
   if (logger)
     logger->LogBoolean(Logger::STRING_WAIT_FOR_USERNAME, wait_for_username);
   UMA_HISTOGRAM_BOOLEAN(
@@ -93,12 +92,10 @@
             STRING_PASSWORDMANAGER_SHOW_INITIAL_PASSWORD_ACCOUNT_SUGGESTIONS);
   }
 
-  PasswordFormFillData fill_data;
-  InitPasswordFormFillData(form_for_autofill, best_matches, &preferred_match,
-                           wait_for_username, &fill_data);
   if (logger)
     logger->LogBoolean(Logger::STRING_WAIT_FOR_USERNAME, wait_for_username);
-  driver->ShowInitialPasswordAccountSuggestions(fill_data);
+  driver->ShowInitialPasswordAccountSuggestions(PasswordFormFillData(
+      form_for_autofill, best_matches, preferred_match, wait_for_username));
 }
 
 }  // namespace
diff --git a/components/password_manager/core/browser/password_form_manager.cc b/components/password_manager/core/browser/password_form_manager.cc
index a8fb6c0..ec2f994 100644
--- a/components/password_manager/core/browser/password_form_manager.cc
+++ b/components/password_manager/core/browser/password_form_manager.cc
@@ -300,7 +300,10 @@
     pending_credentials_.date_created = base::Time::Now();
     votes_uploader_.SendVotesOnSave(observed_form_.form_data, *submitted_form_,
                                     best_matches_, &pending_credentials_);
-    form_saver_->Save(pending_credentials_, best_matches_);
+    std::vector<const autofill::PasswordForm*> best_matches;
+    for (const auto& match : best_matches_)
+      best_matches.push_back(match.second);
+    form_saver_->Save(pending_credentials_, best_matches, base::string16());
   } else {
     ProcessUpdate();
     std::vector<PasswordForm> credentials_to_update =
@@ -869,14 +872,10 @@
                                                      is_manual_generation);
 }
 
-bool PasswordFormManager::RetryPasswordFormPasswordUpdate() const {
-  return retry_password_form_password_update_;
-}
-
 bool PasswordFormManager::IsPasswordUpdate() const {
   return (!GetBestMatches().empty() &&
           IsPossibleChangePasswordFormWithoutUsername()) ||
-         IsPasswordOverridden() || RetryPasswordFormPasswordUpdate();
+         IsPasswordOverridden() || retry_password_form_password_update_;
 }
 
 void PasswordFormManager::LogSubmitPassed() {
diff --git a/components/password_manager/core/browser/password_form_manager.h b/components/password_manager/core/browser/password_form_manager.h
index 00ef4a7..1444fb53 100644
--- a/components/password_manager/core/browser/password_form_manager.h
+++ b/components/password_manager/core/browser/password_form_manager.h
@@ -115,7 +115,6 @@
   bool IsNewLogin() const override;
   bool IsPendingCredentialsPublicSuffixMatch() const override;
   bool IsPossibleChangePasswordFormWithoutUsername() const override;
-  bool RetryPasswordFormPasswordUpdate() const override;
   bool IsPasswordUpdate() const override;
   std::vector<base::WeakPtr<PasswordManagerDriver>> GetDrivers() const override;
   const autofill::PasswordForm* GetSubmittedForm() const override;
diff --git a/components/password_manager/core/browser/password_form_manager_for_ui.h b/components/password_manager/core/browser/password_form_manager_for_ui.h
index 070cc966..06e5984 100644
--- a/components/password_manager/core/browser/password_form_manager_for_ui.h
+++ b/components/password_manager/core/browser/password_form_manager_for_ui.h
@@ -142,10 +142,6 @@
   // field. We use only client heuristics, so it could include signup forms.
   virtual bool IsPossibleChangePasswordFormWithoutUsername() const = 0;
 
-  // A form is considered to be "retry" password if it has only one field which
-  // is a current password field.
-  virtual bool RetryPasswordFormPasswordUpdate() const = 0;
-
   // Helper function that determines whether update or save prompt should be
   // shown for credentials in |provisional_save_manager|.
   virtual bool IsPasswordUpdate() const = 0;
diff --git a/components/password_manager/core/browser/password_form_manager_unittest.cc b/components/password_manager/core/browser/password_form_manager_unittest.cc
index 201aec82..252e48b 100644
--- a/components/password_manager/core/browser/password_form_manager_unittest.cc
+++ b/components/password_manager/core/browser/password_form_manager_unittest.cc
@@ -117,10 +117,10 @@
 
   // FormSaver:
   MOCK_METHOD1(PermanentlyBlacklist, void(autofill::PasswordForm* observed));
-  MOCK_METHOD2(
-      Save,
-      void(const autofill::PasswordForm& pending,
-           const std::map<base::string16, const PasswordForm*>& best_matches));
+  MOCK_METHOD3(Save,
+               void(const autofill::PasswordForm& pending,
+                    const std::vector<const autofill::PasswordForm*>& matches,
+                    const base::string16& old_password));
   MOCK_METHOD4(
       Update,
       void(const autofill::PasswordForm& pending,
@@ -1227,7 +1227,7 @@
   EXPECT_CALL(*client()->mock_driver()->mock_autofill_download_manager(),
               StartUploadRequest(_, false, expected_available_field_types, _,
                                  true, nullptr));
-  EXPECT_CALL(MockFormSaver::Get(form_manager()), Save(_, _))
+  EXPECT_CALL(MockFormSaver::Get(form_manager()), Save(_, _, _))
       .WillOnce(SaveArg<0>(&actual_saved_form));
   form_manager()->Save();
 
@@ -1577,7 +1577,7 @@
   form_manager()->ProvisionallySave(credentials);
 
   PasswordForm saved_result;
-  EXPECT_CALL(MockFormSaver::Get(form_manager()), Save(_, _))
+  EXPECT_CALL(MockFormSaver::Get(form_manager()), Save(_, _, _))
       .WillOnce(SaveArg<0>(&saved_result));
   form_manager()->Save();
 
@@ -1610,7 +1610,7 @@
   form_manager()->ProvisionallySave(credentials);
 
   PasswordForm saved_result;
-  EXPECT_CALL(MockFormSaver::Get(form_manager()), Save(_, _))
+  EXPECT_CALL(MockFormSaver::Get(form_manager()), Save(_, _, _))
       .WillOnce(SaveArg<0>(&saved_result));
   form_manager()->Save();
 
@@ -2070,7 +2070,7 @@
   EXPECT_TRUE(form_manager()->IsNewLogin());
 
   PasswordForm saved_result;
-  EXPECT_CALL(MockFormSaver::Get(form_manager()), Save(_, _))
+  EXPECT_CALL(MockFormSaver::Get(form_manager()), Save(_, _, _))
       .WillOnce(SaveArg<0>(&saved_result));
 
   form_manager()->Save();
@@ -2357,7 +2357,8 @@
 
     // User clicks save, the edited username is saved.
     PasswordForm saved_result;
-    EXPECT_CALL(MockFormSaver::Get(&form_manager), Save(_, IsEmpty()))
+    EXPECT_CALL(MockFormSaver::Get(&form_manager),
+                Save(_, IsEmpty(), base::string16()))
         .WillOnce(SaveArg<0>(&saved_result));
     // Expect a username edited vote.
     FieldTypeMap expected_types;
@@ -2506,8 +2507,7 @@
     // no username vote is uploaded.
     PasswordForm actual_saved_form;
     EXPECT_CALL(MockFormSaver::Get(&form_manager),
-                Save(_, ElementsAre(Pair(saved_match()->username_value,
-                                         Pointee(*saved_match())))))
+                Save(_, ElementsAre(Pointee(*saved_match())), _))
         .WillOnce(SaveArg<0>(&actual_saved_form));
     EXPECT_CALL(*client()->mock_driver()->mock_autofill_download_manager(),
                 StartUploadRequest(_, _, Not(Contains(autofill::USERNAME)), _,
@@ -2549,7 +2549,7 @@
 
   // The user clicks save, empty username is saved.
   PasswordForm saved_result;
-  EXPECT_CALL(MockFormSaver::Get(&form_manager), Save(_, IsEmpty()))
+  EXPECT_CALL(MockFormSaver::Get(&form_manager), Save(_, IsEmpty(), _))
       .WillOnce(SaveArg<0>(&saved_result));
   EXPECT_CALL(*client()->mock_driver()->mock_autofill_download_manager(),
               StartUploadRequest(_, _, Not(Contains(autofill::USERNAME)), _, _,
@@ -2585,8 +2585,7 @@
   // The user clicks save, the edited username is saved.
   PasswordForm saved_result;
   EXPECT_CALL(MockFormSaver::Get(form_manager()),
-              Save(_, ElementsAre(Pair(psl_saved_match()->username_value,
-                                       Pointee(*psl_saved_match())))))
+              Save(_, ElementsAre(Pointee(*psl_saved_match())), _))
       .WillOnce(SaveArg<0>(&saved_result));
   // As the credential is re-used successfully, expect a username vote.
   EXPECT_CALL(
@@ -2666,7 +2665,7 @@
 
         // User clicks save, selected password is saved.
         PasswordForm saved_result;
-        EXPECT_CALL(MockFormSaver::Get(&form_manager), Save(_, IsEmpty()))
+        EXPECT_CALL(MockFormSaver::Get(&form_manager), Save(_, IsEmpty(), _))
             .WillOnce(SaveArg<0>(&saved_result));
         // Expect a password edited vote.
         FieldTypeMap expected_types;
@@ -3025,7 +3024,7 @@
   // PSL matched credential should not be updated, since we are not sure that
   // this is the same credential as submitted one.
   PasswordForm new_credentials;
-  EXPECT_CALL(MockFormSaver::Get(&form_manager), Save(_, _))
+  EXPECT_CALL(MockFormSaver::Get(&form_manager), Save(_, _, _))
       .WillOnce(testing::SaveArg<0>(&new_credentials));
   // As the username is re-used, expect a username vote.
   EXPECT_CALL(
@@ -3359,7 +3358,7 @@
   EXPECT_TRUE(form_manager.IsNewLogin());
 
   PasswordForm new_credentials;
-  EXPECT_CALL(MockFormSaver::Get(&form_manager), Save(_, _))
+  EXPECT_CALL(MockFormSaver::Get(&form_manager), Save(_, _, _))
       .WillOnce(SaveArg<0>(&new_credentials));
 
   form_manager.Save();
@@ -3821,7 +3820,7 @@
   std::unique_ptr<PasswordFormManager> clone = form_manager->Clone();
 
   PasswordForm passed;
-  EXPECT_CALL(MockFormSaver::Get(clone.get()), Save(_, IsEmpty()))
+  EXPECT_CALL(MockFormSaver::Get(clone.get()), Save(_, IsEmpty(), _))
       .WillOnce(SaveArg<0>(&passed));
   clone->Save();
   // The date is expected to be different. Reset it so that we can easily
@@ -3872,7 +3871,7 @@
   form_manager.reset();
 
   PasswordForm passed;
-  EXPECT_CALL(MockFormSaver::Get(clone.get()), Save(_, IsEmpty()))
+  EXPECT_CALL(MockFormSaver::Get(clone.get()), Save(_, IsEmpty(), _))
       .WillOnce(SaveArg<0>(&passed));
   clone->Save();
   // The date is expected to be different. Reset it so that we can easily
@@ -4572,7 +4571,7 @@
     form_manager()->ProvisionallySave(credentials);
 
     PasswordForm saved_result;
-    EXPECT_CALL(MockFormSaver::Get(form_manager()), Save(_, _));
+    EXPECT_CALL(MockFormSaver::Get(form_manager()), Save(_, _, _));
     base::HistogramTester histogram_tester;
     form_manager()->Save();
     histogram_tester.ExpectUniqueSample(
diff --git a/components/password_manager/core/browser/password_generation_state.cc b/components/password_manager/core/browser/password_generation_state.cc
index 7f3963c5..6dae836 100644
--- a/components/password_manager/core/browser/password_generation_state.cc
+++ b/components/password_manager/core/browser/password_generation_state.cc
@@ -31,7 +31,8 @@
                         nullptr /* credentials_to_update */,
                         &presaved_.value() /* old_primary_key */);
   } else {
-    form_saver_->Save(generated, {} /* best_matches */);
+    form_saver_->Save(generated, {} /* matches */,
+                      base::string16() /* old_password */);
   }
   presaved_ = std::move(generated);
 }
diff --git a/components/password_manager/core/browser/password_manager_util.cc b/components/password_manager/core/browser/password_manager_util.cc
index 6b3d9084..db8c51d2 100644
--- a/components/password_manager/core/browser/password_manager_util.cc
+++ b/components/password_manager/core/browser/password_manager_util.cc
@@ -247,4 +247,51 @@
   *preferred_match = *matches.begin();
 }
 
+const PasswordForm* GetMatchForUpdating(
+    const PasswordForm& submitted_form,
+    const std::map<base::string16, const PasswordForm*>& credentials) {
+  // This is the case for the credential management API. It should not depend on
+  // form managers. Once that's the case, this should be turned into a DCHECK.
+  // TODO(crbug/947030): turn it into a DCHECK.
+  if (!submitted_form.federation_origin.opaque())
+    return nullptr;
+
+  // Try to return form with matching |username_value|.
+  auto it = credentials.find(submitted_form.username_value);
+  if (it != credentials.end()) {
+    if (!it->second->is_public_suffix_match)
+      return it->second;
+
+    const auto& password_to_save = submitted_form.new_password_value.empty()
+                                       ? submitted_form.password_value
+                                       : submitted_form.new_password_value;
+    // Normally, the copy of the PSL matched credentials, adapted for the
+    // current domain, is saved automatically without asking the user, because
+    // the copy likely represents the same account, i.e., the one for which
+    // the user already agreed to store a password.
+    //
+    // However, if the user changes the suggested password, it might indicate
+    // that the autofilled credentials and |submitted_password_form|
+    // actually correspond to two different accounts (see
+    // http://crbug.com/385619).
+    return password_to_save == it->second->password_value ? it->second
+                                                          : nullptr;
+  }
+
+  // Next attempt is to find a match by password value. It should not be tried
+  // when the username was actually detected.
+  if (submitted_form.type == PasswordForm::TYPE_API ||
+      !submitted_form.username_value.empty())
+    return nullptr;
+
+  for (const auto& stored_match : credentials) {
+    if (stored_match.second->password_value == submitted_form.password_value)
+      return stored_match.second;
+  }
+
+  // Last try. The submitted form had no username but a password. Assume that
+  // it's an existing credential.
+  return credentials.empty() ? nullptr : credentials.begin()->second;
+}
+
 }  // namespace password_manager_util
diff --git a/components/password_manager/core/browser/password_manager_util.h b/components/password_manager/core/browser/password_manager_util.h
index a2d96579a..9f4331e 100644
--- a/components/password_manager/core/browser/password_manager_util.h
+++ b/components/password_manager/core/browser/password_manager_util.h
@@ -125,6 +125,18 @@
     std::vector<const autofill::PasswordForm*>* not_best_matches,
     const autofill::PasswordForm** preferred_match);
 
+// If the user submits a form, they may have used existing credentials, new
+// credentials, or modified existing credentials that should be updated.
+// The function returns a form from |credentials| that is the best candidate to
+// use for an update. Returned value is NULL if |submitted_form| looks like a
+// new credential for the site to be saved.
+// |submitted_form| is the form being submitted.
+// |credentials| are all the credentials relevant for the current site including
+// PSL and Android matches.
+const autofill::PasswordForm* GetMatchForUpdating(
+    const autofill::PasswordForm& submitted_form,
+    const std::map<base::string16, const autofill::PasswordForm*>& credentials);
+
 }  // namespace password_manager_util
 
 #endif  // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_PASSWORD_MANAGER_UTIL_H_
diff --git a/components/password_manager/core/browser/password_manager_util_unittest.cc b/components/password_manager/core/browser/password_manager_util_unittest.cc
index 45ed1d1..6280fa6 100644
--- a/components/password_manager/core/browser/password_manager_util_unittest.cc
+++ b/components/password_manager/core/browser/password_manager_util_unittest.cc
@@ -30,6 +30,7 @@
 
 constexpr char kTestAndroidRealm[] = "android://hash@com.example.beta.android";
 constexpr char kTestFederationURL[] = "https://google.com/";
+constexpr char kTestURL[] = "https://example.com/login/";
 constexpr char kTestUsername[] = "Username";
 constexpr char kTestUsername2[] = "Username2";
 constexpr char kTestPassword[] = "12345";
@@ -43,6 +44,26 @@
   return form;
 }
 
+autofill::PasswordForm GetTestCredential() {
+  autofill::PasswordForm form;
+  form.scheme = autofill::PasswordForm::SCHEME_HTML;
+  form.origin = GURL(kTestURL);
+  form.signon_realm = form.origin.GetOrigin().spec();
+  form.username_value = base::ASCIIToUTF16(kTestUsername);
+  form.password_value = base::ASCIIToUTF16(kTestPassword);
+  return form;
+}
+
+std::map<base::string16, const autofill::PasswordForm*> MapFromCredentials(
+    const std::vector<const autofill::PasswordForm*>& forms) {
+  std::map<base::string16, const autofill::PasswordForm*> result;
+  for (const autofill::PasswordForm* form : forms) {
+    auto inserted = result.emplace(form->username_value, form);
+    EXPECT_TRUE(inserted.second);
+  }
+  return result;
+}
+
 }  // namespace
 
 using password_manager::UnorderedPasswordFormElementsAre;
@@ -210,4 +231,126 @@
   }
 }
 
+TEST(PasswordManagerUtil, GetMatchForUpdating_MatchUsername) {
+  autofill::PasswordForm stored = GetTestCredential();
+  autofill::PasswordForm parsed = GetTestCredential();
+  parsed.password_value = base::ASCIIToUTF16("new_password");
+
+  EXPECT_EQ(&stored,
+            GetMatchForUpdating(parsed, MapFromCredentials({&stored})));
+}
+
+TEST(PasswordManagerUtil, GetMatchForUpdating_RejectUnknownUsername) {
+  autofill::PasswordForm stored = GetTestCredential();
+  autofill::PasswordForm parsed = GetTestCredential();
+  parsed.username_value = base::ASCIIToUTF16("other_username");
+
+  EXPECT_EQ(nullptr,
+            GetMatchForUpdating(parsed, MapFromCredentials({&stored})));
+}
+
+TEST(PasswordManagerUtil, GetMatchForUpdating_FederatedCredential) {
+  autofill::PasswordForm stored = GetTestCredential();
+  autofill::PasswordForm parsed = GetTestCredential();
+  parsed.password_value.clear();
+  parsed.federation_origin = url::Origin::Create(GURL(kTestFederationURL));
+
+  EXPECT_EQ(nullptr,
+            GetMatchForUpdating(parsed, MapFromCredentials({&stored})));
+}
+
+TEST(PasswordManagerUtil, GetMatchForUpdating_MatchUsernamePSL) {
+  autofill::PasswordForm stored = GetTestCredential();
+  stored.is_public_suffix_match = true;
+  autofill::PasswordForm parsed = GetTestCredential();
+
+  EXPECT_EQ(&stored,
+            GetMatchForUpdating(parsed, MapFromCredentials({&stored})));
+}
+
+TEST(PasswordManagerUtil, GetMatchForUpdating_MatchUsernamePSLAnotherPassword) {
+  autofill::PasswordForm stored = GetTestCredential();
+  stored.is_public_suffix_match = true;
+  autofill::PasswordForm parsed = GetTestCredential();
+  parsed.password_value = base::ASCIIToUTF16("new_password");
+
+  EXPECT_EQ(nullptr,
+            GetMatchForUpdating(parsed, MapFromCredentials({&stored})));
+}
+
+TEST(PasswordManagerUtil,
+     GetMatchForUpdating_MatchUsernamePSLNewPasswordKnown) {
+  autofill::PasswordForm stored = GetTestCredential();
+  stored.is_public_suffix_match = true;
+  autofill::PasswordForm parsed = GetTestCredential();
+  parsed.new_password_value = parsed.password_value;
+  parsed.password_value.clear();
+
+  EXPECT_EQ(&stored,
+            GetMatchForUpdating(parsed, MapFromCredentials({&stored})));
+}
+
+TEST(PasswordManagerUtil,
+     GetMatchForUpdating_MatchUsernamePSLNewPasswordUnknown) {
+  autofill::PasswordForm stored = GetTestCredential();
+  stored.is_public_suffix_match = true;
+  autofill::PasswordForm parsed = GetTestCredential();
+  parsed.new_password_value = base::ASCIIToUTF16("new_password");
+  parsed.password_value.clear();
+
+  EXPECT_EQ(nullptr,
+            GetMatchForUpdating(parsed, MapFromCredentials({&stored})));
+}
+
+TEST(PasswordManagerUtil, GetMatchForUpdating_EmptyUsernameFindByPassword) {
+  autofill::PasswordForm stored = GetTestCredential();
+  autofill::PasswordForm parsed = GetTestCredential();
+  parsed.username_value.clear();
+
+  EXPECT_EQ(&stored,
+            GetMatchForUpdating(parsed, MapFromCredentials({&stored})));
+}
+
+TEST(PasswordManagerUtil, GetMatchForUpdating_EmptyUsernameFindByPasswordPSL) {
+  autofill::PasswordForm stored = GetTestCredential();
+  stored.is_public_suffix_match = true;
+  autofill::PasswordForm parsed = GetTestCredential();
+  parsed.username_value.clear();
+
+  EXPECT_EQ(&stored,
+            GetMatchForUpdating(parsed, MapFromCredentials({&stored})));
+}
+
+TEST(PasswordManagerUtil, GetMatchForUpdating_EmptyUsernameCMAPI) {
+  autofill::PasswordForm stored = GetTestCredential();
+  autofill::PasswordForm parsed = GetTestCredential();
+  parsed.username_value.clear();
+  parsed.type = PasswordForm::TYPE_API;
+
+  // In case of the Credential Management API we know for sure that the site
+  // meant empty username. Don't try any other heuristics.
+  EXPECT_EQ(nullptr,
+            GetMatchForUpdating(parsed, MapFromCredentials({&stored})));
+}
+
+TEST(PasswordManagerUtil, GetMatchForUpdating_EmptyUsernamePickFirst) {
+  autofill::PasswordForm stored1 = GetTestCredential();
+  stored1.username_value = base::ASCIIToUTF16("Adam");
+  stored1.password_value = base::ASCIIToUTF16("Adam_password");
+  autofill::PasswordForm stored2 = GetTestCredential();
+  stored2.username_value = base::ASCIIToUTF16("Ben");
+  stored2.password_value = base::ASCIIToUTF16("Ben_password");
+  autofill::PasswordForm stored3 = GetTestCredential();
+  stored3.username_value = base::ASCIIToUTF16("Cindy");
+  stored3.password_value = base::ASCIIToUTF16("Cindy_password");
+
+  autofill::PasswordForm parsed = GetTestCredential();
+  parsed.username_value.clear();
+
+  // The credential with the first username is picked.
+  EXPECT_EQ(&stored1,
+            GetMatchForUpdating(
+                parsed, MapFromCredentials({&stored3, &stored2, &stored1})));
+}
+
 }  // namespace password_manager_util
diff --git a/components/password_manager/core/browser/stub_form_saver.h b/components/password_manager/core/browser/stub_form_saver.h
index d3cf6b2..87ca96d 100644
--- a/components/password_manager/core/browser/stub_form_saver.h
+++ b/components/password_manager/core/browser/stub_form_saver.h
@@ -20,8 +20,8 @@
   // FormSaver:
   void PermanentlyBlacklist(autofill::PasswordForm* observed) override {}
   void Save(const autofill::PasswordForm& pending,
-            const std::map<base::string16, const autofill::PasswordForm*>&
-                best_matches) override {}
+            const std::vector<const autofill::PasswordForm*>& matches,
+            const base::string16& old_password) override {}
   void Update(const autofill::PasswordForm& pending,
               const std::map<base::string16, const autofill::PasswordForm*>&
                   best_matches,
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 1128437..bdc6d624 100644
--- a/components/password_manager/core/browser/sync/password_sync_bridge.cc
+++ b/components/password_manager/core/browser/sync/password_sync_bridge.cc
@@ -396,10 +396,8 @@
                 remote_entity_change, /*sync_time=*/time_now));
         DCHECK_LE(changes.size(), 1U);
         if (changes.empty()) {
-          metrics_util::LogPasswordSyncState(
-              metrics_util::NOT_SYNCING_FAILED_UPDATE);
-          return syncer::ModelError(
-              FROM_HERE, "Failed to update an entry in the password store.");
+          // TODO(mamir): introduce error detection.
+          continue;
         }
         DCHECK(changes[0].primary_key() == primary_key);
         password_store_changes.push_back(changes[0]);
@@ -436,10 +434,8 @@
       // DCHECK_LE(changes.size(), 1U);
       DCHECK_LE(changes.size(), 2U);
       if (changes.empty()) {
-        metrics_util::LogPasswordSyncState(
-            metrics_util::NOT_SYNCING_FAILED_ADD);
-        return syncer::ModelError(
-            FROM_HERE, "Failed to add an entry in the password store.");
+        // TODO(mamir): introduce error detection.
+        continue;
       }
 
       if (changes.size() == 1) {
@@ -515,8 +511,8 @@
           // and the last one should be the one representing the actual addition
           // in the DB.
           if (changes.empty()) {
-            return syncer::ModelError(
-                FROM_HERE, "Failed to add an entry to the password store.");
+            // TODO(mamir): introduce error detection.
+            continue;
           }
           // TODO(crbug.com/939302): It's not yet clear if the DCHECK_LE below
           // is legit. However, recent crashes suggest that 2 changes are
@@ -543,8 +539,8 @@
           changes = password_store_sync_->UpdateLoginSync(
               PasswordFromEntityChange(entity_change, /*sync_time=*/time_now));
           if (changes.empty()) {
-            return syncer::ModelError(
-                FROM_HERE, "Failed to update an entry in the password store.");
+            // TODO(mamir): introduce error detection.
+            continue;
           }
           DCHECK_EQ(1U, changes.size());
           DCHECK(changes[0].primary_key() ==
diff --git a/components/password_manager/core/browser/sync/password_sync_bridge_unittest.cc b/components/password_manager/core/browser/sync/password_sync_bridge_unittest.cc
index 959b5e5..ec5bb41 100644
--- a/components/password_manager/core/browser/sync/password_sync_bridge_unittest.cc
+++ b/components/password_manager/core/browser/sync/password_sync_bridge_unittest.cc
@@ -637,23 +637,6 @@
   EXPECT_TRUE(error);
 }
 
-// This tests that if adding logins to the store fails,
-// ShouldMergeSync() would return an error without crashing.
-TEST_F(PasswordSyncBridgeTest,
-       ShouldMergeSyncRemoteAndLocalPasswordsWithErrorWhenStoreAddFails) {
-  // Simulate a failed AddLoginSync() by returning an empty change list.
-  ON_CALL(*mock_password_store_sync(), AddLoginSync(_))
-      .WillByDefault(testing::Return(PasswordStoreChangeList()));
-
-  EXPECT_CALL(*mock_password_store_sync(), RollbackTransaction());
-  base::Optional<syncer::ModelError> error = bridge()->MergeSyncData(
-      bridge()->CreateMetadataChangeList(),
-      {syncer::EntityChange::CreateAdd(
-          /*storage_key=*/"",
-          SpecificsToEntity(CreateSpecificsWithSignonRealm(kSignonRealm1)))});
-  EXPECT_TRUE(error);
-}
-
 // This tests that if storing model type state fails,
 // ShouldMergeSync() would return an error without crashing.
 TEST_F(
diff --git a/components/password_manager/ios/password_form_helper.mm b/components/password_manager/ios/password_form_helper.mm
index 92cca5d..77d92a7 100644
--- a/components/password_manager/ios/password_form_helper.mm
+++ b/components/password_manager/ios/password_form_helper.mm
@@ -406,14 +406,12 @@
             const std::vector<autofill::PasswordForm>& forms) {
     PasswordFormHelper* strongSelf = weakSelf;
     for (const auto& form : forms) {
-      autofill::PasswordFormFillData formData;
       std::map<base::string16, const autofill::PasswordForm*> matches;
-      // Initialize |matches| to satisfy the expectation from
-      // InitPasswordFormFillData() that the preferred match (3rd parameter)
-      // should be one of the |matches|.
+      // Initialize |matches| to satisfy the expectation from the constructor
+      // that the preferred match (3rd parameter) should be one of the
+      // |matches|.
       matches.insert(std::make_pair(form.username_value, &form));
-      autofill::InitPasswordFormFillData(form, matches, &form, false,
-                                         &formData);
+      autofill::PasswordFormFillData formData(form, matches, form, false);
       [strongSelf fillPasswordForm:formData
                       withUsername:base::SysNSStringToUTF16(username)
                           password:base::SysNSStringToUTF16(password)
diff --git a/components/policy/BUILD.gn b/components/policy/BUILD.gn
index 2142d27f..a791d14 100644
--- a/components/policy/BUILD.gn
+++ b/components/policy/BUILD.gn
@@ -178,7 +178,7 @@
   chrome_version_abspath = "//chrome/VERSION"
   chrome_version_path = rebase_path(chrome_version_abspath, root_build_dir)
   deps = [
-    ":translate_policy_templates",
+    ":translate_policy_templates_grit",
   ]
   inputs = [ chrome_version_abspath ] + policy_templates_translation_outputs
   outputs = []
diff --git a/components/policy/resources/policy_templates.json b/components/policy/resources/policy_templates.json
index 65423ae1..fcfb2c0 100644
--- a/components/policy/resources/policy_templates.json
+++ b/components/policy/resources/policy_templates.json
@@ -3772,9 +3772,9 @@
       'tags': [],
       'desc': '''Enables merging of the extension install list policies <ph name="EXTENSION_INSTALL_BLACKLIST_POLICY_NAME">ExtensionInstallBlacklist</ph>, <ph name="EXTENSION_INSTALL_WHITELIST_POLICY_NAME">ExtensionInstallWhitelist</ph> and <ph name="EXTENSION_INSTALL_FORCELIST_POLICY_NAME">ExtensionInstallForcelist</ph>.
 
-      If you enable this setting, the values from machine platform policy, machine cloud policy and user platfrom policy are merged into a single list and used as a whole instead of only using the values from the single source with highest priority.
+      If you enable this setting, the values from machine platform policy, machine cloud policy and user platform policy are merged into a single list and used as a whole instead of only using the values from the single source with highest priority.
 
-      If you disable this setting or leave it unset, only list entries from the highest priority source are taken and all other sources are shown as conflicts but ingoner.''',
+      If you disable this setting or leave it unset, only list entries from the highest priority source are taken and all other sources are shown as conflicts but ignored.''',
     },
     {
       'name': 'ShowHomeButton',
diff --git a/components/resources/default_300_percent/google_chrome b/components/resources/default_300_percent/google_chrome
new file mode 160000
index 0000000..8f1d67a
--- /dev/null
+++ b/components/resources/default_300_percent/google_chrome
@@ -0,0 +1 @@
+Subproject commit 8f1d67a398043566a35fe63a02e4d7834e4af1b4
diff --git a/components/signin/core/browser/BUILD.gn b/components/signin/core/browser/BUILD.gn
index ab58dfb..61a9dab1 100644
--- a/components/signin/core/browser/BUILD.gn
+++ b/components/signin/core/browser/BUILD.gn
@@ -134,6 +134,8 @@
     "account_reconcilor_delegate.h",
     "chrome_connected_header_helper.cc",
     "chrome_connected_header_helper.h",
+    "consistency_cookie_manager_android.cc",
+    "consistency_cookie_manager_android.h",
     "cookie_settings_util.cc",
     "cookie_settings_util.h",
     "dice_account_reconcilor_delegate.cc",
diff --git a/components/signin/core/browser/account_reconcilor.cc b/components/signin/core/browser/account_reconcilor.cc
index 03144f3..f67da3c 100644
--- a/components/signin/core/browser/account_reconcilor.cc
+++ b/components/signin/core/browser/account_reconcilor.cc
@@ -32,6 +32,10 @@
 #include "services/identity/public/cpp/accounts_in_cookie_jar_info.h"
 #include "services/identity/public/cpp/accounts_mutator.h"
 
+#if defined(OS_ANDROID)
+#include "components/signin/core/browser/consistency_cookie_manager_android.h"
+#endif
+
 using signin::AccountReconcilorDelegate;
 using signin_metrics::AccountReconcilorState;
 
@@ -228,6 +232,16 @@
     if (start_reconcile_if_tokens_available && IsIdentityManagerReady())
       StartReconcile();
   }
+
+#if defined(OS_ANDROID)
+  // The ConsistencyCookieManager is not created earlier, because it requires
+  // the reconcilor state to be initialized.
+  if (base::FeatureList::IsEnabled(signin::kMiceFeature)) {
+    consistency_cookie_manager_ =
+        std::make_unique<signin::ConsistencyCookieManagerAndroid>(client_,
+                                                                  this);
+  }
+#endif
 }
 
 #if defined(OS_IOS)
@@ -237,6 +251,7 @@
 #endif  // defined(OS_IOS)
 
 void AccountReconcilor::EnableReconcile() {
+  SetState(AccountReconcilorState::ACCOUNT_RECONCILOR_SCHEDULED);
   RegisterWithAllDependencies();
 #if !defined(OS_IOS)
   // TODO(droger): Investigate why this breaks tests on iOS.
@@ -247,6 +262,7 @@
 
 void AccountReconcilor::DisableReconcile(bool logout_all_accounts) {
   AbortReconcile();
+  SetState(AccountReconcilorState::ACCOUNT_RECONCILOR_OK);
   UnregisterWithAllDependencies();
 
   if (logout_all_accounts)
diff --git a/components/signin/core/browser/account_reconcilor.h b/components/signin/core/browser/account_reconcilor.h
index f757c53..6476c1a 100644
--- a/components/signin/core/browser/account_reconcilor.h
+++ b/components/signin/core/browser/account_reconcilor.h
@@ -34,6 +34,10 @@
 
 namespace signin {
 class AccountReconcilorDelegate;
+
+#if defined(OS_ANDROID)
+class ConsistencyCookieManagerAndroid;
+#endif
 }
 
 class SigninClient;
@@ -389,6 +393,11 @@
   bool is_wkhttp_system_cookie_store_enabled_ = false;
 #endif  // defined(OS_IOS)
 
+#if defined(OS_ANDROID)
+  std::unique_ptr<signin::ConsistencyCookieManagerAndroid>
+      consistency_cookie_manager_;
+#endif
+
   base::WeakPtrFactory<AccountReconcilor> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(AccountReconcilor);
diff --git a/components/signin/core/browser/consistency_cookie_manager_android.cc b/components/signin/core/browser/consistency_cookie_manager_android.cc
new file mode 100644
index 0000000..65bd69d3f
--- /dev/null
+++ b/components/signin/core/browser/consistency_cookie_manager_android.cc
@@ -0,0 +1,37 @@
+// 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/signin/core/browser/consistency_cookie_manager_android.h"
+
+#include "base/logging.h"
+
+namespace signin {
+
+ConsistencyCookieManagerAndroid::ConsistencyCookieManagerAndroid(
+    SigninClient* signin_client,
+    AccountReconcilor* reconcilor)
+    : account_reconcilor_state_(reconcilor->GetState()),
+      signin_client_(signin_client),
+      account_reconcilor_observer_(this) {
+  DCHECK(signin_client_);
+  DCHECK(reconcilor);
+  account_reconcilor_observer_.Add(reconcilor);
+  UpdateCookie();
+}
+
+ConsistencyCookieManagerAndroid::~ConsistencyCookieManagerAndroid() = default;
+
+void ConsistencyCookieManagerAndroid::OnStateChanged(
+    signin_metrics::AccountReconcilorState state) {
+  if (state == account_reconcilor_state_)
+    return;
+  account_reconcilor_state_ = state;
+  UpdateCookie();
+}
+
+void ConsistencyCookieManagerAndroid::UpdateCookie() {
+  // TODO(droger): update the cookie.
+}
+
+}  // namespace signin
diff --git a/components/signin/core/browser/consistency_cookie_manager_android.h b/components/signin/core/browser/consistency_cookie_manager_android.h
new file mode 100644
index 0000000..265d5595
--- /dev/null
+++ b/components/signin/core/browser/consistency_cookie_manager_android.h
@@ -0,0 +1,50 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_SIGNIN_CORE_BROWSER_CONSISTENCY_COOKIE_MANAGER_ANDROID_H_
+#define COMPONENTS_SIGNIN_CORE_BROWSER_CONSISTENCY_COOKIE_MANAGER_ANDROID_H_
+
+#include "base/macros.h"
+#include "base/scoped_observer.h"
+#include "components/signin/core/browser/account_reconcilor.h"
+#include "components/signin/core/browser/signin_metrics.h"
+
+class SigninClient;
+
+namespace signin {
+
+// The ConsistencyCookieManagerAndroid checks if:
+// - the account reconcilor is running
+// - the accounts on the device are updating
+// - the user has started to interact with device account settings (from Chrome)
+// If one of these conditions is true, then this object sets a cookie on Gaia
+// with a "Updating" value.
+//
+// Otherwise the value of the cookie is "Consistent" if the accounts are
+// consistent (web accounts match device accounts) or "Inconsistent".
+class ConsistencyCookieManagerAndroid : public AccountReconcilor::Observer {
+ public:
+  ConsistencyCookieManagerAndroid(SigninClient* signin_client,
+                                  AccountReconcilor* reconcilor);
+
+  ~ConsistencyCookieManagerAndroid() override;
+
+ private:
+  // AccountReconcilor::Observer:
+  void OnStateChanged(signin_metrics::AccountReconcilorState state) override;
+
+  void UpdateCookie();
+
+  signin_metrics::AccountReconcilorState account_reconcilor_state_ =
+      signin_metrics::ACCOUNT_RECONCILOR_OK;
+  SigninClient* signin_client_ = nullptr;
+  ScopedObserver<AccountReconcilor, AccountReconcilor::Observer>
+      account_reconcilor_observer_;
+
+  DISALLOW_COPY_AND_ASSIGN(ConsistencyCookieManagerAndroid);
+};
+
+}  // namespace signin
+
+#endif  // COMPONENTS_SIGNIN_CORE_BROWSER_CONSISTENCY_COOKIE_MANAGER_ANDROID_H_
diff --git a/components/sync/BUILD.gn b/components/sync/BUILD.gn
index 78a0158..60db3b1 100644
--- a/components/sync/BUILD.gn
+++ b/components/sync/BUILD.gn
@@ -13,7 +13,7 @@
   sync_user_agent_product = "Chrome"
 }
 
-jumbo_static_library("sync") {
+jumbo_static_library("base") {
   sources = [
     "base/bind_to_task_runner.h",
     "base/cancelation_observer.cc",
@@ -86,6 +86,35 @@
     "base/unrecoverable_error_info.h",
     "base/weak_handle.cc",
     "base/weak_handle.h",
+  ]
+
+  configs += [ "//build/config:precompiled_headers" ]
+
+  public_deps = [
+    "//base",
+    "//components/invalidation/public",
+    "//components/sync/protocol",
+  ]
+  deps = [
+    "//base:i18n",
+    "//components/os_crypt",
+    "//components/pref_registry",
+    "//components/prefs",
+    "//components/reading_list/features:reading_list_buildflags",
+    "//components/version_info",
+    "//crypto",
+    "//net",
+    "//third_party/zlib",
+  ]
+
+  if (is_chromeos) {
+    # Required by get_session_name.cc on Chrome OS.
+    deps += [ "//chromeos/constants" ]
+  }
+}
+
+jumbo_static_library("sync") {
+  sources = [
     "device_info/device_count_metrics_provider.cc",
     "device_info/device_count_metrics_provider.h",
     "device_info/device_info.cc",
@@ -391,13 +420,6 @@
     "engine_impl/update_handler.h",
     "engine_impl/uss_migrator.cc",
     "engine_impl/uss_migrator.h",
-    "js/js_backend.h",
-    "js/js_controller.h",
-    "js/js_event_details.cc",
-    "js/js_event_details.h",
-    "js/js_event_handler.h",
-    "js/sync_js_controller.cc",
-    "js/sync_js_controller.h",
     "model/blocking_model_type_store.h",
     "model/change_processor.cc",
     "model/change_processor.h",
@@ -559,22 +581,13 @@
     "syncable/write_transaction.h",
     "syncable/write_transaction_info.cc",
     "syncable/write_transaction_info.h",
-    "user_events/fake_user_event_service.cc",
-    "user_events/fake_user_event_service.h",
-    "user_events/no_op_user_event_service.cc",
-    "user_events/no_op_user_event_service.h",
-    "user_events/user_event_model_type_controller.cc",
-    "user_events/user_event_model_type_controller.h",
-    "user_events/user_event_service.h",
-    "user_events/user_event_service_impl.cc",
-    "user_events/user_event_service_impl.h",
-    "user_events/user_event_sync_bridge.cc",
-    "user_events/user_event_sync_bridge.h",
   ]
 
   configs += [ "//build/config:precompiled_headers" ]
 
   public_deps = [
+    ":base",
+    ":js",
     "//base",
     "//components/invalidation/public",
     "//components/sync/protocol",
@@ -588,22 +601,17 @@
     "//components/invalidation/impl:feature_list",
     "//components/keyed_service/core",
     "//components/metrics",
-    "//components/os_crypt",
-    "//components/pref_registry",
     "//components/prefs",
-    "//components/reading_list/features:reading_list_buildflags",
     "//components/signin/core/browser",
     "//components/variations",
     "//components/version_info",
     "//components/version_info:generate_version_info",
-    "//crypto",
     "//google_apis",
     "//services/identity/public/cpp",
     "//services/network/public/cpp",
     "//sql",
     "//third_party/cacheinvalidation",
     "//third_party/crc32c",
-    "//third_party/zlib",
     "//third_party/zlib/google:compression_utils",
     "//ui/base",
   ]
@@ -613,11 +621,6 @@
     sources += [ "android/model_type_helper.cc" ]
   }
 
-  if (is_chromeos) {
-    # Required by get_session_name.cc on Chrome OS.
-    deps += [ "//chromeos/constants" ]
-  }
-
   if (is_mac) {
     libs = [
       "CoreFoundation.framework",
@@ -641,6 +644,49 @@
   configs += [ "//build/config/compiler:wexit_time_destructors" ]
 }
 
+jumbo_static_library("js") {
+  sources = [
+    "js/js_backend.h",
+    "js/js_controller.h",
+    "js/js_event_details.cc",
+    "js/js_event_details.h",
+    "js/js_event_handler.h",
+    "js/sync_js_controller.cc",
+    "js/sync_js_controller.h",
+  ]
+
+  public_deps = [
+    ":base",
+  ]
+}
+
+jumbo_static_library("user_events") {
+  sources = [
+    "user_events/fake_user_event_service.cc",
+    "user_events/fake_user_event_service.h",
+    "user_events/no_op_user_event_service.cc",
+    "user_events/no_op_user_event_service.h",
+    "user_events/user_event_model_type_controller.cc",
+    "user_events/user_event_model_type_controller.h",
+    "user_events/user_event_service.h",
+    "user_events/user_event_service_impl.cc",
+    "user_events/user_event_service_impl.h",
+    "user_events/user_event_sync_bridge.cc",
+    "user_events/user_event_sync_bridge.h",
+  ]
+
+  public_deps = [
+    ":base",
+    ":sync",
+    "//base",
+    "//components/keyed_service/core",
+    "//components/sync/protocol",
+  ]
+  deps = [
+    "//components/signin/core/browser:shared",
+  ]
+}
+
 static_library("test_support_base") {
   testonly = true
   sources = [
@@ -674,7 +720,6 @@
     ":sync",
   ]
 
-  defines = [ "SYNC_TEST" ]
   configs += [ "//build/config/compiler:wexit_time_destructors" ]
 }
 
@@ -745,7 +790,6 @@
     "//services/network:test_support",
   ]
 
-  defines = [ "SYNC_TEST" ]
   configs += [ "//build/config/compiler:wexit_time_destructors" ]
 }
 
@@ -780,8 +824,6 @@
     "model/test_model_type_store_service.h",
   ]
 
-  defines = [ "SYNC_TEST" ]
-
   public_deps = [
     ":test_support_base",
   ]
@@ -982,6 +1024,7 @@
     ":test_support_driver",
     ":test_support_engine",
     ":test_support_model",
+    ":user_events",
     "//base",
     "//base/test:test_support",
     "//components/invalidation/impl",
@@ -1028,8 +1071,6 @@
       "//components/policy/core/browser",
     ]
   }
-
-  defines = [ "SYNC_TEST" ]
 }
 
 static_library("test_support_fake_server") {
diff --git a/components/sync/device_info/device_info_sync_bridge.cc b/components/sync/device_info/device_info_sync_bridge.cc
index ce7788c..87f6a09 100644
--- a/components/sync/device_info/device_info_sync_bridge.cc
+++ b/components/sync/device_info/device_info_sync_bridge.cc
@@ -34,6 +34,8 @@
 using Record = ModelTypeStore::Record;
 using RecordList = ModelTypeStore::RecordList;
 using WriteBatch = ModelTypeStore::WriteBatch;
+using ClientIdToSpecifics =
+    std::map<std::string, std::unique_ptr<sync_pb::DeviceInfoSpecifics>>;
 
 namespace {
 
@@ -81,6 +83,28 @@
   return specifics;
 }
 
+// Parses the content of |record_list| into |*all_data|. The output
+// parameter is first for binding purposes.
+base::Optional<ModelError> ParseSpecificsOnBackendSequence(
+    ClientIdToSpecifics* all_data,
+    std::unique_ptr<ModelTypeStore::RecordList> record_list) {
+  DCHECK(all_data);
+  DCHECK(all_data->empty());
+  DCHECK(record_list);
+
+  for (const Record& r : *record_list) {
+    std::unique_ptr<DeviceInfoSpecifics> specifics =
+        std::make_unique<DeviceInfoSpecifics>();
+    if (!specifics->ParseFromString(r.value)) {
+      return ModelError(FROM_HERE, "Failed to deserialize specifics.");
+    }
+
+    all_data->emplace(specifics->cache_guid(), std::move(specifics));
+  }
+
+  return base::nullopt;
+}
+
 }  // namespace
 
 DeviceInfoSyncBridge::DeviceInfoSyncBridge(
@@ -346,29 +370,28 @@
   }
 
   store_ = std::move(store);
-  store_->ReadAllData(base::BindOnce(&DeviceInfoSyncBridge::OnReadAllData,
-                                     weak_ptr_factory_.GetWeakPtr()));
+
+  auto all_data = std::make_unique<ClientIdToSpecifics>();
+  ClientIdToSpecifics* all_data_copy = all_data.get();
+
+  store_->ReadAllDataAndPreprocess(
+      base::BindOnce(&ParseSpecificsOnBackendSequence,
+                     base::Unretained(all_data_copy)),
+      base::BindOnce(&DeviceInfoSyncBridge::OnReadAllData,
+                     weak_ptr_factory_.GetWeakPtr(), std::move(all_data)));
 }
 
 void DeviceInfoSyncBridge::OnReadAllData(
-    const base::Optional<syncer::ModelError>& error,
-    std::unique_ptr<RecordList> record_list) {
+    std::unique_ptr<ClientIdToSpecifics> all_data,
+    const base::Optional<syncer::ModelError>& error) {
+  DCHECK(all_data);
+
   if (error) {
     change_processor()->ReportError(*error);
     return;
   }
 
-  for (const Record& r : *record_list) {
-    std::unique_ptr<DeviceInfoSpecifics> specifics =
-        std::make_unique<DeviceInfoSpecifics>();
-    if (specifics->ParseFromString(r.value)) {
-      all_data_[specifics->cache_guid()] = std::move(specifics);
-    } else {
-      change_processor()->ReportError(
-          {FROM_HERE, "Failed to deserialize specifics."});
-      return;
-    }
-  }
+  all_data_ = std::move(*all_data);
 
   has_data_loaded_ = true;
   LoadMetadataIfReady();
diff --git a/components/sync/device_info/device_info_sync_bridge.h b/components/sync/device_info/device_info_sync_bridge.h
index 8ec046f..ca54a4f 100644
--- a/components/sync/device_info/device_info_sync_bridge.h
+++ b/components/sync/device_info/device_info_sync_bridge.h
@@ -92,8 +92,8 @@
   // Methods used as callbacks given to DataTypeStore.
   void OnStoreCreated(const base::Optional<syncer::ModelError>& error,
                       std::unique_ptr<ModelTypeStore> store);
-  void OnReadAllData(const base::Optional<syncer::ModelError>& error,
-                     std::unique_ptr<ModelTypeStore::RecordList> record_list);
+  void OnReadAllData(std::unique_ptr<ClientIdToSpecifics> all_data,
+                     const base::Optional<syncer::ModelError>& error);
   void OnReadAllMetadata(const base::Optional<syncer::ModelError>& error,
                          std::unique_ptr<MetadataBatch> metadata_batch);
   void OnCommit(const base::Optional<syncer::ModelError>& error);
diff --git a/components/sync/engine/fake_model_type_processor.cc b/components/sync/engine/fake_model_type_processor.cc
index c6e4346..cb9b3d0 100644
--- a/components/sync/engine/fake_model_type_processor.cc
+++ b/components/sync/engine/fake_model_type_processor.cc
@@ -28,6 +28,6 @@
 
 void FakeModelTypeProcessor::OnUpdateReceived(
     const sync_pb::ModelTypeState& type_state,
-    const UpdateResponseDataList& updates) {}
+    UpdateResponseDataList updates) {}
 
 }  // namespace syncer
diff --git a/components/sync/engine/fake_model_type_processor.h b/components/sync/engine/fake_model_type_processor.h
index 29e3662..57e70cb 100644
--- a/components/sync/engine/fake_model_type_processor.h
+++ b/components/sync/engine/fake_model_type_processor.h
@@ -24,7 +24,7 @@
   void OnCommitCompleted(const sync_pb::ModelTypeState& type_state,
                          const CommitResponseDataList& response_list) override;
   void OnUpdateReceived(const sync_pb::ModelTypeState& type_state,
-                        const UpdateResponseDataList& updates) override;
+                        UpdateResponseDataList updates) override;
 };
 
 }  // namespace syncer
diff --git a/components/sync/engine/model_type_processor.h b/components/sync/engine/model_type_processor.h
index 933a1bd..671888ee 100644
--- a/components/sync/engine/model_type_processor.h
+++ b/components/sync/engine/model_type_processor.h
@@ -47,7 +47,7 @@
   // Informs this object that there are some incoming updates it should
   // handle.
   virtual void OnUpdateReceived(const sync_pb::ModelTypeState& type_state,
-                                const UpdateResponseDataList& updates) = 0;
+                                UpdateResponseDataList updates) = 0;
 };
 
 }  // namespace syncer
diff --git a/components/sync/engine/model_type_processor_proxy.cc b/components/sync/engine/model_type_processor_proxy.cc
index 2956d23..a7c2a76 100644
--- a/components/sync/engine/model_type_processor_proxy.cc
+++ b/components/sync/engine/model_type_processor_proxy.cc
@@ -62,10 +62,10 @@
 
 void ModelTypeProcessorProxy::OnUpdateReceived(
     const sync_pb::ModelTypeState& type_state,
-    const UpdateResponseDataList& updates) {
-  task_runner_->PostTask(FROM_HERE,
-                         base::BindOnce(&ModelTypeProcessor::OnUpdateReceived,
-                                        processor_, type_state, updates));
+    UpdateResponseDataList updates) {
+  task_runner_->PostTask(
+      FROM_HERE, base::BindOnce(&ModelTypeProcessor::OnUpdateReceived,
+                                processor_, type_state, std::move(updates)));
 }
 
 }  // namespace syncer
diff --git a/components/sync/engine/model_type_processor_proxy.h b/components/sync/engine/model_type_processor_proxy.h
index dd36462..aa4cf7e 100644
--- a/components/sync/engine/model_type_processor_proxy.h
+++ b/components/sync/engine/model_type_processor_proxy.h
@@ -28,7 +28,7 @@
   void OnCommitCompleted(const sync_pb::ModelTypeState& type_state,
                          const CommitResponseDataList& response_list) override;
   void OnUpdateReceived(const sync_pb::ModelTypeState& type_state,
-                        const UpdateResponseDataList& updates) override;
+                        UpdateResponseDataList updates) override;
 
  private:
   base::WeakPtr<ModelTypeProcessor> processor_;
diff --git a/components/sync/engine/net/http_bridge.cc b/components/sync/engine/net/http_bridge.cc
index e9a485d..340c531 100644
--- a/components/sync/engine/net/http_bridge.cc
+++ b/components/sync/engine/net/http_bridge.cc
@@ -46,23 +46,6 @@
   UMA_HISTOGRAM_BOOLEAN("Sync.URLFetchTimedOut", timed_out);
 }
 
-void RecordSyncRequestContentLengthHistograms(int64_t compressed_content_length,
-                                              int64_t original_content_length) {
-  UMA_HISTOGRAM_COUNTS_1M("Sync.RequestContentLength.Compressed",
-                          compressed_content_length);
-  UMA_HISTOGRAM_COUNTS_1M("Sync.RequestContentLength.Original",
-                          original_content_length);
-}
-
-void RecordSyncResponseContentLengthHistograms(
-    int64_t compressed_content_length,
-    int64_t original_content_length) {
-  UMA_HISTOGRAM_COUNTS_1M("Sync.ResponseContentLength.Compressed",
-                          compressed_content_length);
-  UMA_HISTOGRAM_COUNTS_1M("Sync.ResponseContentLength.Original",
-                          original_content_length);
-}
-
 base::LazyInstance<scoped_refptr<base::SequencedTaskRunner>>::Leaky
     g_io_capable_task_runner_for_tests = LAZY_INSTANCE_INITIALIZER;
 
@@ -316,8 +299,6 @@
   std::string request_to_send;
   compression::GzipCompress(request_content_, &request_to_send);
   url_loader->AttachStringForUpload(request_to_send, content_type_);
-  RecordSyncRequestContentLengthHistograms(request_to_send.size(),
-                                           request_content_.size());
 
   // Sync relies on HTTP errors being detectable (and distinct from
   // net/connection errors).
@@ -407,15 +388,14 @@
     fetch_state_.response_headers = url_loader->ResponseInfo()->headers;
   }
 
-  OnURLLoadCompleteInternal(
-      http_status_code, url_loader->NetError(), url_loader->GetContentSize(),
-      url_loader->GetFinalURL(), std::move(response_body));
+  OnURLLoadCompleteInternal(http_status_code, url_loader->NetError(),
+                            url_loader->GetFinalURL(),
+                            std::move(response_body));
 }
 
 void HttpBridge::OnURLLoadCompleteInternal(
     int http_status_code,
     int net_error_code,
-    int64_t compressed_content_length,
     const GURL& final_url,
     std::unique_ptr<std::string> response_body) {
   DCHECK(network_task_runner_->RunsTasksInCurrentSequence());
@@ -456,10 +436,6 @@
 
   UpdateNetworkTime();
 
-  int64_t original_content_length = fetch_state_.response_content.size();
-  RecordSyncResponseContentLengthHistograms(compressed_content_length,
-                                            original_content_length);
-
   fetch_state_.url_loader.reset();
   url_loader_factory_ = nullptr;
 
diff --git a/components/sync/engine/net/http_bridge.h b/components/sync/engine/net/http_bridge.h
index 4f7ca49a..81ab8d39 100644
--- a/components/sync/engine/net/http_bridge.h
+++ b/components/sync/engine/net/http_bridge.h
@@ -106,7 +106,6 @@
   // Actual implementation of the load complete callback. Called by tests too.
   void OnURLLoadCompleteInternal(int http_status_code,
                                  int net_error_code,
-                                 int64_t compressed_content_length,
                                  const GURL& final_url,
                                  std::unique_ptr<std::string> response_body);
 
diff --git a/components/sync/engine/net/http_bridge_unittest.cc b/components/sync/engine/net/http_bridge_unittest.cc
index 8276d91f..5cd18c84 100644
--- a/components/sync/engine/net/http_bridge_unittest.cc
+++ b/components/sync/engine/net/http_bridge_unittest.cc
@@ -151,8 +151,7 @@
     ASSERT_TRUE(test_->GetIOThreadTaskRunner()->BelongsToCurrentThread());
 
     // Set up a dummy content response.
-    OnURLLoadCompleteInternal(200, net::OK, 0 /* content length, irrelevant */,
-                              GURL("http://www.google.com"),
+    OnURLLoadCompleteInternal(200, net::OK, GURL("http://www.google.com"),
                               std::make_unique<std::string>("success!"));
   }
   MAYBE_SyncHttpBridgeTest* test_;
diff --git a/components/sync/engine/non_blocking_sync_common.cc b/components/sync/engine/non_blocking_sync_common.cc
index 35dda50..4ae1efa 100644
--- a/components/sync/engine/non_blocking_sync_common.cc
+++ b/components/sync/engine/non_blocking_sync_common.cc
@@ -21,9 +21,6 @@
 
 UpdateResponseData::UpdateResponseData() {}
 
-UpdateResponseData::UpdateResponseData(const UpdateResponseData& other) =
-    default;
-
 UpdateResponseData::~UpdateResponseData() {}
 
 size_t EstimateMemoryUsage(const CommitRequestData& value) {
diff --git a/components/sync/engine/non_blocking_sync_common.h b/components/sync/engine/non_blocking_sync_common.h
index 60b96a5..a0fc0e1 100644
--- a/components/sync/engine/non_blocking_sync_common.h
+++ b/components/sync/engine/non_blocking_sync_common.h
@@ -61,18 +61,20 @@
 
 struct UpdateResponseData {
   UpdateResponseData();
-  UpdateResponseData(const UpdateResponseData& other);
   ~UpdateResponseData();
 
   EntityDataPtr entity;
 
   int64_t response_version = 0;
   std::string encryption_key_name;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(UpdateResponseData);
 };
 
 using CommitRequestDataList = std::vector<std::unique_ptr<CommitRequestData>>;
 using CommitResponseDataList = std::vector<CommitResponseData>;
-using UpdateResponseDataList = std::vector<UpdateResponseData>;
+using UpdateResponseDataList = std::vector<std::unique_ptr<UpdateResponseData>>;
 
 // Returns the estimate of dynamically allocated memory in bytes.
 size_t EstimateMemoryUsage(const CommitRequestData& value);
diff --git a/components/sync/engine_impl/model_type_worker.cc b/components/sync/engine_impl/model_type_worker.cc
index ee668b8..f3032fe 100644
--- a/components/sync/engine_impl/model_type_worker.cc
+++ b/components/sync/engine_impl/model_type_worker.cc
@@ -41,9 +41,10 @@
 
 bool ContainsDuplicateClientTagHash(const UpdateResponseDataList& updates) {
   std::vector<std::string> client_tag_hashes;
-  for (const UpdateResponseData& update : updates) {
-    if (!update.entity->client_tag_hash.empty()) {
-      client_tag_hashes.push_back(update.entity->client_tag_hash);
+  for (const std::unique_ptr<UpdateResponseData>& update : updates) {
+    DCHECK(update);
+    if (!update->entity->client_tag_hash.empty()) {
+      client_tag_hashes.push_back(update->entity->client_tag_hash);
     }
   }
   return ContainsDuplicate(std::move(client_tag_hashes));
@@ -51,8 +52,9 @@
 
 bool ContainsDuplicateServerID(const UpdateResponseDataList& updates) {
   std::vector<std::string> server_ids;
-  for (const UpdateResponseData& update : updates) {
-    server_ids.push_back(update.entity->id);
+  for (const std::unique_ptr<UpdateResponseData>& update : updates) {
+    DCHECK(update);
+    server_ids.push_back(update->entity->id);
   }
   return ContainsDuplicate(std::move(server_ids));
 }
@@ -206,17 +208,18 @@
       }
     }
 
-    UpdateResponseData response_data;
+    auto response_data = std::make_unique<UpdateResponseData>();
     switch (PopulateUpdateResponseData(cryptographer_.get(), *update_entity,
-                                       &response_data)) {
+                                       response_data.get())) {
       case SUCCESS:
-        pending_updates_.push_back(response_data);
-        if (!response_data.entity->client_tag_hash.empty()) {
-          client_tag_hashes.push_back(response_data.entity->client_tag_hash);
+        if (!response_data->entity->client_tag_hash.empty()) {
+          client_tag_hashes.push_back(response_data->entity->client_tag_hash);
         }
+        pending_updates_.push_back(std::move(response_data));
         break;
       case DECRYPTION_PENDING:
-        entries_pending_decryption_[update_entity->id_string()] = response_data;
+        entries_pending_decryption_[update_entity->id_string()] =
+            std::move(response_data);
         break;
       case FAILED_TO_DECRYPT:
         // Failed to decrypt the entity. Likely it is corrupt. Move on.
@@ -437,10 +440,12 @@
           suffix,
       contains_duplicate_client_tag_hashes_after_deduping_server_ids);
 
-  model_type_processor_->OnUpdateReceived(model_type_state_, pending_updates_);
+  int num_updates_applied = pending_updates_.size();
+  model_type_processor_->OnUpdateReceived(model_type_state_,
+                                          std::move(pending_updates_));
 
   UpdateCounters* counters = debug_info_emitter_->GetMutableUpdateCounters();
-  counters->num_updates_applied += pending_updates_.size();
+  counters->num_updates_applied += num_updates_applied;
   debug_info_emitter_->EmitUpdateCountersUpdate();
   debug_info_emitter_->EmitStatusCountersUpdate();
 
@@ -563,7 +568,7 @@
 void ModelTypeWorker::DecryptStoredEntities() {
   for (auto it = entries_pending_decryption_.begin();
        it != entries_pending_decryption_.end();) {
-    const UpdateResponseData& encrypted_update = it->second;
+    const UpdateResponseData& encrypted_update = *it->second;
     EntityDataPtr data = encrypted_update.entity;
 
     sync_pb::EntitySpecifics specifics;
@@ -598,11 +603,11 @@
       }
     }
 
-    UpdateResponseData decrypted_update;
-    decrypted_update.response_version = encrypted_update.response_version;
-    decrypted_update.encryption_key_name = encryption_key_name;
-    decrypted_update.entity = data->UpdateSpecifics(specifics);
-    pending_updates_.push_back(decrypted_update);
+    auto decrypted_update = std::make_unique<UpdateResponseData>();
+    decrypted_update->response_version = encrypted_update.response_version;
+    decrypted_update->encryption_key_name = encryption_key_name;
+    decrypted_update->entity = data->UpdateSpecifics(specifics);
+    pending_updates_.push_back(std::move(decrypted_update));
     it = entries_pending_decryption_.erase(it);
   }
 }
@@ -612,14 +617,15 @@
   pending_updates_.swap(candidates);
 
   std::map<std::string, size_t> id_to_index;
-  for (UpdateResponseData& candidate : candidates) {
-    if (candidate.entity->id.empty()) {
+  for (std::unique_ptr<UpdateResponseData>& candidate : candidates) {
+    DCHECK(candidate);
+    if (candidate->entity->id.empty()) {
       continue;
     }
     // Try to insert. If we already saw an item with the same server id,
     // this will fail but give us its iterator.
     auto it_and_success =
-        id_to_index.emplace(candidate.entity->id, pending_updates_.size());
+        id_to_index.emplace(candidate->entity->id, pending_updates_.size());
     if (it_and_success.second) {
       // New server id, append at the end. Note that we already inserted
       // the correct index (|pending_updates_.size()|) above.
@@ -637,16 +643,17 @@
   pending_updates_.swap(candidates);
 
   std::map<std::string, size_t> tag_to_index;
-  for (UpdateResponseData& candidate : candidates) {
+  for (std::unique_ptr<UpdateResponseData>& candidate : candidates) {
+    DCHECK(candidate);
     // Items with empty client tag hash just get passed through.
-    if (candidate.entity->client_tag_hash.empty()) {
+    if (candidate->entity->client_tag_hash.empty()) {
       pending_updates_.push_back(std::move(candidate));
       continue;
     }
     // Try to insert. If we already saw an item with the same client tag hash,
     // this will fail but give us its iterator.
     auto it_and_success = tag_to_index.emplace(
-        candidate.entity->client_tag_hash, pending_updates_.size());
+        candidate->entity->client_tag_hash, pending_updates_.size());
     if (it_and_success.second) {
       // New client tag hash, append at the end. Note that we already inserted
       // the correct index (|pending_updates_.size()|) above.
diff --git a/components/sync/engine_impl/model_type_worker.h b/components/sync/engine_impl/model_type_worker.h
index 2ecb7d3..18ad52a 100644
--- a/components/sync/engine_impl/model_type_worker.h
+++ b/components/sync/engine_impl/model_type_worker.h
@@ -237,7 +237,8 @@
 
   // A map of update responses, keyed by server_id.
   // Holds updates encrypted with pending keys.
-  std::map<std::string, UpdateResponseData> entries_pending_decryption_;
+  std::map<std::string, std::unique_ptr<UpdateResponseData>>
+      entries_pending_decryption_;
 
   // Accumulates all the updates from a single GetUpdates cycle in memory so
   // they can all be sent to the processor at once.
diff --git a/components/sync/engine_impl/model_type_worker_unittest.cc b/components/sync/engine_impl/model_type_worker_unittest.cc
index 7774c10..c2cf7872 100644
--- a/components/sync/engine_impl/model_type_worker_unittest.cc
+++ b/components/sync/engine_impl/model_type_worker_unittest.cc
@@ -7,6 +7,7 @@
 #include <stdint.h>
 
 #include <utility>
+#include <vector>
 
 #include "base/bind.h"
 #include "base/strings/stringprintf.h"
@@ -723,11 +724,12 @@
   TriggerUpdateFromServer(10, kTag1, kValue1);
 
   ASSERT_EQ(1U, processor()->GetNumUpdateResponses());
-  UpdateResponseDataList updates_list = processor()->GetNthUpdateResponse(0);
+  std::vector<const UpdateResponseData*> updates_list =
+      processor()->GetNthUpdateResponse(0);
   EXPECT_EQ(1U, updates_list.size());
 
   ASSERT_TRUE(processor()->HasUpdateResponse(kHash1));
-  UpdateResponseData update = processor()->GetUpdateResponse(kHash1);
+  const UpdateResponseData& update = processor()->GetUpdateResponse(kHash1);
   const EntityData& entity = update.entity.value();
 
   EXPECT_FALSE(entity.id.empty());
@@ -773,11 +775,15 @@
 
   // Make sure all the updates arrived, in order.
   ASSERT_EQ(1u, processor()->GetNumUpdateResponses());
-  UpdateResponseDataList result = processor()->GetNthUpdateResponse(0);
+  std::vector<const UpdateResponseData*> result =
+      processor()->GetNthUpdateResponse(0);
   ASSERT_EQ(3u, result.size());
-  EXPECT_EQ(GenerateTagHash(kTag1), result[0].entity->client_tag_hash);
-  EXPECT_EQ(GenerateTagHash(kTag2), result[1].entity->client_tag_hash);
-  EXPECT_EQ(GenerateTagHash(kTag3), result[2].entity->client_tag_hash);
+  ASSERT_TRUE(result[0]);
+  EXPECT_EQ(GenerateTagHash(kTag1), result[0]->entity->client_tag_hash);
+  ASSERT_TRUE(result[1]);
+  EXPECT_EQ(GenerateTagHash(kTag2), result[1]->entity->client_tag_hash);
+  ASSERT_TRUE(result[2]);
+  EXPECT_EQ(GenerateTagHash(kTag3), result[2]->entity->client_tag_hash);
 }
 
 TEST_F(ModelTypeWorkerTest, ReceiveUpdates_DuplicateHashWithinPartialUpdate) {
@@ -808,10 +814,12 @@
 
   // Make sure the duplicate entry got de-duped, and the last one won.
   ASSERT_EQ(1u, processor()->GetNumUpdateResponses());
-  UpdateResponseDataList result = processor()->GetNthUpdateResponse(0);
+  std::vector<const UpdateResponseData*> result =
+      processor()->GetNthUpdateResponse(0);
   ASSERT_EQ(1u, result.size());
-  EXPECT_EQ(GenerateTagHash(kTag1), result[0].entity->client_tag_hash);
-  EXPECT_EQ(kValue2, result[0].entity->specifics.preference().value());
+  ASSERT_TRUE(result[0]);
+  EXPECT_EQ(GenerateTagHash(kTag1), result[0]->entity->client_tag_hash);
+  EXPECT_EQ(kValue2, result[0]->entity->specifics.preference().value());
 }
 
 TEST_F(ModelTypeWorkerTest, ReceiveUpdates_DuplicateHashAcrossPartialUpdates) {
@@ -844,10 +852,12 @@
 
   // Make sure the duplicate entry got de-duped, and the last one won.
   ASSERT_EQ(1u, processor()->GetNumUpdateResponses());
-  UpdateResponseDataList result = processor()->GetNthUpdateResponse(0);
+  std::vector<const UpdateResponseData*> result =
+      processor()->GetNthUpdateResponse(0);
   ASSERT_EQ(1u, result.size());
-  EXPECT_EQ(GenerateTagHash(kTag1), result[0].entity->client_tag_hash);
-  EXPECT_EQ(kValue2, result[0].entity->specifics.preference().value());
+  ASSERT_TRUE(result[0]);
+  EXPECT_EQ(GenerateTagHash(kTag1), result[0]->entity->client_tag_hash);
+  EXPECT_EQ(kValue2, result[0]->entity->specifics.preference().value());
 }
 
 TEST_F(ModelTypeWorkerTest,
@@ -892,10 +902,13 @@
 
   // Make sure the empty client tag hashes did *not* get de-duped.
   ASSERT_EQ(1u, processor()->GetNumUpdateResponses());
-  UpdateResponseDataList result = processor()->GetNthUpdateResponse(0);
+  std::vector<const UpdateResponseData*> result =
+      processor()->GetNthUpdateResponse(0);
   ASSERT_EQ(2u, result.size());
-  EXPECT_EQ(entity1.id_string(), result[0].entity->id);
-  EXPECT_EQ(entity2.id_string(), result[1].entity->id);
+  ASSERT_TRUE(result[0]);
+  EXPECT_EQ(entity1.id_string(), result[0]->entity->id);
+  ASSERT_TRUE(result[1]);
+  EXPECT_EQ(entity2.id_string(), result[1]->entity->id);
 }
 
 TEST_F(ModelTypeWorkerTest, ReceiveUpdates_MultipleDuplicateHashes) {
@@ -932,14 +945,18 @@
 
   // Make sure the duplicate entries got de-duped, and the last one won.
   ASSERT_EQ(1u, processor()->GetNumUpdateResponses());
-  UpdateResponseDataList result = processor()->GetNthUpdateResponse(0);
+  std::vector<const UpdateResponseData*> result =
+      processor()->GetNthUpdateResponse(0);
   ASSERT_EQ(3u, result.size());
-  EXPECT_EQ(GenerateTagHash(kTag1), result[0].entity->client_tag_hash);
-  EXPECT_EQ(GenerateTagHash(kTag2), result[1].entity->client_tag_hash);
-  EXPECT_EQ(GenerateTagHash(kTag3), result[2].entity->client_tag_hash);
-  EXPECT_EQ(kValue1, result[0].entity->specifics.preference().value());
-  EXPECT_EQ(kValue2, result[1].entity->specifics.preference().value());
-  EXPECT_EQ(kValue3, result[2].entity->specifics.preference().value());
+  ASSERT_TRUE(result[0]);
+  ASSERT_TRUE(result[1]);
+  ASSERT_TRUE(result[2]);
+  EXPECT_EQ(GenerateTagHash(kTag1), result[0]->entity->client_tag_hash);
+  EXPECT_EQ(GenerateTagHash(kTag2), result[1]->entity->client_tag_hash);
+  EXPECT_EQ(GenerateTagHash(kTag3), result[2]->entity->client_tag_hash);
+  EXPECT_EQ(kValue1, result[0]->entity->specifics.preference().value());
+  EXPECT_EQ(kValue2, result[1]->entity->specifics.preference().value());
+  EXPECT_EQ(kValue3, result[2]->entity->specifics.preference().value());
 }
 
 TEST_F(ModelTypeWorkerTest,
@@ -987,9 +1004,11 @@
 
   // Make sure the first update has been discarded.
   ASSERT_EQ(1u, processor()->GetNumUpdateResponses());
-  UpdateResponseDataList result = processor()->GetNthUpdateResponse(0);
+  std::vector<const UpdateResponseData*> result =
+      processor()->GetNthUpdateResponse(0);
   ASSERT_EQ(1u, result.size());
-  EXPECT_EQ(entity2.id_string(), result[0].entity->id);
+  ASSERT_TRUE(result[0]);
+  EXPECT_EQ(entity2.id_string(), result[0]->entity->id);
 }
 
 // Test that an update download coming in multiple parts gets accumulated into
@@ -1006,17 +1025,21 @@
 
   // Processor received exactly one update with entities in the right order.
   ASSERT_EQ(1U, processor()->GetNumUpdateResponses());
-  UpdateResponseDataList updates = processor()->GetNthUpdateResponse(0);
+  std::vector<const UpdateResponseData*> updates =
+      processor()->GetNthUpdateResponse(0);
   ASSERT_EQ(2U, updates.size());
-  EXPECT_EQ(GenerateTagHash(kTag1), updates[0].entity->client_tag_hash);
-  EXPECT_EQ(GenerateTagHash(kTag2), updates[1].entity->client_tag_hash);
+  ASSERT_TRUE(updates[0]);
+  EXPECT_EQ(GenerateTagHash(kTag1), updates[0]->entity->client_tag_hash);
+  ASSERT_TRUE(updates[1]);
+  EXPECT_EQ(GenerateTagHash(kTag2), updates[1]->entity->client_tag_hash);
 
   // A subsequent update doesn't pass the same entities again.
   TriggerUpdateFromServer(10, kTag3, kValue3);
   ASSERT_EQ(2U, processor()->GetNumUpdateResponses());
   updates = processor()->GetNthUpdateResponse(1);
   ASSERT_EQ(1U, updates.size());
-  EXPECT_EQ(GenerateTagHash(kTag3), updates[0].entity->client_tag_hash);
+  ASSERT_TRUE(updates[0]);
+  EXPECT_EQ(GenerateTagHash(kTag3), updates[0]->entity->client_tag_hash);
 }
 
 // Test that updates with no entities behave correctly.
@@ -1110,7 +1133,8 @@
   // Update local cryptographer, verify everything is pushed to processor.
   DecryptPendingKey();
   ASSERT_EQ(1U, processor()->GetNumUpdateResponses());
-  UpdateResponseDataList updates_list = processor()->GetNthUpdateResponse(0);
+  std::vector<const UpdateResponseData*> updates_list =
+      processor()->GetNthUpdateResponse(0);
   EXPECT_EQ(
       server()->GetProgress().SerializeAsString(),
       processor()->GetNthUpdateState(0).progress_marker().SerializeAsString());
@@ -1161,7 +1185,7 @@
 
   // Test some basic properties regarding the update.
   ASSERT_TRUE(processor()->HasUpdateResponse(kHash1));
-  UpdateResponseData update1 = processor()->GetUpdateResponse(kHash1);
+  const UpdateResponseData& update1 = processor()->GetUpdateResponse(kHash1);
   EXPECT_EQ(kTag1, update1.entity->specifics.preference().name());
   EXPECT_EQ(kValue1, update1.entity->specifics.preference().value());
   EXPECT_TRUE(update1.encryption_key_name.empty());
@@ -1174,7 +1198,7 @@
 
   // Test its basic features and the value of encryption_key_name.
   ASSERT_TRUE(processor()->HasUpdateResponse(kHash2));
-  UpdateResponseData update2 = processor()->GetUpdateResponse(kHash2);
+  const UpdateResponseData& update2 = processor()->GetUpdateResponse(kHash2);
   EXPECT_EQ(kTag2, update2.entity->specifics.preference().name());
   EXPECT_EQ(kValue2, update2.entity->specifics.preference().value());
   EXPECT_FALSE(update2.encryption_key_name.empty());
@@ -1301,7 +1325,7 @@
   DecryptPendingKey();
   EXPECT_EQ(1U, processor()->GetNumUpdateResponses());
   ASSERT_TRUE(processor()->HasUpdateResponse(kHash1));
-  UpdateResponseData update = processor()->GetUpdateResponse(kHash1);
+  const UpdateResponseData& update = processor()->GetUpdateResponse(kHash1);
   EXPECT_EQ(kTag1, update.entity->specifics.preference().name());
   EXPECT_EQ(kValue1, update.entity->specifics.preference().value());
   EXPECT_EQ(GetLocalCryptographerKeyName(), update.encryption_key_name);
@@ -1731,7 +1755,7 @@
 
   // Test its basic features and the value of encryption_key_name.
   ASSERT_TRUE(processor()->HasUpdateResponse(kHash1));
-  UpdateResponseData update = processor()->GetUpdateResponse(kHash1);
+  const UpdateResponseData& update = processor()->GetUpdateResponse(kHash1);
   EXPECT_FALSE(update.entity->specifics.password().has_encrypted());
   EXPECT_FALSE(update.entity->specifics.has_encrypted());
   ASSERT_TRUE(
@@ -1747,7 +1771,7 @@
        ReceiveDecryptablePasswordShouldWaitTillKeyArrives) {
   NormalInitialize();
 
-  // Receive an encrypted password, encrypted with the second ecnryption key.
+  // Receive an encrypted password, encrypted with the second encryption key.
   sync_pb::PasswordSpecificsData unencrypted_password;
   unencrypted_password.set_password_value(kPassword);
   sync_pb::EntitySpecifics encrypted_specifics =
@@ -1807,7 +1831,7 @@
   DecryptPendingKey();
   EXPECT_EQ(1U, processor()->GetNumUpdateResponses());
   ASSERT_TRUE(processor()->HasUpdateResponse(kHash1));
-  UpdateResponseData update = processor()->GetUpdateResponse(kHash1);
+  const UpdateResponseData& update = processor()->GetUpdateResponse(kHash1);
   // Password should now be decrypted and sent to the processor.
   EXPECT_TRUE(update.entity->specifics.has_password());
   EXPECT_FALSE(update.entity->specifics.password().has_encrypted());
diff --git a/components/sync/engine_impl/uss_migrator_unittest.cc b/components/sync/engine_impl/uss_migrator_unittest.cc
index 41f0a129..1470187 100644
--- a/components/sync/engine_impl/uss_migrator_unittest.cc
+++ b/components/sync/engine_impl/uss_migrator_unittest.cc
@@ -7,6 +7,7 @@
 #include <memory>
 #include <string>
 #include <utility>
+#include <vector>
 
 #include "base/test/scoped_task_environment.h"
 #include "base/time/time.h"
@@ -133,12 +134,14 @@
   const sync_pb::ModelTypeState& state = processor()->GetNthUpdateState(0);
   EXPECT_EQ(kToken1, state.progress_marker().token());
 
-  UpdateResponseData update = processor()->GetNthUpdateResponse(0).at(0);
-  const EntityData& entity = update.entity.value();
+  const UpdateResponseData* update =
+      std::move(processor()->GetNthUpdateResponse(0).at(0));
+  ASSERT_TRUE(update);
+  const EntityData& entity = update->entity.value();
 
   EXPECT_FALSE(entity.id.empty());
   EXPECT_EQ(kHash1, entity.client_tag_hash);
-  EXPECT_EQ(1, update.response_version);
+  EXPECT_EQ(1, update->response_version);
   EXPECT_EQ(ctime, entity.creation_time);
   EXPECT_EQ(ctime, entity.modification_time);
   EXPECT_EQ(kTag1, entity.non_unique_name);
@@ -163,10 +166,14 @@
   EXPECT_EQ(3U, processor()->GetNthUpdateResponse(0).size());
   EXPECT_EQ(3, migrated_entity_count);
 
-  UpdateResponseDataList updates = processor()->GetNthUpdateResponse(0);
-  EXPECT_EQ(kTag1, updates.at(0).entity.value().specifics.preference().name());
-  EXPECT_EQ(kTag2, updates.at(1).entity.value().specifics.preference().name());
-  EXPECT_EQ(kTag3, updates.at(2).entity.value().specifics.preference().name());
+  std::vector<const UpdateResponseData*> updates =
+      processor()->GetNthUpdateResponse(0);
+  ASSERT_TRUE(updates.at(0));
+  EXPECT_EQ(kTag1, updates.at(0)->entity.value().specifics.preference().name());
+  ASSERT_TRUE(updates.at(1));
+  EXPECT_EQ(kTag2, updates.at(1)->entity.value().specifics.preference().name());
+  ASSERT_TRUE(updates.at(2));
+  EXPECT_EQ(kTag3, updates.at(2)->entity.value().specifics.preference().name());
 
   const sync_pb::ModelTypeState& state = processor()->GetNthUpdateState(0);
   EXPECT_EQ(kToken1, state.progress_marker().token());
@@ -193,10 +200,14 @@
   EXPECT_EQ(kPreviouslyMigratedEntityCount + 3,
             cumulative_migrated_entity_count);
 
-  UpdateResponseDataList updates = processor()->GetNthUpdateResponse(0);
-  EXPECT_EQ(kTag1, updates.at(0).entity.value().specifics.preference().name());
-  EXPECT_EQ(kTag2, updates.at(1).entity.value().specifics.preference().name());
-  EXPECT_EQ(kTag3, updates.at(2).entity.value().specifics.preference().name());
+  std::vector<const UpdateResponseData*> updates =
+      processor()->GetNthUpdateResponse(0);
+  ASSERT_TRUE(updates.at(0));
+  EXPECT_EQ(kTag1, updates.at(0)->entity.value().specifics.preference().name());
+  ASSERT_TRUE(updates.at(1));
+  EXPECT_EQ(kTag2, updates.at(1)->entity.value().specifics.preference().name());
+  ASSERT_TRUE(updates.at(2));
+  EXPECT_EQ(kTag3, updates.at(2)->entity.value().specifics.preference().name());
 
   const sync_pb::ModelTypeState& state = processor()->GetNthUpdateState(0);
   EXPECT_EQ(kToken1, state.progress_marker().token());
diff --git a/components/sync/model/metadata_batch.cc b/components/sync/model/metadata_batch.cc
index 7645723b..bfdceff 100644
--- a/components/sync/model/metadata_batch.cc
+++ b/components/sync/model/metadata_batch.cc
@@ -29,9 +29,7 @@
 void MetadataBatch::AddMetadata(
     const std::string& storage_key,
     std::unique_ptr<sync_pb::EntityMetadata> metadata) {
-  // TODO(crbug.com/914396): change metadata_map_ type to avoid unnecessary copy
-  // here.
-  metadata_map_.insert(std::make_pair(storage_key, *metadata));
+  metadata_map_.insert(std::make_pair(storage_key, std::move(metadata)));
 }
 
 const sync_pb::ModelTypeState& MetadataBatch::GetModelTypeState() const {
diff --git a/components/sync/model/metadata_batch.h b/components/sync/model/metadata_batch.h
index ee6a650..68f4c1a 100644
--- a/components/sync/model/metadata_batch.h
+++ b/components/sync/model/metadata_batch.h
@@ -15,7 +15,8 @@
 namespace syncer {
 
 // Map of storage keys to EntityMetadata proto.
-using EntityMetadataMap = std::map<std::string, sync_pb::EntityMetadata>;
+using EntityMetadataMap =
+    std::map<std::string, std::unique_ptr<sync_pb::EntityMetadata>>;
 
 // Container used to pass sync metadata from services to their processor.
 class MetadataBatch {
diff --git a/components/sync/model_impl/client_tag_based_model_type_processor.cc b/components/sync/model_impl/client_tag_based_model_type_processor.cc
index 1133fb8..cadbaaf7 100644
--- a/components/sync/model_impl/client_tag_based_model_type_processor.cc
+++ b/components/sync/model_impl/client_tag_based_model_type_processor.cc
@@ -140,8 +140,11 @@
     EntityMetadataMap metadata_map(batch->TakeAllMetadata());
 
     for (auto it = metadata_map.begin(); it != metadata_map.end(); it++) {
+      std::unique_ptr<sync_pb::EntityMetadata> metadata(std::move(it->second));
+      // As CreateFromMetadata() takes sync_pb::EntityMetadata by value, move it
+      // to avoid copying.
       std::unique_ptr<ProcessorEntity> entity =
-          ProcessorEntity::CreateFromMetadata(it->first, std::move(it->second));
+          ProcessorEntity::CreateFromMetadata(it->first, std::move(*metadata));
       storage_key_to_tag_hash_[entity->storage_key()] =
           entity->metadata().client_tag_hash();
       entities_[entity->metadata().client_tag_hash()] = std::move(entity);
@@ -625,27 +628,23 @@
   }
 }
 
-// Returns an updates list that has client tag hashes populated for every
-// update entity.
-UpdateResponseDataList PopulateClientTagsForWalletData(
-    const ModelType& type,
-    ModelTypeSyncBridge* bridge,
-    const UpdateResponseDataList& updates) {
+// Populates the client tag hashes for every update entity in |updates|.
+void PopulateClientTagsForWalletData(const ModelType& type,
+                                     ModelTypeSyncBridge* bridge,
+                                     UpdateResponseDataList* updates) {
   DCHECK(bridge->SupportsGetClientTag());
   UpdateResponseDataList updates_with_client_tags;
-  for (const UpdateResponseData& update : updates) {
-    if (update.entity->parent_id == "0") {
+  for (std::unique_ptr<UpdateResponseData>& update : *updates) {
+    DCHECK(update);
+    if (update->entity->parent_id == "0") {
       // Ignore the permanent root node. Other places in this file detect them
       // by having empty client tags; this cannot be used for wallet_data as no
       // wallet_data entity has a client tag.
       continue;
     }
-    updates_with_client_tags.push_back(update);
-    updates_with_client_tags.back().entity =
-        update.entity->UpdateClientTagHash(GenerateSyncableHash(
-            type, bridge->GetClientTag(update.entity.value())));
+    update->entity = update->entity->UpdateClientTagHash(GenerateSyncableHash(
+        type, bridge->GetClientTag(update->entity.value())));
   }
-  return updates_with_client_tags;
 }
 
 // Returns whether the state has a version_watermark based GC directive, which
@@ -658,7 +657,7 @@
 
 void ClientTagBasedModelTypeProcessor::OnUpdateReceived(
     const sync_pb::ModelTypeState& model_type_state,
-    const UpdateResponseDataList& updates) {
+    UpdateResponseDataList updates) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(model_ready_to_sync_);
   DCHECK(!model_error_);
@@ -667,9 +666,6 @@
     return;
   }
 
-  const UpdateResponseDataList* updates_to_process = &updates;
-  UpdateResponseDataList pre_processed_updates;
-
   if (type_ == AUTOFILL_WALLET_DATA) {
     // The client tag based processor requires client tags to function properly.
     // However, the wallet data type does not have any client tags. This hacky
@@ -678,9 +674,7 @@
     // fully use client tags, or to use a different processor.
     // TODO(crbug.com/874001): Remove this feature-specific logic when the right
     // solution for Wallet data has been decided.
-    pre_processed_updates =
-        PopulateClientTagsForWalletData(type_, bridge_, updates);
-    updates_to_process = &pre_processed_updates;
+    PopulateClientTagsForWalletData(type_, bridge_, &updates);
   }
 
   base::Optional<ModelError> error;
@@ -694,9 +688,9 @@
   // has.
   bool is_initial_sync = !model_type_state_.initial_sync_done();
   if (is_initial_sync || HasClearAllDirective(model_type_state)) {
-    error = OnFullUpdateReceived(model_type_state, *updates_to_process);
+    error = OnFullUpdateReceived(model_type_state, updates);
   } else {
-    error = OnIncrementalUpdateReceived(model_type_state, *updates_to_process);
+    error = OnIncrementalUpdateReceived(model_type_state, updates);
     ExpireEntriesIfNeeded(model_type_state.progress_marker());
   }
 
@@ -973,13 +967,14 @@
   model_type_state_ = model_type_state;
   metadata_changes->UpdateModelTypeState(model_type_state_);
 
-  for (const UpdateResponseData& update : updates) {
-    const std::string& client_tag_hash = update.entity->client_tag_hash;
+  for (const std::unique_ptr<syncer::UpdateResponseData>& update : updates) {
+    DCHECK(update);
+    const std::string& client_tag_hash = update->entity->client_tag_hash;
     if (client_tag_hash.empty()) {
       // Ignore updates missing a client tag hash (e.g. permanent nodes).
       continue;
     }
-    if (update.entity->is_deleted()) {
+    if (update->entity->is_deleted()) {
       DLOG(WARNING) << "Ignoring tombstone found during initial update: "
                     << "client_tag_hash = " << client_tag_hash << " for "
                     << ModelTypeToString(type_);
@@ -988,7 +983,7 @@
     if (bridge_->SupportsGetClientTag() &&
         client_tag_hash !=
             GenerateSyncableHash(
-                type_, bridge_->GetClientTag(update.entity.value()))) {
+                type_, bridge_->GetClientTag(update->entity.value()))) {
       DLOG(WARNING) << "Received unexpected client tag hash: "
                     << client_tag_hash << " for " << ModelTypeToString(type_);
       continue;
@@ -1003,10 +998,10 @@
                   << " for " << ModelTypeToString(type_);
     }
 #endif  // DCHECK_IS_ON()
-    ProcessorEntity* entity = CreateEntity(update.entity.value());
-    entity->RecordAcceptedUpdate(update);
+    ProcessorEntity* entity = CreateEntity(update->entity.value());
+    entity->RecordAcceptedUpdate(*update);
     const std::string& storage_key = entity->storage_key();
-    entity_data.push_back(EntityChange::CreateAdd(storage_key, update.entity));
+    entity_data.push_back(EntityChange::CreateAdd(storage_key, update->entity));
     if (!storage_key.empty())
       metadata_changes->UpdateMetadata(storage_key, entity->metadata());
   }
@@ -1039,8 +1034,9 @@
   // re-encryption phase at the end.
   std::unordered_set<std::string> already_updated;
 
-  for (const UpdateResponseData& update : updates) {
-    ProcessorEntity* entity = ProcessUpdate(update, &entity_changes);
+  for (const std::unique_ptr<syncer::UpdateResponseData>& update : updates) {
+    DCHECK(update);
+    ProcessorEntity* entity = ProcessUpdate(*update, &entity_changes);
 
     if (!entity) {
       // The update is either of the following:
diff --git a/components/sync/model_impl/client_tag_based_model_type_processor.h b/components/sync/model_impl/client_tag_based_model_type_processor.h
index c16ed1c..fcf5877 100644
--- a/components/sync/model_impl/client_tag_based_model_type_processor.h
+++ b/components/sync/model_impl/client_tag_based_model_type_processor.h
@@ -90,7 +90,7 @@
   void OnCommitCompleted(const sync_pb::ModelTypeState& type_state,
                          const CommitResponseDataList& response_list) override;
   void OnUpdateReceived(const sync_pb::ModelTypeState& type_state,
-                        const UpdateResponseDataList& updates) override;
+                        UpdateResponseDataList updates) override;
 
   // ModelTypeControllerDelegate implementation.
   void OnSyncStarting(const DataTypeActivationRequest& request,
diff --git a/components/sync/model_impl/client_tag_based_model_type_processor_unittest.cc b/components/sync/model_impl/client_tag_based_model_type_processor_unittest.cc
index 195bf0d..0f86207 100644
--- a/components/sync/model_impl/client_tag_based_model_type_processor_unittest.cc
+++ b/components/sync/model_impl/client_tag_based_model_type_processor_unittest.cc
@@ -459,7 +459,7 @@
   UpdateResponseDataList update;
   update.push_back(worker()->GenerateTypeRootUpdateData(ModelType::SESSIONS));
 
-  worker()->UpdateFromServer(update);
+  worker()->UpdateFromServer(std::move(update));
   // Root node update should be filtered out.
   EXPECT_EQ(0U, ProcessorEntityCount());
 }
@@ -1026,7 +1026,7 @@
   UpdateResponseDataList update;
   update.push_back(worker()->GenerateTypeRootUpdateData(ModelType::SESSIONS));
 
-  worker()->UpdateFromServer(update);
+  worker()->UpdateFromServer(std::move(update));
   // Root node update should be filtered out.
   EXPECT_EQ(0U, ProcessorEntityCount());
 }
@@ -1594,7 +1594,7 @@
   update.push_back(worker()->GenerateUpdateData(
       kHash3, GenerateSpecifics(kKey3, kValue3), 1, "k2"));
   // Set desired encryption key to k2 to force updates to some items.
-  worker()->UpdateWithEncryptionKey("k2", update);
+  worker()->UpdateWithEncryptionKey("k2", std::move(update));
 
   OnCommitDataLoaded();
   // kKey1 needed data so once that's loaded, kKey1 and kKey2 are queued for
@@ -1724,7 +1724,7 @@
   updates.push_back(worker()->GenerateUpdateData(
       /*tag_hash=*/"", GenerateSpecifics(kKey1, kValue1), 1, "k1"));
 
-  worker()->UpdateFromServer(updates);
+  worker()->UpdateFromServer(std::move(updates));
 
   // Verify that the data wasn't actually stored.
   EXPECT_EQ(0U, db()->metadata_count());
@@ -1743,7 +1743,7 @@
       /*tag_hash=*/"", GenerateSpecifics(kKey1, kValue1), 1, "k1"));
 
   base::HistogramTester histogram_tester;
-  worker()->UpdateFromServer(updates);
+  worker()->UpdateFromServer(std::move(updates));
 
   // The duration should get recorded.
   histogram_tester.ExpectTotalCount(
@@ -1763,7 +1763,7 @@
   UpdateResponseDataList updates;
   updates.push_back(worker()->GenerateUpdateData(
       /*tag_hash=*/"", GenerateSpecifics(kKey1, kValue1), 1, "k1"));
-  worker()->UpdateFromServer(updates);
+  worker()->UpdateFromServer(std::move(updates));
 
   ASSERT_EQ(1, bridge()->merge_call_count());
   histogram_tester.ExpectTotalCount(
@@ -1793,7 +1793,8 @@
   // Create 2 entries, one is version 3, another is version 1.
   sync_pb::GarbageCollectionDirective garbage_collection_directive;
   garbage_collection_directive.set_version_watermark(1);
-  worker()->UpdateWithGarbageCollection(updates, garbage_collection_directive);
+  worker()->UpdateWithGarbageCollection(std::move(updates),
+                                        garbage_collection_directive);
   WriteItemAndAck(kKey1, kValue1);
   WriteItemAndAck(kKey2, kValue2);
 
@@ -1823,15 +1824,15 @@
   InitializeToMetadataLoaded(/*initial_sync_done=*/false);
   OnSyncStarting("SomeAccountId", STORAGE_IN_MEMORY);
 
-  UpdateResponseDataList updates;
-  updates.push_back(worker()->GenerateUpdateData(
+  UpdateResponseDataList updates1;
+  updates1.push_back(worker()->GenerateUpdateData(
       /*tag_hash=*/"", GenerateSpecifics(kKey1, kValue1), 1, "k1"));
   sync_pb::GarbageCollectionDirective garbage_collection_directive;
   garbage_collection_directive.set_version_watermark(1);
 
   {
     base::HistogramTester histogram_tester;
-    worker()->UpdateWithGarbageCollection(updates,
+    worker()->UpdateWithGarbageCollection(std::move(updates1),
                                           garbage_collection_directive);
     ASSERT_EQ(1, bridge()->merge_call_count());
 
@@ -1842,9 +1843,12 @@
   }
 
   {
+    UpdateResponseDataList updates2;
+    updates2.push_back(worker()->GenerateUpdateData(
+        /*tag_hash=*/"", GenerateSpecifics(kKey1, kValue1), 1, "k1"));
     base::HistogramTester histogram_tester;
     // Send one more update with the same data.
-    worker()->UpdateWithGarbageCollection(updates,
+    worker()->UpdateWithGarbageCollection(std::move(updates2),
                                           garbage_collection_directive);
     ASSERT_EQ(2, bridge()->merge_call_count());
 
@@ -1922,7 +1926,8 @@
       /*tag_hash=*/"", GenerateSpecifics(kKey1, kValue1), 1, "k1"));
   sync_pb::GarbageCollectionDirective garbage_collection_directive;
   garbage_collection_directive.set_version_watermark(1);
-  worker()->UpdateWithGarbageCollection(updates, garbage_collection_directive);
+  worker()->UpdateWithGarbageCollection(std::move(updates),
+                                        garbage_collection_directive);
 
   // Verify that the data was stored.
   EXPECT_EQ(1U, db()->metadata_count());
@@ -1938,7 +1943,7 @@
   EntitySpecifics specifics2 = bridge()->WriteItem(kKey1, kValue2);
   UpdateResponseDataList update;
   update.push_back(worker()->GenerateUpdateData(kHash1, specifics1, 1, "k1"));
-  worker()->UpdateWithEncryptionKey("k1", update);
+  worker()->UpdateWithEncryptionKey("k1", std::move(update));
 
   EXPECT_EQ(2U, worker()->GetNumPendingCommits());
   worker()->VerifyNthPendingCommit(1, {kHash1}, {specifics2});
@@ -1959,7 +1964,7 @@
 
   UpdateResponseDataList update;
   update.push_back(worker()->GenerateUpdateData(kHash1, specifics1, 1, "k1"));
-  worker()->UpdateWithEncryptionKey("k1", update);
+  worker()->UpdateWithEncryptionKey("k1", std::move(update));
 
   EXPECT_EQ(2U, worker()->GetNumPendingCommits());
   worker()->VerifyNthPendingCommit(1, {kHash1}, {specifics2});
@@ -1985,7 +1990,7 @@
   updates.push_back(
       worker()->GenerateUpdateData(kHash3, GenerateSpecifics(kKey3, kValue3)));
   bridge()->SetKeyToIgnore(kKey3);
-  worker()->UpdateFromServer(updates);
+  worker()->UpdateFromServer(std::move(updates));
   EXPECT_EQ(1, bridge()->merge_call_count());
   EXPECT_EQ(1U, ProcessorEntityCount());
   // Metadata should be written under kKey1. This means that UpdateStorageKey
@@ -2021,7 +2026,7 @@
   UpdateResponseDataList update;
   update.push_back(worker()->GenerateUpdateData(
       kHash1, GenerateSpecifics(kKey1, kValue1), 1, "ek1"));
-  worker()->UpdateWithEncryptionKey("ek2", update);
+  worker()->UpdateWithEncryptionKey("ek2", std::move(update));
   worker()->VerifyPendingCommits({{kHash1}});
 }
 
diff --git a/components/sync/model_impl/model_type_store_impl_unittest.cc b/components/sync/model_impl/model_type_store_impl_unittest.cc
index 3ad5312..69a6ef8 100644
--- a/components/sync/model_impl/model_type_store_impl_unittest.cc
+++ b/components/sync/model_impl/model_type_store_impl_unittest.cc
@@ -141,7 +141,7 @@
   for (const auto& kv : expected_metadata) {
     auto it = actual_metadata.find(kv.first);
     ASSERT_TRUE(it != actual_metadata.end());
-    EXPECT_EQ(kv.second.SerializeAsString(), it->second.SerializeAsString());
+    EXPECT_EQ(kv.second.SerializeAsString(), it->second->SerializeAsString());
     actual_metadata.erase(it);
   }
   EXPECT_EQ(0U, actual_metadata.size());
diff --git a/components/sync/model_impl/processor_entity_unittest.cc b/components/sync/model_impl/processor_entity_unittest.cc
index a58a855..9c7414b 100644
--- a/components/sync/model_impl/processor_entity_unittest.cc
+++ b/components/sync/model_impl/processor_entity_unittest.cc
@@ -42,36 +42,38 @@
   return entity_data;
 }
 
-UpdateResponseData GenerateUpdate(const ProcessorEntity& entity,
-                                  const std::string& hash,
-                                  const std::string& id,
-                                  const std::string& name,
-                                  const std::string& value,
-                                  const base::Time& mtime,
-                                  int64_t version) {
+std::unique_ptr<UpdateResponseData> GenerateUpdate(
+    const ProcessorEntity& entity,
+    const std::string& hash,
+    const std::string& id,
+    const std::string& name,
+    const std::string& value,
+    const base::Time& mtime,
+    int64_t version) {
   std::unique_ptr<EntityData> data = GenerateEntityData(hash, name, value);
   data->id = id;
   data->modification_time = mtime;
-  UpdateResponseData update;
-  update.entity = data->PassToPtr();
-  update.response_version = version;
+  auto update = std::make_unique<UpdateResponseData>();
+  update->entity = data->PassToPtr();
+  update->response_version = version;
   return update;
 }
 
-UpdateResponseData GenerateTombstone(const ProcessorEntity& entity,
-                                     const std::string& hash,
-                                     const std::string& id,
-                                     const std::string& name,
-                                     const base::Time& mtime,
-                                     int64_t version) {
+std::unique_ptr<UpdateResponseData> GenerateTombstone(
+    const ProcessorEntity& entity,
+    const std::string& hash,
+    const std::string& id,
+    const std::string& name,
+    const base::Time& mtime,
+    int64_t version) {
   std::unique_ptr<EntityData> data = std::make_unique<EntityData>();
   data->client_tag_hash = hash;
   data->non_unique_name = name;
   data->id = id;
   data->modification_time = mtime;
-  UpdateResponseData update;
-  update.entity = data->PassToPtr();
-  update.response_version = version;
+  auto update = std::make_unique<UpdateResponseData>();
+  update->entity = data->PassToPtr();
+  update->response_version = version;
   return update;
 }
 
@@ -114,8 +116,9 @@
 
   std::unique_ptr<ProcessorEntity> CreateSynced() {
     std::unique_ptr<ProcessorEntity> entity = CreateNew();
-    entity->RecordAcceptedUpdate(
-        GenerateUpdate(*entity, kHash, kId, kName, kValue1, ctime_, 1));
+    std::unique_ptr<UpdateResponseData> update =
+        GenerateUpdate(*entity, kHash, kId, kName, kValue1, ctime_, 1);
+    entity->RecordAcceptedUpdate(*update);
     DCHECK(!entity->IsUnsynced());
     return entity;
   }
@@ -230,8 +233,9 @@
   std::unique_ptr<ProcessorEntity> entity = CreateNew();
 
   const base::Time mtime = base::Time::Now();
-  entity->RecordAcceptedUpdate(
-      GenerateUpdate(*entity, kHash, kId, kName, kValue1, mtime, 10));
+  std::unique_ptr<UpdateResponseData> update =
+      GenerateUpdate(*entity, kHash, kId, kName, kValue1, mtime, 10);
+  entity->RecordAcceptedUpdate(*update);
 
   EXPECT_EQ(kId, entity->metadata().server_id());
   EXPECT_FALSE(entity->metadata().is_deleted());
@@ -260,8 +264,9 @@
   EXPECT_EQ("", entity->storage_key());
 
   const base::Time mtime = base::Time::Now();
-  entity->RecordAcceptedUpdate(
-      GenerateUpdate(*entity, kHash, kId, kName, kValue1, mtime, 10));
+  std::unique_ptr<UpdateResponseData> update =
+      GenerateUpdate(*entity, kHash, kId, kName, kValue1, mtime, 10);
+  entity->RecordAcceptedUpdate(*update);
   entity->SetStorageKey(kKey);
   EXPECT_EQ(kKey, entity->storage_key());
 }
@@ -271,8 +276,9 @@
   std::unique_ptr<ProcessorEntity> entity = CreateNew();
 
   const base::Time mtime = base::Time::Now();
-  entity->RecordAcceptedUpdate(
-      GenerateTombstone(*entity, kHash, kId, kName, mtime, 1));
+  std::unique_ptr<UpdateResponseData> tombstone =
+      GenerateTombstone(*entity, kHash, kId, kName, mtime, 1);
+  entity->RecordAcceptedUpdate(*tombstone);
 
   EXPECT_EQ(kId, entity->metadata().server_id());
   EXPECT_TRUE(entity->metadata().is_deleted());
@@ -298,8 +304,9 @@
   std::unique_ptr<ProcessorEntity> entity = CreateSynced();
   // A deletion update one version later.
   const base::Time mtime = base::Time::Now();
-  entity->RecordAcceptedUpdate(
-      GenerateTombstone(*entity, kHash, kId, kName, mtime, 2));
+  std::unique_ptr<UpdateResponseData> tombstone =
+      GenerateTombstone(*entity, kHash, kId, kName, mtime, 2);
+  entity->RecordAcceptedUpdate(*tombstone);
 
   EXPECT_TRUE(entity->metadata().is_deleted());
   EXPECT_EQ(0, entity->metadata().sequence_number());
@@ -568,8 +575,9 @@
 
   // Before anything gets committed, we receive a remote tombstone, but local
   // would usually win so the remote update is ignored.
-  entity->RecordIgnoredUpdate(
-      GenerateTombstone(*entity, kHash, kId, kName, base::Time::Now(), 2));
+  std::unique_ptr<UpdateResponseData> tombstone =
+      GenerateTombstone(*entity, kHash, kId, kName, base::Time::Now(), 2);
+  entity->RecordIgnoredUpdate(*tombstone);
 
   EXPECT_EQ(kId, entity->metadata().server_id());
   EXPECT_TRUE(entity->IsUnsynced());
diff --git a/components/sync/nigori/nigori_model_type_processor.cc b/components/sync/nigori/nigori_model_type_processor.cc
index 2e363c4..c697dd4 100644
--- a/components/sync/nigori/nigori_model_type_processor.cc
+++ b/components/sync/nigori/nigori_model_type_processor.cc
@@ -41,7 +41,7 @@
 
 void NigoriModelTypeProcessor::OnUpdateReceived(
     const sync_pb::ModelTypeState& type_state,
-    const UpdateResponseDataList& updates) {
+    UpdateResponseDataList updates) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   NOTIMPLEMENTED();
 }
diff --git a/components/sync/nigori/nigori_model_type_processor.h b/components/sync/nigori/nigori_model_type_processor.h
index 4297f77..fed0e00 100644
--- a/components/sync/nigori/nigori_model_type_processor.h
+++ b/components/sync/nigori/nigori_model_type_processor.h
@@ -34,7 +34,7 @@
   void OnCommitCompleted(const sync_pb::ModelTypeState& type_state,
                          const CommitResponseDataList& response_list) override;
   void OnUpdateReceived(const sync_pb::ModelTypeState& type_state,
-                        const UpdateResponseDataList& updates) override;
+                        UpdateResponseDataList updates) override;
 
   // ModelTypeControllerDelegate implementation.
   void OnSyncStarting(const DataTypeActivationRequest& request,
diff --git a/components/sync/test/engine/mock_model_type_processor.cc b/components/sync/test/engine/mock_model_type_processor.cc
index 5f1755c..95af9259 100644
--- a/components/sync/test/engine/mock_model_type_processor.cc
+++ b/components/sync/test/engine/mock_model_type_processor.cc
@@ -39,21 +39,19 @@
 void MockModelTypeProcessor::OnCommitCompleted(
     const sync_pb::ModelTypeState& type_state,
     const CommitResponseDataList& response_list) {
-  base::Closure task =
-      base::Bind(&MockModelTypeProcessor::OnCommitCompletedImpl,
-                 base::Unretained(this), type_state, response_list);
-  pending_tasks_.push_back(task);
+  pending_tasks_.push_back(
+      base::BindOnce(&MockModelTypeProcessor::OnCommitCompletedImpl,
+                     base::Unretained(this), type_state, response_list));
   if (is_synchronous_)
     RunQueuedTasks();
 }
 
 void MockModelTypeProcessor::OnUpdateReceived(
     const sync_pb::ModelTypeState& type_state,
-    const UpdateResponseDataList& response_list) {
-  base::Closure task =
-      base::Bind(&MockModelTypeProcessor::OnUpdateReceivedImpl,
-                 base::Unretained(this), type_state, response_list);
-  pending_tasks_.push_back(task);
+    UpdateResponseDataList response_list) {
+  pending_tasks_.push_back(base::BindOnce(
+      &MockModelTypeProcessor::OnUpdateReceivedImpl, base::Unretained(this),
+      type_state, std::move(response_list)));
   if (is_synchronous_)
     RunQueuedTasks();
 }
@@ -64,7 +62,7 @@
 
 void MockModelTypeProcessor::RunQueuedTasks() {
   for (auto it = pending_tasks_.begin(); it != pending_tasks_.end(); ++it) {
-    it->Run();
+    std::move(*it).Run();
   }
   pending_tasks_.clear();
 }
@@ -134,10 +132,15 @@
   return received_update_responses_.size();
 }
 
-UpdateResponseDataList MockModelTypeProcessor::GetNthUpdateResponse(
-    size_t n) const {
+std::vector<const UpdateResponseData*>
+MockModelTypeProcessor::GetNthUpdateResponse(size_t n) const {
   DCHECK_LT(n, GetNumUpdateResponses());
-  return received_update_responses_[n];
+  std::vector<const UpdateResponseData*> nth_update_responses;
+  for (const std::unique_ptr<UpdateResponseData>& response :
+       received_update_responses_[n]) {
+    nth_update_responses.push_back(response.get());
+  }
+  return nth_update_responses;
 }
 
 sync_pb::ModelTypeState MockModelTypeProcessor::GetNthUpdateState(
@@ -168,11 +171,11 @@
   return it != update_response_items_.end();
 }
 
-UpdateResponseData MockModelTypeProcessor::GetUpdateResponse(
+const UpdateResponseData& MockModelTypeProcessor::GetUpdateResponse(
     const std::string& tag_hash) const {
   DCHECK(HasUpdateResponse(tag_hash));
   auto it = update_response_items_.find(tag_hash);
-  return it->second;
+  return *it->second;
 }
 
 bool MockModelTypeProcessor::HasCommitResponse(
@@ -229,17 +232,17 @@
 
 void MockModelTypeProcessor::OnUpdateReceivedImpl(
     const sync_pb::ModelTypeState& type_state,
-    const UpdateResponseDataList& response_list) {
-  received_update_responses_.push_back(response_list);
+    UpdateResponseDataList response_list) {
   type_states_received_on_update_.push_back(type_state);
   for (auto it = response_list.begin(); it != response_list.end(); ++it) {
-    const std::string client_tag_hash = it->entity->client_tag_hash;
-    update_response_items_.insert(std::make_pair(client_tag_hash, *it));
-
+    const std::string client_tag_hash = (*it)->entity->client_tag_hash;
     // Server wins.  Set the model's base version.
-    SetBaseVersion(client_tag_hash, it->response_version);
-    SetServerAssignedId(client_tag_hash, it->entity->id);
+    SetBaseVersion(client_tag_hash, (*it)->response_version);
+    SetServerAssignedId(client_tag_hash, (*it)->entity->id);
+
+    update_response_items_.insert(std::make_pair(client_tag_hash, it->get()));
   }
+  received_update_responses_.push_back(std::move(response_list));
 }
 
 // Fetches the sequence number as of the most recent update request.
diff --git a/components/sync/test/engine/mock_model_type_processor.h b/components/sync/test/engine/mock_model_type_processor.h
index 691a224..dd9a755 100644
--- a/components/sync/test/engine/mock_model_type_processor.h
+++ b/components/sync/test/engine/mock_model_type_processor.h
@@ -48,7 +48,7 @@
   void OnCommitCompleted(const sync_pb::ModelTypeState& type_state,
                          const CommitResponseDataList& response_list) override;
   void OnUpdateReceived(const sync_pb::ModelTypeState& type_state,
-                        const UpdateResponseDataList& response_list) override;
+                        UpdateResponseDataList response_list) override;
 
   // By default, this object behaves as if all messages are processed
   // immediately.  Sometimes it is useful to defer work until later, as might
@@ -75,9 +75,9 @@
 
   // Getters to access the log of received update responses.
   //
-  // Does not includes repsonses that are in pending tasks.
+  // Does not includes responses that are in pending tasks.
   size_t GetNumUpdateResponses() const;
-  UpdateResponseDataList GetNthUpdateResponse(size_t n) const;
+  std::vector<const UpdateResponseData*> GetNthUpdateResponse(size_t n) const;
   sync_pb::ModelTypeState GetNthUpdateState(size_t n) const;
 
   // Getters to access the log of received commit responses.
@@ -89,7 +89,8 @@
 
   // Getters to access the lastest update response for a given tag_hash.
   bool HasUpdateResponse(const std::string& tag_hash) const;
-  UpdateResponseData GetUpdateResponse(const std::string& tag_hash) const;
+  const UpdateResponseData& GetUpdateResponse(
+      const std::string& tag_hash) const;
 
   // Getters to access the lastest commit response for a given tag_hash.
   bool HasCommitResponse(const std::string& tag_hash) const;
@@ -113,7 +114,7 @@
   //
   // Implemented as an Impl method so we can defer its execution in some cases.
   void OnUpdateReceivedImpl(const sync_pb::ModelTypeState& type_state,
-                            const UpdateResponseDataList& response_list);
+                            UpdateResponseDataList response_list);
 
   // Getter and setter for per-item sequence number tracking.
   int64_t GetCurrentSequenceNumber(const std::string& tag_hash) const;
@@ -131,7 +132,7 @@
   // State related to the implementation of deferred work.
   // See SetSynchronousExecution() for details.
   bool is_synchronous_;
-  std::vector<base::Closure> pending_tasks_;
+  std::vector<base::OnceClosure> pending_tasks_;
   std::unique_ptr<CommitQueue> commit_queue_;
 
   // A log of messages received by this object.
@@ -142,7 +143,7 @@
 
   // Latest responses received, indexed by tag_hash.
   std::map<const std::string, CommitResponseData> commit_response_items_;
-  std::map<const std::string, UpdateResponseData> update_response_items_;
+  std::map<const std::string, const UpdateResponseData*> update_response_items_;
 
   // The per-item state maps.
   std::map<const std::string, int64_t> sequence_numbers_;
diff --git a/components/sync/test/engine/mock_model_type_worker.cc b/components/sync/test/engine/mock_model_type_worker.cc
index 27ae551a..4916f1ae 100644
--- a/components/sync/test/engine/mock_model_type_worker.cc
+++ b/components/sync/test/engine/mock_model_type_worker.cc
@@ -146,15 +146,15 @@
   UpdateResponseDataList updates;
   updates.push_back(
       GenerateUpdateData(tag_hash, specifics, version_offset, ekn));
-  UpdateFromServer(updates);
+  UpdateFromServer(std::move(updates));
 }
 
-void MockModelTypeWorker::UpdateFromServer(
-    const UpdateResponseDataList& updates) {
-  processor_->OnUpdateReceived(model_type_state_, updates);
+void MockModelTypeWorker::UpdateFromServer(UpdateResponseDataList updates) {
+  processor_->OnUpdateReceived(model_type_state_, std::move(updates));
 }
 
-UpdateResponseData MockModelTypeWorker::GenerateUpdateData(
+std::unique_ptr<syncer::UpdateResponseData>
+MockModelTypeWorker::GenerateUpdateData(
     const std::string& tag_hash,
     const sync_pb::EntitySpecifics& specifics,
     int64_t version_offset,
@@ -179,23 +179,24 @@
                              ? "encrypted"
                              : data.specifics.preference().name();
 
-  UpdateResponseData response_data;
-  response_data.entity = data.PassToPtr();
-  response_data.response_version = version;
-  response_data.encryption_key_name = ekn;
+  auto response_data = std::make_unique<syncer::UpdateResponseData>();
+  response_data->entity = data.PassToPtr();
+  response_data->response_version = version;
+  response_data->encryption_key_name = ekn;
 
   return response_data;
 }
 
-UpdateResponseData MockModelTypeWorker::GenerateUpdateData(
+std::unique_ptr<syncer::UpdateResponseData>
+MockModelTypeWorker::GenerateUpdateData(
     const std::string& tag_hash,
     const sync_pb::EntitySpecifics& specifics) {
   return GenerateUpdateData(tag_hash, specifics, 1,
                             model_type_state_.encryption_key_name());
 }
 
-UpdateResponseData MockModelTypeWorker::GenerateTypeRootUpdateData(
-    const ModelType& model_type) {
+std::unique_ptr<syncer::UpdateResponseData>
+MockModelTypeWorker::GenerateTypeRootUpdateData(const ModelType& model_type) {
   EntityData data;
   data.id = syncer::ModelTypeToRootTag(model_type);
   data.parent_id = "r";
@@ -206,10 +207,10 @@
   data.creation_time = base::Time::UnixEpoch();
   data.modification_time = base::Time::UnixEpoch();
 
-  UpdateResponseData response_data;
-  response_data.entity = data.PassToPtr();
+  auto response_data = std::make_unique<syncer::UpdateResponseData>();
+  response_data->entity = data.PassToPtr();
   // Similar to what's done in the loopback_server.
-  response_data.response_version = 0;
+  response_data->response_version = 0;
   return response_data;
 }
 
@@ -228,14 +229,14 @@
       data.creation_time + base::TimeDelta::FromSeconds(version);
   data.non_unique_name = "Name Non Unique";
 
-  UpdateResponseData response_data;
-  response_data.entity = data.PassToPtr();
-  response_data.response_version = version;
-  response_data.encryption_key_name = model_type_state_.encryption_key_name();
+  auto response_data = std::make_unique<UpdateResponseData>();
+  response_data->entity = data.PassToPtr();
+  response_data->response_version = version;
+  response_data->encryption_key_name = model_type_state_.encryption_key_name();
 
   UpdateResponseDataList list;
-  list.push_back(response_data);
-  processor_->OnUpdateReceived(model_type_state_, list);
+  list.push_back(std::move(response_data));
+  processor_->OnUpdateReceived(model_type_state_, std::move(list));
 }
 
 void MockModelTypeWorker::AckOnePendingCommit() {
@@ -296,9 +297,9 @@
 
 void MockModelTypeWorker::UpdateWithEncryptionKey(
     const std::string& ekn,
-    const UpdateResponseDataList& update) {
+    UpdateResponseDataList update) {
   model_type_state_.set_encryption_key_name(ekn);
-  processor_->OnUpdateReceived(model_type_state_, update);
+  processor_->OnUpdateReceived(model_type_state_, std::move(update));
 }
 
 void MockModelTypeWorker::UpdateWithGarbageCollection(
@@ -308,10 +309,10 @@
 }
 
 void MockModelTypeWorker::UpdateWithGarbageCollection(
-    const UpdateResponseDataList& update,
+    UpdateResponseDataList update,
     const sync_pb::GarbageCollectionDirective& gcd) {
   *model_type_state_.mutable_progress_marker()->mutable_gc_directive() = gcd;
-  processor_->OnUpdateReceived(model_type_state_, update);
+  processor_->OnUpdateReceived(model_type_state_, std::move(update));
 }
 
 std::string MockModelTypeWorker::GenerateId(const std::string& tag_hash) {
diff --git a/components/sync/test/engine/mock_model_type_worker.h b/components/sync/test/engine/mock_model_type_worker.h
index 1269f7ef..97584215 100644
--- a/components/sync/test/engine/mock_model_type_worker.h
+++ b/components/sync/test/engine/mock_model_type_worker.h
@@ -77,7 +77,7 @@
                         const sync_pb::EntitySpecifics& specifics,
                         int64_t version_offset,
                         const std::string& ekn);
-  void UpdateFromServer(const UpdateResponseDataList& updates);
+  void UpdateFromServer(UpdateResponseDataList updates);
 
   // Returns an UpdateResponseData representing an update received from
   // the server. Updates server state accordingly.
@@ -87,7 +87,7 @@
   // the same version) or new updates.
   //
   // |ekn| is the encryption key name this item will fake having.
-  UpdateResponseData GenerateUpdateData(
+  std::unique_ptr<syncer::UpdateResponseData> GenerateUpdateData(
       const std::string& tag_hash,
       const sync_pb::EntitySpecifics& specifics,
       int64_t version_offset,
@@ -95,13 +95,14 @@
 
   // Mostly same as GenerateUpdateData above, but set 1 as |version_offset|, and
   // use model_type_state_.encryption_key_name() as |ekn|.
-  UpdateResponseData GenerateUpdateData(
+  std::unique_ptr<syncer::UpdateResponseData> GenerateUpdateData(
       const std::string& tag_hash,
       const sync_pb::EntitySpecifics& specifics);
 
   // Returns an UpdateResponseData representing an update received from
   // the server for a type root node.
-  UpdateResponseData GenerateTypeRootUpdateData(const ModelType& model_type);
+  std::unique_ptr<syncer::UpdateResponseData> GenerateTypeRootUpdateData(
+      const ModelType& model_type);
 
   // Triggers a server-side deletion of the entity with |tag_hash|; updates
   // server state accordingly.
@@ -120,10 +121,10 @@
   // containing the data in |update|, which defaults to an empty list.
   void UpdateWithEncryptionKey(const std::string& ekn);
   void UpdateWithEncryptionKey(const std::string& ekn,
-                               const UpdateResponseDataList& update);
+                               UpdateResponseDataList update);
 
   void UpdateWithGarbageCollection(
-      const UpdateResponseDataList& updates,
+      UpdateResponseDataList updates,
       const sync_pb::GarbageCollectionDirective& gcd);
 
   void UpdateWithGarbageCollection(
diff --git a/components/sync/test/test_matchers.h b/components/sync/test/test_matchers.h
index 619866b..616ea7b5 100644
--- a/components/sync/test/test_matchers.h
+++ b/components/sync/test/test_matchers.h
@@ -5,7 +5,10 @@
 #ifndef COMPONENTS_SYNC_TEST_TEST_MATCHERS_H_
 #define COMPONENTS_SYNC_TEST_TEST_MATCHERS_H_
 
+#include <map>
+#include <memory>
 #include <string>
+#include <utility>
 #include <vector>
 
 #include "components/sync/model/metadata_batch.h"
@@ -43,8 +46,21 @@
                           arg->GetModelTypeState(), result_listener)) {
     return false;
   }
-  return ExplainMatchResult(testing::Matcher<EntityMetadataMap>(entities),
-                            arg->TakeAllMetadata(), result_listener);
+
+  // We need to convert the map values to non-pointers in order to make them
+  // copyable and use gmock.
+  std::map<std::string, std::unique_ptr<sync_pb::EntityMetadata>> metadata =
+      arg->TakeAllMetadata();
+  std::map<std::string, sync_pb::EntityMetadata> copyable_metadata;
+  for (std::pair<const std::string, std::unique_ptr<sync_pb::EntityMetadata>>&
+           kv : metadata) {
+    copyable_metadata[kv.first] = std::move(*(kv.second));
+  }
+
+  return ExplainMatchResult(
+      testing::Matcher<std::map<std::string, sync_pb::EntityMetadata>>(
+          entities),
+      copyable_metadata, result_listener);
 }
 
 // Matcher for sync_pb::ModelTypeState: verifies that field
diff --git a/components/sync_bookmarks/bookmark_model_merger.cc b/components/sync_bookmarks/bookmark_model_merger.cc
index 18b55b3..f5922ec 100644
--- a/components/sync_bookmarks/bookmark_model_merger.cc
+++ b/components/sync_bookmarks/bookmark_model_merger.cc
@@ -5,6 +5,7 @@
 #include "components/sync_bookmarks/bookmark_model_merger.h"
 
 #include <algorithm>
+#include <memory>
 #include <set>
 #include <string>
 #include <utility>
@@ -136,16 +137,18 @@
       id_to_updates;
   // Tombstones carry only the sync id and cannot be merged with the local
   // model. Hence, we ignore tombstones.
-  for (const UpdateResponseData& update : *updates) {
-    const EntityData& update_entity = update.entity.value();
+  for (const std::unique_ptr<syncer::UpdateResponseData>& update : *updates) {
+    DCHECK(update);
+    const EntityData& update_entity = update->entity.value();
     if (update_entity.is_deleted()) {
       continue;
     }
-    id_to_updates[update_entity.id] = &update;
+    id_to_updates[update_entity.id] = update.get();
   }
 
-  for (const UpdateResponseData& update : *updates) {
-    const EntityData& update_entity = update.entity.value();
+  for (const std::unique_ptr<UpdateResponseData>& update : *updates) {
+    DCHECK(update);
+    const EntityData& update_entity = update->entity.value();
     if (update_entity.is_deleted()) {
       continue;
     }
@@ -169,7 +172,7 @@
     }
     const UpdateResponseData* parent_update =
         id_to_updates[update_entity.parent_id];
-    updates_tree[parent_update].push_back(&update);
+    updates_tree[parent_update].push_back(update.get());
   }
 
   // Sort all child updates.
@@ -215,14 +218,15 @@
   // perform the primary match. If there are multiple match candidates it
   // selects the first one.
   // Associate permanent folders.
-  for (const UpdateResponseData& update : *updates_) {
-    const EntityData& update_entity = update.entity.value();
+  for (const std::unique_ptr<UpdateResponseData>& update : *updates_) {
+    DCHECK(update);
+    const EntityData& update_entity = update->entity.value();
     const bookmarks::BookmarkNode* permanent_folder =
         GetPermanentFolder(update_entity);
     if (!permanent_folder) {
       continue;
     }
-    MergeSubtree(permanent_folder, &update);
+    MergeSubtree(permanent_folder, update.get());
   }
   // TODO(crbug.com/516866): Check that both models match now.
 
diff --git a/components/sync_bookmarks/bookmark_model_merger_unittest.cc b/components/sync_bookmarks/bookmark_model_merger_unittest.cc
index 1eedd363..4af9f09b 100644
--- a/components/sync_bookmarks/bookmark_model_merger_unittest.cc
+++ b/components/sync_bookmarks/bookmark_model_merger_unittest.cc
@@ -28,7 +28,7 @@
 const char kBookmarkBarId[] = "bookmark_bar_id";
 const char kBookmarkBarTag[] = "bookmark_bar";
 
-syncer::UpdateResponseData CreateUpdateResponseData(
+std::unique_ptr<syncer::UpdateResponseData> CreateUpdateResponseData(
     const std::string& server_id,
     const std::string& parent_id,
     const std::string& title,
@@ -50,24 +50,24 @@
   bookmark_specifics->set_favicon(icon_data);
 
   data.is_folder = is_folder;
-  syncer::UpdateResponseData response_data;
-  response_data.entity = data.PassToPtr();
+  auto response_data = std::make_unique<syncer::UpdateResponseData>();
+  response_data->entity = data.PassToPtr();
   // Similar to what's done in the loopback_server.
-  response_data.response_version = 0;
+  response_data->response_version = 0;
   return response_data;
 }
 
-syncer::UpdateResponseData CreateBookmarkBarNodeUpdateData() {
+std::unique_ptr<syncer::UpdateResponseData> CreateBookmarkBarNodeUpdateData() {
   syncer::EntityData data;
   data.id = kBookmarkBarId;
   data.server_defined_unique_tag = kBookmarkBarTag;
 
   data.specifics.mutable_bookmark();
 
-  syncer::UpdateResponseData response_data;
-  response_data.entity = data.PassToPtr();
+  auto response_data = std::make_unique<syncer::UpdateResponseData>();
+  response_data->entity = data.PassToPtr();
   // Similar to what's done in the loopback_server.
-  response_data.response_version = 0;
+  response_data->response_version = 0;
   return response_data;
 }
 
diff --git a/components/sync_bookmarks/bookmark_model_type_processor.cc b/components/sync_bookmarks/bookmark_model_type_processor.cc
index 4cb7d49..03bca3c 100644
--- a/components/sync_bookmarks/bookmark_model_type_processor.cc
+++ b/components/sync_bookmarks/bookmark_model_type_processor.cc
@@ -168,7 +168,7 @@
 
 void BookmarkModelTypeProcessor::OnUpdateReceived(
     const sync_pb::ModelTypeState& model_type_state,
-    const syncer::UpdateResponseDataList& updates) {
+    syncer::UpdateResponseDataList updates) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(!model_type_state.cache_guid().empty());
   DCHECK_EQ(model_type_state.cache_guid(), cache_guid_);
diff --git a/components/sync_bookmarks/bookmark_model_type_processor.h b/components/sync_bookmarks/bookmark_model_type_processor.h
index d0030bd..12d9a61 100644
--- a/components/sync_bookmarks/bookmark_model_type_processor.h
+++ b/components/sync_bookmarks/bookmark_model_type_processor.h
@@ -49,7 +49,7 @@
       const sync_pb::ModelTypeState& type_state,
       const syncer::CommitResponseDataList& response_list) override;
   void OnUpdateReceived(const sync_pb::ModelTypeState& type_state,
-                        const syncer::UpdateResponseDataList& updates) override;
+                        syncer::UpdateResponseDataList updates) override;
 
   // ModelTypeControllerDelegate implementation.
   void OnSyncStarting(const syncer::DataTypeActivationRequest& request,
diff --git a/components/sync_bookmarks/bookmark_model_type_processor_unittest.cc b/components/sync_bookmarks/bookmark_model_type_processor_unittest.cc
index 9edc84a..3f80973 100644
--- a/components/sync_bookmarks/bookmark_model_type_processor_unittest.cc
+++ b/components/sync_bookmarks/bookmark_model_type_processor_unittest.cc
@@ -5,6 +5,7 @@
 #include "components/sync_bookmarks/bookmark_model_type_processor.h"
 
 #include <string>
+#include <utility>
 
 #include "base/bind_helpers.h"
 #include "base/strings/utf_string_conversions.h"
@@ -47,7 +48,7 @@
   std::string server_tag;
 };
 
-syncer::UpdateResponseData CreateUpdateResponseData(
+std::unique_ptr<syncer::UpdateResponseData> CreateUpdateResponseData(
     const BookmarkInfo& bookmark_info,
     const syncer::UniquePosition& unique_position,
     int response_version) {
@@ -66,9 +67,9 @@
     bookmark_specifics->set_url(bookmark_info.url);
   }
 
-  syncer::UpdateResponseData response_data;
-  response_data.entity = data.PassToPtr();
-  response_data.response_version = response_version;
+  auto response_data = std::make_unique<syncer::UpdateResponseData>();
+  response_data->entity = data.PassToPtr();
+  response_data->response_version = response_version;
   return response_data;
 }
 
@@ -128,7 +129,7 @@
     updates.push_back(
         CreateUpdateResponseData(bookmark, pos, /*response_version=*/0));
   }
-  processor->OnUpdateReceived(CreateDummyModelTypeState(), updates);
+  processor->OnUpdateReceived(CreateDummyModelTypeState(), std::move(updates));
   AssertState(processor, bookmarks);
 }
 
@@ -211,7 +212,8 @@
       bookmark_model()->bookmark_bar_node();
   EXPECT_TRUE(bookmarkbar->empty());
 
-  processor()->OnUpdateReceived(CreateDummyModelTypeState(), updates);
+  processor()->OnUpdateReceived(CreateDummyModelTypeState(),
+                                std::move(updates));
 
   ASSERT_THAT(bookmarkbar->GetChild(0), NotNull());
   EXPECT_THAT(bookmarkbar->GetChild(0)->GetTitle(), Eq(ASCIIToUTF16(kTitle)));
@@ -250,7 +252,8 @@
                                 /*server_tag=*/std::string()},
                                kRandomPosition, /*response_version=*/1));
 
-  processor()->OnUpdateReceived(CreateDummyModelTypeState(), updates);
+  processor()->OnUpdateReceived(CreateDummyModelTypeState(),
+                                std::move(updates));
 
   // Check if the bookmark has been updated properly.
   EXPECT_THAT(bookmark_bar->GetChild(0), Eq(bookmark_node));
@@ -287,10 +290,11 @@
       CreateUpdateResponseData({kNodeId, kTitle, kUrl, kBookmarkBarId,
                                 /*server_tag=*/std::string()},
                                kRandomPosition, /*response_version=*/1));
-  updates[0].response_version++;
+  updates[0]->response_version++;
 
   EXPECT_CALL(*schedule_save_closure(), Run());
-  processor()->OnUpdateReceived(CreateDummyModelTypeState(), updates);
+  processor()->OnUpdateReceived(CreateDummyModelTypeState(),
+                                std::move(updates));
 }
 
 TEST_F(BookmarkModelTypeProcessorTest, ShouldDecodeSyncMetadata) {
@@ -476,17 +480,18 @@
 
   // Push an update that is encrypted with the new encryption key.
   const std::string kNodeId = "node_id";
-  syncer::UpdateResponseData response_data = CreateUpdateResponseData(
-      {kNodeId, "title", "http://www.url.com", /*parent_id=*/kBookmarkBarId,
-       /*server_tag=*/std::string()},
-      syncer::UniquePosition::InitialPosition(
-          syncer::UniquePosition::RandomSuffix()),
-      /*response_version=*/0);
-  response_data.encryption_key_name = kEncryptionKeyName;
+  std::unique_ptr<syncer::UpdateResponseData> response_data =
+      CreateUpdateResponseData(
+          {kNodeId, "title", "http://www.url.com", /*parent_id=*/kBookmarkBarId,
+           /*server_tag=*/std::string()},
+          syncer::UniquePosition::InitialPosition(
+              syncer::UniquePosition::RandomSuffix()),
+          /*response_version=*/0);
+  response_data->encryption_key_name = kEncryptionKeyName;
 
   syncer::UpdateResponseDataList updates;
-  updates.push_back(response_data);
-  processor()->OnUpdateReceived(model_type_state, updates);
+  updates.push_back(std::move(response_data));
+  processor()->OnUpdateReceived(model_type_state, std::move(updates));
 
   // The bookmarks shouldn't be marked for committing.
   ASSERT_THAT(tracker->GetEntityForSyncId(kNodeId), NotNull());
diff --git a/components/sync_bookmarks/bookmark_remote_updates_handler.cc b/components/sync_bookmarks/bookmark_remote_updates_handler.cc
index 2f1a4c6..4968538 100644
--- a/components/sync_bookmarks/bookmark_remote_updates_handler.cc
+++ b/components/sync_bookmarks/bookmark_remote_updates_handler.cc
@@ -4,6 +4,7 @@
 
 #include "components/sync_bookmarks/bookmark_remote_updates_handler.h"
 
+#include <memory>
 #include <set>
 #include <string>
 #include <unordered_map>
@@ -299,8 +300,9 @@
       parent_to_children;
 
   // Add only non-deletions to |id_to_updates|.
-  for (const syncer::UpdateResponseData& update : *updates) {
-    const syncer::EntityData& update_entity = update.entity.value();
+  for (const std::unique_ptr<syncer::UpdateResponseData>& update : *updates) {
+    DCHECK(update);
+    const syncer::EntityData& update_entity = update->entity.value();
     // Ignore updates to root nodes.
     if (update_entity.parent_id == "0") {
       continue;
@@ -308,7 +310,7 @@
     if (update_entity.is_deleted()) {
       continue;
     }
-    id_to_updates[update_entity.id] = &update;
+    id_to_updates[update_entity.id] = update.get();
   }
   // Iterate over |id_to_updates| and construct |roots| and
   // |parent_to_children|.
@@ -331,15 +333,16 @@
 
   int root_node_updates_count = 0;
   // Add deletions.
-  for (const syncer::UpdateResponseData& update : *updates) {
-    const syncer::EntityData& update_entity = update.entity.value();
+  for (const std::unique_ptr<syncer::UpdateResponseData>& update : *updates) {
+    DCHECK(update);
+    const syncer::EntityData& update_entity = update->entity.value();
     // Ignore updates to root nodes.
     if (update_entity.parent_id == "0") {
       root_node_updates_count++;
       continue;
     }
     if (update_entity.is_deleted()) {
-      ordered_updates.push_back(&update);
+      ordered_updates.push_back(update.get());
     }
   }
   // All non root updates should have been included in |ordered_updates|.
diff --git a/components/sync_bookmarks/bookmark_remote_updates_handler_unittest.cc b/components/sync_bookmarks/bookmark_remote_updates_handler_unittest.cc
index 765909f..abf7179 100644
--- a/components/sync_bookmarks/bookmark_remote_updates_handler_unittest.cc
+++ b/components/sync_bookmarks/bookmark_remote_updates_handler_unittest.cc
@@ -45,7 +45,7 @@
 const char kOtherBookmarksId[] = "other_bookmarks_id";
 const char kOtherBookmarksTag[] = "other_bookmarks";
 
-syncer::UpdateResponseData CreateUpdateResponseData(
+std::unique_ptr<syncer::UpdateResponseData> CreateUpdateResponseData(
     const std::string& server_id,
     const std::string& parent_id,
     const std::string& title,
@@ -64,15 +64,15 @@
     bookmark_specifics->set_title(title);
   }
   data.is_folder = true;
-  syncer::UpdateResponseData response_data;
-  response_data.entity = data.PassToPtr();
-  response_data.response_version = version;
+  auto response_data = std::make_unique<syncer::UpdateResponseData>();
+  response_data->entity = data.PassToPtr();
+  response_data->response_version = version;
   return response_data;
 }
 
 // Overload that assign a random position. Should only be used when position
 // doesn't matter.
-syncer::UpdateResponseData CreateUpdateResponseData(
+std::unique_ptr<syncer::UpdateResponseData> CreateUpdateResponseData(
     const std::string& server_id,
     const std::string& parent_id,
     bool is_deletion,
@@ -83,7 +83,7 @@
                                       syncer::UniquePosition::RandomSuffix()));
 }
 
-syncer::UpdateResponseData CreateBookmarkRootUpdateData() {
+std::unique_ptr<syncer::UpdateResponseData> CreateBookmarkRootUpdateData() {
   syncer::EntityData data;
   data.id = syncer::ModelTypeToRootTag(syncer::BOOKMARKS);
   data.parent_id = kRootParentId;
@@ -92,14 +92,14 @@
 
   data.specifics.mutable_bookmark();
 
-  syncer::UpdateResponseData response_data;
-  response_data.entity = data.PassToPtr();
+  auto response_data = std::make_unique<syncer::UpdateResponseData>();
+  response_data->entity = data.PassToPtr();
   // Similar to what's done in the loopback_server.
-  response_data.response_version = 0;
+  response_data->response_version = 0;
   return response_data;
 }
 
-syncer::UpdateResponseData CreatePermanentFolderUpdateData(
+std::unique_ptr<syncer::UpdateResponseData> CreatePermanentFolderUpdateData(
     const std::string& id,
     const std::string& tag) {
   syncer::EntityData data;
@@ -109,18 +109,22 @@
 
   data.specifics.mutable_bookmark();
 
-  syncer::UpdateResponseData response_data;
-  response_data.entity = data.PassToPtr();
+  auto response_data = std::make_unique<syncer::UpdateResponseData>();
+  response_data->entity = data.PassToPtr();
   // Similar to what's done in the loopback_server.
-  response_data.response_version = 0;
+  response_data->response_version = 0;
   return response_data;
 }
 
 syncer::UpdateResponseDataList CreatePermanentFoldersUpdateData() {
-  return {
-      CreatePermanentFolderUpdateData(kBookmarkBarId, kBookmarkBarTag),
-      CreatePermanentFolderUpdateData(kOtherBookmarksId, kOtherBookmarksTag),
-      CreatePermanentFolderUpdateData(kMobileBookmarksId, kMobileBookmarksTag)};
+  syncer::UpdateResponseDataList updates;
+  updates.push_back(
+      CreatePermanentFolderUpdateData(kBookmarkBarId, kBookmarkBarTag));
+  updates.push_back(
+      CreatePermanentFolderUpdateData(kOtherBookmarksId, kOtherBookmarksTag));
+  updates.push_back(
+      CreatePermanentFolderUpdateData(kMobileBookmarksId, kMobileBookmarksTag));
+  return updates;
 }
 
 std::unique_ptr<sync_pb::EntityMetadata> CreateEntityMetadata(
@@ -593,12 +597,12 @@
   bookmark_specifics->set_icon_url(kIconUrl.spec());
   bookmark_specifics->set_favicon("PNG");
   data.is_folder = false;
-  syncer::UpdateResponseData response_data;
-  response_data.entity = data.PassToPtr();
+  auto response_data = std::make_unique<syncer::UpdateResponseData>();
+  response_data->entity = data.PassToPtr();
   // Similar to what's done in the loopback_server.
-  response_data.response_version = 0;
+  response_data->response_version = 0;
 
-  updates.push_back(response_data);
+  updates.push_back(std::move(response_data));
 
   EXPECT_CALL(*favicon_service(),
               AddPageNoVisitForBookmark(kUrl, base::UTF8ToUTF16(kTitle)));
@@ -629,12 +633,12 @@
   bookmark_specifics->set_title(kTitle);
   bookmark_specifics->set_url(kUrl.spec());
   data.is_folder = false;
-  syncer::UpdateResponseData response_data;
-  response_data.entity = data.PassToPtr();
+  auto response_data = std::make_unique<syncer::UpdateResponseData>();
+  response_data->entity = data.PassToPtr();
   // Similar to what's done in the loopback_server.
-  response_data.response_version = 0;
+  response_data->response_version = 0;
 
-  updates.push_back(response_data);
+  updates.push_back(std::move(response_data));
 
   EXPECT_CALL(*favicon_service(),
               DeleteFaviconMappings(ElementsAre(kUrl),
@@ -700,11 +704,11 @@
   data.specifics = specifics;
   data.is_folder = true;
 
-  syncer::UpdateResponseData response_data;
-  response_data.entity = data.PassToPtr();
+  auto response_data = std::make_unique<syncer::UpdateResponseData>();
+  response_data->entity = data.PassToPtr();
   // Similar to what's done in the loopback_server.
-  response_data.response_version = 0;
-  updates.push_back(response_data);
+  response_data->response_version = 0;
+  updates.push_back(std::move(response_data));
 
   testing::NiceMock<favicon::MockFaviconService> favicon_service;
   BookmarkRemoteUpdatesHandler updates_handler(bookmark_model.get(),
@@ -738,12 +742,12 @@
 
   const std::string kId0 = "id0";
   syncer::UpdateResponseDataList updates;
-  syncer::UpdateResponseData response_data =
+  std::unique_ptr<syncer::UpdateResponseData> response_data =
       CreateUpdateResponseData(/*server_id=*/kId0,
                                /*parent_id=*/kBookmarkBarId,
                                /*is_deletion=*/false);
-  response_data.encryption_key_name = "out_of_date_encryption_key_name";
-  updates.push_back(response_data);
+  response_data->encryption_key_name = "out_of_date_encryption_key_name";
+  updates.push_back(std::move(response_data));
 
   BookmarkRemoteUpdatesHandler updates_handler(bookmark_model.get(),
                                                &favicon_service, &tracker);
@@ -765,7 +769,8 @@
   ASSERT_THAT(tracker()->GetEntityForSyncId(kId0), NotNull());
   EXPECT_THAT(tracker()->GetEntityForSyncId(kId0)->IsUnsynced(), Eq(false));
 
-  updates_handler()->Process({}, /*got_new_encryption_requirements=*/true);
+  updates_handler()->Process(syncer::UpdateResponseDataList(),
+                             /*got_new_encryption_requirements=*/true);
   EXPECT_THAT(tracker()->GetEntityForSyncId(kId0)->IsUnsynced(), Eq(true));
   // Permanent nodes shouldn't be committed. They are only created on the server
   // and synced down.
@@ -792,12 +797,12 @@
   const std::string kId = "id";
   const std::string kTitle = "title";
   syncer::UpdateResponseDataList updates;
-  syncer::UpdateResponseData response_data =
+  std::unique_ptr<syncer::UpdateResponseData> response_data =
       CreateUpdateResponseData(/*server_id=*/kId,
                                /*parent_id=*/kBookmarkBarId,
                                /*is_deletion=*/false);
-  response_data.encryption_key_name = "encryption_key_name";
-  updates.push_back(response_data);
+  response_data->encryption_key_name = "encryption_key_name";
+  updates.push_back(std::move(response_data));
 
   BookmarkRemoteUpdatesHandler updates_handler(bookmark_model.get(),
                                                &favicon_service, &tracker);
@@ -820,14 +825,15 @@
   // Push a remote deletion for the same entity with an out of date encryption
   // key name.
   updates.clear();
-  response_data = CreateUpdateResponseData(/*server_id=*/kId,
-                                           /*parent_id=*/kBookmarkBarId,
-                                           /*is_deletion=*/true);
-  response_data.encryption_key_name = "out_of_date_encryption_key_name";
+  std::unique_ptr<syncer::UpdateResponseData> response_data2 =
+      CreateUpdateResponseData(/*server_id=*/kId,
+                               /*parent_id=*/kBookmarkBarId,
+                               /*is_deletion=*/true);
+  response_data2->encryption_key_name = "out_of_date_encryption_key_name";
   // Increment the server version to make sure the update isn't discarded as
   // reflection.
-  response_data.response_version++;
-  updates.push_back(response_data);
+  response_data2->response_version++;
+  updates.push_back(std::move(response_data2));
 
   base::HistogramTester histogram_tester;
   updates_handler.Process(updates, /*got_new_encryption_requirements=*/false);
@@ -854,15 +860,17 @@
   EXPECT_THAT(tracker()->GetEntityForSyncId(kId0)->IsUnsynced(), Eq(false));
 
   // Push another update to for the same entity.
-  syncer::UpdateResponseData response_data =
+  std::unique_ptr<syncer::UpdateResponseData> response_data =
       CreateUpdateResponseData(/*server_id=*/kId0,
                                /*parent_id=*/kBookmarkBarId,
                                /*is_deletion=*/false);
 
   // Increment the server version to make sure the update isn't discarded as
   // reflection.
-  response_data.response_version++;
-  updates_handler()->Process({response_data},
+  response_data->response_version++;
+  syncer::UpdateResponseDataList new_updates;
+  new_updates.push_back(std::move(response_data));
+  updates_handler()->Process(new_updates,
                              /*got_new_encryption_requirements=*/true);
   EXPECT_THAT(tracker()->GetEntityForSyncId(kId0)->IsUnsynced(), Eq(false));
 }
diff --git a/components/sync_sessions/session_store.cc b/components/sync_sessions/session_store.cc
index eb0de73..eae3f55 100644
--- a/components/sync_sessions/session_store.cc
+++ b/components/sync_sessions/session_store.cc
@@ -434,7 +434,7 @@
     }
 
     const base::Time mtime =
-        syncer::ProtoTimeToTime(metadata_it->second.modification_time());
+        syncer::ProtoTimeToTime(metadata_it->second->modification_time());
 
     if (specifics.session_tag() != local_session_info_.session_tag) {
       UpdateTrackerWithSpecifics(specifics, mtime, &session_tracker_);
diff --git a/components/sync_sessions/session_sync_bridge_unittest.cc b/components/sync_sessions/session_sync_bridge_unittest.cc
index 75f092c7..07db29d 100644
--- a/components/sync_sessions/session_sync_bridge_unittest.cc
+++ b/components/sync_sessions/session_sync_bridge_unittest.cc
@@ -90,11 +90,11 @@
   return data.PassToPtr();
 }
 
-syncer::UpdateResponseData SpecificsToUpdateResponse(
+std::unique_ptr<syncer::UpdateResponseData> SpecificsToUpdateResponse(
     const sync_pb::SessionSpecifics& specifics,
     base::Time mtime = base::Time::Now()) {
-  syncer::UpdateResponseData data;
-  data.entity = SpecificsToEntity(specifics, mtime);
+  auto data = std::make_unique<syncer::UpdateResponseData>();
+  data->entity = SpecificsToEntity(specifics, mtime);
   return data;
 }
 
@@ -107,14 +107,15 @@
   return storage_key_to_data;
 }
 
-syncer::UpdateResponseData CreateTombstone(const std::string& client_tag) {
+std::unique_ptr<syncer::UpdateResponseData> CreateTombstone(
+    const std::string& client_tag) {
   EntityData tombstone;
   tombstone.client_tag_hash =
       syncer::GenerateSyncableHash(syncer::SESSIONS, client_tag);
 
-  syncer::UpdateResponseData data;
-  data.entity = tombstone.PassToPtr();
-  data.response_version = 2;
+  auto data = std::make_unique<syncer::UpdateResponseData>();
+  data->entity = tombstone.PassToPtr();
+  data->response_version = 2;
   return data;
 }
 
@@ -236,7 +237,7 @@
     for (const SessionSpecifics& specifics : remote_data) {
       initial_updates.push_back(SpecificsToUpdateResponse(specifics));
     }
-    real_processor_->OnUpdateReceived(state, initial_updates);
+    real_processor_->OnUpdateReceived(state, std::move(initial_updates));
   }
 
   std::map<std::string, std::unique_ptr<EntityData>> GetAllData() {
@@ -1267,8 +1268,10 @@
 
   // Mimic receiving a remote deletion of the foreign session.
   EXPECT_CALL(mock_foreign_session_updated_cb(), Run());
-  real_processor()->OnUpdateReceived(
-      state, {CreateTombstone(SessionStore::GetClientTag(foreign_header))});
+  syncer::UpdateResponseDataList updates;
+  updates.push_back(
+      CreateTombstone(SessionStore::GetClientTag(foreign_header)));
+  real_processor()->OnUpdateReceived(state, std::move(updates));
 
   foreign_session_tab = nullptr;
   EXPECT_FALSE(bridge()->GetOpenTabsUIDelegate()->GetForeignTab(
@@ -1364,8 +1367,10 @@
 
   // Mimic receiving a remote deletion of both entities.
   EXPECT_CALL(mock_processor(), Put(_, _, _)).Times(0);
-  real_processor()->OnUpdateReceived(state, {CreateTombstone(kLocalSessionTag),
-                                             CreateTombstone(tab_client_tag1)});
+  syncer::UpdateResponseDataList updates;
+  updates.push_back(CreateTombstone(kLocalSessionTag));
+  updates.push_back(CreateTombstone(tab_client_tag1));
+  real_processor()->OnUpdateReceived(state, std::move(updates));
 
   // State should remain unchanged (deletions ignored).
   EXPECT_THAT(
@@ -1572,7 +1577,7 @@
       Delete(SessionStore::GetTabStorageKey(kStaleSessionTag, kTabNodeId), _));
 
   EXPECT_CALL(mock_foreign_session_updated_cb(), Run()).Times(AtLeast(1));
-  real_processor()->OnUpdateReceived(state, updates);
+  real_processor()->OnUpdateReceived(state, std::move(updates));
 }
 
 }  // namespace
diff --git a/components/update_client/BUILD.gn b/components/update_client/BUILD.gn
index fc11b0c..5f87e658 100644
--- a/components/update_client/BUILD.gn
+++ b/components/update_client/BUILD.gn
@@ -4,26 +4,15 @@
 
 import("//net/features.gni")
 
-source_set("network_public") {
+source_set("network_impl") {
   sources = [
     "net/network_chromium.h",
-  ]
-
-  deps = [
-    "//base",
-  ]
-}
-
-source_set("network") {
-  sources = [
     "net/network_impl.cc",
     "net/network_impl.h",
   ]
 
-  visibility = [ ":*" ]
-
   deps = [
-    ":network_public",
+    ":update_client",
     "//base",
     "//net",
     "//services/network/public/cpp:cpp",
@@ -53,6 +42,14 @@
   ]
 }
 
+group("common_impl") {
+  public_deps = [
+    ":network_impl",
+    ":patch_impl",
+    ":unzip_impl",
+  ]
+}
+
 static_library("update_client") {
   sources = [
     "action_runner.cc",
@@ -125,29 +122,16 @@
     "utils.h",
   ]
 
-  # Allows callers to include the network factory through "update_client" deps.
-  public_deps = [
-    ":network_public",
-  ]
-
   deps = [
-    ":network",
-    ":network_public",
     "//base",
     "//components/client_update_protocol",
     "//components/crx_file",
-    "//components/data_use_measurement/core",
     "//components/prefs",
     "//components/version_info:version_info",
     "//courgette:courgette_lib",
     "//crypto",
     "//url",
   ]
-
-  allow_circular_includes_from = [
-    ":network",
-    ":network_public",
-  ]
 }
 
 static_library("test_support") {
@@ -166,6 +150,7 @@
   ]
 
   deps = [
+    ":network_impl",
     ":patch_impl",
     ":unzip_impl",
     "//base",
@@ -233,7 +218,7 @@
   }
 
   deps = [
-    ":network",
+    ":network_impl",
     ":patch_impl",
     ":test_support",
     ":unit_tests_bundle_data",
diff --git a/components/update_client/DEPS b/components/update_client/DEPS
index 0a397d6..b89a744 100644
--- a/components/update_client/DEPS
+++ b/components/update_client/DEPS
@@ -1,7 +1,6 @@
 include_rules = [
   "+components/client_update_protocol",
   "+components/crx_file",
-  "+components/data_use_measurement/core",
   "+components/services/patch",
   "+components/prefs",
   "+components/services/unzip",
diff --git a/components/update_client/url_fetcher_downloader.cc b/components/update_client/url_fetcher_downloader.cc
index 03eb432..7a550feb 100644
--- a/components/update_client/url_fetcher_downloader.cc
+++ b/components/update_client/url_fetcher_downloader.cc
@@ -14,7 +14,6 @@
 #include "base/sequenced_task_runner.h"
 #include "base/task/post_task.h"
 #include "base/task/task_traits.h"
-#include "components/data_use_measurement/core/data_use_user_data.h"
 #include "components/update_client/network.h"
 #include "components/update_client/utils.h"
 #include "url/gurl.h"
diff --git a/components/update_client/utils.cc b/components/update_client/utils.cc
index 06fa82a..ded9add 100644
--- a/components/update_client/utils.cc
+++ b/components/update_client/utils.cc
@@ -21,7 +21,6 @@
 #include "base/strings/string_util.h"
 #include "base/values.h"
 #include "components/crx_file/id_util.h"
-#include "components/data_use_measurement/core/data_use_user_data.h"
 #include "components/update_client/component.h"
 #include "components/update_client/configurator.h"
 #include "components/update_client/network.h"
diff --git a/components/variations/variations_associated_data.cc b/components/variations/variations_associated_data.cc
index 0dffe8f..07443b18 100644
--- a/components/variations/variations_associated_data.cc
+++ b/components/variations/variations_associated_data.cc
@@ -37,8 +37,9 @@
                    const ActiveGroupId& group_identifier,
                    const VariationID id,
                    const bool force) {
-#if !defined(NDEBUG)
-    DCHECK_EQ(4, ID_COLLECTION_COUNT);
+    static_assert(3 == ID_COLLECTION_COUNT,
+                  "If you add a new collection key, add handling code here!");
+#if DCHECK_IS_ON()
     // Ensure that at most one of the trigger/non-trigger/signed-in web property
     // IDs are set.
     if (key == GOOGLE_WEB_PROPERTIES || key == GOOGLE_WEB_PROPERTIES_TRIGGER ||
diff --git a/components/variations/variations_associated_data.h b/components/variations/variations_associated_data.h
index 0bd96ea..006ecfd 100644
--- a/components/variations/variations_associated_data.h
+++ b/components/variations/variations_associated_data.h
@@ -65,9 +65,6 @@
   // server side experimental behavior, transmitted through the
   // X-Client-Data header.
   GOOGLE_WEB_PROPERTIES_TRIGGER,
-  // This collection is used by Chrome Sync services, transmitted through the
-  // Sync Event Logger.
-  CHROME_SYNC_EVENT_LOGGER,
   // The total count of collections.
   ID_COLLECTION_COUNT,
 };
diff --git a/components/variations/variations_associated_data_unittest.cc b/components/variations/variations_associated_data_unittest.cc
index 31fd334..71602a8f 100644
--- a/components/variations/variations_associated_data_unittest.cc
+++ b/components/variations/variations_associated_data_unittest.cc
@@ -152,54 +152,4 @@
             GetGoogleVariationID(GOOGLE_WEB_PROPERTIES, "trial", "group"));
 }
 
-// Ensure that two collections can coexist without affecting each other.
-TEST_F(VariationsAssociatedDataTest, CollectionsCoexist) {
-  const std::string default_name = "default";
-  int default_group_number = -1;
-  scoped_refptr<base::FieldTrial> trial_true(
-      CreateFieldTrial("d1", 10, default_name, &default_group_number));
-  ASSERT_EQ(default_group_number, trial_true->group());
-  ASSERT_EQ(default_name, trial_true->group_name());
-
-  EXPECT_EQ(EMPTY_ID,
-            GetIDForTrial(GOOGLE_WEB_PROPERTIES, trial_true.get()));
-  EXPECT_EQ(EMPTY_ID,
-            GetIDForTrial(GOOGLE_WEB_PROPERTIES_TRIGGER, trial_true.get()));
-  EXPECT_EQ(EMPTY_ID,
-            GetIDForTrial(CHROME_SYNC_EVENT_LOGGER, trial_true.get()));
-
-  AssociateGoogleVariationID(GOOGLE_WEB_PROPERTIES, trial_true->trial_name(),
-      default_name, TEST_VALUE_A);
-  EXPECT_EQ(TEST_VALUE_A,
-            GetIDForTrial(GOOGLE_WEB_PROPERTIES, trial_true.get()));
-  EXPECT_EQ(EMPTY_ID,
-            GetIDForTrial(CHROME_SYNC_EVENT_LOGGER, trial_true.get()));
-
-  AssociateGoogleVariationID(CHROME_SYNC_EVENT_LOGGER, trial_true->trial_name(),
-                             default_name, TEST_VALUE_A);
-  EXPECT_EQ(TEST_VALUE_A,
-            GetIDForTrial(GOOGLE_WEB_PROPERTIES, trial_true.get()));
-  EXPECT_EQ(TEST_VALUE_A,
-            GetIDForTrial(CHROME_SYNC_EVENT_LOGGER, trial_true.get()));
-
-  trial_true = CreateFieldTrial("d2", 10, default_name, &default_group_number);
-  ASSERT_EQ(default_group_number, trial_true->group());
-  ASSERT_EQ(default_name, trial_true->group_name());
-
-  AssociateGoogleVariationID(GOOGLE_WEB_PROPERTIES_TRIGGER,
-                             trial_true->trial_name(), default_name,
-                             TEST_VALUE_A);
-  EXPECT_EQ(TEST_VALUE_A,
-            GetIDForTrial(GOOGLE_WEB_PROPERTIES_TRIGGER, trial_true.get()));
-  EXPECT_EQ(EMPTY_ID,
-            GetIDForTrial(CHROME_SYNC_EVENT_LOGGER, trial_true.get()));
-
-  AssociateGoogleVariationID(CHROME_SYNC_EVENT_LOGGER, trial_true->trial_name(),
-                             default_name, TEST_VALUE_A);
-  EXPECT_EQ(TEST_VALUE_A,
-            GetIDForTrial(GOOGLE_WEB_PROPERTIES_TRIGGER, trial_true.get()));
-  EXPECT_EQ(TEST_VALUE_A,
-            GetIDForTrial(CHROME_SYNC_EVENT_LOGGER, trial_true.get()));
-}
-
 }  // namespace variations
diff --git a/components/variations/variations_http_header_provider.cc b/components/variations/variations_http_header_provider.cc
index 6865ef24..24c43fc 100644
--- a/components/variations/variations_http_header_provider.cc
+++ b/components/variations/variations_http_header_provider.cc
@@ -264,10 +264,9 @@
       case GOOGLE_WEB_PROPERTIES_TRIGGER:
         proto.add_trigger_variation_id(entry.first);
         break;
-      case CHROME_SYNC_EVENT_LOGGER:
       case ID_COLLECTION_COUNT:
-        // These cases included to get full enum coverage for switch, so that
-        // new enums introduce compiler warnings. Nothing to do for these.
+        // This case included to get full enum coverage for switch, so that
+        // new enums introduce compiler warnings. Nothing to do for this.
         break;
     }
   }
diff --git a/components/variations/variations_seed_processor.cc b/components/variations/variations_seed_processor.cc
index 58e41585..653cf29 100644
--- a/components/variations/variations_seed_processor.cc
+++ b/components/variations/variations_seed_processor.cc
@@ -55,12 +55,6 @@
                                     experiment.name(),
                                     variation_id);
   }
-  if (experiment.has_chrome_sync_experiment_id()) {
-    const VariationID variation_id =
-        static_cast<VariationID>(experiment.chrome_sync_experiment_id());
-    AssociateGoogleVariationIDForce(CHROME_SYNC_EVENT_LOGGER, trial_name,
-                                    experiment.name(), variation_id);
-  }
 }
 
 // Executes |callback| on every override defined by |experiment|.
diff --git a/content/browser/file_url_loader_factory.cc b/content/browser/file_url_loader_factory.cc
index 6b6e001b..5a2d068 100644
--- a/content/browser/file_url_loader_factory.cc
+++ b/content/browser/file_url_loader_factory.cc
@@ -619,12 +619,14 @@
     }
 
     if (!net::GetMimeTypeFromFile(path, &head.mime_type)) {
+      std::string new_type;
       net::SniffMimeType(
           initial_read_buffer, initial_read_result, request.url, head.mime_type,
           GetContentClient()->browser()->ForceSniffingFileUrlsForHtml()
               ? net::ForceSniffFileUrlsForHtml::kEnabled
               : net::ForceSniffFileUrlsForHtml::kDisabled,
-          &head.mime_type);
+          &new_type);
+      head.mime_type.assign(new_type);
       head.did_mime_sniff = true;
     }
     if (head.headers) {
diff --git a/content/browser/frame_host/render_frame_host_manager.cc b/content/browser/frame_host/render_frame_host_manager.cc
index 42163b9..5cb04c8b 100644
--- a/content/browser/frame_host/render_frame_host_manager.cc
+++ b/content/browser/frame_host/render_frame_host_manager.cc
@@ -37,6 +37,7 @@
 #include "content/browser/renderer_host/render_process_host_impl.h"
 #include "content/browser/renderer_host/render_view_host_factory.h"
 #include "content/browser/renderer_host/render_view_host_impl.h"
+#include "content/browser/renderer_host/render_widget_host_impl.h"
 #include "content/browser/site_instance_impl.h"
 #include "content/browser/webui/web_ui_controller_factory_registry.h"
 #include "content/common/frame_messages.h"
@@ -2376,15 +2377,31 @@
 
   bool new_rfh_has_view = !!render_frame_host_->GetView();
 
-  // Show the new RenderWidgetHost if the new frame is a local root and not
-  // hidden or crashed. If the frame is not a local root this is redundant as
-  // the ancestor RenderWidgetHost would already be shown.
-  // TODO(danakj): Is this only really needed for a main frame, because the
-  // RenderWidget is not being newly recreated for the new frame due to
-  // https://crbug.com/419087 ? Would sub frames be marked correctly as shown
-  // from creation of their RenderWidgetHost?
-  if (!delegate_->IsHidden() && new_rfh_has_view)
-    render_frame_host_->GetView()->Show();
+  if (new_rfh_has_view) {
+    if (!delegate_->IsHidden()) {
+      // Show the new RenderWidgetHost if the new frame is a local root and not
+      // hidden or crashed. If the frame is not a local root this is redundant
+      // as the ancestor RenderWidgetHost would already be shown.
+      // TODO(danakj): Is this only really needed for a main frame, because the
+      // RenderWidget is not being newly recreated for the new frame due to
+      // https://crbug.com/419087 ? Would sub frames be marked correctly as
+      // shown from creation of their RenderWidgetHost?
+      render_frame_host_->GetView()->Show();
+    } else {
+      // The Browser side RWHI is initialized as hidden, but the RenderWidget is
+      // not if it is going to be visible after navigation commit. We are
+      // bringing their visibility state into alignment here either by showing
+      // the RWHI or hiding the RenderWidget. See https://crbug/936858 for more
+      // details.
+      // TODO(jonross): This is not needed once https://crbug/419087 is
+      // resolved.
+      RenderWidgetHostImpl* rwhi = RenderWidgetHostImpl::From(
+          render_frame_host_->GetView()->GetRenderWidgetHost());
+      rwhi->SetHiddenOnCommit();
+      // TODO(ejoe): This can be removed if the RenderWidget can be always
+      // created as hidden by default.
+    }
+  }
 
   // The process will no longer try to exit, so we can decrement the count.
   render_frame_host_->GetProcess()->RemovePendingView();
diff --git a/content/browser/frame_host/render_frame_host_manager_unittest.cc b/content/browser/frame_host/render_frame_host_manager_unittest.cc
index 373e229..0f9839b 100644
--- a/content/browser/frame_host/render_frame_host_manager_unittest.cc
+++ b/content/browser/frame_host/render_frame_host_manager_unittest.cc
@@ -33,6 +33,7 @@
 #include "content/common/frame_owner_properties.h"
 #include "content/common/input_messages.h"
 #include "content/common/view_messages.h"
+#include "content/common/widget_messages.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/render_widget_host.h"
 #include "content/public/browser/render_widget_host_iterator.h"
@@ -943,6 +944,45 @@
             *manager->GetRenderWidgetHostView()->GetBackgroundColor());
 }
 
+// Test that we properly set the RenderWidget to be hidden if the visibility of
+// the web contents becomes hidden after the start of navigation but before we
+// commit the navigation. See https://crbug.com/936858 for more details.
+TEST_F(RenderFrameHostManagerTest, WebContentVisibilityHiddenBeforeCommit) {
+  set_should_create_webui(true);
+  scoped_refptr<SiteInstance> blank_instance =
+      SiteInstance::Create(browser_context());
+  blank_instance->GetProcess()->Init();
+
+  // Create a blank tab.
+  std::unique_ptr<TestWebContents> web_contents(
+      TestWebContents::Create(browser_context(), blank_instance));
+  RenderFrameHostManager* manager = web_contents->GetRenderManagerForTesting();
+  manager->current_host()->CreateRenderView(-1, MSG_ROUTING_NONE,
+                                            base::UnguessableToken::Create(),
+                                            FrameReplicationState(), false);
+  EXPECT_TRUE(manager->current_host()->IsRenderViewLive());
+  EXPECT_TRUE(manager->current_frame_host()->IsRenderFrameLive());
+
+  // Start navigation.
+  const GURL kUrl("chrome://foo");
+  NavigationEntryImpl entry(
+      nullptr /* instance */, kUrl, Referrer(), base::string16() /* title */,
+      ui::PAGE_TRANSITION_TYPED, false /* is_renderer_init */,
+      nullptr /* blob_url_loader_factory */);
+  RenderFrameHostImpl* host = NavigateToEntry(manager, &entry);
+
+  // Hide the web contents before commit.
+  web_contents->WasHidden();
+
+  // Commit.
+  manager->DidNavigateFrame(host, true /* was_caused_by_user_gesture */,
+                            false /* is_same_document_navigation */);
+
+  // We should send a WidgetMsg_WasHidden message to the Renderer.
+  auto* rph = static_cast<MockRenderProcessHost*>(host->GetProcess());
+  EXPECT_TRUE(rph->sink().GetUniqueMessageMatching(WidgetMsg_WasHidden::ID));
+}
+
 // Tests WebUI creation.
 TEST_F(RenderFrameHostManagerTest, WebUI) {
   set_should_create_webui(true);
diff --git a/content/browser/renderer_host/render_widget_host_impl.cc b/content/browser/renderer_host/render_widget_host_impl.cc
index edfc500c..9cafebc 100644
--- a/content/browser/renderer_host/render_widget_host_impl.cc
+++ b/content/browser/renderer_host/render_widget_host_impl.cc
@@ -848,6 +848,10 @@
   SynchronizeVisualProperties();
 }
 
+void RenderWidgetHostImpl::SetHiddenOnCommit() {
+  Send(new WidgetMsg_WasHidden(routing_id_));
+}
+
 #if defined(OS_ANDROID)
 void RenderWidgetHostImpl::SetImportance(ChildProcessImportance importance) {
   if (importance_ == importance)
diff --git a/content/browser/renderer_host/render_widget_host_impl.h b/content/browser/renderer_host/render_widget_host_impl.h
index cb0b14ec..e6d6317 100644
--- a/content/browser/renderer_host/render_widget_host_impl.h
+++ b/content/browser/renderer_host/render_widget_host_impl.h
@@ -311,6 +311,11 @@
   void WasShown(bool record_presentation_time,
                 base::TimeTicks tab_switch_start_time = base::TimeTicks());
 
+  // Send a WidgetMsg_WasHidden message to the RenderWidget, without caring
+  // about the visibility state of the RenderWidgetHostImpl. This is used to
+  // address https://crbug.com/936858. See the bug for more details.
+  void SetHiddenOnCommit();
+
 #if defined(OS_ANDROID)
   // Set the importance of widget. The importance is passed onto
   // RenderProcessHost which aggregates importance of all of its widgets.
diff --git a/content/renderer/media/webrtc/rtc_video_decoder_adapter.cc b/content/renderer/media/webrtc/rtc_video_decoder_adapter.cc
index 2373e1e..a933f65 100644
--- a/content/renderer/media/webrtc/rtc_video_decoder_adapter.cc
+++ b/content/renderer/media/webrtc/rtc_video_decoder_adapter.cc
@@ -182,7 +182,7 @@
       config_(config),
       weak_this_factory_(this) {
   DVLOG(1) << __func__;
-  DETACH_FROM_THREAD(decoding_thread_checker_);
+  DETACH_FROM_SEQUENCE(decoding_sequence_checker_);
   weak_this_ = weak_this_factory_.GetWeakPtr();
 }
 
@@ -217,7 +217,7 @@
     const webrtc::VideoCodec* codec_settings,
     int32_t number_of_cores) {
   DVLOG(1) << __func__;
-  DCHECK_CALLED_ON_VALID_THREAD(decoding_thread_checker_);
+  DCHECK_CALLED_ON_VALID_SEQUENCE(decoding_sequence_checker_);
   DCHECK_EQ(webrtc::PayloadStringToCodecType(format_.name),
             codec_settings->codecType);
 
@@ -232,7 +232,7 @@
     const webrtc::CodecSpecificInfo* codec_specific_info,
     int64_t render_time_ms) {
   DVLOG(2) << __func__;
-  DCHECK_CALLED_ON_VALID_THREAD(decoding_thread_checker_);
+  DCHECK_CALLED_ON_VALID_SEQUENCE(decoding_sequence_checker_);
 
   // Hardware VP9 decoders don't handle more than one spatial layer. Fall back
   // to software decoding. See https://crbug.com/webrtc/9304.
@@ -308,7 +308,7 @@
 int32_t RTCVideoDecoderAdapter::RegisterDecodeCompleteCallback(
     webrtc::DecodedImageCallback* callback) {
   DVLOG(2) << __func__;
-  DCHECK_CALLED_ON_VALID_THREAD(decoding_thread_checker_);
+  DCHECK_CALLED_ON_VALID_SEQUENCE(decoding_sequence_checker_);
   DCHECK(callback);
 
   base::AutoLock auto_lock(lock_);
@@ -443,7 +443,7 @@
 
 bool RTCVideoDecoderAdapter::ShouldReinitializeForSettingHDRColorSpace(
     const webrtc::EncodedImage& input_image) const {
-  DCHECK_CALLED_ON_VALID_THREAD(decoding_thread_checker_);
+  DCHECK_CALLED_ON_VALID_SEQUENCE(decoding_sequence_checker_);
 
   if (config_.profile() == media::VP9PROFILE_PROFILE2 &&
       input_image.ColorSpace()) {
@@ -459,7 +459,7 @@
 
 bool RTCVideoDecoderAdapter::ReinitializeSync(
     const media::VideoDecoderConfig& config) {
-  DCHECK_CALLED_ON_VALID_THREAD(decoding_thread_checker_);
+  DCHECK_CALLED_ON_VALID_SEQUENCE(decoding_sequence_checker_);
 
   base::ScopedAllowBaseSyncPrimitivesOutsideBlockingScope allow_wait;
   bool result = false;
diff --git a/content/renderer/media/webrtc/rtc_video_decoder_adapter.h b/content/renderer/media/webrtc/rtc_video_decoder_adapter.h
index a057e86..9df22e1e 100644
--- a/content/renderer/media/webrtc/rtc_video_decoder_adapter.h
+++ b/content/renderer/media/webrtc/rtc_video_decoder_adapter.h
@@ -12,8 +12,8 @@
 #include "base/macros.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/memory/weak_ptr.h"
+#include "base/sequence_checker.h"
 #include "base/synchronization/lock.h"
-#include "base/threading/thread_checker.h"
 #include "content/common/content_export.h"
 #include "media/base/decode_status.h"
 #include "media/base/video_codecs.h"
@@ -128,8 +128,8 @@
   base::circular_deque<base::TimeDelta> decode_timestamps_;
 
   // Thread management.
-  THREAD_CHECKER(worker_thread_checker_);
-  THREAD_CHECKER(decoding_thread_checker_);
+  SEQUENCE_CHECKER(worker_sequence_checker_);
+  SEQUENCE_CHECKER(decoding_sequence_checker_);
 
   base::WeakPtr<RTCVideoDecoderAdapter> weak_this_;
   base::WeakPtrFactory<RTCVideoDecoderAdapter> weak_this_factory_;
diff --git a/content/renderer/pepper/url_request_info_util.cc b/content/renderer/pepper/url_request_info_util.cc
index 90211f1..8492d79 100644
--- a/content/renderer/pepper/url_request_info_util.cc
+++ b/content/renderer/pepper/url_request_info_util.cc
@@ -182,7 +182,7 @@
   dest->SetReportUploadProgress(data->record_upload_progress);
 
   if (!data->method.empty())
-    dest->SetHTTPMethod(WebString::FromUTF8(data->method));
+    dest->SetHttpMethod(WebString::FromUTF8(data->method));
 
   dest->SetSiteForCookies(frame->GetDocument().SiteForCookies());
 
diff --git a/content/renderer/render_view_browsertest.cc b/content/renderer/render_view_browsertest.cc
index 3bb88dc9..f4066ee 100644
--- a/content/renderer/render_view_browsertest.cc
+++ b/content/renderer/render_view_browsertest.cc
@@ -721,7 +721,7 @@
   auto form_navigation_info = std::make_unique<blink::WebNavigationInfo>();
   form_navigation_info->url_request =
       blink::WebURLRequest(GURL("chrome://foo"));
-  form_navigation_info->url_request.SetHTTPMethod("POST");
+  form_navigation_info->url_request.SetHttpMethod("POST");
   form_navigation_info->url_request.SetRequestorOrigin(requestor_origin);
   form_navigation_info->frame_type =
       network::mojom::RequestContextFrameType::kTopLevel;
@@ -823,7 +823,7 @@
   data_navigation_info->url_request =
       blink::WebURLRequest(GURL("data:text/html,foo"));
   data_navigation_info->url_request.SetRequestorOrigin(requestor_origin);
-  data_navigation_info->url_request.SetHTTPMethod("POST");
+  data_navigation_info->url_request.SetHttpMethod("POST");
   data_navigation_info->frame_type =
       network::mojom::RequestContextFrameType::kTopLevel;
   data_navigation_info->navigation_type =
diff --git a/docs/README.md b/docs/README.md
index 18adff55..36cd796 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -263,6 +263,8 @@
     What are .build_config files and how they are used.
 *   [Android App Bundles](../build/android/docs/android_app_bundles.md) -
     How to build Android app bundles for Chrome.
+*   [Dynamic Feature Modules (DFMs)](android_dynamic_feature_modules.md) - How
+    to create dynamic feature modules.
 
 ### Misc iOS-Specific Docs
 *   [Continuous Build and Test Infrastructure for Chromium for iOS](ios/infra.md)
diff --git a/docs/android_dynamic_feature_modules.md b/docs/android_dynamic_feature_modules.md
new file mode 100644
index 0000000..7401805b
--- /dev/null
+++ b/docs/android_dynamic_feature_modules.md
@@ -0,0 +1,687 @@
+# Dynamic Feature Modules (DFMs)
+
+[Android App bundles and Dynamic Feature Modules (DFMs)](https://developer.android.com/guide/app-bundle)
+is a Play Store feature that allows delivering pieces of an app when they are
+needed rather than at install time. We use DFMs to modularize Chrome and make
+Chrome's install size smaller.
+
+[TOC]
+
+
+## Limitations
+
+Currently (March 2019), DFMs have the following limitations:
+
+* **WebView:** We don't support DFMs for WebView. If your feature is used by
+  WebView you cannot put it into a DFM. See
+  [crbug/949717](https://bugs.chromium.org/p/chromium/issues/detail?id=949717)
+  for progress.
+* **Android K:** DFMs are based on split APKs, a feature introduced in Android
+  L. Therefore, we don't support DFMs on Android K. As a workaround
+  you can add your feature to the Android K APK build. See
+  [crbug/881354](https://bugs.chromium.org/p/chromium/issues/detail?id=881354)
+  for progress.
+* **Native Code:** We cannot move native Chrome code into a DFM. See
+  [crbug/874564](https://bugs.chromium.org/p/chromium/issues/detail?id=874564)
+  for progress.
+
+## Getting started
+
+This guide walks you through the steps to create a DFM called _Foo_ and add it
+to the public Monochrome bundle. If you want to ship a DFM, you will also have
+to add it to the public Chrome Modern and Trichrome Chrome bundle as well as the
+downstream bundles.
+
+*** note
+**Note:** To make your own module you'll essentially have to replace every
+instance of `foo`/`Foo`/`FOO` with `your_feature_name`/`YourFeatureName`/
+`YOUR_FEATURE_NAME`.
+***
+
+
+### Create DFM target
+
+DFMs are APKs. They have a manifest and can contain Java and native code as well
+as resources. This section walks you through creating the module target in our
+build system.
+
+First, create the file `//chrome/android/features/foo/java/AndroidManifest.xml`
+and add:
+
+```xml
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:dist="http://schemas.android.com/apk/distribution"
+    featureSplit="foo"
+    package="{{manifest_package}}">
+
+    <!-- For Chrome Modern use android:minSdkVersion="21". -->
+    <uses-sdk
+        android:minSdkVersion="24"
+        android:targetSdkVersion="{{target_sdk_version}}" />
+
+    <!-- dist:onDemand="true" makes this a separately installed module.
+         dist:onDemand="false" would always install the module alongside the
+         rest of Chrome. -->
+    <dist:module
+        dist:onDemand="true"
+        dist:title="@string/foo_module_title">
+        <!-- This will prevent the module to become part of the Android K
+             build in case we ever want to use bundles on Android K. -->
+        <dist:fusing dist:include="false" />
+    </dist:module>
+
+    <!-- Remove hasCode="false" when adding Java code. -->
+    <application hasCode="false" />
+</manifest>
+```
+
+Then, add a package ID for Foo so that Foo's resources have unique identifiers.
+For this, add a new ID to
+`//chrome/android/features/module_names_to_package_ids.gni`:
+
+```gn
+resource_packages_id_mapping = [
+  ...,
+  "foo=0x{XX}", # Set {XX} to next lower hex number.
+]
+```
+
+Next, create a template that contains the Foo module target.
+
+*** note
+**Note:** We put the module target into a template because we have to
+instantiate it for each Chrome bundle (Chrome Modern, Monochrome and Trichrome
+for both upstream and downstream) you want to ship your module in.
+***
+
+To do this, create `//chrome/android/features/foo/foo_module_tmpl.gni` and add
+the following:
+
+```gn
+import("//build/config/android/rules.gni")
+import("//build/config/locales.gni")
+import("//chrome/android/features/module_names_to_package_ids.gni")
+
+template("foo_module_tmpl") {
+  _manifest = "$target_gen_dir/$target_name/AndroidManifest.xml"
+  _manifest_target = "${target_name}__manifest"
+  jinja_template(_manifest_target) {
+    input = "//chrome/android/features/foo/java/AndroidManifest.xml"
+    output = _manifest
+    variables = [
+      "target_sdk_version=$android_sdk_version",
+      "manifest_package=${invoker.manifest_package}",
+    ]
+  }
+
+  android_app_bundle_module(target_name) {
+    forward_variables_from(invoker,
+                           [
+                             "base_module_target",
+                             "module_name",
+                             "uncompress_shared_libraries",
+                             "version_code",
+                             "version_name",
+                           ])
+    android_manifest = _manifest
+    android_manifest_dep = ":${_manifest_target}"
+    proguard_enabled = !is_java_debug
+    aapt_locale_whitelist = locales
+    package_name = "foo"
+    package_name_to_id_mapping = resource_packages_id_mapping
+  }
+}
+```
+
+Then, instantiate the module template in `//chrome/android/BUILD.gn` inside the
+`monochrome_public_bundle_tmpl` template and add it to the bundle target:
+
+```gn
+...
+import("modules/foo/foo_module_tmpl.gni")
+...
+template("monochrome_public_bundle_tmpl") {
+  ...
+  foo_module_tmpl("${target_name}__foo_bundle_module") {
+    manifest_package = manifest_package
+    module_name = "Foo" + _bundle_name
+    base_module_target = ":$_base_module_target_name"
+    version_code = monochrome_version_code
+    version_name = chrome_version_name
+    uncompress_shared_libraries = true
+  }
+  ...
+  android_app_bundle(target_name) {
+    ...
+    extra_modules += [
+      {
+        name = "foo"
+        module_target = ":${target_name}__foo_bundle_module"
+      },
+    ]
+  }
+}
+```
+
+The next step is to add Foo to the list of feature modules for UMA recording.
+For this, add `foo` to the `AndroidFeatureModuleName` in
+`//tools/metrics/histograms/histograms.xml`:
+
+```xml
+<histogram_suffixes name="AndroidFeatureModuleName" ...>
+  ...
+  <suffix name="foo" label="Super Duper Foo Module" />
+  ...
+</histogram_suffixes>
+```
+
+<!--- TODO(tiborg): Add info about install UI. -->
+Lastly, give your module a title that Chrome and Play can use for the install
+UI. To do this, add a string to
+`//chrome/android/java/strings/android_chrome_strings.grd`:
+
+```xml
+...
+<message name="IDS_FOO_MODULE_TITLE"
+  desc="Text shown when the Foo module is referenced in install start, success,
+        failure UI (e.g. in IDS_MODULE_INSTALL_START_TEXT, which will expand to
+        'Installing Foo for Chrome…').">
+  Foo
+</message>
+...
+```
+
+Congrats! You added the DFM Foo to Monochrome. That is a big step but not very
+useful so far. In the next sections you'll learn how to add code and resources
+to it.
+
+
+### Building and installing modules
+
+Before we are going to jump into adding content to Foo, let's take a look on how
+to build and deploy the Monochrome bundle with the Foo DFM. The remainder of
+this guide assumes the environment variable `OUTDIR` is set to a properly
+configured GN build directory (e.g. `out/Debug`).
+
+To build and install the Monochrome bundle to your connected device, run:
+
+```shell
+$ autoninja -C $OUTDIR monochrome_public_bundle
+$ $OUTDIR/bin/monochrome_public_bundle install -m base -m foo
+```
+
+This will install Foo alongside the rest of Chrome. The rest of Chrome is called
+_base_ module in the bundle world. The Base module will always be put on the
+device when initially installing Chrome.
+
+*** note
+**Note:** You have to specify `-m base` here to make it explicit which modules
+will be installed. If you only specify `-m foo` the command will fail. It is
+also possible to specify no modules. In that case, the script will install the
+set of modules that the Play Store would install when first installing Chrome.
+That may be different than just specifying `-m base` if we have non-on-demand
+modules.
+***
+
+You can then check that the install worked with:
+
+```shell
+$ adb shell dumpsys package org.chromium.chrome | grep splits
+>   splits=[base, config.en, foo]
+```
+
+Then try installing the Monochrome bundle without your module and print the
+installed modules:
+
+```shell
+$ $OUTDIR/bin/monochrome_public_bundle install -m base
+$ adb shell dumpsys package org.chromium.chrome | grep splits
+>   splits=[base, config.en]
+```
+
+
+### Adding java code
+
+To make Foo useful, let's add some Java code to it. This section will walk you
+through the required steps.
+
+First, define a module interface in the new file
+`//chrome/android/features/foo/public/java/src/org/chromium/chrome/features/foo/Foo.java`:
+
+```java
+package org.chromium.chrome.features.foo;
+
+/** Interface to call into Foo feature. */
+public interface Foo {
+    /** Magical function. */
+    void bar();
+}
+```
+
+*** note
+**Note:** To reflect the separation from "Chrome browser" code, features should
+be defined in their own package name, distinct from the chrome package - i.e.
+`org.chromium.chrome.features.<feature-name>`.
+***
+
+Next, define an implementation that goes into the module in the new file
+`//chrome/android/features/foo/java/src/org/chromium/chrome/features/foo/FooImpl.java`:
+
+```java
+package org.chromium.chrome.features.foo;
+
+import org.chromium.base.Log;
+
+public class FooImpl implements Foo {
+    @Override
+    public void bar() {
+        Log.i("FOO", "bar in module");
+    }
+}
+```
+
+In order to get the Foo implementation depending on whether the Foo DFM
+is present, we will add a module provider class handling that logic. For
+this, create the file
+`//chrome/android/features/foo/public/java/src/org/chromium/chrome/features/foo/FooModuleProvider.java`
+and add:
+
+```java
+package org.chromium.chrome.features.foo;
+
+/** Provides the Foo implementation. */
+public class FooModuleProvider {
+    private static Foo sFoo;
+
+    /**
+     * Returns Foo implementation or null if Foo module is not installed.
+     */
+    public static Foo getFoo {
+        if (sFoo == null) {
+            try {
+                sFoo = (Foo) Class
+                    .forName("org.chromium.chrome.features.foo.FooImpl")
+                    .newInstance();
+            } catch (ClassNotFoundException | InstantiationException
+                    | IllegalAccessException | IllegalArgumentException e) {
+                // Foo module is not installed. Leave sFoo as null.
+            }
+        }
+        return sFoo;
+    }
+}
+```
+
+You can then use this provider to access the module if it is installed. To test
+that, instantiate Foo and call `bar()` somewhere in Chrome:
+
+```java
+if (FooModuleProvider.getFoo() != null) {
+    FooModuleProvider.getFoo().bar();
+} else {
+    Log.i("FOO", "module not installed");
+}
+```
+
+The interface and module provider have to be available regardless of whether the
+Foo DFM is present. Therefore, put those classes into the base module. For this
+create a list of those Java files in
+`//chrome/android/features/foo/public/foo_public_java_sources.gni`:
+
+```gn
+foo_public_java_sources = [
+  "//chrome/android/features/foo/public/java/src/org/chromium/chrome/features/foo/Foo.java",
+  "//chrome/android/features/foo/public/java/src/org/chromium/chrome/features/foo/FooModuleProvider.java",
+]
+```
+
+Then add this list to `chrome_java in //chrome/android/BUILD.gn`:
+
+```gn
+...
+import("modules/foo/public/foo_public_java_sources.gni")
+...
+android_library("chrome_java") {
+  ...
+  java_files += foo_public_java_sources
+}
+...
+```
+
+The actual implementation, however, should go into the Foo DFM. For this
+purpose, create a new file `//chrome/android/features/foo/BUILD.gn` and make a
+library with the module Java code in it:
+
+```gn
+import("//build/config/android/rules.gni")
+
+android_library("java") {
+  # Define like ordinary Java Android library.
+  java_files = [
+    "java/src/org/chromium/chrome/features/foo/FooImpl.java",
+    # Add other Java classes that should go into the Foo DFM here.
+  ]
+  # Put other Chrome libs into the classpath so that you can call into the rest
+  # of Chrome from the Foo DFM.
+  classpath_deps = [
+    "//base:base_java",
+    "//chrome/android:chrome_java",
+    # etc.
+    # Also, you'll need to depend on any //third_party or //components code you
+    # are using in the module code.
+  ]
+}
+```
+
+Then, add this new library as a dependency of the Foo module target in
+`//chrome/android/features/foo/foo_module_tmpl.gni`:
+
+```gn
+android_app_bundle_module(target_name) {
+  ...
+  deps = [
+    "//chrome/android/module/foo:java",
+  ]
+}
+```
+
+Finally, tell Android that your module is now containing code. Do that by
+removing the `hasCode="false"` attribute from the `<application>` tag in
+`//chrome/android/features/foo/java/AndroidManifest.xml`. You should be left
+with an empty tag like so:
+
+```xml
+...
+    <application />
+...
+```
+
+Rebuild and install `monochrome_public_bundle`. Start Chrome and run through a
+flow that tries to executes `bar()`. Depending on whether you installed your
+module (`-m foo`) "`bar in module`" or "`module not installed`" is printed to
+logcat. Yay!
+
+
+### Adding native code
+
+Coming soon (
+[crbug/874564](https://bugs.chromium.org/p/chromium/issues/detail?id=874564)).
+
+You can already add third party native code or native Chrome code that has no
+dependency on other Chrome code. To add such code add it as a loadable module to
+the bundle module target in `//chrome/android/features/foo/foo_module_tmpl.gni`:
+
+```gn
+...
+template("foo_module_tmpl") {
+  ...
+  android_app_bundle_module(target_name) {
+    ...
+    loadable_modules = [ "//path/to/lib.so" ]
+  }
+}
+```
+
+
+### Adding android resources
+
+In this section we will add the required build targets to add Android resources
+to the Foo DFM.
+
+First, add a resources target to `//chrome/android/features/foo/BUILD.gn` and
+add it as a dependency on Foo's `java` target in the same file:
+
+```gn
+...
+android_resources("java_resources") {
+  # Define like ordinary Android resources target.
+  ...
+  custom_package = "org.chromium.chrome.features.foo"
+}
+...
+android_library("java") {
+  ...
+  deps = [
+    ":java_resources",
+  ]
+}
+```
+
+To add strings follow steps
+[here](http://dev.chromium.org/developers/design-documents/ui-localization) to
+add new Java GRD file. Then create
+`//chrome/android/features/foo/java/strings/android_foo_strings.grd` as follows:
+
+```xml
+<?xml version="1.0" encoding="UTF-8"?>
+<grit
+    current_release="1"
+    latest_public_release="0"
+    output_all_resource_defines="false">
+  <outputs>
+    <output
+        filename="values-am/android_foo_strings.xml"
+        lang="am"
+        type="android" />
+    <!-- List output file for all other supported languages. See
+         //chrome/android/java/strings/android_chrome_strings.grd for the full
+         list. -->
+    ...
+  </outputs>
+  <translations>
+    <file lang="am" path="vr_translations/android_foo_strings_am.xtb" />
+    <!-- Here, too, list XTB files for all other supported languages. -->
+    ...
+  </translations>
+  <release allow_pseudo="false" seq="1">
+    <messages fallback_to_english="true">
+      <message name="IDS_BAR_IMPL_TEXT" desc="Magical string.">
+        impl
+      </message>
+    </messages>
+  </release>
+</grit>
+```
+
+Then, create a new GRD target and add it as a dependency on `java_resources` in
+`//chrome/android/features/foo/BUILD.gn`:
+
+```gn
+...
+java_strings_grd("java_strings_grd") {
+  defines = chrome_grit_defines
+  grd_file = "java/strings/android_foo_strings.grd"
+  outputs = [
+    "values-am/android_foo_strings.xml",
+    # Here, too, list output files for other supported languages.
+    ...
+  ]
+}
+...
+android_resources("java_resources") {
+  ...
+  deps = [":java_strings_grd"]
+  custom_package = "org.chromium.chrome.features.foo"
+}
+...
+```
+
+You can then access Foo's resources using the
+`org.chromium.chrome.features.foo.R` class. To do this change
+`//chrome/android/features/foo/java/src/org/chromium/chrome/features/foo/FooImpl.java`
+to:
+
+```java
+package org.chromium.chrome.features.foo;
+
+import org.chromium.base.ContextUtils;
+import org.chromium.base.Log;
+import org.chromium.chrome.features.foo.R;
+
+public class FooImpl implements Foo {
+    @Override
+    public void bar() {
+        Log.i("FOO", ContextUtils.getApplicationContext().getString(
+                R.string.bar_impl_text));
+    }
+}
+```
+
+*** note
+**Warning:** While your module is emulated (see [below](#on-demand-install))
+your resources are only available through
+`ContextUtils.getApplicationContext()`. Not through activities, etc. We
+therefore recommend that you only access DFM resources this way. See
+[crbug/949729](https://bugs.chromium.org/p/chromium/issues/detail?id=949729)
+for progress on making this more robust.
+***
+
+
+### Module install
+
+So far, we have installed the Foo DFM as a true split (`-m foo` option on the
+install script). In production, however, we have to explicitly install the Foo
+DFM for users to get it. There are two install options: _on-demand_ and
+_deferred_.
+
+
+#### On-demand install
+
+On-demand requesting a module will try to download and install the
+module as soon as possible regardless of whether the user is on a metered
+connection or whether they have turned updates off in the Play Store app.
+
+To request a module on-demand we can make use of the `ModuleInstaller` from
+`//components/module_installer/`. For this add, the following function to
+`FooModuleProvider` in
+`//chrome/android/features/foo/public/java/src/org/chromium/chrome/foo/FooModuleProvider.java`:
+
+```java
+/**
+ * On-demand install Foo module.
+ * @param onFinishedListener listener to be called when install has finished.
+ */
+public static installModule(
+        OnModuleInstallFinishedListener onFinishedListener) {
+    ModuleInstaller.install("foo", (success) -> {
+        if (success) {
+            assert getFoo() != null;
+        }
+        onFinishedListener.onFinished(success);
+    });
+}
+```
+
+Then, use this new function to request the module and call `bar()` on install
+completion:
+
+```java
+// Need to call init before accessing any modules. Can be called multiple times.
+ModuleInstaller.init();
+FooModuleProvider.installModule((success) -> {
+    FooModuleProvider.getFoo().bar();
+});
+```
+
+**Optionally**, you can show UI telling the user about the install flow. For
+this, add the function below to `FooModuleProvider`. Then use
+`installModuleWithUi(...)` instead of `installModule(...)`. Note, it is possible
+to only show either one of the  install, failure and success UI or any
+combination of the three.
+
+```java
+public static void installModuleWithUi(
+        Tab tab, OnModuleInstallFinishedListener onFinishedListener) {
+    ModuleInstallUi ui =
+            new ModuleInstallUi(
+                    tab,
+                    R.string.foo_module_title,
+                    new ModuleInstallUi.FailureUiListener() {
+                        @Override
+                        public void onRetry() {
+                            installModuleWithUi(tab, onFinishedListener);
+                        }
+
+                        @Override
+                        public void onCancel() {
+                            onFinishedListener.onFinished(false);
+                        }
+                    });
+    // At the time of writing, shows toast informing user about install start.
+    ui.showInstallStartUi();
+    installModule(
+            (success) -> {
+                if (!success) {
+                    // At the time of writing, shows infobar allowing user
+                    // to retry install.
+                    ui.showInstallFailureUi();
+                    return;
+                }
+                // At the time of writing, shows toast informing user about
+                // install success.
+                ui.showInstallSuccessUi();
+                onFinishedListener.onFinished(true);
+            });
+}
+```
+
+To test on-demand install, "fake-install" the DFM. It's fake because
+the DFM is not installed as a true split. Instead it will be emulated by Chrome.
+Fake-install and launch Chrome with the following command:
+
+```shell
+$ $OUTDIR/bin/monochrome_public_bundle install -m base -f foo
+$ $OUTDIR/bin/monochrome_public_bundle launch \
+    --args="--fake-feature-module-install"
+```
+
+When running the install code, the Foo DFM module will be emulated.
+This will be the case in production right after installing the module. Emulation
+will last until Play Store has a chance to install your module as a true split.
+This usually takes about a day.
+
+*** note
+**Warning:** There are subtle differences between emulating a module and
+installing it as a true split. We therefore recommend that you always test both
+install methods.
+***
+
+
+#### Deferred install
+
+Deferred install means that the DFM is installed in the background when the
+device is on an unmetered connection and charging. The DFM will only be
+available after Chrome restarts. When deferred installing a module it will
+not be faked installed.
+
+To defer install Foo do the following:
+
+```java
+ModuleInstaller.installDeferred("foo");
+```
+
+
+### Integration test APK and Android K support
+
+On Android K we still ship an APK. To make the Foo feature available on Android
+K add its code to the APK build. For this, add the `java` target to
+the `chrome_public_common_apk_or_module_tmpl` in
+`//chrome/android/chrome_public_apk_tmpl.gni` like so:
+
+```gn
+template("chrome_public_common_apk_or_module_tmpl") {
+  ...
+  target(_target_type, target_name) {
+    ...
+    if (_target_type != "android_app_bundle_module") {
+      deps += [
+        "//chrome/android/module/foo:java",
+      ]
+    }
+  }
+}
+```
+
+This will also add Foo's Java to the integration test APK. You may also have to
+add `java` as a dependency of `chrome_test_java` if you want to call into Foo
+from test code.
diff --git a/docs/android_emulator.md b/docs/android_emulator.md
index 68fd764..394fac5 100644
--- a/docs/android_emulator.md
+++ b/docs/android_emulator.md
@@ -79,8 +79,7 @@
 You can run an emulator without creating a window on your desktop (useful for
 `ssh`):
 ```shell
-$ sudo apt-get install xvfb-run
-$ xvfb-run ~/Android/Sdk/emulator/emulator -gpu off @EMULATOR_ID
+$ ~/Android/Sdk/emulator/emulator -no-window @EMULATOR_ID
 ```
 
 ### Writable system partition
diff --git a/extensions/browser/url_loader_factory_manager.cc b/extensions/browser/url_loader_factory_manager.cc
index 3365702..56bbb4fc 100644
--- a/extensions/browser/url_loader_factory_manager.cc
+++ b/extensions/browser/url_loader_factory_manager.cc
@@ -62,6 +62,7 @@
 const char* kHardcodedPartOfCorbAllowlist[] = {
     "0149C10F1124F1ED6ACAD85C45E87A76A9DDC667",
     "039F93DD1DF836F1D4E2084C1BEFDB46A854A9D1",
+    "03E5D80A49C309F7B55ED6BD2B0EDEB38021ED4E",
     "072D729E856B1F2C9894AEEC3A5DF65E519D6BEE",
     "07333481B7B8D7F57A9BA64FB98CF86EA87455FC",
     "086E69ED9071DCB20C93A081A68360963AB09385",
@@ -92,11 +93,13 @@
     "2AA94E2D3F4DA33F0D3BCF5DD48F69B8BDB26F52",
     "2C116B242B7425D359E188AB053B3F88DB78F78D",
     "2E2D8A405430172AB15ADCC09740F3EEE990D605",
+    "30E95BD31B11118CE488EB4FC5FF7135E0C59425",
     "31E6100DC7B4EAB4ABF6CA2A4E191D3945D3C731",
     "3230014EA01150A27C1887B700E019B27A6DBA08",
     "3334952C8387B357A41DD8349D39AD9E7C423943",
     "34FB670464B5F16EF5ABD6CD53E26030E97C26B3",
     "360D8A88545D0C21CBDE2B55EA9E4E4285282B4C",
+    "3787567233ED6BACC4FC05387BE30E03434CE4CC",
     "37AC33A3A46D271CCE57DD6CB3FACE6B01F5A347",
     "38B4D1CC339580F506BC86D4027A49721AFB4BB9",
     "3BC834B48C2C13765147FBAD710F792F026378D8",
@@ -112,6 +115,7 @@
     "4884C48FB64665BD86509BA1F2D22D75D61ACBF9",
     "4913450195D177430217CAB64B356DC6F6B0F483",
     "49FFDF2212E50090963E33159DBF853B5C475340",
+    "4A4E0B370B65C9EC65C8A615B1B9FB55A1B3E1FD",
     "4A8EEEB4754A3E87C3A177CB31474A30967CB3C8",
     "4E4167EDA0CFF22F261C0655E979B9474BF67C04",
     "5053323D1F7B6EEC97A77A350DB6D0D8E51CD0AC",
@@ -166,6 +170,7 @@
     "93934B0B87347437699EB62A8921F59F40C36D7A",
     "93BBF911E8871F6FCC8170448FD2DF5B9EF233E5",
     "95E78675D2DB61DC688586CD7A24202A260907A4",
+    "965A185A30475F208A7365E134F48C64CF08C997",
     "973E35633030AD27DABEC99609424A61386C7309",
     "97E04C5632954E778306CAC40B3F95C470B463B6",
     "98EF7B1601119AEE1FCC28EE5CE247DED5676539",
@@ -228,6 +233,7 @@
     "F608282162AD48CE45D5BC2F6F467B56E88EBFA4",
     "F73F9EF0207603992CA3C00A7A0CB223D5571B3F",
     "F9287A33E15038F2591F23E6E9C486717C7202DD",
+    "FCB9E071ACBA414EDA9AD90973B55062AE665827",
     "FCC2DC6574A3CA28ED77195926C67F612292C5C3",
     "FEE3DC8C722657A4A5B0F72CA48CF950DC956148",
     "FF0DA4BD87A88469B10709B99E79D4B0E11C0CA6",
diff --git a/gpu/ipc/service/direct_composition_surface_win.cc b/gpu/ipc/service/direct_composition_surface_win.cc
index d8470b1..969d5f37 100644
--- a/gpu/ipc/service/direct_composition_surface_win.cc
+++ b/gpu/ipc/service/direct_composition_surface_win.cc
@@ -479,7 +479,8 @@
   // |protected_video_type|. Returns true on success.
   bool ReallocateSwapChain(const gfx::Size& swap_chain_size,
                            bool use_yuv_swap_chain,
-                           ui::ProtectedVideoType protected_video_type);
+                           ui::ProtectedVideoType protected_video_type,
+                           bool z_order);
 
   // Returns true if YUV swap chain should be preferred over BGRA swap chain.
   // This changes over time based on stats recorded in |presentation_history|.
@@ -1237,7 +1238,7 @@
   if (!swap_chain_ || swap_chain_resized || toggle_yuv_swapchain ||
       toggle_protected_video) {
     if (!ReallocateSwapChain(swap_chain_size, use_yuv_swap_chain,
-                             params.protected_video_type)) {
+                             params.protected_video_type, params.z_order)) {
       ReleaseSwapChainResources();
       return false;
     }
@@ -1558,7 +1559,8 @@
 bool DCLayerTree::SwapChainPresenter::ReallocateSwapChain(
     const gfx::Size& swap_chain_size,
     bool use_yuv_swap_chain,
-    ui::ProtectedVideoType protected_video_type) {
+    ui::ProtectedVideoType protected_video_type,
+    bool z_order) {
   TRACE_EVENT2("gpu", "DCLayerTree::SwapChainPresenter::ReallocateSwapChain",
                "size", swap_chain_size.ToString(), "yuv", use_yuv_swap_chain);
 
@@ -1681,6 +1683,16 @@
       DLOG(ERROR) << "Failed to create BGRA swap chain of size "
                   << swap_chain_size.ToString() << " with error 0x" << std::hex
                   << hr;
+
+      // TODO(magchen): Temporary for debugging underlay swap chain failures.
+      bool supports_scaled_overlays = g_supports_scaled_overlays;
+      gfx::Size overlay_monitor_size = g_overlay_monitor_size;
+      base::debug::Alias(&hr);
+      base::debug::Alias(&supports_scaled_overlays);
+      base::debug::Alias(&overlay_monitor_size);
+      base::debug::Alias(&swap_chain_size);
+      base::debug::Alias(&z_order);
+      base::debug::DumpWithoutCrashing();
       return false;
     }
   }
diff --git a/gpu/vulkan/vulkan_surface.cc b/gpu/vulkan/vulkan_surface.cc
index 7ae845b..ad7919b7 100644
--- a/gpu/vulkan/vulkan_surface.cc
+++ b/gpu/vulkan/vulkan_surface.cc
@@ -106,8 +106,7 @@
       return false;
     }
   }
-  // Delay creating SwapChain to when the surface size is specified by Resize().
-  return true;
+  return CreateSwapChain(gfx::Size());
 }
 
 void VulkanSurface::Destroy() {
@@ -129,6 +128,10 @@
 }
 
 bool VulkanSurface::SetSize(const gfx::Size& size) {
+  return CreateSwapChain(size);
+}
+
+bool VulkanSurface::CreateSwapChain(const gfx::Size& size) {
   // Get Surface Information.
   VkSurfaceCapabilitiesKHR surface_caps;
   VkResult result = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(
diff --git a/gpu/vulkan/vulkan_surface.h b/gpu/vulkan/vulkan_surface.h
index 440e0f66..08260c21 100644
--- a/gpu/vulkan/vulkan_surface.h
+++ b/gpu/vulkan/vulkan_surface.h
@@ -50,6 +50,8 @@
   VkSurfaceFormatKHR surface_format() const { return surface_format_; }
 
  private:
+  bool CreateSwapChain(const gfx::Size& size);
+
   const VkInstance vk_instance_;
   gfx::Size size_;
   VkSurfaceKHR surface_ = VK_NULL_HANDLE;
diff --git a/gpu/vulkan/x/vulkan_surface_x11.cc b/gpu/vulkan/x/vulkan_surface_x11.cc
index 7efcd56..4801bf3 100644
--- a/gpu/vulkan/x/vulkan_surface_x11.cc
+++ b/gpu/vulkan/x/vulkan_surface_x11.cc
@@ -86,9 +86,15 @@
     VkInstance vk_instance,
     Window parent_window) {
   XDisplay* display = gfx::GetXDisplay();
-  Window window =
-      XCreateWindow(display, parent_window, 0, 0, 1, 1, 0, CopyFromParent,
-                    InputOutput, CopyFromParent, 0, nullptr);
+  XWindowAttributes attributes;
+  if (!XGetWindowAttributes(display, parent_window, &attributes)) {
+    LOG(ERROR) << "XGetWindowAttributes failed for window " << parent_window
+               << ".";
+    return nullptr;
+  }
+  Window window = XCreateWindow(display, parent_window, 0, 0, attributes.width,
+                                attributes.height, 0, CopyFromParent,
+                                InputOutput, CopyFromParent, 0, nullptr);
   if (!window) {
     LOG(ERROR) << "XCreateWindow failed.";
     return nullptr;
diff --git a/ios/chrome/browser/component_updater/BUILD.gn b/ios/chrome/browser/component_updater/BUILD.gn
index 09b3fb8..273566f6 100644
--- a/ios/chrome/browser/component_updater/BUILD.gn
+++ b/ios/chrome/browser/component_updater/BUILD.gn
@@ -11,8 +11,7 @@
     "//base",
     "//components/component_updater",
     "//components/update_client",
-    "//components/update_client:patch_impl",
-    "//components/update_client:unzip_impl",
+    "//components/update_client:common_impl",
     "//ios/chrome/browser",
     "//ios/chrome/browser/google",
     "//ios/chrome/common",
diff --git a/ios/chrome/browser/sync/BUILD.gn b/ios/chrome/browser/sync/BUILD.gn
index 9a9af94..66f2e2f9 100644
--- a/ios/chrome/browser/sync/BUILD.gn
+++ b/ios/chrome/browser/sync/BUILD.gn
@@ -54,6 +54,7 @@
     "//components/sessions",
     "//components/signin/core/browser",
     "//components/sync",
+    "//components/sync:user_events",
     "//components/sync_preferences",
     "//components/sync_sessions",
     "//components/version_info",
diff --git a/ios/chrome/test/app/sync_test_util.mm b/ios/chrome/test/app/sync_test_util.mm
index f218aca5..821c5b6 100644
--- a/ios/chrome/test/app/sync_test_util.mm
+++ b/ios/chrome/test/app/sync_test_util.mm
@@ -193,17 +193,19 @@
   std::vector<sync_pb::SyncEntity> autofill_profiles =
       gSyncFakeServer->GetSyncEntitiesByModelType(syncer::AUTOFILL_PROFILE);
   std::string entity_id;
+  std::string client_tag_hash;
   for (const sync_pb::SyncEntity& autofill_profile : autofill_profiles) {
     if (autofill_profile.specifics().autofill_profile().guid() == guid) {
       entity_id = autofill_profile.id_string();
+      client_tag_hash = autofill_profile.client_defined_unique_tag();
       break;
     }
   }
   // Delete the entity if it exists.
   if (!entity_id.empty()) {
     std::unique_ptr<syncer::LoopbackServerEntity> entity;
-    entity =
-        syncer::PersistentTombstoneEntity::CreateNew(entity_id, std::string());
+    entity = syncer::PersistentTombstoneEntity::CreateNew(entity_id,
+                                                          client_tag_hash);
     gSyncFakeServer->InjectEntity(std::move(entity));
   }
 }
diff --git a/ios/web_view/BUILD.gn b/ios/web_view/BUILD.gn
index 0d6b08e4..ef2be60 100644
--- a/ios/web_view/BUILD.gn
+++ b/ios/web_view/BUILD.gn
@@ -292,6 +292,7 @@
   "//components/signin/ios/browser:active_state_manager",
   "//components/strings:components_strings_grit",
   "//components/sync",
+  "//components/sync:user_events",
   "//components/language/ios/browser",
   "//components/sync_sessions",
   "//components/translate/core/browser",
diff --git a/media/gpu/BUILD.gn b/media/gpu/BUILD.gn
index 016b6c05..eccf4872 100644
--- a/media/gpu/BUILD.gn
+++ b/media/gpu/BUILD.gn
@@ -307,6 +307,9 @@
   if (use_v4l2_codec) {
     deps += [ "//media/gpu/v4l2" ]
   }
+  if (is_linux) {
+    deps += [ ":video_frame_mapper" ]
+  }
 }
 
 source_set("image_processor_common") {
@@ -326,6 +329,41 @@
   ]
 }
 
+source_set("video_frame_mapper") {
+  defines = [ "MEDIA_GPU_IMPLEMENTATION" ]
+  sources = [
+    "video_frame_mapper_factory.cc",
+  ]
+  public_deps = [
+    ":video_frame_mapper_common",
+  ]
+  deps = []
+
+  # generic_dmabuf_video_frame_mapper
+  if (is_linux) {
+    deps += [ "//media/gpu/linux:video_frame_mapper" ]
+  }
+
+  # vaapi_dmabuf_video_frame_mapper
+  if (use_vaapi) {
+    deps += [ "//media/gpu/vaapi" ]
+  }
+}
+
+source_set("video_frame_mapper_common") {
+  defines = [ "MEDIA_GPU_IMPLEMENTATION" ]
+  sources = [
+    "video_frame_mapper.h",
+    "video_frame_mapper_factory.h",
+  ]
+
+  public_deps = [
+    ":buildflags",
+    "//base",
+    "//media",
+  ]
+}
+
 # TODO(watk): Run this on bots. http://crbug.com/461437
 if (is_win || is_android || use_v4l2_codec || use_vaapi) {
   test("video_decode_accelerator_unittest") {
@@ -640,6 +678,7 @@
     ":buildflags",
     ":gpu",
     "test:frame_validator",
+    "test:helpers",
     "test:image_processor",
     "test:render_helpers",
     "//base/test:test_support",
diff --git a/media/gpu/image_processor_test.cc b/media/gpu/image_processor_test.cc
index b6829117..683506cb 100644
--- a/media/gpu/image_processor_test.cc
+++ b/media/gpu/image_processor_test.cc
@@ -47,10 +47,13 @@
   std::unique_ptr<test::ImageProcessorClient> CreateImageProcessorClient(
       const test::Image& input_image,
       const test::Image& output_image) {
+    const VideoPixelFormat input_format = input_image.PixelFormat();
+    const VideoPixelFormat output_format = output_image.PixelFormat();
     auto input_config_layout = test::CreateVideoFrameLayout(
-        input_image.PixelFormat(), input_image.Size());
-    auto output_config_layout = test::CreateVideoFrameLayout(
-        output_image.PixelFormat(), output_image.Size());
+        input_format, input_image.Size(), VideoFrame::NumPlanes(input_format));
+    auto output_config_layout =
+        test::CreateVideoFrameLayout(output_format, output_image.Size(),
+                                     VideoFrame::NumPlanes(output_format));
     LOG_ASSERT(input_config_layout);
     LOG_ASSERT(output_config_layout);
     ImageProcessor::PortConfig input_config(*input_config_layout,
@@ -64,8 +67,7 @@
     LOG_ASSERT(output_image.IsMetadataLoaded());
     std::vector<std::unique_ptr<test::VideoFrameProcessor>> frame_processors;
     // TODO(crbug.com/944823): Use VideoFrameValidator for RGB formats.
-    if (IsYuvPlanar(input_image.PixelFormat()) &&
-        IsYuvPlanar(output_image.PixelFormat())) {
+    if (IsYuvPlanar(input_format) && IsYuvPlanar(output_format)) {
       auto vf_validator = test::VideoFrameValidator::Create(
           {output_image.Checksum()}, output_image.PixelFormat());
       frame_processors.push_back(std::move(vf_validator));
diff --git a/media/gpu/linux/BUILD.gn b/media/gpu/linux/BUILD.gn
index b970a7f..034c9a2 100644
--- a/media/gpu/linux/BUILD.gn
+++ b/media/gpu/linux/BUILD.gn
@@ -5,6 +5,8 @@
 import("//build/buildflag_header.gni")
 import("//build/config/ui.gni")
 
+assert(is_linux)
+
 source_set("linux") {
   defines = [ "MEDIA_GPU_IMPLEMENTATION" ]
   sources = [
@@ -24,3 +26,18 @@
     deps += [ "//ui/ozone" ]
   }
 }
+
+source_set("video_frame_mapper") {
+  defines = [ "MEDIA_GPU_IMPLEMENTATION" ]
+  sources = [
+    "generic_dmabuf_video_frame_mapper.cc",
+    "generic_dmabuf_video_frame_mapper.h",
+  ]
+
+  deps = [
+    "//base",
+    "//media",
+    "//media/gpu:common",
+    "//media/gpu:video_frame_mapper_common",
+  ]
+}
diff --git a/media/gpu/test/generic_dmabuf_video_frame_mapper.cc b/media/gpu/linux/generic_dmabuf_video_frame_mapper.cc
similarity index 97%
rename from media/gpu/test/generic_dmabuf_video_frame_mapper.cc
rename to media/gpu/linux/generic_dmabuf_video_frame_mapper.cc
index b7db0ed..6b8e68b 100644
--- a/media/gpu/test/generic_dmabuf_video_frame_mapper.cc
+++ b/media/gpu/linux/generic_dmabuf_video_frame_mapper.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 "media/gpu/test/generic_dmabuf_video_frame_mapper.h"
+#include "media/gpu/linux/generic_dmabuf_video_frame_mapper.h"
 
 #include <sys/mman.h>
 
@@ -14,7 +14,6 @@
 #include "media/gpu/macros.h"
 
 namespace media {
-namespace test {
 
 namespace {
 
@@ -120,5 +119,4 @@
                                 plane_addrs, chunks, std::move(video_frame));
 }
 
-}  // namespace test
 }  // namespace media
diff --git a/media/gpu/linux/generic_dmabuf_video_frame_mapper.h b/media/gpu/linux/generic_dmabuf_video_frame_mapper.h
new file mode 100644
index 0000000..dddd56cb
--- /dev/null
+++ b/media/gpu/linux/generic_dmabuf_video_frame_mapper.h
@@ -0,0 +1,27 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_GPU_LINUX_GENERIC_DMABUF_VIDEO_FRAME_MAPPER_H_
+#define MEDIA_GPU_LINUX_GENERIC_DMABUF_VIDEO_FRAME_MAPPER_H_
+
+#include "media/gpu/media_gpu_export.h"
+#include "media/gpu/video_frame_mapper.h"
+
+namespace media {
+
+// The GenericDmaBufVideoFrameMapper implements functionality to map DMABUF-
+// backed video frames into memory.
+class MEDIA_GPU_EXPORT GenericDmaBufVideoFrameMapper : public VideoFrameMapper {
+ public:
+  GenericDmaBufVideoFrameMapper() = default;
+  ~GenericDmaBufVideoFrameMapper() override = default;
+
+  // VideoFrameMapper implementation.
+  scoped_refptr<VideoFrame> Map(
+      scoped_refptr<const VideoFrame> video_frame) const override;
+  DISALLOW_COPY_AND_ASSIGN(GenericDmaBufVideoFrameMapper);
+};
+
+}  // namespace media
+#endif  // MEDIA_GPU_LINUX_GENERIC_DMABUF_VIDEO_FRAME_MAPPER_H_
diff --git a/media/gpu/test/BUILD.gn b/media/gpu/test/BUILD.gn
index 4a7a173..60eb18f 100644
--- a/media/gpu/test/BUILD.gn
+++ b/media/gpu/test/BUILD.gn
@@ -8,10 +8,17 @@
 source_set("helpers") {
   testonly = true
   sources = [
+    "image.cc",
+    "image.h",
     "video_accelerator_unittest_helpers.h",
+    "video_frame_helpers.cc",
+    "video_frame_helpers.h",
   ]
   deps = [
     "//base:base",
+    "//media:test_support",
+    "//media/gpu",
+    "//third_party/libyuv",
   ]
 }
 
@@ -22,12 +29,10 @@
     "rendering_helper.h",
     "texture_ref.cc",
     "texture_ref.h",
-    "video_frame_helpers.cc",
-    "video_frame_helpers.h",
   ]
   deps = [
+    ":helpers",
     "//media/gpu",
-    "//third_party/libyuv",
     "//ui/gl/init:init",
   ]
   if (use_ozone) {
@@ -35,44 +40,17 @@
   }
 }
 
-source_set("frame_mapper") {
-  testonly = true
-  sources = [
-    "video_frame_mapper.h",
-    "video_frame_mapper_factory.cc",
-    "video_frame_mapper_factory.h",
-  ]
-  if (is_chromeos) {
-    sources += [
-      "generic_dmabuf_video_frame_mapper.cc",
-      "generic_dmabuf_video_frame_mapper.h",
-    ]
-    if (use_vaapi) {
-      sources += [
-        "vaapi_dmabuf_video_frame_mapper.cc",
-        "vaapi_dmabuf_video_frame_mapper.h",
-      ]
-    }
-  }
-
-  deps = [
-    "//media/gpu",
-  ]
-}
-
 source_set("frame_validator") {
   testonly = true
   sources = [
     "video_frame_validator.cc",
     "video_frame_validator.h",
   ]
-  public_deps = [
-    ":frame_mapper",
-  ]
   deps = [
     ":decode_helpers",
     ":helpers",
     "//media/gpu",
+    "//media/gpu:video_frame_mapper",
   ]
 }
 
@@ -83,9 +61,10 @@
     "video_frame_file_writer.h",
   ]
   deps = [
-    ":frame_mapper",
+    ":helpers",
     ":render_helpers",
     "//media/gpu",
+    "//media/gpu:video_frame_mapper",
     "//ui/gfx/codec:codec",
   ]
 }
@@ -120,8 +99,8 @@
     ":render_helpers",
   ]
   deps = [
-    ":frame_mapper",
     "//media/gpu",
+    "//media/gpu:video_frame_mapper",
   ]
 }
 
@@ -192,12 +171,11 @@
 static_library("image_processor") {
   testonly = true
   sources = [
-    "image.cc",
-    "image.h",
     "image_processor/image_processor_client.cc",
     "image_processor/image_processor_client.h",
   ]
   deps = [
+    ":helpers",
     ":render_helpers",
     "//media:test_support",
     "//media/gpu",
diff --git a/media/gpu/test/generic_dmabuf_video_frame_mapper.h b/media/gpu/test/generic_dmabuf_video_frame_mapper.h
deleted file mode 100644
index 61755636..0000000
--- a/media/gpu/test/generic_dmabuf_video_frame_mapper.h
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef MEDIA_GPU_TEST_GENERIC_DMABUF_VIDEO_FRAME_MAPPER_H_
-#define MEDIA_GPU_TEST_GENERIC_DMABUF_VIDEO_FRAME_MAPPER_H_
-
-#include "media/gpu/test/video_frame_mapper.h"
-
-namespace media {
-namespace test {
-
-// VideoFrameMapper for DMAbuf-backed VideoFrame.
-class GenericDmaBufVideoFrameMapper : public VideoFrameMapper {
- public:
-  GenericDmaBufVideoFrameMapper() = default;
-  ~GenericDmaBufVideoFrameMapper() override = default;
-
-  // VideoFrameMapper implementation.
-  scoped_refptr<VideoFrame> Map(
-      scoped_refptr<const VideoFrame> video_frame) const override;
-  DISALLOW_COPY_AND_ASSIGN(GenericDmaBufVideoFrameMapper);
-};
-
-}  // namespace test
-}  // namespace media
-#endif  // MEDIA_GPU_TEST_GENERIC_DMABUF_VIDEO_FRAME_MAPPER_H_
diff --git a/media/gpu/test/image_processor/image_processor_client.cc b/media/gpu/test/image_processor/image_processor_client.cc
index c27e712..4871aaac 100644
--- a/media/gpu/test/image_processor/image_processor_client.cc
+++ b/media/gpu/test/image_processor/image_processor_client.cc
@@ -20,6 +20,7 @@
 #include "media/base/video_frame_layout.h"
 #include "media/gpu/image_processor_factory.h"
 #include "media/gpu/test/image.h"
+#include "media/gpu/test/video_frame_helpers.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/gfx/geometry/rect.h"
 
@@ -34,6 +35,9 @@
 scoped_refptr<VideoFrame> CloneVideoFrameWithLayout(
     const VideoFrame* const src_frame,
     const VideoFrameLayout& dst_layout) {
+  if (!src_frame)
+    return nullptr;
+
   LOG_ASSERT(src_frame->IsMappable());
   LOG_ASSERT(src_frame->format() == dst_layout.format());
   // Create VideoFrame, which allocates and owns data.
@@ -143,44 +147,12 @@
   DCHECK_CALLED_ON_VALID_THREAD(test_main_thread_checker_);
   LOG_ASSERT(image_processor_);
   LOG_ASSERT(input_image.IsLoaded());
-  LOG_ASSERT(input_image.DataSize() ==
-             VideoFrame::AllocationSize(input_image.PixelFormat(),
-                                        input_image.Size()));
-
-  const auto format = input_image.PixelFormat();
-  const auto visible_size = input_image.Size();
-
-  // Create planes for layout. We cannot use WrapExternalData() because it
-  // calls GetDefaultLayout() and it supports only a few pixel formats.
-  const size_t num_planes = VideoFrame::NumPlanes(format);
-  std::vector<VideoFrameLayout::Plane> planes(num_planes);
-  const auto strides = VideoFrame::ComputeStrides(format, visible_size);
-  size_t offset = 0;
-  for (size_t i = 0; i < num_planes; ++i) {
-    planes[i].stride = strides[i];
-    planes[i].offset = offset;
-    offset += VideoFrame::PlaneSize(format, i, visible_size).GetArea();
-  }
-
-  auto layout = VideoFrameLayout::CreateWithPlanes(
-      format, visible_size, std::move(planes), {input_image.DataSize()});
-  if (!layout) {
-    LOG(ERROR) << "Failed to create VideoFrameLayout";
-    return nullptr;
-  }
-
-  auto frame = VideoFrame::WrapExternalDataWithLayout(
-      *layout, gfx::Rect(visible_size), visible_size, input_image.Data(),
-      input_image.DataSize(), base::TimeDelta());
-  if (!frame) {
-    LOG(ERROR) << "Failed to create VideoFrame";
-    return nullptr;
-  }
 
   const auto& input_layout = image_processor_->input_layout();
   if (VideoFrame::IsStorageTypeMappable(
           image_processor_->input_storage_type())) {
-    return CloneVideoFrameWithLayout(frame.get(), input_layout);
+    return CloneVideoFrameWithLayout(
+        CreateVideoFrameFromImage(input_image).get(), input_layout);
   } else {
 #if defined(OS_CHROMEOS)
     LOG_ASSERT(image_processor_->input_storage_type() ==
diff --git a/media/gpu/test/video_encode_accelerator_unittest_helpers.cc b/media/gpu/test/video_encode_accelerator_unittest_helpers.cc
index 56ed42a..16d11ab 100644
--- a/media/gpu/test/video_encode_accelerator_unittest_helpers.cc
+++ b/media/gpu/test/video_encode_accelerator_unittest_helpers.cc
@@ -7,13 +7,14 @@
 #include "base/bind_helpers.h"
 #include "build/build_config.h"
 #include "media/gpu/test/texture_ref.h"
-#include "media/gpu/test/video_frame_mapper_factory.h"
+#include "media/gpu/video_frame_mapper.h"
+#include "media/gpu/video_frame_mapper_factory.h"
 #include "ui/gfx/buffer_format_util.h"
 #include "ui/gfx/native_pixmap.h"
 
 #if defined(OS_POSIX)
 #include <sys/mman.h>
-#endif
+#endif  // defined(OS_POSIX)
 
 namespace media {
 namespace test {
@@ -28,7 +29,7 @@
 
   scoped_refptr<VideoFrame> mapped_dst_frame = dst_frame;
   if (dst_frame->storage_type() == VideoFrame::STORAGE_DMABUFS) {
-    auto video_frame_mapper = test::VideoFrameMapperFactory::CreateMapper(true);
+    auto video_frame_mapper = VideoFrameMapperFactory::CreateMapper(true);
     LOG_ASSERT(video_frame_mapper);
     mapped_dst_frame = video_frame_mapper->Map(dst_frame);
     if (!mapped_dst_frame) {
@@ -47,7 +48,7 @@
   }
   return true;
 }
-#endif
+#endif  // defined(OS_CHROMEOS)
 
 }  // namespace
 
@@ -71,7 +72,7 @@
     LOG(ERROR) << "Failed to copy mapped buffer to dmabuf fds";
     return nullptr;
   }
-#endif
+#endif  // defined(OS_CHROMEOS)
   return dmabuf_frame;
 }
 
diff --git a/media/gpu/test/video_frame_file_writer.cc b/media/gpu/test/video_frame_file_writer.cc
index 62ff599..a739eeb 100644
--- a/media/gpu/test/video_frame_file_writer.cc
+++ b/media/gpu/test/video_frame_file_writer.cc
@@ -11,8 +11,8 @@
 #include "base/files/file_util.h"
 #include "base/memory/ptr_util.h"
 #include "base/strings/stringprintf.h"
-#include "media/gpu/test/video_frame_mapper.h"
-#include "media/gpu/test/video_frame_mapper_factory.h"
+#include "media/gpu/video_frame_mapper.h"
+#include "media/gpu/video_frame_mapper_factory.h"
 #include "ui/gfx/codec/png_codec.h"
 
 namespace media {
diff --git a/media/gpu/test/video_frame_file_writer.h b/media/gpu/test/video_frame_file_writer.h
index 14c07490..eda9f24 100644
--- a/media/gpu/test/video_frame_file_writer.h
+++ b/media/gpu/test/video_frame_file_writer.h
@@ -16,14 +16,15 @@
 #include "media/gpu/test/video_frame_helpers.h"
 
 namespace media {
+
+class VideoFrameMapper;
+
 namespace test {
 
 // Default output folder used to store frames.
 constexpr const base::FilePath::CharType* kDefaultOutputFolder =
     FILE_PATH_LITERAL("video_frames");
 
-class VideoFrameMapper;
-
 // The video frame file writer class implements functionality to write video
 // frames to file. The supported output formats are PNG and raw I420 YUV.
 class VideoFrameFileWriter : public VideoFrameProcessor {
diff --git a/media/gpu/test/video_frame_helpers.cc b/media/gpu/test/video_frame_helpers.cc
index 490a5c3..bf831e15 100644
--- a/media/gpu/test/video_frame_helpers.cc
+++ b/media/gpu/test/video_frame_helpers.cc
@@ -9,6 +9,7 @@
 
 #include "base/memory/scoped_refptr.h"
 #include "media/base/video_frame.h"
+#include "media/gpu/test/image.h"
 #include "third_party/libyuv/include/libyuv.h"
 #include "ui/gfx/gpu_memory_buffer.h"
 
@@ -169,13 +170,62 @@
   return video_frame;
 }
 
-base::Optional<VideoFrameLayout> CreateVideoFrameLayout(
-    VideoPixelFormat pixel_format,
-    const gfx::Size& size) {
-  return VideoFrameLayout::CreateWithStrides(
-      pixel_format, size, VideoFrame::ComputeStrides(pixel_format, size),
-      std::vector<size_t>(VideoFrame::NumPlanes(pixel_format),
-                          0) /* buffer_sizes */);
+base::Optional<VideoFrameLayout> CreateVideoFrameLayout(VideoPixelFormat format,
+                                                        const gfx::Size& size,
+                                                        bool single_buffer) {
+  const size_t num_planes = VideoFrame::NumPlanes(format);
+  const size_t num_buffers = single_buffer ? 1u : num_planes;
+  // If num_buffers = 1, all the planes are stored in the same buffer.
+  // If num_buffers = num_planes, each of plane is stored in a separate
+  // buffer and located in the beginning of the buffer.
+  std::vector<size_t> buffer_sizes(num_buffers);
+  std::vector<VideoFrameLayout::Plane> planes(num_planes);
+  const auto strides = VideoFrame::ComputeStrides(format, size);
+  size_t offset = 0;
+  for (size_t i = 0; i < num_planes; ++i) {
+    planes[i].stride = strides[i];
+    if (num_buffers == 1) {
+      planes[i].offset = offset;
+      offset += VideoFrame::PlaneSize(format, i, size).GetArea();
+    } else {
+      planes[i].offset = 0;
+      buffer_sizes[i] = VideoFrame::PlaneSize(format, i, size).GetArea();
+    }
+  }
+
+  if (num_buffers == 1)
+    buffer_sizes[0] = VideoFrame::AllocationSize(format, size);
+
+  return VideoFrameLayout::CreateWithPlanes(format, size, std::move(planes),
+                                            std::move(buffer_sizes));
+}
+
+scoped_refptr<const VideoFrame> CreateVideoFrameFromImage(const Image& image) {
+  DCHECK(image.IsLoaded());
+  const auto format = image.PixelFormat();
+  const auto& visible_size = image.Size();
+  // Loaded image data must be tight.
+  DCHECK_EQ(image.DataSize(), VideoFrame::AllocationSize(format, visible_size));
+
+  // Create planes for layout. We cannot use WrapExternalData() because it
+  // calls GetDefaultLayout() and it supports only a few pixel formats.
+  base::Optional<VideoFrameLayout> layout =
+      CreateVideoFrameLayout(format, visible_size, 1u /* num_buffers */);
+  if (!layout) {
+    LOG(ERROR) << "Failed to create VideoFrameLayout";
+    return nullptr;
+  }
+
+  scoped_refptr<const VideoFrame> video_frame =
+      VideoFrame::WrapExternalDataWithLayout(
+          *layout, gfx::Rect(visible_size), visible_size, image.Data(),
+          image.DataSize(), base::TimeDelta());
+  if (!video_frame) {
+    LOG(ERROR) << "Failed to create VideoFrame";
+    return nullptr;
+  }
+
+  return video_frame;
 }
 
 }  // namespace test
diff --git a/media/gpu/test/video_frame_helpers.h b/media/gpu/test/video_frame_helpers.h
index 0bbc9fe..cf11a65d 100644
--- a/media/gpu/test/video_frame_helpers.h
+++ b/media/gpu/test/video_frame_helpers.h
@@ -17,6 +17,8 @@
 
 namespace test {
 
+class Image;
+
 // The video frame processor defines an abstract interface for classes that are
 // interested in processing video frames (e.g. FrameValidator,...).
 class VideoFrameProcessor {
@@ -55,11 +57,18 @@
     const gfx::Size& size,
     gfx::BufferUsage buffer_usage = gfx::BufferUsage::SCANOUT_VDA_WRITE);
 
-// Create a video frame layout for the specified |pixel_format| and |size|. The
-// created layout will have a separate buffer for each plane in the format.
+// Create a video frame layout for the specified |pixel_format| and
+// |coded_size|. If |single_buffer| is true, the created VideoFrameLayout
+// represents all the planes are stored in the same buffer. Otherwise, it
+// represents each plane is stored in separated planes.
 base::Optional<VideoFrameLayout> CreateVideoFrameLayout(
     VideoPixelFormat pixel_format,
-    const gfx::Size& size);
+    const gfx::Size& size,
+    bool single_buffer);
+
+// Get VideoFrame that contains Load()ed data. The returned VideoFrame doesn't
+// own the data and thus must not be changed.
+scoped_refptr<const VideoFrame> CreateVideoFrameFromImage(const Image& image);
 
 }  // namespace test
 }  // namespace media
diff --git a/media/gpu/test/video_frame_mapper_factory.cc b/media/gpu/test/video_frame_mapper_factory.cc
deleted file mode 100644
index e2bf6542..0000000
--- a/media/gpu/test/video_frame_mapper_factory.cc
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "media/gpu/test/video_frame_mapper_factory.h"
-
-#include "media/gpu/buildflags.h"
-
-#if defined(OS_CHROMEOS)
-#include "media/gpu/test/generic_dmabuf_video_frame_mapper.h"
-
-#if BUILDFLAG(USE_VAAPI)
-#include "media/gpu/test/vaapi_dmabuf_video_frame_mapper.h"
-#endif  // BUILDFLAG(USE_VAAPI)
-
-#endif  // defined(OS_CHROMEOS)
-
-namespace media {
-namespace test {
-
-// static
-std::unique_ptr<VideoFrameMapper> VideoFrameMapperFactory::CreateMapper() {
-#if BUILDFLAG(USE_VAAPI)
-  return CreateMapper(false);
-#else
-  return CreateMapper(true);
-#endif
-}
-
-// static
-std::unique_ptr<VideoFrameMapper> VideoFrameMapperFactory::CreateMapper(
-    bool linear_buffer_mapper) {
-#if defined(OS_CHROMEOS)
-  if (linear_buffer_mapper) {
-    return std::make_unique<GenericDmaBufVideoFrameMapper>();
-  }
-
-#if BUILDFLAG(USE_VAAPI)
-  return VaapiDmaBufVideoFrameMapper::Create();
-#endif  // BUILDFLAG(USE_VAAPI)
-#endif  // defined(OS_CHROMEOS)
-
-  NOTREACHED();
-  return nullptr;
-}
-
-}  // namespace test
-}  // namespace media
diff --git a/media/gpu/test/video_frame_validator.cc b/media/gpu/test/video_frame_validator.cc
index efc731c..5a0f096 100644
--- a/media/gpu/test/video_frame_validator.cc
+++ b/media/gpu/test/video_frame_validator.cc
@@ -13,8 +13,8 @@
 #include "build/build_config.h"
 #include "media/base/video_frame.h"
 #include "media/gpu/test/video_decode_accelerator_unittest_helpers.h"
-#include "media/gpu/test/video_frame_mapper.h"
-#include "media/gpu/test/video_frame_mapper_factory.h"
+#include "media/gpu/video_frame_mapper.h"
+#include "media/gpu/video_frame_mapper_factory.h"
 
 namespace media {
 namespace test {
@@ -30,7 +30,7 @@
     LOG(ERROR) << "Failed to create VideoFrameMapper.";
     return nullptr;
   }
-#endif
+#endif  // defined(OS_CHROMEOS)
 
   auto video_frame_validator = base::WrapUnique(new VideoFrameValidator(
       expected_frame_checksums, std::move(video_frame_mapper),
@@ -125,7 +125,7 @@
 #if defined(OS_LINUX)
   if (validated_frame->storage_type() == VideoFrame::STORAGE_DMABUFS)
     validated_frame = video_frame_mapper_->Map(std::move(validated_frame));
-#endif
+#endif  // defined(OS_LINUX)
 
   if (validated_frame->format() != validation_format_) {
     validated_frame =
diff --git a/media/gpu/test/video_frame_validator.h b/media/gpu/test/video_frame_validator.h
index e51df3c..39101c1 100644
--- a/media/gpu/test/video_frame_validator.h
+++ b/media/gpu/test/video_frame_validator.h
@@ -24,11 +24,10 @@
 namespace media {
 
 class VideoFrame;
+class VideoFrameMapper;
 
 namespace test {
 
-class VideoFrameMapper;
-
 // VideoFrameValidator validates the pixel content of each video frame.
 // It maps a video frame by using VideoFrameMapper, and converts the mapped
 // frame to |validation_format| to resolve pixel format differences on different
diff --git a/media/gpu/test/video_player/video.cc b/media/gpu/test/video_player/video.cc
index c5bd4f01..91be797 100644
--- a/media/gpu/test/video_player/video.cc
+++ b/media/gpu/test/video_player/video.cc
@@ -17,15 +17,18 @@
 namespace media {
 namespace test {
 
-// Suffix to append to the video file path to get the metadata file path.
+// Suffix appended to the video file path to get the metadata file path, if no
+// explicit metadata file path was specified.
 constexpr const base::FilePath::CharType* kMetadataSuffix =
     FILE_PATH_LITERAL(".json");
 
 base::FilePath Video::test_data_path_ = base::FilePath();
 
-Video::Video(const base::FilePath& file_path) : file_path_(file_path) {}
+Video::Video(const base::FilePath& file_path,
+             const base::FilePath& metadata_file_path)
+    : file_path_(file_path), metadata_file_path_(metadata_file_path) {}
 
-Video::~Video() {}
+Video::~Video() = default;
 
 bool Video::Load() {
   // TODO(dstaessens@) Investigate reusing existing infrastructure such as
@@ -33,13 +36,12 @@
   DCHECK(!file_path_.empty());
   DCHECK(data_.empty());
 
-  // The specified path can be either an absolute path, a path relative to the
-  // current directory, or relative to the test data path.
-  if (!file_path_.IsAbsolute()) {
-    if (!PathExists(file_path_))
-      file_path_ = test_data_path_.Append(file_path_);
-    file_path_ = base::MakeAbsoluteFilePath(file_path_);
+  base::Optional<base::FilePath> resolved_path = ResolveFilePath(file_path_);
+  if (!resolved_path) {
+    LOG(ERROR) << "Video file not found: " << file_path_;
+    return false;
   }
+  file_path_ = resolved_path.value();
   VLOGF(2) << "File path: " << file_path_;
 
   int64_t file_size;
@@ -116,17 +118,21 @@
     return false;
   }
 
-  const base::FilePath json_path = file_path_.AddExtension(kMetadataSuffix);
-  VLOGF(2) << "File path: " << json_path;
+  // If no custom metadata file path was specified, use <video_path>.json.
+  if (metadata_file_path_.empty())
+    metadata_file_path_ = file_path_.AddExtension(kMetadataSuffix);
 
-  if (!base::PathExists(json_path)) {
-    VLOGF(1) << "Video metadata file not found: " << json_path;
+  base::Optional<base::FilePath> resolved_path =
+      ResolveFilePath(metadata_file_path_);
+  if (!resolved_path) {
+    VLOGF(1) << "Video metadata file not found: " << metadata_file_path_;
     return false;
   }
+  metadata_file_path_ = resolved_path.value();
 
   std::string json_data;
-  if (!base::ReadFileToString(json_path, &json_data)) {
-    VLOGF(1) << "Failed to read video metadata file: " << json_path;
+  if (!base::ReadFileToString(metadata_file_path_, &json_data)) {
+    VLOGF(1) << "Failed to read video metadata file: " << metadata_file_path_;
     return false;
   }
 
@@ -134,15 +140,15 @@
   std::unique_ptr<base::Value> metadata(
       reader.ReadToValueDeprecated(json_data));
   if (!metadata) {
-    VLOGF(1) << "Failed to parse video metadata: " << json_path << ": "
-             << reader.GetErrorMessage();
+    VLOGF(1) << "Failed to parse video metadata: " << metadata_file_path_
+             << ": " << reader.GetErrorMessage();
     return false;
   }
 
   const base::Value* profile =
       metadata->FindKeyOfType("profile", base::Value::Type::STRING);
   if (!profile) {
-    VLOGF(1) << "Key \"profile\" is not found in " << json_path;
+    VLOGF(1) << "Key \"profile\" is not found in " << metadata_file_path_;
     return false;
   }
   profile_ = ConvertStringtoProfile(profile->GetString());
@@ -155,7 +161,7 @@
   const base::Value* num_frames =
       metadata->FindKeyOfType("num_frames", base::Value::Type::INTEGER);
   if (!num_frames) {
-    VLOGF(1) << "Key \"num_frames\" is not found in " << json_path;
+    VLOGF(1) << "Key \"num_frames\" is not found in " << metadata_file_path_;
     return false;
   }
   num_frames_ = static_cast<uint32_t>(num_frames->GetInt());
@@ -163,7 +169,7 @@
   const base::Value* num_fragments =
       metadata->FindKeyOfType("num_fragments", base::Value::Type::INTEGER);
   if (!num_fragments) {
-    VLOGF(1) << "Key \"num_fragments\" is not found in " << json_path;
+    VLOGF(1) << "Key \"num_fragments\" is not found in " << metadata_file_path_;
     return false;
   }
   num_fragments_ = static_cast<uint32_t>(num_fragments->GetInt());
@@ -171,13 +177,13 @@
   const base::Value* width =
       metadata->FindKeyOfType("width", base::Value::Type::INTEGER);
   if (!width) {
-    VLOGF(1) << "Key \"width\" is not found in " << json_path;
+    VLOGF(1) << "Key \"width\" is not found in " << metadata_file_path_;
     return false;
   }
   const base::Value* height =
       metadata->FindKeyOfType("height", base::Value::Type::INTEGER);
   if (!height) {
-    VLOGF(1) << "Key \"height\" is not found in " << json_path;
+    VLOGF(1) << "Key \"height\" is not found in " << metadata_file_path_;
     return false;
   }
   resolution_ = gfx::Size(static_cast<uint32_t>(width->GetInt()),
@@ -186,7 +192,7 @@
   const base::Value* md5_checksums =
       metadata->FindKeyOfType("md5_checksums", base::Value::Type::LIST);
   if (!md5_checksums) {
-    VLOGF(1) << "Key \"md5_checksums\" is not found in " << json_path;
+    VLOGF(1) << "Key \"md5_checksums\" is not found in " << metadata_file_path_;
     return false;
   }
   for (const base::Value& checksum : md5_checksums->GetList()) {
@@ -196,7 +202,8 @@
   const base::Value* thumbnail_checksums =
       metadata->FindKeyOfType("thumbnail_checksums", base::Value::Type::LIST);
   if (!thumbnail_checksums) {
-    VLOGF(1) << "Key \"thumbnail_checksums\" is not found in " << json_path;
+    VLOGF(1) << "Key \"thumbnail_checksums\" is not found in "
+             << metadata_file_path_;
     return false;
   }
   for (const base::Value& checksum : thumbnail_checksums->GetList()) {
@@ -212,6 +219,21 @@
   return profile_ != VIDEO_CODEC_PROFILE_UNKNOWN || num_frames_ != 0;
 }
 
+base::Optional<base::FilePath> Video::ResolveFilePath(
+    const base::FilePath& file_path) {
+  // If the path exists it's either absolute or relative to our working dir.
+  if (PathExists(file_path)) {
+    return base::MakeAbsoluteFilePath(file_path);
+  }
+  // If the path doesn't exist, it might be relative to the test data dir.
+  base::FilePath resolved_path =
+      base::MakeAbsoluteFilePath(test_data_path_.Append(file_path));
+  if (PathExists(resolved_path)) {
+    return resolved_path;
+  }
+  return base::Optional<base::FilePath>();
+}
+
 // static
 VideoCodecProfile Video::ConvertStringtoProfile(const std::string& profile) {
   if (profile == "H264PROFILE_MAIN") {
diff --git a/media/gpu/test/video_player/video.h b/media/gpu/test/video_player/video.h
index efce18d8..a16f96f 100644
--- a/media/gpu/test/video_player/video.h
+++ b/media/gpu/test/video_player/video.h
@@ -22,7 +22,8 @@
 // * Use a file stream rather than loading potentially huge files into memory.
 class Video {
  public:
-  explicit Video(const base::FilePath& file_path);
+  Video(const base::FilePath& file_path,
+        const base::FilePath& metadata_file_path);
   ~Video();
 
   // Load the video file from disk.
@@ -67,11 +68,19 @@
   // Return true if video metadata is already loaded.
   bool IsMetadataLoaded() const;
 
+  // Resolve the specified |file_path|. The path can be absolute, relative to
+  // the current directory, or relative to the test data path. Returns the
+  // resolved path if resolving to an existing file was successful.
+  base::Optional<base::FilePath> ResolveFilePath(
+      const base::FilePath& file_path);
+
   // The path where all test video files are stored.
   // TODO(dstaessens@) Avoid using a static data path here.
   static base::FilePath test_data_path_;
-  // The video file's path, can be absolute or relative to the above path.
+  // The video file path, can be relative to the test data path.
   base::FilePath file_path_;
+  // Video metadata file path, can be relative to the test data path.
+  base::FilePath metadata_file_path_;
 
   // The video's data stream.
   std::vector<uint8_t> data_;
diff --git a/media/gpu/test/video_player/video_player_test_environment.cc b/media/gpu/test/video_player/video_player_test_environment.cc
index af67b57b..4dc9586a 100644
--- a/media/gpu/test/video_player/video_player_test_environment.cc
+++ b/media/gpu/test/video_player/video_player_test_environment.cc
@@ -33,10 +33,12 @@
 // static
 VideoPlayerTestEnvironment* VideoPlayerTestEnvironment::Create(
     const base::FilePath& video_path,
+    const base::FilePath& video_metadata_path,
     bool enable_validator,
     bool output_frames) {
   auto video = std::make_unique<media::test::Video>(
-      video_path.empty() ? base::FilePath(kDefaultTestVideoPath) : video_path);
+      video_path.empty() ? base::FilePath(kDefaultTestVideoPath) : video_path,
+      video_metadata_path);
   if (!video->Load()) {
     LOG(ERROR) << "Failed to load " << video_path;
     return nullptr;
diff --git a/media/gpu/test/video_player/video_player_test_environment.h b/media/gpu/test/video_player/video_player_test_environment.h
index 663770e..6a5a4dd 100644
--- a/media/gpu/test/video_player/video_player_test_environment.h
+++ b/media/gpu/test/video_player/video_player_test_environment.h
@@ -26,9 +26,11 @@
 // the entire test run.
 class VideoPlayerTestEnvironment : public ::testing::Environment {
  public:
-  static VideoPlayerTestEnvironment* Create(const base::FilePath& video_path,
-                                            bool enable_validator,
-                                            bool output_frames);
+  static VideoPlayerTestEnvironment* Create(
+      const base::FilePath& video_path,
+      const base::FilePath& video_metadata_path,
+      bool enable_validator,
+      bool output_frames);
   ~VideoPlayerTestEnvironment() override;
 
   // Set up the video decode test environment, only called once.
diff --git a/media/gpu/vaapi/BUILD.gn b/media/gpu/vaapi/BUILD.gn
index 7f92ed8..36c50c8 100644
--- a/media/gpu/vaapi/BUILD.gn
+++ b/media/gpu/vaapi/BUILD.gn
@@ -37,6 +37,8 @@
     "va_surface.h",
     "vaapi_common.cc",
     "vaapi_common.h",
+    "vaapi_dmabuf_video_frame_mapper.cc",
+    "vaapi_dmabuf_video_frame_mapper.h",
     "vaapi_h264_accelerator.cc",
     "vaapi_h264_accelerator.h",
     "vaapi_jpeg_decoder.cc",
@@ -77,6 +79,7 @@
     "//gpu/ipc/service",
     "//media",
     "//media/gpu:common",
+    "//media/gpu:video_frame_mapper_common",
     "//third_party/libyuv",
     "//ui/gfx/geometry",
   ]
diff --git a/media/gpu/test/vaapi_dmabuf_video_frame_mapper.cc b/media/gpu/vaapi/vaapi_dmabuf_video_frame_mapper.cc
similarity index 95%
rename from media/gpu/test/vaapi_dmabuf_video_frame_mapper.cc
rename to media/gpu/vaapi/vaapi_dmabuf_video_frame_mapper.cc
index b0a322b..3e31e41c 100644
--- a/media/gpu/test/vaapi_dmabuf_video_frame_mapper.cc
+++ b/media/gpu/vaapi/vaapi_dmabuf_video_frame_mapper.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 "media/gpu/test/vaapi_dmabuf_video_frame_mapper.h"
+#include "media/gpu/vaapi/vaapi_dmabuf_video_frame_mapper.h"
 
 #include "base/bind.h"
 #include "base/bind_helpers.h"
@@ -15,12 +15,11 @@
 #include "media/gpu/vaapi/vaapi_wrapper.h"
 #include "media/video/picture.h"
 
-#if defined(OS_POSIX)
+#if defined(USE_OZONE) || defined(USE_EGL)
 #include "media/gpu/vaapi/vaapi_picture_native_pixmap.h"
-#endif
+#endif  // defined(USE_OZONE) || defined(USE_EGL)
 
 namespace media {
-namespace test {
 
 namespace {
 
@@ -116,11 +115,11 @@
   }
 
   gfx::GpuMemoryBufferHandle gmb_handle;
-#if defined(OS_POSIX)
+#if defined(USE_OZONE) || defined(USE_EGL)
   gmb_handle =
       VaapiPictureNativePixmap::CreateGpuMemoryBufferHandleFromVideoFrame(
           video_frame.get());
-#endif
+#endif  // defined(USE_OZONE) || defined(USE_EGL)
   if (gmb_handle.is_null()) {
     VLOGF(1) << "Failed to CreateGMBHandleFromVideoFrame.";
     return nullptr;
@@ -153,5 +152,4 @@
                                 std::move(va_image));
 }
 
-}  // namespace test
 }  // namespace media
diff --git a/media/gpu/test/vaapi_dmabuf_video_frame_mapper.h b/media/gpu/vaapi/vaapi_dmabuf_video_frame_mapper.h
similarity index 62%
rename from media/gpu/test/vaapi_dmabuf_video_frame_mapper.h
rename to media/gpu/vaapi/vaapi_dmabuf_video_frame_mapper.h
index 3b10c51..db3aff9 100644
--- a/media/gpu/test/vaapi_dmabuf_video_frame_mapper.h
+++ b/media/gpu/vaapi/vaapi_dmabuf_video_frame_mapper.h
@@ -2,25 +2,24 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef MEDIA_GPU_TEST_VAAPI_DMABUF_VIDEO_FRAME_MAPPER_H_
-#define MEDIA_GPU_TEST_VAAPI_DMABUF_VIDEO_FRAME_MAPPER_H_
+#ifndef MEDIA_GPU_VAAPI_VAAPI_DMABUF_VIDEO_FRAME_MAPPER_H_
+#define MEDIA_GPU_VAAPI_VAAPI_DMABUF_VIDEO_FRAME_MAPPER_H_
 
 #include <memory>
 
-#include "media/gpu/test/video_frame_mapper.h"
+#include "media/gpu/media_gpu_export.h"
+#include "media/gpu/video_frame_mapper.h"
 
 namespace media {
 
 class VaapiPictureFactory;
 class VaapiWrapper;
 
-namespace test {
-
-// VideoFrameMapper that gains access to the memory referred by DMABuf-backed
-// video frame using VA-API.
+// VideoFrameMapper that provides access to the memory referred by DMABuf-backed
+// video frames using VA-API.
 // VaapiDmaBufVideoFrameMapper creates a new VaapiPicture from the given
-// VideoFrame and use a VaapiWrapper to access the memory there.
-class VaapiDmaBufVideoFrameMapper : public VideoFrameMapper {
+// VideoFrame and use the VaapiWrapper to access the memory there.
+class MEDIA_GPU_EXPORT VaapiDmaBufVideoFrameMapper : public VideoFrameMapper {
  public:
   ~VaapiDmaBufVideoFrameMapper() override;
 
@@ -40,7 +39,6 @@
   DISALLOW_COPY_AND_ASSIGN(VaapiDmaBufVideoFrameMapper);
 };
 
-}  // namespace test
 }  // namespace media
 
-#endif  // MEDIA_GPU_TEST_VAAPI_DMABUF_VIDEO_FRAME_MAPPER_H_
+#endif  // MEDIA_GPU_VAAPI_VAAPI_DMABUF_VIDEO_FRAME_MAPPER_H_
diff --git a/media/gpu/video_decode_accelerator_perf_tests.cc b/media/gpu/video_decode_accelerator_perf_tests.cc
index 6b3397d..48245aa 100644
--- a/media/gpu/video_decode_accelerator_perf_tests.cc
+++ b/media/gpu/video_decode_accelerator_perf_tests.cc
@@ -185,13 +185,16 @@
   base::CommandLine::StringVector args = cmd_line->GetArgs();
   base::FilePath video_path =
       (args.size() >= 1) ? base::FilePath(args[0]) : base::FilePath();
+  base::FilePath video_metadata_path =
+      (args.size() >= 2) ? base::FilePath(args[1]) : base::FilePath();
 
   // Set the default test data path.
   media::test::Video::SetTestDataPath(media::GetTestDataPath());
 
   // Set up our test environment.
   media::test::VideoPlayerTestEnvironment* test_environment =
-      media::test::VideoPlayerTestEnvironment::Create(video_path, false, false);
+      media::test::VideoPlayerTestEnvironment::Create(
+          video_path, video_metadata_path, false, false);
   if (!test_environment)
     return 0;
 
diff --git a/media/gpu/video_decode_accelerator_tests.cc b/media/gpu/video_decode_accelerator_tests.cc
index 09bb6cb..0970a25c 100644
--- a/media/gpu/video_decode_accelerator_tests.cc
+++ b/media/gpu/video_decode_accelerator_tests.cc
@@ -21,12 +21,17 @@
 
 // Video decoder tests usage message.
 constexpr const char* usage_msg =
-    "usage: video_decode_accelerator_tests [--help] [--disable_validator]\n"
-    "                                      [--output_frames] [<video>]\n";
+    "usage: video_decode_accelerator_tests\n"
+    "           [--help] [--disable_validator] [--output_frames]\n"
+    "           [<video path>] [<video metadata path>]\n";
+
 // Video decoder tests help message.
 constexpr const char* help_msg =
     "Run the video decode accelerator tests on the specified video. If no\n"
     "video is specified the default \"test-25fps.h264\" video will be used.\n"
+    "\nThe video metadata path should specify the location of a json file\n"
+    "containing the video's metadata, such as frame checksums. By default\n"
+    "<video path>.json will be used.\n"
     "\nThe following arguments are supported:\n"
     "  --disable_validator  disable frame validation, useful on old\n"
     "                       platforms that don't support import mode.\n"
@@ -199,7 +204,7 @@
 // decoder, without waiting for the result of the previous decode requests.
 TEST_F(VideoDecoderTest, FlushAtEndOfStream_MultipleOutstandingDecodes) {
   VideoDecoderClientConfig config;
-  config.max_outstanding_decode_requests = 5;
+  config.max_outstanding_decode_requests = 4;
   auto tvp = CreateVideoPlayer(g_env->Video(), config);
 
   tvp->Play();
@@ -268,7 +273,7 @@
   // initializing gtest, to overwrite the default gtest help message.
   LOG_ASSERT(cmd_line);
   if (cmd_line->HasSwitch("help")) {
-    std::cout << media::test::usage_msg << media::test::help_msg;
+    std::cout << media::test::usage_msg << "\n" << media::test::help_msg;
     return 0;
   }
 
@@ -281,6 +286,8 @@
   base::CommandLine::StringVector args = cmd_line->GetArgs();
   base::FilePath video_path =
       (args.size() >= 1) ? base::FilePath(args[0]) : base::FilePath();
+  base::FilePath video_metadata_path =
+      (args.size() >= 2) ? base::FilePath(args[1]) : base::FilePath();
 
   // Parse command line arguments.
   bool enable_validator = true;
@@ -288,13 +295,15 @@
   base::CommandLine::SwitchMap switches = cmd_line->GetSwitches();
   for (base::CommandLine::SwitchMap::const_iterator it = switches.begin();
        it != switches.end(); ++it) {
+    if (it->first.find("gtest_") == 0 ||               // Handled by GoogleTest
+        it->first == "v" || it->first == "vmodule") {  // Handled by Chrome
+      continue;
+    }
+
     if (it->first == "disable_validator") {
       enable_validator = false;
     } else if (it->first == "output_frames") {
       output_frames = true;
-    } else if (it->first.find("gtest_") == 0 || it->first == "v" ||
-               it->first == "vmodule") {
-      // Ignore
     } else {
       std::cout << "unknown option: --" << it->first << "\n"
                 << media::test::usage_msg;
@@ -305,7 +314,7 @@
   // Set up our test environment.
   media::test::VideoPlayerTestEnvironment* test_environment =
       media::test::VideoPlayerTestEnvironment::Create(
-          video_path, enable_validator, output_frames);
+          video_path, video_metadata_path, enable_validator, output_frames);
   if (!test_environment)
     return 0;
 
diff --git a/media/gpu/test/video_frame_mapper.h b/media/gpu/video_frame_mapper.h
similarity index 69%
rename from media/gpu/test/video_frame_mapper.h
rename to media/gpu/video_frame_mapper.h
index 079759f..9ba8c051 100644
--- a/media/gpu/test/video_frame_mapper.h
+++ b/media/gpu/video_frame_mapper.h
@@ -2,19 +2,20 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef MEDIA_GPU_TEST_VIDEO_FRAME_MAPPER_H_
-#define MEDIA_GPU_TEST_VIDEO_FRAME_MAPPER_H_
+#ifndef MEDIA_GPU_VIDEO_FRAME_MAPPER_H_
+#define MEDIA_GPU_VIDEO_FRAME_MAPPER_H_
 
 #include "base/macros.h"
 #include "base/memory/scoped_refptr.h"
 #include "media/base/video_frame.h"
+#include "media/gpu/media_gpu_export.h"
 
 namespace media {
-namespace test {
 
-// VideoFrameMapper is a class for mapping a video frame referred by VideoFrame.
+// The VideoFrameMapper interface allows mapping video frames into memory so
+// that their contents can be accessed directly.
 // VideoFrameMapper should be created by using VideoFrameMapperFactory.
-class VideoFrameMapper {
+class MEDIA_GPU_EXPORT VideoFrameMapper {
  public:
   virtual ~VideoFrameMapper() = default;
 
@@ -28,7 +29,6 @@
   DISALLOW_COPY_AND_ASSIGN(VideoFrameMapper);
 };
 
-}  // namespace test
 }  // namespace media
 
-#endif  // MEDIA_GPU_TEST_VIDEO_FRAME_MAPPER_H_
+#endif  // MEDIA_GPU_VIDEO_FRAME_MAPPER_H_
diff --git a/media/gpu/video_frame_mapper_factory.cc b/media/gpu/video_frame_mapper_factory.cc
new file mode 100644
index 0000000..a3f3ad5
--- /dev/null
+++ b/media/gpu/video_frame_mapper_factory.cc
@@ -0,0 +1,44 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/gpu/video_frame_mapper_factory.h"
+
+#include "build/build_config.h"
+#include "media/gpu/buildflags.h"
+
+#if defined(OS_LINUX)
+#include "media/gpu/linux/generic_dmabuf_video_frame_mapper.h"
+#endif  // defined(OS_LINUX)
+
+#if BUILDFLAG(USE_VAAPI) && defined(OS_LINUX)
+#include "media/gpu/vaapi/vaapi_dmabuf_video_frame_mapper.h"
+#endif  // BUILDFLAG(USE_VAAPI) && defined(OS_LINUX)
+
+namespace media {
+
+// static
+std::unique_ptr<VideoFrameMapper> VideoFrameMapperFactory::CreateMapper() {
+#if BUILDFLAG(USE_VAAPI) && defined(OS_LINUX)
+  return CreateMapper(false);
+#else
+  return CreateMapper(true);
+#endif  // BUILDFLAG(USE_VAAPI) && defined(OS_LINUX)
+}
+
+// static
+std::unique_ptr<VideoFrameMapper> VideoFrameMapperFactory::CreateMapper(
+    bool linear_buffer_mapper) {
+#if defined(OS_LINUX)
+  if (linear_buffer_mapper)
+    return std::make_unique<GenericDmaBufVideoFrameMapper>();
+#endif  // defined(OS_LINUX)
+
+#if BUILDFLAG(USE_VAAPI) && defined(OS_LINUX)
+  return VaapiDmaBufVideoFrameMapper::Create();
+#endif  // BUILDFLAG(USE_VAAPI) && defined(OS_LINUX)
+
+  return nullptr;
+}
+
+}  // namespace media
diff --git a/media/gpu/test/video_frame_mapper_factory.h b/media/gpu/video_frame_mapper_factory.h
similarity index 71%
rename from media/gpu/test/video_frame_mapper_factory.h
rename to media/gpu/video_frame_mapper_factory.h
index 8dd0d4e..d237d03 100644
--- a/media/gpu/test/video_frame_mapper_factory.h
+++ b/media/gpu/video_frame_mapper_factory.h
@@ -2,19 +2,19 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef MEDIA_GPU_TEST_VIDEO_FRAME_MAPPER_FACTORY_H_
-#define MEDIA_GPU_TEST_VIDEO_FRAME_MAPPER_FACTORY_H_
+#ifndef MEDIA_GPU_VIDEO_FRAME_MAPPER_FACTORY_H_
+#define MEDIA_GPU_VIDEO_FRAME_MAPPER_FACTORY_H_
 
 #include <memory>
 
-#include "media/gpu/test/video_frame_mapper.h"
+#include "media/gpu/media_gpu_export.h"
+#include "media/gpu/video_frame_mapper.h"
 
 namespace media {
-namespace test {
 
 // A factory function for VideoFrameMapper.
 // The appropriate VideoFrameMapper is a platform-dependent.
-class VideoFrameMapperFactory {
+class MEDIA_GPU_EXPORT VideoFrameMapperFactory {
  public:
   // Create an instance of the frame mapper.
   static std::unique_ptr<VideoFrameMapper> CreateMapper();
@@ -25,7 +25,6 @@
       bool force_linear_buffer_mapper);
 };
 
-}  // namespace test
 }  // namespace media
 
-#endif  // MEDIA_GPU_TEST_VIDEO_FRAME_MAPPER_FACTORY_H_
+#endif  // MEDIA_GPU_VIDEO_FRAME_MAPPER_FACTORY_H_
diff --git a/media/renderers/paint_canvas_video_renderer.cc b/media/renderers/paint_canvas_video_renderer.cc
index 8f829f1..25c4c88b 100644
--- a/media/renderers/paint_canvas_video_renderer.cc
+++ b/media/renderers/paint_canvas_video_renderer.cc
@@ -223,6 +223,7 @@
   sk_sp<SkImage> img = YUVGrBackendTexturesToSkImage(
       context_3d.gr_context, video_frame->ColorSpace(), video_frame->format(),
       yuv_textures);
+  context_3d.gr_context->flush();
 
   DeleteYUVTextures(video_frame, context_3d, yuv_textures_info);
 
@@ -269,6 +270,7 @@
   sk_sp<SkImage> img = YUVGrBackendTexturesToSkImage(
       context_3d.gr_context, video_frame->ColorSpace(), video_frame->format(),
       yuv_textures, result_texture);
+  context_3d.gr_context->flush();
 
   DeleteYUVTextures(video_frame, context_3d, yuv_textures_info);
 
@@ -1299,7 +1301,7 @@
   }
 
   GrGLTextureInfo src_texture_info{};
-  yuv_image->getBackendTexture(false).getGLTextureInfo(&src_texture_info);
+  yuv_image->getBackendTexture(true).getGLTextureInfo(&src_texture_info);
 
   gpu::gles2::GLES2Interface* source_gl = context_3d.gl;
   gpu::MailboxHolder mailbox_holder;
diff --git a/net/http/http_basic_state.cc b/net/http/http_basic_state.cc
index 3c1eb58..a9efbe1 100644
--- a/net/http/http_basic_state.cc
+++ b/net/http/http_basic_state.cc
@@ -24,7 +24,6 @@
     : read_buf_(base::MakeRefCounted<GrowableIOBuffer>()),
       connection_(std::move(connection)),
       using_proxy_(using_proxy),
-      can_send_early_(false),
       http_09_on_non_default_ports_enabled_(
           http_09_on_non_default_ports_enabled) {
   CHECK(connection_) << "ClientSocketHandle passed to HttpBasicState must "
@@ -34,7 +33,6 @@
 HttpBasicState::~HttpBasicState() = default;
 
 void HttpBasicState::Initialize(const HttpRequestInfo* request_info,
-                                bool can_send_early,
                                 RequestPriority priority,
                                 const NetLogWithSource& net_log) {
   DCHECK(!parser_.get());
@@ -46,7 +44,6 @@
       read_buf_.get(), net_log);
   parser_->set_http_09_on_non_default_ports_enabled(
       http_09_on_non_default_ports_enabled_);
-  can_send_early_ = can_send_early;
 }
 
 std::unique_ptr<ClientSocketHandle> HttpBasicState::ReleaseConnection() {
diff --git a/net/http/http_basic_state.h b/net/http/http_basic_state.h
index 58c122ca..d811f9f 100644
--- a/net/http/http_basic_state.h
+++ b/net/http/http_basic_state.h
@@ -35,7 +35,6 @@
 
   // Initialize() must be called before using any of the other methods.
   void Initialize(const HttpRequestInfo* request_info,
-                  bool can_send_early,
                   RequestPriority priority,
                   const NetLogWithSource& net_log);
 
@@ -43,7 +42,6 @@
 
   bool using_proxy() const { return using_proxy_; }
 
-  bool can_send_early() const { return can_send_early_; }
   bool http_09_on_non_default_ports_enabled() const {
     return http_09_on_non_default_ports_enabled_;
   }
@@ -82,8 +80,6 @@
 
   const bool using_proxy_;
 
-  bool can_send_early_;
-
   const bool http_09_on_non_default_ports_enabled_;
 
   GURL url_;
diff --git a/net/http/http_basic_state_unittest.cc b/net/http/http_basic_state_unittest.cc
index dd24b55..a811646 100644
--- a/net/http/http_basic_state_unittest.cc
+++ b/net/http/http_basic_state_unittest.cc
@@ -46,7 +46,7 @@
 TEST(HttpBasicStateTest, InitializeWorks) {
   HttpBasicState state(std::make_unique<ClientSocketHandle>(), false, false);
   const HttpRequestInfo request_info;
-  state.Initialize(&request_info, false, LOW, NetLogWithSource());
+  state.Initialize(&request_info, LOW, NetLogWithSource());
   EXPECT_TRUE(state.parser());
 }
 
@@ -55,7 +55,7 @@
   HttpRequestInfo request_info;
   request_info.traffic_annotation =
       MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
-  state.Initialize(&request_info, false, LOW, NetLogWithSource());
+  state.Initialize(&request_info, LOW, NetLogWithSource());
   EXPECT_EQ(TRAFFIC_ANNOTATION_FOR_TESTS,
             NetworkTrafficAnnotationTag(state.traffic_annotation()));
 }
@@ -63,7 +63,7 @@
 TEST(HttpBasicStateTest, DeleteParser) {
   HttpBasicState state(std::make_unique<ClientSocketHandle>(), false, false);
   const HttpRequestInfo request_info;
-  state.Initialize(&request_info, false, LOW, NetLogWithSource());
+  state.Initialize(&request_info, LOW, NetLogWithSource());
   EXPECT_TRUE(state.parser());
   state.DeleteParser();
   EXPECT_EQ(NULL, state.parser());
@@ -76,7 +76,7 @@
   HttpRequestInfo request_info;
   request_info.url = GURL("http://www.example.com/path?foo=bar#hoge");
   request_info.method = "PUT";
-  state.Initialize(&request_info, false, LOW, NetLogWithSource());
+  state.Initialize(&request_info, LOW, NetLogWithSource());
   EXPECT_EQ("PUT /path?foo=bar HTTP/1.1\r\n", state.GenerateRequestLine());
 }
 
@@ -87,7 +87,7 @@
   HttpRequestInfo request_info;
   request_info.url = GURL("http://www.example.com/path?foo=bar#hoge");
   request_info.method = "PUT";
-  state.Initialize(&request_info, false, LOW, NetLogWithSource());
+  state.Initialize(&request_info, LOW, NetLogWithSource());
   EXPECT_EQ("PUT http://www.example.com/path?foo=bar HTTP/1.1\r\n",
             state.GenerateRequestLine());
 }
diff --git a/net/http/http_basic_stream.cc b/net/http/http_basic_stream.cc
index 9278d66..e516919 100644
--- a/net/http/http_basic_stream.cc
+++ b/net/http/http_basic_stream.cc
@@ -31,8 +31,12 @@
                                       const NetLogWithSource& net_log,
                                       CompletionOnceCallback callback) {
   DCHECK(request_info->traffic_annotation.is_valid());
-  state_.Initialize(request_info, can_send_early, priority, net_log);
-  return OK;
+  state_.Initialize(request_info, priority, net_log);
+  int ret = OK;
+  if (!can_send_early) {
+    ret = parser()->ConfirmHandshake(std::move(callback));
+  }
+  return ret;
 }
 
 int HttpBasicStream::SendRequest(const HttpRequestHeaders& headers,
diff --git a/net/http/http_network_session.cc b/net/http/http_network_session.cc
index 23775ab0..1c32ed9e 100644
--- a/net/http/http_network_session.cc
+++ b/net/http/http_network_session.cc
@@ -95,6 +95,7 @@
       time_func(&base::TimeTicks::Now),
       enable_http2_alternative_service(false),
       enable_websocket_over_http2(false),
+      enable_early_data(false),
       enable_quic(false),
       enable_quic_proxies_for_https_urls(false),
       quic_max_packet_length(quic::kDefaultMaxPacketSize),
@@ -438,6 +439,7 @@
   GetAlpnProtos(&server_config->alpn_protos);
   server_config->ignore_certificate_errors = params_.ignore_certificate_errors;
   *proxy_config = *server_config;
+  server_config->early_data_enabled = params_.enable_early_data;
 }
 
 void HttpNetworkSession::DumpMemoryStats(
diff --git a/net/http/http_network_session.h b/net/http/http_network_session.h
index 2a1a34d..5852772 100644
--- a/net/http/http_network_session.h
+++ b/net/http/http_network_session.h
@@ -127,6 +127,9 @@
     // Whether to enable Websocket over HTTP/2.
     bool enable_websocket_over_http2;
 
+    // Enables 0-RTT support.
+    bool enable_early_data;
+
     // Enables QUIC support.
     bool enable_quic;
 
diff --git a/net/http/http_network_transaction_unittest.cc b/net/http/http_network_transaction_unittest.cc
index bf83c0a..52c3c54b 100644
--- a/net/http/http_network_transaction_unittest.cc
+++ b/net/http/http_network_transaction_unittest.cc
@@ -19640,4 +19640,381 @@
 
 #endif  // BUILDFLAG(ENABLE_REPORTING)
 
+TEST_F(HttpNetworkTransactionTest, ZeroRTTDoesntConfirm) {
+  HttpRequestInfo request;
+  request.method = "GET";
+  request.url = GURL("https://www.example.org/");
+  request.traffic_annotation =
+      net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
+
+  MockWrite data_writes[] = {
+      MockWrite("GET / HTTP/1.1\r\n"
+                "Host: www.example.org\r\n"
+                "Connection: keep-alive\r\n\r\n"),
+  };
+
+  // The proxy responds to the connect with a 407, using a persistent
+  // connection.
+  MockRead data_reads[] = {
+      MockRead("HTTP/1.1 200 OK\r\n"),
+      MockRead("Content-Length: 1\r\n\r\n"),
+      MockRead(SYNCHRONOUS, "1"),
+  };
+
+  StaticSocketDataProvider data(data_reads, data_writes);
+  session_deps_.socket_factory->AddSocketDataProvider(&data);
+  SSLSocketDataProvider ssl(SYNCHRONOUS, OK);
+  ssl.confirm = MockConfirm(SYNCHRONOUS, OK);
+  session_deps_.enable_early_data = true;
+  session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl);
+
+  std::unique_ptr<HttpNetworkSession> session(CreateSession(&session_deps_));
+
+  TestCompletionCallback callback;
+  auto trans =
+      std::make_unique<HttpNetworkTransaction>(DEFAULT_PRIORITY, session.get());
+
+  int rv = trans->Start(&request, callback.callback(), NetLogWithSource());
+  EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
+
+  rv = callback.WaitForResult();
+  EXPECT_THAT(rv, IsOk());
+
+  const HttpResponseInfo* response = trans->GetResponseInfo();
+  ASSERT_TRUE(response);
+  ASSERT_TRUE(response->headers);
+  EXPECT_EQ(200, response->headers->response_code());
+  EXPECT_EQ(1, response->headers->GetContentLength());
+
+  // Check that ConfirmHandshake wasn't called.
+  ASSERT_FALSE(ssl.ConfirmDataConsumed());
+  ASSERT_TRUE(ssl.WriteBeforeConfirm());
+
+  trans.reset();
+
+  session->CloseAllConnections();
+}
+
+TEST_F(HttpNetworkTransactionTest, ZeroRTTSyncConfirmSyncWrite) {
+  HttpRequestInfo request;
+  request.method = "POST";
+  request.url = GURL("https://www.example.org/");
+  request.traffic_annotation =
+      net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
+
+  MockWrite data_writes[] = {
+      MockWrite(SYNCHRONOUS,
+                "POST / HTTP/1.1\r\n"
+                "Host: www.example.org\r\n"
+                "Connection: keep-alive\r\n"
+                "Content-Length: 0\r\n\r\n"),
+  };
+
+  // The proxy responds to the connect with a 407, using a persistent
+  // connection.
+  MockRead data_reads[] = {
+      MockRead("HTTP/1.1 200 OK\r\n"),
+      MockRead("Content-Length: 1\r\n\r\n"),
+      MockRead(SYNCHRONOUS, "1"),
+  };
+
+  StaticSocketDataProvider data(data_reads, data_writes);
+  session_deps_.socket_factory->AddSocketDataProvider(&data);
+  SSLSocketDataProvider ssl(SYNCHRONOUS, OK);
+  ssl.confirm = MockConfirm(SYNCHRONOUS, OK);
+  session_deps_.enable_early_data = true;
+  session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl);
+
+  std::unique_ptr<HttpNetworkSession> session(CreateSession(&session_deps_));
+
+  TestCompletionCallback callback;
+  auto trans =
+      std::make_unique<HttpNetworkTransaction>(DEFAULT_PRIORITY, session.get());
+
+  int rv = trans->Start(&request, callback.callback(), NetLogWithSource());
+  EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
+
+  rv = callback.WaitForResult();
+  EXPECT_THAT(rv, IsOk());
+
+  const HttpResponseInfo* response = trans->GetResponseInfo();
+  ASSERT_TRUE(response);
+  ASSERT_TRUE(response->headers);
+  EXPECT_EQ(200, response->headers->response_code());
+  EXPECT_EQ(1, response->headers->GetContentLength());
+
+  // Check that the Write didn't get called before ConfirmHandshake completed.
+  ASSERT_FALSE(ssl.WriteBeforeConfirm());
+
+  trans.reset();
+
+  session->CloseAllConnections();
+}
+
+TEST_F(HttpNetworkTransactionTest, ZeroRTTSyncConfirmAsyncWrite) {
+  HttpRequestInfo request;
+  request.method = "POST";
+  request.url = GURL("https://www.example.org/");
+  request.traffic_annotation =
+      net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
+
+  MockWrite data_writes[] = {
+      MockWrite(ASYNC,
+                "POST / HTTP/1.1\r\n"
+                "Host: www.example.org\r\n"
+                "Connection: keep-alive\r\n"
+                "Content-Length: 0\r\n\r\n"),
+  };
+
+  // The proxy responds to the connect with a 407, using a persistent
+  // connection.
+  MockRead data_reads[] = {
+      MockRead("HTTP/1.1 200 OK\r\n"),
+      MockRead("Content-Length: 1\r\n\r\n"),
+      MockRead(SYNCHRONOUS, "1"),
+  };
+
+  StaticSocketDataProvider data(data_reads, data_writes);
+  session_deps_.socket_factory->AddSocketDataProvider(&data);
+  SSLSocketDataProvider ssl(SYNCHRONOUS, OK);
+  ssl.confirm = MockConfirm(SYNCHRONOUS, OK);
+  session_deps_.enable_early_data = true;
+  session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl);
+
+  std::unique_ptr<HttpNetworkSession> session(CreateSession(&session_deps_));
+
+  TestCompletionCallback callback;
+  auto trans =
+      std::make_unique<HttpNetworkTransaction>(DEFAULT_PRIORITY, session.get());
+
+  int rv = trans->Start(&request, callback.callback(), NetLogWithSource());
+  EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
+
+  rv = callback.WaitForResult();
+  EXPECT_THAT(rv, IsOk());
+
+  const HttpResponseInfo* response = trans->GetResponseInfo();
+  ASSERT_TRUE(response);
+  ASSERT_TRUE(response->headers);
+  EXPECT_EQ(200, response->headers->response_code());
+  EXPECT_EQ(1, response->headers->GetContentLength());
+
+  // Check that the Write didn't get called before ConfirmHandshake completed.
+  ASSERT_FALSE(ssl.WriteBeforeConfirm());
+
+  trans.reset();
+
+  session->CloseAllConnections();
+}
+
+TEST_F(HttpNetworkTransactionTest, ZeroRTTAsyncConfirmSyncWrite) {
+  HttpRequestInfo request;
+  request.method = "POST";
+  request.url = GURL("https://www.example.org/");
+  request.traffic_annotation =
+      net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
+
+  MockWrite data_writes[] = {
+      MockWrite(SYNCHRONOUS,
+                "POST / HTTP/1.1\r\n"
+                "Host: www.example.org\r\n"
+                "Connection: keep-alive\r\n"
+                "Content-Length: 0\r\n\r\n"),
+  };
+
+  // The proxy responds to the connect with a 407, using a persistent
+  // connection.
+  MockRead data_reads[] = {
+      MockRead("HTTP/1.1 200 OK\r\n"),
+      MockRead("Content-Length: 1\r\n\r\n"),
+      MockRead(SYNCHRONOUS, "1"),
+  };
+
+  StaticSocketDataProvider data(data_reads, data_writes);
+  session_deps_.socket_factory->AddSocketDataProvider(&data);
+  SSLSocketDataProvider ssl(SYNCHRONOUS, OK);
+  ssl.confirm = MockConfirm(ASYNC, OK);
+  session_deps_.enable_early_data = true;
+  session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl);
+
+  std::unique_ptr<HttpNetworkSession> session(CreateSession(&session_deps_));
+
+  TestCompletionCallback callback;
+  auto trans =
+      std::make_unique<HttpNetworkTransaction>(DEFAULT_PRIORITY, session.get());
+
+  int rv = trans->Start(&request, callback.callback(), NetLogWithSource());
+  EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
+
+  rv = callback.WaitForResult();
+  EXPECT_THAT(rv, IsOk());
+
+  const HttpResponseInfo* response = trans->GetResponseInfo();
+  ASSERT_TRUE(response);
+  ASSERT_TRUE(response->headers);
+  EXPECT_EQ(200, response->headers->response_code());
+  EXPECT_EQ(1, response->headers->GetContentLength());
+
+  // Check that the Write didn't get called before ConfirmHandshake completed.
+  ASSERT_FALSE(ssl.WriteBeforeConfirm());
+
+  trans.reset();
+
+  session->CloseAllConnections();
+}
+
+TEST_F(HttpNetworkTransactionTest, ZeroRTTAsyncConfirmAsyncWrite) {
+  HttpRequestInfo request;
+  request.method = "POST";
+  request.url = GURL("https://www.example.org/");
+  request.traffic_annotation =
+      net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
+
+  MockWrite data_writes[] = {
+      MockWrite(ASYNC,
+                "POST / HTTP/1.1\r\n"
+                "Host: www.example.org\r\n"
+                "Connection: keep-alive\r\n"
+                "Content-Length: 0\r\n\r\n"),
+  };
+
+  // The proxy responds to the connect with a 407, using a persistent
+  // connection.
+  MockRead data_reads[] = {
+      MockRead("HTTP/1.1 200 OK\r\n"),
+      MockRead("Content-Length: 1\r\n\r\n"),
+      MockRead(SYNCHRONOUS, "1"),
+  };
+
+  StaticSocketDataProvider data(data_reads, data_writes);
+  session_deps_.socket_factory->AddSocketDataProvider(&data);
+  SSLSocketDataProvider ssl(SYNCHRONOUS, OK);
+  ssl.confirm = MockConfirm(ASYNC, OK);
+  session_deps_.enable_early_data = true;
+  session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl);
+
+  std::unique_ptr<HttpNetworkSession> session(CreateSession(&session_deps_));
+
+  TestCompletionCallback callback;
+  auto trans =
+      std::make_unique<HttpNetworkTransaction>(DEFAULT_PRIORITY, session.get());
+
+  int rv = trans->Start(&request, callback.callback(), NetLogWithSource());
+  EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
+
+  rv = callback.WaitForResult();
+  EXPECT_THAT(rv, IsOk());
+
+  const HttpResponseInfo* response = trans->GetResponseInfo();
+  ASSERT_TRUE(response);
+  ASSERT_TRUE(response->headers);
+  EXPECT_EQ(200, response->headers->response_code());
+  EXPECT_EQ(1, response->headers->GetContentLength());
+
+  // Check that the Write didn't get called before ConfirmHandshake completed.
+  ASSERT_FALSE(ssl.WriteBeforeConfirm());
+
+  trans.reset();
+
+  session->CloseAllConnections();
+}
+
+TEST_F(HttpNetworkTransactionTest, ZeroRTTConfirmErrorSync) {
+  HttpRequestInfo request;
+  request.method = "POST";
+  request.url = GURL("https://www.example.org/");
+  request.traffic_annotation =
+      net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
+
+  MockWrite data_writes[] = {
+      MockWrite("POST / HTTP/1.1\r\n"
+                "Host: www.example.org\r\n"
+                "Connection: keep-alive\r\n"
+                "Content-Length: 0\r\n\r\n"),
+  };
+
+  // The proxy responds to the connect with a 407, using a persistent
+  // connection.
+  MockRead data_reads[] = {
+      MockRead("HTTP/1.1 200 OK\r\n"),
+      MockRead("Content-Length: 1\r\n\r\n"),
+      MockRead(SYNCHRONOUS, "1"),
+  };
+
+  StaticSocketDataProvider data(data_reads, data_writes);
+  session_deps_.socket_factory->AddSocketDataProvider(&data);
+  SSLSocketDataProvider ssl(SYNCHRONOUS, OK);
+  ssl.confirm = MockConfirm(SYNCHRONOUS, ERR_SSL_PROTOCOL_ERROR);
+  session_deps_.enable_early_data = true;
+  session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl);
+
+  std::unique_ptr<HttpNetworkSession> session(CreateSession(&session_deps_));
+
+  TestCompletionCallback callback;
+  auto trans =
+      std::make_unique<HttpNetworkTransaction>(DEFAULT_PRIORITY, session.get());
+
+  int rv = trans->Start(&request, callback.callback(), NetLogWithSource());
+  EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
+
+  rv = callback.WaitForResult();
+  EXPECT_THAT(rv, IsError(ERR_SSL_PROTOCOL_ERROR));
+
+  // Check that the Write didn't get called before ConfirmHandshake completed.
+  ASSERT_FALSE(ssl.WriteBeforeConfirm());
+
+  trans.reset();
+
+  session->CloseAllConnections();
+}
+
+TEST_F(HttpNetworkTransactionTest, ZeroRTTConfirmErrorAsync) {
+  HttpRequestInfo request;
+  request.method = "POST";
+  request.url = GURL("https://www.example.org/");
+  request.traffic_annotation =
+      net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
+
+  MockWrite data_writes[] = {
+      MockWrite("POST / HTTP/1.1\r\n"
+                "Host: www.example.org\r\n"
+                "Connection: keep-alive\r\n"
+                "Content-Length: 0\r\n\r\n"),
+  };
+
+  // The proxy responds to the connect with a 407, using a persistent
+  // connection.
+  MockRead data_reads[] = {
+      MockRead("HTTP/1.1 200 OK\r\n"),
+      MockRead("Content-Length: 1\r\n\r\n"),
+      MockRead(SYNCHRONOUS, "1"),
+  };
+
+  StaticSocketDataProvider data(data_reads, data_writes);
+  session_deps_.socket_factory->AddSocketDataProvider(&data);
+  SSLSocketDataProvider ssl(SYNCHRONOUS, OK);
+  ssl.confirm = MockConfirm(ASYNC, ERR_SSL_PROTOCOL_ERROR);
+  session_deps_.enable_early_data = true;
+  session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl);
+
+  std::unique_ptr<HttpNetworkSession> session(CreateSession(&session_deps_));
+
+  TestCompletionCallback callback;
+  auto trans =
+      std::make_unique<HttpNetworkTransaction>(DEFAULT_PRIORITY, session.get());
+
+  int rv = trans->Start(&request, callback.callback(), NetLogWithSource());
+  EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
+
+  rv = callback.WaitForResult();
+  EXPECT_THAT(rv, IsError(ERR_SSL_PROTOCOL_ERROR));
+
+  // Check that the Write didn't get called before ConfirmHandshake completed.
+  ASSERT_FALSE(ssl.WriteBeforeConfirm());
+
+  trans.reset();
+
+  session->CloseAllConnections();
+}
+
 }  // namespace net
diff --git a/net/http/http_proxy_connect_job.cc b/net/http/http_proxy_connect_job.cc
index 5792433..a6dd03637 100644
--- a/net/http/http_proxy_connect_job.cc
+++ b/net/http/http_proxy_connect_job.cc
@@ -623,8 +623,9 @@
   spdy_stream_request_ = std::make_unique<SpdyStreamRequest>();
   return spdy_stream_request_->StartRequest(
       SPDY_BIDIRECTIONAL_STREAM, spdy_session,
-      GURL("https://" + params_->endpoint().ToString()), kH2QuicTunnelPriority,
-      socket_tag(), spdy_session->net_log(),
+      GURL("https://" + params_->endpoint().ToString()),
+      false /* no early data */, kH2QuicTunnelPriority, socket_tag(),
+      spdy_session->net_log(),
       base::BindOnce(&HttpProxyConnectJob::OnIOComplete,
                      base::Unretained(this)),
       params_->traffic_annotation());
diff --git a/net/http/http_proxy_connect_job_unittest.cc b/net/http/http_proxy_connect_job_unittest.cc
index c4eb083..dc877737 100644
--- a/net/http/http_proxy_connect_job_unittest.cc
+++ b/net/http/http_proxy_connect_job_unittest.cc
@@ -194,6 +194,8 @@
     session_deps_.socket_factory->AddSocketDataProvider(data_.get());
 
     if (GetParam() != HTTP) {
+      // Keep the old ssl_data in case there is a draining socket.
+      old_ssl_data_.swap(ssl_data_);
       ssl_data_ =
           std::make_unique<SSLSocketDataProvider>(connect_and_ssl_io_mode, OK);
       if (GetParam() == SPDY) {
@@ -241,6 +243,7 @@
   std::unique_ptr<TestProxyDelegate> proxy_delegate_;
 
   std::unique_ptr<SSLSocketDataProvider> ssl_data_;
+  std::unique_ptr<SSLSocketDataProvider> old_ssl_data_;
   std::unique_ptr<SequencedSocketData> data_;
   SpdySessionDependencies session_deps_;
 
diff --git a/net/http/http_stream_parser.cc b/net/http/http_stream_parser.cc
index 3db1d0e..35c7a4e 100644
--- a/net/http/http_stream_parser.cc
+++ b/net/http/http_stream_parser.cc
@@ -320,6 +320,15 @@
   return result > 0 ? OK : result;
 }
 
+int HttpStreamParser::ConfirmHandshake(CompletionOnceCallback callback) {
+  int ret = stream_socket_->ConfirmHandshake(
+      base::BindOnce(&HttpStreamParser::RunConfirmHandshakeCallback,
+                     weak_ptr_factory_.GetWeakPtr()));
+  if (ret == ERR_IO_PENDING)
+    confirm_handshake_callback_ = std::move(callback);
+  return ret;
+}
+
 int HttpStreamParser::ReadResponseHeaders(CompletionOnceCallback callback) {
   DCHECK(io_state_ == STATE_NONE || io_state_ == STATE_DONE);
   DCHECK(callback_.is_null());
@@ -932,6 +941,10 @@
   return OK;
 }
 
+void HttpStreamParser::RunConfirmHandshakeCallback(int rv) {
+  std::move(confirm_handshake_callback_).Run(rv);
+}
+
 int HttpStreamParser::FindAndParseResponseHeaders(int new_bytes) {
   DCHECK_GT(new_bytes, 0);
   DCHECK_EQ(0, read_buf_unused_offset_);
diff --git a/net/http/http_stream_parser.h b/net/http/http_stream_parser.h
index c09a709..16d24e2 100644
--- a/net/http/http_stream_parser.h
+++ b/net/http/http_stream_parser.h
@@ -73,6 +73,8 @@
                   HttpResponseInfo* response,
                   CompletionOnceCallback callback);
 
+  int ConfirmHandshake(CompletionOnceCallback callback);
+
   int ReadResponseHeaders(CompletionOnceCallback callback);
 
   int ReadResponseBody(IOBuffer* buf,
@@ -183,6 +185,8 @@
   // This handles most of the logic for DoReadHeadersComplete.
   int HandleReadHeaderResult(int result);
 
+  void RunConfirmHandshakeCallback(int rv);
+
   // Examines |read_buf_| to find the start and end of the headers. If they are
   // found, parse them with DoParseResponseHeaders().  Return the offset for
   // the end of the headers, or -1 if the complete headers were not found, or
@@ -266,6 +270,9 @@
   scoped_refptr<IOBuffer> user_read_buf_;
   int user_read_buf_len_;
 
+  // The callback to notify a user that the handshake has been confirmed.
+  CompletionOnceCallback confirm_handshake_callback_;
+
   // The callback to notify a user that their request or response is
   // complete or there was an error
   CompletionOnceCallback callback_;
diff --git a/net/socket/socket_test_util.cc b/net/socket/socket_test_util.cc
index 3692ea4..7c949f5 100644
--- a/net/socket/socket_test_util.cc
+++ b/net/socket/socket_test_util.cc
@@ -157,6 +157,12 @@
 
 MockConnect::~MockConnect() = default;
 
+MockConfirm::MockConfirm() : mode(SYNCHRONOUS), result(OK) {}
+
+MockConfirm::MockConfirm(IoMode io_mode, int r) : mode(io_mode), result(r) {}
+
+MockConfirm::~MockConfirm() = default;
+
 bool SocketDataProvider::IsIdle() const {
   return true;
 }
@@ -1461,6 +1467,8 @@
     int buf_len,
     CompletionOnceCallback callback,
     const NetworkTrafficAnnotationTag& traffic_annotation) {
+  if (!data_->is_confirm_data_consumed)
+    data_->write_called_before_confirm = true;
   return stream_socket_->Write(buf, buf_len, std::move(callback),
                                traffic_annotation);
 }
@@ -1486,6 +1494,28 @@
     stream_socket_->Disconnect();
 }
 
+void MockSSLClientSocket::RunConfirmHandshakeCallback(
+    CompletionOnceCallback callback,
+    int result) {
+  data_->is_confirm_data_consumed = true;
+  std::move(callback).Run(result);
+}
+
+int MockSSLClientSocket::ConfirmHandshake(CompletionOnceCallback callback) {
+  DCHECK(stream_socket_->IsConnected());
+  if (data_->is_confirm_data_consumed)
+    return data_->confirm.result;
+  if (data_->confirm.mode == ASYNC) {
+    RunCallbackAsync(
+        base::BindOnce(&MockSSLClientSocket::RunConfirmHandshakeCallback,
+                       base::Unretained(this), std::move(callback)),
+        data_->confirm.result);
+    return ERR_IO_PENDING;
+  }
+  data_->is_confirm_data_consumed = true;
+  return data_->confirm.result;
+}
+
 bool MockSSLClientSocket::IsConnected() const {
   return stream_socket_->IsConnected();
 }
diff --git a/net/socket/socket_test_util.h b/net/socket/socket_test_util.h
index 1b41630..44b8bf7 100644
--- a/net/socket/socket_test_util.h
+++ b/net/socket/socket_test_util.h
@@ -94,6 +94,18 @@
   IPEndPoint peer_addr;
 };
 
+struct MockConfirm {
+  // Asynchronous confirm success.
+  // Creates a MockConfirm with |mode| ASYC and |result| OK.
+  MockConfirm();
+  // Creates a MockConfirm with the specified mode and result.
+  MockConfirm(IoMode io_mode, int r);
+  ~MockConfirm();
+
+  IoMode mode;
+  int result;
+};
+
 // MockRead and MockWrite shares the same interface and members, but we'd like
 // to have distinct types because we don't want to have them used
 // interchangably. To do this, a struct template is defined, and MockRead and
@@ -441,9 +453,18 @@
   // Returns whether MockConnect data has been consumed.
   bool ConnectDataConsumed() const { return is_connect_data_consumed; }
 
+  // Returns whether MockConfirm data has been consumed.
+  bool ConfirmDataConsumed() const { return is_confirm_data_consumed; }
+
+  // Returns whether a Write occurred before ConfirmHandshake completed.
+  bool WriteBeforeConfirm() const { return write_called_before_confirm; }
+
   // Result for Connect().
   MockConnect connect;
 
+  // Result for Confirm().
+  MockConfirm confirm;
+
   // Result for GetNegotiatedProtocol().
   NextProto next_proto;
 
@@ -459,6 +480,8 @@
   uint16_t expected_ssl_version_max;
 
   bool is_connect_data_consumed = false;
+  bool is_confirm_data_consumed = false;
+  bool write_called_before_confirm = false;
 };
 
 // Uses the sequence_number field in the mock reads and writes to
@@ -898,6 +921,7 @@
   // StreamSocket implementation.
   int Connect(CompletionOnceCallback callback) override;
   void Disconnect() override;
+  int ConfirmHandshake(CompletionOnceCallback callback) override;
   bool IsConnected() const override;
   bool IsConnectedAndIdle() const override;
   bool WasEverUsed() const override;
@@ -941,6 +965,8 @@
   void RunCallbackAsync(CompletionOnceCallback callback, int result);
   void RunCallback(CompletionOnceCallback callback, int result);
 
+  void RunConfirmHandshakeCallback(CompletionOnceCallback callback, int result);
+
   bool connected_ = false;
   NetLogWithSource net_log_;
   std::unique_ptr<StreamSocket> stream_socket_;
diff --git a/net/spdy/bidirectional_stream_spdy_impl.cc b/net/spdy/bidirectional_stream_spdy_impl.cc
index f1281455..32d0999 100644
--- a/net/spdy/bidirectional_stream_spdy_impl.cc
+++ b/net/spdy/bidirectional_stream_spdy_impl.cc
@@ -78,7 +78,8 @@
 
   int rv = stream_request_.StartRequest(
       SPDY_BIDIRECTIONAL_STREAM, spdy_session_, request_info_->url,
-      request_info_->priority, request_info_->socket_tag, net_log,
+      false /* no early data */, request_info_->priority,
+      request_info_->socket_tag, net_log,
       base::Bind(&BidirectionalStreamSpdyImpl::OnStreamInitialized,
                  weak_factory_.GetWeakPtr()),
       traffic_annotation);
diff --git a/net/spdy/spdy_http_stream.cc b/net/spdy/spdy_http_stream.cc
index 8d7ea84..63c692a 100644
--- a/net/spdy/spdy_http_stream.cc
+++ b/net/spdy/spdy_http_stream.cc
@@ -156,8 +156,8 @@
   }
 
   int rv = stream_request_.StartRequest(
-      SPDY_REQUEST_RESPONSE_STREAM, spdy_session_, request_info_->url, priority,
-      request_info_->socket_tag, stream_net_log,
+      SPDY_REQUEST_RESPONSE_STREAM, spdy_session_, request_info_->url,
+      can_send_early, priority, request_info_->socket_tag, stream_net_log,
       base::BindOnce(&SpdyHttpStream::OnStreamCreated,
                      weak_factory_.GetWeakPtr(), std::move(callback)),
       NetworkTrafficAnnotationTag(request_info->traffic_annotation));
diff --git a/net/spdy/spdy_network_transaction_unittest.cc b/net/spdy/spdy_network_transaction_unittest.cc
index feaf25b..b4e5603 100644
--- a/net/spdy/spdy_network_transaction_unittest.cc
+++ b/net/spdy/spdy_network_transaction_unittest.cc
@@ -8195,4 +8195,540 @@
 
 #endif  // BUILDFLAG(ENABLE_WEBSOCKETS)
 
+TEST_F(SpdyNetworkTransactionTest, ZeroRTTDoesntConfirm) {
+  spdy::SpdySerializedFrame req(
+      spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST));
+  MockWrite writes[] = {CreateMockWrite(req, 0)};
+
+  spdy::SpdySerializedFrame resp(
+      spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
+  spdy::SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true));
+  MockRead reads[] = {
+      CreateMockRead(resp, 1), CreateMockRead(body, 2),
+      MockRead(ASYNC, 0, 3)  // EOF
+  };
+
+  SequencedSocketData data(reads, writes);
+  auto session_deps = std::make_unique<SpdySessionDependencies>();
+  session_deps->enable_early_data = true;
+  NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_,
+                                     std::move(session_deps));
+  auto ssl_provider = std::make_unique<SSLSocketDataProvider>(ASYNC, OK);
+  // Configure |ssl_provider| to fail if ConfirmHandshake is called. The request
+  // should still succeed.
+  ssl_provider->confirm = MockConfirm(SYNCHRONOUS, ERR_SSL_PROTOCOL_ERROR);
+  helper.RunToCompletionWithSSLData(&data, std::move(ssl_provider));
+  TransactionHelperResult out = helper.output();
+  EXPECT_THAT(out.rv, IsOk());
+  EXPECT_EQ("HTTP/1.1 200", out.status_line);
+  EXPECT_EQ("hello!", out.response_data);
+}
+
+// Run multiple concurrent streams that don't require handshake confirmation.
+TEST_F(SpdyNetworkTransactionTest, ZeroRTTNoConfirmMultipleStreams) {
+  spdy::SpdySerializedFrame req1(
+      spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST));
+  spdy::SpdySerializedFrame req2(
+      spdy_util_.ConstructSpdyGet(nullptr, 0, 3, LOWEST));
+  MockWrite writes1[] = {CreateMockWrite(req1, 0), CreateMockWrite(req2, 3)};
+
+  spdy::SpdySerializedFrame resp1(
+      spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
+  spdy::SpdySerializedFrame body1(spdy_util_.ConstructSpdyDataFrame(1, true));
+  spdy::SpdySerializedFrame resp2(
+      spdy_util_.ConstructSpdyGetReply(nullptr, 0, 3));
+  spdy::SpdySerializedFrame body2(spdy_util_.ConstructSpdyDataFrame(3, true));
+  MockRead reads1[] = {
+      CreateMockRead(resp1, 1), CreateMockRead(body1, 2),
+      CreateMockRead(resp2, 4), CreateMockRead(body2, 5),
+      MockRead(ASYNC, 0, 6)  // EOF
+  };
+
+  SequencedSocketData data1(reads1, writes1);
+  SequencedSocketData data2({}, {});
+  auto session_deps = std::make_unique<SpdySessionDependencies>();
+  session_deps->enable_early_data = true;
+  NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_,
+                                     std::move(session_deps));
+  auto ssl_provider1 = std::make_unique<SSLSocketDataProvider>(ASYNC, OK);
+  ssl_provider1->confirm = MockConfirm(SYNCHRONOUS, ERR_SSL_PROTOCOL_ERROR);
+  auto ssl_provider2 = std::make_unique<SSLSocketDataProvider>(ASYNC, OK);
+  ssl_provider2->confirm = MockConfirm(SYNCHRONOUS, ERR_SSL_PROTOCOL_ERROR);
+
+  helper.RunPreTestSetup();
+  helper.AddDataWithSSLSocketDataProvider(&data1, std::move(ssl_provider1));
+  helper.AddDataWithSSLSocketDataProvider(&data2, std::move(ssl_provider2));
+  EXPECT_TRUE(helper.StartDefaultTest());
+
+  HttpNetworkTransaction trans2(DEFAULT_PRIORITY, helper.session());
+  HttpRequestInfo request2;
+  request2.method = "GET";
+  request2.url = GURL(kDefaultUrl);
+  request2.traffic_annotation =
+      net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
+  TestCompletionCallback callback2;
+  int rv = trans2.Start(&request2, callback2.callback(), log_);
+  EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
+
+  helper.FinishDefaultTest();
+  EXPECT_THAT(callback2.GetResult(ERR_IO_PENDING), IsOk());
+  helper.VerifyDataConsumed();
+
+  TransactionHelperResult out = helper.output();
+  EXPECT_THAT(out.rv, IsOk());
+  EXPECT_EQ("HTTP/1.1 200", out.status_line);
+  EXPECT_EQ("hello!", out.response_data);
+}
+
+// Run multiple concurrent streams that require handshake confirmation.
+TEST_F(SpdyNetworkTransactionTest, ZeroRTTConfirmMultipleStreams) {
+  spdy::SpdyHeaderBlock req_block1(
+      spdy_util_.ConstructPostHeaderBlock(kDefaultUrl, 0));
+  spdy::SpdySerializedFrame req1(
+      spdy_util_.ConstructSpdyHeaders(1, std::move(req_block1), LOWEST, true));
+  spdy::SpdyHeaderBlock req_block2(
+      spdy_util_.ConstructPostHeaderBlock(kDefaultUrl, 0));
+  spdy::SpdySerializedFrame req2(
+      spdy_util_.ConstructSpdyHeaders(3, std::move(req_block2), LOWEST, true));
+  MockWrite writes[] = {
+      CreateMockWrite(req1, 0),
+      CreateMockWrite(req2, 3),
+  };
+  spdy::SpdySerializedFrame resp1(
+      spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
+  spdy::SpdySerializedFrame body1(spdy_util_.ConstructSpdyDataFrame(1, true));
+  spdy::SpdySerializedFrame resp2(
+      spdy_util_.ConstructSpdyGetReply(nullptr, 0, 3));
+  spdy::SpdySerializedFrame body2(spdy_util_.ConstructSpdyDataFrame(3, true));
+  MockRead reads[] = {
+      CreateMockRead(resp1, 1), CreateMockRead(body1, 2),
+      CreateMockRead(resp2, 4), CreateMockRead(body2, 5),
+      MockRead(ASYNC, 0, 6)  // EOF
+  };
+
+  SequencedSocketData data1(reads, writes);
+  SequencedSocketData data2({}, {});
+  UsePostRequest();
+  auto session_deps = std::make_unique<SpdySessionDependencies>();
+  session_deps->enable_early_data = true;
+  NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_,
+                                     std::move(session_deps));
+  auto ssl_provider1 = std::make_unique<SSLSocketDataProvider>(ASYNC, OK);
+  ssl_provider1->confirm = MockConfirm(ASYNC, OK);
+  auto ssl_provider2 = std::make_unique<SSLSocketDataProvider>(ASYNC, OK);
+  ssl_provider2->confirm = MockConfirm(ASYNC, OK);
+
+  helper.RunPreTestSetup();
+  helper.AddDataWithSSLSocketDataProvider(&data1, std::move(ssl_provider1));
+  helper.AddDataWithSSLSocketDataProvider(&data2, std::move(ssl_provider2));
+
+  HttpNetworkTransaction trans1(DEFAULT_PRIORITY, helper.session());
+  HttpRequestInfo request1;
+  request1.method = "POST";
+  request1.url = GURL(kDefaultUrl);
+  request1.traffic_annotation =
+      net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
+  TestCompletionCallback callback1;
+  int rv = trans1.Start(&request1, callback1.callback(), log_);
+  EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
+
+  HttpNetworkTransaction trans2(DEFAULT_PRIORITY, helper.session());
+  HttpRequestInfo request2;
+  request2.method = "POST";
+  request2.url = GURL(kDefaultUrl);
+  request2.traffic_annotation =
+      net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
+  TestCompletionCallback callback2;
+  rv = trans2.Start(&request2, callback2.callback(), log_);
+  EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
+
+  EXPECT_THAT(callback1.GetResult(ERR_IO_PENDING), IsOk());
+  EXPECT_THAT(callback2.GetResult(ERR_IO_PENDING), IsOk());
+
+  const HttpResponseInfo* response1 = trans1.GetResponseInfo();
+  ASSERT_TRUE(response1);
+  ASSERT_TRUE(response1->headers);
+  EXPECT_EQ(HttpResponseInfo::CONNECTION_INFO_HTTP2,
+            response1->connection_info);
+  EXPECT_EQ("HTTP/1.1 200", response1->headers->GetStatusLine());
+  std::string response_data;
+  ReadTransaction(&trans1, &response_data);
+  EXPECT_EQ("hello!", response_data);
+
+  const HttpResponseInfo* response2 = trans2.GetResponseInfo();
+  ASSERT_TRUE(response2);
+  ASSERT_TRUE(response2->headers);
+  EXPECT_EQ(HttpResponseInfo::CONNECTION_INFO_HTTP2,
+            response2->connection_info);
+  EXPECT_EQ("HTTP/1.1 200", response2->headers->GetStatusLine());
+  ReadTransaction(&trans2, &response_data);
+  EXPECT_EQ("hello!", response_data);
+
+  helper.VerifyDataConsumed();
+}
+
+// Run multiple concurrent streams, the first require a confirmation and the
+// second not requiring confirmation.
+TEST_F(SpdyNetworkTransactionTest, ZeroRTTConfirmNoConfirmStreams) {
+  // This test orders the writes such that the GET (no confirmation) is written
+  // before the POST (confirmation required).
+  spdy::SpdyHeaderBlock req_block1(
+      spdy_util_.ConstructGetHeaderBlock(kDefaultUrl));
+  spdy::SpdySerializedFrame req1(
+      spdy_util_.ConstructSpdyHeaders(1, std::move(req_block1), LOWEST, true));
+  spdy::SpdyHeaderBlock req_block2(
+      spdy_util_.ConstructPostHeaderBlock(kDefaultUrl, 0));
+  spdy::SpdySerializedFrame req2(
+      spdy_util_.ConstructSpdyHeaders(3, std::move(req_block2), LOWEST, true));
+  MockWrite writes[] = {
+      CreateMockWrite(req1, 0),
+      CreateMockWrite(req2, 3),
+  };
+  spdy::SpdySerializedFrame resp1(
+      spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
+  spdy::SpdySerializedFrame body1(spdy_util_.ConstructSpdyDataFrame(1, true));
+  spdy::SpdySerializedFrame resp2(
+      spdy_util_.ConstructSpdyGetReply(nullptr, 0, 3));
+  spdy::SpdySerializedFrame body2(spdy_util_.ConstructSpdyDataFrame(3, true));
+  MockRead reads[] = {
+      CreateMockRead(resp1, 1), CreateMockRead(body1, 2),
+      CreateMockRead(resp2, 4), CreateMockRead(body2, 5),
+      MockRead(ASYNC, 0, 6)  // EOF
+  };
+
+  SequencedSocketData data1(reads, writes);
+  SequencedSocketData data2({}, {});
+  UsePostRequest();
+  auto session_deps = std::make_unique<SpdySessionDependencies>();
+  session_deps->enable_early_data = true;
+  NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_,
+                                     std::move(session_deps));
+  auto ssl_provider1 = std::make_unique<SSLSocketDataProvider>(ASYNC, OK);
+  ssl_provider1->confirm = MockConfirm(ASYNC, OK);
+  auto ssl_provider2 = std::make_unique<SSLSocketDataProvider>(ASYNC, OK);
+  ssl_provider2->confirm = MockConfirm(ASYNC, OK);
+
+  helper.RunPreTestSetup();
+  helper.AddDataWithSSLSocketDataProvider(&data1, std::move(ssl_provider1));
+  helper.AddDataWithSSLSocketDataProvider(&data2, std::move(ssl_provider2));
+
+  // TODO(https://crbug.com/949724): Explicitly verify the ordering of
+  // ConfirmHandshake and the second stream.
+
+  HttpNetworkTransaction trans1(DEFAULT_PRIORITY, helper.session());
+  HttpRequestInfo request1;
+  request1.method = "POST";
+  request1.url = GURL(kDefaultUrl);
+  request1.traffic_annotation =
+      net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
+  TestCompletionCallback callback1;
+  int rv = trans1.Start(&request1, callback1.callback(), log_);
+  EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
+
+  HttpNetworkTransaction trans2(DEFAULT_PRIORITY, helper.session());
+  HttpRequestInfo request2;
+  request2.method = "GET";
+  request2.url = GURL(kDefaultUrl);
+  request2.traffic_annotation =
+      net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
+  TestCompletionCallback callback2;
+  rv = trans2.Start(&request2, callback2.callback(), log_);
+  EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
+
+  EXPECT_THAT(callback1.GetResult(ERR_IO_PENDING), IsOk());
+  EXPECT_THAT(callback2.GetResult(ERR_IO_PENDING), IsOk());
+
+  const HttpResponseInfo* response1 = trans1.GetResponseInfo();
+  ASSERT_TRUE(response1);
+  ASSERT_TRUE(response1->headers);
+  EXPECT_EQ(HttpResponseInfo::CONNECTION_INFO_HTTP2,
+            response1->connection_info);
+  EXPECT_EQ("HTTP/1.1 200", response1->headers->GetStatusLine());
+  std::string response_data;
+  ReadTransaction(&trans1, &response_data);
+  EXPECT_EQ("hello!", response_data);
+
+  const HttpResponseInfo* response2 = trans2.GetResponseInfo();
+  ASSERT_TRUE(response2);
+  ASSERT_TRUE(response2->headers);
+  EXPECT_EQ(HttpResponseInfo::CONNECTION_INFO_HTTP2,
+            response2->connection_info);
+  EXPECT_EQ("HTTP/1.1 200", response2->headers->GetStatusLine());
+  ReadTransaction(&trans2, &response_data);
+  EXPECT_EQ("hello!", response_data);
+
+  helper.VerifyDataConsumed();
+}
+
+// Run multiple concurrent streams, the first not requiring confirmation and the
+// second requiring confirmation.
+TEST_F(SpdyNetworkTransactionTest, ZeroRTTNoConfirmConfirmStreams) {
+  // This test orders the writes such that the GET (no confirmation) is written
+  // before the POST (confirmation required).
+  spdy::SpdyHeaderBlock req_block1(
+      spdy_util_.ConstructGetHeaderBlock(kDefaultUrl));
+  spdy::SpdySerializedFrame req1(
+      spdy_util_.ConstructSpdyHeaders(1, std::move(req_block1), LOWEST, true));
+  spdy::SpdyHeaderBlock req_block2(
+      spdy_util_.ConstructPostHeaderBlock(kDefaultUrl, 0));
+  spdy::SpdySerializedFrame req2(
+      spdy_util_.ConstructSpdyHeaders(3, std::move(req_block2), LOWEST, true));
+  MockWrite writes[] = {
+      CreateMockWrite(req1, 0),
+      CreateMockWrite(req2, 3),
+  };
+  spdy::SpdySerializedFrame resp1(
+      spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
+  spdy::SpdySerializedFrame body1(spdy_util_.ConstructSpdyDataFrame(1, true));
+  spdy::SpdySerializedFrame resp2(
+      spdy_util_.ConstructSpdyGetReply(nullptr, 0, 3));
+  spdy::SpdySerializedFrame body2(spdy_util_.ConstructSpdyDataFrame(3, true));
+  MockRead reads[] = {
+      CreateMockRead(resp1, 1), CreateMockRead(body1, 2),
+      CreateMockRead(resp2, 4), CreateMockRead(body2, 5),
+      MockRead(ASYNC, 0, 6)  // EOF
+  };
+
+  SequencedSocketData data1(reads, writes);
+  SequencedSocketData data2({}, {});
+  UsePostRequest();
+  auto session_deps = std::make_unique<SpdySessionDependencies>();
+  session_deps->enable_early_data = true;
+  NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_,
+                                     std::move(session_deps));
+  auto ssl_provider1 = std::make_unique<SSLSocketDataProvider>(ASYNC, OK);
+  ssl_provider1->confirm = MockConfirm(ASYNC, OK);
+  auto ssl_provider2 = std::make_unique<SSLSocketDataProvider>(ASYNC, OK);
+  ssl_provider2->confirm = MockConfirm(ASYNC, OK);
+
+  helper.RunPreTestSetup();
+  helper.AddDataWithSSLSocketDataProvider(&data1, std::move(ssl_provider1));
+  helper.AddDataWithSSLSocketDataProvider(&data2, std::move(ssl_provider2));
+
+  // TODO(https://crbug.com/949724): Explicitly verify the ordering of
+  // ConfirmHandshake and the second stream.
+
+  HttpNetworkTransaction trans1(DEFAULT_PRIORITY, helper.session());
+  HttpRequestInfo request1;
+  request1.method = "GET";
+  request1.url = GURL(kDefaultUrl);
+  request1.traffic_annotation =
+      net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
+  TestCompletionCallback callback1;
+  int rv = trans1.Start(&request1, callback1.callback(), log_);
+  EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
+
+  HttpNetworkTransaction trans2(DEFAULT_PRIORITY, helper.session());
+  HttpRequestInfo request2;
+  request2.method = "POST";
+  request2.url = GURL(kDefaultUrl);
+  request2.traffic_annotation =
+      net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
+  TestCompletionCallback callback2;
+  rv = trans2.Start(&request2, callback2.callback(), log_);
+  EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
+
+  EXPECT_THAT(callback1.GetResult(ERR_IO_PENDING), IsOk());
+  EXPECT_THAT(callback2.GetResult(ERR_IO_PENDING), IsOk());
+
+  const HttpResponseInfo* response1 = trans1.GetResponseInfo();
+  ASSERT_TRUE(response1);
+  ASSERT_TRUE(response1->headers);
+  EXPECT_EQ(HttpResponseInfo::CONNECTION_INFO_HTTP2,
+            response1->connection_info);
+  EXPECT_EQ("HTTP/1.1 200", response1->headers->GetStatusLine());
+  std::string response_data;
+  ReadTransaction(&trans1, &response_data);
+  EXPECT_EQ("hello!", response_data);
+
+  const HttpResponseInfo* response2 = trans2.GetResponseInfo();
+  ASSERT_TRUE(response2);
+  ASSERT_TRUE(response2->headers);
+  EXPECT_EQ(HttpResponseInfo::CONNECTION_INFO_HTTP2,
+            response2->connection_info);
+  EXPECT_EQ("HTTP/1.1 200", response2->headers->GetStatusLine());
+  ReadTransaction(&trans2, &response_data);
+  EXPECT_EQ("hello!", response_data);
+
+  helper.VerifyDataConsumed();
+}
+
+TEST_F(SpdyNetworkTransactionTest, ZeroRTTSyncConfirmSyncWrite) {
+  spdy::SpdySerializedFrame req(spdy_util_.ConstructSpdyPost(
+      kDefaultUrl, 1, kUploadDataSize, LOWEST, nullptr, 0));
+  spdy::SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true));
+  MockWrite writes[] = {
+      CreateMockWrite(req, 0, SYNCHRONOUS),
+      CreateMockWrite(body, 1),  // POST upload frame
+  };
+
+  spdy::SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(nullptr, 0));
+  MockRead reads[] = {
+      CreateMockRead(resp, 2), CreateMockRead(body, 3),
+      MockRead(ASYNC, 0, 4)  // EOF
+  };
+
+  SequencedSocketData data(reads, writes);
+  UsePostRequest();
+  auto session_deps = std::make_unique<SpdySessionDependencies>();
+  session_deps->enable_early_data = true;
+  NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_,
+                                     std::move(session_deps));
+  auto ssl_provider = std::make_unique<SSLSocketDataProvider>(ASYNC, OK);
+  ssl_provider->confirm = MockConfirm(SYNCHRONOUS, OK);
+  helper.RunToCompletionWithSSLData(&data, std::move(ssl_provider));
+  TransactionHelperResult out = helper.output();
+  EXPECT_THAT(out.rv, IsOk());
+  EXPECT_EQ("HTTP/1.1 200", out.status_line);
+  EXPECT_EQ("hello!", out.response_data);
+}
+
+TEST_F(SpdyNetworkTransactionTest, ZeroRTTSyncConfirmAsyncWrite) {
+  spdy::SpdySerializedFrame req(spdy_util_.ConstructSpdyPost(
+      kDefaultUrl, 1, kUploadDataSize, LOWEST, nullptr, 0));
+  spdy::SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true));
+  MockWrite writes[] = {
+      CreateMockWrite(req, 0, ASYNC),
+      CreateMockWrite(body, 1),  // POST upload frame
+  };
+
+  spdy::SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(nullptr, 0));
+  MockRead reads[] = {
+      CreateMockRead(resp, 2), CreateMockRead(body, 3),
+      MockRead(ASYNC, 0, 4)  // EOF
+  };
+
+  SequencedSocketData data(reads, writes);
+  UsePostRequest();
+  auto session_deps = std::make_unique<SpdySessionDependencies>();
+  session_deps->enable_early_data = true;
+  NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_,
+                                     std::move(session_deps));
+  auto ssl_provider = std::make_unique<SSLSocketDataProvider>(ASYNC, OK);
+  ssl_provider->confirm = MockConfirm(SYNCHRONOUS, OK);
+  helper.RunToCompletionWithSSLData(&data, std::move(ssl_provider));
+  TransactionHelperResult out = helper.output();
+  EXPECT_THAT(out.rv, IsOk());
+  EXPECT_EQ("HTTP/1.1 200", out.status_line);
+  EXPECT_EQ("hello!", out.response_data);
+}
+
+TEST_F(SpdyNetworkTransactionTest, ZeroRTTAsyncConfirmSyncWrite) {
+  spdy::SpdySerializedFrame req(spdy_util_.ConstructSpdyPost(
+      kDefaultUrl, 1, kUploadDataSize, LOWEST, nullptr, 0));
+  spdy::SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true));
+  MockWrite writes[] = {
+      CreateMockWrite(req, 0, SYNCHRONOUS),
+      CreateMockWrite(body, 1),  // POST upload frame
+  };
+
+  spdy::SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(nullptr, 0));
+  MockRead reads[] = {
+      CreateMockRead(resp, 2), CreateMockRead(body, 3),
+      MockRead(ASYNC, 0, 4)  // EOF
+  };
+
+  SequencedSocketData data(reads, writes);
+  UsePostRequest();
+  auto session_deps = std::make_unique<SpdySessionDependencies>();
+  session_deps->enable_early_data = true;
+  NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_,
+                                     std::move(session_deps));
+  auto ssl_provider = std::make_unique<SSLSocketDataProvider>(ASYNC, OK);
+  ssl_provider->confirm = MockConfirm(ASYNC, OK);
+  helper.RunToCompletionWithSSLData(&data, std::move(ssl_provider));
+  TransactionHelperResult out = helper.output();
+  EXPECT_THAT(out.rv, IsOk());
+  EXPECT_EQ("HTTP/1.1 200", out.status_line);
+  EXPECT_EQ("hello!", out.response_data);
+}
+
+TEST_F(SpdyNetworkTransactionTest, ZeroRTTAsyncConfirmAsyncWrite) {
+  spdy::SpdySerializedFrame req(spdy_util_.ConstructSpdyPost(
+      kDefaultUrl, 1, kUploadDataSize, LOWEST, nullptr, 0));
+  spdy::SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true));
+  MockWrite writes[] = {
+      CreateMockWrite(req, 0, ASYNC),
+      CreateMockWrite(body, 1),  // POST upload frame
+  };
+
+  spdy::SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(nullptr, 0));
+  MockRead reads[] = {
+      CreateMockRead(resp, 2), CreateMockRead(body, 3),
+      MockRead(ASYNC, 0, 4)  // EOF
+  };
+
+  SequencedSocketData data(reads, writes);
+  UsePostRequest();
+  auto session_deps = std::make_unique<SpdySessionDependencies>();
+  session_deps->enable_early_data = true;
+  NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_,
+                                     std::move(session_deps));
+  auto ssl_provider = std::make_unique<SSLSocketDataProvider>(ASYNC, OK);
+  ssl_provider->confirm = MockConfirm(ASYNC, OK);
+  helper.RunToCompletionWithSSLData(&data, std::move(ssl_provider));
+  TransactionHelperResult out = helper.output();
+  EXPECT_THAT(out.rv, IsOk());
+  EXPECT_EQ("HTTP/1.1 200", out.status_line);
+  EXPECT_EQ("hello!", out.response_data);
+}
+
+TEST_F(SpdyNetworkTransactionTest, ZeroRTTConfirmErrorSync) {
+  spdy::SpdySerializedFrame req(spdy_util_.ConstructSpdyPost(
+      kDefaultUrl, 1, kUploadDataSize, LOWEST, nullptr, 0));
+  spdy::SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true));
+  MockWrite writes[] = {
+      CreateMockWrite(req, 0), CreateMockWrite(body, 1),  // POST upload frame
+  };
+
+  spdy::SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(nullptr, 0));
+  MockRead reads[] = {
+      CreateMockRead(resp, 2), CreateMockRead(body, 3),
+      MockRead(ASYNC, 0, 4)  // EOF
+  };
+
+  SequencedSocketData data(reads, writes);
+  UsePostRequest();
+  auto session_deps = std::make_unique<SpdySessionDependencies>();
+  session_deps->enable_early_data = true;
+  NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_,
+                                     std::move(session_deps));
+  auto ssl_provider = std::make_unique<SSLSocketDataProvider>(ASYNC, OK);
+  ssl_provider->confirm = MockConfirm(SYNCHRONOUS, ERR_SSL_PROTOCOL_ERROR);
+  helper.RunPreTestSetup();
+  helper.AddDataWithSSLSocketDataProvider(&data, std::move(ssl_provider));
+  helper.RunDefaultTest();
+  TransactionHelperResult out = helper.output();
+  EXPECT_THAT(out.rv, IsError(ERR_SSL_PROTOCOL_ERROR));
+}
+
+TEST_F(SpdyNetworkTransactionTest, ZeroRTTConfirmErrorAsync) {
+  spdy::SpdySerializedFrame req(spdy_util_.ConstructSpdyPost(
+      kDefaultUrl, 1, kUploadDataSize, LOWEST, nullptr, 0));
+  spdy::SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true));
+  MockWrite writes[] = {
+      CreateMockWrite(req, 0), CreateMockWrite(body, 1),  // POST upload frame
+  };
+
+  spdy::SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(nullptr, 0));
+  MockRead reads[] = {
+      CreateMockRead(resp, 2), CreateMockRead(body, 3),
+      MockRead(ASYNC, 0, 4)  // EOF
+  };
+
+  SequencedSocketData data(reads, writes);
+  UsePostRequest();
+  auto session_deps = std::make_unique<SpdySessionDependencies>();
+  session_deps->enable_early_data = true;
+  NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_,
+                                     std::move(session_deps));
+  auto ssl_provider = std::make_unique<SSLSocketDataProvider>(ASYNC, OK);
+  ssl_provider->confirm = MockConfirm(ASYNC, ERR_SSL_PROTOCOL_ERROR);
+  helper.RunPreTestSetup();
+  helper.AddDataWithSSLSocketDataProvider(&data, std::move(ssl_provider));
+  helper.RunDefaultTest();
+  TransactionHelperResult out = helper.output();
+  EXPECT_THAT(out.rv, IsError(ERR_SSL_PROTOCOL_ERROR));
+}
+
 }  // namespace net
diff --git a/net/spdy/spdy_proxy_client_socket_unittest.cc b/net/spdy/spdy_proxy_client_socket_unittest.cc
index 1b09efe..329e2217e 100644
--- a/net/spdy/spdy_proxy_client_socket_unittest.cc
+++ b/net/spdy/spdy_proxy_client_socket_unittest.cc
@@ -199,6 +199,7 @@
   ProxyServer proxy_;
   SpdySessionKey endpoint_spdy_session_key_;
   std::unique_ptr<CommonConnectJobParams> common_connect_job_params_;
+  SSLSocketDataProvider ssl_;
 
   DISALLOW_COPY_AND_ASSIGN(SpdyProxyClientSocketTest);
 };
@@ -215,7 +216,8 @@
                                  proxy_,
                                  PRIVACY_MODE_DISABLED,
                                  SpdySessionKey::IsProxySession::kFalse,
-                                 SocketTag()) {
+                                 SocketTag()),
+      ssl_(SYNCHRONOUS, OK) {
   session_deps_.net_log = net_log_.bound().net_log();
 }
 
@@ -241,11 +243,10 @@
   data_->set_connect_data(connect_data_);
   session_deps_.socket_factory->AddSocketDataProvider(data_.get());
 
-  SSLSocketDataProvider ssl(SYNCHRONOUS, OK);
-  ssl.ssl_info.cert =
+  ssl_.ssl_info.cert =
       ImportCertFromFile(GetTestCertsDirectory(), "spdy_pooling.pem");
-  ASSERT_TRUE(ssl.ssl_info.cert);
-  session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl);
+  ASSERT_TRUE(ssl_.ssl_info.cert);
+  session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl_);
 
   session_ = SpdySessionDependencies::SpdyCreateSession(&session_deps_);
   common_connect_job_params_ = std::make_unique<CommonConnectJobParams>(
diff --git a/net/spdy/spdy_session.cc b/net/spdy/spdy_session.cc
index a88cca7..9b6ac6832 100644
--- a/net/spdy/spdy_session.cc
+++ b/net/spdy/spdy_session.cc
@@ -666,6 +666,7 @@
     SpdyStreamType type,
     const base::WeakPtr<SpdySession>& session,
     const GURL& url,
+    bool can_send_early,
     RequestPriority priority,
     const SocketTag& socket_tag,
     const NetLogWithSource& net_log,
@@ -680,18 +681,25 @@
   type_ = type;
   session_ = session;
   url_ = SimplifyUrlForRequest(url);
+  can_send_early_ = can_send_early;
   priority_ = priority;
   socket_tag_ = socket_tag;
   net_log_ = net_log;
   callback_ = std::move(callback);
   traffic_annotation_ = MutableNetworkTrafficAnnotationTag(traffic_annotation);
 
+  next_state_ = STATE_WAIT_FOR_CONFIRMATION;
+  int rv = DoLoop(OK);
+  if (rv != OK)
+    return rv;
+
   base::WeakPtr<SpdyStream> stream;
-  int rv = session->TryCreateStream(weak_ptr_factory_.GetWeakPtr(), &stream);
-  if (rv == OK) {
-    Reset();
-    stream_ = stream;
-  }
+  rv = session->TryCreateStream(weak_ptr_factory_.GetWeakPtr(), &stream);
+  if (rv != OK)
+    return rv;
+
+  Reset();
+  stream_ = stream;
   return rv;
 }
 
@@ -754,11 +762,74 @@
   session_.reset();
   stream_.reset();
   url_ = GURL();
+  can_send_early_ = false;
   priority_ = MINIMUM_PRIORITY;
   socket_tag_ = SocketTag();
   net_log_ = NetLogWithSource();
   callback_.Reset();
   traffic_annotation_.reset();
+  next_state_ = STATE_NONE;
+}
+
+void SpdyStreamRequest::OnIOComplete(int rv) {
+  if (rv != OK) {
+    OnRequestCompleteFailure(rv);
+  } else {
+    DoLoop(rv);
+  }
+}
+
+int SpdyStreamRequest::DoLoop(int rv) {
+  do {
+    State state = next_state_;
+    next_state_ = STATE_NONE;
+    switch (state) {
+      case STATE_WAIT_FOR_CONFIRMATION:
+        CHECK_EQ(OK, rv);
+        return DoWaitForConfirmation();
+        break;
+      case STATE_REQUEST_STREAM:
+        CHECK_EQ(OK, rv);
+        return DoRequestStream(rv);
+        break;
+      default:
+        NOTREACHED() << "next_state_: " << next_state_;
+        break;
+    }
+  } while (next_state_ != STATE_NONE && next_state_ && rv != ERR_IO_PENDING);
+  return rv;
+}
+
+int SpdyStreamRequest::DoWaitForConfirmation() {
+  if (can_send_early_) {
+    next_state_ = STATE_NONE;
+    return OK;
+  }
+
+  int rv = session_->ConfirmHandshake(base::BindOnce(
+      &SpdyStreamRequest::OnIOComplete, weak_ptr_factory_.GetWeakPtr()));
+  // If ConfirmHandshake returned synchronously, exit the state machine early
+  // so StartRequest can call TryCreateStream synchronously. Otherwise,
+  // TryCreateStream will be called asynchronously as part of the confirmation
+  // state machine.
+  next_state_ = rv == ERR_IO_PENDING ? STATE_REQUEST_STREAM : STATE_NONE;
+  return rv;
+}
+
+int SpdyStreamRequest::DoRequestStream(int rv) {
+  DCHECK_NE(ERR_IO_PENDING, rv);
+  next_state_ = STATE_NONE;
+  if (rv < 0)
+    return rv;
+
+  base::WeakPtr<SpdyStream> stream;
+  rv = session_->TryCreateStream(weak_ptr_factory_.GetWeakPtr(), &stream);
+  if (rv == OK) {
+    OnRequestCompleteSuccess(stream);
+  } else if (rv != ERR_IO_PENDING) {
+    OnRequestCompleteFailure(rv);
+  }
+  return rv;
 }
 
 // static
@@ -854,6 +925,7 @@
       error_on_close_(OK),
       initial_settings_(initial_settings),
       greased_http2_frame_(greased_http2_frame),
+      in_confirm_handshake_(false),
       max_concurrent_streams_(kInitialMaxConcurrentStreams),
       max_concurrent_pushed_streams_(
           initial_settings.at(spdy::SETTINGS_MAX_CONCURRENT_STREAMS)),
@@ -915,6 +987,8 @@
   CHECK(!in_io_loop_);
   DcheckDraining();
 
+  DCHECK(waiting_for_confirmation_callbacks_.empty());
+
   // TODO(akalin): Check connection->is_initialized().
   DCHECK(socket_);
   // With SPDY we can't recycle sockets.
@@ -1033,6 +1107,20 @@
                stream->traffic_annotation());
 }
 
+int SpdySession::ConfirmHandshake(CompletionOnceCallback callback) {
+  int rv = ERR_IO_PENDING;
+  if (!in_confirm_handshake_) {
+    rv = socket_->ConfirmHandshake(
+        base::BindOnce(&SpdySession::NotifyRequestsOfConfirmation,
+                       weak_factory_.GetWeakPtr()));
+  }
+  if (rv == ERR_IO_PENDING) {
+    in_confirm_handshake_ = true;
+    waiting_for_confirmation_callbacks_.push_back(std::move(callback));
+  }
+  return rv;
+}
+
 std::unique_ptr<spdy::SpdySerializedFrame> SpdySession::CreateHeaders(
     spdy::SpdyStreamId stream_id,
     RequestPriority priority,
@@ -2395,6 +2483,15 @@
   return OK;
 }
 
+void SpdySession::NotifyRequestsOfConfirmation(int rv) {
+  for (auto& callback : waiting_for_confirmation_callbacks_) {
+    base::ThreadTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE, base::BindOnce(std::move(callback), rv));
+  }
+  waiting_for_confirmation_callbacks_.clear();
+  in_confirm_handshake_ = false;
+}
+
 void SpdySession::SendInitialData() {
   DCHECK(enable_sending_initial_data_);
   DCHECK(buffered_spdy_framer_.get());
@@ -2784,6 +2881,10 @@
   }
   MakeUnavailable();
 
+  // Notify any requests waiting for handshake confirmation that there is an
+  // error.
+  NotifyRequestsOfConfirmation(err);
+
   // Mark host_port_pair requiring HTTP/1.1 for subsequent connections.
   if (err == ERR_HTTP_1_1_REQUIRED) {
     http_server_properties_->SetHTTP11Required(host_port_pair());
diff --git a/net/spdy/spdy_session.h b/net/spdy/spdy_session.h
index 235c85a..827f1b8 100644
--- a/net/spdy/spdy_session.h
+++ b/net/spdy/spdy_session.h
@@ -203,6 +203,9 @@
   // is not created, an error is returned, and ReleaseStream() may not
   // be called.
   //
+  // If |can_send_early| is true, this request is allowed to be sent over
+  // TLS 1.3 0RTT without confirming the handshake.
+  //
   // If OK is returned, must not be called again without
   // ReleaseStream() being called first. If ERR_IO_PENDING is
   // returned, must not be called again without CancelRequest() or
@@ -211,6 +214,7 @@
   int StartRequest(SpdyStreamType type,
                    const base::WeakPtr<SpdySession>& session,
                    const GURL& url,
+                   bool can_send_early,
                    RequestPriority priority,
                    const SocketTag& socket_tag,
                    const NetLogWithSource& net_log,
@@ -241,6 +245,17 @@
  private:
   friend class SpdySession;
 
+  enum State {
+    STATE_NONE,
+    STATE_WAIT_FOR_CONFIRMATION,
+    STATE_REQUEST_STREAM,
+  };
+
+  void OnIOComplete(int rv);
+  int DoLoop(int rv);
+  int DoWaitForConfirmation();
+  int DoRequestStream(int rv);
+
   // Called by |session_| when the stream attempt has finished
   // successfully.
   void OnRequestCompleteSuccess(const base::WeakPtr<SpdyStream>& stream);
@@ -262,11 +277,13 @@
   base::WeakPtr<SpdySession> session_;
   base::WeakPtr<SpdyStream> stream_;
   GURL url_;
+  bool can_send_early_;
   RequestPriority priority_;
   SocketTag socket_tag_;
   NetLogWithSource net_log_;
   CompletionOnceCallback callback_;
   MutableNetworkTrafficAnnotationTag traffic_annotation_;
+  State next_state_;
 
   base::WeakPtrFactory<SpdyStreamRequest> weak_ptr_factory_;
 
@@ -382,6 +399,11 @@
                           spdy::SpdyFrameType frame_type,
                           std::unique_ptr<SpdyBufferProducer> producer);
 
+  // Runs the handshake to completion to confirm the handshake with the server.
+  // If ERR_IO_PENDING is returned, then when the handshake is confirmed,
+  // |callback| will be called.
+  int ConfirmHandshake(CompletionOnceCallback callback);
+
   // Creates and returns a HEADERS frame for |stream_id|.
   std::unique_ptr<spdy::SpdySerializedFrame> CreateHeaders(
       spdy::SpdyStreamId stream_id,
@@ -704,6 +726,8 @@
   int DoWrite();
   int DoWriteComplete(int result);
 
+  void NotifyRequestsOfConfirmation(int rv);
+
   // TODO(akalin): Rename the Send* and Write* functions below to
   // Enqueue*.
 
@@ -1054,6 +1078,13 @@
   // https://tools.ietf.org/html/draft-bishop-httpbis-grease-00.
   const base::Optional<SpdySessionPool::GreasedHttp2Frame> greased_http2_frame_;
 
+  // The callbacks to notify a request that the handshake has been confirmed.
+  std::vector<CompletionOnceCallback> waiting_for_confirmation_callbacks_;
+
+  // True if there is an ongoing handshake confirmation with outstanding
+  // requests.
+  bool in_confirm_handshake_;
+
   // Limits
   size_t max_concurrent_streams_;
   size_t max_concurrent_pushed_streams_;
diff --git a/net/spdy/spdy_session_fuzzer.cc b/net/spdy/spdy_session_fuzzer.cc
index 4b920299..417cd9a 100644
--- a/net/spdy/spdy_session_fuzzer.cc
+++ b/net/spdy/spdy_session_fuzzer.cc
@@ -131,9 +131,9 @@
   net::TestCompletionCallback wait_for_start;
   int rv = stream_request.StartRequest(
       net::SPDY_REQUEST_RESPONSE_STREAM, spdy_session,
-      GURL("http://www.example.invalid/"), net::DEFAULT_PRIORITY,
-      net::SocketTag(), bound_test_net_log.bound(), wait_for_start.callback(),
-      TRAFFIC_ANNOTATION_FOR_TESTS);
+      GURL("http://www.example.invalid/"), false /* no early data */,
+      net::DEFAULT_PRIORITY, net::SocketTag(), bound_test_net_log.bound(),
+      wait_for_start.callback(), TRAFFIC_ANNOTATION_FOR_TESTS);
 
   if (rv == net::ERR_IO_PENDING) {
     rv = wait_for_start.WaitForResult();
diff --git a/net/spdy/spdy_session_unittest.cc b/net/spdy/spdy_session_unittest.cc
index 05dd8a9..4ee2aee 100644
--- a/net/spdy/spdy_session_unittest.cc
+++ b/net/spdy/spdy_session_unittest.cc
@@ -444,7 +444,7 @@
   StreamRequestDestroyingCallback callback1;
   ASSERT_EQ(ERR_IO_PENDING,
             request1.StartRequest(SPDY_BIDIRECTIONAL_STREAM, session_,
-                                  test_url_, MEDIUM, SocketTag(),
+                                  test_url_, false, MEDIUM, SocketTag(),
                                   NetLogWithSource(), callback1.MakeCallback(),
                                   TRAFFIC_ANNOTATION_FOR_TESTS));
 
@@ -452,7 +452,7 @@
   TestCompletionCallback callback2;
   ASSERT_EQ(ERR_IO_PENDING,
             request2->StartRequest(SPDY_BIDIRECTIONAL_STREAM, session_,
-                                   test_url_, MEDIUM, SocketTag(),
+                                   test_url_, false, MEDIUM, SocketTag(),
                                    NetLogWithSource(), callback2.callback(),
                                    TRAFFIC_ANNOTATION_FOR_TESTS));
 
@@ -879,8 +879,8 @@
 
   SpdyStreamRequest stream_request;
   int rv = stream_request.StartRequest(
-      SPDY_REQUEST_RESPONSE_STREAM, session_, test_url_, MEDIUM, SocketTag(),
-      NetLogWithSource(), CompletionOnceCallback(),
+      SPDY_REQUEST_RESPONSE_STREAM, session_, test_url_, false, MEDIUM,
+      SocketTag(), NetLogWithSource(), CompletionOnceCallback(),
       TRAFFIC_ANNOTATION_FOR_TESTS);
   EXPECT_THAT(rv, IsError(ERR_FAILED));
 
@@ -1305,7 +1305,7 @@
   TestCompletionCallback callback4;
   EXPECT_EQ(ERR_IO_PENDING,
             request4.StartRequest(SPDY_REQUEST_RESPONSE_STREAM, session_,
-                                  test_url_, MEDIUM, SocketTag(),
+                                  test_url_, false, MEDIUM, SocketTag(),
                                   NetLogWithSource(), callback4.callback(),
                                   TRAFFIC_ANNOTATION_FOR_TESTS));
 
@@ -1411,9 +1411,10 @@
   // Start request.
   SpdyStreamRequest request;
   TestCompletionCallback callback;
-  int rv = request.StartRequest(
-      SPDY_REQUEST_RESPONSE_STREAM, session_, test_url_, MEDIUM, SocketTag(),
-      NetLogWithSource(), callback.callback(), TRAFFIC_ANNOTATION_FOR_TESTS);
+  int rv =
+      request.StartRequest(SPDY_REQUEST_RESPONSE_STREAM, session_, test_url_,
+                           false, MEDIUM, SocketTag(), NetLogWithSource(),
+                           callback.callback(), TRAFFIC_ANNOTATION_FOR_TESTS);
   EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
 
   // Stream is stalled.
@@ -1479,7 +1480,7 @@
   TestCompletionCallback callback2;
   EXPECT_EQ(ERR_IO_PENDING,
             request2.StartRequest(SPDY_REQUEST_RESPONSE_STREAM, session_,
-                                  test_url_, MEDIUM, SocketTag(),
+                                  test_url_, false, MEDIUM, SocketTag(),
                                   NetLogWithSource(), callback2.callback(),
                                   TRAFFIC_ANNOTATION_FOR_TESTS));
 
@@ -2000,12 +2001,11 @@
   SpdyStreamRequest request;
   ASSERT_EQ(ERR_IO_PENDING,
             request.StartRequest(SPDY_BIDIRECTIONAL_STREAM, session_, test_url_,
-                                 MEDIUM, SocketTag(), NetLogWithSource(),
+                                 false, MEDIUM, SocketTag(), NetLogWithSource(),
                                  stream_releaser.MakeCallback(&request),
                                  TRAFFIC_ANNOTATION_FOR_TESTS));
 
   base::RunLoop().RunUntilIdle();
-
   EXPECT_THAT(stream_releaser.WaitForResult(), IsOk());
 
   data.Resume();
@@ -2056,7 +2056,7 @@
   SpdyStreamRequest request;
   ASSERT_THAT(
       request.StartRequest(SPDY_BIDIRECTIONAL_STREAM, session_, test_url_,
-                           MEDIUM, SocketTag(), NetLogWithSource(),
+                           false, MEDIUM, SocketTag(), NetLogWithSource(),
                            callback->callback(), TRAFFIC_ANNOTATION_FOR_TESTS),
       IsError(ERR_IO_PENDING));
 
@@ -2089,14 +2089,14 @@
   TestCompletionCallback callback1;
   SpdyStreamRequest request1;
   ASSERT_EQ(OK, request1.StartRequest(SPDY_REQUEST_RESPONSE_STREAM, session_,
-                                      test_url_, LOWEST, SocketTag(),
+                                      test_url_, false, LOWEST, SocketTag(),
                                       NetLogWithSource(), callback1.callback(),
                                       TRAFFIC_ANNOTATION_FOR_TESTS));
   TestCompletionCallback callback2;
   SpdyStreamRequest request2;
   ASSERT_EQ(ERR_IO_PENDING,
             request2.StartRequest(SPDY_REQUEST_RESPONSE_STREAM, session_,
-                                  test_url_, LOWEST, SocketTag(),
+                                  test_url_, false, LOWEST, SocketTag(),
                                   NetLogWithSource(), callback2.callback(),
                                   TRAFFIC_ANNOTATION_FOR_TESTS));
 
@@ -2856,7 +2856,7 @@
   SpdyStreamRequest request2;
   ASSERT_EQ(ERR_IO_PENDING,
             request2.StartRequest(SPDY_REQUEST_RESPONSE_STREAM, session_,
-                                  test_url_, LOWEST, SocketTag(),
+                                  test_url_, false, LOWEST, SocketTag(),
                                   NetLogWithSource(), callback2.callback(),
                                   TRAFFIC_ANNOTATION_FOR_TESTS));
 
@@ -2864,7 +2864,7 @@
   SpdyStreamRequest request3;
   ASSERT_EQ(ERR_IO_PENDING,
             request3.StartRequest(SPDY_REQUEST_RESPONSE_STREAM, session_,
-                                  test_url_, LOWEST, SocketTag(),
+                                  test_url_, false, LOWEST, SocketTag(),
                                   NetLogWithSource(), callback3.callback(),
                                   TRAFFIC_ANNOTATION_FOR_TESTS));
 
@@ -2969,7 +2969,7 @@
   SpdyStreamRequest request2;
   ASSERT_EQ(ERR_IO_PENDING,
             request2.StartRequest(SPDY_BIDIRECTIONAL_STREAM, session_,
-                                  test_url_, LOWEST, SocketTag(),
+                                  test_url_, false, LOWEST, SocketTag(),
                                   NetLogWithSource(), callback2.callback(),
                                   TRAFFIC_ANNOTATION_FOR_TESTS));
 
@@ -2977,7 +2977,7 @@
   SpdyStreamRequest request3;
   ASSERT_EQ(ERR_IO_PENDING,
             request3.StartRequest(SPDY_BIDIRECTIONAL_STREAM, session_,
-                                  test_url_, LOWEST, SocketTag(),
+                                  test_url_, false, LOWEST, SocketTag(),
                                   NetLogWithSource(), callback3.callback(),
                                   TRAFFIC_ANNOTATION_FOR_TESTS));
 
diff --git a/net/spdy/spdy_test_util_common.cc b/net/spdy/spdy_test_util_common.cc
index 0a2d5007..988d82b 100644
--- a/net/spdy/spdy_test_util_common.cc
+++ b/net/spdy/spdy_test_util_common.cc
@@ -251,8 +251,9 @@
     const NetLogWithSource& net_log) {
   SpdyStreamRequest stream_request;
   int rv = stream_request.StartRequest(
-      type, session, url, priority, SocketTag(), net_log,
-      CompletionOnceCallback(), TRAFFIC_ANNOTATION_FOR_TESTS);
+      type, session, url, false /* no early data */, priority, SocketTag(),
+      net_log, CompletionOnceCallback(), TRAFFIC_ANNOTATION_FOR_TESTS);
+
   return
       (rv == OK) ? stream_request.ReleaseStream() : base::WeakPtr<SpdyStream>();
 }
@@ -342,7 +343,8 @@
       enable_websocket_over_http2(false),
       net_log(nullptr),
       http_09_on_non_default_ports_enabled(false),
-      disable_idle_sockets_close_on_memory_pressure(false) {
+      disable_idle_sockets_close_on_memory_pressure(false),
+      enable_early_data(false) {
   http2_settings[spdy::SETTINGS_INITIAL_WINDOW_SIZE] =
       kDefaultInitialWindowSize;
 }
@@ -395,6 +397,7 @@
       session_deps->http_09_on_non_default_ports_enabled;
   params.disable_idle_sockets_close_on_memory_pressure =
       session_deps->disable_idle_sockets_close_on_memory_pressure;
+  params.enable_early_data = session_deps->enable_early_data;
   return params;
 }
 
diff --git a/net/spdy/spdy_test_util_common.h b/net/spdy/spdy_test_util_common.h
index def43e7..e0423d25 100644
--- a/net/spdy/spdy_test_util_common.h
+++ b/net/spdy/spdy_test_util_common.h
@@ -235,6 +235,7 @@
   NetLog* net_log;
   bool http_09_on_non_default_ports_enabled;
   bool disable_idle_sockets_close_on_memory_pressure;
+  bool enable_early_data;
 };
 
 class SpdyURLRequestContext : public URLRequestContext {
diff --git a/net/url_request/url_request_unittest.cc b/net/url_request/url_request_unittest.cc
index a2cde27e..788259f 100644
--- a/net/url_request/url_request_unittest.cc
+++ b/net/url_request/url_request_unittest.cc
@@ -12584,4 +12584,400 @@
 }
 #endif
 
+// Provides a response to the 0RTT request indicating whether it was received
+// as early data, sending HTTP_TOO_EARLY if enabled.
+class ZeroRTTResponse : public test_server::BasicHttpResponse {
+ public:
+  ZeroRTTResponse(bool zero_rtt, bool send_too_early)
+      : zero_rtt_(zero_rtt), send_too_early_(send_too_early) {}
+  ~ZeroRTTResponse() override {}
+
+  void SendResponse(const test_server::SendBytesCallback& send,
+                    const test_server::SendCompleteCallback& done) override {
+    AddCustomHeader("Vary", "Early-Data");
+    set_content_type("text/plain");
+    AddCustomHeader("Cache-Control", "no-cache");
+    if (zero_rtt_) {
+      if (send_too_early_)
+        set_code(HTTP_TOO_EARLY);
+      set_content("1");
+    } else {
+      set_content("0");
+    }
+
+    // Since the EmbeddedTestServer doesn't keep the socket open by default,
+    // it is explicitly kept alive to allow the remaining leg of the 0RTT
+    // handshake to be received after the early data.
+    send.Run(ToResponseString(), base::DoNothing());
+  }
+
+ private:
+  bool zero_rtt_;
+  bool send_too_early_;
+
+  DISALLOW_COPY_AND_ASSIGN(ZeroRTTResponse);
+};
+
+std::unique_ptr<test_server::HttpResponse> HandleZeroRTTRequest(
+    const test_server::HttpRequest& request) {
+  if (request.GetURL().path() != "/zerortt")
+    return nullptr;
+  auto iter = request.headers.find("Early-Data");
+  bool zero_rtt = iter != request.headers.end() && iter->second == "1";
+  return std::make_unique<ZeroRTTResponse>(zero_rtt, false);
+}
+
+class HTTPSEarlyDataTest : public TestWithScopedTaskEnvironment {
+ public:
+  HTTPSEarlyDataTest()
+      : context_(true), test_server_(net::EmbeddedTestServer::TYPE_HTTPS) {
+    auto params = std::make_unique<HttpNetworkSession::Params>();
+    params->enable_early_data = true;
+    context_.set_http_network_session_params(std::move(params));
+
+    context_.set_network_delegate(&network_delegate_);
+    cert_verifier_.set_default_result(OK);
+    context_.set_cert_verifier(&cert_verifier_);
+
+    ssl_config_service_ = std::make_unique<TestSSLConfigService>();
+    ssl_config_service_->set_max_version(SSL_PROTOCOL_VERSION_TLS1_3);
+    context_.set_ssl_config_service(ssl_config_service_.get());
+
+    context_.Init();
+
+    ssl_config_.version_max = SSL_PROTOCOL_VERSION_TLS1_3;
+    ssl_config_.early_data_enabled = true;
+    test_server_.SetSSLConfig(net::EmbeddedTestServer::CERT_OK, ssl_config_);
+    test_server_.AddDefaultHandlers(
+        base::FilePath(FILE_PATH_LITERAL("net/data/ssl")));
+    test_server_.RegisterRequestHandler(
+        base::BindRepeating(&HandleZeroRTTRequest));
+  }
+
+  ~HTTPSEarlyDataTest() override = default;
+
+  void ResetSSLConfig(net::EmbeddedTestServer::ServerCertificate cert,
+                      uint16_t version) {
+    ssl_config_.version_max = version;
+    test_server_.ResetSSLConfig(cert, ssl_config_);
+  }
+
+ protected:
+  MockCertVerifier cert_verifier_;
+  TestNetworkDelegate network_delegate_;  // Must outlive URLRequest.
+  std::unique_ptr<TestSSLConfigService> ssl_config_service_;
+  TestURLRequestContext context_;
+
+  SSLServerConfig ssl_config_;
+  EmbeddedTestServer test_server_;
+};
+
+// TLSEarlyDataTest tests that we handle early data correctly.
+TEST_F(HTTPSEarlyDataTest, TLSEarlyDataTest) {
+  ASSERT_TRUE(test_server_.Start());
+  context_.http_transaction_factory()->GetSession()->ClearSSLSessionCache();
+
+  {
+    TestDelegate d;
+    std::unique_ptr<URLRequest> r(context_.CreateRequest(
+        test_server_.GetURL("/zerortt"), DEFAULT_PRIORITY, &d,
+        TRAFFIC_ANNOTATION_FOR_TESTS));
+    r->Start();
+    EXPECT_TRUE(r->is_pending());
+
+    base::RunLoop().Run();
+
+    EXPECT_EQ(1, d.response_started_count());
+
+    EXPECT_EQ(SSL_CONNECTION_VERSION_TLS1_3,
+              SSLConnectionStatusToVersion(r->ssl_info().connection_status));
+    EXPECT_TRUE(r->ssl_info().unverified_cert.get());
+    EXPECT_TRUE(test_server_.GetCertificate()->EqualsIncludingChain(
+        r->ssl_info().cert.get()));
+
+    // The Early-Data header should be omitted in the initial request, and the
+    // handler should return "0".
+    EXPECT_EQ("0", d.data_received());
+  }
+
+  context_.http_transaction_factory()->GetSession()->CloseAllConnections();
+
+  {
+    TestDelegate d;
+    std::unique_ptr<URLRequest> r(context_.CreateRequest(
+        test_server_.GetURL("/zerortt"), DEFAULT_PRIORITY, &d,
+        TRAFFIC_ANNOTATION_FOR_TESTS));
+
+    r->Start();
+    EXPECT_TRUE(r->is_pending());
+
+    base::RunLoop().Run();
+
+    EXPECT_EQ(1, d.response_started_count());
+
+    EXPECT_EQ(SSL_CONNECTION_VERSION_TLS1_3,
+              SSLConnectionStatusToVersion(r->ssl_info().connection_status));
+    EXPECT_TRUE(r->ssl_info().unverified_cert.get());
+    EXPECT_TRUE(test_server_.GetCertificate()->EqualsIncludingChain(
+        r->ssl_info().cert.get()));
+
+    // The Early-Data header should be a single '1' in the resumed request, and
+    // the handler should return "1".
+    EXPECT_EQ("1", d.data_received());
+  }
+}
+
+// TLSEarlyDataTest tests that we handle early data correctly for POST.
+TEST_F(HTTPSEarlyDataTest, TLSEarlyDataPOSTTest) {
+  ASSERT_TRUE(test_server_.Start());
+  context_.http_transaction_factory()->GetSession()->ClearSSLSessionCache();
+
+  {
+    TestDelegate d;
+    std::unique_ptr<URLRequest> r(context_.CreateRequest(
+        test_server_.GetURL("/zerortt"), DEFAULT_PRIORITY, &d,
+        TRAFFIC_ANNOTATION_FOR_TESTS));
+    r->Start();
+    EXPECT_TRUE(r->is_pending());
+
+    base::RunLoop().Run();
+
+    EXPECT_EQ(1, d.response_started_count());
+
+    EXPECT_EQ(SSL_CONNECTION_VERSION_TLS1_3,
+              SSLConnectionStatusToVersion(r->ssl_info().connection_status));
+    EXPECT_TRUE(r->ssl_info().unverified_cert.get());
+    EXPECT_TRUE(test_server_.GetCertificate()->EqualsIncludingChain(
+        r->ssl_info().cert.get()));
+
+    // The Early-Data header should be omitted in the initial request, and the
+    // handler should return "0".
+    EXPECT_EQ("0", d.data_received());
+  }
+
+  context_.http_transaction_factory()->GetSession()->CloseAllConnections();
+
+  {
+    TestDelegate d;
+    std::unique_ptr<URLRequest> r(context_.CreateRequest(
+        test_server_.GetURL("/zerortt"), DEFAULT_PRIORITY, &d,
+        TRAFFIC_ANNOTATION_FOR_TESTS));
+    r->set_method("POST");
+    r->Start();
+    EXPECT_TRUE(r->is_pending());
+
+    base::RunLoop().Run();
+
+    EXPECT_EQ(1, d.response_started_count());
+
+    EXPECT_EQ(SSL_CONNECTION_VERSION_TLS1_3,
+              SSLConnectionStatusToVersion(r->ssl_info().connection_status));
+    EXPECT_TRUE(r->ssl_info().unverified_cert.get());
+    EXPECT_TRUE(test_server_.GetCertificate()->EqualsIncludingChain(
+        r->ssl_info().cert.get()));
+
+    // The Early-Data header should be omitted in the request, since we don't
+    // send POSTs over early data, and the handler should return "0".
+    EXPECT_EQ("0", d.data_received());
+  }
+}
+
+std::unique_ptr<test_server::HttpResponse> HandleTooEarly(
+    bool* sent_425,
+    const test_server::HttpRequest& request) {
+  if (request.GetURL().path() != "/tooearly")
+    return nullptr;
+  auto iter = request.headers.find("Early-Data");
+  bool zero_rtt = iter != request.headers.end() && iter->second == "1";
+  if (zero_rtt)
+    *sent_425 = true;
+  return std::make_unique<ZeroRTTResponse>(zero_rtt, true);
+}
+
+// Test that we handle 425 (Too Early) correctly.
+TEST_F(HTTPSEarlyDataTest, TLSEarlyDataTooEarlyTest) {
+  bool sent_425 = false;
+  test_server_.RegisterRequestHandler(
+      base::BindRepeating(&HandleTooEarly, base::Unretained(&sent_425)));
+  ASSERT_TRUE(test_server_.Start());
+  context_.http_transaction_factory()->GetSession()->ClearSSLSessionCache();
+
+  {
+    TestDelegate d;
+    std::unique_ptr<URLRequest> r(context_.CreateRequest(
+        test_server_.GetURL("/tooearly"), DEFAULT_PRIORITY, &d,
+        TRAFFIC_ANNOTATION_FOR_TESTS));
+    r->Start();
+    EXPECT_TRUE(r->is_pending());
+
+    d.RunUntilComplete();
+
+    EXPECT_EQ(1, d.response_started_count());
+
+    EXPECT_EQ(SSL_CONNECTION_VERSION_TLS1_3,
+              SSLConnectionStatusToVersion(r->ssl_info().connection_status));
+    EXPECT_TRUE(r->ssl_info().unverified_cert.get());
+    EXPECT_TRUE(test_server_.GetCertificate()->EqualsIncludingChain(
+        r->ssl_info().cert.get()));
+
+    // The Early-Data header should be omitted in the initial request, and the
+    // handler should return "0".
+    EXPECT_EQ("0", d.data_received());
+    EXPECT_FALSE(sent_425);
+  }
+
+  context_.http_transaction_factory()->GetSession()->CloseAllConnections();
+
+  {
+    TestDelegate d;
+    std::unique_ptr<URLRequest> r(context_.CreateRequest(
+        test_server_.GetURL("/tooearly"), DEFAULT_PRIORITY, &d,
+        TRAFFIC_ANNOTATION_FOR_TESTS));
+
+    r->Start();
+    EXPECT_TRUE(r->is_pending());
+
+    d.RunUntilComplete();
+
+    EXPECT_EQ(1, d.response_started_count());
+
+    EXPECT_EQ(SSL_CONNECTION_VERSION_TLS1_3,
+              SSLConnectionStatusToVersion(r->ssl_info().connection_status));
+    EXPECT_TRUE(r->ssl_info().unverified_cert.get());
+    EXPECT_TRUE(test_server_.GetCertificate()->EqualsIncludingChain(
+        r->ssl_info().cert.get()));
+
+    // The resumption request will encounter a 425 error and retry without early
+    // data, and the handler should return "0".
+    EXPECT_EQ("0", d.data_received());
+    EXPECT_TRUE(sent_425);
+  }
+}
+
+// TLSEarlyDataRejectTest tests that we gracefully handle an early data reject
+// and retry without early data.
+TEST_F(HTTPSEarlyDataTest, TLSEarlyDataRejectTest) {
+  ASSERT_TRUE(test_server_.Start());
+  context_.http_transaction_factory()->GetSession()->ClearSSLSessionCache();
+
+  {
+    TestDelegate d;
+    std::unique_ptr<URLRequest> r(context_.CreateRequest(
+        test_server_.GetURL("/zerortt"), DEFAULT_PRIORITY, &d,
+        TRAFFIC_ANNOTATION_FOR_TESTS));
+
+    r->Start();
+    EXPECT_TRUE(r->is_pending());
+
+    d.RunUntilComplete();
+
+    EXPECT_EQ(SSL_CONNECTION_VERSION_TLS1_3,
+              SSLConnectionStatusToVersion(r->ssl_info().connection_status));
+    EXPECT_TRUE(r->ssl_info().unverified_cert.get());
+    EXPECT_TRUE(test_server_.GetCertificate()->EqualsIncludingChain(
+        r->ssl_info().cert.get()));
+
+    // The Early-Data header should be omitted in the initial request, and the
+    // handler should return "0".
+    EXPECT_EQ("0", d.data_received());
+  }
+
+  context_.http_transaction_factory()->GetSession()->CloseAllConnections();
+
+  // The certificate in the resumption is changed to confirm that the
+  // certificate change is observed.
+  scoped_refptr<X509Certificate> old_cert = test_server_.GetCertificate();
+  ResetSSLConfig(net::EmbeddedTestServer::CERT_EXPIRED,
+                 SSL_PROTOCOL_VERSION_TLS1_3);
+
+  {
+    TestDelegate d;
+    std::unique_ptr<URLRequest> r(context_.CreateRequest(
+        test_server_.GetURL("/zerortt"), DEFAULT_PRIORITY, &d,
+        TRAFFIC_ANNOTATION_FOR_TESTS));
+
+    r->Start();
+    EXPECT_TRUE(r->is_pending());
+
+    d.RunUntilComplete();
+
+    EXPECT_EQ(1, d.response_started_count());
+
+    EXPECT_EQ(SSL_CONNECTION_VERSION_TLS1_3,
+              SSLConnectionStatusToVersion(r->ssl_info().connection_status));
+    EXPECT_TRUE(r->ssl_info().unverified_cert.get());
+    EXPECT_TRUE(test_server_.GetCertificate()->EqualsIncludingChain(
+        r->ssl_info().cert.get()));
+    EXPECT_FALSE(old_cert->EqualsIncludingChain(r->ssl_info().cert.get()));
+
+    // The Early-Data header should be omitted in the rejected request, and the
+    // handler should return "0".
+    EXPECT_EQ("0", d.data_received());
+  }
+}
+
+// TLSEarlyDataTLS12RejectTest tests that we gracefully handle an early data
+// reject from a TLS 1.2 server and retry without early data.
+TEST_F(HTTPSEarlyDataTest, TLSEarlyDataTLS12RejectTest) {
+  ASSERT_TRUE(test_server_.Start());
+  context_.http_transaction_factory()->GetSession()->ClearSSLSessionCache();
+
+  {
+    TestDelegate d;
+    std::unique_ptr<URLRequest> r(context_.CreateRequest(
+        test_server_.GetURL("/zerortt"), DEFAULT_PRIORITY, &d,
+        TRAFFIC_ANNOTATION_FOR_TESTS));
+
+    r->Start();
+    EXPECT_TRUE(r->is_pending());
+
+    d.RunUntilComplete();
+
+    EXPECT_EQ(1, d.response_started_count());
+
+    EXPECT_EQ(SSL_CONNECTION_VERSION_TLS1_3,
+              SSLConnectionStatusToVersion(r->ssl_info().connection_status));
+    EXPECT_TRUE(r->ssl_info().unverified_cert.get());
+    EXPECT_TRUE(test_server_.GetCertificate()->EqualsIncludingChain(
+        r->ssl_info().cert.get()));
+
+    // The Early-Data header should be omitted in the initial request, and the
+    // handler should return "0".
+    EXPECT_EQ("0", d.data_received());
+  }
+
+  context_.http_transaction_factory()->GetSession()->CloseAllConnections();
+
+  // The certificate in the resumption is changed to confirm that the
+  // certificate change is observed.
+  scoped_refptr<X509Certificate> old_cert = test_server_.GetCertificate();
+  ResetSSLConfig(net::EmbeddedTestServer::CERT_EXPIRED,
+                 SSL_PROTOCOL_VERSION_TLS1_2);
+
+  {
+    TestDelegate d;
+    std::unique_ptr<URLRequest> r(context_.CreateRequest(
+        test_server_.GetURL("/zerortt"), DEFAULT_PRIORITY, &d,
+        TRAFFIC_ANNOTATION_FOR_TESTS));
+
+    r->Start();
+    EXPECT_TRUE(r->is_pending());
+
+    d.RunUntilComplete();
+
+    EXPECT_EQ(1, d.response_started_count());
+
+    EXPECT_EQ(SSL_CONNECTION_VERSION_TLS1_2,
+              SSLConnectionStatusToVersion(r->ssl_info().connection_status));
+    EXPECT_TRUE(r->ssl_info().unverified_cert.get());
+    EXPECT_TRUE(test_server_.GetCertificate()->EqualsIncludingChain(
+        r->ssl_info().cert.get()));
+    EXPECT_FALSE(old_cert->EqualsIncludingChain(r->ssl_info().cert.get()));
+
+    // The Early-Data header should be omitted in the rejected request, and the
+    // handler should return "0".
+    EXPECT_EQ("0", d.data_received());
+  }
+}
+
 }  // namespace net
diff --git a/net/websockets/websocket_basic_handshake_stream.cc b/net/websockets/websocket_basic_handshake_stream.cc
index 9ec81a6..65f63e0 100644
--- a/net/websockets/websocket_basic_handshake_stream.cc
+++ b/net/websockets/websocket_basic_handshake_stream.cc
@@ -203,7 +203,17 @@
     CompletionOnceCallback callback) {
   DCHECK(request_info->traffic_annotation.is_valid());
   url_ = request_info->url;
-  state_.Initialize(request_info, can_send_early, priority, net_log);
+  // The WebSocket may receive a socket in the early data state from
+  // HttpNetworkTransaction, which means it must call ConfirmHandshake() for
+  // requests that need replay protection. However, the first request on any
+  // WebSocket stream is a GET with an idempotent request
+  // (https://tools.ietf.org/html/rfc6455#section-1.3), so there is no need to
+  // call ConfirmHandshake().
+  //
+  // Data after the WebSockets handshake may not be replayable, but the
+  // handshake is guaranteed to be confirmed once the HTTP response is received.
+  DCHECK(can_send_early);
+  state_.Initialize(request_info, priority, net_log);
   return OK;
 }
 
diff --git a/net/websockets/websocket_http2_handshake_stream.cc b/net/websockets/websocket_http2_handshake_stream.cc
index 343b4cd..7e38991 100644
--- a/net/websockets/websocket_http2_handshake_stream.cc
+++ b/net/websockets/websocket_http2_handshake_stream.cc
@@ -121,8 +121,10 @@
 
   callback_ = std::move(callback);
   spdy_stream_request_ = std::make_unique<SpdyStreamRequest>();
+  // The initial request for the WebSocket is a CONNECT, so there is no need to
+  // call ConfirmHandshake().
   int rv = spdy_stream_request_->StartRequest(
-      SPDY_BIDIRECTIONAL_STREAM, session_, request_info_->url, priority_,
+      SPDY_BIDIRECTIONAL_STREAM, session_, request_info_->url, true, priority_,
       request_info_->socket_tag, net_log_,
       base::BindOnce(&WebSocketHttp2HandshakeStream::StartRequestCallback,
                      base::Unretained(this)),
diff --git a/printing/backend/printing_restrictions.cc b/printing/backend/printing_restrictions.cc
index 038558e8..c094188 100644
--- a/printing/backend/printing_restrictions.cc
+++ b/printing/backend/printing_restrictions.cc
@@ -33,7 +33,7 @@
 base::Optional<ColorModeRestriction> GetAllowedColorModesForName(
     const std::string& mode_name) {
   if (mode_name == "any")
-    return ColorModeRestriction::kNone;
+    return ColorModeRestriction::kUnset;
 
   return GetColorModeForName(mode_name);
 }
@@ -55,7 +55,7 @@
 base::Optional<DuplexModeRestriction> GetAllowedDuplexModesForName(
     const std::string& mode_name) {
   if (mode_name == "any")
-    return DuplexModeRestriction::kNone;
+    return DuplexModeRestriction::kUnset;
 
   if (mode_name == "simplex")
     return DuplexModeRestriction::kSimplex;
@@ -80,7 +80,7 @@
 base::Optional<PinModeRestriction> GetAllowedPinModesForName(
     const std::string& mode_name) {
   if (mode_name == "any")
-    return PinModeRestriction::kNone;
+    return PinModeRestriction::kUnset;
 
   return GetPinModeForName(mode_name);
 }
diff --git a/printing/backend/printing_restrictions.h b/printing/backend/printing_restrictions.h
index 16bb0236..e05692e 100644
--- a/printing/backend/printing_restrictions.h
+++ b/printing/backend/printing_restrictions.h
@@ -16,7 +16,7 @@
 // Allowed printing modes as a bitmask.
 // This is used in pref file and should never change.
 enum class ColorModeRestriction {
-  kNone = 0x0,
+  kUnset = 0x0,
   kMonochrome = 0x1,
   kColor = 0x2,
 };
@@ -24,7 +24,7 @@
 // Allowed duplex modes as a bitmask.
 // This is used in pref file and should never change.
 enum class DuplexModeRestriction {
-  kNone = 0x0,
+  kUnset = 0x0,
   kSimplex = 0x1,
   kLongEdge = 0x2,
   kShortEdge = 0x4,
@@ -34,9 +34,9 @@
 // Allowed PIN printing modes.
 // This is used in pref file and should never change.
 enum class PinModeRestriction {
-  kNone,
-  kPin,
-  kNoPin,
+  kUnset = 0,
+  kPin = 1,
+  kNoPin = 2,
 };
 
 struct PRINTING_EXPORT PrintingRestrictions {
diff --git a/services/audio/owning_audio_manager_accessor.h b/services/audio/owning_audio_manager_accessor.h
index 11abdf4d..03f2c94c 100644
--- a/services/audio/owning_audio_manager_accessor.h
+++ b/services/audio/owning_audio_manager_accessor.h
@@ -45,7 +45,8 @@
  private:
 #if defined(OS_WIN)
   // Required to access CoreAudio.
-  base::win::ScopedCOMInitializer com_initializer_;
+  base::win::ScopedCOMInitializer com_initializer_{
+      base::win::ScopedCOMInitializer::kMTA};
 #endif
   AudioManagerFactoryCallback audio_manager_factory_cb_;
   std::unique_ptr<media::AudioManager> audio_manager_;
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 03005c4..8d4a5bc 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -4853,6 +4853,28 @@
             ]
         }
     ],
+    "SyncButterWallet": [
+        {
+            "platforms": [
+                "linux",
+                "mac",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled_DownstreamBehindButton",
+                    "enable_features": [
+                        "AutofillEnableAccountWalletStorage",
+                        "AutofillSaveCardImprovedUserConsent",
+                        "SyncSupportSecondaryAccount"
+                    ],
+                    "disable_features": [
+                        "AlwaysShowServerCardsInSyncTransport"
+                    ]
+                }
+            ]
+        }
+    ],
     "SyncE2ELatency": [
         {
             "platforms": [
@@ -5293,27 +5315,6 @@
             ]
         }
     ],
-    "UseGoogleLocalNtp": [
-        {
-            "platforms": [
-                "chromeos",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "UseGoogleLocalNtp",
-                    "enable_features": [
-                        "DoodlesOnLocalNtp",
-                        "PromosOnLocalNtp",
-                        "SearchSuggestionsOnLocalNtp",
-                        "UseGoogleLocalNtp"
-                    ]
-                }
-            ]
-        }
-    ],
     "UsePdfCompositorServiceForPrint": [
         {
             "platforms": [
@@ -5801,6 +5802,21 @@
             ]
         }
     ],
+    "WebRTC-Audio-NetEqDelayHistogram": [
+        {
+            "platforms": [
+                "windows",
+                "mac",
+                "chromeos",
+                "linux"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled-95-0.9993"
+                }
+            ]
+        }
+    ],
     "WebRTC-Audio-NetEqForceTargetDelayPercentile": [
         {
             "platforms": [
diff --git a/third_party/blink/common/features.cc b/third_party/blink/common/features.cc
index 4c520a0..22de4a7 100644
--- a/third_party/blink/common/features.cc
+++ b/third_party/blink/common/features.cc
@@ -177,6 +177,11 @@
 #endif
 };
 
+// Freeze scheduler task queues in background on network idle.
+// This feature only works if stop-in-background is enabled.
+const base::Feature kFreezeBackgroundTabOnNetworkIdle{
+    "freeze-background-tab-on-network-idle", base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Freeze non-timer task queues in background, after allowed grace time.
 // "stop" is a legacy name.
 const base::Feature kStopNonTimersInBackground {
diff --git a/third_party/blink/public/common/features.h b/third_party/blink/public/common/features.h
index 2ef4d5d..4b675ff 100644
--- a/third_party/blink/public/common/features.h
+++ b/third_party/blink/public/common/features.h
@@ -54,6 +54,8 @@
     kServiceWorkerAggressiveCodeCache;
 BLINK_COMMON_EXPORT extern const base::Feature kServiceWorkerUpdateDelay;
 BLINK_COMMON_EXPORT extern const base::Feature kStopInBackground;
+BLINK_COMMON_EXPORT extern const base::Feature
+    kFreezeBackgroundTabOnNetworkIdle;
 BLINK_COMMON_EXPORT extern const base::Feature kStopNonTimersInBackground;
 BLINK_COMMON_EXPORT extern const base::Feature kTextFragmentAnchor;
 BLINK_COMMON_EXPORT extern const base::Feature kWasmCodeCache;
diff --git a/third_party/blink/public/mojom/web_feature/web_feature.mojom b/third_party/blink/public/mojom/web_feature/web_feature.mojom
index 8e15ca6..f13b016 100644
--- a/third_party/blink/public/mojom/web_feature/web_feature.mojom
+++ b/third_party/blink/public/mojom/web_feature/web_feature.mojom
@@ -2255,8 +2255,6 @@
   kHTMLImportsOnReverseOriginTrials = 2847,
   kElementCreateShadowRootOnReverseOriginTrials = 2848,
   kDocumentRegisterElementOnReverseOriginTrials = 2849,
-  kV8Animation_Effect_AttributeGetter = 2850,
-  kV8Animation_Effect_AttributeSetter = 2851,
 
   // Add new features immediately above this line. Don't change assigned
   // numbers of any item, and don't reuse removed slots.
diff --git a/third_party/blink/public/platform/web_url_request.h b/third_party/blink/public/platform/web_url_request.h
index 0572432..02633c7 100644
--- a/third_party/blink/public/platform/web_url_request.h
+++ b/third_party/blink/public/platform/web_url_request.h
@@ -212,7 +212,7 @@
   BLINK_PLATFORM_EXPORT base::TimeDelta TimeoutInterval() const;
 
   BLINK_PLATFORM_EXPORT WebString HttpMethod() const;
-  BLINK_PLATFORM_EXPORT void SetHTTPMethod(const WebString&);
+  BLINK_PLATFORM_EXPORT void SetHttpMethod(const WebString&);
 
   BLINK_PLATFORM_EXPORT WebString HttpHeaderField(const WebString& name) const;
   // It's not possible to set the referrer header using this method. Use
diff --git a/third_party/blink/renderer/build/scripts/templates/element_factory.cc.tmpl b/third_party/blink/renderer/build/scripts/templates/element_factory.cc.tmpl
index 46dc718..40f1f25a 100644
--- a/third_party/blink/renderer/build/scripts/templates/element_factory.cc.tmpl
+++ b/third_party/blink/renderer/build/scripts/templates/element_factory.cc.tmpl
@@ -18,18 +18,16 @@
 
 namespace blink {
 
-typedef {{namespace}}Element* (*{{namespace}}ConstructorFunction)(
-    Document&,
-    const CreateElementFlags);
+using {{namespace}}ConstructorFunction = {{namespace}}Element* (*)(
+    Document&, const CreateElementFlags);
 
-typedef HashMap<AtomicString, {{namespace}}ConstructorFunction> {{namespace}}FunctionMap;
+using {{namespace}}FunctionMap = HashMap<AtomicString, {{namespace}}ConstructorFunction>;
 
-static {{namespace}}FunctionMap* g_{{namespace}}_constructors = 0;
+static {{namespace}}FunctionMap* g_{{namespace|lower}}_constructors = nullptr;
 
 {% for tag in tags|sort if not tag.noConstructor %}
-static {{namespace}}Element* {{namespace}}{{tag|symbol}}Constructor(
-    Document& document,
-    const CreateElementFlags flags) {
+static {{namespace}}Element* {{namespace}}{{tag.name.to_upper_camel_case()}}Constructor(
+    Document& document, const CreateElementFlags flags) {
   {% if tag.runtimeEnabled %}
   if (!RuntimeEnabledFeatures::{{tag.runtimeEnabled}}Enabled())
     return {{fallback_interface}}::Create({{cpp_namespace}}::{{tag|symbol}}Tag, document);
@@ -47,27 +45,27 @@
   {{namespace}}ConstructorFunction func;
 };
 
-static void create{{namespace}}FunctionMap() {
-  DCHECK(!g_{{namespace}}_constructors);
-  g_{{namespace}}_constructors = new {{namespace}}FunctionMap;
+static void Create{{namespace}}FunctionMap() {
+  DCHECK(!g_{{namespace|lower}}_constructors);
+  g_{{namespace|lower}}_constructors = new {{namespace}}FunctionMap;
   // Empty array initializer lists are illegal [dcl.init.aggr] and will not
   // compile in MSVC. If tags list is empty, add check to skip this.
   static const Create{{namespace}}FunctionMapData data[] = {
   {% for tag in tags|sort if not tag.noConstructor %}
-    { {{cpp_namespace}}::{{tag|symbol}}Tag, {{namespace}}{{tag|symbol}}Constructor },
+    { {{cpp_namespace}}::{{tag|symbol}}Tag, {{namespace}}{{tag.name.to_upper_camel_case()}}Constructor },
   {% endfor %}
   };
   for (size_t i = 0; i < base::size(data); i++)
-    g_{{namespace}}_constructors->Set(data[i].tag.LocalName(), data[i].func);
+    g_{{namespace|lower}}_constructors->Set(data[i].tag.LocalName(), data[i].func);
 }
 
 {{namespace}}Element* {{namespace}}ElementFactory::Create(
-    const AtomicString& localName,
+    const AtomicString& local_name,
     Document& document,
     const CreateElementFlags flags) {
-  if (!g_{{namespace}}_constructors)
-    create{{namespace}}FunctionMap();
-  if ({{namespace}}ConstructorFunction function = g_{{namespace}}_constructors->at(localName))
+  if (!g_{{namespace|lower}}_constructors)
+    Create{{namespace}}FunctionMap();
+  if ({{namespace}}ConstructorFunction function = g_{{namespace|lower}}_constructors->at(local_name))
     return function(document, flags);
   return nullptr;
 }
diff --git a/third_party/blink/renderer/build/scripts/templates/element_factory.h.tmpl b/third_party/blink/renderer/build/scripts/templates/element_factory.h.tmpl
index 87ae48e8..3c1c9c87 100644
--- a/third_party/blink/renderer/build/scripts/templates/element_factory.h.tmpl
+++ b/third_party/blink/renderer/build/scripts/templates/element_factory.h.tmpl
@@ -16,9 +16,9 @@
 
 class {{namespace}}ElementFactory {
  public:
-  // If |localName| is unknown, nullptr is returned.
+  // If |local_name| is unknown, nullptr is returned.
   static {{namespace}}Element* Create(
-      const AtomicString& localName,
+      const AtomicString& local_name,
       Document&,
       const CreateElementFlags flags);
 };
diff --git a/third_party/blink/renderer/core/animation/animation.idl b/third_party/blink/renderer/core/animation/animation.idl
index 7b78ea2..a096e1d 100644
--- a/third_party/blink/renderer/core/animation/animation.idl
+++ b/third_party/blink/renderer/core/animation/animation.idl
@@ -28,19 +28,19 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-// https://drafts.csswg.org/web-animations/#the-animation-interface
+// https://drafts.csswg.org/web-animations/#animation
 
 enum AnimationPlayState { "idle", "pending", "running", "paused", "finished" };
 
 [
-    Exposed=Window,
     Constructor(optional AnimationEffect? effect = null, optional AnimationTimeline? timeline),
+    Exposed(Window WebAnimationsAPI),
     ConstructorCallWith=ExecutionContext,
     RaisesException=Constructor,
     ActiveScriptWrappable
 ] interface Animation : EventTarget {
-    [Measure] attribute AnimationEffect? effect;
     // TODO(suzyh): Make timeline mutable.
+    [RuntimeEnabled=WebAnimationsAPI] attribute AnimationEffect? effect;
     [RuntimeEnabled=WebAnimationsAPI] readonly attribute AnimationTimeline? timeline;
     [Measure] attribute double?    startTime;
     [Measure, RaisesException=Setter] attribute double?    currentTime;
diff --git a/third_party/blink/renderer/core/animation/animation_effect.idl b/third_party/blink/renderer/core/animation/animation_effect.idl
index bf779a5..1aca06b 100644
--- a/third_party/blink/renderer/core/animation/animation_effect.idl
+++ b/third_party/blink/renderer/core/animation/animation_effect.idl
@@ -30,8 +30,9 @@
 
 // https://drafts.csswg.org/web-animations/#the-animationeffect-interface
 
-[Exposed=Window]
-interface AnimationEffect {
+[
+    RuntimeEnabled=WebAnimationsAPI
+] interface AnimationEffect {
     EffectTiming getTiming();
     ComputedEffectTiming getComputedTiming();
     [RaisesException] void updateTiming(optional OptionalEffectTiming timing);
diff --git a/third_party/blink/renderer/core/animation/keyframe_effect.idl b/third_party/blink/renderer/core/animation/keyframe_effect.idl
index c1e196c..79a9096 100644
--- a/third_party/blink/renderer/core/animation/keyframe_effect.idl
+++ b/third_party/blink/renderer/core/animation/keyframe_effect.idl
@@ -28,19 +28,19 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-// https://drafts.csswg.org/web-animations/#the-keyframeeffect-interface
+// https://drafts.csswg.org/web-animations/#the-keyframeeffect-interfaces
 
 enum CompositeOperation { "replace", "add", "accumulate" };
 
 [
-    Exposed=Window,
     Constructor(Element? target, object? keyframes, optional (unrestricted double or KeyframeEffectOptions) options),
     Constructor(KeyframeEffect source),
     ConstructorCallWith=ScriptState,
-    RaisesException=Constructor
+    RaisesException=Constructor,
+    RuntimeEnabled=WebAnimationsAPI
 ] interface KeyframeEffect : AnimationEffect {
   attribute Element? target;
-  [RuntimeEnabled=WebAnimationsAPI] attribute CompositeOperation composite;
-  [CallWith=ScriptState, RuntimeEnabled=WebAnimationsAPI] sequence<object> getKeyframes();
-  [CallWith=ScriptState, RaisesException, RuntimeEnabled=WebAnimationsAPI] void setKeyframes(object? keyframes);
+  attribute CompositeOperation composite;
+  [CallWith=ScriptState] sequence<object> getKeyframes();
+  [CallWith=ScriptState, RaisesException] void setKeyframes(object? keyframes);
 };
diff --git a/third_party/blink/renderer/core/exported/web_associated_url_loader_impl.cc b/third_party/blink/renderer/core/exported/web_associated_url_loader_impl.cc
index fe996f5e4..974f117 100644
--- a/third_party/blink/renderer/core/exported/web_associated_url_loader_impl.cc
+++ b/third_party/blink/renderer/core/exported/web_associated_url_loader_impl.cc
@@ -363,7 +363,7 @@
     allow_load = observer_ && IsValidHTTPToken(method) &&
                  !FetchUtils::IsForbiddenMethod(method);
     if (allow_load) {
-      new_request.SetHTTPMethod(FetchUtils::NormalizeMethod(method));
+      new_request.SetHttpMethod(FetchUtils::NormalizeMethod(method));
       HTTPRequestHeaderValidator validator;
       new_request.VisitHttpHeaderFields(&validator);
       allow_load = validator.IsSafe();
diff --git a/third_party/blink/renderer/core/exported/web_associated_url_loader_impl_test.cc b/third_party/blink/renderer/core/exported/web_associated_url_loader_impl_test.cc
index a2c10f0..56cc969 100644
--- a/third_party/blink/renderer/core/exported/web_associated_url_loader_impl_test.cc
+++ b/third_party/blink/renderer/core/exported/web_associated_url_loader_impl_test.cc
@@ -168,7 +168,7 @@
     request.SetFetchRequestMode(network::mojom::FetchRequestMode::kSameOrigin);
     request.SetFetchCredentialsMode(
         network::mojom::FetchCredentialsMode::kOmit);
-    request.SetHTTPMethod(WebString::FromUTF8(unsafe_method));
+    request.SetHttpMethod(WebString::FromUTF8(unsafe_method));
     WebAssociatedURLLoaderOptions options;
     options.untrusted_http = true;
     CheckFails(request, options);
diff --git a/third_party/blink/renderer/core/fetch/fetch_manager.cc b/third_party/blink/renderer/core/fetch/fetch_manager.cc
index 3b6de7f..030bce2 100644
--- a/third_party/blink/renderer/core/fetch/fetch_manager.cc
+++ b/third_party/blink/renderer/core/fetch/fetch_manager.cc
@@ -681,7 +681,7 @@
   ResourceRequest request(fetch_request_data_->Url());
   request.SetRequestorOrigin(fetch_request_data_->Origin());
   request.SetRequestContext(fetch_request_data_->Context());
-  request.SetHTTPMethod(fetch_request_data_->Method());
+  request.SetHttpMethod(fetch_request_data_->Method());
   request.SetFetchWindowId(fetch_request_data_->WindowId());
 
   switch (fetch_request_data_->Mode()) {
@@ -784,7 +784,7 @@
   request.SetRequestorOrigin(fetch_request_data_->Origin());
   request.SetRequestContext(fetch_request_data_->Context());
   request.SetUseStreamOnResponse(true);
-  request.SetHTTPMethod(fetch_request_data_->Method());
+  request.SetHttpMethod(fetch_request_data_->Method());
   request.SetFetchCredentialsMode(network::mojom::FetchCredentialsMode::kOmit);
   request.SetFetchRedirectMode(FetchRedirectMode::kError);
   request.SetFetchImportanceMode(fetch_request_data_->Importance());
diff --git a/third_party/blink/renderer/core/layout/layout_block.cc b/third_party/blink/renderer/core/layout/layout_block.cc
index 02f9a1d7..6ba6e5c 100644
--- a/third_party/blink/renderer/core/layout/layout_block.cc
+++ b/third_party/blink/renderer/core/layout/layout_block.cc
@@ -653,23 +653,22 @@
     return;
   }
 
-  if (relayout_children) {
-    child.SetChildNeedsLayout(kMarkOnlyThis);
-  } else {
-    // FIXME: Technically percentage height objects only need a relayout if
-    // their percentage isn't going to be turned into an auto value. Add a
-    // method to determine this, so that we can avoid the relayout.
-    bool has_relative_logical_height =
-        child.HasRelativeLogicalHeight() ||
-        (child.IsAnonymous() && HasRelativeLogicalHeight()) ||
-        child.StretchesToViewport();
-    if ((has_relative_logical_height && !IsLayoutView()) ||
-        (height_available_to_children_changed_ &&
-         ChangeInAvailableLogicalHeightAffectsChild(this, child)) ||
-        (child.IsListMarker() && IsListItem() &&
-         ToLayoutBlockFlow(this)->ContainsFloats())) {
+  // FIXME: Technically percentage height objects only need a relayout if
+  // their percentage isn't going to be turned into an auto value. Add a
+  // method to determine this, so that we can avoid the relayout.
+  bool has_relative_logical_height =
+      child.HasRelativeLogicalHeight() ||
+      (child.IsAnonymous() && HasRelativeLogicalHeight()) ||
+      child.StretchesToViewport();
+  if (relayout_children || (has_relative_logical_height && !IsLayoutView()) ||
+      (height_available_to_children_changed_ &&
+       ChangeInAvailableLogicalHeightAffectsChild(this, child)) ||
+      (child.IsListMarker() && IsListItem() &&
+       ToLayoutBlockFlow(this)->ContainsFloats())) {
+    if (child.IsLayoutNGMixin())
       child.SetSelfNeedsLayoutForAvailableSpace(true);
-    }
+    else
+      child.SetChildNeedsLayout(kMarkOnlyThis);
   }
 }
 
diff --git a/third_party/blink/renderer/core/layout/layout_box.cc b/third_party/blink/renderer/core/layout/layout_box.cc
index 525e0cf..8a85b6ba 100644
--- a/third_party/blink/renderer/core/layout/layout_box.cc
+++ b/third_party/blink/renderer/core/layout/layout_box.cc
@@ -63,11 +63,13 @@
 #include "third_party/blink/renderer/core/layout/layout_table_cell.h"
 #include "third_party/blink/renderer/core/layout/layout_view.h"
 #include "third_party/blink/renderer/core/layout/ng/geometry/ng_box_strut.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_box_fragment.h"
 #include "third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.h"
 #include "third_party/blink/renderer/core/layout/ng/ng_constraint_space.h"
 #include "third_party/blink/renderer/core/layout/ng/ng_fragmentation_utils.h"
 #include "third_party/blink/renderer/core/layout/ng/ng_layout_result.h"
 #include "third_party/blink/renderer/core/layout/ng/ng_layout_utils.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_length_utils.h"
 #include "third_party/blink/renderer/core/layout/shapes/shape_outside_info.h"
 #include "third_party/blink/renderer/core/page/autoscroll_controller.h"
 #include "third_party/blink/renderer/core/page/page.h"
@@ -2333,8 +2335,22 @@
   if (cached_layout_result->HasOrthogonalFlowRoots())
     return nullptr;
 
-  if (!MaySkipLayout(NGBlockNode(this), *cached_layout_result, new_space))
+  NGBlockNode node(this);
+  if (!MaySkipLayout(node, *cached_layout_result, new_space))
     return nullptr;
+  // It is possible that our intrinsic size has changed; check for that here.
+  // TODO(cbiesinger): Move this to ::MaySkipLayout.
+  if (new_space.IsShrinkToFit() || NeedMinMaxSize(StyleRef())) {
+    NGBoxFragment fragment(
+        new_space.GetWritingMode(), StyleRef().Direction(),
+        To<NGPhysicalBoxFragment>(*cached_layout_result->PhysicalFragment()));
+    // If we get here, we know that border and padding haven't changed.
+    NGBoxStrut border_padding = fragment.Borders() + fragment.Padding();
+    LayoutUnit size =
+        ComputeInlineSizeForFragment(new_space, node, border_padding);
+    if (size != fragment.InlineSize())
+      return nullptr;
+  }
 
   const NGConstraintSpace& old_space =
       cached_layout_result->GetConstraintSpaceForCaching();
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.cc
index 1e582c2f..9909f22 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.cc
@@ -131,6 +131,24 @@
   return builder.ToTextFragment();
 }
 
+bool IsStickyImage(const NGInlineItem& candidate,
+                   const NGInlineItemResults& item_results,
+                   NGLineBreaker::WhitespaceState trailing_whitespace,
+                   const String& text) {
+  if (!IsImage(candidate))
+    return false;
+  if (trailing_whitespace != NGLineBreaker::WhitespaceState::kNone &&
+      trailing_whitespace != NGLineBreaker::WhitespaceState::kUnknown)
+    return false;
+
+  if (item_results.size() >= 1) {
+    // If this image follows a <wbr> the image isn't sticky.
+    const auto& last = item_results[item_results.size() - 1];
+    return text[last.start_offset] != kZeroWidthSpaceCharacter;
+  }
+  return true;
+}
+
 }  // namespace
 
 NGLineBreaker::NGLineBreaker(NGInlineNode node,
@@ -387,11 +405,11 @@
     // opportunity if we're trailing.
     if (state_ == LineBreakState::kTrailing &&
         CanBreakAfterLast(*item_results)) {
-      if (sticky_images_quirk_ && IsImage(item) &&
-          (trailing_whitespace_ == WhitespaceState::kNone ||
-           trailing_whitespace_ == WhitespaceState::kUnknown)) {
-        // If this is an image that follows text that doesn't end with something
-        // breakable, we cannot break between the two items.
+      // If the sticky images quirk is enabled, and this is an image that
+      // follows text that doesn't end with something breakable, we cannot break
+      // between the two items.
+      if (sticky_images_quirk_ &&
+          IsStickyImage(item, *item_results, trailing_whitespace_, Text())) {
         HandleAtomicInline(item, percentage_resolution_block_size_for_min_max,
                            line_info);
         continue;
diff --git a/third_party/blink/renderer/core/loader/form_submission.cc b/third_party/blink/renderer/core/loader/form_submission.cc
index 339fa19..98172c9 100644
--- a/third_party/blink/renderer/core/loader/form_submission.cc
+++ b/third_party/blink/renderer/core/loader/form_submission.cc
@@ -290,7 +290,7 @@
     frame_request.SetFrameName(target_);
 
   if (method_ == FormSubmission::kPostMethod) {
-    frame_request.GetResourceRequest().SetHTTPMethod(http_names::kPOST);
+    frame_request.GetResourceRequest().SetHttpMethod(http_names::kPOST);
     frame_request.GetResourceRequest().SetHttpBody(form_data_);
 
     // construct some user headers if necessary
diff --git a/third_party/blink/renderer/core/loader/frame_fetch_context_test.cc b/third_party/blink/renderer/core/loader/frame_fetch_context_test.cc
index f1bf9fb..8c0d9c52 100644
--- a/third_party/blink/renderer/core/loader/frame_fetch_context_test.cc
+++ b/third_party/blink/renderer/core/loader/frame_fetch_context_test.cc
@@ -1143,7 +1143,7 @@
   const KURL document_url("https://www2.example.com/fuga/hoge.html");
   const String origin = "https://www2.example.com";
   ResourceRequest request(KURL("https://localhost/"));
-  request.SetHTTPMethod("PUT");
+  request.SetHttpMethod("PUT");
 
   GetNetworkStateNotifier().SetSaveDataEnabledOverride(true);
   document->SetSecurityOrigin(SecurityOrigin::Create(KURL(origin)));
diff --git a/third_party/blink/renderer/core/loader/history_item.cc b/third_party/blink/renderer/core/loader/history_item.cc
index 50d2953f..974a37c6 100644
--- a/third_party/blink/renderer/core/loader/history_item.cc
+++ b/third_party/blink/renderer/core/loader/history_item.cc
@@ -157,7 +157,7 @@
   request.SetHttpReferrer(referrer_);
   request.SetCacheMode(cache_mode);
   if (form_data_) {
-    request.SetHTTPMethod(http_names::kPOST);
+    request.SetHttpMethod(http_names::kPOST);
     request.SetHttpBody(form_data_);
     request.SetHTTPContentType(form_content_type_);
     request.SetHTTPOriginToMatchReferrerIfNeeded();
diff --git a/third_party/blink/renderer/core/loader/idleness_detector.cc b/third_party/blink/renderer/core/loader/idleness_detector.cc
index 774a4c4..d32e45a8 100644
--- a/third_party/blink/renderer/core/loader/idleness_detector.cc
+++ b/third_party/blink/renderer/core/loader/idleness_detector.cc
@@ -4,6 +4,7 @@
 
 #include "third_party/blink/renderer/core/loader/idleness_detector.h"
 
+#include "services/resource_coordinator/public/cpp/resource_coordinator_features.h"
 #include "third_party/blink/public/platform/modules/service_worker/web_service_worker_network_provider.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/platform/task_type.h"
@@ -11,6 +12,7 @@
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/core/frame/settings.h"
 #include "third_party/blink/renderer/core/loader/document_loader.h"
+#include "third_party/blink/renderer/core/page/page.h"
 #include "third_party/blink/renderer/core/paint/first_meaningful_paint_detector.h"
 #include "third_party/blink/renderer/core/probe/core_probes.h"
 #include "third_party/blink/renderer/platform/instrumentation/resource_coordinator/frame_resource_coordinator.h"
@@ -121,6 +123,19 @@
   return network_2_quiet_start_time_;
 }
 
+bool IdlenessDetector::NetworkIsAlmostIdle() {
+  if (in_network_2_quiet_period_)
+    return false;
+  if (!network_2_quiet_.is_null())
+    return false;
+  if (network_2_quiet_start_time_.is_null())
+    return false;
+  TimeTicks current_time = TimeTicks::Now();
+  if (current_time - network_2_quiet_start_time_ <= network_quiet_window_)
+    return false;
+  return true;
+}
+
 TimeTicks IdlenessDetector::GetNetworkIdleTime() {
   return network_0_quiet_start_time_;
 }
@@ -145,6 +160,12 @@
     }
     FirstMeaningfulPaintDetector::From(*local_frame_->GetDocument())
         .OnNetwork2Quiet();
+    if (local_frame_->IsMainFrame()) {
+      if (Page* page = local_frame_->GetPage()) {
+        if (PageScheduler* scheduler = page->GetPageScheduler())
+          scheduler->OnLocalMainFrameNetworkAlmostIdle();
+      }
+    }
     in_network_2_quiet_period_ = false;
     network_2_quiet_ = TimeTicks();
   }
diff --git a/third_party/blink/renderer/core/loader/idleness_detector.h b/third_party/blink/renderer/core/loader/idleness_detector.h
index 91a63a5..2997d44 100644
--- a/third_party/blink/renderer/core/loader/idleness_detector.h
+++ b/third_party/blink/renderer/core/loader/idleness_detector.h
@@ -37,6 +37,7 @@
 
   TimeTicks GetNetworkAlmostIdleTime();
   TimeTicks GetNetworkIdleTime();
+  bool NetworkIsAlmostIdle();
 
   void Trace(blink::Visitor*);
 
diff --git a/third_party/blink/renderer/core/loader/ping_loader.cc b/third_party/blink/renderer/core/loader/ping_loader.cc
index 2902f6d..611b58e 100644
--- a/third_party/blink/renderer/core/loader/ping_loader.cc
+++ b/third_party/blink/renderer/core/loader/ping_loader.cc
@@ -180,7 +180,7 @@
   }
 
   ResourceRequest request(url);
-  request.SetHTTPMethod(http_names::kPOST);
+  request.SetHttpMethod(http_names::kPOST);
   request.SetKeepalive(true);
   request.SetRequestContext(mojom::RequestContextType::BEACON);
   beacon.Serialize(request);
@@ -210,7 +210,7 @@
     return;
 
   ResourceRequest request(ping_url);
-  request.SetHTTPMethod(http_names::kPOST);
+  request.SetHttpMethod(http_names::kPOST);
   request.SetHTTPContentType("text/ping");
   request.SetHttpBody(EncodedFormData::Create("PING"));
   request.SetHttpHeaderField(http_names::kCacheControl, "max-age=0");
@@ -244,7 +244,7 @@
                                      scoped_refptr<EncodedFormData> report,
                                      ViolationReportType type) {
   ResourceRequest request(report_url);
-  request.SetHTTPMethod(http_names::kPOST);
+  request.SetHttpMethod(http_names::kPOST);
   switch (type) {
     case kContentSecurityPolicyViolationReport:
       request.SetHTTPContentType("application/csp-report");
diff --git a/third_party/blink/renderer/core/loader/resource/image_resource_test.cc b/third_party/blink/renderer/core/loader/resource/image_resource_test.cc
index b073a4aa..d51eb74 100644
--- a/third_party/blink/renderer/core/loader/resource/image_resource_test.cc
+++ b/third_party/blink/renderer/core/loader/resource/image_resource_test.cc
@@ -1416,7 +1416,7 @@
   KURL test_url(kTestURL);
   ScopedMockedURLLoad scoped_mocked_url_load(test_url, GetTestFilePath());
   ResourceRequest resource_request(test_url);
-  resource_request.SetHTTPMethod(http_names::kPOST);
+  resource_request.SetHttpMethod(http_names::kPOST);
   FetchParameters params(resource_request);
   params.SetAllowImagePlaceholder();
   ImageResource* image_resource = ImageResource::Fetch(params, CreateFetcher());
diff --git a/third_party/blink/renderer/core/loader/threadable_loader.cc b/third_party/blink/renderer/core/loader/threadable_loader.cc
index 87b3013..24596cc 100644
--- a/third_party/blink/renderer/core/loader/threadable_loader.cc
+++ b/third_party/blink/renderer/core/loader/threadable_loader.cc
@@ -162,7 +162,7 @@
 
   std::unique_ptr<ResourceRequest> preflight_request =
       std::make_unique<ResourceRequest>(request_url);
-  preflight_request->SetHTTPMethod(http_names::kOPTIONS);
+  preflight_request->SetHttpMethod(http_names::kOPTIONS);
   preflight_request->SetHttpHeaderField(http_names::kAccessControlRequestMethod,
                                         request.HttpMethod());
   preflight_request->SetPriority(request.Priority());
diff --git a/third_party/blink/renderer/core/page/page.cc b/third_party/blink/renderer/core/page/page.cc
index a6e2d342..3319ef8 100644
--- a/third_party/blink/renderer/core/page/page.cc
+++ b/third_party/blink/renderer/core/page/page.cc
@@ -54,6 +54,7 @@
 #include "third_party/blink/renderer/core/inspector/console_message_storage.h"
 #include "third_party/blink/renderer/core/layout/layout_view.h"
 #include "third_party/blink/renderer/core/layout/text_autosizer.h"
+#include "third_party/blink/renderer/core/loader/idleness_detector.h"
 #include "third_party/blink/renderer/core/page/autoscroll_controller.h"
 #include "third_party/blink/renderer/core/page/chrome_client.h"
 #include "third_party/blink/renderer/core/page/context_menu_controller.h"
@@ -903,6 +904,13 @@
   return false;
 }
 
+bool Page::LocalMainFrameNetworkIsAlmostIdle() const {
+  LocalFrame* frame = DynamicTo<LocalFrame>(MainFrame());
+  if (!frame)
+    return true;
+  return frame->GetIdlenessDetector()->NetworkIsAlmostIdle();
+}
+
 void Page::AddAutoplayFlags(int32_t value) {
   autoplay_flags_ |= value;
 }
diff --git a/third_party/blink/renderer/core/page/page.h b/third_party/blink/renderer/core/page/page.h
index b782c20..f3d748e4 100644
--- a/third_party/blink/renderer/core/page/page.h
+++ b/third_party/blink/renderer/core/page/page.h
@@ -308,6 +308,7 @@
   void ReportIntervention(const String& message) override;
   bool RequestBeginMainFrameNotExpected(bool new_state) override;
   void SetLifecycleState(PageLifecycleState) override;
+  bool LocalMainFrameNetworkIsAlmostIdle() const override;
 
   void AddAutoplayFlags(int32_t flags);
   void ClearAutoplayFlags();
diff --git a/third_party/blink/renderer/core/workers/worker_classic_script_loader.cc b/third_party/blink/renderer/core/workers/worker_classic_script_loader.cc
index 5e3c70c..e2134f5 100644
--- a/third_party/blink/renderer/core/workers/worker_classic_script_loader.cc
+++ b/third_party/blink/renderer/core/workers/worker_classic_script_loader.cc
@@ -109,7 +109,7 @@
   fetch_client_settings_object_fetcher_ = fetch_client_settings_object_fetcher;
 
   ResourceRequest request(url);
-  request.SetHTTPMethod(http_names::kGET);
+  request.SetHttpMethod(http_names::kGET);
   request.SetExternalRequestStateFromRequestorAddressSpace(
       creation_address_space);
   request.SetRequestContext(request_context);
@@ -148,7 +148,7 @@
   is_worker_global_scope_ = execution_context.IsWorkerGlobalScope();
 
   ResourceRequest request(url);
-  request.SetHTTPMethod(http_names::kGET);
+  request.SetHttpMethod(http_names::kGET);
   request.SetExternalRequestStateFromRequestorAddressSpace(
       creation_address_space);
   request.SetRequestContext(request_context);
diff --git a/third_party/blink/renderer/core/xmlhttprequest/xml_http_request.cc b/third_party/blink/renderer/core/xmlhttprequest/xml_http_request.cc
index 0c439de4..78e6fc1 100644
--- a/third_party/blink/renderer/core/xmlhttprequest/xml_http_request.cc
+++ b/third_party/blink/renderer/core/xmlhttprequest/xml_http_request.cc
@@ -1059,7 +1059,7 @@
 
   ResourceRequest request(url_);
   request.SetRequestorOrigin(GetSecurityOrigin());
-  request.SetHTTPMethod(method_);
+  request.SetHttpMethod(method_);
   request.SetRequestContext(mojom::RequestContextType::XML_HTTP_REQUEST);
   request.SetFetchRequestMode(
       upload_events ? network::mojom::FetchRequestMode::kCorsWithForcedPreflight
diff --git a/third_party/blink/renderer/modules/accessibility/ax_position.cc b/third_party/blink/renderer/modules/accessibility/ax_position.cc
index 5a123db..e57e4a02 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_position.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_position.cc
@@ -223,14 +223,14 @@
   DCHECK(container_node->IsContainerNode());
   if (container->AccessibilityIsIgnored()) {
     container = container->ParentObjectUnignored();
-    // |container_node| could potentially become nullptr if the unignored parent
-    // is an anonymous layout block.
+    if (!container)
+      return {};
+
+    // |container_node| could potentially become nullptr if the unignored
+    // parent is an anonymous layout block.
     container_node = container->GetNode();
   }
 
-  if (!container)
-    return {};
-
   AXPosition ax_position(*container);
   // |ComputeNodeAfterPosition| returns nullptr for "after children"
   // positions.
diff --git a/third_party/blink/renderer/modules/eventsource/event_source.cc b/third_party/blink/renderer/modules/eventsource/event_source.cc
index 7e9305f..6707873 100644
--- a/third_party/blink/renderer/modules/eventsource/event_source.cc
+++ b/third_party/blink/renderer/modules/eventsource/event_source.cc
@@ -124,7 +124,7 @@
 
   ExecutionContext& execution_context = *this->GetExecutionContext();
   ResourceRequest request(current_url_);
-  request.SetHTTPMethod(http_names::kGET);
+  request.SetHttpMethod(http_names::kGET);
   request.SetHttpHeaderField(http_names::kAccept, "text/event-stream");
   request.SetHttpHeaderField(http_names::kCacheControl, "no-cache");
   request.SetRequestContext(mojom::RequestContextType::EVENT_SOURCE);
diff --git a/third_party/blink/renderer/modules/exported/web_ax_object.cc b/third_party/blink/renderer/modules/exported/web_ax_object.cc
index fbdfaf7..16a8acf 100644
--- a/third_party/blink/renderer/modules/exported/web_ax_object.cc
+++ b/third_party/blink/renderer/modules/exported/web_ax_object.cc
@@ -782,7 +782,7 @@
   focus_offset = -1;
   focus_affinity = ax::mojom::TextAffinity::kDownstream;
 
-  if (IsDetached())
+  if (IsDetached() || GetDocument().IsNull())
     return;
 
   WebAXObject focus = FromWebDocumentFocused(GetDocument());
@@ -901,7 +901,7 @@
 }
 
 unsigned WebAXObject::SelectionEnd() const {
-  if (IsDetached())
+  if (IsDetached() || GetDocument().IsNull())
     return 0;
 
   WebAXObject focus = FromWebDocumentFocused(GetDocument());
@@ -922,7 +922,7 @@
 }
 
 unsigned WebAXObject::SelectionStart() const {
-  if (IsDetached())
+  if (IsDetached() || GetDocument().IsNull())
     return 0;
 
   WebAXObject focus = FromWebDocumentFocused(GetDocument());
diff --git a/third_party/blink/renderer/modules/media_controls/BUILD.gn b/third_party/blink/renderer/modules/media_controls/BUILD.gn
index 600a477..ea9cc14 100644
--- a/third_party/blink/renderer/modules/media_controls/BUILD.gn
+++ b/third_party/blink/renderer/modules/media_controls/BUILD.gn
@@ -113,6 +113,8 @@
       "touchless/elements/media_controls_touchless_play_button_element.h",
       "touchless/elements/media_controls_touchless_seek_button_element.cc",
       "touchless/elements/media_controls_touchless_seek_button_element.h",
+      "touchless/elements/media_controls_touchless_time_display_element.cc",
+      "touchless/elements/media_controls_touchless_time_display_element.h",
       "touchless/elements/media_controls_touchless_timeline_element.cc",
       "touchless/elements/media_controls_touchless_timeline_element.h",
       "touchless/elements/media_controls_touchless_volume_button_element.cc",
diff --git a/third_party/blink/renderer/modules/media_controls/elements/media_control_time_display_element.cc b/third_party/blink/renderer/modules/media_controls/elements/media_control_time_display_element.cc
index b6dbc098..9322101 100644
--- a/third_party/blink/renderer/modules/media_controls/elements/media_control_time_display_element.cc
+++ b/third_party/blink/renderer/modules/media_controls/elements/media_control_time_display_element.cc
@@ -7,6 +7,7 @@
 #include "third_party/blink/public/platform/web_size.h"
 #include "third_party/blink/renderer/modules/media_controls/elements/media_control_elements_helper.h"
 #include "third_party/blink/renderer/modules/media_controls/media_controls_impl.h"
+#include "third_party/blink/renderer/modules/media_controls/media_controls_shared_helper.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/text/platform_locale.h"
 
@@ -53,30 +54,7 @@
 }
 
 String MediaControlTimeDisplayElement::FormatTime() const {
-  double time = std::isfinite(current_value_) ? current_value_ : 0;
-
-  int seconds = static_cast<int>(fabs(time));
-  int minutes = seconds / 60;
-  int hours = minutes / 60;
-
-  seconds %= 60;
-  minutes %= 60;
-
-  const char* negative_sign = (time < 0 ? "-" : "");
-
-  // [0-10) minutes duration is m:ss
-  // [10-60) minutes duration is mm:ss
-  // [1-10) hours duration is h:mm:ss
-  // [10-100) hours duration is hh:mm:ss
-  // [100-1000) hours duration is hhh:mm:ss
-  // etc.
-
-  if (hours > 0) {
-    return String::Format("%s%d:%02d:%02d", negative_sign, hours, minutes,
-                          seconds);
-  }
-
-  return String::Format("%s%d:%02d", negative_sign, minutes, seconds);
+  return MediaControlsSharedHelpers::FormatTime(current_value_);
 }
 
 void MediaControlTimeDisplayElement::SetAriaLabel() {
diff --git a/third_party/blink/renderer/modules/media_controls/media_controls_shared_helper.cc b/third_party/blink/renderer/modules/media_controls/media_controls_shared_helper.cc
index 9071fdf..33b7671 100644
--- a/third_party/blink/renderer/modules/media_controls/media_controls_shared_helper.cc
+++ b/third_party/blink/renderer/modules/media_controls/media_controls_shared_helper.cc
@@ -52,4 +52,32 @@
   return base::nullopt;
 }
 
+String MediaControlsSharedHelpers::FormatTime(double time) {
+  if (!std::isfinite(time))
+    time = 0;
+
+  int seconds = static_cast<int>(fabs(time));
+  int minutes = seconds / 60;
+  int hours = minutes / 60;
+
+  seconds %= 60;
+  minutes %= 60;
+
+  const char* negative_sign = (time < 0 ? "-" : "");
+
+  // [0-10) minutes duration is m:ss
+  // [10-60) minutes duration is mm:ss
+  // [1-10) hours duration is h:mm:ss
+  // [10-100) hours duration is hh:mm:ss
+  // [100-1000) hours duration is hhh:mm:ss
+  // etc.
+
+  if (hours > 0) {
+    return String::Format("%s%d:%02d:%02d", negative_sign, hours, minutes,
+                          seconds);
+  }
+
+  return String::Format("%s%d:%02d", negative_sign, minutes, seconds);
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/media_controls/media_controls_shared_helper.h b/third_party/blink/renderer/modules/media_controls/media_controls_shared_helper.h
index d8a3e97c..daf14b4 100644
--- a/third_party/blink/renderer/modules/media_controls/media_controls_shared_helper.h
+++ b/third_party/blink/renderer/modules/media_controls/media_controls_shared_helper.h
@@ -7,6 +7,7 @@
 
 #include "base/optional.h"
 #include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
 
 namespace blink {
 
@@ -18,6 +19,8 @@
  public:
   static base::Optional<unsigned> GetCurrentBufferedTimeRange(
       HTMLMediaElement& media_element);
+
+  static String FormatTime(double time);
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/media_controls/touchless/elements/media_controls_touchless_time_display_element.cc b/third_party/blink/renderer/modules/media_controls/touchless/elements/media_controls_touchless_time_display_element.cc
new file mode 100644
index 0000000..154d6ad
--- /dev/null
+++ b/third_party/blink/renderer/modules/media_controls/touchless/elements/media_controls_touchless_time_display_element.cc
@@ -0,0 +1,49 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/modules/media_controls/touchless/elements/media_controls_touchless_time_display_element.h"
+
+#include "third_party/blink/renderer/core/html/media/html_media_element.h"
+#include "third_party/blink/renderer/modules/media_controls/media_controls_shared_helper.h"
+#include "third_party/blink/renderer/modules/media_controls/touchless/media_controls_touchless_impl.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
+
+namespace blink {
+
+MediaControlsTouchlessTimeDisplayElement::
+    MediaControlsTouchlessTimeDisplayElement(
+        MediaControlsTouchlessImpl& media_controls)
+    : HTMLDivElement(media_controls.GetDocument()),
+      MediaControlsTouchlessElement(media_controls),
+      current_time_(0.0),
+      duration_(0.0) {
+  SetShadowPseudoId(
+      AtomicString("-internal-media-controls-touchless-time-display"));
+  UpdateTimeDisplay();
+}
+
+void MediaControlsTouchlessTimeDisplayElement::OnTimeUpdate() {
+  current_time_ = MediaElement().currentTime();
+  UpdateTimeDisplay();
+}
+
+void MediaControlsTouchlessTimeDisplayElement::OnDurationChange() {
+  duration_ = MediaElement().duration();
+  UpdateTimeDisplay();
+}
+
+void MediaControlsTouchlessTimeDisplayElement::Trace(blink::Visitor* visitor) {
+  HTMLDivElement::Trace(visitor);
+  MediaControlsTouchlessElement::Trace(visitor);
+}
+
+void MediaControlsTouchlessTimeDisplayElement::UpdateTimeDisplay() {
+  StringBuilder builder;
+  builder.Append(MediaControlsSharedHelpers::FormatTime(current_time_));
+  builder.Append(" / ");
+  builder.Append(MediaControlsSharedHelpers::FormatTime(duration_));
+  setInnerText(builder.ToAtomicString(), ASSERT_NO_EXCEPTION);
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/media_controls/touchless/elements/media_controls_touchless_time_display_element.h b/third_party/blink/renderer/modules/media_controls/touchless/elements/media_controls_touchless_time_display_element.h
new file mode 100644
index 0000000..d0eb117
--- /dev/null
+++ b/third_party/blink/renderer/modules/media_controls/touchless/elements/media_controls_touchless_time_display_element.h
@@ -0,0 +1,37 @@
+// 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_MODULES_MEDIA_CONTROLS_TOUCHLESS_ELEMENTS_MEDIA_CONTROLS_TOUCHLESS_TIME_DISPLAY_ELEMENT_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIA_CONTROLS_TOUCHLESS_ELEMENTS_MEDIA_CONTROLS_TOUCHLESS_TIME_DISPLAY_ELEMENT_H_
+
+#include "third_party/blink/renderer/core/html/html_div_element.h"
+#include "third_party/blink/renderer/modules/media_controls/touchless/elements/media_controls_touchless_element.h"
+
+namespace blink {
+
+class MediaControlsTouchlessTimeDisplayElement
+    : public HTMLDivElement,
+      public MediaControlsTouchlessElement {
+  USING_GARBAGE_COLLECTED_MIXIN(MediaControlsTouchlessTimeDisplayElement);
+
+ public:
+  explicit MediaControlsTouchlessTimeDisplayElement(
+      MediaControlsTouchlessImpl&);
+
+  // MediaControlsTouchlessMediaEventListenerObserver overrides
+  void OnTimeUpdate() override;
+  void OnDurationChange() override;
+
+  void Trace(blink::Visitor* visitor) override;
+
+ private:
+  void UpdateTimeDisplay();
+
+  double current_time_;
+  double duration_;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIA_CONTROLS_TOUCHLESS_ELEMENTS_MEDIA_CONTROLS_TOUCHLESS_TIME_DISPLAY_ELEMENT_H_
diff --git a/third_party/blink/renderer/modules/media_controls/touchless/media_controls_touchless_impl.cc b/third_party/blink/renderer/modules/media_controls/touchless/media_controls_touchless_impl.cc
index 4b97be01..a010853d6 100644
--- a/third_party/blink/renderer/modules/media_controls/touchless/media_controls_touchless_impl.cc
+++ b/third_party/blink/renderer/modules/media_controls/touchless/media_controls_touchless_impl.cc
@@ -19,9 +19,11 @@
 #include "third_party/blink/renderer/core/html/track/text_track.h"
 #include "third_party/blink/renderer/core/html/track/text_track_list.h"
 #include "third_party/blink/renderer/core/page/chrome_client.h"
+#include "third_party/blink/renderer/modules/media_controls/elements/media_control_elements_helper.h"
 #include "third_party/blink/renderer/modules/media_controls/media_controls_orientation_lock_delegate.h"
 #include "third_party/blink/renderer/modules/media_controls/media_controls_text_track_manager.h"
 #include "third_party/blink/renderer/modules/media_controls/touchless/elements/media_controls_touchless_overlay_element.h"
+#include "third_party/blink/renderer/modules/media_controls/touchless/elements/media_controls_touchless_time_display_element.h"
 #include "third_party/blink/renderer/modules/media_controls/touchless/elements/media_controls_touchless_timeline_element.h"
 #include "third_party/blink/renderer/modules/media_controls/touchless/media_controls_touchless_media_event_listener.h"
 #include "third_party/blink/renderer/modules/media_controls/touchless/media_controls_touchless_resource_loader.h"
@@ -81,11 +83,18 @@
       MakeGarbageCollected<MediaControlsTouchlessImpl>(media_element);
   MediaControlsTouchlessOverlayElement* overlay_element =
       MakeGarbageCollected<MediaControlsTouchlessOverlayElement>(*controls);
+
+  MediaControlsTouchlessTimeDisplayElement* time_display_element =
+      MakeGarbageCollected<MediaControlsTouchlessTimeDisplayElement>(*controls);
   MediaControlsTouchlessTimelineElement* timeline_element =
       MakeGarbageCollected<MediaControlsTouchlessTimelineElement>(*controls);
 
   controls->ParserAppendChild(overlay_element);
-  controls->ParserAppendChild(timeline_element);
+
+  Element* bottom_container = MediaControlElementsHelper::CreateDiv(
+      "-internal-media-controls-touchless-bottom-container", controls);
+  bottom_container->ParserAppendChild(time_display_element);
+  bottom_container->ParserAppendChild(timeline_element);
 
   // Controls start hidden.
   controls->MakeTransparent();
diff --git a/third_party/blink/renderer/modules/media_controls/touchless/media_controls_touchless_impl_test.cc b/third_party/blink/renderer/modules/media_controls/touchless/media_controls_touchless_impl_test.cc
index 8d5c1f1b..e6a9727 100644
--- a/third_party/blink/renderer/modules/media_controls/touchless/media_controls_touchless_impl_test.cc
+++ b/third_party/blink/renderer/modules/media_controls/touchless/media_controls_touchless_impl_test.cc
@@ -275,6 +275,24 @@
                    progress_bar_width / loaded_bar_width);
 }
 
+TEST_F(MediaControlsTouchlessImplTest, TimeDisplay) {
+  const double duration = 4000;
+  const double current_time = 3599;
+  const char expect_display[] = "59:59 / 1:06:40";
+
+  Element* time_display = GetControlByShadowPseudoId(
+      "-internal-media-controls-touchless-time-display");
+
+  EXPECT_EQ(time_display->InnerHTMLAsString(), "0:00 / 0:00");
+
+  LoadMediaWithDuration(duration);
+  MediaElement().setCurrentTime(current_time);
+  test::RunPendingTasks();
+  MediaElement().DispatchEvent(*Event::Create(event_type_names::kTimeupdate));
+
+  EXPECT_EQ(time_display->InnerHTMLAsString(), expect_display);
+}
+
 TEST_F(MediaControlsTouchlessImplTestWithMockScheduler, ControlsShowAndHide) {
   // Controls should starts hidden.
   ASSERT_FALSE(IsControlsVisible());
diff --git a/third_party/blink/renderer/modules/media_controls/touchless/resources/gradient_bg.png b/third_party/blink/renderer/modules/media_controls/touchless/resources/gradient_bg.png
new file mode 100644
index 0000000..25f2a02
--- /dev/null
+++ b/third_party/blink/renderer/modules/media_controls/touchless/resources/gradient_bg.png
Binary files differ
diff --git a/third_party/blink/renderer/modules/media_controls/touchless/resources/mediaControlsTouchless.css b/third_party/blink/renderer/modules/media_controls/touchless/resources/mediaControlsTouchless.css
index a2e86f64..271fb96 100644
--- a/third_party/blink/renderer/modules/media_controls/touchless/resources/mediaControlsTouchless.css
+++ b/third_party/blink/renderer/modules/media_controls/touchless/resources/mediaControlsTouchless.css
@@ -3,6 +3,9 @@
    found in the LICENSE file. */
 
 video::-internal-media-controls-touchless {
+  width: inherit;
+  height: inherit;
+
   position: relative;
   direction: ltr;
   display: flex;
@@ -25,6 +28,7 @@
   height: 104px;
   background-color: rgba(32, 33, 36, .9);
   position: absolute;
+  z-index: 1;
   margin: auto;
   top: 0;
   left: 0;
@@ -98,10 +102,33 @@
   background-repeat: no-repeat;
 }
 
-video::-internal-media-controls-touchless-timeline {
-  position: absolute;
-  bottom: 0;
+video::-internal-media-controls-touchless-bottom-container {
+  display: flex;
+  flex-direction: column;
+  justify-content: flex-end;
+  height: 100%;
   width: 100%;
+  z-index: 0;
+  background-size: auto 48px;
+
+  background:
+    -webkit-image-set(url('gradient_bg.png') 1x)
+    repeat-x bottom left;
+}
+
+video::-internal-media-controls-touchless-time-display {
+  color: #ffffff;
+  font-family: Roboto-Regular, Roboto, sans-serif;
+  font-size: 14px;
+  width: 100%;
+  bottom: auto;
+  padding-left: 8px;
+  padding-bottom: 8px;
+}
+
+video::-internal-media-controls-touchless-timeline {
+  width: 100%;
+  bottom: auto;
   height: 4px;
   background-color: rgba(0, 0, 0, 0.2);
 }
diff --git a/third_party/blink/renderer/platform/audio/vector_math_test.cc b/third_party/blink/renderer/platform/audio/vector_math_test.cc
index 5d85c7a..6965f16 100644
--- a/third_party/blink/renderer/platform/audio/vector_math_test.cc
+++ b/third_party/blink/renderer/platform/audio/vector_math_test.cc
@@ -14,6 +14,7 @@
 
 #include "build/build_config.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
 #include "third_party/blink/renderer/platform/wtf/math_extras.h"
 
 namespace blink {
@@ -70,7 +71,11 @@
 // blink::vector_math functions.
 template <typename T>
 class TestVector {
+  STACK_ALLOCATED();
+
   class Iterator {
+    STACK_ALLOCATED();
+
    public:
     // These types are used by std::iterator_traits used by std::equal used by
     // TestVector::operator==.
diff --git a/third_party/blink/renderer/platform/bindings/dom_wrapper_world.h b/third_party/blink/renderer/platform/bindings/dom_wrapper_world.h
index 251116e..fadd745c 100644
--- a/third_party/blink/renderer/platform/bindings/dom_wrapper_world.h
+++ b/third_party/blink/renderer/platform/bindings/dom_wrapper_world.h
@@ -38,6 +38,7 @@
 #include "third_party/blink/public/platform/web_isolated_world_ids.h"
 #include "third_party/blink/renderer/platform/bindings/script_state.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
 #include "third_party/blink/renderer/platform/wtf/hash_set.h"
 #include "third_party/blink/renderer/platform/wtf/ref_counted.h"
 #include "v8/include/v8.h"
@@ -52,6 +53,8 @@
 // is identified by a world id that is a per-thread global identifier (see
 // WorldId enum).
 class PLATFORM_EXPORT DOMWrapperWorld : public RefCounted<DOMWrapperWorld> {
+  USING_FAST_MALLOC(DOMWrapperWorld);
+
  public:
   // Per-thread global identifiers for DOMWrapperWorld.
   enum WorldId {
diff --git a/third_party/blink/renderer/platform/bindings/shared_persistent.h b/third_party/blink/renderer/platform/bindings/shared_persistent.h
index 155baae..35a9036 100644
--- a/third_party/blink/renderer/platform/bindings/shared_persistent.h
+++ b/third_party/blink/renderer/platform/bindings/shared_persistent.h
@@ -43,6 +43,8 @@
 // instead.
 template <typename T>
 class SharedPersistent : public RefCounted<SharedPersistent<T>> {
+  USING_FAST_MALLOC(SharedPersistent);
+
  public:
   static scoped_refptr<SharedPersistent<T>> Create(v8::Local<T> value,
                                                    v8::Isolate* isolate) {
diff --git a/third_party/blink/renderer/platform/blob/blob_data.h b/third_party/blink/renderer/platform/blob/blob_data.h
index 7ec4f688..c5829fe3 100644
--- a/third_party/blink/renderer/platform/blob/blob_data.h
+++ b/third_party/blink/renderer/platform/blob/blob_data.h
@@ -37,6 +37,7 @@
 #include "third_party/blink/public/mojom/blob/blob.mojom-blink.h"
 #include "third_party/blink/public/mojom/blob/data_element.mojom-blink.h"
 #include "third_party/blink/renderer/platform/weborigin/kurl.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
 #include "third_party/blink/renderer/platform/wtf/forward.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
 #include "third_party/blink/renderer/platform/wtf/thread_safe_ref_counted.h"
@@ -150,6 +151,8 @@
 
 class PLATFORM_EXPORT BlobDataHandle
     : public ThreadSafeRefCounted<BlobDataHandle> {
+  USING_FAST_MALLOC(BlobDataHandle);
+
  public:
   // For empty blob construction.
   static scoped_refptr<BlobDataHandle> Create() {
diff --git a/third_party/blink/renderer/platform/exported/platform.cc b/third_party/blink/renderer/platform/exported/platform.cc
index 080739a5..fd98073c 100644
--- a/third_party/blink/renderer/platform/exported/platform.cc
+++ b/third_party/blink/renderer/platform/exported/platform.cc
@@ -66,6 +66,7 @@
 #include "third_party/blink/renderer/platform/scheduler/common/simple_thread_scheduler.h"
 #include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h"
 #include "third_party/blink/renderer/platform/scheduler/public/thread.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
 #include "third_party/blink/renderer/platform/wtf/hash_map.h"
 #include "third_party/webrtc/api/async_resolver_factory.h"
 #include "third_party/webrtc/api/rtp_parameters.h"
@@ -76,6 +77,8 @@
 namespace {
 
 class DefaultConnector {
+  USING_FAST_MALLOC(DefaultConnector);
+
  public:
   DefaultConnector() {
     service_manager::mojom::ConnectorRequest request;
diff --git a/third_party/blink/renderer/platform/exported/web_url_request.cc b/third_party/blink/renderer/platform/exported/web_url_request.cc
index 53bbb5918..cf4833f 100644
--- a/third_party/blink/renderer/platform/exported/web_url_request.cc
+++ b/third_party/blink/renderer/platform/exported/web_url_request.cc
@@ -148,8 +148,8 @@
   return resource_request_->HttpMethod();
 }
 
-void WebURLRequest::SetHTTPMethod(const WebString& http_method) {
-  resource_request_->SetHTTPMethod(http_method);
+void WebURLRequest::SetHttpMethod(const WebString& http_method) {
+  resource_request_->SetHttpMethod(http_method);
 }
 
 WebString WebURLRequest::HttpHeaderField(const WebString& name) const {
diff --git a/third_party/blink/renderer/platform/fonts/font_fallback_iterator.h b/third_party/blink/renderer/platform/fonts/font_fallback_iterator.h
index 646d5f0b..9ef4bed 100644
--- a/third_party/blink/renderer/platform/fonts/font_fallback_iterator.h
+++ b/third_party/blink/renderer/platform/fonts/font_fallback_iterator.h
@@ -8,6 +8,7 @@
 #include "base/memory/scoped_refptr.h"
 #include "third_party/blink/renderer/platform/fonts/font_data_for_range_set.h"
 #include "third_party/blink/renderer/platform/fonts/font_fallback_priority.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
 #include "third_party/blink/renderer/platform/wtf/hash_map.h"
 #include "third_party/blink/renderer/platform/wtf/ref_counted.h"
 #include "third_party/blink/renderer/platform/wtf/text/unicode.h"
@@ -20,6 +21,8 @@
 class SimpleFontData;
 
 class FontFallbackIterator : public RefCounted<FontFallbackIterator> {
+  USING_FAST_MALLOC(FontFallbackIterator);
+
  public:
   static scoped_refptr<FontFallbackIterator> Create(const FontDescription&,
                                              scoped_refptr<FontFallbackList>,
diff --git a/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_font_cache.h b/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_font_cache.h
index d3ad8a1..5826f0a 100644
--- a/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_font_cache.h
+++ b/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_font_cache.h
@@ -37,6 +37,8 @@
 // FIXME, crbug.com/609099: We should fix the FontCache to only keep one
 // FontPlatformData object independent of size, then consider using this here.
 class HbFontCacheEntry : public RefCounted<HbFontCacheEntry> {
+  USING_FAST_MALLOC(HbFontCacheEntry);
+
  public:
   static scoped_refptr<HbFontCacheEntry> Create(hb_font_t* hb_font);
 
diff --git a/third_party/blink/renderer/platform/geometry/float_polygon.h b/third_party/blink/renderer/platform/geometry/float_polygon.h
index 96af011..a7b3fbb 100644
--- a/third_party/blink/renderer/platform/geometry/float_polygon.h
+++ b/third_party/blink/renderer/platform/geometry/float_polygon.h
@@ -77,6 +77,8 @@
 };
 
 class PLATFORM_EXPORT VertexPair {
+  DISALLOW_NEW();
+
  public:
   virtual ~VertexPair() = default;
 
diff --git a/third_party/blink/renderer/platform/graphics/color_behavior.h b/third_party/blink/renderer/platform/graphics/color_behavior.h
index 021c70f..a0cf331 100644
--- a/third_party/blink/renderer/platform/graphics/color_behavior.h
+++ b/third_party/blink/renderer/platform/graphics/color_behavior.h
@@ -6,11 +6,14 @@
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_COLOR_BEHAVIOR_H_
 
 #include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
 #include "ui/gfx/color_space.h"
 
 namespace blink {
 
 class PLATFORM_EXPORT ColorBehavior {
+  DISALLOW_NEW();
+
  public:
   // This specifies to ignore color profiles embedded in images entirely. No
   // transformations will be applied to any pixel data, and no SkImages will be
diff --git a/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.h b/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.h
index e786820..58cd1f8f 100644
--- a/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.h
+++ b/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.h
@@ -7,6 +7,7 @@
 
 #include "base/macros.h"
 #include "third_party/blink/renderer/platform/graphics/compositor_element_id.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
 #include "third_party/blink/renderer/platform/wtf/hash_map.h"
 #include "third_party/blink/renderer/platform/wtf/hash_set.h"
 #include "third_party/blink/renderer/platform/wtf/vector.h"
@@ -42,6 +43,7 @@
 // Mutates a cc property tree to reflect Blink paint property tree
 // state. Intended for use by PaintArtifactCompositor.
 class PropertyTreeManager {
+  DISALLOW_NEW();
 
  public:
   PropertyTreeManager(PropertyTreeManagerClient&);
diff --git a/third_party/blink/renderer/platform/graphics/contiguous_container_test.cc b/third_party/blink/renderer/platform/graphics/contiguous_container_test.cc
index fa48ea7..06ab6dd4 100644
--- a/third_party/blink/renderer/platform/graphics/contiguous_container_test.cc
+++ b/third_party/blink/renderer/platform/graphics/contiguous_container_test.cc
@@ -354,6 +354,8 @@
   // GMock mock objects (e.g. MockDestructible) aren't guaranteed to be safe
   // to memcpy (which is required for appendByMoving).
   class DestructionNotifier {
+    USING_FAST_MALLOC(DestructionNotifier);
+
    public:
     DestructionNotifier(bool* flag = nullptr) : flag_(flag) {}
     ~DestructionNotifier() {
diff --git a/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.h b/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.h
index 5997783..09e2948 100644
--- a/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.h
+++ b/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.h
@@ -44,6 +44,7 @@
 #include "third_party/blink/renderer/platform/graphics/gpu/webgl_image_conversion.h"
 #include "third_party/blink/renderer/platform/graphics/graphics_types_3d.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
 #include "third_party/blink/renderer/platform/wtf/deque.h"
 #include "third_party/blink/renderer/platform/wtf/ref_counted.h"
 #include "third_party/blink/renderer/platform/wtf/vector.h"
@@ -249,6 +250,8 @@
   // when the DrawingBuffer is using a CHROMIUM image for its backing
   // store and RGB emulation is in use (basically, macOS only).
   class PLATFORM_EXPORT ScopedRGBEmulationForBlitFramebuffer {
+    STACK_ALLOCATED();
+
    public:
     ScopedRGBEmulationForBlitFramebuffer(DrawingBuffer*,
                                          bool is_user_draw_framebuffer_bound);
@@ -299,6 +302,8 @@
   // This structure should wrap all public entrypoints that may modify GL state.
   // It will restore all state when it drops out of scope.
   class ScopedStateRestorer {
+    USING_FAST_MALLOC(ScopedStateRestorer);
+
    public:
     ScopedStateRestorer(DrawingBuffer*);
     ~ScopedStateRestorer();
diff --git a/third_party/blink/renderer/platform/graphics/gpu/graphics_context_3d_utils.h b/third_party/blink/renderer/platform/graphics/gpu/graphics_context_3d_utils.h
index 54b117b3..4b96832 100644
--- a/third_party/blink/renderer/platform/graphics/gpu/graphics_context_3d_utils.h
+++ b/third_party/blink/renderer/platform/graphics/gpu/graphics_context_3d_utils.h
@@ -9,6 +9,7 @@
 #include "gpu/command_buffer/common/mailbox.h"
 #include "gpu/command_buffer/common/sync_token.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
 #include "third_party/blink/renderer/platform/wtf/hash_map.h"
 #include "third_party/skia/include/core/SkImage.h"
 #include "third_party/skia/include/gpu/GrTexture.h"
@@ -20,6 +21,8 @@
 class WebGraphicsContext3DProviderWrapper;
 
 class PLATFORM_EXPORT GraphicsContext3DUtils {
+  USING_FAST_MALLOC(GraphicsContext3DUtils);
+
  public:
   // The constructor takes a weak ref to the wrapper because it internally
   // it generates callbacks that may outlive the wrapper.
diff --git a/third_party/blink/renderer/platform/graphics/image_data_buffer.h b/third_party/blink/renderer/platform/graphics/image_data_buffer.h
index 9dc4c9a9b..5474eb3 100644
--- a/third_party/blink/renderer/platform/graphics/image_data_buffer.h
+++ b/third_party/blink/renderer/platform/graphics/image_data_buffer.h
@@ -35,6 +35,7 @@
 #include "third_party/blink/renderer/platform/graphics/graphics_types.h"
 #include "third_party/blink/renderer/platform/graphics/static_bitmap_image.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
 #include "third_party/blink/renderer/platform/wtf/typed_arrays/uint8_clamped_array.h"
 #include "third_party/blink/renderer/platform/wtf/vector.h"
@@ -43,6 +44,8 @@
 namespace blink {
 
 class PLATFORM_EXPORT ImageDataBuffer {
+  USING_FAST_MALLOC(ImageDataBuffer);
+
  public:
   static std::unique_ptr<ImageDataBuffer> Create(
       scoped_refptr<StaticBitmapImage>);
diff --git a/third_party/blink/renderer/platform/graphics/paint/paint_chunk_subset.h b/third_party/blink/renderer/platform/graphics/paint/paint_chunk_subset.h
index b775ee0..9b34b18 100644
--- a/third_party/blink/renderer/platform/graphics/paint/paint_chunk_subset.h
+++ b/third_party/blink/renderer/platform/graphics/paint/paint_chunk_subset.h
@@ -5,6 +5,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_PAINT_CHUNK_SUBSET_H_
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_PAINT_CHUNK_SUBSET_H_
 
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
 #include "third_party/blink/renderer/platform/wtf/vector.h"
 
 namespace blink {
@@ -13,6 +14,8 @@
 
 // Provides access to a subset of a Vector<PaintChunk>.
 class PaintChunkSubset {
+  DISALLOW_NEW();
+
  public:
   PaintChunkSubset(const Vector<PaintChunk>& chunks,
                    const Vector<wtf_size_t>& subset_indices)
diff --git a/third_party/blink/renderer/platform/graphics/paint/paint_property_node.h b/third_party/blink/renderer/platform/graphics/paint/paint_property_node.h
index 5b23cc2..2980189 100644
--- a/third_party/blink/renderer/platform/graphics/paint/paint_property_node.h
+++ b/third_party/blink/renderer/platform/graphics/paint/paint_property_node.h
@@ -10,6 +10,7 @@
 #include "base/memory/scoped_refptr.h"
 #include "third_party/blink/renderer/platform/json/json_values.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
 #include "third_party/blink/renderer/platform/wtf/ref_counted.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
 
@@ -87,6 +88,8 @@
 
 template <typename NodeType>
 class PaintPropertyNode : public RefCounted<NodeType> {
+  USING_FAST_MALLOC(PaintPropertyNode);
+
  public:
   // Parent property node, or nullptr if this is the root node.
   const NodeType* Parent() const { return parent_.get(); }
diff --git a/third_party/blink/renderer/platform/graphics/paint/raster_invalidator.h b/third_party/blink/renderer/platform/graphics/paint/raster_invalidator.h
index dd9aa8b..05b2205 100644
--- a/third_party/blink/renderer/platform/graphics/paint/raster_invalidator.h
+++ b/third_party/blink/renderer/platform/graphics/paint/raster_invalidator.h
@@ -10,6 +10,7 @@
 #include "third_party/blink/renderer/platform/graphics/paint/paint_chunk.h"
 #include "third_party/blink/renderer/platform/graphics/paint/paint_chunk_subset.h"
 #include "third_party/blink/renderer/platform/graphics/paint/raster_invalidation_tracking.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
 #include "third_party/blink/renderer/platform/wtf/hash_map.h"
 #include "third_party/blink/renderer/platform/wtf/vector.h"
 
@@ -19,6 +20,8 @@
 class IntRect;
 
 class PLATFORM_EXPORT RasterInvalidator {
+  USING_FAST_MALLOC(RasterInvalidator);
+
  public:
   using RasterInvalidationFunction = std::function<void(const IntRect&)>;
 
diff --git a/third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.h b/third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.h
index 7122d30..a9a6eac 100644
--- a/third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.h
+++ b/third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.h
@@ -16,6 +16,7 @@
 #include "third_party/blink/renderer/platform/graphics/paint/scroll_paint_property_node.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
 #include "third_party/blink/renderer/platform/transforms/transformation_matrix.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
 
 namespace blink {
 
@@ -46,6 +47,8 @@
   // Stores a transform and origin with an optimization for the identity and
   // 2d translation cases that avoids allocating a full matrix and origin.
   class TransformAndOrigin {
+    DISALLOW_NEW();
+
    public:
     TransformAndOrigin() {}
     // These constructors are not explicit so that we can use FloatSize or
diff --git a/third_party/blink/renderer/platform/graphics/picture_snapshot.h b/third_party/blink/renderer/platform/graphics/picture_snapshot.h
index c15f336..861bc8785 100644
--- a/third_party/blink/renderer/platform/graphics/picture_snapshot.h
+++ b/third_party/blink/renderer/platform/graphics/picture_snapshot.h
@@ -37,6 +37,7 @@
 #include "third_party/blink/renderer/platform/graphics/graphics_context.h"
 #include "third_party/blink/renderer/platform/json/json_values.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
 #include "third_party/blink/renderer/platform/wtf/ref_counted.h"
 #include "third_party/blink/renderer/platform/wtf/time.h"
 #include "third_party/skia/include/core/SkPicture.h"
@@ -48,6 +49,8 @@
 class FloatRect;
 
 class PLATFORM_EXPORT PictureSnapshot : public RefCounted<PictureSnapshot> {
+  USING_FAST_MALLOC(PictureSnapshot);
+
  public:
   struct TilePictureStream : RefCounted<TilePictureStream> {
     FloatPoint layer_offset;
diff --git a/third_party/blink/renderer/platform/graphics/scoped_interpolation_quality.h b/third_party/blink/renderer/platform/graphics/scoped_interpolation_quality.h
index 1cdc020b..7e7db86 100644
--- a/third_party/blink/renderer/platform/graphics/scoped_interpolation_quality.h
+++ b/third_party/blink/renderer/platform/graphics/scoped_interpolation_quality.h
@@ -7,6 +7,7 @@
 
 #include "third_party/blink/renderer/platform/graphics/graphics_context.h"
 #include "third_party/blink/renderer/platform/graphics/graphics_types.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
 
 namespace blink {
 
@@ -16,6 +17,8 @@
 // Save/Restore methods) to ensure the restoration behavior is the expected
 // one.
 class ScopedInterpolationQuality {
+  STACK_ALLOCATED();
+
  public:
   ScopedInterpolationQuality(GraphicsContext& context,
                              InterpolationQuality interpolation_quality)
diff --git a/third_party/blink/renderer/platform/graphics/squashing_disallowed_reasons.h b/third_party/blink/renderer/platform/graphics/squashing_disallowed_reasons.h
index aa61d19..a3d986d7 100644
--- a/third_party/blink/renderer/platform/graphics/squashing_disallowed_reasons.h
+++ b/third_party/blink/renderer/platform/graphics/squashing_disallowed_reasons.h
@@ -33,6 +33,8 @@
   V(MaskMismatch)
 
 class PLATFORM_EXPORT SquashingDisallowedReason {
+  DISALLOW_NEW();
+
  private:
   // This contains ordinal values for squashing disallowed reasons and will be
   // used to generate the squashing disallowed reason bits.
diff --git a/third_party/blink/renderer/platform/graphics/web_graphics_context_3d_provider_wrapper.h b/third_party/blink/renderer/platform/graphics/web_graphics_context_3d_provider_wrapper.h
index 93a0854b..1bb689c4 100644
--- a/third_party/blink/renderer/platform/graphics/web_graphics_context_3d_provider_wrapper.h
+++ b/third_party/blink/renderer/platform/graphics/web_graphics_context_3d_provider_wrapper.h
@@ -14,10 +14,13 @@
 #include "third_party/blink/public/platform/web_graphics_context_3d_provider.h"
 #include "third_party/blink/renderer/platform/graphics/gpu/graphics_context_3d_utils.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
 
 namespace blink {
 
 class PLATFORM_EXPORT WebGraphicsContext3DProviderWrapper {
+  USING_FAST_MALLOC(WebGraphicsContext3DProviderWrapper);
+
  public:
   class DestructionObserver {
    public:
diff --git a/third_party/blink/renderer/platform/heap/heap_thread_test.cc b/third_party/blink/renderer/platform/heap/heap_thread_test.cc
index 7eab071..989478ea 100644
--- a/third_party/blink/renderer/platform/heap/heap_thread_test.cc
+++ b/third_party/blink/renderer/platform/heap/heap_thread_test.cc
@@ -12,6 +12,7 @@
 #include "third_party/blink/renderer/platform/heap/thread_state.h"
 #include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h"
 #include "third_party/blink/renderer/platform/scheduler/public/thread.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
 
 namespace blink {
 namespace heap_thread_test {
@@ -69,6 +70,8 @@
 };
 
 class AlternatingThreadTester {
+  STACK_ALLOCATED();
+
  public:
   void Test() {
     MutexLocker locker(ActiveThreadMutex());
diff --git a/third_party/blink/renderer/platform/heap/incremental_marking_test.cc b/third_party/blink/renderer/platform/heap/incremental_marking_test.cc
index b947057..767cab6 100644
--- a/third_party/blink/renderer/platform/heap/incremental_marking_test.cc
+++ b/third_party/blink/renderer/platform/heap/incremental_marking_test.cc
@@ -18,7 +18,7 @@
 #include "third_party/blink/renderer/platform/heap/thread_state.h"
 #include "third_party/blink/renderer/platform/heap/trace_traits.h"
 #include "third_party/blink/renderer/platform/heap/visitor.h"
-
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
 namespace blink {
 namespace incremental_marking_test {
 
@@ -77,6 +77,8 @@
 
 // Base class for initializing worklists.
 class IncrementalMarkingScopeBase {
+  DISALLOW_NEW();
+
  public:
   explicit IncrementalMarkingScopeBase(ThreadState* thread_state)
       : thread_state_(thread_state), heap_(thread_state_->Heap()) {
diff --git a/third_party/blink/renderer/platform/heap/object_start_bitmap_test.cc b/third_party/blink/renderer/platform/heap/object_start_bitmap_test.cc
index b9a8dc8..0fc3752 100644
--- a/third_party/blink/renderer/platform/heap/object_start_bitmap_test.cc
+++ b/third_party/blink/renderer/platform/heap/object_start_bitmap_test.cc
@@ -5,6 +5,7 @@
 #include "third_party/blink/renderer/platform/heap/heap_page.h"
 
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
 
 namespace blink {
 
@@ -19,6 +20,8 @@
 // Abstraction for objects that hides ObjectStartBitmap::kGranularity and
 // the base address as getting either of it wrong will result in failed DCHECKs.
 class Object {
+  STACK_ALLOCATED();
+
  public:
   static Address kBaseOffset;
 
diff --git a/third_party/blink/renderer/platform/heap/persistent_node.h b/third_party/blink/renderer/platform/heap/persistent_node.h
index 51df614..50c6f4fff 100644
--- a/third_party/blink/renderer/platform/heap/persistent_node.h
+++ b/third_party/blink/renderer/platform/heap/persistent_node.h
@@ -109,6 +109,8 @@
 template <ThreadAffinity affinity,
           WeaknessPersistentConfiguration weakness_configuration>
 class PersistentNodePtr {
+  STACK_ALLOCATED();
+
  public:
   PersistentNode* Get() const { return ptr_; }
   bool IsInitialized() const { return ptr_; }
@@ -128,6 +130,8 @@
 // but can be polled to see whether it is initialized without the mutex.
 template <WeaknessPersistentConfiguration weakness_configuration>
 class CrossThreadPersistentNodePtr {
+  STACK_ALLOCATED();
+
  public:
   PersistentNode* Get() const {
 #if DCHECK_IS_ON()
diff --git a/third_party/blink/renderer/platform/histogram.h b/third_party/blink/renderer/platform/histogram.h
index 0df2eae6..0f0a6cc7 100644
--- a/third_party/blink/renderer/platform/histogram.h
+++ b/third_party/blink/renderer/platform/histogram.h
@@ -10,6 +10,7 @@
 #include "base/metrics/histogram_macros.h"
 #include "base/time/tick_clock.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
 #include "third_party/blink/renderer/platform/wtf/time.h"
 
 namespace base {
@@ -19,6 +20,8 @@
 namespace blink {
 
 class PLATFORM_EXPORT CustomCountHistogram {
+  USING_FAST_MALLOC(CustomCountHistogram);
+
  public:
   // Min values should be >=1 as emitted 0s still go into the underflow bucket.
   CustomCountHistogram(const char* name,
@@ -48,6 +51,8 @@
 };
 
 class PLATFORM_EXPORT SparseHistogram {
+  USING_FAST_MALLOC(SparseHistogram);
+
  public:
   explicit SparseHistogram(const char* name);
 
@@ -66,6 +71,8 @@
 };
 
 class PLATFORM_EXPORT ScopedUsHistogramTimer {
+  USING_FAST_MALLOC(ScopedUsHistogramTimer);
+
  public:
   explicit ScopedUsHistogramTimer(CustomCountHistogram& counter)
       : start_time_(CurrentTimeTicks()), counter_(counter) {}
@@ -80,6 +87,8 @@
 };
 
 class PLATFORM_EXPORT ScopedHighResUsHistogramTimer {
+  USING_FAST_MALLOC(ScopedUsHistogramTimer);
+
  public:
   explicit ScopedHighResUsHistogramTimer(CustomCountHistogram& counter)
       : start_time_(CurrentTimeTicks()), counter_(counter) {}
diff --git a/third_party/blink/renderer/platform/loader/fetch/memory_cache_correctness_test.cc b/third_party/blink/renderer/platform/loader/fetch/memory_cache_correctness_test.cc
index c986aa2..da5b41af9 100644
--- a/third_party/blink/renderer/platform/loader/fetch/memory_cache_correctness_test.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/memory_cache_correctness_test.cc
@@ -452,7 +452,7 @@
 
 TEST_F(MemoryCacheCorrectnessTest, PostToSameURLTwice) {
   ResourceRequest request1{KURL(kResourceURL)};
-  request1.SetHTTPMethod(http_names::kPOST);
+  request1.SetHttpMethod(http_names::kPOST);
   request1.SetRequestorOrigin(GetSecurityOrigin());
   RawResource* resource1 =
       RawResource::CreateForTest(request1, ResourceType::kRaw);
@@ -460,7 +460,7 @@
   AddResourceToMemoryCache(resource1);
 
   ResourceRequest request2{KURL(kResourceURL)};
-  request2.SetHTTPMethod(http_names::kPOST);
+  request2.SetHttpMethod(http_names::kPOST);
   request2.SetRequestorOrigin(GetSecurityOrigin());
   FetchParameters fetch2(request2);
   RawResource* resource2 = RawResource::FetchSynchronously(fetch2, Fetcher());
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_request.cc b/third_party/blink/renderer/platform/loader/fetch/resource_request.cc
index 37c61db..ffa2bad 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_request.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_request.cc
@@ -97,7 +97,7 @@
   std::unique_ptr<ResourceRequest> request =
       std::make_unique<ResourceRequest>(new_url);
   request->SetRequestorOrigin(RequestorOrigin());
-  request->SetHTTPMethod(new_method);
+  request->SetHttpMethod(new_method);
   request->SetSiteForCookies(new_site_for_cookies);
   request->SetTopFrameOrigin(std::move(new_top_frame_origin));
   String referrer =
@@ -199,7 +199,7 @@
   return http_method_;
 }
 
-void ResourceRequest::SetHTTPMethod(const AtomicString& http_method) {
+void ResourceRequest::SetHttpMethod(const AtomicString& http_method) {
   http_method_ = http_method;
 }
 
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_request.h b/third_party/blink/renderer/platform/loader/fetch/resource_request.h
index f79a3ead..18e5b4caa 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_request.h
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_request.h
@@ -130,7 +130,7 @@
   }
 
   const AtomicString& HttpMethod() const;
-  void SetHTTPMethod(const AtomicString&);
+  void SetHttpMethod(const AtomicString&);
 
   const HTTPHeaderMap& HttpHeaderFields() const;
   const AtomicString& HttpHeaderField(const AtomicString& name) const;
diff --git a/third_party/blink/renderer/platform/mac/local_current_graphics_context.h b/third_party/blink/renderer/platform/mac/local_current_graphics_context.h
index cd0708d..32e20aa 100644
--- a/third_party/blink/renderer/platform/mac/local_current_graphics_context.h
+++ b/third_party/blink/renderer/platform/mac/local_current_graphics_context.h
@@ -24,6 +24,7 @@
 #include "third_party/blink/renderer/platform/geometry/int_rect.h"
 #include "third_party/blink/renderer/platform/mac/graphics_context_canvas.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
 
 @class NSGraphicsContext;
 
@@ -37,6 +38,8 @@
 // This class automatically saves and restores the current NSGraphicsContext for
 // functions which call out into AppKit and rely on the currentContext being set
 class PLATFORM_EXPORT LocalCurrentGraphicsContext {
+  STACK_ALLOCATED();
+
  public:
   LocalCurrentGraphicsContext(GraphicsContext&, const IntRect& dirty_rect);
   LocalCurrentGraphicsContext(cc::PaintCanvas*,
diff --git a/third_party/blink/renderer/platform/mojo/interface_invalidator.h b/third_party/blink/renderer/platform/mojo/interface_invalidator.h
index 50028aa..00d90f31 100644
--- a/third_party/blink/renderer/platform/mojo/interface_invalidator.h
+++ b/third_party/blink/renderer/platform/mojo/interface_invalidator.h
@@ -9,12 +9,15 @@
 #include "base/memory/weak_ptr.h"
 #include "base/observer_list.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
 
 namespace blink {
 
 // Notifies weak interface bindings to be invalidated when this object is
 // destroyed.
 class PLATFORM_EXPORT InterfaceInvalidator {
+  USING_FAST_MALLOC(InterfaceInvalidator);
+
  public:
   InterfaceInvalidator();
   ~InterfaceInvalidator();
diff --git a/third_party/blink/renderer/platform/network/content_security_policy_response_headers.h b/third_party/blink/renderer/platform/network/content_security_policy_response_headers.h
index 7bab26f..ff1c629 100644
--- a/third_party/blink/renderer/platform/network/content_security_policy_response_headers.h
+++ b/third_party/blink/renderer/platform/network/content_security_policy_response_headers.h
@@ -37,6 +37,8 @@
 class HTTPHeaderMap;
 
 class PLATFORM_EXPORT ContentSecurityPolicyResponseHeaders final {
+  STACK_ALLOCATED();
+
  public:
   ContentSecurityPolicyResponseHeaders() = default;
   explicit ContentSecurityPolicyResponseHeaders(const ResourceResponse&);
diff --git a/third_party/blink/renderer/platform/scheduler/common/metrics_helper.h b/third_party/blink/renderer/platform/scheduler/common/metrics_helper.h
index d4f0cc53..27bbd73 100644
--- a/third_party/blink/renderer/platform/scheduler/common/metrics_helper.h
+++ b/third_party/blink/renderer/platform/scheduler/common/metrics_helper.h
@@ -13,6 +13,7 @@
 #include "components/scheduling_metrics/total_duration_metric_reporter.h"
 #include "third_party/blink/public/platform/web_thread_type.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
 
 namespace base {
 namespace sequence_manager {
@@ -35,6 +36,8 @@
 // Note that this is code reuse, not data reuse -- each thread should have its
 // own instantiation of this class.
 class PLATFORM_EXPORT MetricsHelper {
+  DISALLOW_NEW();
+
  public:
   MetricsHelper(WebThreadType thread_type, bool has_cpu_timing_for_each_task);
   ~MetricsHelper();
diff --git a/third_party/blink/renderer/platform/scheduler/common/pollable_thread_safe_flag.h b/third_party/blink/renderer/platform/scheduler/common/pollable_thread_safe_flag.h
index 5a8d1b1..35e1067 100644
--- a/third_party/blink/renderer/platform/scheduler/common/pollable_thread_safe_flag.h
+++ b/third_party/blink/renderer/platform/scheduler/common/pollable_thread_safe_flag.h
@@ -8,6 +8,7 @@
 #include "base/atomicops.h"
 #include "base/macros.h"
 #include "base/synchronization/lock.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
 
 // A PollableThreadSafeFlag can be polled without requiring a lock, but can only
 // be updated if a lock is held. This enables lock-free checking as to whether a
@@ -16,6 +17,8 @@
 // lock-protected critical section as any other variables on which the condition
 // depends.
 class PollableThreadSafeFlag {
+  DISALLOW_NEW();
+
  public:
   explicit PollableThreadSafeFlag(base::Lock* write_lock);
   ~PollableThreadSafeFlag();
diff --git a/third_party/blink/renderer/platform/scheduler/common/post_cancellable_task_unittest.cc b/third_party/blink/renderer/platform/scheduler/common/post_cancellable_task_unittest.cc
index f9ad74fc..1c2e4228 100644
--- a/third_party/blink/renderer/platform/scheduler/common/post_cancellable_task_unittest.cc
+++ b/third_party/blink/renderer/platform/scheduler/common/post_cancellable_task_unittest.cc
@@ -7,6 +7,7 @@
 #include "base/memory/weak_ptr.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/renderer/platform/scheduler/test/fake_task_runner.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
 #include "third_party/blink/renderer/platform/wtf/functional.h"
 
 namespace blink {
@@ -21,6 +22,8 @@
 }
 
 class CancellationTestHelper {
+  DISALLOW_NEW();
+
  public:
   CancellationTestHelper() : weak_ptr_factory_(this) {}
 
diff --git a/third_party/blink/renderer/platform/scheduler/common/tracing_helper.h b/third_party/blink/renderer/platform/scheduler/common/tracing_helper.h
index e35aeb6..acbced1 100644
--- a/third_party/blink/renderer/platform/scheduler/common/tracing_helper.h
+++ b/third_party/blink/renderer/platform/scheduler/common/tracing_helper.h
@@ -13,6 +13,7 @@
 #include "base/time/time.h"
 #include "base/trace_event/trace_event.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
 
 namespace blink {
@@ -50,6 +51,8 @@
 // wouldn't be helpful in our case because removing one takes linear time
 // and tracers may be created and disposed frequently.
 class PLATFORM_EXPORT TraceableVariableController {
+  DISALLOW_NEW();
+
  public:
   TraceableVariableController();
   ~TraceableVariableController();
@@ -87,6 +90,8 @@
 
 template <const char* category>
 class StateTracer {
+  DISALLOW_NEW();
+
  public:
   StateTracer(const char* name, const void* object)
       : name_(name), object_(object), slice_is_open_(false) {
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.cc b/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.cc
index 4ae82ef..78cac46 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.cc
@@ -45,9 +45,17 @@
 // The amount of time to wait before suspending shared timers, and loading
 // etc. after the renderer has been backgrounded. This is used only if
 // background suspension is enabled.
-constexpr base::TimeDelta kDelayForBackgroundTabFreezing =
+constexpr base::TimeDelta kDefaultDelayForBackgroundTabFreezing =
     base::TimeDelta::FromMinutes(5);
 
+// The amount of time to wait before checking network idleness
+// after the page has been backgrounded. If network is idle,
+// suspend shared timers, and loading etc. This is used only if
+// freeze-background-tab-on-network-idle feature is enabled.
+// This value should be smaller than kDefaultDelayForBackgroundTabFreezing.
+constexpr base::TimeDelta kDefaultDelayForBackgroundAndNetworkIdleTabFreezing =
+    base::TimeDelta::FromMinutes(1);
+
 // Values coming from the field trial config are interpreted as follows:
 //   -1 is "not set". Scheduler should use a reasonable default.
 //   0 corresponds to base::nullopt.
@@ -107,6 +115,25 @@
   return settings;
 }
 
+base::TimeDelta GetDelayForBackgroundTabFreezing() {
+  static const base::FeatureParam<int> kDelayForBackgroundTabFreezingMillis{
+      &features::kStopInBackground, "DelayForBackgroundTabFreezingMills",
+      static_cast<int>(kDefaultDelayForBackgroundTabFreezing.InMilliseconds())};
+  return base::TimeDelta::FromMilliseconds(
+      kDelayForBackgroundTabFreezingMillis.Get());
+}
+
+base::TimeDelta GetDelayForBackgroundAndNetworkIdleTabFreezing() {
+  static const base::FeatureParam<int>
+      kDelayForBackgroundAndNetworkIdleTabFreezingMillis{
+          &features::kFreezeBackgroundTabOnNetworkIdle,
+          "DelayForBackgroundAndNetworkIdleTabFreezingMills",
+          static_cast<int>(kDefaultDelayForBackgroundAndNetworkIdleTabFreezing
+                               .InMilliseconds())};
+  return base::TimeDelta::FromMilliseconds(
+      kDelayForBackgroundAndNetworkIdleTabFreezingMillis.Get());
+}
+
 }  // namespace
 
 PageSchedulerImpl::PageSchedulerImpl(
@@ -114,6 +141,8 @@
     MainThreadSchedulerImpl* main_thread_scheduler)
     : main_thread_scheduler_(main_thread_scheduler),
       page_visibility_(kDefaultPageVisibility),
+      page_visibility_changed_time_(
+          main_thread_scheduler->GetTickClock()->NowTicks()),
       audio_state_(AudioState::kSilent),
       is_frozen_(false),
       reported_background_throttling_since_navigation_(false),
@@ -124,6 +153,11 @@
       keep_active_(main_thread_scheduler->SchedulerKeepActive()),
       background_time_budget_pool_(nullptr),
       delegate_(delegate),
+      delay_for_background_tab_freezing_(GetDelayForBackgroundTabFreezing()),
+      freeze_on_network_idle_enabled_(base::FeatureList::IsEnabled(
+          blink::features::kFreezeBackgroundTabOnNetworkIdle)),
+      delay_for_background_and_network_idle_tab_freezing_(
+          GetDelayForBackgroundAndNetworkIdleTabFreezing()),
       weak_factory_(this) {
   page_lifecycle_state_tracker_.reset(new PageLifecycleStateTracker(
       this, kDefaultPageVisibility == PageVisibilityState::kVisible
@@ -136,17 +170,6 @@
       &PageSchedulerImpl::OnAudioSilent, base::Unretained(this)));
   do_freeze_page_callback_.Reset(base::BindRepeating(
       &PageSchedulerImpl::DoFreezePage, base::Unretained(this)));
-
-  int32_t delay_for_background_tab_freezing_millis;
-  if (base::StringToInt(
-          base::GetFieldTrialParamValue("BackgroundTabFreezing",
-                                        "DelayForBackgroundTabFreezingMills"),
-          &delay_for_background_tab_freezing_millis)) {
-    delay_for_background_tab_freezing_ = base::TimeDelta::FromMilliseconds(
-        delay_for_background_tab_freezing_millis);
-  } else {
-    delay_for_background_tab_freezing_ = kDelayForBackgroundTabFreezing;
-  }
 }
 
 PageSchedulerImpl::~PageSchedulerImpl() {
@@ -174,6 +197,8 @@
   if (page_visibility_ == page_visibility)
     return;
   page_visibility_ = page_visibility;
+  page_visibility_changed_time_ =
+      main_thread_scheduler_->GetTickClock()->NowTicks();
 
   switch (page_visibility_) {
     case PageVisibilityState::kVisible:
@@ -192,7 +217,9 @@
   if (ShouldFreezePage()) {
     main_thread_scheduler_->ControlTaskRunner()->PostDelayedTask(
         FROM_HERE, do_freeze_page_callback_.GetCallback(),
-        delay_for_background_tab_freezing_);
+        freeze_on_network_idle_enabled_
+            ? delay_for_background_and_network_idle_tab_freezing_
+            : delay_for_background_tab_freezing_);
   }
 
   for (FrameSchedulerImpl* frame_scheduler : frame_schedulers_)
@@ -392,7 +419,9 @@
   if (ShouldFreezePage()) {
     main_thread_scheduler_->ControlTaskRunner()->PostDelayedTask(
         FROM_HERE, do_freeze_page_callback_.GetCallback(),
-        delay_for_background_tab_freezing_);
+        freeze_on_network_idle_enabled_
+            ? delay_for_background_and_network_idle_tab_freezing_
+            : delay_for_background_tab_freezing_);
   }
 }
 
@@ -606,8 +635,46 @@
   return IsBackgrounded();
 }
 
+void PageSchedulerImpl::OnLocalMainFrameNetworkAlmostIdle() {
+  if (!freeze_on_network_idle_enabled_)
+    return;
+
+  if (!ShouldFreezePage())
+    return;
+
+  if (IsFrozen())
+    return;
+
+  // If delay_for_background_and_network_idle_tab_freezing_ passes after
+  // the page is not visible, we should freeze the page.
+  TimeDelta passed = main_thread_scheduler_->GetTickClock()->NowTicks() -
+                     page_visibility_changed_time_;
+  if (passed < delay_for_background_and_network_idle_tab_freezing_)
+    return;
+
+  SetPageFrozenImpl(true, NotificationPolicy::kNotifyFrames);
+}
+
 void PageSchedulerImpl::DoFreezePage() {
   DCHECK(ShouldFreezePage());
+
+  if (freeze_on_network_idle_enabled_) {
+    DCHECK(delegate_);
+    TimeDelta passed = main_thread_scheduler_->GetTickClock()->NowTicks() -
+                       page_visibility_changed_time_;
+    // The page will be frozen if:
+    // (1) the main frame is remote, or,
+    // (2) the local main frame's network is almost idle, or,
+    // (3) delay_for_background_tab passes after the page is not visible.
+    if (!delegate_->LocalMainFrameNetworkIsAlmostIdle() &&
+        passed < delay_for_background_tab_freezing_) {
+      main_thread_scheduler_->ControlTaskRunner()->PostDelayedTask(
+          FROM_HERE, do_freeze_page_callback_.GetCallback(),
+          delay_for_background_tab_freezing_ - passed);
+      return;
+    }
+  }
+
   SetPageFrozenImpl(true, NotificationPolicy::kNotifyFrames);
 }
 
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.h b/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.h
index 4d99d82..d27eaa0 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.h
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.h
@@ -56,6 +56,7 @@
   void SetKeepActive(bool) override;
   bool IsMainFrameLocal() const override;
   void SetIsMainFrameLocal(bool is_local) override;
+  void OnLocalMainFrameNetworkAlmostIdle() override;
 
   std::unique_ptr<FrameScheduler> CreateFrameScheduler(
       FrameScheduler::Delegate* delegate,
@@ -243,6 +244,7 @@
   MainThreadSchedulerImpl* main_thread_scheduler_;
 
   PageVisibilityState page_visibility_;
+  base::TimeTicks page_visibility_changed_time_;
   AudioState audio_state_;
   bool is_frozen_;
   bool reported_background_throttling_since_navigation_;
@@ -256,7 +258,15 @@
   CancelableClosureHolder do_throttle_page_callback_;
   CancelableClosureHolder on_audio_silent_closure_;
   CancelableClosureHolder do_freeze_page_callback_;
-  base::TimeDelta delay_for_background_tab_freezing_;
+  const base::TimeDelta delay_for_background_tab_freezing_;
+
+  // Whether a background page can be frozen before
+  // |delay_for_background_tab_freezing_| if network is idle.
+  const bool freeze_on_network_idle_enabled_;
+
+  // Delay after which a background page can be frozen if network is idle.
+  const base::TimeDelta delay_for_background_and_network_idle_tab_freezing_;
+
   std::unique_ptr<PageLifecycleStateTracker> page_lifecycle_state_tracker_;
   base::WeakPtrFactory<PageSchedulerImpl> weak_factory_;
 
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl_unittest.cc b/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl_unittest.cc
index 0dedb70b..a49cdd3c 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl_unittest.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl_unittest.cc
@@ -49,6 +49,22 @@
 using base::Bucket;
 using testing::UnorderedElementsAreArray;
 
+class MockPageSchedulerDelegate : public PageScheduler::Delegate {
+ public:
+  MockPageSchedulerDelegate() : idle_(false) {}
+
+  void SetLocalMainFrameNetworkIsAlmostIdle(bool idle) { idle_ = idle; }
+  bool LocalMainFrameNetworkIsAlmostIdle() const override { return idle_; }
+
+ private:
+  void ReportIntervention(const WTF::String&) override {}
+  bool RequestBeginMainFrameNotExpected(bool) override { return false; }
+  void SetLifecycleState(PageLifecycleState) override {}
+  bool IsOrdinary() const override { return true; }
+
+  bool idle_;
+};
+
 class PageSchedulerImplTest : public testing::Test {
  public:
   PageSchedulerImplTest() {
@@ -73,7 +89,9 @@
         base::sequence_manager::SequenceManagerForTest::Create(
             nullptr, test_task_runner_, test_task_runner_->GetMockTickClock()),
         base::nullopt));
-    page_scheduler_.reset(new PageSchedulerImpl(nullptr, scheduler_.get()));
+    page_scheduler_delegate_.reset(new MockPageSchedulerDelegate());
+    page_scheduler_.reset(new PageSchedulerImpl(page_scheduler_delegate_.get(),
+                                                scheduler_.get()));
     frame_scheduler_ =
         FrameSchedulerImpl::Create(page_scheduler_.get(), nullptr, nullptr,
                                    FrameScheduler::FrameType::kSubframe);
@@ -104,6 +122,12 @@
     return page_scheduler_->delay_for_background_tab_freezing_;
   }
 
+  base::TimeDelta delay_for_background_and_network_idle_tab_freezing() const {
+    return page_scheduler_->delay_for_background_and_network_idle_tab_freezing_;
+  }
+
+  PageScheduler::Delegate* delegate() { return page_scheduler_->delegate_; }
+
   static base::TimeDelta recent_audio_delay() {
     return PageSchedulerImpl::kRecentAudioDelay;
   }
@@ -210,10 +234,21 @@
     EXPECT_EQ(5, counter);
   }
 
+  bool NetworkIsAlmostIdle() const {
+    return page_scheduler_delegate_->LocalMainFrameNetworkIsAlmostIdle();
+  }
+
+  void NotifyLocalMainFrameNetworkIsAlmostIdle() {
+    EXPECT_FALSE(page_scheduler_delegate_->LocalMainFrameNetworkIsAlmostIdle());
+    page_scheduler_delegate_->SetLocalMainFrameNetworkIsAlmostIdle(true);
+    page_scheduler_->OnLocalMainFrameNetworkAlmostIdle();
+  }
+
   scoped_refptr<base::TestMockTimeTaskRunner> test_task_runner_;
   std::unique_ptr<MainThreadSchedulerImpl> scheduler_;
   std::unique_ptr<PageSchedulerImpl> page_scheduler_;
   std::unique_ptr<FrameSchedulerImpl> frame_scheduler_;
+  std::unique_ptr<MockPageSchedulerDelegate> page_scheduler_delegate_;
 
  private:
   base::test::ScopedFeatureList feature_list_;
@@ -1426,6 +1461,91 @@
   EXPECT_FALSE(page_scheduler_->IsFrozen());
 }
 
+class PageSchedulerImplFreezeBackgroundTabOnNetworkIdleEnabledTest
+    : public PageSchedulerImplTest {
+ public:
+  PageSchedulerImplFreezeBackgroundTabOnNetworkIdleEnabledTest()
+      : PageSchedulerImplTest(
+            {blink::features::kStopInBackground,
+             blink::features::kFreezeBackgroundTabOnNetworkIdle},
+            {}) {}
+};
+
+TEST_F(PageSchedulerImplFreezeBackgroundTabOnNetworkIdleEnabledTest,
+       PageFrozenOnlyOnLocalMainFrameNetworkIdle) {
+  page_scheduler_->SetPageVisible(true);
+  EXPECT_FALSE(ShouldFreezePage());
+  EXPECT_FALSE(page_scheduler_->IsFrozen());
+  EXPECT_FALSE(NetworkIsAlmostIdle());
+
+  // After network is idle, page should freeze after delay for quick
+  // background tab freezing.
+  NotifyLocalMainFrameNetworkIsAlmostIdle();
+  EXPECT_TRUE(NetworkIsAlmostIdle());
+  EXPECT_FALSE(page_scheduler_->IsFrozen());
+  page_scheduler_->SetPageVisible(false);
+  test_task_runner_->FastForwardBy(
+      delay_for_background_and_network_idle_tab_freezing() +
+      base::TimeDelta::FromMilliseconds(100));
+  EXPECT_TRUE(page_scheduler_->IsFrozen());
+}
+
+TEST_F(PageSchedulerImplFreezeBackgroundTabOnNetworkIdleEnabledTest,
+       PageFrozenOnlyOnLocalMainFrameNetworkAlmostIdleNoRegress) {
+  page_scheduler_->SetPageVisible(true);
+  EXPECT_FALSE(ShouldFreezePage());
+  EXPECT_FALSE(page_scheduler_->IsFrozen());
+  EXPECT_FALSE(NetworkIsAlmostIdle());
+
+  // Page should freeze after delay for background tab freezing.
+  page_scheduler_->SetPageVisible(false);
+  EXPECT_TRUE(ShouldFreezePage());
+  test_task_runner_->FastForwardBy(delay_for_background_tab_freezing() +
+                                   base::TimeDelta::FromMilliseconds(100));
+  EXPECT_TRUE(page_scheduler_->IsFrozen());
+}
+
+TEST_F(PageSchedulerImplFreezeBackgroundTabOnNetworkIdleEnabledTest,
+       PageFrozenWhenNetworkIdleAfterQuickFreezingDelay) {
+  page_scheduler_->SetPageVisible(true);
+  EXPECT_FALSE(ShouldFreezePage());
+  EXPECT_FALSE(page_scheduler_->IsFrozen());
+  EXPECT_FALSE(NetworkIsAlmostIdle());
+
+  page_scheduler_->SetPageVisible(false);
+  EXPECT_TRUE(ShouldFreezePage());
+  test_task_runner_->FastForwardBy(
+      delay_for_background_and_network_idle_tab_freezing() +
+      base::TimeDelta::FromMilliseconds(100));
+  EXPECT_FALSE(page_scheduler_->IsFrozen());
+
+  NotifyLocalMainFrameNetworkIsAlmostIdle();
+  test_task_runner_->FastForwardBy(base::TimeDelta::FromMilliseconds(100));
+  EXPECT_TRUE(page_scheduler_->IsFrozen());
+}
+
+TEST_F(PageSchedulerImplFreezeBackgroundTabOnNetworkIdleEnabledTest,
+       PageNotFrozenWhenVisibleBeforeNetworkIdle) {
+  page_scheduler_->SetPageVisible(true);
+  EXPECT_FALSE(ShouldFreezePage());
+  EXPECT_FALSE(page_scheduler_->IsFrozen());
+  EXPECT_FALSE(NetworkIsAlmostIdle());
+
+  page_scheduler_->SetPageVisible(false);
+  EXPECT_TRUE(ShouldFreezePage());
+  test_task_runner_->FastForwardBy(
+      delay_for_background_and_network_idle_tab_freezing() +
+      base::TimeDelta::FromMilliseconds(100));
+  EXPECT_FALSE(page_scheduler_->IsFrozen());
+
+  // Page should not freeze after delay for background tab freezing, because
+  // the page is visible.
+  page_scheduler_->SetPageVisible(true);
+  EXPECT_FALSE(ShouldFreezePage());
+  test_task_runner_->FastForwardBy(delay_for_background_tab_freezing());
+  EXPECT_FALSE(page_scheduler_->IsFrozen());
+}
+
 class PageSchedulerImplPageTransitionTest : public PageSchedulerImplTest {
  public:
   typedef PageSchedulerImpl::PageLifecycleStateTransition Transition;
diff --git a/third_party/blink/renderer/platform/scheduler/public/aggregated_metric_reporter.h b/third_party/blink/renderer/platform/scheduler/public/aggregated_metric_reporter.h
index a705015..7622f19 100644
--- a/third_party/blink/renderer/platform/scheduler/public/aggregated_metric_reporter.h
+++ b/third_party/blink/renderer/platform/scheduler/public/aggregated_metric_reporter.h
@@ -13,6 +13,7 @@
 #include "base/threading/thread_checker.h"
 #include "base/time/time.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
 
 namespace base {
 class HistogramBase;
@@ -29,6 +30,8 @@
 // All values reported to RecordTask should have lower values.
 template <class TaskClass, class ValueType>
 class AggregatedMetricReporter {
+  DISALLOW_NEW();
+
  public:
   // Aggregation function: takes ValueType, returns the integer value to return
   // to histogram and modifies the passed value.
diff --git a/third_party/blink/renderer/platform/scheduler/public/frame_or_worker_scheduler.h b/third_party/blink/renderer/platform/scheduler/public/frame_or_worker_scheduler.h
index ef19a2d..94523d85 100644
--- a/third_party/blink/renderer/platform/scheduler/public/frame_or_worker_scheduler.h
+++ b/third_party/blink/renderer/platform/scheduler/public/frame_or_worker_scheduler.h
@@ -36,6 +36,8 @@
   };
 
   class PLATFORM_EXPORT LifecycleObserverHandle {
+    USING_FAST_MALLOC(LifecycleObserverHandle);
+
    public:
     LifecycleObserverHandle(FrameOrWorkerScheduler* scheduler,
                             Observer* observer);
@@ -51,6 +53,8 @@
   // RAII handle which should be kept alive as long as the feature is active
   // and the policy should be applied.
   class PLATFORM_EXPORT SchedulingAffectingFeatureHandle {
+    DISALLOW_NEW();
+
    public:
     SchedulingAffectingFeatureHandle() = default;
     SchedulingAffectingFeatureHandle(SchedulingAffectingFeatureHandle&&);
diff --git a/third_party/blink/renderer/platform/scheduler/public/page_scheduler.h b/third_party/blink/renderer/platform/scheduler/public/page_scheduler.h
index 6551978..4783bcc 100644
--- a/third_party/blink/renderer/platform/scheduler/public/page_scheduler.h
+++ b/third_party/blink/renderer/platform/scheduler/public/page_scheduler.h
@@ -30,6 +30,9 @@
     // compositor.
     virtual bool RequestBeginMainFrameNotExpected(bool new_state) = 0;
     virtual void SetLifecycleState(PageLifecycleState) = 0;
+    // Returns true iff the network is idle for the local main frame.
+    // Always returns false if the main frame is remote.
+    virtual bool LocalMainFrameNetworkIsAlmostIdle() const { return true; }
   };
 
   virtual ~PageScheduler() = default;
@@ -45,6 +48,9 @@
   // Whether the main frame of this page is local or not (remote).
   virtual bool IsMainFrameLocal() const = 0;
   virtual void SetIsMainFrameLocal(bool) = 0;
+  // Invoked when the local main frame's network becomes almost idle.
+  // Never invoked if the main frame is remote.
+  virtual void OnLocalMainFrameNetworkAlmostIdle() = 0;
 
   // Creates a new FrameScheduler. The caller is responsible for deleting
   // it. All tasks executed by the frame scheduler will be attributed to
diff --git a/third_party/blink/renderer/platform/scheduler/public/pending_user_input_type.h b/third_party/blink/renderer/platform/scheduler/public/pending_user_input_type.h
index a2e54de7..72dcd1db 100644
--- a/third_party/blink/renderer/platform/scheduler/public/pending_user_input_type.h
+++ b/third_party/blink/renderer/platform/scheduler/public/pending_user_input_type.h
@@ -5,6 +5,8 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_PUBLIC_PENDING_USER_INPUT_TYPE_H_
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_PUBLIC_PENDING_USER_INPUT_TYPE_H_
 
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+
 namespace blink {
 namespace scheduler {
 
@@ -56,6 +58,8 @@
 
 // A wrapper around a set of input types that have been flagged as pending.
 class PLATFORM_EXPORT PendingUserInputInfo {
+  DISALLOW_NEW();
+
  public:
   constexpr explicit PendingUserInputInfo(PendingUserInputType mask)
       : mask_(mask) {}
diff --git a/third_party/blink/renderer/platform/scheduler/test/fake_page_scheduler.h b/third_party/blink/renderer/platform/scheduler/test/fake_page_scheduler.h
index 9a419e10..27675de 100644
--- a/third_party/blink/renderer/platform/scheduler/test/fake_page_scheduler.h
+++ b/third_party/blink/renderer/platform/scheduler/test/fake_page_scheduler.h
@@ -54,6 +54,7 @@
   void SetKeepActive(bool keep_active) override {}
   bool IsMainFrameLocal() const override { return true; }
   void SetIsMainFrameLocal(bool is_local) override {}
+  void OnLocalMainFrameNetworkAlmostIdle() override {}
 
   std::unique_ptr<FrameScheduler> CreateFrameScheduler(
       FrameScheduler::Delegate* delegate,
diff --git a/third_party/blink/renderer/platform/scheduler/test/renderer_scheduler_test_support.cc b/third_party/blink/renderer/platform/scheduler/test/renderer_scheduler_test_support.cc
index a49deb5..d79e217 100644
--- a/third_party/blink/renderer/platform/scheduler/test/renderer_scheduler_test_support.cc
+++ b/third_party/blink/renderer/platform/scheduler/test/renderer_scheduler_test_support.cc
@@ -116,6 +116,7 @@
   void SetKeepActive(bool) override {}
   bool IsMainFrameLocal() const override { return true; }
   void SetIsMainFrameLocal(bool) override {}
+  void OnLocalMainFrameNetworkAlmostIdle() override {}
   base::TimeTicks EnableVirtualTime() override { return base::TimeTicks(); }
   void DisableVirtualTimeForTesting() override {}
   bool VirtualTimeAllowedToAdvance() const override { return true; }
diff --git a/third_party/blink/renderer/platform/shared_buffer.cc b/third_party/blink/renderer/platform/shared_buffer.cc
index f2bcc3d..b2dd1d36 100644
--- a/third_party/blink/renderer/platform/shared_buffer.cc
+++ b/third_party/blink/renderer/platform/shared_buffer.cc
@@ -29,6 +29,7 @@
 #include <memory>
 
 #include "third_party/blink/renderer/platform/instrumentation/tracing/web_process_memory_dump.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
 #include "third_party/blink/renderer/platform/wtf/text/unicode.h"
 #include "third_party/blink/renderer/platform/wtf/text/utf8.h"
 #include "third_party/skia/include/core/SkData.h"
@@ -46,6 +47,8 @@
 }
 
 class SharedBuffer::Segment final {
+  USING_FAST_MALLOC(Segment);
+
  public:
   Segment() {
     ptr_.reset(static_cast<char*>(WTF::Partitions::FastMalloc(
diff --git a/third_party/blink/renderer/platform/speech/platform_speech_synthesis_voice.h b/third_party/blink/renderer/platform/speech/platform_speech_synthesis_voice.h
index 54e2b05..bb993d3b 100644
--- a/third_party/blink/renderer/platform/speech/platform_speech_synthesis_voice.h
+++ b/third_party/blink/renderer/platform/speech/platform_speech_synthesis_voice.h
@@ -28,6 +28,7 @@
 
 #include "base/memory/scoped_refptr.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
 #include "third_party/blink/renderer/platform/wtf/ref_counted.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
 
@@ -35,6 +36,8 @@
 
 class PLATFORM_EXPORT PlatformSpeechSynthesisVoice final
     : public RefCounted<PlatformSpeechSynthesisVoice> {
+  USING_FAST_MALLOC(PlatformSpeechSynthesisVoice);
+
  public:
   static scoped_refptr<PlatformSpeechSynthesisVoice> Create(
       const String& voice_uri,
diff --git a/third_party/blink/renderer/platform/testing/shape_result_perf_test.cc b/third_party/blink/renderer/platform/testing/shape_result_perf_test.cc
index e635da9..47a2aaf 100644
--- a/third_party/blink/renderer/platform/testing/shape_result_perf_test.cc
+++ b/third_party/blink/renderer/platform/testing/shape_result_perf_test.cc
@@ -11,6 +11,7 @@
 #include "third_party/blink/renderer/platform/fonts/font_description.h"
 #include "third_party/blink/renderer/platform/testing/font_test_helpers.h"
 #include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
 
 using blink::test::CreateTestFont;
 
@@ -21,6 +22,8 @@
 static const int kTimeCheckInterval = 1000000;
 
 class ShapeResultPerfTest {
+  USING_FAST_MALLOC(ShapeResultPerfTest);
+
  public:
   enum FontName {
     ahem,
diff --git a/third_party/blink/renderer/platform/testing/unit_test_helpers.h b/third_party/blink/renderer/platform/testing/unit_test_helpers.h
index a60ebea..59abf5f 100644
--- a/third_party/blink/renderer/platform/testing/unit_test_helpers.h
+++ b/third_party/blink/renderer/platform/testing/unit_test_helpers.h
@@ -28,6 +28,7 @@
 
 #include "base/memory/scoped_refptr.h"
 #include "third_party/blink/renderer/platform/timer.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
 
 namespace blink {
@@ -79,6 +80,8 @@
 scoped_refptr<SharedBuffer> ReadFromFile(const String& path);
 
 class LineReader {
+  DISALLOW_NEW();
+
  public:
   LineReader(const std::string& text);
   bool GetNextLine(std::string* line);
diff --git a/third_party/blink/renderer/platform/testing/viewport_layers_setup.h b/third_party/blink/renderer/platform/testing/viewport_layers_setup.h
index febdac8..26d30e8 100644
--- a/third_party/blink/renderer/platform/testing/viewport_layers_setup.h
+++ b/third_party/blink/renderer/platform/testing/viewport_layers_setup.h
@@ -8,6 +8,7 @@
 #include <memory>
 
 #include "third_party/blink/renderer/platform/testing/fake_graphics_layer_client.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
 
 namespace cc {
 class AnimationHost;
@@ -20,6 +21,8 @@
 class LayerTreeHostEmbedder;
 
 class ViewportLayersSetup {
+  DISALLOW_NEW();
+
  public:
   ViewportLayersSetup();
   ~ViewportLayersSetup();
diff --git a/third_party/blink/renderer/platform/text/bidi_resolver_test.cc b/third_party/blink/renderer/platform/text/bidi_resolver_test.cc
index fe3e701..5210704 100644
--- a/third_party/blink/renderer/platform/text/bidi_resolver_test.cc
+++ b/third_party/blink/renderer/platform/text/bidi_resolver_test.cc
@@ -36,6 +36,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/renderer/platform/text/bidi_test_harness.h"
 #include "third_party/blink/renderer/platform/text/text_run_iterator.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
 
 namespace blink {
 
@@ -113,6 +114,8 @@
 }
 
 class BidiTestRunner {
+  STACK_ALLOCATED();
+
  public:
   BidiTestRunner()
       : tests_run_(0),
diff --git a/third_party/blink/renderer/platform/wtf/conditional_destructor.h b/third_party/blink/renderer/platform/wtf/conditional_destructor.h
index 34fbfded..690e687 100644
--- a/third_party/blink/renderer/platform/wtf/conditional_destructor.h
+++ b/third_party/blink/renderer/platform/wtf/conditional_destructor.h
@@ -5,6 +5,8 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_CONDITIONAL_DESTRUCTOR_H_
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_CONDITIONAL_DESTRUCTOR_H_
 
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+
 namespace WTF {
 
 // ConditionalDestructor defines the destructor of the derived object.
@@ -16,6 +18,8 @@
 // method.
 template <typename Derived, bool noDestructor>
 class ConditionalDestructor {
+  USING_FAST_MALLOC(ConditionalDestructor);
+
  public:
   ~ConditionalDestructor() { static_cast<Derived*>(this)->Finalize(); }
 };
diff --git a/third_party/blink/renderer/platform/wtf/doubly_linked_list.h b/third_party/blink/renderer/platform/wtf/doubly_linked_list.h
index a38511a..9a32a7c 100644
--- a/third_party/blink/renderer/platform/wtf/doubly_linked_list.h
+++ b/third_party/blink/renderer/platform/wtf/doubly_linked_list.h
@@ -35,6 +35,8 @@
 // This class allows nodes to share code without dictating data member layout.
 template <typename T>
 class DoublyLinkedListNode {
+  USING_FAST_MALLOC(DoublyLinkedListNode);
+
  public:
   DoublyLinkedListNode();
 
diff --git a/third_party/blink/renderer/platform/wtf/text/integer_to_string_conversion.h b/third_party/blink/renderer/platform/wtf/text/integer_to_string_conversion.h
index b6c9dde..09f1528 100644
--- a/third_party/blink/renderer/platform/wtf/text/integer_to_string_conversion.h
+++ b/third_party/blink/renderer/platform/wtf/text/integer_to_string_conversion.h
@@ -27,6 +27,7 @@
 
 #include "base/numerics/safe_conversions.h"
 #include "base/stl_util.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
 #include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
 #include "third_party/blink/renderer/platform/wtf/text/unicode.h"
 
@@ -37,6 +38,8 @@
 // optimization here instead of base::CheckedNumeric::UnsignedAbs().
 template <typename IntegerType>
 class IntegerToStringConverter {
+  USING_FAST_MALLOC(IntegerToStringConverter);
+
  public:
   static_assert(std::is_integral<IntegerType>::value,
                 "IntegerType must be a type of integer.");
diff --git a/third_party/blink/renderer/platform/wtf/text/string_builder.h b/third_party/blink/renderer/platform/wtf/text/string_builder.h
index 205eca2f..a7bb9521 100644
--- a/third_party/blink/renderer/platform/wtf/text/string_builder.h
+++ b/third_party/blink/renderer/platform/wtf/text/string_builder.h
@@ -37,6 +37,8 @@
 namespace WTF {
 
 class WTF_EXPORT StringBuilder {
+  USING_FAST_MALLOC(StringBuilder);
+
  public:
   StringBuilder() : no_buffer_() {}
   ~StringBuilder() { Clear(); }
diff --git a/third_party/blink/renderer/platform/wtf/text/string_impl.h b/third_party/blink/renderer/platform/wtf/text/string_impl.h
index 1945833..db513b23 100644
--- a/third_party/blink/renderer/platform/wtf/text/string_impl.h
+++ b/third_party/blink/renderer/platform/wtf/text/string_impl.h
@@ -31,6 +31,7 @@
 #include "base/memory/ref_counted.h"
 #include "base/numerics/checked_math.h"
 #include "build/build_config.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
 #include "third_party/blink/renderer/platform/wtf/forward.h"
 #include "third_party/blink/renderer/platform/wtf/hash_map.h"
 #include "third_party/blink/renderer/platform/wtf/text/ascii_ctype.h"
diff --git a/third_party/blink/renderer/platform/wtf/thread_restriction_verifier.h b/third_party/blink/renderer/platform/wtf/thread_restriction_verifier.h
index ef2d9a8f..f6bd1cfc 100644
--- a/third_party/blink/renderer/platform/wtf/thread_restriction_verifier.h
+++ b/third_party/blink/renderer/platform/wtf/thread_restriction_verifier.h
@@ -35,6 +35,7 @@
 
 #if DCHECK_IS_ON()
 
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
 #include "third_party/blink/renderer/platform/wtf/threading.h"
 
 namespace WTF {
@@ -45,6 +46,8 @@
 // called.  The mode may be changed by calling useMutexMode (or
 // turnOffVerification).
 class ThreadRestrictionVerifier {
+  DISALLOW_NEW();
+
  public:
   ThreadRestrictionVerifier() : shared_(false), owning_thread_(0) {}
 
diff --git a/third_party/blink/renderer/platform/wtf/typed_arrays/array_buffer_view.h b/third_party/blink/renderer/platform/wtf/typed_arrays/array_buffer_view.h
index 6301434..0029479 100644
--- a/third_party/blink/renderer/platform/wtf/typed_arrays/array_buffer_view.h
+++ b/third_party/blink/renderer/platform/wtf/typed_arrays/array_buffer_view.h
@@ -28,6 +28,7 @@
 
 #include <limits.h>
 #include "base/memory/scoped_refptr.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
 #include "third_party/blink/renderer/platform/wtf/ref_counted.h"
 #include "third_party/blink/renderer/platform/wtf/typed_arrays/array_buffer.h"
 #include "third_party/blink/renderer/platform/wtf/wtf_export.h"
@@ -35,6 +36,8 @@
 namespace WTF {
 
 class WTF_EXPORT ArrayBufferView : public RefCounted<ArrayBufferView> {
+  USING_FAST_MALLOC(ArrayBuffer);
+
  public:
   enum ViewType {
     kTypeInt8,
diff --git a/third_party/blink/web_tests/FlagExpectations/enable-blink-features=LayoutNG b/third_party/blink/web_tests/FlagExpectations/enable-blink-features=LayoutNG
index 0417bf17..3d27f8f 100644
--- a/third_party/blink/web_tests/FlagExpectations/enable-blink-features=LayoutNG
+++ b/third_party/blink/web_tests/FlagExpectations/enable-blink-features=LayoutNG
@@ -61,7 +61,6 @@
 crbug.com/591099 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/sizing/hori-block-size-small-or-larger-than-container-with-min-or-max-content-2a.html [ Pass ]
 crbug.com/591099 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/sizing/hori-block-size-small-or-larger-than-container-with-min-or-max-content-2b.html [ Pass ]
 crbug.com/591099 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/sizing/vert-block-size-small-or-larger-than-container-with-min-or-max-content-2a.html [ Pass ]
-crbug.com/613663 external/wpt/quirks/table-cell-width-calculation.html [ Failure ]
 
 # The new 'break-spaces' value is not implemented yet in LayoutNG
 crbug.com/922437 external/wpt/css/css-text/overflow-wrap/overflow-wrap-anywhere-002.html [ Skip ]
@@ -108,7 +107,6 @@
 crbug.com/714962 external/wpt/css/css-fonts/font-features-across-space-1.html [ Pass ]
 crbug.com/714962 external/wpt/css/css-fonts/font-features-across-space-3.html [ Pass ]
 crbug.com/591099 external/wpt/css/css-grid/grid-items/grid-item-percentage-sizes-003.html [ Pass ]
-crbug.com/919818 external/wpt/css/css-lists/list-and-block-textarea-001.html [ Failure ]
 crbug.com/591099 external/wpt/css/css-multicol/multicol-span-all-list-item-002.html [ Pass ]
 crbug.com/933054 external/wpt/css/css-position/position-absolute-container-dynamic-002.html [ Pass ]
 crbug.com/591099 external/wpt/css/css-position/position-absolute-in-inline-001.html [ Pass ]
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 19de12a..937804a 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -3025,6 +3025,16 @@
 crbug.com/939181 virtual/not-site-per-process/external/wpt/html/browsers/origin/cross-origin-objects/cross-origin-objects.html [ Failure Timeout ]
 
 # ====== New tests from wpt-importer added here ======
+crbug.com/626703 external/wpt/css/css-text-decor/text-underline-position-073-manual.html [ Skip ]
+crbug.com/626703 external/wpt/css/css-text-decor/text-underline-position-072-manual.html [ Skip ]
+crbug.com/626703 external/wpt/css/css-text-decor/text-underline-position-071-manual.html [ Skip ]
+crbug.com/626703 external/wpt/css/css-text-decor/text-underline-position-020-manual.html [ Skip ]
+crbug.com/626703 external/wpt/css/css-text-decor/text-underline-position-076-manual.html [ Skip ]
+crbug.com/626703 external/wpt/css/css-text-decor/text-underline-position-075-manual.html [ Skip ]
+crbug.com/626703 external/wpt/css/css-text-decor/text-underline-position-021-manual.html [ Skip ]
+crbug.com/626703 external/wpt/css/css-text-decor/text-underline-position-022-manual.html [ Skip ]
+crbug.com/626703 external/wpt/css/css-text-decor/text-underline-position-074-manual.html [ Skip ]
+crbug.com/626703 external/wpt/css/css-text-decor/text-underline-position-019-manual.html [ Skip ]
 crbug.com/626703 external/wpt/css/css-text-decor/text-decoration-line-048-manual.html [ Skip ]
 crbug.com/626703 external/wpt/css/css-text-decor/text-decoration-line-046a-manual.html [ Skip ]
 crbug.com/626703 external/wpt/css/css-text-decor/text-decoration-line-082-manual.html [ Skip ]
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_5.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_5.json
index 98b65df..afeae43 100644
--- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_5.json
+++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_5.json
@@ -2005,6 +2005,66 @@
      {}
     ]
    ],
+   "css/css-text-decor/text-underline-position-019-manual.html": [
+    [
+     "/css/css-text-decor/text-underline-position-019-manual.html",
+     {}
+    ]
+   ],
+   "css/css-text-decor/text-underline-position-020-manual.html": [
+    [
+     "/css/css-text-decor/text-underline-position-020-manual.html",
+     {}
+    ]
+   ],
+   "css/css-text-decor/text-underline-position-021-manual.html": [
+    [
+     "/css/css-text-decor/text-underline-position-021-manual.html",
+     {}
+    ]
+   ],
+   "css/css-text-decor/text-underline-position-022-manual.html": [
+    [
+     "/css/css-text-decor/text-underline-position-022-manual.html",
+     {}
+    ]
+   ],
+   "css/css-text-decor/text-underline-position-071-manual.html": [
+    [
+     "/css/css-text-decor/text-underline-position-071-manual.html",
+     {}
+    ]
+   ],
+   "css/css-text-decor/text-underline-position-072-manual.html": [
+    [
+     "/css/css-text-decor/text-underline-position-072-manual.html",
+     {}
+    ]
+   ],
+   "css/css-text-decor/text-underline-position-073-manual.html": [
+    [
+     "/css/css-text-decor/text-underline-position-073-manual.html",
+     {}
+    ]
+   ],
+   "css/css-text-decor/text-underline-position-074-manual.html": [
+    [
+     "/css/css-text-decor/text-underline-position-074-manual.html",
+     {}
+    ]
+   ],
+   "css/css-text-decor/text-underline-position-075-manual.html": [
+    [
+     "/css/css-text-decor/text-underline-position-075-manual.html",
+     {}
+    ]
+   ],
+   "css/css-text-decor/text-underline-position-076-manual.html": [
+    [
+     "/css/css-text-decor/text-underline-position-076-manual.html",
+     {}
+    ]
+   ],
    "css/css-text/text-transform/text-transform-copy-paste-001-manual.html": [
     [
      "/css/css-text/text-transform/text-transform-copy-paste-001-manual.html",
@@ -324095,7 +324155,7 @@
    "support"
   ],
   "content-security-policy/README.html": [
-   "becc48b65ac0397326aa4cb1cb9e17b29bd473b3",
+   "98fd5c4bf789b99b959bab3fbdadb1ad408db5a0",
    "support"
   ],
   "content-security-policy/base-uri/base-uri-allow.sub.html": [
@@ -374434,6 +374494,46 @@
    "df76952a1fd0e284217d819392e538f5edd95211",
    "reftest"
   ],
+  "css/css-text-decor/text-underline-position-019-manual.html": [
+   "0308fbc8c9ed4b54865a5b382741c148c6853981",
+   "manual"
+  ],
+  "css/css-text-decor/text-underline-position-020-manual.html": [
+   "7602308c8d6c7f7c40eaf896ebe02aff93932d13",
+   "manual"
+  ],
+  "css/css-text-decor/text-underline-position-021-manual.html": [
+   "046e6298cc76ddaf9ae8fce1eaec9c32f9a217ba",
+   "manual"
+  ],
+  "css/css-text-decor/text-underline-position-022-manual.html": [
+   "5ffbad2a056eeea663e43c879d3fcc482b3511a5",
+   "manual"
+  ],
+  "css/css-text-decor/text-underline-position-071-manual.html": [
+   "9605cfd24e9bca94bc0cff30fbabc84b324a2b22",
+   "manual"
+  ],
+  "css/css-text-decor/text-underline-position-072-manual.html": [
+   "3a097f4bb48a816cf54fddc050763163491ae26b",
+   "manual"
+  ],
+  "css/css-text-decor/text-underline-position-073-manual.html": [
+   "ab827e5fcd0804699567d01d2524be5ab47a6503",
+   "manual"
+  ],
+  "css/css-text-decor/text-underline-position-074-manual.html": [
+   "2dc1f42be677d59084b37be4567cc018d83c9a94",
+   "manual"
+  ],
+  "css/css-text-decor/text-underline-position-075-manual.html": [
+   "64d16f2b169dceafbcf98ba5dd9dc837c762af7e",
+   "manual"
+  ],
+  "css/css-text-decor/text-underline-position-076-manual.html": [
+   "00323e53aacbe46fd2be07ed05b2b19db229753a",
+   "manual"
+  ],
   "css/css-text/META.yml": [
    "e7914c0fc7ff9e6999f8847dc1800968472483f2",
    "support"
diff --git a/third_party/blink/web_tests/external/wpt/content-security-policy/README.html b/third_party/blink/web_tests/external/wpt/content-security-policy/README.html
index becc48b6..98fd5c4b 100644
--- a/third_party/blink/web_tests/external/wpt/content-security-policy/README.html
+++ b/third_party/blink/web_tests/external/wpt/content-security-policy/README.html
@@ -62,7 +62,7 @@
 
     <p>This code includes three tests. The first one in the script block will generate a failure if it runs. The second one, in the onerror handler for the img which does not exist should also generate a failure if it runs. But for a successful CSP implementation, neither of these tests does run. The final test is run by the link to <span class=code>../support/checkReport.sub.js</span>. It will load some script in the page (make sure its not blocked by your policy!) which contacts the server asynchronously and sees if the expected report was sent. This should always run an generate a positive or negative result even if the inline tests are blocked as we expect.</p>
 
-    <p>Now, to acutally exercise these tests against a policy, we'll need to set headers. In the same directory we'll place this file:</p>
+    <p>Now, to actually exercise these tests against a policy, we'll need to set headers. In the same directory we'll place this file:</p>
 
     <p class=codeTitle>script-src-1_1.html.sub.headers</p>
     <pre><code class="html">
diff --git a/third_party/blink/web_tests/external/wpt/css/css-lists/list-and-block-textarea-001.html b/third_party/blink/web_tests/external/wpt/css/css-lists/list-and-block-textarea-001.html
index 96af38a8..13f723ac5 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-lists/list-and-block-textarea-001.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-lists/list-and-block-textarea-001.html
@@ -8,17 +8,17 @@
 <script src="/resources/testharnessreport.js"></script>
 
 <style>
-textarea {
-  border: 0px;
-  padding: 0px;
+
+ul, textarea {
+  font-size: 16px;
 }
 </style>
 
 <div id="log"></div>
 
 <ul>
-  <li id="target">
-    <textarea rows="3" cols="20" style="display:block; height:45px">
+  <li id="target1">
+    <textarea id="target2" rows="3" cols="20" style="display:block; height:45px">
       hello
     </textarea>
   </li>
@@ -26,8 +26,9 @@
 
 <script>
 test(function() {
-  var height = document.getElementById("target").offsetHeight;
-  assert_equals(height, 45, "the height of li should be 45px, and no extra line generated")
+  var height1 = document.getElementById("target1").offsetHeight;
+  var height2 = document.getElementById("target2").offsetHeight;
+  assert_equals(height1, height2, "List marker and textarea should be in the same line, no line-break between them.")
 }, "list and block textarea");
 
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-underline-position-019-manual.html b/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-underline-position-019-manual.html
new file mode 100644
index 0000000..0308fbc
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-underline-position-019-manual.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>text-underline-position auto</title>
+<meta name="assert" content="text-decoration:underline; text-underline-position:auto; the underline is placed at or under the alphabetic baseline, unless the script works better with a line further from the baseline">
+<link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
+<link rel="help" href="https://drafts.csswg.org/css-text-decor-3/#line-decoration">
+<!-- cosmetic styling -->
+<style>
+#htmlsrc { margin: 2em; }
+#htmlsrc p {
+	font-size: 28px;
+	border-radius: 5px;
+	line-height: 1.5;
+	}
+:lang(mn) { font-family: "Mongolian Baiti", "Noto sans Mongolian", serif; }
+:lang(ja) { font-family: "Hiragino Maru Gothic Pro", "MS Mincho", sans-serif; }
+</style>
+<!-- the test -->
+<style>
+div span {
+text-decoration:underline;
+text-underline-position:auto;
+}</style>
+</head>
+<body>
+<p class="instructions">Test passes if the underline is placed at or under the alphabetic baseline, unless the script works better with a line further from the baseline.</p>
+<div id="htmlsrc">
+
+<div>
+<p lang="en"><span>The quick brown f</span><span>ox jumps over the lazy dog.</span></p>
+<p lang="ar"><span>وب جهانی را به‌درس</span><span>تی جهانی سازیم!</span></p>
+<p lang="ja"><span>可能性を最大限に</span><span>導き出すために</span></p>
+<p lang="zh"><span>引发网络的全部潜能</span><span>引發網絡的全部潛能</span></p>
+<p lang="mn"><span>ᠣᠯᠠᠨ ᠦᠨᠳᠦᠰ</span><span>ᠦᠲᠡᠨ ᠦ ᠪᠣᠯᠭᠠᠬᠤ ᠦᠢᠯᠡ ᠠᠵᠢᠯᠯᠠᠭ᠎ᠠ</span></p>
+<p lang="my"><span>အပြည်ပြည်ဆိုင်ရာ</span><span>လှုပ်ရှားမှု၊</span></p>
+<p lang="th"><span>กูกินกุ้งปิ้ง</span><span>อยู่ในถ้ำ กูกินกุ้งปิ้งอยู่ในถ้ำ</span></p>
+<p lang="bo"><span>འཛམ་གླིང་ཡོངས་འབྲེལ་འདི་ ངོ་མ་འ</span><span>བད་རང་ འཛམ་གླིང་ཡོངས་ལུ་ཁྱབ་ཚུགསཔ་བཟོ་བ།</span></p>
+<p lang="hi"><span>वर्ल्ड वाईड वेब को स</span><span>चमुच विश्वव्यापी बना रहें हैं !</span></p>
+</div>  </div>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-underline-position-020-manual.html b/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-underline-position-020-manual.html
new file mode 100644
index 0000000..7602308c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-underline-position-020-manual.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>text-underline-position under</title>
+<meta name="assert" content="text-decoration:underline; text-underline-position:under; the underline is low enough to avoid crossing the descenders">
+<link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
+<link rel="help" href="https://drafts.csswg.org/css-text-decor-3/#line-decoration">
+<!-- cosmetic styling -->
+<style>
+#htmlsrc { margin: 2em; }
+#htmlsrc p {
+	font-size: 28px;
+	border-radius: 5px;
+	line-height: 1.5;
+	}
+:lang(mn) { font-family: "Mongolian Baiti", "Noto sans Mongolian", serif; }
+:lang(ja) { font-family: "Hiragino Maru Gothic Pro", "MS Mincho", sans-serif; }
+</style>
+<!-- the test -->
+<style>
+div span {
+text-decoration:underline;
+text-underline-position:under;
+}</style>
+</head>
+<body>
+<p class="instructions">Test passes if the underline is low enough to avoid crossing the descenders.</p>
+<div id="htmlsrc">
+
+<div>
+<p lang="en"><span>The quick brown f</span><span>ox jumps over the lazy dog.</span></p>
+<p lang="ar"><span>وب جهانی را به‌درس</span><span>تی جهانی سازیم!</span></p>
+<p lang="ja"><span>可能性を最大限に</span><span>導き出すために</span></p>
+<p lang="zh"><span>引发网络的全部潜能</span><span>引發網絡的全部潛能</span></p>
+<p lang="mn"><span>ᠣᠯᠠᠨ ᠦᠨᠳᠦᠰ</span><span>ᠦᠲᠡᠨ ᠦ ᠪᠣᠯᠭᠠᠬᠤ ᠦᠢᠯᠡ ᠠᠵᠢᠯᠯᠠᠭ᠎ᠠ</span></p>
+<p lang="my"><span>အပြည်ပြည်ဆိုင်ရာ</span><span>လှုပ်ရှားမှု၊</span></p>
+<p lang="th"><span>กูกินกุ้งปิ้ง</span><span>อยู่ในถ้ำ กูกินกุ้งปิ้งอยู่ในถ้ำ</span></p>
+<p lang="bo"><span>འཛམ་གླིང་ཡོངས་འབྲེལ་འདི་ ངོ་མ་འ</span><span>བད་རང་ འཛམ་གླིང་ཡོངས་ལུ་ཁྱབ་ཚུགསཔ་བཟོ་བ།</span></p>
+<p lang="hi"><span>वर्ल्ड वाईड वेब को स</span><span>चमुच विश्वव्यापी बना रहें हैं !</span></p>
+</div>  </div>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-underline-position-021-manual.html b/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-underline-position-021-manual.html
new file mode 100644
index 0000000..046e629
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-underline-position-021-manual.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>text-underline-position under left</title>
+<meta name="assert" content="text-decoration:underline; text-underline-position:under left; the underline is low enough to avoid crossing the descenders">
+<link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
+<link rel="help" href="https://drafts.csswg.org/css-text-decor-3/#line-decoration">
+<!-- cosmetic styling -->
+<style>
+#htmlsrc { margin: 2em; }
+#htmlsrc p {
+	font-size: 28px;
+	border-radius: 5px;
+	line-height: 1.5;
+	}
+:lang(mn) { font-family: "Mongolian Baiti", "Noto sans Mongolian", serif; }
+:lang(ja) { font-family: "Hiragino Maru Gothic Pro", "MS Mincho", sans-serif; }
+</style>
+<!-- the test -->
+<style>
+div span {
+text-decoration:underline;
+text-underline-position:under left;
+}</style>
+</head>
+<body>
+<p class="instructions">Test passes if the underline is low enough to avoid crossing the descenders.</p>
+<div id="htmlsrc">
+
+<div>
+<p lang="en"><span>The quick brown f</span><span>ox jumps over the lazy dog.</span></p>
+<p lang="ar"><span>وب جهانی را به‌درس</span><span>تی جهانی سازیم!</span></p>
+<p lang="ja"><span>可能性を最大限に</span><span>導き出すために</span></p>
+<p lang="zh"><span>引发网络的全部潜能</span><span>引發網絡的全部潛能</span></p>
+<p lang="mn"><span>ᠣᠯᠠᠨ ᠦᠨᠳᠦᠰ</span><span>ᠦᠲᠡᠨ ᠦ ᠪᠣᠯᠭᠠᠬᠤ ᠦᠢᠯᠡ ᠠᠵᠢᠯᠯᠠᠭ᠎ᠠ</span></p>
+<p lang="my"><span>အပြည်ပြည်ဆိုင်ရာ</span><span>လှုပ်ရှားမှု၊</span></p>
+<p lang="th"><span>กูกินกุ้งปิ้ง</span><span>อยู่ในถ้ำ กูกินกุ้งปิ้งอยู่ในถ้ำ</span></p>
+<p lang="bo"><span>འཛམ་གླིང་ཡོངས་འབྲེལ་འདི་ ངོ་མ་འ</span><span>བད་རང་ འཛམ་གླིང་ཡོངས་ལུ་ཁྱབ་ཚུགསཔ་བཟོ་བ།</span></p>
+<p lang="hi"><span>वर्ल्ड वाईड वेब को स</span><span>चमुच विश्वव्यापी बना रहें हैं !</span></p>
+</div>  </div>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-underline-position-022-manual.html b/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-underline-position-022-manual.html
new file mode 100644
index 0000000..5ffbad2a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-underline-position-022-manual.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>text-underline-position under right</title>
+<meta name="assert" content="text-decoration:underline; text-underline-position:under right; the underline is low enough to avoid crossing the descenders">
+<link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
+<link rel="help" href="https://drafts.csswg.org/css-text-decor-3/#line-decoration">
+<!-- cosmetic styling -->
+<style>
+#htmlsrc { margin: 2em; }
+#htmlsrc p {
+	font-size: 28px;
+	border-radius: 5px;
+	line-height: 1.5;
+	}
+:lang(mn) { font-family: "Mongolian Baiti", "Noto sans Mongolian", serif; }
+:lang(ja) { font-family: "Hiragino Maru Gothic Pro", "MS Mincho", sans-serif; }
+</style>
+<!-- the test -->
+<style>
+div span {
+text-decoration:underline;
+text-underline-position:under right;
+}</style>
+</head>
+<body>
+<p class="instructions">Test passes if the underline is low enough to avoid crossing the descenders.</p>
+<div id="htmlsrc">
+
+<div>
+<p lang="en"><span>The quick brown f</span><span>ox jumps over the lazy dog.</span></p>
+<p lang="ar"><span>وب جهانی را به‌درس</span><span>تی جهانی سازیم!</span></p>
+<p lang="ja"><span>可能性を最大限に</span><span>導き出すために</span></p>
+<p lang="zh"><span>引发网络的全部潜能</span><span>引發網絡的全部潛能</span></p>
+<p lang="mn"><span>ᠣᠯᠠᠨ ᠦᠨᠳᠦᠰ</span><span>ᠦᠲᠡᠨ ᠦ ᠪᠣᠯᠭᠠᠬᠤ ᠦᠢᠯᠡ ᠠᠵᠢᠯᠯᠠᠭ᠎ᠠ</span></p>
+<p lang="my"><span>အပြည်ပြည်ဆိုင်ရာ</span><span>လှုပ်ရှားမှု၊</span></p>
+<p lang="th"><span>กูกินกุ้งปิ้ง</span><span>อยู่ในถ้ำ กูกินกุ้งปิ้งอยู่ในถ้ำ</span></p>
+<p lang="bo"><span>འཛམ་གླིང་ཡོངས་འབྲེལ་འདི་ ངོ་མ་འ</span><span>བད་རང་ འཛམ་གླིང་ཡོངས་ལུ་ཁྱབ་ཚུགསཔ་བཟོ་བ།</span></p>
+<p lang="hi"><span>वर्ल्ड वाईड वेब को स</span><span>चमुच विश्वव्यापी बना रहें हैं !</span></p>
+</div>  </div>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-underline-position-071-manual.html b/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-underline-position-071-manual.html
new file mode 100644
index 0000000..9605cfd
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-underline-position-071-manual.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>text-underline-position tbrl auto vertical-rl</title>
+<meta name="assert" content="text-decoration:underline; text-underline-position:auto; the underline is placed alongside the characters, and may or may not cross any descenders">
+<link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
+<link rel="help" href="https://drafts.csswg.org/css-text-decor-3/#line-decoration">
+<!-- cosmetic styling -->
+<style>
+#htmlsrc { margin: 2em; }
+#htmlsrc p {
+	font-size: 28px;
+	border-radius: 5px;
+	line-height: 1.5;
+	}
+.hint { color: brown; font-family: sans-serif; font-size: 90%; }
+.hint:before { content: '❗ '; }
+:lang(mn) { font-family: "Mongolian Baiti", "Noto sans Mongolian", serif; }
+</style>
+<!-- the test -->
+<style>
+div span {
+text-decoration:underline;
+text-underline-position:auto;
+}</style>
+</head>
+<body>
+<p class="instructions">Test passes if the underline is placed alongside the characters - it may or may not cross any descenders.<br/><span class="hint">Skip the test if the text is not vertical.</span></p>
+<div id="htmlsrc" style="writing-mode:vertical-rl">
+
+<div>
+<p lang="zh"><span>引发网络的全部潜能</span><span>引發網絡的全部潛能</span></p>
+<p lang="ja"><span>可能性を最大限に</span><span>導き出すために</span></p>
+<p lang="mn"><span>ᠣᠯᠠᠨ ᠦᠨᠳᠦᠰ</span><span>ᠦᠲᠡᠨ ᠦ ᠪᠣᠯᠭᠠᠬᠤ ᠦᠢᠯᠡ ᠠᠵᠢᠯᠯᠠᠭ᠎ᠠ</span></p>
+<p lang="en"><span>The quick brown f</span><span>ox jumps over the lazy dog.</span></p>
+<p lang="ar"><span>وب جهانی را به‌درس</span><span>تی جهانی سازیم!</span></p>
+<p lang="my"><span>အပြည်ပြည်ဆိုင်ရာ</span><span>လှုပ်ရှားမှု၊</span></p>
+<p lang="th"><span>กูกินกุ้งปิ้ง</span><span>อยู่ในถ้ำ กูกินกุ้งปิ้งอยู่ในถ้ำ</span></p>
+<p lang="bo"><span>འཛམ་གླིང་ཡོངས་འབྲེལ་འདི་ ངོ་མ་འ</span><span>བད་རང་ འཛམ་གླིང་ཡོངས་ལུ་ཁྱབ་ཚུགསཔ་བཟོ་བ།</span></p>
+<p lang="hi"><span>वर्ल्ड वाईड वेब को स</span><span>चमुच विश्वव्यापी बना रहें हैं !</span></p>
+</div>  </div>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-underline-position-072-manual.html b/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-underline-position-072-manual.html
new file mode 100644
index 0000000..3a097f4
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-underline-position-072-manual.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>text-underline-position under vertical-rl</title>
+<meta name="assert" content="text-decoration:underline; text-underline-position:under; the line is far enough away to avoid crossing any descenders">
+<link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
+<link rel="help" href="https://drafts.csswg.org/css-text-decor-3/#line-decoration">
+<!-- cosmetic styling -->
+<style>
+#htmlsrc { margin: 2em; }
+#htmlsrc p {
+	font-size: 28px;
+	border-radius: 5px;
+	line-height: 1.5;
+	}
+.hint { color: brown; font-family: sans-serif; font-size: 90%; }
+.hint:before { content: '❗ '; }
+:lang(mn) { font-family: "Mongolian Baiti", "Noto sans Mongolian", serif; }
+</style>
+<!-- the test -->
+<style>
+div span {
+text-decoration:underline;
+text-underline-position:under;
+}</style>
+</head>
+<body>
+<p class="instructions">Test passes if the line is far enough away to avoid crossing any descenders.<br/><span class="hint">Skip the test if the text is not vertical.</span></p>
+<div id="htmlsrc" style="writing-mode:vertical-rl">
+
+<div>
+<p lang="zh"><span>引发网络的全部潜能</span><span>引發網絡的全部潛能</span></p>
+<p lang="ja"><span>可能性を最大限に</span><span>導き出すために</span></p>
+<p lang="mn"><span>ᠣᠯᠠᠨ ᠦᠨᠳᠦᠰ</span><span>ᠦᠲᠡᠨ ᠦ ᠪᠣᠯᠭᠠᠬᠤ ᠦᠢᠯᠡ ᠠᠵᠢᠯᠯᠠᠭ᠎ᠠ</span></p>
+<p lang="en"><span>The quick brown f</span><span>ox jumps over the lazy dog.</span></p>
+<p lang="ar"><span>وب جهانی را به‌درس</span><span>تی جهانی سازیم!</span></p>
+<p lang="my"><span>အပြည်ပြည်ဆိုင်ရာ</span><span>လှုပ်ရှားမှု၊</span></p>
+<p lang="th"><span>กูกินกุ้งปิ้ง</span><span>อยู่ในถ้ำ กูกินกุ้งปิ้งอยู่ในถ้ำ</span></p>
+<p lang="bo"><span>འཛམ་གླིང་ཡོངས་འབྲེལ་འདི་ ངོ་མ་འ</span><span>བད་རང་ འཛམ་གླིང་ཡོངས་ལུ་ཁྱབ་ཚུགསཔ་བཟོ་བ།</span></p>
+<p lang="hi"><span>वर्ल्ड वाईड वेब को स</span><span>चमुच विश्वव्यापी बना रहें हैं !</span></p>
+</div>  </div>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-underline-position-073-manual.html b/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-underline-position-073-manual.html
new file mode 100644
index 0000000..ab827e5
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-underline-position-073-manual.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>text-underline-position left vertical-rl</title>
+<meta name="assert" content="text-decoration:underline; text-underline-position:left; the line is to the left of the characters for all lines">
+<link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
+<link rel="help" href="https://drafts.csswg.org/css-text-decor-3/#line-decoration">
+<!-- cosmetic styling -->
+<style>
+#htmlsrc { margin: 2em; }
+#htmlsrc p {
+	font-size: 28px;
+	border-radius: 5px;
+	line-height: 1.5;
+	}
+.hint { color: brown; font-family: sans-serif; font-size: 90%; }
+.hint:before { content: '❗ '; }
+:lang(mn) { font-family: "Mongolian Baiti", "Noto sans Mongolian", serif; }
+</style>
+<!-- the test -->
+<style>
+div span {
+text-decoration:underline;
+text-underline-position:left;
+}</style>
+</head>
+<body>
+<p class="instructions">Test passes if the line is to the LEFT of the characters for all lines.<br/><span class="hint">Skip the test if the text is not vertical.</span></p>
+<div id="htmlsrc" style="writing-mode:vertical-rl">
+
+<div>
+<p lang="zh"><span>引发网络的全部潜能</span><span>引發網絡的全部潛能</span></p>
+<p lang="ja"><span>可能性を最大限に</span><span>導き出すために</span></p>
+<p lang="mn"><span>ᠣᠯᠠᠨ ᠦᠨᠳᠦᠰ</span><span>ᠦᠲᠡᠨ ᠦ ᠪᠣᠯᠭᠠᠬᠤ ᠦᠢᠯᠡ ᠠᠵᠢᠯᠯᠠᠭ᠎ᠠ</span></p>
+<p lang="en"><span>The quick brown f</span><span>ox jumps over the lazy dog.</span></p>
+</div>  </div>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-underline-position-074-manual.html b/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-underline-position-074-manual.html
new file mode 100644
index 0000000..2dc1f42
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-underline-position-074-manual.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>text-underline-position under left vertical-rl</title>
+<meta name="assert" content="text-decoration:underline; text-underline-position:under left; the line is to the left of the characters for all lines">
+<link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
+<link rel="help" href="https://drafts.csswg.org/css-text-decor-3/#line-decoration">
+<!-- cosmetic styling -->
+<style>
+#htmlsrc { margin: 2em; }
+#htmlsrc p {
+	font-size: 28px;
+	border-radius: 5px;
+	line-height: 1.5;
+	}
+.hint { color: brown; font-family: sans-serif; font-size: 90%; }
+.hint:before { content: '❗ '; }
+:lang(mn) { font-family: "Mongolian Baiti", "Noto sans Mongolian", serif; }
+</style>
+<!-- the test -->
+<style>
+div span {
+text-decoration:underline;
+text-underline-position:under left;
+}</style>
+</head>
+<body>
+<p class="instructions">Test passes if the line is to the LEFT of the characters for all lines.<br/><span class="hint">Skip the test if the text is not vertical.</span></p>
+<div id="htmlsrc" style="writing-mode:vertical-rl">
+
+<div>
+<p lang="zh"><span>引发网络的全部潜能</span><span>引發網絡的全部潛能</span></p>
+<p lang="ja"><span>可能性を最大限に</span><span>導き出すために</span></p>
+<p lang="mn"><span>ᠣᠯᠠᠨ ᠦᠨᠳᠦᠰ</span><span>ᠦᠲᠡᠨ ᠦ ᠪᠣᠯᠭᠠᠬᠤ ᠦᠢᠯᠡ ᠠᠵᠢᠯᠯᠠᠭ᠎ᠠ</span></p>
+<p lang="en"><span>The quick brown f</span><span>ox jumps over the lazy dog.</span></p>
+</div>  </div>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-underline-position-075-manual.html b/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-underline-position-075-manual.html
new file mode 100644
index 0000000..64d16f2
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-underline-position-075-manual.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>text-underline-position right vertical-rl</title>
+<meta name="assert" content="text-decoration:underline; text-underline-position:right; the line is to the right of the characters for all lines">
+<link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
+<link rel="help" href="https://drafts.csswg.org/css-text-decor-3/#line-decoration">
+<!-- cosmetic styling -->
+<style>
+#htmlsrc { margin: 2em; }
+#htmlsrc p {
+	font-size: 28px;
+	border-radius: 5px;
+	line-height: 1.5;
+	}
+.hint { color: brown; font-family: sans-serif; font-size: 90%; }
+.hint:before { content: '❗ '; }
+:lang(mn) { font-family: "Mongolian Baiti", "Noto sans Mongolian", serif; }
+</style>
+<!-- the test -->
+<style>
+div span {
+text-decoration:underline;
+text-underline-position:right;
+}</style>
+</head>
+<body>
+<p class="instructions">Test passes if the line is to the RIGHT of the characters for all lines.<br/><span class="hint">Skip the test if the text is not vertical.</span></p>
+<div id="htmlsrc" style="writing-mode:vertical-rl">
+
+<div>
+<p lang="zh"><span>引发网络的全部潜能</span><span>引發網絡的全部潛能</span></p>
+<p lang="ja"><span>可能性を最大限に</span><span>導き出すために</span></p>
+<p lang="mn"><span>ᠣᠯᠠᠨ ᠦᠨᠳᠦᠰ</span><span>ᠦᠲᠡᠨ ᠦ ᠪᠣᠯᠭᠠᠬᠤ ᠦᠢᠯᠡ ᠠᠵᠢᠯᠯᠠᠭ᠎ᠠ</span></p>
+<p lang="en"><span>The quick brown f</span><span>ox jumps over the lazy dog.</span></p>
+</div>  </div>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-underline-position-076-manual.html b/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-underline-position-076-manual.html
new file mode 100644
index 0000000..00323e53
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-underline-position-076-manual.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>text-underline-position under right vertical-rl</title>
+<meta name="assert" content="text-decoration:underline; text-underline-position:under right; the line is to the right of the characters for all lines">
+<link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
+<link rel="help" href="https://drafts.csswg.org/css-text-decor-3/#line-decoration">
+<!-- cosmetic styling -->
+<style>
+#htmlsrc { margin: 2em; }
+#htmlsrc p {
+	font-size: 28px;
+	border-radius: 5px;
+	line-height: 1.5;
+	}
+.hint { color: brown; font-family: sans-serif; font-size: 90%; }
+.hint:before { content: '❗ '; }
+:lang(mn) { font-family: "Mongolian Baiti", "Noto sans Mongolian", serif; }
+</style>
+<!-- the test -->
+<style>
+div span {
+text-decoration:underline;
+text-underline-position:under right;
+}</style>
+</head>
+<body>
+<p class="instructions">Test passes if the line is to the RIGHT of the characters for all lines.<br/><span class="hint">Skip the test if the text is not vertical.</span></p>
+<div id="htmlsrc" style="writing-mode:vertical-rl">
+
+<div>
+<p lang="zh"><span>引发网络的全部潜能</span><span>引發網絡的全部潛能</span></p>
+<p lang="ja"><span>可能性を最大限に</span><span>導き出すために</span></p>
+<p lang="mn"><span>ᠣᠯᠠᠨ ᠦᠨᠳᠦᠰ</span><span>ᠦᠲᠡᠨ ᠦ ᠪᠣᠯᠭᠠᠬᠤ ᠦᠢᠯᠡ ᠠᠵᠢᠯᠯᠠᠭ᠎ᠠ</span></p>
+<p lang="en"><span>The quick brown f</span><span>ox jumps over the lazy dog.</span></p>
+</div>  </div>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/web-animations/interfaces/Animation/style-change-events-expected.txt b/third_party/blink/web_tests/external/wpt/web-animations/interfaces/Animation/style-change-events-expected.txt
index 89e308f..3826b238 100644
--- a/third_party/blink/web_tests/external/wpt/web-animations/interfaces/Animation/style-change-events-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/web-animations/interfaces/Animation/style-change-events-expected.txt
@@ -1,6 +1,5 @@
 This is a testharness.js-based test.
-FAIL All property keys are recognized assert_in_array: Test property 'pending' should be one of the properties on  Animation value "pending" not in array ["effect", "startTime", "currentTime", "playbackRate", "playState", "id", "onfinish", "oncancel", "finish", "play", "pause", "reverse", "cancel", "finished", "ready", "timeline", "Animation constructor"]
-PASS Animation.effect does NOT trigger a style change event
+FAIL All property keys are recognized assert_in_array: Test property 'pending' should be one of the properties on  Animation value "pending" not in array ["startTime", "currentTime", "playbackRate", "playState", "id", "onfinish", "oncancel", "finish", "play", "pause", "reverse", "cancel", "finished", "ready", "timeline", "effect", "Animation constructor"]
 PASS Animation.startTime does NOT trigger a style change event
 PASS Animation.currentTime does NOT trigger a style change event
 PASS Animation.playbackRate does NOT trigger a style change event
@@ -16,6 +15,7 @@
 PASS Animation.finished does NOT trigger a style change event
 PASS Animation.ready does NOT trigger a style change event
 FAIL Animation.timeline does NOT trigger a style change event promise_test: Unhandled rejection with value: object "TypeError: Cannot assign to read only property 'timeline' of object '#<Animation>'"
+PASS Animation.effect does NOT trigger a style change event
 PASS Animation.Animation constructor does NOT trigger a style change event
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/RTCDataChannel-send-expected.txt b/third_party/blink/web_tests/external/wpt/webrtc/RTCDataChannel-send-expected.txt
index 89f96f8..f81efcb 100644
--- a/third_party/blink/web_tests/external/wpt/webrtc/RTCDataChannel-send-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/webrtc/RTCDataChannel-send-expected.txt
@@ -9,5 +9,6 @@
 FAIL Data channel should be able to send ArrayBuffer message and receive as Blob promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to set the 'binaryType' property on 'RTCDataChannel': Blob support not implemented yet"
 FAIL Data channel binaryType should receive message as Blob by default assert_equals: Expect initial binaryType value to be blob expected "blob" but got "arraybuffer"
 FAIL Sending multiple messages with different types should succeed and be received assert_unreached: Unexpected promise rejection: NotSupportedError: Failed to execute 'send' on 'RTCDataChannel': Blob support not implemented yet Reached unreachable code
+FAIL Calling send() up to max size should succeed, above max size should fail assert_equals: Size mismatch expected 524288 but got 262528
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/RTCDataChannel-send.html b/third_party/blink/web_tests/external/wpt/webrtc/RTCDataChannel-send.html
index cb8b4ed..4b8d0c2 100644
--- a/third_party/blink/web_tests/external/wpt/webrtc/RTCDataChannel-send.html
+++ b/third_party/blink/web_tests/external/wpt/webrtc/RTCDataChannel-send.html
@@ -295,5 +295,22 @@
       assert_equals(channel.readyState, 'closing');
       channel.send(helloString);
     }, 'Calling send() when data channel is in closing state should succeed');
-   */
+  */
+
+promise_test(async t => {
+  let pc1 = new RTCPeerConnection();
+  let [channel1, channel2] = await createDataChannelPair(pc1);
+  let message = 'hello888'; // 8 bytes
+  while (message.length <= pc1.sctp.maxMessageSize) {
+    channel1.send(message);
+    let received_message = await awaitMessage(channel2);
+    assert_equals(received_message.length, message.length, "Size mismatch");
+    // Double size
+    message = message + message;
+  }
+  // "send" method step 4:
+  // If the byte size of "data" exceeds the value of maxMessageSize, throw
+  // a TypeError.
+  assert_throws('TypeError', () => channel1.send(message));
+}, 'Calling send() up to max size should succeed, above max size should fail');
 </script>
diff --git a/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt b/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt
index 461e561..489890f 100644
--- a/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt
+++ b/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt
@@ -39,35 +39,6 @@
     setter maxDecibels
     setter minDecibels
     setter smoothingTimeConstant
-interface Animation : EventTarget
-    attribute @@toStringTag
-    getter currentTime
-    getter effect
-    getter id
-    getter oncancel
-    getter onfinish
-    getter playState
-    getter playbackRate
-    getter startTime
-    method cancel
-    method constructor
-    method finish
-    method pause
-    method play
-    method reverse
-    setter currentTime
-    setter effect
-    setter id
-    setter oncancel
-    setter onfinish
-    setter playbackRate
-    setter startTime
-interface AnimationEffect
-    attribute @@toStringTag
-    method constructor
-    method getComputedTiming
-    method getTiming
-    method updateTiming
 interface AnimationEvent : Event
     attribute @@toStringTag
     getter animationName
@@ -3734,11 +3705,6 @@
     method has
     method keys
     method values
-interface KeyframeEffect : AnimationEffect
-    attribute @@toStringTag
-    getter target
-    method constructor
-    setter target
 interface LinearAccelerationSensor : Accelerometer
     attribute @@toStringTag
     method constructor
diff --git a/third_party/blink/web_tests/virtual/stable/webexposed/web-animations-api-expected.txt b/third_party/blink/web_tests/virtual/stable/webexposed/web-animations-api-expected.txt
index 4f7981f..52da4863 100644
--- a/third_party/blink/web_tests/virtual/stable/webexposed/web-animations-api-expected.txt
+++ b/third_party/blink/web_tests/virtual/stable/webexposed/web-animations-api-expected.txt
@@ -2,6 +2,8 @@
 PASS Element.animate() should be exposed.
 PASS Element.getAnimations() should not be exposed without experimental web platform features enabled.
 This test is expected to fail in LayoutTests/webexposed.
+PASS Animation constructor should not be exposed without experimental web platform features enabled.
+This test is expected to fail in LayoutTests/webexposed.
 PASS Timeline should not be exposed without experimental web platform features enabled.
 This test is expected to fail in LayoutTests/webexposed.
 PASS document.timeline should not be exposed without experimental web platform features enabled.
diff --git a/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCDataChannel-send-expected.txt b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCDataChannel-send-expected.txt
new file mode 100644
index 0000000..b9e5fe0e
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCDataChannel-send-expected.txt
@@ -0,0 +1,14 @@
+This is a testharness.js-based test.
+PASS Calling send() when data channel is in connecting state should throw InvalidStateError
+PASS Data channel should be able to send simple string and receive as string
+PASS Data channel should be able to send unicode string and receive as unicode string
+PASS Data channel should ignore binaryType and always receive string message as string
+PASS Data channel should be able to send Uint8Array message and receive as ArrayBuffer
+PASS Data channel should be able to send ArrayBuffer message and receive as ArrayBuffer
+FAIL Data channel should be able to send Blob message and receive as ArrayBuffer promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'send' on 'RTCDataChannel': Blob support not implemented yet"
+FAIL Data channel should be able to send ArrayBuffer message and receive as Blob promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to set the 'binaryType' property on 'RTCDataChannel': Blob support not implemented yet"
+FAIL Data channel binaryType should receive message as Blob by default assert_equals: Expect initial binaryType value to be blob expected "blob" but got "arraybuffer"
+FAIL Sending multiple messages with different types should succeed and be received assert_unreached: Unexpected promise rejection: NotSupportedError: Failed to execute 'send' on 'RTCDataChannel': Blob support not implemented yet Reached unreachable code
+FAIL Calling send() up to max size should succeed, above max size should fail promise_test: Unhandled rejection with value: object "TypeError: Cannot read property 'maxMessageSize' of null"
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/webexposed/web-animations-api-expected.txt b/third_party/blink/web_tests/webexposed/web-animations-api-expected.txt
index f00a887..c9ca885 100644
--- a/third_party/blink/web_tests/webexposed/web-animations-api-expected.txt
+++ b/third_party/blink/web_tests/webexposed/web-animations-api-expected.txt
@@ -2,6 +2,8 @@
 PASS Element.animate() should be exposed.
 FAIL Element.getAnimations() should not be exposed without experimental web platform features enabled.
 This test is expected to fail in LayoutTests/webexposed. assert_false: expected false got true
+FAIL Animation constructor should not be exposed without experimental web platform features enabled.
+This test is expected to fail in LayoutTests/webexposed. assert_false: expected false got true
 FAIL Timeline should not be exposed without experimental web platform features enabled.
 This test is expected to fail in LayoutTests/webexposed. assert_false: expected false got true
 FAIL document.timeline should not be exposed without experimental web platform features enabled.
diff --git a/third_party/blink/web_tests/webexposed/web-animations-api.html b/third_party/blink/web_tests/webexposed/web-animations-api.html
index cb621d0..403e292 100644
--- a/third_party/blink/web_tests/webexposed/web-animations-api.html
+++ b/third_party/blink/web_tests/webexposed/web-animations-api.html
@@ -11,6 +11,10 @@
 }, 'Element.getAnimations() should not be exposed without experimental web platform features enabled.\nThis test is expected to fail in LayoutTests/webexposed.')
 
 test(function() {
+    assert_false('Animation' in window);
+},'Animation constructor should not be exposed without experimental web platform features enabled.\nThis test is expected to fail in LayoutTests/webexposed.');
+
+test(function() {
     assert_false('AnimationTimeline' in window);
 },'Timeline should not be exposed without experimental web platform features enabled.\nThis test is expected to fail in LayoutTests/webexposed.');
 
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml
index eaf5104..77b9db073 100644
--- a/tools/metrics/actions/actions.xml
+++ b/tools/metrics/actions/actions.xml
@@ -16404,6 +16404,15 @@
   </description>
 </action>
 
+<action name="ProfileChooser_ManageGoogleAccountClicked">
+  <owner>droger@chromium.org</owner>
+  <owner>msarda@chromium.org</owner>
+  <owner>tangltom@chromium.org</owner>
+  <description>
+    User clicked 'Manage your Google Account' in the profile chooser menu.
+  </description>
+</action>
+
 <action name="ProfileChooser_PasswordsClicked">
   <owner>vasilii@chromium.org</owner>
   <owner>ewald@chromium.org</owner>
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index be215bb..37543477 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -22720,8 +22720,6 @@
   <int value="2847" label="HTMLImportsOnReverseOriginTrials"/>
   <int value="2848" label="ElementCreateShadowRootOnReverseOriginTrials"/>
   <int value="2849" label="DocumentRegisterElementOnReverseOriginTrials"/>
-  <int value="2850" label="V8Animation_Effect_AttributeGetter"/>
-  <int value="2851" label="V8Animation_Effect_AttributeSetter"/>
 </enum>
 
 <enum name="FeaturePolicyFeature">
@@ -57390,6 +57388,7 @@
   <int value="12" label="External default-install on Chrome OS"/>
   <int value="13" label="Policy-install on Chrome OS"/>
   <int value="14" label="System default app on Chrome OS"/>
+  <int value="15" label="Install icon in the Omnibox"/>
 </enum>
 
 <enum name="WebappUninstallDialogAction">
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index e935b1a..7bc8402a 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -27027,6 +27027,7 @@
 
 <histogram name="Download.DangerousFile.Reason" enum="DangerousFile.Reason"
     expires_after="M77">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     Indicates why a download is marked as DANGEROUS_FILE. Grouped by reason,
@@ -27182,6 +27183,7 @@
 
 <histogram name="Download.DownloadDangerPrompt"
     enum="SBClientDownloadExtensions">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     Records when user is shown the download danger prompt while attempting to
@@ -28287,6 +28289,7 @@
 
 <histogram name="Download.TargetConnectionSecurity"
     enum="DownloadConnectionSecurity" expires_after="M77">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     The connection security state of a download, indicating whether its final
@@ -34127,6 +34130,7 @@
 
 <histogram name="ExtensionBlacklist.BlacklistInstalled"
     enum="ExtensionLocation">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     The number of extensions that were blacklisted when already installed,
@@ -34137,6 +34141,7 @@
 
 <histogram name="ExtensionBlacklist.BlockCRX" enum="ExtensionLocation"
     expires_after="M77">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     The number of extensions that have been blocked from installing grouped by
@@ -34146,6 +34151,7 @@
 </histogram>
 
 <histogram name="ExtensionBlacklist.SilentInstall" enum="ExtensionLocation">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     The number of extensions that have been silently installed in a blacklisted
@@ -34158,6 +34164,7 @@
 
 <histogram name="ExtensionBlacklist.UnblacklistInstalled"
     enum="ExtensionLocation" expires_after="M77">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     The number of extensions that were unblacklisted when installed, grouped by
@@ -86549,6 +86556,7 @@
 <histogram
     name="PasswordManager.IsSyncPasswordHashSavedForAdvancedProtectionUser"
     enum="IsSyncPasswordHashSaved" expires_after="2019-10-05">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     This metric is recorded shortly after Chrome Startup, only for Advanced
@@ -87622,6 +87630,7 @@
 
 <histogram name="PasswordProtection.ChromeSettingsAction"
     enum="PasswordProtectionWarningAction">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     Records how a user interacts with the chrome://settings page that displays
@@ -87633,6 +87642,7 @@
 <histogram
     name="PasswordProtection.GaiaPasswordReusesBeforeGaiaPasswordChanged"
     units="reuses">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     The number of Gaia password reuse warnings shown to user before user changes
@@ -87645,6 +87655,7 @@
 
 <histogram name="PasswordProtection.InterstitialAction"
     enum="PasswordProtectionWarningAction">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     Records how a user interacts with the password protection interstitial
@@ -87668,6 +87679,7 @@
 
 <histogram name="PasswordProtection.InterstitialString"
     enum="PasswordProtectionInterstitialStringType">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     Records the type of strings shown to the user on chrome://reset-password
@@ -87677,6 +87689,7 @@
 
 <histogram name="PasswordProtection.ModalWarningDialogAction"
     enum="PasswordProtectionWarningAction">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     Records how a user interacts with the password protection modal warning
@@ -87715,6 +87728,7 @@
 
 <histogram name="PasswordProtection.PageInfoAction"
     enum="PasswordProtectionWarningAction">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     Records how a user interacts with page info bubble that displays the
@@ -87725,6 +87739,7 @@
 </histogram>
 
 <histogram name="PasswordProtection.PageZoomFactor">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     Reports the zoom factor for a login page, when the protected password is
@@ -87734,6 +87749,7 @@
 
 <histogram base="true" name="PasswordProtection.PasswordAlertModeOutcome"
     enum="PasswordProtectionRequestOutcome">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     Records the outcome of the password alert mode, indicating if password alert
@@ -87743,6 +87759,7 @@
 
 <histogram name="PasswordProtection.PasswordProtectionResponseOrErrorCode"
     enum="CombinedHttpResponseAndNetErrorCode">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     Response or error codes for PasswordProtectionRequest. Logged after chrome
@@ -87764,6 +87781,7 @@
 
 <histogram name="PasswordProtection.PasswordReuseSyncAccountType"
     enum="PasswordProtectionSyncAccountType">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     When password protection service detects a reuse of Chrome sync password,
@@ -87786,6 +87804,7 @@
 </histogram>
 
 <histogram name="PasswordProtection.RequestNetworkDuration" units="ms">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     The time it takes for PasswordProtectionService request. It is not recorded
@@ -87795,6 +87814,7 @@
 
 <histogram base="true" name="PasswordProtection.RequestOutcome"
     enum="PasswordProtectionRequestOutcome">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     Records the outcome of the password protection request, indicating if
@@ -87829,6 +87849,7 @@
 </histogram>
 
 <histogram name="PasswordProtection.Verdict" enum="PasswordProtectionVerdict">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     Verdict types returned by Safe Browsing server for a password protection
@@ -87838,6 +87859,7 @@
 </histogram>
 
 <histogram name="PasswordProtection.VisualFeatureExtractionDuration" units="ms">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     The time it takes to extract the visual features of a login page before
@@ -102425,6 +102447,7 @@
 
 <histogram name="SafeBrowsing.AdvancedProtection.APTokenFetchStatus"
     enum="GoogleServiceAuthError">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     For users known to be already enrolled in Advanced Proection, records the
@@ -102437,6 +102460,7 @@
 
 <histogram name="SafeBrowsing.AdvancedProtection.TokenFetchStatus"
     enum="GoogleServiceAuthError">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     Records the service error of refreshing OAuth2 access token during fetching
@@ -102467,6 +102491,7 @@
 
 <histogram name="SafeBrowsing.ContentsSize.Height" units="DIPs"
     expires_after="M77">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     Records the height of content area when the user opens a new browser window
@@ -102476,6 +102501,7 @@
 
 <histogram name="SafeBrowsing.ContentsSize.Width" units="DIPs"
     expires_after="M77">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     Records the width of content area when the user opens a new browser window
@@ -102497,6 +102523,7 @@
 
 <histogram name="SafeBrowsing.FileTypeUpdate.DynamicUpdateResult"
     enum="SBFileTypeUpdateResult" expires_after="M77">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     The result of reading/parsing/accepting a new proto for the FileTypePolices
@@ -102510,6 +102537,7 @@
 
 <histogram name="SafeBrowsing.FileTypeUpdate.DynamicUpdateTypeCount"
     units="number of file types" expires_after="M77">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     Number of file types (aka file extensions) present in the FileTypePolicies
@@ -102523,6 +102551,7 @@
 
 <histogram name="SafeBrowsing.FileTypeUpdate.DynamicUpdateVersion"
     units="FileTypePolicies Version">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     Integer version number citing which version of the proto data chrome just
@@ -102536,6 +102565,7 @@
 
 <histogram name="SafeBrowsing.FileTypeUpdate.ResourceBundleResult"
     enum="SBFileTypeUpdateResult" expires_after="M77">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     The result of reading/parsing/accepting a new proto for the FileTypePolices
@@ -102548,6 +102578,7 @@
 
 <histogram name="SafeBrowsing.FileTypeUpdate.ResourceBundleTypeCount"
     units="number of file types" expires_after="M77">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     Number of file types (aka file extensions) present in the FileTypePolicies
@@ -102560,6 +102591,7 @@
 
 <histogram name="SafeBrowsing.FileTypeUpdate.ResourceBundleVersion"
     units="FileTypePolicies Version">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     Integer version number citing which version of the proto data chrome just
@@ -102571,23 +102603,27 @@
 </histogram>
 
 <histogram name="SafeBrowsing.FontSize.Default" units="pts" expires_after="M77">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>Records the default font size on user startup.</summary>
 </histogram>
 
 <histogram name="SafeBrowsing.FontSize.DefaultFixed" units="pts"
     expires_after="M77">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>Records the default fixed font size on user startup.</summary>
 </histogram>
 
 <histogram name="SafeBrowsing.FontSize.Minimum" units="pts" expires_after="M77">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>Records the minimum font size on user startup.</summary>
 </histogram>
 
 <histogram name="SafeBrowsing.FontSize.MinimumLogical" units="pts"
     expires_after="M77">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>Records the minimum logical font size on user startup.</summary>
 </histogram>
@@ -102661,6 +102697,7 @@
 
 <histogram name="SafeBrowsing.NavigationObserver.NavigationEventCleanUpCount"
     expires_after="M77">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     Count of how many NavigationEvents get removed in each periodic clean up.
@@ -102670,6 +102707,7 @@
 
 <histogram name="SafeBrowsing.NotificationImageReporter.NetError"
     enum="NetErrorCodes">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     The net error code for failed reports sent by NotificationImageReporter, or
@@ -102700,6 +102738,7 @@
 </histogram>
 
 <histogram name="SafeBrowsing.Pref.Extended" enum="BooleanEnabled">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     Whether the Safe Browsing extended reporting service is currently enabled.
@@ -102708,6 +102747,7 @@
 </histogram>
 
 <histogram name="SafeBrowsing.Pref.General" enum="BooleanEnabled">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     Whether the Safe Browsing service is currently enabled. Recorded for all
@@ -102764,6 +102804,7 @@
 
 <histogram name="SafeBrowsing.ReferrerAttributionResult"
     enum="SafeBrowsingAttributionResultTypes">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     The result of referrer attribution, including different types of success or
@@ -102773,6 +102814,7 @@
 </histogram>
 
 <histogram name="SafeBrowsing.ReferrerHasInvalidTabID" enum="BooleanInvalid">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     Number of times referrer attribution encounters an invalid tab ID. This is
@@ -102782,6 +102824,7 @@
 </histogram>
 
 <histogram name="SafeBrowsing.ReferrerURLChainSize">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     The length of referrer URL chain we get from referrer attribution. This is
@@ -102948,6 +102991,7 @@
 </histogram>
 
 <histogram name="SafeBrowsing.V4Database.Size" units="KB">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     The size of the SafeBrowsing database or file on disk in kilobytes, after
@@ -102957,6 +103001,7 @@
 
 <histogram name="SafeBrowsing.V4DatabaseOpen.Time" units="ms"
     expires_after="M77">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     The time it takes to parse and load the SafeBrowsing database from disk, in
@@ -103026,6 +103071,7 @@
 
 <histogram name="SafeBrowsing.V4GetHash.CacheHit.Result"
     enum="SafeBrowsingV4FullHashCacheResult">
+  <owner>vakh@chromium.org</owner>
   <owner>kcarattini@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>Track cache hits for V4 full hashes.</summary>
@@ -103033,6 +103079,7 @@
 
 <histogram name="SafeBrowsing.V4GetHash.Check.Result"
     enum="SafeBrowsingV4GetHashCheckResult" expires_after="M77">
+  <owner>vakh@chromium.org</owner>
   <owner>kcarattini@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>Track get hash response hits for V4 full hash requests.</summary>
@@ -103040,8 +103087,9 @@
 
 <histogram name="SafeBrowsing.V4GetHash.Network.Result"
     enum="CombinedHttpResponseAndNetErrorCode">
-  <owner>kcarattini@google.com</owner>
   <owner>vakh@google.com</owner>
+  <owner>kcarattini@google.com</owner>
+  <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     Response or error codes from the SafeBrowsing Pver4 service. Logged after a
     GetHash or request finishes to capture the response code or error code for
@@ -103050,6 +103098,7 @@
 </histogram>
 
 <histogram name="SafeBrowsing.V4GetHash.Network.Time" units="ms">
+  <owner>vakh@chromium.org</owner>
   <owner>kcarattini@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
@@ -103060,6 +103109,7 @@
 
 <histogram name="SafeBrowsing.V4GetHash.Parse.Result"
     enum="SafeBrowsingParseV4HashResult" expires_after="M77">
+  <owner>vakh@chromium.org</owner>
   <owner>kcarattini@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
@@ -103069,6 +103119,7 @@
 
 <histogram name="SafeBrowsing.V4GetHash.Result"
     enum="SafeBrowsingV4OperationResult">
+  <owner>vakh@chromium.org</owner>
   <owner>kcarattini@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
@@ -103133,6 +103184,7 @@
 
 <histogram name="SafeBrowsing.V4LocalDatabaseManager.AreAllStoresAvailableNow"
     enum="SafeBrowsingStoreAvailabilityResult">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     Records the outcome of a check to AreAllStoresAvailableNow. This will be
@@ -103156,6 +103208,7 @@
 
 <histogram name="SafeBrowsing.V4ProcessFullUpdate.ApplyUpdate.Result"
     enum="SafeBrowsingV4ApplyUpdateResult">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     Track the result of applying a full update for a store received from PVer4
@@ -103165,6 +103218,7 @@
 
 <histogram name="SafeBrowsing.V4ProcessFullUpdate.ApplyUpdate.Time" units="ms"
     expires_after="M77">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     Records the time it takes to process a SafeBrowsing list full update. It
@@ -103177,6 +103231,7 @@
 
 <histogram name="SafeBrowsing.V4ProcessFullUpdate.DecodeAdditions.Result"
     enum="SafeBrowsingV4DecodeResult" expires_after="M77">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     Track the result of decoding the Rice-encoded list of additions of 4-byte
@@ -103188,6 +103243,7 @@
 
 <histogram name="SafeBrowsing.V4ProcessFullUpdate.DecodeAdditions.Time"
     units="ms" expires_after="M77">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     Records the time it takes to decode the Rice-encoded additions to the
@@ -103223,6 +103279,7 @@
 
 <histogram name="SafeBrowsing.V4ProcessPartialUpdate.ApplyUpdate.Result"
     enum="SafeBrowsingV4ApplyUpdateResult">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     Track the result of applying a partial update for a store received from
@@ -103232,6 +103289,7 @@
 
 <histogram name="SafeBrowsing.V4ProcessPartialUpdate.ApplyUpdate.Time"
     units="ms" expires_after="M77">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     Records the time it takes to process a SafeBrowsing list partial update. It
@@ -103246,6 +103304,7 @@
 
 <histogram name="SafeBrowsing.V4ProcessPartialUpdate.DecodeAdditions.Result"
     enum="SafeBrowsingV4DecodeResult" expires_after="M77">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     Track the result of decoding the Rice-encoded list of additions of 4-byte
@@ -103257,6 +103316,7 @@
 
 <histogram name="SafeBrowsing.V4ProcessPartialUpdate.DecodeAdditions.Time"
     units="ms" expires_after="M77">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     Records the time it takes to decode the Rice-encoded additions to the
@@ -103266,6 +103326,7 @@
 
 <histogram name="SafeBrowsing.V4ProcessPartialUpdate.DecodeRemovals.Result"
     enum="SafeBrowsingV4DecodeResult" expires_after="M77">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     Track the result of decoding the Rice-encoded list of indexes of hash
@@ -103276,6 +103337,7 @@
 
 <histogram name="SafeBrowsing.V4ProcessPartialUpdate.DecodeRemovals.Time"
     units="ms" expires_after="M77">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     Records the time it takes to decode the Rice-encoded removals from the
@@ -103312,6 +103374,7 @@
 
 <histogram name="SafeBrowsing.V4ReadFromDisk.ApplyUpdate.Result"
     enum="SafeBrowsingV4ApplyUpdateResult" expires_after="M77">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     Track the result of applying an ListUpdateResponse read from disk after
@@ -103321,6 +103384,7 @@
 
 <histogram name="SafeBrowsing.V4ReadFromDisk.ApplyUpdate.Time" units="ms"
     expires_after="M77">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     Records the time it takes to read, parse, and process a SafeBrowsing list
@@ -103334,6 +103398,7 @@
 
 <histogram name="SafeBrowsing.V4ReadFromDisk.DecodeAdditions.Result"
     enum="SafeBrowsingV4DecodeResult" expires_after="M77">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     Track the result of decoding the Rice-encoded list of additions of 4-byte
@@ -103344,6 +103409,7 @@
 
 <histogram name="SafeBrowsing.V4ReadFromDisk.DecodeAdditions.Time" units="ms"
     expires_after="M77">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     Records the time it takes to decode the Rice-encoded additions to the
@@ -103380,6 +103446,7 @@
 
 <histogram name="SafeBrowsing.V4Store.IsStoreAvailable.ValidStore"
     enum="BooleanAvailable">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     Records how often IsStoreAvailable fails due to an invalid store identifier.
@@ -103389,6 +103456,7 @@
 </histogram>
 
 <histogram name="SafeBrowsing.V4Store.IsStoreValid" enum="BooleanValid">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     Records whether a store has valid data, when it is queried. This is logged
@@ -103399,6 +103467,7 @@
 
 <histogram name="SafeBrowsing.V4StoreRead.Result"
     enum="SafeBrowsingV4StoreReadResult" expires_after="M77">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     Track the parsing results of reading the SafeBrowsing V4 store file from
@@ -103420,6 +103489,7 @@
 
 <histogram name="SafeBrowsing.V4StoreVersionRead" units="version number"
     expires_after="M77">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     Version of V4Store read from a store file. This would be useful in tracking
@@ -103429,6 +103499,7 @@
 
 <histogram name="SafeBrowsing.V4StoreWrite.Result"
     enum="SafeBrowsingV4StoreWriteResult" expires_after="M77">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     Track the results of writing the SafeBrowsing V4 store file to disk.
@@ -103449,6 +103520,7 @@
 </histogram>
 
 <histogram name="SafeBrowsing.V4UnusedStoreFileExists" enum="BooleanExists">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     Track the presence of store files that were previously created but have been
@@ -103470,6 +103542,7 @@
 
 <histogram name="SafeBrowsing.V4Update.Network.Result"
     enum="CombinedHttpResponseAndNetErrorCode">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     Response or error codes when fetching updates from the SafeBrowsing PVer4
@@ -103479,6 +103552,7 @@
 
 <histogram name="SafeBrowsing.V4Update.Parse.Result"
     enum="SafeBrowsingParseV4UpdateResult" expires_after="M77">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     Track the parsing results of a status 200 GetV4Update request.
@@ -103486,6 +103560,7 @@
 </histogram>
 
 <histogram name="SafeBrowsing.V4Update.ResponseSizeKB" units="KB">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     The size of the response sent by the SafeBrowsing PVer4 service, in KB.
@@ -103494,6 +103569,7 @@
 
 <histogram name="SafeBrowsing.V4Update.Result"
     enum="SafeBrowsingV4OperationResult" expires_after="M77">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     Track return status from V4 update attempts. The buckets of this histogram
@@ -103503,6 +103579,7 @@
 
 <histogram name="SafeBrowsing.V4Update.TimedOut" enum="BooleanTimedOut"
     expires_after="M77">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>True if a PVer4 update request timed out.</summary>
 </histogram>
@@ -103940,6 +104017,7 @@
 </histogram>
 
 <histogram name="SB2.Delay" units="ms">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     The time that SafeBrowsing actually delayed the browsing experience. It
@@ -103951,6 +104029,7 @@
 </histogram>
 
 <histogram name="SB2.Delay.MainFrame" units="ms">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     The time that SafeBrowsing actually delayed the browsing experience. It
@@ -103961,6 +104040,7 @@
 </histogram>
 
 <histogram name="SB2.Delay.Subresource" units="ms">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     The time that SafeBrowsing actually delayed the browsing experience. It
@@ -103988,6 +104068,7 @@
 
 <histogram name="SB2.DownloadChecks" enum="SB2DownloadChecks"
     expires_after="M77">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     Records results of SafeBrowsing download check, including both url check and
@@ -104029,6 +104110,7 @@
 </histogram>
 
 <histogram name="SB2.DownloadUrlCheckDuration" units="ms" expires_after="M77">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>The time it takes for SafeBrowsing to check a download url.</summary>
 </histogram>
@@ -104360,6 +104442,7 @@
 </histogram>
 
 <histogram name="SB2.Network" units="ms">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     The time that it took to receive a response from the Google SafeBrowsing
@@ -104583,6 +104666,7 @@
 </histogram>
 
 <histogram name="SB2.RemoteCall.CanCheckUrl" enum="BooleanCanCheckUrl">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     Count of how many URLs were actually checked vs skipped via
@@ -104593,6 +104677,7 @@
 
 <histogram name="SB2.RemoteCall.CanUseLocalBlacklists"
     enum="SB2RemoteCallCanUseLocalBlacklists" expires_after="M77">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     Indicates whether the local Safe Browsing blacklists can be used on this
@@ -104601,6 +104686,7 @@
 </histogram>
 
 <histogram name="SB2.RemoteCall.CheckDelta" units="microseconds">
+  <owner>vakh@chromium.org</owner>
   <owner>csharrison@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
@@ -104651,6 +104737,7 @@
 </histogram>
 
 <histogram name="SB2.RemoteCall.Elapsed" units="ms">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     Latency of URL-classification API calls from Chrome via
@@ -104678,6 +104765,7 @@
 
 <histogram name="SB2.RemoteCall.InternalErrorStatusCode2"
     enum="GooglePlayServicesConnectionResult" expires_after="M77">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     The status code provided by GmsCore if it is unable to respond to a URL
@@ -104690,6 +104778,7 @@
 
 <histogram name="SB2.RemoteCall.LocalBlacklistsAgeInMs" units="ms"
     expires_after="M77">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     Indicates the time since the local blacklists were successfully updated
@@ -104701,6 +104790,7 @@
 
 <histogram name="SB2.RemoteCall.LocalBlacklistsLookupResult"
     enum="SB2RemoteCallLocalBlacklistsLookupResult">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     Indicates the result of local Safe Browsing blacklist lookup on Android.
@@ -104711,6 +104801,7 @@
 
 <histogram name="SB2.RemoteCall.LocalBlacklistsUpdateSucceeded"
     enum="BooleanSuccess" expires_after="M77">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     Indicates the result of local blacklists' update. Logged each time an update
@@ -104720,6 +104811,7 @@
 </histogram>
 
 <histogram name="SB2.RemoteCall.Result" enum="SB2RemoteCallResult">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     Result of URL-classification API calls from Chrome via
@@ -104731,6 +104823,7 @@
 
 <histogram name="SB2.RemoteCall.ThreatSubType.PotentiallyHarmfulApp"
     enum="SB2RemoteCallThreatSubType">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     The threat sub-type annotated for URLs classified as PHA via remote calls
@@ -104741,6 +104834,7 @@
 
 <histogram name="SB2.RemoteCall.ThreatSubType.SocialEngineering"
     enum="SB2RemoteCallThreatSubType">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     The threat sub-type annotated returned for URLs classified as social
@@ -104776,6 +104870,7 @@
 </histogram>
 
 <histogram name="SB2.ResourceTypes2" enum="ContentResourceType2">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     Resource types of resources that were inspected by Safe Browsing in the
@@ -104983,6 +105078,7 @@
 
 <histogram name="SBClientDownload.CheckDownloadStats"
     enum="SBClientDownloadCheckDownloadStats">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <owner>mattm@chromium.org</owner>
   <summary>
@@ -104997,6 +105093,7 @@
 
 <histogram name="SBClientDownload.CheckWhitelistResult"
     enum="WhitelistedDownloadType">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     For each download supported by the SafeBrowsing download protection service,
@@ -105007,6 +105104,7 @@
 
 <histogram name="SBClientDownload.DmgFileArchivedBinariesCount" units="count"
     expires_after="M77">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     The original number of archived_binaries found in a DMG-like file when it's
@@ -105017,6 +105115,7 @@
 
 <histogram name="SBClientDownload.DmgFileFailureByType"
     enum="SBClientDownloadExtensions" expires_after="M77">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     Counts of DMG-like file types that failed to be successfully analyzed by the
@@ -105038,6 +105137,7 @@
 
 <histogram name="SBClientDownload.DmgFileHasExecutableByType"
     enum="SBClientDownloadExtensions" expires_after="M77">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     Counts of DMG-like file types which were analyzed by the SafeBrowsing
@@ -105047,6 +105147,7 @@
 
 <histogram name="SBClientDownload.DmgFileHasNoExecutableByType"
     enum="SBClientDownloadExtensions" expires_after="M77">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     Counts of DMG-like file types which were analyzed by the SafeBrowsing
@@ -105069,6 +105170,7 @@
 
 <histogram name="SBClientDownload.DmgFileSuccessByType"
     enum="SBClientDownloadExtensions">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     Counts of DMG-like file types that were successfully analyzed by the
@@ -105078,6 +105180,7 @@
 
 <histogram name="SBClientDownload.DmgTooBigToUnpack" enum="Boolean"
     expires_after="M77">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     Records whether a given DMG download was too big to unpack, as specified by
@@ -105088,6 +105191,7 @@
 
 <histogram name="SBClientDownload.DownloadExtensions"
     enum="SBClientDownloadExtensions">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <owner>mattm@chromium.org</owner>
   <summary>
@@ -105098,6 +105202,7 @@
 
 <histogram name="SBClientDownload.DownloadFileHasDetachedSignatures"
     enum="Boolean" expires_after="M77">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     A Mac-only metric that records whether a given download contains a detached
@@ -105107,6 +105212,7 @@
 </histogram>
 
 <histogram name="SBClientDownload.DownloadFileHasDmgSignature" enum="Boolean">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     A Mac-only metric that records whether a given download file is a
@@ -105118,6 +105224,7 @@
 <histogram
     name="SBClientDownload.DownloadFileWithoutDiskImageExtensionHasKolySignature"
     enum="Boolean">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     Records whether download files without an Apple disk image file extension
@@ -105128,6 +105235,7 @@
 </histogram>
 
 <histogram name="SBClientDownload.DownloadRequestDuration" units="ms">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <owner>mattm@chromium.org</owner>
   <summary>
@@ -105141,6 +105249,7 @@
 </histogram>
 
 <histogram name="SBClientDownload.DownloadRequestNetError" enum="NetErrorCodes">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <owner>mattm@chromium.org</owner>
   <summary>
@@ -105149,6 +105258,7 @@
 </histogram>
 
 <histogram name="SBClientDownload.DownloadRequestNetworkDuration" units="ms">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <owner>mattm@chromium.org</owner>
   <summary>
@@ -105159,6 +105269,7 @@
 
 <histogram name="SBClientDownload.DownloadRequestNetworkStats"
     enum="SBClientDownloadCheckDownloadStats">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <owner>mattm@chromium.org</owner>
   <summary>
@@ -105168,6 +105279,7 @@
 </histogram>
 
 <histogram name="SBClientDownload.DownloadRequestPayloadSize" units="bytes">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <owner>mattm@chromium.org</owner>
   <summary>
@@ -105177,6 +105289,7 @@
 
 <histogram name="SBClientDownload.DownloadRequestResponseCode"
     enum="HttpResponseCode">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <owner>mattm@chromium.org</owner>
   <summary>
@@ -105187,6 +105300,7 @@
 
 <histogram name="SBClientDownload.DownloadRequestTimeoutDuration" units="ms"
     expires_after="M77">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <owner>mattm@chromium.org</owner>
   <summary>
@@ -105199,6 +105313,7 @@
 
 <histogram name="SBClientDownload.DownloadRequestTimeoutStats"
     enum="SBClientDownloadCheckDownloadStats" expires_after="M77">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <owner>mattm@chromium.org</owner>
   <summary>
@@ -105210,6 +105325,7 @@
 
 <histogram name="SBClientDownload.ExtractDmgFeaturesTime" units="ms"
     expires_after="M77">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <owner>rsesek@chromium.org</owner>
   <summary>
@@ -105220,6 +105336,7 @@
 
 <histogram name="SBClientDownload.ExtractImageHeadersTime" units="ms"
     expires_after="M77">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <owner>grt@chromium.org</owner>
   <summary>
@@ -105229,6 +105346,7 @@
 </histogram>
 
 <histogram name="SBClientDownload.ExtractRarFeaturesTime" units="ms">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     Records the time it takes for the SafeBrowsing download service to extract
@@ -105238,6 +105356,7 @@
 
 <histogram name="SBClientDownload.ExtractSignatureFeaturesTime" units="ms"
     expires_after="M77">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <owner>mattm@chromium.org</owner>
   <summary>
@@ -105248,6 +105367,7 @@
 </histogram>
 
 <histogram name="SBClientDownload.ExtractZipFeaturesTime" units="ms">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <owner>mattm@chromium.org</owner>
   <summary>
@@ -105291,6 +105411,7 @@
 </histogram>
 
 <histogram name="SBClientDownload.RarFileArchivedBinariesCount" units="count">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     The original number of archived_binaries found in a rar-like file when it's
@@ -105301,6 +105422,7 @@
 
 <histogram name="SBClientDownload.RarFileHasArchiveButNoExecutable"
     enum="Boolean">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     For each rar file analyzed by the SafeBrowsing download service, records
@@ -105310,6 +105432,7 @@
 </histogram>
 
 <histogram name="SBClientDownload.RarFileHasExecutable" enum="Boolean">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     For each rar file analyzed by the SafeBrowsing download service, records if
@@ -105318,6 +105441,7 @@
 </histogram>
 
 <histogram name="SBClientDownload.RarFileSuccess" enum="BooleanSuccess">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     For each rar file analyzed by the SafeBrowsing download service, records if
@@ -105327,6 +105451,7 @@
 
 <histogram name="SBClientDownload.RarHeadersEncrypted" enum="Boolean"
     expires_after="M79">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     Records whether the RAR headers are encrypted, preventing any meaningful
@@ -105337,6 +105462,7 @@
 </histogram>
 
 <histogram name="SBClientDownload.RarOpenSuccess" enum="BooleanSuccess">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     For each rar file analyzed by the SafeBrowsing download service, records if
@@ -105347,6 +105473,7 @@
 </histogram>
 
 <histogram name="SBClientDownload.RarValidArchive" enum="BooleanSuccess">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     For each rar file analyzed by the SafeBrowsing download service, records if
@@ -105358,6 +105485,7 @@
 
 <histogram name="SBClientDownload.SignedBinaryDownload"
     enum="SBClientDownloadIsSignedBinary" expires_after="M77">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <owner>mattm@chromium.org</owner>
   <summary>
@@ -105380,6 +105508,7 @@
 
 <histogram name="SBClientDownload.UnsupportedScheme"
     enum="SBClientDownloadExtensions">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     Records how often different file extensions are downloaded with schemes that
@@ -105388,6 +105517,7 @@
 </histogram>
 
 <histogram name="SBClientDownload.ZipFileArchivedBinariesCount" units="count">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     The original number of archived_binaries found in a zip-like file when it's
@@ -105411,6 +105541,7 @@
 
 <histogram name="SBClientDownload.ZipFileFailureByType"
     enum="SBClientDownloadExtensions" expires_after="M77">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     Counts of ZIP-like file types that failed to be successfully analyzed by the
@@ -105420,6 +105551,7 @@
 
 <histogram name="SBClientDownload.ZipFileHasArchiveButNoExecutable"
     enum="Boolean">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <owner>mattm@chromium.org</owner>
   <summary>
@@ -105430,6 +105562,7 @@
 </histogram>
 
 <histogram name="SBClientDownload.ZipFileHasExecutable" enum="Boolean">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <owner>mattm@chromium.org</owner>
   <summary>
@@ -105440,6 +105573,7 @@
 
 <histogram name="SBClientDownload.ZipFileLocalFileHeadersSkipped" units="count"
     expires_after="M75">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     The number of Local File Headers (identified by their magic number) present
@@ -105452,6 +105586,7 @@
 
 <histogram name="SBClientDownload.ZipFileSuccess" enum="BooleanSuccess"
     expires_after="M77">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     For each zip file analyzed by the SafeBrowsing download service, records if
@@ -105461,6 +105596,7 @@
 
 <histogram name="SBClientDownload.ZipFileSuccessByType"
     enum="SBClientDownloadExtensions">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     Counts of ZIP-like file types were successfully analyzed by the SafeBrowsing
@@ -105469,6 +105605,7 @@
 </histogram>
 
 <histogram name="SBClientDownload.ZipTooBigToUnpack" enum="Boolean">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     Records whether a given ZIP download was too big to unpack, as specified by
@@ -105479,6 +105616,7 @@
 
 <histogram name="SBClientMalware.ClassificationStart" enum="BooleanHit"
     expires_after="M77">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     The number of pages that we could have possibly classified (essentially the
@@ -105490,6 +105628,7 @@
 
 <histogram name="SBClientMalware.IPBlacklistRequestNetError"
     enum="NetErrorCodes">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     The net error code for all ClientMalwareRequest URLFetchers.
@@ -105497,6 +105636,7 @@
 </histogram>
 
 <histogram name="SBClientMalware.IPBlacklistRequestPayloadSize" units="bytes">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     The size of the upload data for ClientMalwareRequest URLFetchers.
@@ -105505,6 +105645,7 @@
 
 <histogram name="SBClientMalware.IPBlacklistRequestResponseCode"
     enum="HttpResponseCode">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     For ClientMalwareRequest URLFetchers with successful status, the HTTP
@@ -105514,6 +105655,7 @@
 
 <histogram name="SBClientMalware.PreClassificationCheckFail"
     enum="SBClientDetectionPreClassificationCheckFail" expires_after="M77">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     Records the number of malware classifications that were skipped because a
@@ -105523,6 +105665,7 @@
 
 <histogram name="SBClientMalware.ResourceUrlMatchedBadIp"
     enum="BooleanMatchedBadIp" expires_after="M77">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     True if at least one resource url matched the malware IP list. Recorded when
@@ -105531,6 +105674,7 @@
 </histogram>
 
 <histogram name="SBClientMalware.SentReports" enum="SBClientMalwareSentReports">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     Measures the success rate of sending malware reports. Sending a report can
@@ -105541,6 +105685,7 @@
 
 <histogram name="SBClientMalware.ServerDeterminesMalware"
     enum="BooleanIsMalware">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>The counts for malware verdicts given by server side model.</summary>
 </histogram>
@@ -105559,6 +105704,7 @@
 
 <histogram name="SBClientPhishing.CancelClassificationReason"
     enum="SBClientPhishingCancelClassificationReason" expires_after="M77">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     The counts for various reasons why an in-progress phishing classification
@@ -105580,6 +105726,7 @@
 
 <histogram name="SBClientPhishing.ClassificationStart" enum="BooleanHit"
     expires_after="M77">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     The number of pages that we could have possibly classified (essentially the
@@ -105591,6 +105738,7 @@
 
 <histogram name="SBClientPhishing.ClientDeterminesPhishing"
     enum="BooleanIsPhishing">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     The counts for phishing verdicts given by client side model.
@@ -105609,6 +105757,7 @@
 
 <histogram name="SBClientPhishing.ClientModelStatus"
     enum="SBClientPhishingClientModelStatus" expires_after="M77">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     The counts for various model status codes that we get after loading a new
@@ -105677,6 +105826,7 @@
 </histogram>
 
 <histogram name="SBClientPhishing.IllegalFeatureValue">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     The number of features which were omitted from phishing classification
@@ -105712,6 +105862,7 @@
 
 <histogram name="SBClientPhishing.PreClassificationCheckFail"
     enum="SBClientDetectionPreClassificationCheckFail" expires_after="M77">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     Records the number of phishing classifications that were skipped because a
@@ -105721,6 +105872,7 @@
 
 <histogram name="SBClientPhishing.ReportLimitSkipped" enum="BooleanHit"
     expires_after="M77">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     The number of phishing classifications that were previously cached as being
@@ -105729,6 +105881,7 @@
 </histogram>
 
 <histogram name="SBClientPhishing.RequestNotSerialized">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     The number of phishing classifier pingbacks that were skipped because
@@ -105737,6 +105890,7 @@
 </histogram>
 
 <histogram name="SBClientPhishing.RequestSatisfiedFromCache" enum="BooleanHit">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     The number of times that a cached phishing classification result was used,
@@ -105746,6 +105900,7 @@
 
 <histogram name="SBClientPhishing.ScorerCreationStatus"
     enum="SBClientPhishingScorerCreationStatus">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     Records the status when we create a scorer object for the client-side
@@ -105755,6 +105910,7 @@
 
 <histogram name="SBClientPhishing.ServerDeterminesPhishing"
     enum="BooleanIsPhishing" expires_after="M77">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     The counts for phishing verdicts given by server side model.
@@ -105763,6 +105919,7 @@
 
 <histogram name="SBClientPhishing.SkipClassificationReason"
     enum="SBClientPhishingSkipClassificationReason" expires_after="M77">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     The counts for various reasons why a phishing classification is skipped.
@@ -105770,6 +105927,7 @@
 </histogram>
 
 <histogram name="SBClientPhishing.TermFeatureBreakIterError">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     The number of phishing classifications that were aborted because the term
@@ -105779,6 +105937,7 @@
 
 <histogram name="SBClientPhishing.TermFeatureChunkTime" units="ms"
     expires_after="M77">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     The time that an individual chunk of term feature extraction work took.
@@ -105786,6 +105945,7 @@
 </histogram>
 
 <histogram name="SBClientPhishing.TermFeatureIterations" expires_after="M77">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     The number of iterations that the term feature extractor took to finish.
@@ -105793,6 +105953,7 @@
 </histogram>
 
 <histogram name="SBClientPhishing.TermFeatureTimeout" expires_after="M77">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     The number of phishing classification that were aborted because term feature
@@ -105802,6 +105963,7 @@
 
 <histogram name="SBClientPhishing.TermFeatureTotalTime" units="ms"
     expires_after="M77">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     The time that the term feature extarctor took to finish, summed across all
@@ -105810,6 +105972,7 @@
 </histogram>
 
 <histogram name="SBClientPhishing.TooManyFeatures">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     The number of times that the limit on the number of phishing classifier
@@ -105820,6 +105983,7 @@
 
 <histogram name="SBClientPhishing.URLFeatureTime" units="ms"
     expires_after="M77">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     The time taken to extract URL features for the phishing classifier.
@@ -105827,6 +105991,7 @@
 </histogram>
 
 <histogram name="SBDownloadFeedback.Activations" enum="DownloadItem.DangerType">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <owner>mattm@chromium.org</owner>
   <summary>
@@ -105836,6 +106001,7 @@
 </histogram>
 
 <histogram name="SBDownloadFeedback.ActiveFeedbacks" expires_after="M77">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <owner>mattm@chromium.org</owner>
   <summary>
@@ -105845,6 +106011,7 @@
 </histogram>
 
 <histogram name="SBDownloadFeedback.Eligible" enum="DownloadItem.DangerType">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <owner>mattm@chromium.org</owner>
   <summary>
@@ -105854,6 +106021,7 @@
 </histogram>
 
 <histogram name="SBDownloadFeedback.EmptyFilePathFailure" enum="Boolean">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     Count of times download feedback cannot be sent due to empty file path.
@@ -105873,6 +106041,7 @@
 </histogram>
 
 <histogram name="SBDownloadFeedback.SizeEligibleKB" units="KB">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <owner>mattm@chromium.org</owner>
   <summary>
@@ -105883,6 +106052,7 @@
 
 <histogram name="SBDownloadFeedback.SizeFailure" units="bytes"
     expires_after="M77">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <owner>mattm@chromium.org</owner>
   <summary>
@@ -105892,6 +106062,7 @@
 
 <histogram name="SBDownloadFeedback.SizeSuccess" units="bytes"
     expires_after="M77">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <owner>mattm@chromium.org</owner>
   <summary>
@@ -105901,6 +106072,7 @@
 
 <histogram name="SBDownloadFeedback.UploadRequestedByServer"
     enum="DownloadUploadRequestedByServer">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     For each non-SAFE file, records whether the server requested that that file
@@ -105912,6 +106084,7 @@
 
 <histogram name="SBDownloadFeedback.UploadResult"
     enum="SBDownloadFeedbackUploadResult" expires_after="M77">
+  <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <owner>mattm@chromium.org</owner>
   <summary>
@@ -121344,7 +121517,7 @@
 </histogram>
 
 <histogram name="Sync.AutofillProfile.AddOrUpdateOrigin"
-    enum="AutofillProfileSyncChangeOrigin" expires_after="M75">
+    enum="AutofillProfileSyncChangeOrigin" expires_after="M76">
   <owner>jkrcal@chromium.org</owner>
   <owner>treib@chromium.org</owner>
   <summary>
@@ -121354,7 +121527,7 @@
 </histogram>
 
 <histogram name="Sync.AutofillProfile.DeleteOrigin"
-    enum="AutofillProfileSyncChangeOrigin" expires_after="M75">
+    enum="AutofillProfileSyncChangeOrigin" expires_after="M76">
   <owner>jkrcal@chromium.org</owner>
   <owner>treib@chromium.org</owner>
   <summary>
@@ -123298,7 +123471,11 @@
   </summary>
 </histogram>
 
-<histogram name="Sync.RequestContentLength.Compressed" units="bytes">
+<histogram name="Sync.RequestContentLength.Compressed" units="bytes"
+    expires_after="2019-04-10">
+  <obsolete>
+    Deleted in M75, Issue 900075.
+  </obsolete>
   <owner>gangwu@chromium.org</owner>
   <summary>
     The request content size for a single HTTP/HTTPS call from sync client to
@@ -123306,7 +123483,11 @@
   </summary>
 </histogram>
 
-<histogram name="Sync.RequestContentLength.Original" units="bytes">
+<histogram name="Sync.RequestContentLength.Original" units="bytes"
+    expires_after="2019-04-10">
+  <obsolete>
+    Deleted in M75, Issue 900075.
+  </obsolete>
   <owner>gangwu@chromium.org</owner>
   <summary>
     The original request content size for a single HTTP/HTTPS call from sync
@@ -123329,7 +123510,11 @@
   <summary>Enumeration of types of simple conflict resolutions.</summary>
 </histogram>
 
-<histogram name="Sync.ResponseContentLength.Compressed" units="bytes">
+<histogram name="Sync.ResponseContentLength.Compressed" units="bytes"
+    expires_after="2019-04-10">
+  <obsolete>
+    Deleted in M75, Issue 900073.
+  </obsolete>
   <owner>gangwu@chromium.org</owner>
   <summary>
     The response content size for a single HTTP/HTTPS call from sync server to
@@ -123337,7 +123522,11 @@
   </summary>
 </histogram>
 
-<histogram name="Sync.ResponseContentLength.Original" units="bytes">
+<histogram name="Sync.ResponseContentLength.Original" units="bytes"
+    expires_after="2019-04-10">
+  <obsolete>
+    Deleted in M75, Issue 900073.
+  </obsolete>
   <owner>gangwu@chromium.org</owner>
   <summary>
     The original response content size for a single HTTP/HTTPS call from sync
diff --git a/ui/accessibility/platform/ax_platform_node_textrangeprovider_win.cc b/ui/accessibility/platform/ax_platform_node_textrangeprovider_win.cc
index 93e2d52d..0cab9a6f 100644
--- a/ui/accessibility/platform/ax_platform_node_textrangeprovider_win.cc
+++ b/ui/accessibility/platform/ax_platform_node_textrangeprovider_win.cc
@@ -26,8 +26,8 @@
 
 HRESULT AXPlatformNodeTextRangeProviderWin::CreateTextRangeProvider(
     ui::AXPlatformNodeWin* owner,
-    AXNodePosition::AXPositionInstance start,
-    AXNodePosition::AXPositionInstance end,
+    AXPositionInstance start,
+    AXPositionInstance end,
     ITextRangeProvider** provider) {
   CComObject<AXPlatformNodeTextRangeProviderWin>* text_range_provider = nullptr;
   HRESULT hr = CComObject<AXPlatformNodeTextRangeProviderWin>::CreateInstance(
@@ -300,6 +300,9 @@
 
   switch (unit) {
     case TextUnit_Character:
+      new_position =
+          MoveEndpointByCharacter(position_to_move, count, units_moved);
+      break;
     case TextUnit_Format:
     case TextUnit_Word:
     case TextUnit_Paragraph:
@@ -467,4 +470,34 @@
   return current_endpoint;
 }
 
+AXPlatformNodeTextRangeProviderWin::AXPositionInstance
+AXPlatformNodeTextRangeProviderWin::MoveEndpointByCharacter(
+    const AXPositionInstance& endpoint,
+    const int count,
+    int* count_moved) {
+  auto current_endpoint = endpoint->Clone();
+  const bool forwards = count > 0;
+  int iteration = 0;
+  for (iteration = 0; iteration < std::abs(count); ++iteration) {
+    AXPositionInstance next_endpoint;
+    if (forwards)
+      next_endpoint = current_endpoint->CreateNextCharacterPosition(
+          ui::AXBoundaryBehavior::CrossBoundary);
+    else
+      next_endpoint = current_endpoint->CreatePreviousCharacterPosition(
+          ui::AXBoundaryBehavior::CrossBoundary);
+
+    // End of document
+    if (next_endpoint->IsNullPosition())
+      break;
+    current_endpoint = std::move(next_endpoint);
+  }
+
+  *count_moved = iteration;
+
+  if (!forwards)
+    *count_moved *= -1;
+
+  return current_endpoint;
+}
 }  // namespace ui
diff --git a/ui/accessibility/platform/ax_platform_node_textrangeprovider_win.h b/ui/accessibility/platform/ax_platform_node_textrangeprovider_win.h
index 4b8972d..b3ae3cd 100644
--- a/ui/accessibility/platform/ax_platform_node_textrangeprovider_win.h
+++ b/ui/accessibility/platform/ax_platform_node_textrangeprovider_win.h
@@ -81,12 +81,16 @@
   STDMETHODIMP GetChildren(SAFEARRAY** children) override;
 
  private:
+  using AXPositionInstance = AXNodePosition::AXPositionInstance;
+  using AXNodeRange = AXRange<AXNodePosition::AXPositionInstance::element_type>;
+
   friend class AXPlatformNodeTextRangeProviderTest;
   base::string16 GetString();
   ui::AXPlatformNodeWin* owner() const;
 
-  using AXPositionInstance = AXNodePosition::AXPositionInstance;
-  using AXNodeRange = AXRange<AXNodePosition::AXPositionInstance::element_type>;
+  AXPositionInstance MoveEndpointByCharacter(const AXPositionInstance& endpoint,
+                                             const int count,
+                                             int* count_moved);
 
   AXPositionInstance MoveEndpointByDocument(const AXPositionInstance& endpoint,
                                             const int count,
diff --git a/ui/accessibility/platform/ax_platform_node_textrangeprovider_win_unittest.cc b/ui/accessibility/platform/ax_platform_node_textrangeprovider_win_unittest.cc
index 7acdbe50..7ec2b75 100644
--- a/ui/accessibility/platform/ax_platform_node_textrangeprovider_win_unittest.cc
+++ b/ui/accessibility/platform/ax_platform_node_textrangeprovider_win_unittest.cc
@@ -718,6 +718,204 @@
   }
 }
 
+TEST_F(AXPlatformNodeTextRangeProviderTest,
+       TestITextRangeProviderMoveEndpointByCharacter) {
+  ui::AXNodeData text_data;
+  text_data.id = 2;
+  text_data.role = ax::mojom::Role::kStaticText;
+  text_data.SetName("some text");
+
+  ui::AXNodeData more_text_data;
+  more_text_data.id = 3;
+  more_text_data.role = ax::mojom::Role::kStaticText;
+  more_text_data.SetName("more text");
+
+  ui::AXNodeData even_more_text_data;
+  even_more_text_data.id = 4;
+  even_more_text_data.role = ax::mojom::Role::kStaticText;
+  even_more_text_data.SetName("even more text");
+
+  ui::AXNodeData root_data;
+  root_data.id = 1;
+  root_data.role = ax::mojom::Role::kRootWebArea;
+  root_data.child_ids = {2, 3, 4};
+
+  ui::AXTreeUpdate update;
+  ui::AXTreeData tree_data;
+  tree_data.tree_id = ui::AXTreeID::CreateNewAXTreeID();
+  update.tree_data = tree_data;
+  update.has_tree_data = true;
+  update.root_id = root_data.id;
+  update.nodes.push_back(root_data);
+  update.nodes.push_back(text_data);
+  update.nodes.push_back(more_text_data);
+  update.nodes.push_back(even_more_text_data);
+
+  Init(update);
+
+  AXNode* root_node = GetRootNode();
+  AXNodePosition::SetTreeForTesting(tree_.get());
+  AXNode* text_node = root_node->children()[0];
+
+  ComPtr<IRawElementProviderSimple> text_node_raw =
+      QueryInterfaceFromNode<IRawElementProviderSimple>(text_node);
+
+  ComPtr<ITextProvider> text_provider;
+  EXPECT_HRESULT_SUCCEEDED(
+      text_node_raw->GetPatternProvider(UIA_TextPatternId, &text_provider));
+
+  ComPtr<ITextRangeProvider> text_range_provider;
+  EXPECT_HRESULT_SUCCEEDED(
+      text_provider->get_DocumentRange(&text_range_provider));
+
+  base::win::ScopedBstr text_content;
+  ASSERT_HRESULT_SUCCEEDED(
+      text_range_provider->GetText(-1, text_content.Receive()));
+  EXPECT_STREQ(L"some text", text_content);
+  text_content.Reset();
+
+  // Verify MoveEndpointByUnit with zero count has no effect
+  int count;
+  ASSERT_HRESULT_SUCCEEDED(text_range_provider->MoveEndpointByUnit(
+      TextPatternRangeEndpoint_End, TextUnit_Character, /*count*/ 0, &count));
+  ASSERT_EQ(0, count);
+
+  ASSERT_HRESULT_SUCCEEDED(
+      text_range_provider->GetText(-1, text_content.Receive()));
+  EXPECT_STREQ(L"some text", text_content);
+  text_content.Reset();
+
+  // Test start and end node single-unit moves within a single node
+  ASSERT_HRESULT_SUCCEEDED(text_range_provider->MoveEndpointByUnit(
+      TextPatternRangeEndpoint_Start, TextUnit_Character, /*count*/ 1, &count));
+  ASSERT_EQ(1, count);
+
+  ASSERT_HRESULT_SUCCEEDED(
+      text_range_provider->GetText(-1, text_content.Receive()));
+  EXPECT_STREQ(L"ome text", text_content);
+  text_content.Reset();
+
+  ASSERT_HRESULT_SUCCEEDED(text_range_provider->MoveEndpointByUnit(
+      TextPatternRangeEndpoint_End, TextUnit_Character, /*count*/ -1, &count));
+  ASSERT_EQ(-1, count);
+
+  ASSERT_HRESULT_SUCCEEDED(
+      text_range_provider->GetText(-1, text_content.Receive()));
+  EXPECT_STREQ(L"ome tex", text_content);
+  text_content.Reset();
+
+  // Test start and end node multi-unit moves within a single node
+  ASSERT_HRESULT_SUCCEEDED(text_range_provider->MoveEndpointByUnit(
+      TextPatternRangeEndpoint_Start, TextUnit_Character, /*count*/ 2, &count));
+  ASSERT_EQ(2, count);
+
+  ASSERT_HRESULT_SUCCEEDED(
+      text_range_provider->GetText(-1, text_content.Receive()));
+  EXPECT_STREQ(L"e tex", text_content);
+  text_content.Reset();
+
+  ASSERT_HRESULT_SUCCEEDED(text_range_provider->MoveEndpointByUnit(
+      TextPatternRangeEndpoint_End, TextUnit_Character, /*count*/ -3, &count));
+  ASSERT_EQ(-3, count);
+
+  ASSERT_HRESULT_SUCCEEDED(
+      text_range_provider->GetText(-1, text_content.Receive()));
+  EXPECT_STREQ(L"e ", text_content);
+  text_content.Reset();
+
+  // Move end to before start - ensure count is truncated
+  ASSERT_HRESULT_SUCCEEDED(text_range_provider->MoveEndpointByUnit(
+      TextPatternRangeEndpoint_End, TextUnit_Character, /*count*/ -10, &count));
+  ASSERT_EQ(-5, count);
+
+  ASSERT_HRESULT_SUCCEEDED(
+      text_range_provider->GetText(-1, text_content.Receive()));
+  EXPECT_STREQ(L"", text_content);
+  text_content.Reset();
+
+  // Move end back out - ensure both start and end were moved
+  ASSERT_HRESULT_SUCCEEDED(text_range_provider->MoveEndpointByUnit(
+      TextPatternRangeEndpoint_End, TextUnit_Character, /*count*/ 4, &count));
+  ASSERT_EQ(4, count);
+
+  ASSERT_HRESULT_SUCCEEDED(
+      text_range_provider->GetText(-1, text_content.Receive()));
+  EXPECT_STREQ(L"some", text_content);
+  text_content.Reset();
+
+  // Move start past end, ensure a degenerate range is created
+  ASSERT_HRESULT_SUCCEEDED(text_range_provider->MoveEndpointByUnit(
+      TextPatternRangeEndpoint_Start, TextUnit_Character, /*count*/ 7, &count));
+  ASSERT_EQ(7, count);
+
+  ASSERT_HRESULT_SUCCEEDED(
+      text_range_provider->GetText(-1, text_content.Receive()));
+  EXPECT_STREQ(L"", text_content);
+  text_content.Reset();
+
+  // Move start back to its prior position and verify that end was also moved
+  // as part of moving start past end
+  ASSERT_HRESULT_SUCCEEDED(text_range_provider->MoveEndpointByUnit(
+      TextPatternRangeEndpoint_Start, TextUnit_Character, /*count*/ -7,
+      &count));
+  ASSERT_EQ(-7, count);
+
+  ASSERT_HRESULT_SUCCEEDED(
+      text_range_provider->GetText(-1, text_content.Receive()));
+  EXPECT_STREQ(L"some te", text_content);
+  text_content.Reset();
+
+  // Move end into the adjacent node
+  ASSERT_HRESULT_SUCCEEDED(text_range_provider->MoveEndpointByUnit(
+      TextPatternRangeEndpoint_End, TextUnit_Character, /*count*/ 3, &count));
+  ASSERT_EQ(3, count);
+
+  ASSERT_HRESULT_SUCCEEDED(
+      text_range_provider->GetText(-1, text_content.Receive()));
+  EXPECT_STREQ(L"some text", text_content);
+  text_content.Reset();
+
+  // Move end to the end of the document, ensure truncated count
+  ASSERT_HRESULT_SUCCEEDED(text_range_provider->MoveEndpointByUnit(
+      TextPatternRangeEndpoint_End, TextUnit_Character, /*count*/ 30, &count));
+  ASSERT_EQ(24, count);
+
+  ASSERT_HRESULT_SUCCEEDED(
+      text_range_provider->GetText(-1, text_content.Receive()));
+  EXPECT_STREQ(L"some textmore texteven more text", text_content);
+  text_content.Reset();
+
+  // Move start beyond end, ensure truncated count and degenerate range
+  ASSERT_HRESULT_SUCCEEDED(text_range_provider->MoveEndpointByUnit(
+      TextPatternRangeEndpoint_Start, TextUnit_Character, /*count*/ 40,
+      &count));
+  ASSERT_EQ(34, count);
+
+  ASSERT_HRESULT_SUCCEEDED(
+      text_range_provider->GetText(-1, text_content.Receive()));
+  EXPECT_STREQ(L"", text_content);
+  text_content.Reset();
+
+  // Move end before start, ensure both positions are moved
+  ASSERT_HRESULT_SUCCEEDED(text_range_provider->MoveEndpointByUnit(
+      TextPatternRangeEndpoint_End, TextUnit_Character, /*count*/ -10, &count));
+  ASSERT_EQ(-10, count);
+
+  ASSERT_HRESULT_SUCCEEDED(
+      text_range_provider->GetText(-1, text_content.Receive()));
+  EXPECT_STREQ(L"", text_content);
+  text_content.Reset();
+
+  ASSERT_HRESULT_SUCCEEDED(text_range_provider->MoveEndpointByUnit(
+      TextPatternRangeEndpoint_End, TextUnit_Character, /*count*/ 5, &count));
+  ASSERT_EQ(5, count);
+
+  ASSERT_HRESULT_SUCCEEDED(
+      text_range_provider->GetText(-1, text_content.Receive()));
+  EXPECT_STREQ(L" more", text_content);
+  text_content.Reset();
+}
+
 TEST_F(AXPlatformNodeTextRangeProviderTest, TestITextRangeProviderCompare) {
   ui::AXNodeData text_data;
   text_data.id = 2;
diff --git a/ui/display/display_features.cc b/ui/display/display_features.cc
index b902dbac..0a3e792 100644
--- a/ui/display/display_features.cc
+++ b/ui/display/display_features.cc
@@ -10,14 +10,6 @@
 const base::Feature kHighDynamicRange{"HighDynamicRange",
                                       base::FEATURE_ENABLED_BY_DEFAULT};
 
-#if defined(OS_CHROMEOS)
-// Enables using the monitor's provided color space information when
-// rendering.
-// TODO(mcasas): remove this flag http://crbug.com/771345.
-const base::Feature kUseMonitorColorSpace{"UseMonitorColorSpace",
-                                          base::FEATURE_ENABLED_BY_DEFAULT};
-#endif  // OS_CHROMEOS
-
 // This features allows listing all display modes of external displays in the
 // display settings and setting any one of them exactly as requested, which can
 // be very useful for debugging and development purposes.
diff --git a/ui/display/manager/display_manager.cc b/ui/display/manager/display_manager.cc
index 11eee291..8a456e8 100644
--- a/ui/display/manager/display_manager.cc
+++ b/ui/display/manager/display_manager.cc
@@ -2097,13 +2097,7 @@
   new_display.set_rotation(display_info.GetActiveRotation());
   new_display.set_touch_support(display_info.touch_support());
   new_display.set_maximum_cursor_size(display_info.maximum_cursor_size());
-#if defined(OS_CHROMEOS)
-  // TODO(mcasas): remove this check, http://crbug.com/771345.
-  if (base::FeatureList::IsEnabled(features::kUseMonitorColorSpace))
-    new_display.set_color_space(display_info.color_space());
-#else
   new_display.set_color_space(display_info.color_space());
-#endif
 
   if (internal_display_has_accelerometer_ && Display::IsInternalDisplayId(id)) {
     new_display.set_accelerometer_support(
diff --git a/ui/views/accessibility/ax_aura_obj_cache.cc b/ui/views/accessibility/ax_aura_obj_cache.cc
index 426d19fa..960caba 100644
--- a/ui/views/accessibility/ax_aura_obj_cache.cc
+++ b/ui/views/accessibility/ax_aura_obj_cache.cc
@@ -30,12 +30,6 @@
 
 }  // namespace
 
-// static
-AXAuraObjCache* AXAuraObjCache::GetInstance() {
-  static base::NoDestructor<AXAuraObjCache> instance;
-  return instance.get();
-}
-
 AXAuraObjWrapper* AXAuraObjCache::GetOrCreate(View* view) {
   // Avoid problems with transient focus events. https://crbug.com/729449
   if (!view->GetWidget())
@@ -108,7 +102,7 @@
     const ViewAccessibility& view_accessibility =
         focused_view->GetViewAccessibility();
     if (view_accessibility.FocusedVirtualChild())
-      return view_accessibility.FocusedVirtualChild()->GetWrapper();
+      return view_accessibility.FocusedVirtualChild()->GetOrCreateWrapper(this);
 
     return GetOrCreate(focused_view);
   }
@@ -130,7 +124,10 @@
 AXAuraObjCache::AXAuraObjCache() = default;
 
 // Never runs because object is leaked.
-AXAuraObjCache::~AXAuraObjCache() = default;
+AXAuraObjCache::~AXAuraObjCache() {
+  if (!root_windows_.empty() && GetFocusClient(*root_windows_.begin()))
+    GetFocusClient(*root_windows_.begin())->RemoveObserver(this);
+}
 
 View* AXAuraObjCache::GetFocusedView() {
   Widget* focused_widget = focused_widget_for_testing_;
diff --git a/ui/views/accessibility/ax_aura_obj_cache.h b/ui/views/accessibility/ax_aura_obj_cache.h
index 5bb1815..a84bb4d 100644
--- a/ui/views/accessibility/ax_aura_obj_cache.h
+++ b/ui/views/accessibility/ax_aura_obj_cache.h
@@ -34,8 +34,8 @@
 // A cache responsible for assigning id's to a set of interesting Aura views.
 class VIEWS_EXPORT AXAuraObjCache : public aura::client::FocusChangeObserver {
  public:
-  // Get the single instance of this class.
-  static AXAuraObjCache* GetInstance();
+  AXAuraObjCache();
+  ~AXAuraObjCache() override;
 
   class Delegate {
    public:
@@ -104,9 +104,6 @@
  private:
   friend class base::NoDestructor<AXAuraObjCache>;
 
-  AXAuraObjCache();
-  ~AXAuraObjCache() override;
-
   View* GetFocusedView();
 
   // aura::client::FocusChangeObserver override.
diff --git a/ui/views/accessibility/ax_aura_obj_cache_unittest.cc b/ui/views/accessibility/ax_aura_obj_cache_unittest.cc
index 47a46ab..a301131 100644
--- a/ui/views/accessibility/ax_aura_obj_cache_unittest.cc
+++ b/ui/views/accessibility/ax_aura_obj_cache_unittest.cc
@@ -26,31 +26,31 @@
 };
 
 TEST_F(AXAuraObjCacheTest, TestViewRemoval) {
+  AXAuraObjCache cache;
   WidgetAutoclosePtr widget(CreateTopLevelPlatformWidget());
   View* parent = new View();
   widget->GetRootView()->AddChildView(parent);
   View* child = new View();
   parent->AddChildView(child);
 
-  AXAuraObjCache* cache = AXAuraObjCache::GetInstance();
-  AXAuraObjWrapper* ax_widget = cache->GetOrCreate(widget.get());
+  AXAuraObjWrapper* ax_widget = cache.GetOrCreate(widget.get());
   ASSERT_NE(nullptr, ax_widget);
-  AXAuraObjWrapper* ax_parent = cache->GetOrCreate(parent);
+  AXAuraObjWrapper* ax_parent = cache.GetOrCreate(parent);
   ASSERT_NE(nullptr, ax_parent);
-  AXAuraObjWrapper* ax_child = cache->GetOrCreate(child);
+  AXAuraObjWrapper* ax_child = cache.GetOrCreate(child);
   ASSERT_NE(nullptr, ax_child);
 
   // Everything should have an ID, indicating it's in the cache.
-  ASSERT_GT(cache->GetID(widget.get()), 0);
-  ASSERT_GT(cache->GetID(parent), 0);
-  ASSERT_GT(cache->GetID(child), 0);
+  ASSERT_GT(cache.GetID(widget.get()), 0);
+  ASSERT_GT(cache.GetID(parent), 0);
+  ASSERT_GT(cache.GetID(child), 0);
 
   // Removing the parent view should remove both the parent and child
   // from the cache, but leave the widget.
   widget->GetRootView()->RemoveChildView(parent);
-  ASSERT_GT(cache->GetID(widget.get()), 0);
-  ASSERT_EQ(-1, cache->GetID(parent));
-  ASSERT_EQ(-1, cache->GetID(child));
+  ASSERT_GT(cache.GetID(widget.get()), 0);
+  ASSERT_EQ(-1, cache.GetID(parent));
+  ASSERT_EQ(-1, cache.GetID(child));
 
   // Explicitly delete |parent| to prevent a memory leak, since calling
   // RemoveChildView() doesn't delete it.
diff --git a/ui/views/accessibility/ax_aura_obj_wrapper.cc b/ui/views/accessibility/ax_aura_obj_wrapper.cc
index 52acd4f..425a9b2 100644
--- a/ui/views/accessibility/ax_aura_obj_wrapper.cc
+++ b/ui/views/accessibility/ax_aura_obj_wrapper.cc
@@ -6,6 +6,9 @@
 
 namespace views {
 
+AXAuraObjWrapper::AXAuraObjWrapper(AXAuraObjCache* cache)
+    : aura_obj_cache_(cache) {}
+
 bool AXAuraObjWrapper::HandleAccessibleAction(const ui::AXActionData& action) {
   return false;
 }
diff --git a/ui/views/accessibility/ax_aura_obj_wrapper.h b/ui/views/accessibility/ax_aura_obj_wrapper.h
index c26f2e9..3d44480b 100644
--- a/ui/views/accessibility/ax_aura_obj_wrapper.h
+++ b/ui/views/accessibility/ax_aura_obj_wrapper.h
@@ -21,10 +21,13 @@
 
 namespace views {
 
+class AXAuraObjCache;
+
 // An interface abstraction for Aura views that exposes the view-tree formed
 // by the implementing view types.
 class VIEWS_EXPORT AXAuraObjWrapper {
  public:
+  explicit AXAuraObjWrapper(AXAuraObjCache* cache);
   virtual ~AXAuraObjWrapper() = default;
 
   // See ViewAccessibility for details.
@@ -39,6 +42,11 @@
 
   // Actions.
   virtual bool HandleAccessibleAction(const ui::AXActionData& action);
+
+ protected:
+  // The cache associated with this wrapper. Subclasses should initialize this
+  // cache on construction.
+  AXAuraObjCache* aura_obj_cache_ = nullptr;
 };
 
 }  // namespace views
diff --git a/ui/views/accessibility/ax_aura_window_utils_unittest.cc b/ui/views/accessibility/ax_aura_window_utils_unittest.cc
index 35d7bc7..06970e20 100644
--- a/ui/views/accessibility/ax_aura_window_utils_unittest.cc
+++ b/ui/views/accessibility/ax_aura_window_utils_unittest.cc
@@ -113,8 +113,8 @@
   std::unique_ptr<ui::AXTree> GetAccessibilityTreeFromWindow(
       aura::Window* window) {
     ui::AXTreeID tree_id = ui::AXTreeID::CreateNewAXTreeID();
-    AXAuraObjCache* cache = AXAuraObjCache::GetInstance();
-    AXTreeSourceViews tree_source(cache->GetOrCreate(window), tree_id);
+    AXAuraObjCache cache;
+    AXTreeSourceViews tree_source(cache.GetOrCreate(window), tree_id, &cache);
     AuraAXTreeSerializer serializer(&tree_source);
     ui::AXTreeUpdate serialized_tree;
     serializer.SerializeChanges(tree_source.GetRoot(), &serialized_tree);
diff --git a/ui/views/accessibility/ax_root_obj_wrapper.cc b/ui/views/accessibility/ax_root_obj_wrapper.cc
index 05176d2c..e03f06a 100644
--- a/ui/views/accessibility/ax_root_obj_wrapper.cc
+++ b/ui/views/accessibility/ax_root_obj_wrapper.cc
@@ -17,8 +17,9 @@
 #include "ui/views/accessibility/ax_aura_obj_cache.h"
 #include "ui/views/accessibility/ax_window_obj_wrapper.h"
 
-AXRootObjWrapper::AXRootObjWrapper(views::AXAuraObjCache::Delegate* delegate)
-    : delegate_(delegate) {
+AXRootObjWrapper::AXRootObjWrapper(views::AXAuraObjCache::Delegate* delegate,
+                                   views::AXAuraObjCache* cache)
+    : AXAuraObjWrapper(cache), delegate_(delegate) {
   if (display::Screen::GetScreen())
     display::Screen::GetScreen()->AddObserver(this);
 }
@@ -44,7 +45,7 @@
 
 void AXRootObjWrapper::GetChildren(
     std::vector<views::AXAuraObjWrapper*>* out_children) {
-  views::AXAuraObjCache::GetInstance()->GetTopLevelWindows(out_children);
+  aura_obj_cache_->GetTopLevelWindows(out_children);
 }
 
 void AXRootObjWrapper::Serialize(ui::AXNodeData* out_node_data) {
diff --git a/ui/views/accessibility/ax_root_obj_wrapper.h b/ui/views/accessibility/ax_root_obj_wrapper.h
index 67713fbd9..ddea073e 100644
--- a/ui/views/accessibility/ax_root_obj_wrapper.h
+++ b/ui/views/accessibility/ax_root_obj_wrapper.h
@@ -20,7 +20,8 @@
 class VIEWS_EXPORT AXRootObjWrapper : public views::AXAuraObjWrapper,
                                       display::DisplayObserver {
  public:
-  explicit AXRootObjWrapper(views::AXAuraObjCache::Delegate* delegate);
+  AXRootObjWrapper(views::AXAuraObjCache::Delegate* delegate,
+                   views::AXAuraObjCache* cache);
   ~AXRootObjWrapper() override;
 
   // Convenience method to check for existence of a child.
diff --git a/ui/views/accessibility/ax_tree_source_views.cc b/ui/views/accessibility/ax_tree_source_views.cc
index b7d3fc2..1cb8e7cf 100644
--- a/ui/views/accessibility/ax_tree_source_views.cc
+++ b/ui/views/accessibility/ax_tree_source_views.cc
@@ -19,8 +19,9 @@
 namespace views {
 
 AXTreeSourceViews::AXTreeSourceViews(AXAuraObjWrapper* root,
-                                     const ui::AXTreeID& tree_id)
-    : root_(root), tree_id_(tree_id) {
+                                     const ui::AXTreeID& tree_id,
+                                     views::AXAuraObjCache* cache)
+    : root_(root), tree_id_(tree_id), cache_(cache) {
   DCHECK(root_);
   DCHECK_NE(tree_id_, ui::AXTreeIDUnknown());
 }
@@ -40,7 +41,7 @@
     id = action.anchor_node_id;
   }
 
-  AXAuraObjWrapper* obj = AXAuraObjCache::GetInstance()->Get(id);
+  AXAuraObjWrapper* obj = GetFromId(id);
   if (obj)
     obj->HandleAccessibleAction(action);
 }
@@ -49,7 +50,7 @@
   tree_data->tree_id = tree_id_;
   tree_data->loaded = true;
   tree_data->loading_progress = 1.0;
-  AXAuraObjWrapper* focus = AXAuraObjCache::GetInstance()->GetFocus();
+  AXAuraObjWrapper* focus = cache_->GetFocus();
   if (focus)
     tree_data->focus_id = focus->GetUniqueId();
   return true;
@@ -64,12 +65,14 @@
   // Root might not be in the cache.
   if (id == root->GetUniqueId())
     return root;
-  AXAuraObjWrapper* wrapper = AXAuraObjCache::GetInstance()->Get(id);
+  AXAuraObjWrapper* wrapper = cache_->Get(id);
 
   // We must do a lookup in AXVirtualView as well if the main cache doesn't hold
   // this node.
-  if (!wrapper && AXVirtualView::GetFromId(id))
-    return AXVirtualView::GetFromId(id)->GetWrapper();
+  if (!wrapper && AXVirtualView::GetFromId(id)) {
+    AXVirtualView* virtual_view = AXVirtualView::GetFromId(id);
+    return virtual_view->GetOrCreateWrapper(cache_);
+  }
 
   return wrapper;
 }
diff --git a/ui/views/accessibility/ax_tree_source_views.h b/ui/views/accessibility/ax_tree_source_views.h
index 0c110a7..12d1f3b5 100644
--- a/ui/views/accessibility/ax_tree_source_views.h
+++ b/ui/views/accessibility/ax_tree_source_views.h
@@ -18,6 +18,7 @@
 
 namespace views {
 
+class AXAuraObjCache;
 class AXAuraObjWrapper;
 
 // This class exposes the views hierarchy as an accessibility tree permitting
@@ -29,7 +30,9 @@
     : public ui::
           AXTreeSource<AXAuraObjWrapper*, ui::AXNodeData, ui::AXTreeData> {
  public:
-  AXTreeSourceViews(AXAuraObjWrapper* root, const ui::AXTreeID& tree_id);
+  AXTreeSourceViews(AXAuraObjWrapper* root,
+                    const ui::AXTreeID& tree_id,
+                    AXAuraObjCache* cache);
   ~AXTreeSourceViews() override;
 
   // Invokes an action on an Aura object.
@@ -59,6 +62,8 @@
   // ID to use for the AX tree.
   const ui::AXTreeID tree_id_;
 
+  views::AXAuraObjCache* cache_;
+
   DISALLOW_COPY_AND_ASSIGN(AXTreeSourceViews);
 };
 
diff --git a/ui/views/accessibility/ax_tree_source_views_unittest.cc b/ui/views/accessibility/ax_tree_source_views_unittest.cc
index dde5046a..f94c008 100644
--- a/ui/views/accessibility/ax_tree_source_views_unittest.cc
+++ b/ui/views/accessibility/ax_tree_source_views_unittest.cc
@@ -26,8 +26,8 @@
 // TestAXTreeSourceViews provides a root with a default tree ID.
 class TestAXTreeSourceViews : public AXTreeSourceViews {
  public:
-  explicit TestAXTreeSourceViews(AXAuraObjWrapper* root)
-      : AXTreeSourceViews(root, ui::AXTreeID::CreateNewAXTreeID()) {}
+  TestAXTreeSourceViews(AXAuraObjWrapper* root, AXAuraObjCache* cache)
+      : AXTreeSourceViews(root, ui::AXTreeID::CreateNewAXTreeID(), cache) {}
 
   ~TestAXTreeSourceViews() override = default;
 
@@ -79,11 +79,11 @@
 };
 
 TEST_F(AXTreeSourceViewsTest, Basics) {
-  AXAuraObjCache* cache = AXAuraObjCache::GetInstance();
+  AXAuraObjCache cache;
 
   // Start the tree at the Widget's contents view.
-  AXAuraObjWrapper* root = cache->GetOrCreate(widget_->GetContentsView());
-  TestAXTreeSourceViews tree(root);
+  AXAuraObjWrapper* root = cache.GetOrCreate(widget_->GetContentsView());
+  TestAXTreeSourceViews tree(root, &cache);
   EXPECT_EQ(root, tree.GetRoot());
 
   // The root has no parent.
@@ -98,9 +98,9 @@
   AXAuraObjWrapper* label1 = children[0];
   AXAuraObjWrapper* label2 = children[1];
   AXAuraObjWrapper* textfield = children[2];
-  EXPECT_EQ(label1, cache->GetOrCreate(label1_));
-  EXPECT_EQ(label2, cache->GetOrCreate(label2_));
-  EXPECT_EQ(textfield, cache->GetOrCreate(textfield_));
+  EXPECT_EQ(label1, cache.GetOrCreate(label1_));
+  EXPECT_EQ(label2, cache.GetOrCreate(label2_));
+  EXPECT_EQ(textfield, cache.GetOrCreate(textfield_));
 
   // The parents is correct.
   EXPECT_EQ(root, tree.GetParent(label1));
@@ -134,14 +134,14 @@
 }
 
 TEST_F(AXTreeSourceViewsTest, GetTreeDataWithFocus) {
-  AXAuraObjCache* cache = AXAuraObjCache::GetInstance();
-  TestAXTreeSourceViews tree(cache->GetOrCreate(widget_.get()));
+  AXAuraObjCache cache;
+  TestAXTreeSourceViews tree(cache.GetOrCreate(widget_.get()), &cache);
   textfield_->RequestFocus();
 
   ui::AXTreeData tree_data;
   tree.GetTreeData(&tree_data);
   EXPECT_TRUE(tree_data.loaded);
-  EXPECT_EQ(cache->GetID(textfield_), tree_data.focus_id);
+  EXPECT_EQ(cache.GetID(textfield_), tree_data.focus_id);
 }
 
 TEST_F(AXTreeSourceViewsTest, IgnoredView) {
@@ -149,9 +149,9 @@
   ignored_view->GetViewAccessibility().OverrideIsIgnored(true);
   widget_->GetContentsView()->AddChildView(ignored_view);
 
-  AXAuraObjCache* cache = AXAuraObjCache::GetInstance();
-  TestAXTreeSourceViews tree(cache->GetOrCreate(widget_.get()));
-  EXPECT_FALSE(tree.IsValid(cache->GetOrCreate(ignored_view)));
+  AXAuraObjCache cache;
+  TestAXTreeSourceViews tree(cache.GetOrCreate(widget_.get()), &cache);
+  EXPECT_FALSE(tree.IsValid(cache.GetOrCreate(ignored_view)));
 }
 
 }  // namespace
diff --git a/ui/views/accessibility/ax_view_obj_wrapper.cc b/ui/views/accessibility/ax_view_obj_wrapper.cc
index 70c92d4..365abc9 100644
--- a/ui/views/accessibility/ax_view_obj_wrapper.cc
+++ b/ui/views/accessibility/ax_view_obj_wrapper.cc
@@ -16,7 +16,7 @@
 namespace views {
 
 AXViewObjWrapper::AXViewObjWrapper(AXAuraObjCache* aura_obj_cache, View* view)
-    : aura_obj_cache_(aura_obj_cache), view_(view) {
+    : AXAuraObjWrapper(aura_obj_cache), view_(view) {
   if (view->GetWidget())
     aura_obj_cache_->GetOrCreate(view->GetWidget());
   view->AddObserver(this);
@@ -62,8 +62,9 @@
   }
 
   for (int i = 0; i < view_accessibility.virtual_child_count(); ++i) {
-    out_children->push_back(
-        view_accessibility.virtual_child_at(i)->GetWrapper());
+    AXVirtualView* child =
+        const_cast<AXVirtualView*>(view_accessibility.virtual_child_at(i));
+    out_children->push_back(child->GetOrCreateWrapper(aura_obj_cache_));
   }
 }
 
@@ -71,8 +72,22 @@
   if (!view_)
     return;
 
-  view_->GetViewAccessibility().GetAccessibleNodeData(out_node_data);
+  ViewAccessibility& view_accessibility = view_->GetViewAccessibility();
+
+  view_accessibility.GetAccessibleNodeData(out_node_data);
   out_node_data->id = GetUniqueId();
+
+  if (view_accessibility.GetNextFocus()) {
+    out_node_data->AddIntAttribute(
+        ax::mojom::IntAttribute::kNextFocusId,
+        aura_obj_cache_->GetID(view_accessibility.GetNextFocus()));
+  }
+
+  if (view_accessibility.GetPreviousFocus()) {
+    out_node_data->AddIntAttribute(
+        ax::mojom::IntAttribute::kPreviousFocusId,
+        aura_obj_cache_->GetID(view_accessibility.GetPreviousFocus()));
+  }
 }
 
 int32_t AXViewObjWrapper::GetUniqueId() const {
diff --git a/ui/views/accessibility/ax_view_obj_wrapper.h b/ui/views/accessibility/ax_view_obj_wrapper.h
index 545468ee..ebc764f 100644
--- a/ui/views/accessibility/ax_view_obj_wrapper.h
+++ b/ui/views/accessibility/ax_view_obj_wrapper.h
@@ -36,8 +36,6 @@
   void OnViewIsDeleting(View* observed_view) override;
 
  private:
-  AXAuraObjCache* const aura_obj_cache_;
-
   View* view_;
 
   DISALLOW_COPY_AND_ASSIGN(AXViewObjWrapper);
diff --git a/ui/views/accessibility/ax_virtual_view.cc b/ui/views/accessibility/ax_virtual_view.cc
index ec7296e..2ddb1b05 100644
--- a/ui/views/accessibility/ax_virtual_view.cc
+++ b/ui/views/accessibility/ax_virtual_view.cc
@@ -39,10 +39,6 @@
 }
 
 AXVirtualView::AXVirtualView() {
-#if defined(USE_AURA)
-  wrapper_ = std::make_unique<AXVirtualViewWrapper>(this);
-#endif
-
   GetIdMap()[unique_id_.Get()] = this;
   ax_platform_node_ = ui::AXPlatformNode::Create(this);
   DCHECK(ax_platform_node_);
@@ -342,7 +338,12 @@
   return nullptr;
 }
 
-AXVirtualViewWrapper* AXVirtualView::GetWrapper() const {
+AXVirtualViewWrapper* AXVirtualView::GetOrCreateWrapper(
+    views::AXAuraObjCache* cache) {
+#if defined(USE_AURA)
+  if (!wrapper_)
+    wrapper_ = std::make_unique<AXVirtualViewWrapper>(this, cache);
+#endif
   return wrapper_.get();
 }
 
diff --git a/ui/views/accessibility/ax_virtual_view.h b/ui/views/accessibility/ax_virtual_view.h
index e807edc9..dea22fe 100644
--- a/ui/views/accessibility/ax_virtual_view.h
+++ b/ui/views/accessibility/ax_virtual_view.h
@@ -39,6 +39,7 @@
 
 namespace views {
 
+class AXAuraObjCache;
 class View;
 class ViewAccessibility;
 
@@ -143,8 +144,8 @@
   // Gets the real View that owns our shallowest virtual ancestor,, if any.
   View* GetOwnerView() const;
 
-  // Gets a wrapper suitable for use with tree sources.
-  AXVirtualViewWrapper* GetWrapper() const;
+  // Gets or creates a wrapper suitable for use with tree sources.
+  AXVirtualViewWrapper* GetOrCreateWrapper(views::AXAuraObjCache* cache);
 
   // Handle a request from assistive technology to perform an action on this
   // virtual view. Returns true on success, but note that the success/failure is
diff --git a/ui/views/accessibility/ax_virtual_view_wrapper.cc b/ui/views/accessibility/ax_virtual_view_wrapper.cc
index 5e637c2..f9bb872 100644
--- a/ui/views/accessibility/ax_virtual_view_wrapper.cc
+++ b/ui/views/accessibility/ax_virtual_view_wrapper.cc
@@ -4,14 +4,16 @@
 
 #include "ui/views/accessibility/ax_virtual_view_wrapper.h"
 
-#include "ui/views/accessibility/ax_aura_obj_cache.h"
 #include "ui/views/accessibility/ax_view_obj_wrapper.h"
 #include "ui/views/accessibility/ax_virtual_view.h"
+#include "ui/views/accessibility/view_accessibility.h"
+#include "ui/views/view.h"
 
 namespace views {
 
-AXVirtualViewWrapper::AXVirtualViewWrapper(AXVirtualView* virtual_view)
-    : virtual_view_(virtual_view) {}
+AXVirtualViewWrapper::AXVirtualViewWrapper(AXVirtualView* virtual_view,
+                                           AXAuraObjCache* cache)
+    : AXAuraObjWrapper(cache), virtual_view_(virtual_view) {}
 
 AXVirtualViewWrapper::~AXVirtualViewWrapper() = default;
 
@@ -20,11 +22,12 @@
 }
 
 AXAuraObjWrapper* AXVirtualViewWrapper::GetParent() {
-  if (virtual_view_->virtual_parent_view())
-    return virtual_view_->virtual_parent_view()->GetWrapper();
+  if (virtual_view_->virtual_parent_view()) {
+    return const_cast<AXVirtualView*>(virtual_view_->virtual_parent_view())
+        ->GetOrCreateWrapper(aura_obj_cache_);
+  }
   if (virtual_view_->GetOwnerView())
-    return AXAuraObjCache::GetInstance()->GetOrCreate(
-        virtual_view_->GetOwnerView());
+    return aura_obj_cache_->GetOrCreate(virtual_view_->GetOwnerView());
 
   return nullptr;
 }
@@ -32,7 +35,8 @@
 void AXVirtualViewWrapper::GetChildren(
     std::vector<AXAuraObjWrapper*>* out_children) {
   for (int i = 0; i < virtual_view_->GetChildCount(); ++i)
-    out_children->push_back(virtual_view_->child_at(i)->GetWrapper());
+    out_children->push_back(
+        virtual_view_->child_at(i)->GetOrCreateWrapper(aura_obj_cache_));
 }
 
 void AXVirtualViewWrapper::Serialize(ui::AXNodeData* out_node_data) {
diff --git a/ui/views/accessibility/ax_virtual_view_wrapper.h b/ui/views/accessibility/ax_virtual_view_wrapper.h
index 8e03614..6142d6c 100644
--- a/ui/views/accessibility/ax_virtual_view_wrapper.h
+++ b/ui/views/accessibility/ax_virtual_view_wrapper.h
@@ -7,16 +7,19 @@
 
 #include <vector>
 
+#include "ui/views/accessibility/ax_aura_obj_cache.h"
 #include "ui/views/accessibility/ax_aura_obj_wrapper.h"
 #include "ui/views/views_export.h"
 
 namespace views {
+
+class AXAuraObjWrapper;
 class AXVirtualView;
 
 // Wraps (and adapts) an AXVirtualView for use with AXTreeSourceViews.
 class AXVirtualViewWrapper : public AXAuraObjWrapper {
  public:
-  explicit AXVirtualViewWrapper(AXVirtualView* virtual_view);
+  AXVirtualViewWrapper(AXVirtualView* virtual_view, AXAuraObjCache* cache);
   ~AXVirtualViewWrapper() override;
 
   // AXAuraObjWrapper:
@@ -29,7 +32,7 @@
 
  private:
   // Weak.
-  AXVirtualView* const virtual_view_;
+  AXVirtualView* virtual_view_;
 
   DISALLOW_COPY_AND_ASSIGN(AXVirtualViewWrapper);
 };
diff --git a/ui/views/accessibility/ax_widget_obj_wrapper.cc b/ui/views/accessibility/ax_widget_obj_wrapper.cc
index 2869557..1424e17 100644
--- a/ui/views/accessibility/ax_widget_obj_wrapper.cc
+++ b/ui/views/accessibility/ax_widget_obj_wrapper.cc
@@ -15,7 +15,7 @@
 
 AXWidgetObjWrapper::AXWidgetObjWrapper(AXAuraObjCache* aura_obj_cache,
                                        Widget* widget)
-    : aura_obj_cache_(aura_obj_cache), widget_(widget) {
+    : AXAuraObjWrapper(aura_obj_cache), widget_(widget) {
   widget->AddObserver(this);
   widget->AddRemovalsObserver(this);
 }
diff --git a/ui/views/accessibility/ax_widget_obj_wrapper.h b/ui/views/accessibility/ax_widget_obj_wrapper.h
index 080c21a..0116038d 100644
--- a/ui/views/accessibility/ax_widget_obj_wrapper.h
+++ b/ui/views/accessibility/ax_widget_obj_wrapper.h
@@ -42,8 +42,6 @@
   void OnWillRemoveView(Widget* widget, View* view) override;
 
  private:
-  AXAuraObjCache* const aura_obj_cache_;
-
   Widget* widget_;
 
   const ui::AXUniqueId unique_id_;
diff --git a/ui/views/accessibility/ax_window_obj_wrapper.cc b/ui/views/accessibility/ax_window_obj_wrapper.cc
index 7b260c24..d13591bf 100644
--- a/ui/views/accessibility/ax_window_obj_wrapper.cc
+++ b/ui/views/accessibility/ax_window_obj_wrapper.cc
@@ -42,7 +42,7 @@
 
 AXWindowObjWrapper::AXWindowObjWrapper(AXAuraObjCache* aura_obj_cache,
                                        aura::Window* window)
-    : aura_obj_cache_(aura_obj_cache),
+    : AXAuraObjWrapper(aura_obj_cache),
       window_(window),
       is_root_window_(AXAuraWindowUtils::Get()->IsRootWindow(window)) {
   window->AddObserver(this);
@@ -52,9 +52,6 @@
 }
 
 AXWindowObjWrapper::~AXWindowObjWrapper() {
-  if (is_root_window_)
-    aura_obj_cache_->OnRootWindowObjDestroyed(window_);
-
   window_->RemoveObserver(this);
 }
 
@@ -135,6 +132,9 @@
   Widget* widget = GetWidgetForWindow(window);
   if (widget)
     aura_obj_cache_->Remove(widget);
+
+  if (is_root_window_)
+    aura_obj_cache_->OnRootWindowObjDestroyed(window_);
 }
 
 void AXWindowObjWrapper::OnWindowHierarchyChanged(
diff --git a/ui/views/accessibility/ax_window_obj_wrapper.h b/ui/views/accessibility/ax_window_obj_wrapper.h
index 31d8525..2aed3d6 100644
--- a/ui/views/accessibility/ax_window_obj_wrapper.h
+++ b/ui/views/accessibility/ax_window_obj_wrapper.h
@@ -56,8 +56,6 @@
   // that widget's root view.
   void FireEvent(aura::Window* window, ax::mojom::Event event_type);
 
-  AXAuraObjCache* const aura_obj_cache_;
-
   aura::Window* window_;
 
   bool is_root_window_;
diff --git a/ui/views/accessibility/view_accessibility.cc b/ui/views/accessibility/view_accessibility.cc
index 0ccc752..2f2ea9c 100644
--- a/ui/views/accessibility/view_accessibility.cc
+++ b/ui/views/accessibility/view_accessibility.cc
@@ -249,6 +249,22 @@
   custom_data_.AddIntAttribute(ax::mojom::IntAttribute::kSetSize, set_size);
 }
 
+void ViewAccessibility::OverrideNextFocus(Widget* widget) {
+  next_focus_ = widget;
+}
+
+void ViewAccessibility::OverridePreviousFocus(Widget* widget) {
+  previous_focus_ = widget;
+}
+
+Widget* ViewAccessibility::GetNextFocus() {
+  return next_focus_;
+}
+
+Widget* ViewAccessibility::GetPreviousFocus() {
+  return previous_focus_;
+}
+
 gfx::NativeViewAccessible ViewAccessibility::GetNativeObject() {
   return nullptr;
 }
diff --git a/ui/views/accessibility/view_accessibility.h b/ui/views/accessibility/view_accessibility.h
index ebccb37..35f9970 100644
--- a/ui/views/accessibility/view_accessibility.h
+++ b/ui/views/accessibility/view_accessibility.h
@@ -22,6 +22,7 @@
 namespace views {
 
 class View;
+class Widget;
 
 // An object that manages the accessibility interface for a View.
 //
@@ -73,6 +74,14 @@
   // Note: |pos_in_set| is 1-indexed.
   void OverridePosInSet(int pos_in_set, int set_size);
 
+  // Override the next or previous focused widget. Some screen readers may
+  // utilize this information to transition focus from the beginning or end of
+  // one window to another when navigating by its default navigation method.
+  void OverrideNextFocus(Widget* widget);
+  void OverridePreviousFocus(Widget* widget);
+  Widget* GetNextFocus();
+  Widget* GetPreviousFocus();
+
   virtual gfx::NativeViewAccessible GetNativeObject();
   virtual void NotifyAccessibilityEvent(ax::mojom::Event event_type) {}
 #if defined(OS_MACOSX)
@@ -170,6 +179,9 @@
   // "presentational".
   bool is_ignored_;
 
+  Widget* next_focus_ = nullptr;
+  Widget* previous_focus_ = nullptr;
+
   DISALLOW_COPY_AND_ASSIGN(ViewAccessibility);
 };
 
diff --git a/ui/views/accessibility/view_ax_platform_node_delegate_unittest.cc b/ui/views/accessibility/view_ax_platform_node_delegate_unittest.cc
index 6df1963..2ee0c91 100644
--- a/ui/views/accessibility/view_ax_platform_node_delegate_unittest.cc
+++ b/ui/views/accessibility/view_ax_platform_node_delegate_unittest.cc
@@ -182,7 +182,9 @@
 
 class TestAXEventObserver : public AXEventObserver {
  public:
-  TestAXEventObserver() { AXEventManager::Get()->AddObserver(this); }
+  TestAXEventObserver(AXAuraObjCache* cache) : cache_(cache) {
+    AXEventManager::Get()->AddObserver(this);
+  }
 
   ~TestAXEventObserver() override {
     AXEventManager::Get()->RemoveObserver(this);
@@ -190,13 +192,14 @@
 
   // AXEventObserver:
   void OnViewEvent(View* view, ax::mojom::Event event_type) override {
-    AXAuraObjCache* ax = AXAuraObjCache::GetInstance();
     std::vector<AXAuraObjWrapper*> out_children;
-    AXAuraObjWrapper* ax_obj = ax->GetOrCreate(view->GetWidget());
+    AXAuraObjWrapper* ax_obj = cache_->GetOrCreate(view->GetWidget());
     ax_obj->GetChildren(&out_children);
   }
 
  private:
+  AXAuraObjCache* cache_;
+
   DISALLOW_COPY_AND_ASSIGN(TestAXEventObserver);
 };
 
@@ -207,7 +210,8 @@
 TEST_F(ViewAccessibilityTest, LayoutCalledInvalidateRootView) {
   // TODO: Construct a real AutomationManagerAura rather than using this
   // observer to simulate it.
-  TestAXEventObserver observer;
+  AXAuraObjCache cache;
+  TestAXEventObserver observer(&cache);
   std::unique_ptr<Widget> widget(new Widget);
   Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP);
   params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
@@ -226,8 +230,8 @@
   // During the destruction of parent, OnBlur will be called and change the
   // visibility to false.
   parent->SetVisible(true);
-  AXAuraObjCache* ax = AXAuraObjCache::GetInstance();
-  ax->GetOrCreate(widget.get());
+
+  cache.GetOrCreate(widget.get());
 }
 #endif
 
diff --git a/ui/views/mus/ax_remote_host.cc b/ui/views/mus/ax_remote_host.cc
index 4e9c67ee..0200e039 100644
--- a/ui/views/mus/ax_remote_host.cc
+++ b/ui/views/mus/ax_remote_host.cc
@@ -31,15 +31,15 @@
 
 namespace views {
 
-AXRemoteHost::AXRemoteHost() {
+AXRemoteHost::AXRemoteHost(AXAuraObjCache* cache) : cache_(cache) {
   AXEventManager::Get()->AddObserver(this);
-  AXAuraObjCache::GetInstance()->SetDelegate(this);
+  cache_->SetDelegate(this);
 }
 
 AXRemoteHost::~AXRemoteHost() {
   if (widget_)
     StopMonitoringWidget();
-  AXAuraObjCache::GetInstance()->SetDelegate(nullptr);
+  cache_->SetDelegate(nullptr);
   AXEventManager::Get()->RemoveObserver(this);
 }
 
@@ -70,15 +70,15 @@
                                           new std::string(tree_id_.ToString()));
 
   // The cache needs to track the root window to follow focus changes.
-  AXAuraObjCache* cache = AXAuraObjCache::GetInstance();
-  cache->OnRootWindowObjCreated(widget_->GetNativeWindow());
+  cache_->OnRootWindowObjCreated(widget_->GetNativeWindow());
 
   // Start the AX tree with the contents view because the window frame is
   // handled by the window manager in another process.
   View* contents_view = widget_->widget_delegate()->GetContentsView();
-  AXAuraObjWrapper* contents_wrapper = cache->GetOrCreate(contents_view);
+  AXAuraObjWrapper* contents_wrapper = cache_->GetOrCreate(contents_view);
 
-  tree_source_ = std::make_unique<AXTreeSourceMus>(contents_wrapper, tree_id_);
+  tree_source_ =
+      std::make_unique<AXTreeSourceMus>(contents_wrapper, tree_id_, cache_);
   tree_serializer_ = std::make_unique<AuraAXTreeSerializer>(tree_source_.get());
 
   // Inform the serializer of the display device scale factor.
@@ -93,9 +93,8 @@
   DCHECK(widget_->HasObserver(this));
   Screen::GetScreen()->RemoveObserver(this);
   widget_->RemoveObserver(this);
-  AXAuraObjCache* cache = AXAuraObjCache::GetInstance();
-  cache->OnRootWindowObjDestroyed(widget_->GetNativeWindow());
-  cache->Remove(widget_->widget_delegate()->GetContentsView());
+  cache_->OnRootWindowObjDestroyed(widget_->GetNativeWindow());
+  cache_->Remove(widget_->widget_delegate()->GetContentsView());
   widget_ = nullptr;
   // Delete source and serializers to save memory.
   tree_serializer_.reset();
@@ -168,7 +167,7 @@
     return;
 
   // Can return null for views without a widget.
-  AXAuraObjWrapper* aura_obj = AXAuraObjCache::GetInstance()->GetOrCreate(view);
+  AXAuraObjWrapper* aura_obj = cache_->GetOrCreate(view);
   if (!aura_obj)
     return;
   SendEvent(aura_obj, event_type);
@@ -243,7 +242,7 @@
   updates.push_back(update);
 
   // Make sure the focused node is serialized.
-  AXAuraObjWrapper* focus = AXAuraObjCache::GetInstance()->GetFocus();
+  AXAuraObjWrapper* focus = cache_->GetFocus();
   if (focus) {
     ui::AXTreeUpdate focused_node_update;
     tree_serializer_->SerializeChanges(focus, &focused_node_update);
diff --git a/ui/views/mus/ax_remote_host.h b/ui/views/mus/ax_remote_host.h
index f9db944..e67eba5 100644
--- a/ui/views/mus/ax_remote_host.h
+++ b/ui/views/mus/ax_remote_host.h
@@ -44,7 +44,7 @@
                                       public AXAuraObjCache::Delegate,
                                       public AXEventObserver {
  public:
-  AXRemoteHost();
+  explicit AXRemoteHost(AXAuraObjCache* cache);
   ~AXRemoteHost() override;
 
   // Initializes and adds ourself as a client of the host service.
@@ -121,6 +121,7 @@
   using AuraAXTreeSerializer =
       ui::AXTreeSerializer<AXAuraObjWrapper*, ui::AXNodeData, ui::AXTreeData>;
   std::unique_ptr<AuraAXTreeSerializer> tree_serializer_;
+  AXAuraObjCache* cache_;
 
   DISALLOW_COPY_AND_ASSIGN(AXRemoteHost);
 };
diff --git a/ui/views/mus/ax_remote_host_unittest.cc b/ui/views/mus/ax_remote_host_unittest.cc
index 0bcbf8e..dc6f20a 100644
--- a/ui/views/mus/ax_remote_host_unittest.cc
+++ b/ui/views/mus/ax_remote_host_unittest.cc
@@ -112,8 +112,8 @@
   DISALLOW_COPY_AND_ASSIGN(TestView);
 };
 
-AXRemoteHost* CreateRemote(TestAXHostService* service) {
-  std::unique_ptr<AXRemoteHost> remote = std::make_unique<AXRemoteHost>();
+AXRemoteHost* CreateRemote(TestAXHostService* service, AXAuraObjCache* cache_) {
+  std::unique_ptr<AXRemoteHost> remote = std::make_unique<AXRemoteHost>(cache_);
   remote->InitForTesting(service->CreateInterfacePtr());
   remote->FlushForTesting();
   // Install the AXRemoteHost on MusClient so it monitors Widget creation.
@@ -131,11 +131,14 @@
   return widget;
 }
 
-using AXRemoteHostTest = ViewsTestWithDesktopNativeWidget;
+class AXRemoteHostTest : public ViewsTestWithDesktopNativeWidget {
+ public:
+  AXAuraObjCache cache_;
+};
 
 TEST_F(AXRemoteHostTest, CreateRemote) {
   TestAXHostService service(false /*automation_enabled*/);
-  CreateRemote(&service);
+  CreateRemote(&service, &cache_);
 
   // Client registered itself with service.
   EXPECT_EQ(1, service.remote_host_count_);
@@ -143,7 +146,7 @@
 
 TEST_F(AXRemoteHostTest, AutomationEnabled) {
   TestAXHostService service(true /*automation_enabled*/);
-  AXRemoteHost* remote = CreateRemote(&service);
+  AXRemoteHost* remote = CreateRemote(&service, &cache_);
   std::unique_ptr<Widget> widget = CreateTestWidget();
   remote->FlushForTesting();
 
@@ -157,8 +160,7 @@
   // Event was sent with initial hierarchy.
   EXPECT_EQ(TestAXTreeID(), service.last_tree_id_);
   EXPECT_EQ(ax::mojom::Event::kLoadComplete, service.last_event_.event_type);
-  EXPECT_EQ(AXAuraObjCache::GetInstance()->GetID(
-                widget->widget_delegate()->GetContentsView()),
+  EXPECT_EQ(cache_.GetID(widget->widget_delegate()->GetContentsView()),
             service.last_event_.id);
 }
 
@@ -167,7 +169,7 @@
   // If ChromeVox has ever been open during the user session then the remote app
   // can see automation enabled at startup.
   TestAXHostService service(true /*automation_enabled*/);
-  AXRemoteHost* remote = CreateRemote(&service);
+  AXRemoteHost* remote = CreateRemote(&service, &cache_);
   std::unique_ptr<Widget> widget = CreateTestWidget();
   remote->FlushForTesting();
 
@@ -188,7 +190,7 @@
 // event before it is attached to a Widget. https://crbug.com/889121
 TEST_F(AXRemoteHostTest, SendEventOnViewWithNoWidget) {
   TestAXHostService service(true /*automation_enabled*/);
-  AXRemoteHost* remote = CreateRemote(&service);
+  AXRemoteHost* remote = CreateRemote(&service, &cache_);
   std::unique_ptr<Widget> widget = CreateTestWidget();
   remote->FlushForTesting();
 
@@ -203,7 +205,7 @@
 // https://crbug.com/869608
 TEST_F(AXRemoteHostTest, AsyncWidgetClose) {
   TestAXHostService service(true /*automation_enabled*/);
-  AXRemoteHost* remote = CreateRemote(&service);
+  AXRemoteHost* remote = CreateRemote(&service, &cache_);
   remote->FlushForTesting();
 
   Widget* widget = new Widget();  // Owned by native widget.
@@ -228,7 +230,7 @@
 
 TEST_F(AXRemoteHostTest, CreateWidgetThenEnableAutomation) {
   TestAXHostService service(false /*automation_enabled*/);
-  AXRemoteHost* remote = CreateRemote(&service);
+  AXRemoteHost* remote = CreateRemote(&service, &cache_);
   std::unique_ptr<Widget> widget = CreateTestWidget();
   remote->FlushForTesting();
 
@@ -240,26 +242,25 @@
 
   // Event was sent with initial hierarchy.
   EXPECT_EQ(ax::mojom::Event::kLoadComplete, service.last_event_.event_type);
-  EXPECT_EQ(AXAuraObjCache::GetInstance()->GetID(
-                widget->widget_delegate()->GetContentsView()),
+  EXPECT_EQ(cache_.GetID(widget->widget_delegate()->GetContentsView()),
             service.last_event_.id);
 }
 
 TEST_F(AXRemoteHostTest, PerformAction) {
   TestAXHostService service(true /*automation_enabled*/);
-  AXRemoteHost* remote = CreateRemote(&service);
+  AXRemoteHost* remote = CreateRemote(&service, &cache_);
 
   // Create a view to sense the action.
   TestView view;
   view.SetBounds(0, 0, 100, 100);
   std::unique_ptr<Widget> widget = CreateTestWidget();
   widget->GetRootView()->AddChildView(&view);
-  AXAuraObjCache::GetInstance()->GetOrCreate(&view);
+  cache_.GetOrCreate(&view);
 
   // Request an action on the view.
   ui::AXActionData action;
   action.action = ax::mojom::Action::kScrollDown;
-  action.target_node_id = AXAuraObjCache::GetInstance()->GetID(&view);
+  action.target_node_id = cache_.GetID(&view);
   remote->PerformAction(action);
 
   // View received the action.
@@ -269,7 +270,7 @@
 
 TEST_F(AXRemoteHostTest, PerformHitTest) {
   TestAXHostService service(true /*automation_enabled*/);
-  AXRemoteHost* remote = CreateRemote(&service);
+  AXRemoteHost* remote = CreateRemote(&service, &cache_);
 
   // Create a view to sense the action.
   TestView view;
@@ -310,7 +311,7 @@
 
   // Create a widget.
   TestAXHostService service(true /*automation_enabled*/);
-  AXRemoteHost* remote = CreateRemote(&service);
+  AXRemoteHost* remote = CreateRemote(&service, &cache_);
   std::unique_ptr<Widget> widget = CreateTestWidget();
   remote->FlushForTesting();
 
diff --git a/ui/views/mus/ax_tree_source_mus.cc b/ui/views/mus/ax_tree_source_mus.cc
index f014f30..6ca2eb56 100644
--- a/ui/views/mus/ax_tree_source_mus.cc
+++ b/ui/views/mus/ax_tree_source_mus.cc
@@ -12,8 +12,9 @@
 namespace views {
 
 AXTreeSourceMus::AXTreeSourceMus(AXAuraObjWrapper* root,
-                                 const ui::AXTreeID& tree_id)
-    : AXTreeSourceViews(root, tree_id) {}
+                                 const ui::AXTreeID& tree_id,
+                                 AXAuraObjCache* cache)
+    : AXTreeSourceViews(root, tree_id, cache) {}
 
 AXTreeSourceMus::~AXTreeSourceMus() = default;
 
diff --git a/ui/views/mus/ax_tree_source_mus.h b/ui/views/mus/ax_tree_source_mus.h
index 9246bb5..bd069a0 100644
--- a/ui/views/mus/ax_tree_source_mus.h
+++ b/ui/views/mus/ax_tree_source_mus.h
@@ -20,7 +20,9 @@
 class VIEWS_MUS_EXPORT AXTreeSourceMus : public AXTreeSourceViews {
  public:
   // |root| must outlive this object.
-  AXTreeSourceMus(AXAuraObjWrapper* root, const ui::AXTreeID& tree_id);
+  AXTreeSourceMus(AXAuraObjWrapper* root,
+                  const ui::AXTreeID& tree_id,
+                  AXAuraObjCache* cache);
   ~AXTreeSourceMus() override;
 
   void set_device_scale_factor(float scale) { device_scale_factor_ = scale; }
diff --git a/ui/views/mus/ax_tree_source_mus_unittest.cc b/ui/views/mus/ax_tree_source_mus_unittest.cc
index 1f8320bf..01486e4 100644
--- a/ui/views/mus/ax_tree_source_mus_unittest.cc
+++ b/ui/views/mus/ax_tree_source_mus_unittest.cc
@@ -59,19 +59,18 @@
 };
 
 TEST_F(AXTreeSourceMusTest, GetTreeData) {
-  AXAuraObjWrapper* root =
-      AXAuraObjCache::GetInstance()->GetOrCreate(widget_->GetContentsView());
-  AXTreeSourceMus tree(root, ax_tree_id_);
+  AXAuraObjCache cache;
+  AXAuraObjWrapper* root = cache.GetOrCreate(widget_->GetContentsView());
+  AXTreeSourceMus tree(root, ax_tree_id_, &cache);
   ui::AXTreeData tree_data;
   tree.GetTreeData(&tree_data);
   EXPECT_EQ(ax_tree_id_, tree_data.tree_id);
 }
 
 TEST_F(AXTreeSourceMusTest, Serialize) {
-  AXAuraObjCache* cache = AXAuraObjCache::GetInstance();
-  AXAuraObjWrapper* root = cache->GetOrCreate(widget_->GetContentsView());
-
-  AXTreeSourceMus tree(root, ax_tree_id_);
+  AXAuraObjCache cache;
+  AXAuraObjWrapper* root = cache.GetOrCreate(widget_->GetContentsView());
+  AXTreeSourceMus tree(root, ax_tree_id_, &cache);
   EXPECT_EQ(root, tree.GetRoot());
 
   // Serialize the root.
@@ -83,7 +82,7 @@
   EXPECT_EQ(-1, node_data.relative_bounds.offset_container_id);
 
   // Serialize a child.
-  tree.SerializeNode(cache->GetOrCreate(label_), &node_data);
+  tree.SerializeNode(cache.GetOrCreate(label_), &node_data);
 
   // Child has relative position with the root as the container.
   EXPECT_EQ(gfx::RectF(1, 1, 111, 111), node_data.relative_bounds.bounds);
@@ -91,11 +90,11 @@
 }
 
 TEST_F(AXTreeSourceMusTest, ScaleFactor) {
-  AXAuraObjCache* cache = AXAuraObjCache::GetInstance();
-  AXAuraObjWrapper* root = cache->GetOrCreate(widget_->GetContentsView());
+  AXAuraObjCache cache;
+  AXAuraObjWrapper* root = cache.GetOrCreate(widget_->GetContentsView());
 
   // Simulate serializing a widget on a high-dpi display.
-  AXTreeSourceMus tree(root, ax_tree_id_);
+  AXTreeSourceMus tree(root, ax_tree_id_, &cache);
   tree.set_device_scale_factor(2.f);
 
   // Serialize the root.
diff --git a/ui/views/mus/desktop_window_tree_host_mus_unittest.cc b/ui/views/mus/desktop_window_tree_host_mus_unittest.cc
index ca8fe02..627b3b2 100644
--- a/ui/views/mus/desktop_window_tree_host_mus_unittest.cc
+++ b/ui/views/mus/desktop_window_tree_host_mus_unittest.cc
@@ -619,7 +619,8 @@
 
 TEST_F(DesktopWindowTreeHostMusTest, Accessibility) {
   // Pretend we're using the remote AX service, like shortcut_viewer.
-  MusClientTestApi::SetAXRemoteHost(std::make_unique<AXRemoteHost>());
+  AXAuraObjCache cache;
+  MusClientTestApi::SetAXRemoteHost(std::make_unique<AXRemoteHost>(&cache));
 
   std::unique_ptr<Widget> widget = CreateWidget();
   // Widget frame views do not participate in accessibility node hierarchy
diff --git a/ui/views/mus/mus_client.cc b/ui/views/mus/mus_client.cc
index 7df547ce..41920e9d 100644
--- a/ui/views/mus/mus_client.cc
+++ b/ui/views/mus/mus_client.cc
@@ -111,7 +111,9 @@
         std::make_unique<ui::ClipboardClient>(std::move(clipboard_host_ptr)));
 
     if (params.use_accessibility_host) {
-      ax_remote_host_ = std::make_unique<AXRemoteHost>();
+      ax_aura_obj_cache_ = std::make_unique<AXAuraObjCache>();
+      ax_remote_host_ =
+          std::make_unique<AXRemoteHost>(ax_aura_obj_cache_.get());
       ax_remote_host_->Init(connector);
     }
   }
diff --git a/ui/views/mus/mus_client.h b/ui/views/mus/mus_client.h
index 2de1241..e407398 100644
--- a/ui/views/mus/mus_client.h
+++ b/ui/views/mus/mus_client.h
@@ -43,6 +43,7 @@
 
 namespace views {
 
+class AXAuraObjCache;
 class AXRemoteHost;
 class DesktopNativeWidgetAura;
 class MusClientObserver;
@@ -195,6 +196,10 @@
   // under OopAsh.
   std::unique_ptr<AXRemoteHost> ax_remote_host_;
 
+  // A cache keeping track of ids for accessibility. Used in conjunction with
+  // the above remote host.
+  std::unique_ptr<views::AXAuraObjCache> ax_aura_obj_cache_;
+
   DISALLOW_COPY_AND_ASSIGN(MusClient);
 };